Skip to main content

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