1use crate::host::{with_host, JsObj};
16use fusevm::Value;
17
18pub const METHODS: &[&str] = &[
20 "isBuiltin",
21 "createRequire",
22 "wrap",
23 "syncBuiltinESMExports",
24 "runMain",
25 "findPackageJSON",
26];
27
28pub const MODULE_STATIC_METHODS: &[&str] = METHODS;
31
32const BUILTIN_MODULES: &[&str] = &[
37 "assert",
38 "assert/strict",
39 "async_hooks",
40 "buffer",
41 "child_process",
42 "cluster",
43 "console",
44 "crypto",
45 "dgram",
46 "diagnostics_channel",
47 "dns",
48 "dns/promises",
49 "domain",
50 "events",
51 "fs",
52 "fs/promises",
53 "http",
54 "http2",
55 "https",
56 "inspector",
57 "module",
58 "net",
59 "os",
60 "path",
61 "path/posix",
62 "path/win32",
63 "perf_hooks",
64 "process",
65 "punycode",
66 "querystring",
67 "readline",
68 "repl",
69 "stream",
70 "stream/consumers",
71 "string_decoder",
72 "timers",
73 "timers/promises",
74 "tls",
75 "trace_events",
76 "tty",
77 "url",
78 "util",
79 "util/types",
80 "v8",
81 "vm",
82 "wasi",
83 "worker_threads",
84 "zlib",
85];
86
87const CREATE_REQUIRE_SRC: &str = "(function (p) {\n\
92 if (typeof p !== 'string') { p = String(p); }\n\
93 if (p.indexOf('file://') === 0) { p = require('url').fileURLToPath(p); }\n\
94 var dir = require('path').dirname(p);\n\
95 var req = function (spec) { return __cjs_require(spec, dir); };\n\
96 req.resolve = function (spec) { return __cjs_resolve(spec, dir); };\n\
97 req.cache = {};\n\
98 req.main = undefined;\n\
99 req.extensions = {};\n\
100 return req;\n\
101})";
102
103pub fn call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
105 Some(match method {
106 "isBuiltin" => Ok(is_builtin(args)),
107 "createRequire" => create_require(args),
108 "wrap" => Ok(wrap(args)),
109 "syncBuiltinESMExports" => Ok(Value::Undef),
111 "runMain" => Ok(Value::Undef),
114 "findPackageJSON" => Ok(find_package_json(args)),
115 _ => return None,
116 })
117}
118
119pub fn static_call(method: &str, args: &[Value]) -> Option<Result<Value, String>> {
122 call(method, args)
123}
124
125pub fn constant(name: &str) -> Option<Value> {
128 match name {
129 "builtinModules" => Some(builtin_modules_array()),
130 "Module" => Some(with_host(|h| h.alloc(JsObj::Builtin("Module".into())))),
131 _ => None,
132 }
133}
134
135pub fn static_constant(name: &str) -> Option<Value> {
138 match name {
139 "builtinModules" => Some(builtin_modules_array()),
140 "Module" => Some(with_host(|h| h.alloc(JsObj::Builtin("Module".into())))),
141 _ => None,
142 }
143}
144
145fn is_builtin(args: &[Value]) -> Value {
151 let name = super::arg_str(args, 0);
152 let base = name.strip_prefix("node:").unwrap_or(&name);
153 Value::Bool(crate::stdlib::resolve(base).is_some() || name.starts_with("node:"))
154}
155
156fn create_require(args: &[Value]) -> Result<Value, String> {
158 let factory = run_completion(CREATE_REQUIRE_SRC)?;
159 let p = args.first().cloned().unwrap_or(Value::Undef);
160 crate::host::invoke(&factory, vec![p], None)
161}
162
163fn wrap(args: &[Value]) -> Value {
166 let src = super::arg_str(args, 0);
167 with_host(|h| {
168 h.new_str(format!(
169 "(function (exports, require, module, __filename, __dirname) {{ {src}\n}});"
170 ))
171 })
172}
173
174fn find_package_json(args: &[Value]) -> Value {
179 use std::path::{Path, PathBuf};
180 let strip = |s: String| {
181 s.strip_prefix("file://")
182 .map(|x| x.to_string())
183 .unwrap_or(s)
184 };
185 let spec = strip(super::arg_str(args, 0));
186 let base = if args.len() > 1 {
187 Some(strip(super::arg_str(args, 1)))
188 } else {
189 None
190 };
191 let start: PathBuf = {
192 let p = Path::new(&spec);
193 if p.is_absolute() {
194 p.to_path_buf()
195 } else if let Some(b) = base.as_deref() {
196 let bp = Path::new(b);
197 let bdir = if bp.is_dir() {
198 bp
199 } else {
200 bp.parent().unwrap_or(bp)
201 };
202 bdir.join(&spec)
203 } else {
204 std::env::current_dir().unwrap_or_default().join(&spec)
205 }
206 };
207 let mut dir = if start.is_dir() {
208 Some(start.as_path())
209 } else {
210 start.parent()
211 };
212 while let Some(d) = dir {
213 let cand = d.join("package.json");
214 if cand.is_file() {
215 return with_host(|h| h.new_str(cand.to_string_lossy().to_string()));
216 }
217 dir = d.parent();
218 }
219 Value::Undef
220}
221
222fn builtin_modules_array() -> Value {
224 with_host(|h| {
225 let items: Vec<Value> = BUILTIN_MODULES.iter().map(|s| h.new_str(*s)).collect();
226 h.new_array(items)
227 })
228}
229
230fn run_completion(src: &str) -> Result<Value, String> {
233 let prog = crate::compile_completion(src)?;
234 let chunk = crate::load_merged(prog);
235 crate::host::run_chunk_on(chunk)
236}