fix(ext/node): add linkRequests/moduleRequests/instantiate to node:vm (#34131)
## Summary
Implements the newer Node.js `vm.Module` API on `SourceTextModule` for
two-phase linking + instantiation, alongside the existing
`link(linker)` callback flow:
- `linkRequests(modules)` — synchronously attaches resolved modules to a
`SourceTextModule`. Validates length against `moduleRequests` and
detects cache-key collisions between requests sharing the same
`(specifier, attributes)` pair (`ERR_MODULE_LINK_MISMATCH`).
- `instantiate()` — separate, synchronous V8 instantiation step. Throws
`ERR_VM_MODULE_LINK_FAILURE` when linking was skipped or a transitive
dependency was never linked.
- `moduleRequests` — frozen array of `{ __proto__: null, specifier,
attributes, phase }` entries, matching Node's shape.
- `dependencySpecifiers` — frozen list of request specifiers
(deprecated alias).
The V8 resolve callback now reports the unlinked referrer's identifier
in the error message (`request for 'X' can not be resolved on module
'ID' that is not linked`) and attaches the `ERR_VM_MODULE_LINK_FAILURE`
code, so errors thrown from `instantiate()` round-trip cleanly to JS.
`op_vm_module_link` now stores wrapper objects rather than raw
`v8::Module`s so `op_vm_module_instantiate` can walk the link graph,
populate per-referrer resolutions for V8's resolve callback, and surface
descriptive errors for missing transitive links.
Adds five Node compat tests to `config.jsonc`:
- `parallel/test-vm-module-instantiate.js`
- `parallel/test-vm-module-linkmodulerequests.js`
- `parallel/test-vm-module-linkmodulerequests-circular.js`
- `parallel/test-vm-module-linkmodulerequests-deep.js`
- `parallel/test-vm-module-modulerequests.js`
Closes denoland/orchid#109
## Test plan
- [x] `cargo test --test node_compat -- test-vm-module-instantiate`
passes
- [x] `cargo test --test node_compat -- test-vm-module-modulerequests`
passes
- [x] `cargo test --test node_compat --
test-vm-module-linkmodulerequests` passes (3 tests)
- [x] Pre-existing `test-vm-module-after-evaluate.js` still passes
(legacy `link(linker)` path)
- [x] `./tools/format.js && ./tools/lint.js` clean
Co-authored-by: divybot <divybot@users.noreply.github.com>
Co-authored-by: Divy Srivastava <me@littledivy.com> E
em committed
f3fe4ab470ccb2980645b2c8f0453592447ee25e
Parent: 3a6c894
Committed by GitHub <noreply@github.com>
on 5/16/2026, 6:24:05 PM