Skip to content

New module registry fixes#6423

Open
jasnell wants to merge 6 commits intomainfrom
jasnell/new-module-registry-fixes
Open

New module registry fixes#6423
jasnell wants to merge 6 commits intomainfrom
jasnell/new-module-registry-fixes

Conversation

@jasnell
Copy link
Copy Markdown
Collaborator

@jasnell jasnell commented Mar 26, 2026

New module registry: evaluation correctness, require() semantics, and import attributes

Fixes several correctness issues in the new module registry's evaluation, error propagation, and resolution paths. Adds require() return value semantics matching Node.js, and implements support for import attributes (with { type: 'json' }).

Synthetic module evaluation correctness

SyntheticModule::evaluate() previously called actuallyEvaluate() directly, bypassing V8's module status machine. This meant synthetic modules never transitioned to kEvaluated or kErrored — they stayed at kLinked forever. This caused:

  • Errored modules being re-evaluated on every subsequent import instead of returning the cached error
  • require() status checks (kEvaluated, kErrored) never matching for synthetic modules
  • The EvaluateOnce guard in CJS handlers being necessary as a workaround (and breaking cross-isolate sharing — the second isolate to evaluate got empty exports)

The fix: evaluate() now calls module->Evaluate() to enter V8's status machine. V8 calls back into evaluationSteps, which calls actuallyEvaluate() directly (avoiding reentry). V8 manages all status transitions. EvaluateOnce is removed.

Error propagation for errored dependencies

V8's InnerModuleEvaluation skips kErrored checks for SyntheticModule dependencies and skips recursive evaluation for modules at kEvaluating. Two new checks in resolveModuleCallback address this:

  • kErrored: If a resolved module is already errored (e.g., a JSON module that failed to parse), the cached exception is rethrown during instantiation instead of letting V8 link against stale export cells.
  • kEvaluating: If a resolved module is mid-evaluation (ESM → CJS → require(ESM) → static import back to CJS), a "Circular dependency" error is thrown instead of hitting a V8 CHECK crash.

Both checks are also applied on the process redirect path for consistency.

Defensive ensureInstantiated error handling

Both EsModule::evaluate() and SyntheticModule::evaluate() now schedule a ThrowError when ensureInstantiated fails and no exception is already pending. Previously, returning an empty MaybeLocal without a pending exception could cause V8 to read garbage from the exception slot.

require() return value semantics (UNWRAP_DEFAULT)

Added UNWRAP_DEFAULT require option (using WD_STRONG_BOOL(UnwrapDefault)) that controls what require() returns:

  • User bundle ESM: returns the module namespace, matching Node.js require(esm). If the module exports __cjsUnwrapDefault = true (esbuild/bundler convention), returns the default export instead.
  • Builtin ESM (node:assert, node:buffer, etc.): returns the default export, because workerd builtins are ESM that wrap CJS-style APIs in a default export.
  • Synthetic modules (CJS, JSON, Text, Data, WASM): returns the default export.
    createRequire and CommonJsModuleContext::require both use this option.

Import attributes

Replaced the blanket rejection of all import attributes with parsing and validation:

  • with { type: 'json' } — validates the resolved module has ContentType::JSON. Throws TypeError on mismatch.
  • with { type: 'text' } — recognized but rejected with "not yet supported" (pending TC39 Stage 4).
  • with { type: 'bytes' } — recognized but rejected with "not yet supported" (pending immutable ArrayBuffer).
  • Unknown attribute keys — rejected with TypeError.

Added Module::ContentType enum (NONE, JSON, TEXT, DATA, WASM) threaded through module construction. All data/text/json/wasm modules are tagged at registration time in worker-modules.h.

Source phase import error handling

  • Fixed resolveModuleCallback source phase path to propagate pending exceptions from WASM compilation failures instead of masking them with a generic "Source phase import not available" message.
  • Fixed dynamicResolve source phase path: replaced KJ_FAIL_REQUIRE(v8::Exception::SyntaxError, ...) (which produced garbled error messages) with proper js.throwException.

Reference documentation

  • Updated docs/reference/detail/new-module-registry.md with evaluation flow changes, UNWRAP_DEFAULT semantics, import attributes, and ContentType documentation.
  • Added cross-references between all three reference documents.

Tests

  • Circular dependency: ESM → CJS → require(ESM) → static import CJS (prevents V8 CHECK crash)
  • UNWRAP_DEFAULT: bundle ESM returns namespace, __cjsUnwrapDefault returns default, JSON/Text return default
  • Import attributes: type:json succeeds for JSON, fails for non-JSON, unsupported attributes rejected
  • Updated integration test error messages for new attribute handling

@jasnell jasnell requested review from guybedford and npaun March 26, 2026 05:49
@jasnell jasnell requested review from a team as code owners March 26, 2026 05:49
ask-bonk[bot]

This comment was marked as resolved.

@ask-bonk

This comment was marked as resolved.

@jasnell jasnell marked this pull request as draft March 26, 2026 17:21
@jasnell jasnell force-pushed the jasnell/new-module-registry-fixes branch from 122a87e to 861020c Compare March 26, 2026 19:14
@jasnell jasnell marked this pull request as ready for review March 26, 2026 19:37
@jasnell jasnell force-pushed the jasnell/new-module-registry-fixes branch from 9dfd653 to 457e3e1 Compare March 26, 2026 23:46
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 26, 2026

The generated output of @cloudflare/workers-types matches the snapshot in types/generated-snapshot 🎉

@jasnell jasnell force-pushed the jasnell/new-module-registry-fixes branch from 457e3e1 to 19e3f04 Compare March 27, 2026 00:09
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