Skip to content

feat(pluginutils): named exports for reserved-word keys + fix duplicate default export in dataToEsm#2002

Merged
shellscape merged 2 commits into
rollup:masterfrom
semimikoh:fix/datatoesm-default-duplicate-export
May 29, 2026
Merged

feat(pluginutils): named exports for reserved-word keys + fix duplicate default export in dataToEsm#2002
shellscape merged 2 commits into
rollup:masterfrom
semimikoh:fix/datatoesm-default-duplicate-export

Conversation

@semimikoh
Copy link
Copy Markdown
Contributor

@semimikoh semimikoh commented May 29, 2026

…in dataToEsm

Rollup Plugin Name: @rollup/pluginutils

This PR contains:

  • bugfix
  • feature
  • refactor
  • documentation
  • other

Are tests included?

  • yes (bugfixes and features will not be merged without tests)
  • no

Breaking Changes?

  • yes (breaking changes will not be merged unless absolutely necessary)
  • no

List any relevant issue numbers:

resolves #2001

Description

Two related changes to dataToEsm:

1. Feature — emit ES2015-safe named exports for reserved-word / identifier-name keys

A key that is a valid IdentifierName but not a legal binding identifier (a
reserved word, global, or literal — switch, await, null, true, …) is now
re-exported with the unquoted export { _x as switch } form, which is valid
since ES2015. Previously such keys were emitted as named exports only under
includeArbitraryNames, and then via the quoted ES2022 form
(export { _x as "switch" }).

This is what Vite needs for CSS-modules named exports: a class such as .switch
or .default should produce a named export even when targeting browsers that
predate arbitrary module namespace names (vitejs/vite#22393).
The unquoted form covers that without includeArbitraryNames; the quoted form
stays opt-in and is now used only for keys that are not valid IdentifierNames
(e.g. foo-bar).

Behavior change: for callers not passing includeArbitraryNames,
reserved-word / global / literal keys (e.g. null, true, else) now gain an
ES2015-valid named export where previously they had none. This is strictly
additive — it only emits syntax already required by the rest of the output, and
existing imports are unaffected. Two existing test expectations
(serializes null, supports a compact argument) are updated accordingly.

2. Bugfix — avoid a duplicate default export for a default key

dataToEsm always appends a trailing export default {...}. A key named
default was also emitted as export { _x as "default" } (under
includeArbitraryNames), producing two default exports and a
SyntaxError: Duplicate export of 'default'. A default key is now exposed only
through the default export object.

Steps to execute / reproduce

import { dataToEsm } from '@rollup/pluginutils';

// reserved-word named export (ES2015-safe), no includeArbitraryNames needed
dataToEsm({ switch: 'a', normal: 'b' }, { namedExports: true, preferConst: true });
// -> const _arbitrary0 = "a"; export const normal = "b";
//    export { _arbitrary0 as switch }; export default { "switch": "a", normal };

// the duplicate-default bug, before this PR:
const code = dataToEsm({ default: 'x' }, { namedExports: true, includeArbitraryNames: true });
await import('data:text/javascript,' + encodeURIComponent(code));
// previously threw: SyntaxError: Duplicate export of 'default'

@semimikoh semimikoh requested a review from shellscape as a code owner May 29, 2026 04:29
@semimikoh semimikoh changed the title fix(pluginutils): avoid duplicate default export for a 'default' key … feat(pluginutils): named exports for reserved-word keys + fix duplicate default export in dataToEsm May 29, 2026
@shellscape
Copy link
Copy Markdown
Collaborator

@CharlieHelps please do a thorough review of this PR to determine if it's ready to merge. Look at the diff, reviews, comments, and referenced issues/docs/links to build a complete understanding. Then respond with a comment that includes a list of MUST fix items that must be fixed before the the PR is merged, and a list of IMPROVEMENTS that shouldn't block the merge but may be quick improvements or things that could be addressed in a follow up PR. The MUST fix items should explain exactly what's wrong and what needs to change to resolve the problem. Both should be numbered lists and the IMPROVEMENTS list should continue counting up from the MUST list. It's OK if there are no IMPROVEMENTS or MUST fix items, you can just omit the lists.

Copy link
Copy Markdown
Contributor

@charliecreates charliecreates Bot left a comment

Choose a reason for hiding this comment

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

Reviewed the latest diff, linked issue/discussion context, and current CI checks; I don't have actionable feedback to address before merge.

@charliecreates
Copy link
Copy Markdown
Contributor

I did a full review of this PR: diff, tests, existing comments/reviews, and linked context (#2001, discussion #2003, and the referenced Vite PR).

I don’t see any merge-blocking issues, and I also don’t have any non-blocking follow-up improvements to suggest right now.

This looks ready to merge.

@shellscape
Copy link
Copy Markdown
Collaborator

thanks!

@shellscape shellscape merged commit 1d7199a into rollup:master May 29, 2026
15 checks passed
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.

dataToEsm emits a duplicate default export for a default key with includeArbitraryNames

2 participants