turbopack_ecmascript/analyzer/
well_known.rs

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