Skip to content

minifier: Object.defineProperty(exports, "name", ...) key emitted as a template literal breaks cjs-module-lexer #22342

@Dunqing

Description

@Dunqing

The whitespace task on monitor-oxc fails because the minifier rewrites the property-name argument of Object.defineProperty(exports, "name", ...) into a backtick template literal. cjs-module-lexer (which Node uses to figure out CJS named exports for ESM interop) only matches string literals, so those exports silently drop out of the detected set and downstream ESM consumers fail with Named export 'X' not found.

Input

"use strict";
Object.defineProperty(exports, "getInclusionReasons", {
  enumerable: true,
  get: function () { return _debug.getInclusionReasons; }
});
exports.default = getTargets;
exports.isBrowsersQueryValid = isBrowsersQueryValid;

Output (Codegen with minify: true)

"use strict";Object.defineProperty(exports,`getInclusionReasons`,{enumerable:true,get:function(){return _debug.getInclusionReasons}});exports.default=getTargets;exports.isBrowsersQueryValid=isBrowsersQueryValid;

Expected

The property-name string in Object.defineProperty(...) should stay a plain string literal (e.g. "getInclusionReasons"), not a template literal. esbuild and terser both keep these as plain strings.

Impact

Running cjs-module-lexer against the full minified @babel/helper-compilation-targets/lib/index.js:

exports detected
original 9 (__esModule, TargetNames, default, filterItems, getInclusionReasons, isBrowsersQueryValid, isRequired, prettifyTargets, unreleasedLabels)
after minify 2 (default, isBrowsersQueryValid)

The two survivors are the only exports that use exports.x = ... direct assignment. The other seven all use Object.defineProperty(exports, "<name>", ...) and now have backtick keys.

An ESM consumer doing

import { getInclusionReasons } from "@babel/helper-compilation-targets";

then fails at instantiation:

SyntaxError: Named export 'getInclusionReasons' not found. The requested module
'@babel/helper-compilation-targets' is a CommonJS module, which may not support
all module.exports as named exports.

Where

crates/oxc_codegen/src/str.rs, calculate_quote_maybe_backtick. When all three quote costs tie — which is the case for plain ASCII strings with no ", ', `, ${, or \n — backtick wins the tie-break. For property names like "getInclusionReasons" everything ties at zero, so the output ends up as a template literal even though the byte count is identical to a double-quoted string.

Failing job

https://github.com/oxc-project/monitor-oxc/actions/runs/25673585724/job/75365517803 — the whitespace task fails on @babel/helper-define-polyfill-provider/esm/index.node.mjs doing import { getInclusionReasons, ... } from "@babel/helper-compilation-targets" after the dependency has been put through Codegen::minify().

Metadata

Metadata

Assignees

Labels

Type

No type

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions