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