Skip to content

fix(finalizer): unwrap oxc-runtime helper namespace on CJS require#9472

Draft
Kyujenius wants to merge 2 commits into
rolldown:mainfrom
Kyujenius:fix/9263-cjs-class-field-helper-binding
Draft

fix(finalizer): unwrap oxc-runtime helper namespace on CJS require#9472
Kyujenius wants to merge 2 commits into
rolldown:mainfrom
Kyujenius:fix/9263-cjs-class-field-helper-binding

Conversation

@Kyujenius

Copy link
Copy Markdown
Contributor

The oxc transformer lowers class { count = 0 } to _defineProperty(this, "count", 0) under target: es2021 and synthesizes var _defineProperty = require('@oxc-project/runtime/helpers/defineProperty.js'). The helper is a default-only ESM module, but the CJS-wrap path in try_rewrite_global_require_call returned the namespace object - binding _defineProperty to { default: <fn>, __esModule: true } and throwing TypeError: _defineProperty is not a function at init (reproduction).

This mirrors the JSON path (#4984): a new ImportRecordMeta::RuntimeHelper bit is set in the module loader next to JsonModule, and the finalizer appends .default when either bit is present. Scope is intentionally synthesized-helpers only - broadly unwrapping any default-only ESM in CJS callers drifts 8 snapshots including #9261's, so the namespace convention for user-written require stays untouched.

This also matches Rollup's output.interop: "default"; the Rollup test suite (packages/rollup-tests) holds at 1212 passing fixtures with zero regression.

Fixes #9263.

@codspeed-hq

codspeed-hq Bot commented May 20, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 4 untouched benchmarks
⏩ 10 skipped benchmarks1


Comparing Kyujenius:fix/9263-cjs-class-field-helper-binding (9860af2) with main (47a0e3b)2

Open in CodSpeed

Footnotes

  1. 10 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

  2. No successful run was found on main (f6504de) during the generation of this report, so 47a0e3b was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

// Fixes #9263.
if resolved_id.id.as_str().strip_prefix('\0').is_some_and(is_virtual_runtime_helper) {
raw_rec.meta.insert(ImportRecordMeta::RuntimeHelper);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should identify the root cause instead of merely patching the runtime module.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you try to verify external user code will not regress it again?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Synthesized-helpers-only was meant as a deliberate boundary, not a patch - the two call sites carry different contracts. The transformer emits require('@oxc-project/runtime/helpers/...') as a default import expecting the callable, so the namespace round-trip strips it. A user require() of a default-only ESM lives under the esbuild/webpack interop contract instead, which is why #4984 went behind viteMode.

The three new fixtures pin both sides: user code stays namespace, synthesized helpers unwrap to .default, and the multi-helper case shows the boundary is the resolved-id prefix, not a per-helper guard.

@IWANABETHATGUY IWANABETHATGUY marked this pull request as draft May 20, 2026 16:10
@IWANABETHATGUY

Copy link
Copy Markdown
Member

Controls how Rollup handles default, namespace and dynamic imports from external dependencies in formats like CommonJS that do not natively support these concepts.

Apparently, the test case has no relation to Rollup's output.interop, since the option is used to control interop with external modules

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.

[Bug]: Class fields in CJS-classified modules bind _defineProperty to namespace via __toCommonJS, throwing at init

2 participants