Skip to content

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

@ksmth

Description

@ksmth

Reproduction link or steps

Minimal repo: https://github.com/ksmth/rolldown-cjs-class-field-repro

git clone https://github.com/ksmth/rolldown-cjs-class-field-repro
cd rolldown-cjs-class-field-repro
npm install
npm run build
npm run preview   # then open http://localhost:4173

In the browser console:

Uncaught (in promise) TypeError: _defineProperty is not a function
    at new EraParser (assets/index-XXXXXXXX.js:…)

The full repro recipe, three "make it stop" knobs, and a grep snippet to confirm-vs-adjacent-bugs are all in the repo's README.md.

What is expected?

Class fields declared in a CJS-classified source module should still work after Rolldown downlevels them to _defineProperty(this, …). The hoisted _defineProperty binding inside the __commonJSMin wrapper should refer to the helper function, not its module namespace.

What is actually happening?

For every CJS-classified module that uses class fields, Rolldown emits this binding at the top of the __commonJSMin wrapper:

var require_EraParser = /* @__PURE__ */ __commonJSMin(((exports) => {
    var _defineProperty = (init_defineProperty(), __toCommonJS(defineProperty_exports));
    //                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    var EraParser = class extends _Parser.Parser {
        constructor(..._args) {
            super(..._args);
            _defineProperty(this, "priority", 140);   // 💥 TypeError

__toCommonJS(defineProperty_exports) returns the namespace object { __esModule: true, default: <fn> }, not the function — so _defineProperty(this, "priority", 140) throws TypeError: _defineProperty is not a function at module-init time, before any user code runs.

The helper itself is correctly emitted as a top-level function elsewhere in the bundle:

//#region \0@[email protected]/helpers/defineProperty.js
var defineProperty_exports = /* @__PURE__ */ __exportAll({ default: () => _defineProperty });
function _defineProperty(e, r, t) {  }

…and ESM-classified modules call that function directly with no __toCommonJS indirection. The bug is specifically that the local shadow inside __commonJSMin wrappers binds to the namespace instead of to the helper.

In the linked repro this expression shows up 32 times in the unminified dist/assets/index-*.js. You can confirm with:

grep -c '(init_defineProperty(), __toCommonJS(defineProperty_exports))' dist/assets/*.js

Workarounds (any of these makes the bug vanish)

  1. build.target: 'es2022' — class fields are emitted natively, the broken binding is never generated.
  2. A small resolveId plugin that rewrites date-fns/**/*.cjs*.js — keeps the old target, keeps the _defineProperty(this, …) lowering, but no longer goes through CJS classification, so _defineProperty is hoisted as a plain top-level function (no __toCommonJS wrapper) and called directly.
  3. Downgrade to vite@7 (Rollup-based build path).

The repro repo includes config files for each of the first two and verifies all three with grep counts in the README.

Notes / non-duplicates

System Info

  System:
    OS: macOS 26.3.1
    CPU: (12) arm64 Apple M3 Pro
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 22.12.0
    npm: 10.9.0
  Browsers:
    Chrome: 147.0.7727.102
    Firefox: 149.0
    Safari: 26.3.1
  npmPackages:
    vite: 8.0.10
    rolldown: 1.0.0-rc.17 (transitive, pinned by [email protected])
    date-fns: 4.1.0
    @oxc-project/runtime: 0.127.0 (transitive)

Any additional comments?

Hit while bundling a real app whose dep graph reaches date-fns/parse via the require exports condition; the minimal repro pins resolve.conditions: ["require", "node", "default"] to make that condition selection deterministic. The bug is about CJS classification, not specifically about date-fns — any .cjs source with class fields and a sub-ES2022 target should reproduce it.

Metadata

Metadata

Type

Priority

None yet

Effort

None yet

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions