Skip to main content

adding-builtin-module

Scaffolds and registers a new Node.js-compatible built-in module in the skeleton. Use when asked to add a new node:X module, implement a new built-in, or create a new native+JS module pair.

Source facts

Repository
golemcloud/wasm-rquickjs
Last source activity
March 19, 2026 at 20:09
Detected SKILL.md language
English
Stars
23
Forks
6

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
adding-builtin-module
description
Scaffolds and registers a new Node.js-compatible built-in module in the skeleton. Use when asked to add a new node:X module, implement a new built-in, or create a new native+JS module pair.
# Adding a New Built-in Module Step-by-step guide for adding a new Node.js-compatible built-in module to the skeleton. ## Architecture Built-in modules use a **hybrid native+JS pattern**: - **Rust native bridge**: Low-level operations via `#[rquickjs::module]`, registered as `__wasm_rquickjs_builtin/<name>_native` - **JS wrapper**: High-level Node.js-compatible API surface, imports from the native bridge ## Checklist ### 1. Create the Rust native module File: `crates/wasm-rquickjs/skeleton/src/builtin/<name>.rs` ```rust use rquickjs::module::ModuleDef; use rquickjs::prelude::*; pub const <NAME>_JS: &str = include_str!("<name>.js"); pub const REEXPORT_JS: &str = r#"export { default } from 'node:<name>'; export * from 'node:<name>';"#; pub const WIRE_JS: &str = ""; // Only if globals need wiring #[rquickjs::module] mod native_module { // Export functions here #[rquickjs::function] pub fn example_function() -> String { "hello".to_string() } } pub use native_module::js_native_module; ``` ### 2. Create the JS wrapper File: `crates/wasm-rquickjs/skeleton/src/builtin/<name>.js` ```javascript import { example_function } from '__wasm_rquickjs_builtin/<name>_native'; // NOTE: Always use original Rust snake_case names in imports! // Implement Node.js-compatible API surface const result = example_function(); export { result }; export default { result }; ``` ### 3. Add dependencies (if needed) Edit `crates/wasm-rquickjs/skeleton/Cargo.toml_`: - Always use `default-features = false` for crates that may pull in C/native libraries - Use pure-Rust backends (e.g., `rust_backend` feature) for `wasm32-wasip2` compatibility ### 4. Register in `mod.rs` File: `crates/wasm-rquickjs/skeleton/src/builtin/mod.rs` Four places need updating: **a) Add the module declaration** (top of file): ```rust mod <name>; ``` **b) Add to `add_module_resolvers`** — register the internal native path and public names: ```rust .with_module("__wasm_rquickjs_builtin/<name>_native") .with_module("node:<name>") .with_module("<name>") ``` **c) Add to `module_loader` (ModuleLoader)** — map native path to Rust module: ```rust .with_module("__wasm_rquickjs_builtin/<name>_native", <name>::js_native_module) ``` **d) Add to `module_loader` (BuiltinLoader)** — map public names to JS constants: ```rust .with_module("node:<name>", <name>::<NAME>_JS) .with_module("<name>", <name>::REEXPORT_JS) ``` **e) (Optional) Add to `wire_builtins`** — if the module needs to attach globals: ```rust writeln!(result, "{}", <name>::WIRE_JS).unwrap(); ``` ### 5. Update README.md Add the new module to the supported APIs list in `README.md`. ## Critical Gotchas ### `#[rquickjs::module(rename = ...)]` does NOT affect JS imports The `rename` attribute (e.g., `rename = "camelCase"`) only affects Rust-side naming. JavaScript `import` statements must always use the **original Rust `snake_case` names**. Importing the camelCase version causes "Could not find export" errors. ### `u32` return value truncation `rquickjs` may deliver `u32` as signed i32 to JavaScript (e.g., `0xFFFFFFFF` → `-1`). In the JS wrapper, apply `>>> 0`: ```javascript const result = native_fn() >>> 0; ``` ### CJS default export immutability Use `Object.defineProperty` with `writable: false, configurable: false` on default export objects to emulate Node.js behavior where module constants are immutable. ### WASM-compatible dependencies Always use `default-features = false` for crates that may pull in C libraries. Use pure-Rust backends to ensure `wasm32-wasip2` target compatibility. ### No platform-conditional code The skeleton is **always compiled to `wasm32-wasip1`**. Never write conditional code that checks for unix/windows/macOS or any other host platform (e.g., `#[cfg(unix)]`, `#[cfg(windows)]`, `#[cfg(target_os = "...")]`, `process.platform === "win32"`, `path.sep === "\\"`, etc.). Such checks are meaningless in the WASM target and add dead code complexity. ### node:http transport rule **Never use a loopback transport for `node:http`.** Every `node:http` client request MUST go through `wasi:http` (the native Rust `NodeHttpClientRequest`). Do NOT add any fallback that bypasses `wasi:http` by creating direct `node:net` socket connections for loopback/localhost addresses. ## Testing 1. Define an example in `examples/runtime/` (JS file + WIT interface pair) 2. Add tests in `tests/runtime.rs` that use the example 3. Run: `cargo test --test runtime <module> -- --nocapture` If also adding node_compat coverage, use the `fixing-node-compat-test` skill for that workflow.
View on GitHub