1use rquickjs::module::{Declarations, Exports, ModuleDef};
10use rquickjs::{Ctx, Module, Object, Value};
11
12fn declare_all(decl: &Declarations<'_>, names: &[&str]) -> rquickjs::Result<()> {
14 for name in names {
15 decl.declare(*name)?;
16 }
17 Ok(())
18}
19
20fn export_from<'js>(exports: &Exports<'js>, ns: &Object<'js>, names: &[&str]) -> rquickjs::Result<()> {
22 for name in names {
23 exports.export(*name, ns.get::<_, Value<'js>>(*name)?)?;
24 }
25 Ok(())
26}
27
28pub struct PathModule;
31
32const PATH_MEMBERS: &[&str] = &[
33 "join",
34 "resolve",
35 "dirname",
36 "basename",
37 "extname",
38 "normalize",
39 "relative",
40 "isAbsolute",
41 "parse",
42 "format",
43 "toNamespacedPath",
44 "posix",
45 "sep",
46 "delimiter",
47];
48const PATH_EXPORTS: &[&str] = &[
49 "default",
50 "join",
51 "resolve",
52 "dirname",
53 "basename",
54 "extname",
55 "normalize",
56 "relative",
57 "isAbsolute",
58 "parse",
59 "format",
60 "toNamespacedPath",
61 "posix",
62 "sep",
63 "delimiter",
64];
65
66fn path_namespace<'js>(ctx: &Ctx<'js>) -> rquickjs::Result<Object<'js>> {
67 let obj = crate::node::path::path_object(ctx)?;
68 let ns = Object::new(ctx.clone())?;
69 ns.set("default", obj.clone())?;
70 for name in PATH_MEMBERS {
71 ns.set(*name, obj.get::<_, Value<'js>>(*name)?)?;
72 }
73 Ok(ns)
74}
75
76impl ModuleDef for PathModule {
77 fn declare(decl: &Declarations<'_>) -> rquickjs::Result<()> {
78 declare_all(decl, PATH_EXPORTS)
79 }
80
81 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> rquickjs::Result<()> {
82 export_from(exports, &path_namespace(ctx)?, PATH_EXPORTS)
83 }
84}
85
86pub use crate::crypto::CryptoModule;
90
91const CRYPTO_MEMBERS: &[&str] = &[
92 "createHash",
93 "createHmac",
94 "getRandomValues",
95 "randomBytes",
96 "randomFill",
97 "randomFillSync",
98 "randomInt",
99 "randomUUID",
100 "subtle",
101 "webcrypto",
102];
103
104fn crypto_namespace<'js>(ctx: &Ctx<'js>) -> rquickjs::Result<Object<'js>> {
105 let global: Object<'js> = ctx.globals().get("crypto")?;
106 let ns = Object::new(ctx.clone())?;
107 for name in CRYPTO_MEMBERS {
108 if let Ok(value) = global.get::<_, Value<'js>>(*name) {
109 if !value.is_undefined() {
110 ns.set(*name, value)?;
111 }
112 }
113 }
114 ns.set("webcrypto", global)?;
115 Ok(ns)
116}
117
118pub use crate::buffer::BufferModule;
121
122fn buffer_namespace<'js>(ctx: &Ctx<'js>) -> rquickjs::Result<Object<'js>> {
125 let ns = Object::new(ctx.clone())?;
126 for name in ["Buffer", "atob", "btoa"] {
127 if let Ok(value) = ctx.globals().get::<_, Value<'js>>(name) {
128 if !value.is_undefined() {
129 ns.set(name, value)?;
130 }
131 }
132 }
133 let constants = Object::new(ctx.clone())?;
134 constants.set("MAX_LENGTH", u32::MAX)?;
135 constants.set("MAX_STRING_LENGTH", (1_u32 << 30) - 1)?;
136 ns.set("constants", constants)?;
137 Ok(ns)
138}
139
140pub struct OsModule;
143
144const OS_MEMBERS: &[&str] = &[
145 "arch",
146 "availableParallelism",
147 "cpus",
148 "devNull",
149 "endianness",
150 "EOL",
151 "freemem",
152 "getPriority",
153 "homedir",
154 "hostname",
155 "loadavg",
156 "machine",
157 "networkInterfaces",
158 "platform",
159 "release",
160 "setPriority",
161 "tmpdir",
162 "totalmem",
163 "type",
164 "uptime",
165 "userInfo",
166 "version",
167];
168
169fn os_namespace<'js>(ctx: &Ctx<'js>) -> rquickjs::Result<Object<'js>> {
170 crate::os::os_object(ctx)
171}
172
173impl ModuleDef for OsModule {
174 fn declare(decl: &Declarations<'_>) -> rquickjs::Result<()> {
175 decl.declare("default")?;
176 declare_all(decl, OS_MEMBERS)
177 }
178
179 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> rquickjs::Result<()> {
180 let object = os_namespace(ctx)?;
181 export_node_module(ctx, exports, &object, OS_MEMBERS)
182 }
183}
184
185fn export_node_module<'js>(
190 ctx: &Ctx<'js>,
191 exports: &Exports<'js>,
192 object: &Object<'js>,
193 members: &[&str],
194) -> rquickjs::Result<()> {
195 exports.export("default", object.clone())?;
196 for name in members {
197 let value = object
198 .get::<_, Value<'js>>(*name)
199 .unwrap_or_else(|_| Value::new_undefined(ctx.clone()));
200 exports.export(*name, value)?;
201 }
202 Ok(())
203}
204
205pub struct UtilModule;
208
209impl ModuleDef for UtilModule {
210 fn declare(decl: &Declarations<'_>) -> rquickjs::Result<()> {
211 decl.declare("default")?;
212 declare_all(decl, crate::node::util::UTIL_MEMBERS)
213 }
214
215 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> rquickjs::Result<()> {
216 let object = crate::node::util::util_object(ctx)?;
217 export_node_module(ctx, exports, &object, crate::node::util::UTIL_MEMBERS)
218 }
219}
220
221pub struct AssertModule;
223pub struct AssertStrictModule;
225
226impl ModuleDef for AssertModule {
227 fn declare(decl: &Declarations<'_>) -> rquickjs::Result<()> {
228 decl.declare("default")?;
229 declare_all(decl, crate::node::assert::ASSERT_MEMBERS)
230 }
231
232 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> rquickjs::Result<()> {
233 let object = crate::node::assert::assert_object(ctx, false)?;
234 export_node_module(ctx, exports, &object, crate::node::assert::ASSERT_MEMBERS)
235 }
236}
237
238impl ModuleDef for AssertStrictModule {
239 fn declare(decl: &Declarations<'_>) -> rquickjs::Result<()> {
240 decl.declare("default")?;
241 declare_all(decl, crate::node::assert::ASSERT_MEMBERS)
242 }
243
244 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> rquickjs::Result<()> {
245 let object = crate::node::assert::assert_object(ctx, true)?;
246 export_node_module(ctx, exports, &object, crate::node::assert::ASSERT_MEMBERS)
247 }
248}
249
250pub use crate::url::UrlModule;
253
254fn url_namespace<'js>(ctx: &Ctx<'js>) -> rquickjs::Result<Object<'js>> {
258 use rquickjs::function::Func;
259
260 let ns = Object::new(ctx.clone())?;
261 for name in ["URL", "URLSearchParams"] {
262 ns.set(name, ctx.globals().get::<_, Value<'js>>(name)?)?;
263 }
264 ns.set(
265 "domainToASCII",
266 Func::from(|domain: String| crate::url::domain_to_ascii(&domain)),
267 )?;
268 ns.set(
269 "domainToUnicode",
270 Func::from(|domain: String| crate::url::domain_to_unicode(&domain)),
271 )?;
272 ns.set("fileURLToPath", Func::from(crate::url::file_url_to_path))?;
273 ns.set("pathToFileURL", Func::from(crate::url::path_to_file_url))?;
274 ns.set("format", Func::from(crate::url::url_format))?;
275 ns.set(
276 "urlToHttpOptions",
277 Func::from(crate::url::url_class::url_to_http_options),
278 )?;
279 Ok(ns)
280}
281
282pub struct ProcessModule;
284
285impl ModuleDef for ProcessModule {
286 fn declare(decl: &Declarations<'_>) -> rquickjs::Result<()> {
287 decl.declare("default")?;
288 declare_all(decl, crate::node::process::PROCESS_MEMBERS)
289 }
290
291 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> rquickjs::Result<()> {
292 let object = crate::node::process::process_object(ctx)?;
293 export_node_module(ctx, exports, &object, crate::node::process::PROCESS_MEMBERS)
294 }
295}
296
297pub struct TimersModule;
299pub struct TimersPromisesModule;
301
302impl ModuleDef for TimersModule {
303 fn declare(decl: &Declarations<'_>) -> rquickjs::Result<()> {
304 decl.declare("default")?;
305 declare_all(decl, crate::node::timers::TIMERS_MEMBERS)
306 }
307
308 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> rquickjs::Result<()> {
309 let object = crate::node::timers::timers_object(ctx)?;
310 export_node_module(ctx, exports, &object, crate::node::timers::TIMERS_MEMBERS)
311 }
312}
313
314impl ModuleDef for TimersPromisesModule {
315 fn declare(decl: &Declarations<'_>) -> rquickjs::Result<()> {
316 decl.declare("default")?;
317 declare_all(decl, crate::node::timers::TIMERS_PROMISES_MEMBERS)
318 }
319
320 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> rquickjs::Result<()> {
321 let object = crate::node::timers::timers_promises_object(ctx)?;
322 export_node_module(
323 ctx,
324 exports,
325 &object,
326 crate::node::timers::TIMERS_PROMISES_MEMBERS,
327 )
328 }
329}
330
331pub struct EventsModule;
334
335const EVENT_EMITTER_KEY: &str = "ferrijs.node.events.EventEmitter";
339
340fn events_namespace<'js>(ctx: &Ctx<'js>) -> rquickjs::Result<Object<'js>> {
341 use crate::events::{Emitter as _, EventEmitter};
342
343 let symbol: Object<'js> = ctx.globals().get("Symbol")?;
344 let symbol_for: rquickjs::Function<'js> = symbol.get("for")?;
345 let key: Value<'js> = symbol_for.call((EVENT_EMITTER_KEY,))?;
346
347 if let Ok(existing) = ctx.globals().get::<_, Object<'js>>(key.clone()) {
352 return Ok(existing);
353 }
354
355 let ctor = rquickjs::Class::<EventEmitter<'js>>::create_constructor(ctx)?
356 .ok_or_else(|| rquickjs::Error::new_loading("events"))?;
357 EventEmitter::add_event_emitter_prototype(ctx)?;
358 let ctor = ctor
359 .as_object()
360 .cloned()
361 .ok_or_else(|| rquickjs::Error::new_loading("events"))?;
362
363 ctor.set("EventEmitter", ctor.clone())?;
366 ctx.globals().set(key, ctor.clone())?;
367 Ok(ctor)
368}
369
370impl ModuleDef for EventsModule {
371 fn declare(decl: &Declarations<'_>) -> rquickjs::Result<()> {
372 decl.declare("default")?;
373 decl.declare("EventEmitter")?;
374 Ok(())
375 }
376
377 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> rquickjs::Result<()> {
378 let object = events_namespace(ctx)?;
379 export_node_module(ctx, exports, &object, &["EventEmitter"])
380 }
381}
382
383pub type DeclareFn = for<'js> fn(Ctx<'js>, Vec<u8>) -> rquickjs::Result<Module<'js>>;
385
386pub type NamespaceFn = for<'js> fn(&Ctx<'js>) -> rquickjs::Result<Object<'js>>;
388
389pub struct NodeModule {
391 pub specifiers: &'static [&'static str],
392 pub declare: DeclareFn,
393 pub namespace: NamespaceFn,
394}
395
396fn declare_fn<D: ModuleDef>() -> DeclareFn {
397 |ctx, name| Module::declare_def::<D, _>(ctx, name)
398}
399
400#[must_use]
404pub fn modules() -> Vec<NodeModule> {
405 vec![
406 NodeModule {
407 specifiers: &["path", "node:path"],
408 declare: declare_fn::<PathModule>(),
409 namespace: path_namespace,
410 },
411 NodeModule {
412 specifiers: &["buffer", "node:buffer"],
413 declare: declare_fn::<crate::buffer::BufferModule>(),
414 namespace: buffer_namespace,
415 },
416 NodeModule {
417 specifiers: &["fs", "node:fs"],
418 declare: declare_fn::<crate::fs::FsModule>(),
419 namespace: |ctx| crate::fs::fs_object(ctx),
420 },
421 NodeModule {
422 specifiers: &["fs/promises", "node:fs/promises"],
423 declare: declare_fn::<crate::fs::FsPromisesModule>(),
424 namespace: |ctx| crate::fs::fs_promises_object(ctx),
425 },
426 NodeModule {
427 specifiers: &["os", "node:os"],
428 declare: declare_fn::<OsModule>(),
429 namespace: os_namespace,
430 },
431 NodeModule {
432 specifiers: &["util", "node:util"],
433 declare: declare_fn::<UtilModule>(),
434 namespace: |ctx| crate::node::util::util_object(ctx),
435 },
436 NodeModule {
437 specifiers: &["events", "node:events"],
438 declare: declare_fn::<EventsModule>(),
439 namespace: events_namespace,
440 },
441 NodeModule {
442 specifiers: &["assert", "node:assert"],
443 declare: declare_fn::<AssertModule>(),
444 namespace: |ctx| crate::node::assert::assert_object(ctx, false),
445 },
446 NodeModule {
447 specifiers: &["assert/strict", "node:assert/strict"],
448 declare: declare_fn::<AssertStrictModule>(),
449 namespace: |ctx| crate::node::assert::assert_object(ctx, true),
450 },
451 NodeModule {
452 specifiers: &["url", "node:url"],
453 declare: declare_fn::<UrlModule>(),
454 namespace: url_namespace,
455 },
456 NodeModule {
457 specifiers: &["process", "node:process"],
458 declare: declare_fn::<ProcessModule>(),
459 namespace: |ctx| crate::node::process::process_object(ctx),
460 },
461 NodeModule {
462 specifiers: &["timers", "node:timers"],
463 declare: declare_fn::<TimersModule>(),
464 namespace: |ctx| crate::node::timers::timers_object(ctx),
465 },
466 NodeModule {
467 specifiers: &["timers/promises", "node:timers/promises"],
468 declare: declare_fn::<TimersPromisesModule>(),
469 namespace: |ctx| crate::node::timers::timers_promises_object(ctx),
470 },
471 NodeModule {
472 specifiers: &["crypto", "node:crypto"],
473 declare: declare_fn::<crate::crypto::CryptoModule>(),
474 namespace: crypto_namespace,
475 },
476 NodeModule {
477 specifiers: &["zlib", "node:zlib"],
478 declare: declare_fn::<crate::zlib::ZlibModule>(),
479 namespace: zlib_namespace,
480 },
481 NodeModule {
482 specifiers: &["string_decoder", "node:string_decoder"],
483 declare: declare_fn::<crate::string_decoder::StringDecoderModule>(),
484 namespace: string_decoder_namespace,
485 },
486 NodeModule {
487 specifiers: &["perf_hooks", "node:perf_hooks"],
488 declare: declare_fn::<crate::perf_hooks::PerfHooksModule>(),
489 namespace: perf_hooks_namespace,
490 },
491 NodeModule {
492 specifiers: &["tty", "node:tty"],
493 declare: declare_fn::<crate::tty::TtyModule>(),
494 namespace: tty_namespace,
495 },
496 NodeModule {
501 specifiers: &["stream/web", "node:stream/web"],
502 declare: declare_fn::<StreamWebModule>(),
503 namespace: stream_web_namespace,
504 },
505 ]
506}
507
508const ZLIB_MEMBERS: &[&str] = &[
511 "deflate",
512 "deflateSync",
513 "deflateRaw",
514 "deflateRawSync",
515 "gzip",
516 "gzipSync",
517 "inflate",
518 "inflateSync",
519 "inflateRaw",
520 "inflateRawSync",
521 "gunzip",
522 "gunzipSync",
523 "brotliCompress",
524 "brotliCompressSync",
525 "brotliDecompress",
526 "brotliDecompressSync",
527 "zstdCompress",
528 "zstdCompressSync",
529 "zstdDecompress",
530 "zstdDecompressSync",
531];
532
533fn zlib_namespace<'js>(ctx: &Ctx<'js>) -> rquickjs::Result<Object<'js>> {
534 module_default_object::<crate::zlib::ZlibModule>(ctx, "zlib", ZLIB_MEMBERS)
535}
536
537fn string_decoder_namespace<'js>(ctx: &Ctx<'js>) -> rquickjs::Result<Object<'js>> {
538 module_default_object::<crate::string_decoder::StringDecoderModule>(ctx, "string_decoder", &["StringDecoder"])
539}
540
541fn perf_hooks_namespace<'js>(ctx: &Ctx<'js>) -> rquickjs::Result<Object<'js>> {
542 module_default_object::<crate::perf_hooks::PerfHooksModule>(ctx, "perf_hooks", &["performance"])
543}
544
545fn tty_namespace<'js>(ctx: &Ctx<'js>) -> rquickjs::Result<Object<'js>> {
546 module_default_object::<crate::tty::TtyModule>(ctx, "tty", &["isatty"])
547}
548
549const STREAM_WEB_MEMBERS: &[&str] = &[
553 "ReadableStream",
554 "ReadableStreamDefaultReader",
555 "ReadableStreamBYOBReader",
556 "ReadableStreamDefaultController",
557 "ReadableByteStreamController",
558 "ReadableStreamBYOBRequest",
559 "WritableStream",
560 "WritableStreamDefaultWriter",
561 "WritableStreamDefaultController",
562 "TransformStream",
563 "TransformStreamDefaultController",
564 "ByteLengthQueuingStrategy",
565 "CountQueuingStrategy",
566];
567
568pub struct StreamWebModule;
569
570impl ModuleDef for StreamWebModule {
571 fn declare(decl: &Declarations<'_>) -> rquickjs::Result<()> {
572 declare_all(decl, STREAM_WEB_MEMBERS)?;
573 decl.declare("default")?;
574 Ok(())
575 }
576
577 fn evaluate<'js>(ctx: &Ctx<'js>, exports: &Exports<'js>) -> rquickjs::Result<()> {
578 let ns = stream_web_namespace(ctx)?;
579 export_from(exports, &ns, STREAM_WEB_MEMBERS)?;
580 exports.export("default", ns)?;
581 Ok(())
582 }
583}
584
585fn stream_web_namespace<'js>(ctx: &Ctx<'js>) -> rquickjs::Result<Object<'js>> {
586 let globals = ctx.globals();
587 let ns = Object::new(ctx.clone())?;
588 for name in STREAM_WEB_MEMBERS {
589 if let Ok(value) = globals.get::<_, Value<'js>>(*name) {
590 if !value.is_undefined() {
591 ns.set(*name, value)?;
592 }
593 }
594 }
595 Ok(ns)
596}
597
598fn module_default_object<'js, D: ModuleDef>(
607 ctx: &Ctx<'js>,
608 name: &str,
609 members: &[&str],
610) -> rquickjs::Result<Object<'js>> {
611 let (module, _promise) = Module::evaluate_def::<D, _>(ctx.clone(), name)?;
615 let namespace = module.namespace()?;
616 if let Ok(default) = namespace.get::<_, Object<'js>>("default") {
617 return Ok(default);
618 }
619 let ns = Object::new(ctx.clone())?;
620 for member in members {
621 if let Ok(value) = namespace.get::<_, Value<'js>>(*member) {
622 ns.set(*member, value)?;
623 }
624 }
625 Ok(ns)
626}