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