feat: add portable bundler wrapper API - #270
Draft
BridgeAR wants to merge 18 commits into
Draft
Conversation
BridgeAR
added a commit
that referenced
this pull request
Aug 4, 2026
Consumers currently need separate RITM and bundler-specific paths for CommonJS, which duplicates matching, metadata, and replacement behavior. 1. Make synchronous hooks opt into CommonJS and route both formats through the shared Hook registry. 2. Make bundler wrappers format-aware, preserve opaque adapter targets, and carry JSON metadata into runtime hooks. 3. Verify the contracts with native sync hooks, real esbuild and webpack bundles, and nft tracing. Refs: #270 Refs: DataDog/dd-trace-js#9383
Bundler plugins need to own virtual module IDs and resolution. Returning import metadata separately keeps canonical module URLs out of generated import specifiers without duplicating IITM's export-binding logic. Refs: DataDog/dd-trace-js#9383
Consumers currently need separate RITM and bundler-specific paths for CommonJS, which duplicates matching, metadata, and replacement behavior. 1. Make synchronous hooks opt into CommonJS and route both formats through the shared Hook registry. 2. Make bundler wrappers format-aware, preserve opaque adapter targets, and carry JSON metadata into runtime hooks. 3. Verify the contracts with native sync hooks, real esbuild and webpack bundles, and nft tracing. Refs: #270 Refs: DataDog/dd-trace-js#9383
- Keep CommonJS and bundler metadata on opt-in IITM paths. - Remove bundler-specific dependencies, fixtures, and license churn. Existing ESM wrapper registration and callback paths must retain their hot-path behavior. The standalone register-hooks TypeScript test also bypassed the TypeScript loader on Node 18 and stopped the CI matrix. - npm test - npm run test:ts - npm run test:e2e - npm run lint
## Summary Expose the loader-known ESM or CommonJS format as the optional fifth Hook callback argument. ## Why Consumers that support both formats cannot safely infer namespace semantics from user exports. ## Test plan - npm test - npm run test:ts - npm run test:e2e - npm run lint
Emit valid identifier export names without string-literal syntax while preserving quoted names that require it. Webpack 5.54 accepts string-literal export names in the parser but crashes while analyzing the generated module. Most package exports are ordinary identifiers and do not need the newer syntax. - npm test - npm run test:ts - npm run test:e2e - npm run lint - webpack 5.54.0 ESM integration in dd-trace-js - webpack 5.109.2 ESM integration in dd-trace-js
Bundler integrations need synchronous format detection before generating a wrapper, but that behavior was only available inside the ESM loader. The facade keeps format detection synchronous without eagerly loading the wrapper parser.
## Summary Report package-relative names for CommonJS modules registered through extended hooks. ## Why CommonJS hooks historically receive package internals without opting into ESM internal interception. Bundler and synchronous loader wrappers need the same contract or file-specific instrumentation such as express/lib/express.js is skipped. ## Test plan - npm test - npm run test:ts - npm run lint
## Summary Keep require, module, exports, __filename, and __dirname bound to the bundler factory when createWrapperModule emits a CommonJS wrapper. ## Why Shadowing require in the generated factory prevents bundlers from resolving relative dependencies. The synchronous Node loader still uses explicit CommonJS parameters, while bundler wrappers preserve their outer bindings. ## Test plan - npm test - npm run test:ts - npm run lint
## Summary Inject the filesystem reader into shared module-format detection instead of requiring it from the CommonJS helper. ## Why Node.js 21 routes a CommonJS builtin require from an active ESM loader through resolveSync, but its loader worker does not implement that method. Importing fs in the ESM caller and requiring it in the CommonJS facade keeps each path on its native module system. ## Test plan - npm test - npm run lint - Node.js 21.7.3 npm test - Node.js 21.7.3 test/other/double-loading.mjs
Key extended hooks by their existing loader hook instead of tracking a parallel array and WeakMap. The registries must be removed together. One key makes that invariant explicit and avoids extra per-Hook bookkeeping without changing legacy ESM dispatch. - npm test - npm run test:ts - npm run lint
Bundler wrappers copy every ESM export into a patchable local binding, so later source assignments stop reaching consumers of mutable exports. Selected exports retain their defining bindings while all other exports remain patchable through Hook.
BridgeAR
force-pushed
the
BridgeAR/2026-07-15-add-bundler-api
branch
from
September 8, 2026 19:32
5c02a65 to
e37bb9d
Compare
An empty source string was treated as absent, so virtual modules fell through to a file URL read. The CommonJS wrapper also invoked its nested factory without forwarding the outer arguments.
Windows package paths, load-time module formats, and repeated evaluations could bypass hooks, import a wrapper recursively, or replay stale registrations.
Bundler-generated code can execute in a separate module graph without CommonJS filename globals. Some loaders also expose the module format only during load. These paths could miss active hooks, retain failed registrations, or misread valid adapter values.
Bundler adapters need the original source location to compose source maps without parsing generated wrapper code.
Bundlers need IITM package identity, format detection, and wrapper semantics without copying loader internals. Typeless CommonJS detection preserves module.exports identity before wrapper selection. Bundler metadata lookups stay fresh across watch rebuilds. Loader format detection keeps its process cache to avoid repeated synchronous package reads.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bundler plugins currently have to duplicate IITM's wrapper generation or inject resolved URLs into generated ESM. This adds
createWrapperModule(), which returns relative import placeholders and a typed resolution/watch manifest while sharing export-binding generation with the Node loader. Each call parses caller-provided source independently so rebuilds see changed exports.Why
The runtime manifest entry stays non-external. Bundling it keeps generated wrappers and
Hookon the same registry.Refs: DataDog/dd-trace-js#9383