Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 30, 2025

Summary

Fixed duplicate default export bug when using both import and require for the same module.

Changes Made

  • Understand the issue: duplicate default exports when both import and require are used for the same module
  • Create test case to reproduce the issue (crates/rolldown/tests/rolldown/issues/duplicate_default_export/)
  • Analyze the root cause in the export rendering logic
  • Fix the logic in render_chunk_exports to filter out "default" from export_items when rendering exports for CJS wrapped entry points
  • Verify the fix with the test case - no more duplicate default exports
  • Run existing tests to ensure no regressions (all 603 integration tests + all unit tests pass)
  • Apply code formatting (rustfmt)
  • Code review completed
  • Update test to use assert instead of console.log
  • Update snapshot hash after test modification
  • Final verification complete

Technical Details

The fix modifies crates/rolldown/src/utils/chunk/render_chunk_exports.rs to detect when we're processing an entry point with a CJS wrapper and filter out any "default" exports from the export_items list, since render_wrapped_entry_chunk already handles the default export. Other exports are preserved for cross-chunk references.

Original prompt

This section details on the original issue you should resolve

<issue_title>[Bug]: Duplicate default export on import + require usage</issue_title>
<issue_description>### Reproduction link or steps

Have some input code like this :

// Require
const required = require('./file.json')

// Dynamic Import
const dynamicRes = await import('./file.json')

Reproduction : https://github.com/Gugustinette/rolldown-import-require-repro

What is expected?

This is what one of the input file should look I guess :

import { require_file } from "./file-DuStti8x.js";

export default require_file();

What is actually happening?

This is how it looks right now :

import { require_file } from "./file-DuStti8x.js";

export default require_file();

export { file_default as default };

There are 2 default exports, which causes an error whenever running/importing associated modules.

System Info

System:
    OS: macOS 14.5
    CPU: (8) arm64 Apple M1 Pro
    Memory: 111.30 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 24.2.0 - /opt/homebrew/bin/node
    Yarn: 1.22.17 - /usr/local/bin/yarn
    npm: 11.5.2 - /opt/homebrew/bin/npm
    pnpm: 10.8.1 - /opt/homebrew/bin/pnpm
    bun: 1.2.19 - /Users/augustinmercier/.bun/bin/bun
    Deno: 2.3.3 - /Users/augustinmercier/.deno/bin/deno
    Watchman: 2025.05.26.00 - /opt/homebrew/bin/watchman
  Browsers:
    Chrome: 141.0.7390.66
    Firefox: 121.0
    Safari: 17.5
  npmPackages:
    rolldown: ^1.0.0-beta.43 => 1.0.0-beta.43

Any additional comments?

No response</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@graphite-app
Copy link
Contributor

graphite-app bot commented Oct 30, 2025

How to use the Graphite Merge Queue

Add the label graphite: merge to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

@netlify
Copy link

netlify bot commented Oct 30, 2025

Deploy Preview for rolldown-rs canceled.

Name Link
🔨 Latest commit e0d4b3b
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/690306f457e8370008b71efd

Copilot AI changed the title [WIP] Fix duplicate default export issue with import and require Fix duplicate default export when using both import and require Oct 30, 2025
Copilot AI requested a review from IWANABETHATGUY October 30, 2025 04:27
@IWANABETHATGUY
Copy link
Member

@copilot fix the CI error

Copy link
Contributor Author

Copilot AI commented Oct 30, 2025

@copilot fix the CI error

Fixed CI error by committing the updated snapshot hash. The hash changed because the test file was modified to use assert statements. Commit 95455fd

@github-actions
Copy link
Contributor

github-actions bot commented Oct 30, 2025

Benchmarks Rust

  • target: main(d460188)
  • pr: copilot/fix-duplicate-default-export(e0d4b3b)
