Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 29, 2025

  • Understand the issue: Rolldown uses __toESM for all external imports (except side-effect imports), while esbuild only uses it for namespace and default imports
  • Build rolldown and test current behavior
  • Compare with esbuild behavior
  • Identify the code locations that need modification
  • Implement the fix to align with esbuild behavior
  • Create or update tests to verify the fix
  • Run tests to ensure no regressions
  • Address code review feedback
    • Extracted external_import_needs_interop to common utility
    • Deduped external import handling in cjs.rs using .map()
    • Renamed g_WRAP to g_nowrap in test (legacy __esModule artifact)
    • Updated snapshots for content hash change
    • Fixed clippy warning (map_or -> is_some_and)
  • Request code review

Fixes #5434

Original prompt

This section details on the original issue you should resolve

<issue_title>[Bug]: Align with esbuild external interop</issue_title>
<issue_description>### Reproduction link or steps

esbuild:
https://hyrious.me/esbuild-repl/?version=0.23.0&b=e%00main.js%00import+%27a%27%0Aimport+*+as+fs+from+%27b%27%0Aimport+defaultValue+from+%27c%27%0Aimport+%7BreadFileSync%7D+from+%27d%27%0Aconsole.log%28fs%2C+readFileSync%2C+defaultValue%29%0A&o=%7B%0A++treeShaking%3A+true%2C%0A++external%3A+%5B%22c%22%2C+%22a%22%2C+%22b%22%2C+%22d%22%5D%2C%0A%22bundle%22%3A+true%2C%0Aformat%3A+%22cjs%22%2C%0A%7D

rolldown:
https://repl.rolldown.rs/#eNptUcFOhDAQ/ZVJL+waJKs3SdaLiVeNJl6shy4MG0yZNqVd2RD+3UFAILtJD/P6Zl7fm7aiEGkrSsqxSXzd1yTSGcci62FljfMQqUjSWN+AqqHg40wF0WEmcixU0P5D6YAjm81s61Dlz6XG9zNl3cjnzGeGaqMx0ea4KeoYln3xSnQriW2hSL0L2MXCGa1z80MJSxTlcZHiCrPK488WoYW3se3F+pJdwGRrGo9A0mDQw6CUXszsoZUEYIK3wacDACiMqxRDKbLvWor+spPERxI2y22NuoLjWB2OJT0Z8qokdIs4V5hVnL8v6U0Hu1tECJbXOz53Ug5e/2Ww8Wz8c7OF/eM0+MXb7djGiXXvkl2yuz2gV8n9g+h+ARUUwNw=

What is expected?

just like esbuild

What is actually happening?

rolldown would always generate __toESM when interop with external

System Info

n/a

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 29, 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 29, 2025

Deploy Preview for rolldown-rs ready!

Name Link
🔨 Latest commit 42a0cd9
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/6902ee75a866ad0008ceadee
😎 Deploy Preview https://deploy-preview-6751--rolldown-rs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@sapphi-red
Copy link
Member

@copilot please continue

Copy link
Contributor Author

Copilot AI commented Oct 29, 2025

@copilot please continue

I've completed the fix to align with esbuild's external interop behavior. The changes ensure that __toESM is only generated for namespace imports (import * as foo) and default imports (import foo), but not for named imports (import { bar }).

Changes made in commit 62eb628:

  • Updated reference_needed_symbols.rs to conditionally reference __toESM based on import type
  • Modified cjs.rs and utils/mod.rs to check if interop is needed before generating __toESM calls
  • Fixed compute_cross_chunk_links.rs to handle cases where __toESM is not needed
  • Updated test snapshots to reflect the new behavior

All tests pass successfully.

Copilot AI changed the title [WIP] Align with esbuild external interop Align with esbuild external interop Oct 29, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Oct 29, 2025

Benchmarks Rust

  • target: main(bcc6967)
  • pr: copilot/align-esbuild-external-interop(42a0cd9)
group                                                        pr                                     target
-----                                                        --                                     ------
bundle/bundle@multi-duplicated-top-level-symbol              1.00     70.4±2.26ms        ? ?/sec    1.06     74.5±4.32ms        ? ?/sec
bundle/bundle@multi-duplicated-top-level-symbol-sourcemap    1.00     77.5±2.68ms        ? ?/sec    1.07     83.1±2.27ms        ? ?/sec
bundle/bundle@rome_ts                                        1.00    112.8±2.50ms        ? ?/sec    1.04    116.8±2.58ms        ? ?/sec
bundle/bundle@rome_ts-sourcemap                              1.00    127.0±2.34ms        ? ?/sec    1.05    133.2±3.54ms        ? ?/sec
bundle/bundle@threejs                                        1.00     41.1±0.87ms        ? ?/sec    1.09     45.0±2.79ms        ? ?/sec
bundle/bundle@threejs-sourcemap                              1.00     46.0±1.46ms        ? ?/sec    1.01     46.5±0.76ms        ? ?/sec
bundle/bundle@threejs10x                                     1.00    411.7±6.18ms        ? ?/sec    1.00    412.3±6.72ms        ? ?/sec
bundle/bundle@threejs10x-sourcemap                           1.00    474.9±6.63ms        ? ?/sec    1.01    477.9±5.62ms        ? ?/sec
scan/scan@rome_ts                                            1.00     88.2±1.79ms        ? ?/sec    1.01     88.8±2.11ms        ? ?/sec
scan/scan@threejs                                            1.00     30.1±0.58ms        ? ?/sec    1.01     30.5±2.19ms        ? ?/sec
scan/scan@threejs10x                                         1.00    308.8±5.53ms        ? ?/sec    1.00    310.0±4.44ms        ? ?/sec

@sapphi-red
Copy link
Member

@copilot update snapshot

Copy link
Contributor Author

Copilot AI commented Oct 30, 2025

@copilot update snapshot

Done in commit 009a521. Updated the filename hash snapshot to reflect the content change from renaming g_WRAP to g_nowrap (hash changed from Cn6Q72T9 to C73CEcdw).

@sapphi-red
Copy link
Member

@copilot fix CI error

Copy link
Contributor Author

Copilot AI commented Oct 30, 2025

@copilot fix CI error

Fixed in commit 55967f3. The CI error was a clippy warning about using map_or(false, ...) instead of the more idiomatic is_some_and(...). Changed line 142 in cjs.rs to use is_some_and which resolves the clippy::unnecessary_map_or warning.

@sapphi-red sapphi-red changed the title Align with esbuild external interop feat: skip __toESM when not needed to reduce output size Oct 30, 2025
@sapphi-red sapphi-red marked this pull request as ready for review October 30, 2025 03:33
@sapphi-red sapphi-red enabled auto-merge (squash) October 30, 2025 03:33
@hyf0 hyf0 assigned IWANABETHATGUY and unassigned sapphi-red and Copilot Oct 30, 2025
@sapphi-red sapphi-red disabled auto-merge October 30, 2025 04:48
@graphite-app
Copy link
Contributor

graphite-app bot commented Oct 30, 2025

Merge activity

  • Oct 30, 4:49 AM UTC: sapphi-red added this pull request to the Graphite merge queue.
  • Oct 30, 4:55 AM UTC: The Graphite merge queue couldn't merge this PR because it was not satisfying all requirements (Failed CI: 'node-test (ubuntu-latest) / Node Test', 'node-test (macos-latest) / Node Test', 'node-test (windows-latest) / Node Test').

- [x] Understand the issue: Rolldown uses `__toESM` for all external imports (except side-effect imports), while esbuild only uses it for namespace and default imports
- [x] Build rolldown and test current behavior
- [x] Compare with esbuild behavior
- [x] Identify the code locations that need modification
- [x] Implement the fix to align with esbuild behavior
- [x] Create or update tests to verify the fix
- [x] Run tests to ensure no regressions
- [x] Address code review feedback
  - Extracted `external_import_needs_interop` to common utility
  - Deduped external import handling in cjs.rs using .map()
  - Renamed g_WRAP to g_nowrap in test (legacy __esModule artifact)
  - Updated snapshots for content hash change
  - Fixed clippy warning (map_or -> is_some_and)
- [x] Request code review

Fixes #5434

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

<details>

<summary>Original prompt</summary>

>
> ----
>
> *This section details on the original issue you should resolve*
>
> <issue_title>[Bug]: Align with esbuild external interop</issue_title>
> <issue_description>### Reproduction link or steps
>
> **esbuild**:
> https://hyrious.me/esbuild-repl/?version=0.23.0&b=e%00main.js%00import+%27a%27%0Aimport+*+as+fs+from+%27b%27%0Aimport+defaultValue+from+%27c%27%0Aimport+%7BreadFileSync%7D+from+%27d%27%0Aconsole.log%28fs%2C+readFileSync%2C+defaultValue%29%0A&o=%7B%0A++treeShaking%3A+true%2C%0A++external%3A+%5B%22c%22%2C+%22a%22%2C+%22b%22%2C+%22d%22%5D%2C%0A%22bundle%22%3A+true%2C%0Aformat%3A+%22cjs%22%2C%0A%7D
>
> **rolldown**:
> https://repl.rolldown.rs/#eNptUcFOhDAQ/ZVJL+waJKs3SdaLiVeNJl6shy4MG0yZNqVd2RD+3UFAILtJD/P6Zl7fm7aiEGkrSsqxSXzd1yTSGcci62FljfMQqUjSWN+AqqHg40wF0WEmcixU0P5D6YAjm81s61Dlz6XG9zNl3cjnzGeGaqMx0ea4KeoYln3xSnQriW2hSL0L2MXCGa1z80MJSxTlcZHiCrPK488WoYW3se3F+pJdwGRrGo9A0mDQw6CUXszsoZUEYIK3wacDACiMqxRDKbLvWor+spPERxI2y22NuoLjWB2OJT0Z8qokdIs4V5hVnL8v6U0Hu1tECJbXOz53Ug5e/2Ww8Wz8c7OF/eM0+MXb7djGiXXvkl2yuz2gV8n9g+h+ARUUwNw=
>
>
>
>
> ### What is expected?
>
> just like esbuild
>
> ### What is actually happening?
>
> rolldown would always generate `__toESM` when interop with external
>
> ### System Info
>
> ```Shell
> n/a
> ```
>
> ### Any additional comments?
>
> _No response_</issue_description>
>
> ## Comments on the Issue (you are @copilot in this section)
>
> <comments>
> </comments>
>

</details>

- Fixes #5434

<!-- 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/align-esbuild-external-interop branch from 55967f3 to 42a0cd9 Compare October 30, 2025 04:49
@IWANABETHATGUY
Copy link
Member

flaky test

@IWANABETHATGUY IWANABETHATGUY merged commit d460188 into main Oct 30, 2025
40 of 46 checks passed
@IWANABETHATGUY IWANABETHATGUY deleted the copilot/align-esbuild-external-interop branch October 30, 2025 06:33
@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 <165626830+shulaoda@users.noreply.github.com>
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]: Align with esbuild external interop

3 participants