turbopack_ecmascript/analyzer/
well_known.rs

1use std::mem::take;
2
3use anyhow::Result;
4use turbo_rcstr::rcstr;
5use turbo_tasks::Vc;
6use turbopack_core::compile_time_info::CompileTimeInfo;
7use url::Url;
8
9use super::{
10    ConstantValue, JsValue, JsValueUrlKind, ModuleValue, WellKnownFunctionKind,
11    WellKnownObjectKind, imports::ImportAnnotations,
12};
13use crate::analyzer::RequireContextValue;
14
15pub async fn replace_well_known(
16    value: JsValue,
17    compile_time_info: Vc<CompileTimeInfo>,
18    allow_project_root_tracing: bool,
19) -> Result<(JsValue, bool)> {
20    Ok(match value {
21        JsValue::Call(_, box JsValue::WellKnownFunction(kind), args) => (
22            well_known_function_call(
23                kind,
24                JsValue::unknown_empty(false, "this is not analyzed yet"),
25                args,
26                compile_time_info,
27                allow_project_root_tracing,
28            )
29            .await?,
30            true,
31        ),
32        JsValue::Call(usize, callee, args) => {
33            // var fs = require('fs'), fs = __importStar(fs);
34            // TODO(WEB-552) this is not correct and has many false positives!
35            if args.len() == 1
36                && let JsValue::WellKnownObject(_) = &args[0]
37            {
38                return Ok((args[0].clone(), true));
39            }
40            (JsValue::Call(usize, callee, args), false)
41        }
42        JsValue::Member(_, box JsValue::WellKnownObject(kind), box prop) => {
43            well_known_object_member(kind, prop, compile_time_info).await?
44        }
45        JsValue::Member(_, box JsValue::WellKnownFunction(kind), box prop) => {
46            well_known_function_member(kind, prop)
47        }
48        JsValue::Member(_, box JsValue::Array { .. }, box ref prop) => match prop.as_str() {
49            Some("filter") => (
50                JsValue::WellKnownFunction(WellKnownFunctionKind::ArrayFilter),
51                true,
52            ),
53            Some("forEach") => (
54                JsValue::WellKnownFunction(WellKnownFunctionKind::ArrayForEach),
55                true,
56            ),
57            Some("map") => (
58                JsValue::WellKnownFunction(WellKnownFunctionKind::ArrayMap),
59                true,
60            ),
61            _ => (value, false),
62        },
63        _ => (value, false),
64    })
65}
66
67pub async fn well_known_function_call(
68    kind: WellKnownFunctionKind,
69    _this: JsValue,
70    args: Vec<JsValue>,
71    compile_time_info: Vc<CompileTimeInfo>,
72    allow_project_root_tracing: bool,
73) -> Result<JsValue> {
74    Ok(match kind {
75        WellKnownFunctionKind::ObjectAssign => object_assign(args),
76        WellKnownFunctionKind::PathJoin => path_join(args),
77        WellKnownFunctionKind::PathDirname => path_dirname(args),
78        WellKnownFunctionKind::PathResolve(cwd) => path_resolve(*cwd, args),
79        WellKnownFunctionKind::Import => import(args),
80        WellKnownFunctionKind::Require => require(args),
81        WellKnownFunctionKind::RequireContextRequire(value) => {
82            require_context_require(value, args)?
83        }
84        WellKnownFunctionKind::RequireContextRequireKeys(value) => {
85            require_context_require_keys(value, args)?
86        }
87        WellKnownFunctionKind::RequireContextRequireResolve(value) => {
88            require_context_require_resolve(value, args)?
89        }
90        WellKnownFunctionKind::PathToFileUrl => path_to_file_url(args),
91        WellKnownFunctionKind::OsArch => compile_time_info
92            .environment()
93            .compile_target()
94            .await?
95            .arch
96            .as_str()
97            .into(),
98        WellKnownFunctionKind::OsPlatform => compile_time_info
99            .environment()
100            .compile_target()
101            .await?
102            .platform
103            .as_str()
104            .into(),
105        WellKnownFunctionKind::ProcessCwd => {
106            if allow_project_root_tracing
107                && let Some(cwd) = &*compile_time_info.environment().cwd().await?
108            {
109                format!("/ROOT/{}", cwd.path).into()
110            } else {
111                JsValue::unknown(
112                    JsValue::call(Box::new(JsValue::WellKnownFunction(kind)), args),
113                    true,
114                    "process.cwd is not specified in the environment",
115                )
116            }
117        }
118        WellKnownFunctionKind::OsEndianness => compile_time_info
119            .environment()
120            .compile_target()
121            .await?
122            .endianness
123            .as_str()
124            .into(),
125        WellKnownFunctionKind::NodeExpress => {
126            JsValue::WellKnownObject(WellKnownObjectKind::NodeExpressApp)
127        }
128        // bypass
129        WellKnownFunctionKind::NodeResolveFrom => {
130            JsValue::WellKnownFunction(WellKnownFunctionKind::NodeResolveFrom)
131        }
132
133        _ => JsValue::unknown(
134            JsValue::call(Box::new(JsValue::WellKnownFunction(kind)), args),
135            true,
136            "unsupported function",
137        ),
138    })
139}
140
141fn object_assign(args: Vec<JsValue>) -> JsValue {
142    if args.iter().all(|arg| matches!(arg, JsValue::Object { .. })) {
143        if let Some(mut merged_object) = args.into_iter().reduce(|mut acc, cur| {
144            if let JsValue::Object { parts, mutable, .. } = &mut acc
145                && let JsValue::Object {
146                    parts: next_parts,
147                    mutable: next_mutable,
148                    ..
149                } = &cur
150            {
151                parts.extend_from_slice(next_parts);
152                *mutable |= *next_mutable;
153            }
154            acc
155        }) {
156            merged_object.update_total_nodes();
157            merged_object
158        } else {
159            JsValue::unknown(
160                JsValue::call(
161                    Box::new(JsValue::WellKnownFunction(
162                        WellKnownFunctionKind::ObjectAssign,
163                    )),
164                    vec![],
165                ),
166                true,
167                "empty arguments for Object.assign",
168            )
169        }
170    } else {
171        JsValue::unknown(
172            JsValue::call(
173                Box::new(JsValue::WellKnownFunction(
174                    WellKnownFunctionKind::ObjectAssign,
175                )),
176                args,
177            ),
178            true,
179            "only const object assign is supported",
180        )
181    }
182}
183
184fn path_join(args: Vec<JsValue>) -> JsValue {
185    if args.is_empty() {
186        return rcstr!(".").into();
187    }
188    let mut parts = Vec::new();
189    for item in args {
190        if let Some(str) = item.as_str() {
191            let split = str.split('/');
192            parts.extend(split.map(|s| s.into()));
193        } else {
194            parts.push(item);
195        }
196    }
197    let mut results_final = Vec::new();
198    let mut results: Vec<JsValue> = Vec::new();
199    for item in parts {
200        if let Some(str) = item.as_str() {
201            match str {
202                "" | "." => {
203                    if results_final.is_empty() && results.is_empty() {
204                        results_final.push(item);
205                    }
206                }
207                ".." => {
208                    if results.pop().is_none() {
209                        results_final.push(item);
210                    }
211                }
212                _ => results.push(item),
213            }
214        } else {
215            results_final.append(&mut results);
216            results_final.push(item);
217        }
218    }
219    results_final.append(&mut results);
220    let mut iter = results_final.into_iter();
221    let first = iter.next().unwrap();
222    let mut last_is_str = first.as_str().is_some();
223    results.push(first);
224    for part in iter {
225        let is_str = part.as_str().is_some();
226        if last_is_str && is_str {
227            results.push(rcstr!("/").into());
228        } else {
229            results.push(JsValue::alternatives(vec![
230                rcstr!("/").into(),
231                rcstr!("").into(),
232            ]));
233        }
234        results.push(part);
235        last_is_str = is_str;
236    }
237    JsValue::concat(results)
238}
239
240fn path_resolve(cwd: JsValue, mut args: Vec<JsValue>) -> JsValue {
241    // If no path segments are passed, `path.resolve()` will return the absolute
242    // path of the current working directory.
243    if args.is_empty() {
244        return JsValue::unknown_empty(false, "cwd is not static analyzable");
245    }
246    if args.len() == 1 {
247        return args.into_iter().next().unwrap();
248    }
249
250    // path.resolve stops at the string starting with `/`
251    for (idx, arg) in args.iter().enumerate().rev() {
252        if idx != 0
253            && let Some(str) = arg.as_str()
254            && str.starts_with('/')
255        {
256            return path_resolve(cwd, args.drain(idx..).collect());
257        }
258    }
259
260    let mut results_final = Vec::new();
261    let mut results: Vec<JsValue> = Vec::new();
262    for item in args {
263        if let Some(str) = item.as_str() {
264            for str in str.split('/') {
265                match str {
266                    "" | "." => {
267                        if results_final.is_empty() && results.is_empty() {
268                            results_final.push(str.into());
269                        }
270                    }
271                    ".." => {
272                        if results.pop().is_none() {
273                            results_final.push(rcstr!("..").into());
274                        }
275                    }
276                    _ => results.push(str.into()),
277                }
278            }
279        } else {
280            results_final.append(&mut results);
281            results_final.push(item);
282        }
283    }
284    results_final.append(&mut results);
285    let mut iter = results_final.into_iter();
286    let first = iter.next().unwrap();
287
288    let is_already_absolute =
289        first.is_empty_string() == Some(true) || first.starts_with("/") == Some(true);
290
291    let mut last_was_str = first.as_str().is_some();
292
293    if !is_already_absolute {
294        results.push(cwd);
295    }
296
297    results.push(first);
298    for part in iter {
299        let is_str = part.as_str().is_some();
300        if last_was_str && is_str {
301            results.push(rcstr!("/").into());
302        } else {
303            results.push(JsValue::alternatives(vec![
304                rcstr!("/").into(),
305                rcstr!("").into(),
306            ]));
307        }
308        results.push(part);
309        last_was_str = is_str;
310    }
311
312    JsValue::concat(results)
313}
314
315fn path_dirname(mut args: Vec<JsValue>) -> JsValue {
316    if let Some(arg) = args.iter_mut().next() {
317        if let Some(str) = arg.as_str() {
318            if let Some(i) = str.rfind('/') {
319                return JsValue::Constant(ConstantValue::Str(str[..i].to_string().into()));
320            } else {
321                return JsValue::Constant(ConstantValue::Str(rcstr!("").into()));
322            }
323        } else if let JsValue::Concat(_, items) = arg
324            && let Some(last) = items.last_mut()
325            && let Some(str) = last.as_str()
326            && let Some(i) = str.rfind('/')
327        {
328            *last = JsValue::Constant(ConstantValue::Str(str[..i].to_string().into()));
329            return take(arg);
330        }
331    }
332    JsValue::unknown(
333        JsValue::call(
334            Box::new(JsValue::WellKnownFunction(
335                WellKnownFunctionKind::PathDirname,
336            )),
337            args,
338        ),
339        true,
340        "path.dirname with unsupported arguments",
341    )
342}
343
344/// Resolve the contents of an import call, throwing errors
345/// if we come across any unsupported syntax.
346pub fn import(args: Vec<JsValue>) -> JsValue {
347    match &args[..] {
348        [JsValue::Constant(ConstantValue::Str(v))] => {
349            JsValue::promise(JsValue::Module(ModuleValue {
350                module: v.as_atom().into_owned(),
351                annotations: ImportAnnotations::default(),
352            }))
353        }
354        _ => JsValue::unknown(
355            JsValue::call(
356                Box::new(JsValue::WellKnownFunction(WellKnownFunctionKind::Import)),
357                args,
358            ),
359            true,
360            "only a single constant argument is supported",
361        ),
362    }
363}
364
365/// Resolve the contents of a require call, throwing errors
366/// if we come across any unsupported syntax.
367fn require(args: Vec<JsValue>) -> JsValue {
368    if args.len() == 1 {
369        if let Some(s) = args[0].as_str() {
370            JsValue::Module(ModuleValue {
371                module: s.into(),
372                annotations: ImportAnnotations::default(),
373            })
374        } else {
375            JsValue::unknown(
376                JsValue::call(
377                    Box::new(JsValue::WellKnownFunction(WellKnownFunctionKind::Require)),
378                    args,
379                ),
380                true,
381                "only constant argument is supported",
382            )
383        }
384    } else {
385        JsValue::unknown(
386            JsValue::call(
387                Box::new(JsValue::WellKnownFunction(WellKnownFunctionKind::Require)),
388                args,
389            ),
390            true,
391            "only a single argument is supported",
392        )
393    }
394}
395
396/// (try to) statically evaluate `require.context(...)()`
397fn require_context_require(val: RequireContextValue, args: Vec<JsValue>) -> Result<JsValue> {
398    if args.is_empty() {
399        return Ok(JsValue::unknown(
400            JsValue::call(
401                Box::new(JsValue::WellKnownFunction(
402                    WellKnownFunctionKind::RequireContextRequire(val),
403                )),
404                args,
405            ),
406            true,
407            "require.context(...).require() requires an argument specifying the module path",
408        ));
409    }
410
411    let Some(s) = args[0].as_str() else {
412        return Ok(JsValue::unknown(
413            JsValue::call(
414                Box::new(JsValue::WellKnownFunction(
415                    WellKnownFunctionKind::RequireContextRequire(val),
416                )),
417                args,
418            ),
419            true,
420            "require.context(...).require() only accepts a single, constant string argument",
421        ));
422    };
423
424    let Some(m) = val.0.get(s) else {
425        return Ok(JsValue::unknown(
426            JsValue::call(
427                Box::new(JsValue::WellKnownFunction(
428                    WellKnownFunctionKind::RequireContextRequire(val),
429                )),
430                args,
431            ),
432            true,
433            "require.context(...).require() can only be called with an argument that's in the \
434             context",
435        ));
436    };
437
438    Ok(JsValue::Module(ModuleValue {
439        module: m.to_string().into(),
440        annotations: ImportAnnotations::default(),
441    }))
442}
443
444/// (try to) statically evaluate `require.context(...).keys()`
445fn require_context_require_keys(val: RequireContextValue, args: Vec<JsValue>) -> Result<JsValue> {
446    Ok(if args.is_empty() {
447        JsValue::array(val.0.keys().cloned().map(|k| k.into()).collect())
448    } else {
449        JsValue::unknown(
450            JsValue::call(
451                Box::new(JsValue::WellKnownFunction(
452                    WellKnownFunctionKind::RequireContextRequireKeys(val),
453                )),
454                args,
455            ),
456            true,
457            "require.context(...).keys() does not accept arguments",
458        )
459    })
460}
461
462/// (try to) statically evaluate `require.context(...).resolve()`
463fn require_context_require_resolve(
464    val: RequireContextValue,
465    args: Vec<JsValue>,
466) -> Result<JsValue> {
467    if args.len() != 1 {
468        return Ok(JsValue::unknown(
469            JsValue::call(
470                Box::new(JsValue::WellKnownFunction(
471                    WellKnownFunctionKind::RequireContextRequireResolve(val),
472                )),
473                args,
474            ),
475            true,
476            "require.context(...).resolve() only accepts a single, constant string argument",
477        ));
478    }
479
480    let Some(s) = args[0].as_str() else {
481        return Ok(JsValue::unknown(
482            JsValue::call(
483                Box::new(JsValue::WellKnownFunction(
484                    WellKnownFunctionKind::RequireContextRequireResolve(val),
485                )),
486                args,
487            ),
488            true,
489            "require.context(...).resolve() only accepts a single, constant string argument",
490        ));
491    };
492
493    let Some(m) = val.0.get(s) else {
494        return Ok(JsValue::unknown(
495            JsValue::call(
496                Box::new(JsValue::WellKnownFunction(
497                    WellKnownFunctionKind::RequireContextRequireResolve(val),
498                )),
499                args,
500            ),
501            true,
502            "require.context(...).resolve() can only be called with an argument that's in the \
503             context",
504        ));
505    };
506
507    Ok(m.as_str().into())
508}
509
510fn path_to_file_url(args: Vec<JsValue>) -> JsValue {
511    if args.len() == 1 {
512        if let Some(path) = args[0].as_str() {
513            Url::from_file_path(path)
514                .map(|url| JsValue::Url(String::from(url).into(), JsValueUrlKind::Absolute))
515                .unwrap_or_else(|_| {
516                    JsValue::unknown(
517                        JsValue::call(
518                            Box::new(JsValue::WellKnownFunction(
519                                WellKnownFunctionKind::PathToFileUrl,
520                            )),
521                            args,
522                        ),
523                        true,
524                        "url not parseable: path is relative or has an invalid prefix",
525                    )
526                })
527        } else {
528            JsValue::unknown(
529                JsValue::call(
530                    Box::new(JsValue::WellKnownFunction(
531                        WellKnownFunctionKind::PathToFileUrl,
532                    )),
533                    args,
534                ),
535                true,
536                "only constant argument is supported",
537            )
538        }
539    } else {
540        JsValue::unknown(
541            JsValue::call(
542                Box::new(JsValue::WellKnownFunction(
543                    WellKnownFunctionKind::PathToFileUrl,
544                )),
545                args,
546            ),
547            true,
548            "only a single argument is supported",
549        )
550    }
551}
552
553fn well_known_function_member(kind: WellKnownFunctionKind, prop: JsValue) -> (JsValue, bool) {
554    let new_value = match (kind, prop.as_str()) {
555        (WellKnownFunctionKind::Require, Some("resolve")) => {
556            JsValue::WellKnownFunction(WellKnownFunctionKind::RequireResolve)
557        }
558        (WellKnownFunctionKind::Require, Some("cache")) => {
559            JsValue::WellKnownObject(WellKnownObjectKind::RequireCache)
560        }
561        (WellKnownFunctionKind::Require, Some("context")) => {
562            JsValue::WellKnownFunction(WellKnownFunctionKind::RequireContext)
563        }
564        (WellKnownFunctionKind::RequireContextRequire(val), Some("resolve")) => {
565            JsValue::WellKnownFunction(WellKnownFunctionKind::RequireContextRequireResolve(val))
566        }
567        (WellKnownFunctionKind::RequireContextRequire(val), Some("keys")) => {
568            JsValue::WellKnownFunction(WellKnownFunctionKind::RequireContextRequireKeys(val))
569        }
570        (WellKnownFunctionKind::NodeStrongGlobalize, Some("SetRootDir")) => {
571            JsValue::WellKnownFunction(WellKnownFunctionKind::NodeStrongGlobalizeSetRootDir)
572        }
573        (WellKnownFunctionKind::NodeResolveFrom, Some("silent")) => {
574            JsValue::WellKnownFunction(WellKnownFunctionKind::NodeResolveFrom)
575        }
576        (WellKnownFunctionKind::Import, Some("meta")) => {
577            JsValue::WellKnownObject(WellKnownObjectKind::ImportMeta)
578        }
579        #[allow(unreachable_patterns)]
580        (kind, _) => {
581            return (
582                JsValue::member(Box::new(JsValue::WellKnownFunction(kind)), Box::new(prop)),
583                false,
584            );
585        }
586    };
587    (new_value, true)
588}
589
590async fn well_known_object_member(
591    kind: WellKnownObjectKind,
592    prop: JsValue,
593    compile_time_info: Vc<CompileTimeInfo>,
594) -> Result<(JsValue, bool)> {
595    let new_value = match kind {
596        WellKnownObjectKind::GlobalObject => global_object(prop),
597        WellKnownObjectKind::PathModule | WellKnownObjectKind::PathModuleDefault => {
598            path_module_member(kind, prop)
599        }
600        WellKnownObjectKind::FsModule
601        | WellKnownObjectKind::FsModuleDefault
602        | WellKnownObjectKind::FsModulePromises => fs_module_member(kind, prop),
603        WellKnownObjectKind::FsExtraModule | WellKnownObjectKind::FsExtraModuleDefault => {
604            fs_extra_module_member(kind, prop)
605        }
606        WellKnownObjectKind::ModuleModule | WellKnownObjectKind::ModuleModuleDefault => {
607            module_module_member(kind, prop)
608        }
609        WellKnownObjectKind::UrlModule | WellKnownObjectKind::UrlModuleDefault => {
610            url_module_member(kind, prop)
611        }
612        WellKnownObjectKind::ChildProcess | WellKnownObjectKind::ChildProcessDefault => {
613            child_process_module_member(kind, prop)
614        }
615        WellKnownObjectKind::OsModule | WellKnownObjectKind::OsModuleDefault => {
616            os_module_member(kind, prop)
617        }
618        WellKnownObjectKind::NodeProcess => node_process_member(prop, compile_time_info).await?,
619        WellKnownObjectKind::NodePreGyp => node_pre_gyp(prop),
620        WellKnownObjectKind::NodeExpressApp => express(prop),
621        WellKnownObjectKind::NodeProtobufLoader => protobuf_loader(prop),
622        #[allow(unreachable_patterns)]
623        _ => {
624            return Ok((
625                JsValue::member(Box::new(JsValue::WellKnownObject(kind)), Box::new(prop)),
626                false,
627            ));
628        }
629    };
630    Ok((new_value, true))
631}
632
633fn global_object(prop: JsValue) -> JsValue {
634    match prop.as_str() {
635        Some("assign") => JsValue::WellKnownFunction(WellKnownFunctionKind::ObjectAssign),
636        _ => JsValue::unknown(
637            JsValue::member(
638                Box::new(JsValue::WellKnownObject(WellKnownObjectKind::GlobalObject)),
639                Box::new(prop),
640            ),
641            true,
642            "unsupported property on global Object",
643        ),
644    }
645}
646
647fn path_module_member(kind: WellKnownObjectKind, prop: JsValue) -> JsValue {
648    match (kind, prop.as_str()) {
649        (.., Some("join")) => JsValue::WellKnownFunction(WellKnownFunctionKind::PathJoin),
650        (.., Some("dirname")) => JsValue::WellKnownFunction(WellKnownFunctionKind::PathDirname),
651        (.., Some("resolve")) => {
652            // cwd is added while resolving in references.rs
653            JsValue::WellKnownFunction(WellKnownFunctionKind::PathResolve(Box::new(JsValue::from(
654                "",
655            ))))
656        }
657        (WellKnownObjectKind::PathModule, Some("default")) => {
658            JsValue::WellKnownObject(WellKnownObjectKind::PathModuleDefault)
659        }
660        _ => JsValue::unknown(
661            JsValue::member(
662                Box::new(JsValue::WellKnownObject(WellKnownObjectKind::PathModule)),
663                Box::new(prop),
664            ),
665            true,
666            "unsupported property on Node.js path module",
667        ),
668    }
669}
670
671fn fs_module_member(kind: WellKnownObjectKind, prop: JsValue) -> JsValue {
672    if let Some(word) = prop.as_str() {
673        match (kind, word) {
674            (
675                ..,
676                "realpath" | "realpathSync" | "stat" | "statSync" | "existsSync"
677                | "createReadStream" | "exists" | "open" | "openSync" | "readFile" | "readFileSync",
678            ) => {
679                return JsValue::WellKnownFunction(WellKnownFunctionKind::FsReadMethod(
680                    word.into(),
681                ));
682            }
683            (WellKnownObjectKind::FsModule | WellKnownObjectKind::FsModuleDefault, "promises") => {
684                return JsValue::WellKnownObject(WellKnownObjectKind::FsModulePromises);
685            }
686            (WellKnownObjectKind::FsModule, "default") => {
687                return JsValue::WellKnownObject(WellKnownObjectKind::FsModuleDefault);
688            }
689            _ => {}
690        }
691    }
692    JsValue::unknown(
693        JsValue::member(
694            Box::new(JsValue::WellKnownObject(WellKnownObjectKind::FsModule)),
695            Box::new(prop),
696        ),
697        true,
698        "unsupported property on Node.js fs module",
699    )
700}
701
702fn fs_extra_module_member(kind: WellKnownObjectKind, prop: JsValue) -> JsValue {
703    if let Some(word) = prop.as_str() {
704        match (kind, word) {
705            // regular fs methods
706            (
707                ..,
708                "realpath" | "realpathSync" | "stat" | "statSync" | "existsSync"
709                | "createReadStream" | "exists" | "open" | "openSync" | "readFile" | "readFileSync",
710            ) => {
711                return JsValue::WellKnownFunction(WellKnownFunctionKind::FsReadMethod(
712                    word.into(),
713                ));
714            }
715            // fs-extra specific
716            (
717                ..,
718                "pathExists" | "pathExistsSync" | "readJson" | "readJSON" | "readJsonSync"
719                | "readJSONSync",
720            ) => {
721                return JsValue::WellKnownFunction(WellKnownFunctionKind::FsReadMethod(
722                    word.into(),
723                ));
724            }
725            (WellKnownObjectKind::FsExtraModule, "default") => {
726                return JsValue::WellKnownObject(WellKnownObjectKind::FsExtraModuleDefault);
727            }
728            _ => {}
729        }
730    }
731    JsValue::unknown(
732        JsValue::member(
733            Box::new(JsValue::WellKnownObject(WellKnownObjectKind::FsExtraModule)),
734            Box::new(prop),
735        ),
736        true,
737        "unsupported property on fs-extra module",
738    )
739}
740
741fn module_module_member(kind: WellKnownObjectKind, prop: JsValue) -> JsValue {
742    match (kind, prop.as_str()) {
743        (.., Some("createRequire")) => {
744            JsValue::WellKnownFunction(WellKnownFunctionKind::CreateRequire)
745        }
746        (WellKnownObjectKind::ModuleModule, Some("default")) => {
747            JsValue::WellKnownObject(WellKnownObjectKind::ModuleModuleDefault)
748        }
749        _ => JsValue::unknown(
750            JsValue::member(
751                Box::new(JsValue::WellKnownObject(WellKnownObjectKind::ModuleModule)),
752                Box::new(prop),
753            ),
754            true,
755            "unsupported property on Node.js `module` module",
756        ),
757    }
758}
759
760fn url_module_member(kind: WellKnownObjectKind, prop: JsValue) -> JsValue {
761    match (kind, prop.as_str()) {
762        (.., Some("pathToFileURL")) => {
763            JsValue::WellKnownFunction(WellKnownFunctionKind::PathToFileUrl)
764        }
765        (WellKnownObjectKind::UrlModuleDefault, Some("default")) => {
766            JsValue::WellKnownObject(WellKnownObjectKind::UrlModuleDefault)
767        }
768        _ => JsValue::unknown(
769            JsValue::member(
770                Box::new(JsValue::WellKnownObject(WellKnownObjectKind::UrlModule)),
771                Box::new(prop),
772            ),
773            true,
774            "unsupported property on Node.js url module",
775        ),
776    }
777}
778
779fn child_process_module_member(kind: WellKnownObjectKind, prop: JsValue) -> JsValue {
780    let prop_str = prop.as_str();
781    match (kind, prop_str) {
782        (.., Some("spawn" | "spawnSync" | "execFile" | "execFileSync")) => {
783            JsValue::WellKnownFunction(WellKnownFunctionKind::ChildProcessSpawnMethod(
784                prop_str.unwrap().into(),
785            ))
786        }
787        (.., Some("fork")) => JsValue::WellKnownFunction(WellKnownFunctionKind::ChildProcessFork),
788        (WellKnownObjectKind::ChildProcess, Some("default")) => {
789            JsValue::WellKnownObject(WellKnownObjectKind::ChildProcessDefault)
790        }
791
792        _ => JsValue::unknown(
793            JsValue::member(
794                Box::new(JsValue::WellKnownObject(WellKnownObjectKind::ChildProcess)),
795                Box::new(prop),
796            ),
797            true,
798            "unsupported property on Node.js child_process module",
799        ),
800    }
801}
802
803fn os_module_member(kind: WellKnownObjectKind, prop: JsValue) -> JsValue {
804    match (kind, prop.as_str()) {
805        (.., Some("platform")) => JsValue::WellKnownFunction(WellKnownFunctionKind::OsPlatform),
806        (.., Some("arch")) => JsValue::WellKnownFunction(WellKnownFunctionKind::OsArch),
807        (.., Some("endianness")) => JsValue::WellKnownFunction(WellKnownFunctionKind::OsEndianness),
808        (WellKnownObjectKind::OsModule, Some("default")) => {
809            JsValue::WellKnownObject(WellKnownObjectKind::OsModuleDefault)
810        }
811        _ => JsValue::unknown(
812            JsValue::member(
813                Box::new(JsValue::WellKnownObject(WellKnownObjectKind::OsModule)),
814                Box::new(prop),
815            ),
816            true,
817            "unsupported property on Node.js os module",
818        ),
819    }
820}
821
822async fn node_process_member(
823    prop: JsValue,
824    compile_time_info: Vc<CompileTimeInfo>,
825) -> Result<JsValue> {
826    Ok(match prop.as_str() {
827        Some("arch") => compile_time_info
828            .environment()
829            .compile_target()
830            .await?
831            .arch
832            .as_str()
833            .into(),
834        Some("platform") => compile_time_info
835            .environment()
836            .compile_target()
837            .await?
838            .platform
839            .as_str()
840            .into(),
841        Some("cwd") => JsValue::WellKnownFunction(WellKnownFunctionKind::ProcessCwd),
842        Some("argv") => JsValue::WellKnownObject(WellKnownObjectKind::NodeProcessArgv),
843        Some("env") => JsValue::WellKnownObject(WellKnownObjectKind::NodeProcessEnv),
844        _ => JsValue::unknown(
845            JsValue::member(
846                Box::new(JsValue::WellKnownObject(WellKnownObjectKind::NodeProcess)),
847                Box::new(prop),
848            ),
849            true,
850            "unsupported property on Node.js process object",
851        ),
852    })
853}
854
855fn node_pre_gyp(prop: JsValue) -> JsValue {
856    match prop.as_str() {
857        Some("find") => JsValue::WellKnownFunction(WellKnownFunctionKind::NodePreGypFind),
858        _ => JsValue::unknown(
859            JsValue::member(
860                Box::new(JsValue::WellKnownObject(WellKnownObjectKind::NodePreGyp)),
861                Box::new(prop),
862            ),
863            true,
864            "unsupported property on @mapbox/node-pre-gyp module",
865        ),
866    }
867}
868
869fn express(prop: JsValue) -> JsValue {
870    match prop.as_str() {
871        Some("set") => JsValue::WellKnownFunction(WellKnownFunctionKind::NodeExpressSet),
872        _ => JsValue::unknown(
873            JsValue::member(
874                Box::new(JsValue::WellKnownObject(
875                    WellKnownObjectKind::NodeExpressApp,
876                )),
877                Box::new(prop),
878            ),
879            true,
880            "unsupported property on require('express')() object",
881        ),
882    }
883}
884
885fn protobuf_loader(prop: JsValue) -> JsValue {
886    match prop.as_str() {
887        Some("load") | Some("loadSync") => {
888            JsValue::WellKnownFunction(WellKnownFunctionKind::NodeProtobufLoad)
889        }
890        _ => JsValue::unknown(
891            JsValue::member(
892                Box::new(JsValue::WellKnownObject(
893                    WellKnownObjectKind::NodeProtobufLoader,
894                )),
895                Box::new(prop),
896            ),
897            true,
898            "unsupported property on require('@grpc/proto-loader') object",
899        ),
900    }
901}