group                                                        pr                                     target
-----                                                        --                                     ------
bundle/bundle@multi-duplicated-top-level-symbol              1.00     66.6±2.28ms        ? ?/sec    1.02     67.7±2.07ms        ? ?/sec
bundle/bundle@multi-duplicated-top-level-symbol-sourcemap    1.00     73.1±2.10ms        ? ?/sec    1.05     76.5±4.04ms        ? ?/sec
bundle/bundle@rome_ts                                        1.02    112.3±3.11ms        ? ?/sec    1.00    109.8±1.81ms        ? ?/sec
bundle/bundle@rome_ts-sourcemap                              1.00    122.3±1.65ms        ? ?/sec    1.01    123.7±2.58ms        ? ?/sec
bundle/bundle@threejs                                        1.00     41.3±2.39ms        ? ?/sec    1.00     41.2±0.89ms        ? ?/sec
bundle/bundle@threejs-sourcemap                              1.00     43.8±0.61ms        ? ?/sec    1.01     44.2±0.84ms        ? ?/sec
bundle/bundle@threejs10x                                     1.02    409.2±7.34ms        ? ?/sec    1.00    401.9±6.17ms        ? ?/sec
bundle/bundle@threejs10x-sourcemap                           1.00    465.9±6.27ms        ? ?/sec    1.01    469.4±6.70ms        ? ?/sec
scan/scan@rome_ts                                            1.00     87.3±1.55ms        ? ?/sec    1.01     87.9±1.79ms        ? ?/sec
scan/scan@threejs                                            1.00     29.3±0.49ms        ? ?/sec    1.01     29.6±0.49ms        ? ?/sec
scan/scan@threejs10x                                         1.00    302.1±3.78ms        ? ?/sec    1.01    304.7±5.05ms        ? ?/sec

@IWANABETHATGUY IWANABETHATGUY marked this pull request as ready for review October 30, 2025 05:03
@IWANABETHATGUY IWANABETHATGUY force-pushed the copilot/fix-duplicate-default-export branch from 95455fd to 2d03fa5 Compare October 30, 2025 05:03
@IWANABETHATGUY IWANABETHATGUY changed the title Fix duplicate default export when using both import and require fix: duplicate default export when using both import and require Oct 30, 2025
@IWANABETHATGUY IWANABETHATGUY enabled auto-merge (squash) October 30, 2025 06:30
Copy link
Member

shulaoda commented Oct 30, 2025

Merge activity

  • Oct 30, 6:33 AM UTC: The merge label 'graphite: merge' was detected. This PR will be added to the Graphite merge queue once it meets the requirements.
  • Oct 30, 6:34 AM UTC: shulaoda added this pull request to the Graphite merge queue.

## Summary

Fixed duplicate default export bug when using both `import` and `require` for the same module.

### Changes Made
- [x] Understand the issue: duplicate default exports when both `import` and `require` are used for the same module
- [x] Create test case to reproduce the issue (`crates/rolldown/tests/rolldown/issues/duplicate_default_export/`)
- [x] Analyze the root cause in the export rendering logic
- [x] Fix the logic in `render_chunk_exports` to filter out "default" from export_items when rendering exports for CJS wrapped entry points
- [x] Verify the fix with the test case - no more duplicate default exports
- [x] Run existing tests to ensure no regressions (all 603 integration tests + all unit tests pass)
- [x] Apply code formatting (rustfmt)
- [x] Code review completed
- [x] Update test to use assert instead of console.log
- [x] Update snapshot hash after test modification
- [x] Final verification complete

### Technical Details
The fix modifies `crates/rolldown/src/utils/chunk/render_chunk_exports.rs` to detect when we're processing an entry point with a CJS wrapper and filter out any "default" exports from the export_items list, since `render_wrapped_entry_chunk` already handles the default export. Other exports are preserved for cross-chunk references.

<!-- START COPILOT CODING AGENT SUFFIX -->

<details>

<summary>Original prompt</summary>

