Skip to content

feat: add export replacement capabilities to hooks - #292

Draft
BridgeAR wants to merge 8 commits into
mainfrom
BridgeAR/2026-09-09-iitm-export-capability
Draft

BridgeAR wants to merge 8 commits into
mainfrom
BridgeAR/2026-09-09-iitm-export-capability

Conversation

@BridgeAR

@BridgeAR BridgeAR commented Sep 9, 2026

Copy link
Copy Markdown
Member

Hooks that only mutate nested module values currently force IITM to create and synchronize a local cell for every export. This breaks delayed live bindings such as class Sub extends Late and adds startup work for export-heavy ESM modules.

This adds replaceExports: omitted keeps wildcard legacy behavior, an empty list preserves source live bindings, and named entries limit replacement to those bindings. The empty-list ESM fast path uses native re-exports and avoids per-export wrapper state; on 40 ESM modules with 251 exports it reduced import time 61–64% with sync hooks and 48–51% with async hooks across repeated trimmed trials. CommonJS, builtins, and named replacement sets retain the existing compatibility path.

Refs: #280

Hooks that only mutate nested module values currently force IITM to create and synchronize a local cell for every export. This breaks delayed live bindings such as `class Sub extends Late` and adds startup work for export-heavy ESM modules.

This adds `replaceExports`: omitted keeps wildcard legacy behavior, an empty list preserves source live bindings, and named entries limit replacement to those bindings. The empty-list ESM fast path uses native re-exports and avoids per-export wrapper state; on 40 ESM modules with 251 exports it reduced import time 61–64% with sync hooks and 48–51% with async hooks across repeated trimmed trials. CommonJS, builtins, and named replacement sets retain the existing compatibility path.

Refs: #280
Node can expose CommonJS properties that are absent from a builtin's fixed ESM facade. Derived re-exports then fail during wrapper instantiation.

A distinct wrapper URL keeps native passthrough bindings live without resolving them back to the wrapper.
Read-only hooks lose same-origin star exports when native passthrough bypasses
collision repairs. ESM loaders that omit resolve format also lose live bindings
because the format discovered during load is not retained.

The passthrough path skips the CommonJS module.exports marker on Node.js 23 and
newer when it is absent from the replacement set. Capability matching also
converts synthetic file URLs without a guard, rejecting valid loader modules.

A replacement list with no actual export can use native reexports. For 40 ESM
modules with 251 exports on Node.js 22.23.3 / V8 12.4.254.21-node.57, that path
takes 30.53 ms versus 60.02 ms on main (seven fresh-process trials, trimmed mean).
@BridgeAR
BridgeAR force-pushed the BridgeAR/2026-09-09-iitm-export-capability branch from cb7684a to 9bb5570 Compare October 5, 2026 09:53
Legacy imports check Turbopack aliases even when their ordinary module names
already match. Unfiltered imports also convert file URLs without using the
resulting path.
Installing TypeScript 7 beside typescript-estree breaks native import because
the parser still needs the legacy compiler API. An alias retains the latest
compiler export comparison while the parser gets a supported peer.
Read-only hooks contribute no writable exports, so merging their empty sets
only allocates copies. A warmed merge with three read-only hooks drops from
35.4-43.3 ns to 11.7-13.3 ns of CPU time. Measured on Node 22.23.3 / V8
12.4.254.21-node.57 with 50,000 warmups and 200,000 calls x 7 trials in two
fresh processes, discarding the best and worst trials.

Drive-by fix:

* Remove read-only binder state already encoded by the missing write table.
Synchronous loader registration is a singleton, so capability delivery needs
one callback. Replay must finish before that callback receives future updates.

Drive-by fix:
* Remove diamond re-export coverage duplicated by the shared loader fixture.
Internal callbacks depend on loader selection. A package-name include does not
select the package's file tree, while a matching URL filter can retain those
callbacks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant