Skip to content

fix(stub): handle arbitrary module namespace identifier names (#533)#563

Open
guoyangzhen wants to merge 1 commit intounjs:mainfrom
guoyangzhen:fix/stub-namespace-identifiers
Open

fix(stub): handle arbitrary module namespace identifier names (#533)#563
guoyangzhen wants to merge 1 commit intounjs:mainfrom
guoyangzhen:fix/stub-namespace-identifiers

Conversation

@guoyangzhen
Copy link
Copy Markdown

@guoyangzhen guoyangzhen commented Mar 18, 2026

Fixes #533

Problem

Modules using arbitrary module namespace identifiers (e.g. export const 'module.exports') generated invalid stub files:

// Before (invalid JS)
export const module.exports = _module.module.exports;

Fix

Check if the export name is a valid JS identifier. If not, use bracket notation with JSON.stringify() for proper escaping:

// After
export const "module.exports" = _module["module.exports"];

This supports the require(esm) feature where modules export 'module.exports' as an arbitrary identifier name.

Summary by CodeRabbit

  • Bug Fixes
    • Fixed export name generation in code stubs to properly handle non-standard identifiers using bracket notation and quotation as needed.

Modules using arbitrary module namespace identifiers (e.g. export
const 'module.exports') generated invalid stub files like:
  export const module.exports = _module.module.exports;

Now uses bracket notation for non-standard identifiers:
  export const "module.exports" = _module["module.exports"];

Fixes unjs#533
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 18, 2026

📝 Walkthrough

Walkthrough

The PR fixes stub file generation for modules with non-identifier export names. Previously, all exports used dot notation which generated invalid syntax for names like 'module.exports'. The fix conditionally uses bracket notation with quoting for non-identifier names.

Changes

Cohort / File(s) Summary
Stub File Export Handling
src/builders/rollup/stub.ts
Modified export generation logic to detect non-identifier export names and emit them using bracket notation with quotes (e.g., export const "name" = _module["name"];) instead of dot notation, while preserving dot notation for valid identifiers.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 Exports with curious names, we now embrace,
Bracket notation finds its proper place,
No more dot-dancing on invalid ground,
Valid identifiers still safe and sound!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: handling arbitrary module namespace identifier names in stub generation, which is the core purpose of this PR.
Linked Issues check ✅ Passed The PR directly addresses issue #533 by fixing stub generation for arbitrary module namespace identifier names with bracket notation and proper escaping.
Out of Scope Changes check ✅ Passed The changes are scoped to src/builders/rollup/stub.ts and directly address the linked issue requirement without introducing unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/builders/rollup/stub.ts`:
- Around line 161-169: The current code emits invalid ESM like export const
"module.exports" = ..., so change the mapping to create a safe local identifier
for non-identifier names (e.g. __export_<hash>), emit a const binding from
_module (const __export_x = _module[<stringified name>];) and then re-export it
using an export clause that supports string-literal export names (export {
__export_x as <stringified name> };), using the existing name/_module values to
build the two statements; update the map block that handles name to produce
either the simple export const for valid identifiers or the two-line local
binding + export { ... as ... } for arbitrary names.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 915ed282-c3af-4c98-bb6c-ddaa9c8cb48c

📥 Commits

Reviewing files that changed from the base of the PR and between cf1c303 and 859de85.

📒 Files selected for processing (1)
  • src/builders/rollup/stub.ts

Comment on lines +161 to +169
.map((name) => {
// Check if name is a valid JS identifier
// Arbitrary module namespace identifiers (e.g. 'module.exports')
// need bracket notation for member access
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) {
return `export const ${name} = _module.${name};`;
}
return `export const ${JSON.stringify(name)} = _module[${JSON.stringify(name)}];`;
}),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

export const ${JSON.stringify(name)} generates invalid ESM syntax for arbitrary export names.

Line 168 emits export const "module.exports" = ..., which is a parse error and still breaks stub generation for the target case.

💡 Proposed fix
           ...namedExports
             .filter((name) => name !== "default")
-            .map((name) => {
+            .flatMap((name, i) => {
               // Check if name is a valid JS identifier
               // Arbitrary module namespace identifiers (e.g. 'module.exports')
               // need bracket notation for member access
               if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name)) {
-                return `export const ${name} = _module.${name};`;
+                return [`export const ${name} = _module.${name};`];
               }
-              return `export const ${JSON.stringify(name)} = _module[${JSON.stringify(name)}];`;
+              const key = JSON.stringify(name);
+              const local = `_reExport_${i}`;
+              return [
+                `const ${local} = _module[${key}];`,
+                `export { ${local} as ${key} };`,
+              ];
             }),
Is `export const "module.exports" = _module["module.exports"];` valid JavaScript (ESM) syntax?  
What is the correct ESM syntax to export a string-literal export name (e.g. `"module.exports"`) from a local binding?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/builders/rollup/stub.ts` around lines 161 - 169, The current code emits
invalid ESM like export const "module.exports" = ..., so change the mapping to
create a safe local identifier for non-identifier names (e.g. __export_<hash>),
emit a const binding from _module (const __export_x = _module[<stringified
name>];) and then re-export it using an export clause that supports
string-literal export names (export { __export_x as <stringified name> };),
using the existing name/_module values to build the two statements; update the
map block that handles name to produce either the simple export const for valid
identifiers or the two-line local binding + export { ... as ... } for arbitrary
names.

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.

Invalid stub file generated for modules using abitrary module namespace identifier names

1 participant