>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>[Bug]: Duplicate default export on `import` + `require` usage</issue_title>
> <issue_description>### Reproduction link or steps
>
> Have some input code like this :
>
> ```ts
> // Require
> const required = require('./file.json')
>
> // Dynamic Import
> const dynamicRes = await import('./file.json')
> ```
>
> Reproduction : https://github.com/Gugustinette/rolldown-import-require-repro
>
> ### What is expected?
>
> This is what one of the input file should look I guess :
>
> ```js
> import { require_file } from "./file-DuStti8x.js";
>
> export default require_file();
> ```
>
> ### What is actually happening?
>
> This is how it looks right now :
>
> ```js
> import { require_file } from "./file-DuStti8x.js";
>
> export default require_file();
>
> export { file_default as default };
> ```
>
> There are 2 default exports, which causes an error whenever running/importing associated modules.
>
> ### System Info
>
> ```Shell
> System:
>     OS: macOS 14.5
>     CPU: (8) arm64 Apple M1 Pro
>     Memory: 111.30 MB / 16.00 GB
>     Shell: 5.9 - /bin/zsh
>   Binaries:
>     Node: 24.2.0 - /opt/homebrew/bin/node
>     Yarn: 1.22.17 - /usr/local/bin/yarn
>     npm: 11.5.2 - /opt/homebrew/bin/npm
>     pnpm: 10.8.1 - /opt/homebrew/bin/pnpm
>     bun: 1.2.19 - /Users/augustinmercier/.bun/bin/bun
>     Deno: 2.3.3 - /Users/augustinmercier/.deno/bin/deno
>     Watchman: 2025.05.26.00 - /opt/homebrew/bin/watchman
>   Browsers:
>     Chrome: 141.0.7390.66
>     Firefox: 121.0
>     Safari: 17.5
>   npmPackages:
>     rolldown: ^1.0.0-beta.43 => 1.0.0-beta.43
> ```
>
> ### Any additional comments?
>
> _No response_</issue_description>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>

</details>

- Fixes #6571

<!-- START COPILOT CODING AGENT TIPS -->
---

💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
@graphite-app graphite-app bot force-pushed the copilot/fix-duplicate-default-export branch from 2d03fa5 to e0d4b3b Compare October 30, 2025 06:34
@IWANABETHATGUY IWANABETHATGUY merged commit 118bfa0 into main Oct 30, 2025
27 checks passed
@IWANABETHATGUY IWANABETHATGUY deleted the copilot/fix-duplicate-default-export branch October 30, 2025 06:48
@github-actions github-actions bot mentioned this pull request Nov 3, 2025
shulaoda added a commit that referenced this pull request Nov 3, 2025
## [1.0.0-beta.46] - 2025-11-03

### 💥 BREAKING CHANGES

- default `preserveEntrySignatures` to `'exports-only'` to align with
Rollup (#6723) by @Copilot

### 🚀 Features

- link to docs for commonjs prefer_builtin_feature diagnostic (#6793) by
@sapphi-red
- rolldown_plugin_build_import_analysis: align partial logic (#6789) by
@shulaoda
- improve `EVAL` warning message (#6776) by @sapphi-red
- rolldown: oxc v0.96.0 (#6774) by @Boshen
- rolldown_plugin_build_import_analysis: align partial logic (#6773) by
@shulaoda
- skip `__toESM` when not needed to reduce output size (#6751) by
@Copilot
- improve `EMPTY_IMPORT_META` warning message (#6761) by @sapphi-red
- rolldown_plugin_build_import_analysis: align no preload logic (#6762)
by @shulaoda
- rolldown_plugin_vite_css_post: align `RemovedPureCSSFilesCache` logic
(#6745) by @shulaoda
- rolldown_plugin_build_import_analysis: initialize `generateBundle`
logic (#6744) by @shulaoda
- experimental: introduce `freeExternalMemory` to free external memory
immediately (#6721) by @hyf0

### 🐛 Bug Fixes

- plugins: wrap replacePlugin with makeBuiltinPluginCallable (#6782) by
@huang-julien
- debug: make sure `this.resolve` is also tracked under corresponding
session (#6798) by @hyf0
- legal comments above directives are not preserved (#6787) by @shulaoda
- panic when rendering exports to other chunks (#6765) by @Copilot
- transform/inject: escape special characters in import source (#6778)
by @Copilot
- handle default exports from CJS modules correctly (#6767) by
@IWANABETHATGUY
- rolldown_plugin_build_import_analysis: correct aligned logic (#6768)
by @shulaoda
- duplicate default export when using both import and require (#6764) by
@Copilot
- handle arbitrary module namespace identifiers in
`preserveEntrySignatures: 'allow-extension'` (#6753) by @Copilot
- TypeError when loading CJS files after bundling mixed CJS+TS project
(#6743) by @IWANABETHATGUY
- add warnings for import.meta.dirname/filename/url in UMD and IIFE
formats (#6747) by @Copilot
- rolldown_plugin_manifest: should keep `names` field (#6742) by
@shulaoda
- legal comments above import statements are not preserved (#6717) by
@shulaoda
- remove redundant node check in dynamic entry graph construction
(#6730) by @IWANABETHATGUY

### 🚜 Refactor

- remove unnecessary `#[napi(gettter)]`, use function call directly
(#6694) by @hyf0
- explain the reason if memory doesn't get dropped by
`freeExternalMemory` (#6781) by @hyf0
- pre compute exports (#6755) by @IWANABETHATGUY
- dev: receive an interface for DevRuntime rather than WebSocket
directly (#6734) by @sapphi-red
- dev: fix types for the runtime file and expose it properly (#6731) by
@sapphi-red

### 📚 Documentation

- fix magicstring credit (#6812) by @TheAlexLichter
- in-depth/bundling-cjs: wrap some sections with caveats section (#6796)
by @sapphi-red
- in-depth/bundling-cjs: add "Ambiguous `default` import from CJS
modules" section (#6795) by @sapphi-red
- in-depth/bundling-cjs: make `esmExternalRequirePlugin` link more
prominent (#6792) by @sapphi-red
- add explanation about direct eval (#6775) by @sapphi-red
- add "Non-ESM Output Formats" page (#6760) by @sapphi-red
- use oxc for benchmark comparison in native magic string doc (#6740) by
@sapphi-red
- polish `cleanDir` option (#6741) by @hyf0
- fix image in native magic string doc (#6739) by @sapphi-red
- update description about `attachDebugInfo: 'none'` (#6738) by
@IWANABETHATGUY
- add warning about cleanDir behavior with multiple configs (#6735) by
@Copilot

### 🧪 Testing

- skip paths function test (#6771) by @IWANABETHATGUY
- rust/dev: add test about recover from initial build error (#6567) by
@hyf0

### ⚙️ Miscellaneous Tasks

- correct prepare release binding file (#6817) by @shulaoda
- deps: update github-actions (#6806) by @renovate[bot]
- deps: lock file maintenance npm packages (#6814) by @renovate[bot]
- use correct node version (#6809) by @shulaoda
- deps: lock file maintenance rust crates (#6815) by @renovate[bot]
- deps: update npm packages (major) (#6813) by @renovate[bot]
- deps: update dependency dprint-typescript to v0.95.12 (#6805) by
@renovate[bot]
- fix typo (#6801) by @iiio2
- fix typo in is_import_expr_ignored_by_comment method name (#6797) by
@sapphi-red
- deps: update crate-ci/typos action to v1.39.0 (#6794) by
@renovate[bot]
- deps: upgrade Rust to v1.91.0 and fix new clippy lints (#6785) by
@Copilot
- allow dprint to format `packages/rolldown/tests` except diagnostics
directory (#6772) by @Copilot
- deps: update dependency tsdown to v0.15.12 (#6759) by @renovate[bot]
- deps: update dependency rolldown-plugin-dts to v0.17.3 (#6758) by
@renovate[bot]
- test/dev: collect multiple build outputs for each step (#6736) by
@hyf0
- rolldown: change the NAPI-RS binding file to the cjs (#6688) by
@Brooooooklyn
- test: automatically run extra test with `preserve_entry_signatures:
'allow-extension'` (#6727) by @Copilot
- deps: update dependency rolldown-plugin-dts to v0.17.2 (#6732) by
@renovate[bot]
- fix node validation (#6728) by @shulaoda
- deps: lock file maintenance rust crates (#6636) by @renovate[bot]
- deps: update dependency tsdown to v0.15.11 (#6725) by @renovate[bot]

### ❤️ New Contributors

* @huang-julien made their first contribution in
[#6782](#6782)

Co-authored-by: shulaoda <[email protected]>
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]: Duplicate default export on import + require usage

4 participants