Repository: rolldown/rolldown
Pull request #4574: docs: document bundling cjs
Author: login: hyf0
Labels: name: needs-triage
Created: 2025-05-16T13:56:31Z | Merged: 2025-05-16T18:55:45Z

<!-- Thank you for contributing! -->

### Description

<!-- Please insert your description here and provide especially info about the "what" this PR is solving -->



Diff:
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index 1b39aee0ab94..06e47b6a3ca7 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -43,6 +43,7 @@ const sharedSidebar = [
       { text: 'Module Types', link: '/guide/in-depth/module-types.md' },
       { text: 'Top Level Await', link: '/guide/in-depth/tla-in-rolldown.md' },
       { text: 'Advanced Chunks', link: '/guide/in-depth/advanced-chunks.md' },
+      { text: 'Bundling CJS', link: '/guide/in-depth/bundling-cjs.md' },
       // { text: 'Use Strict', link: '/guide/in-depth/use-strict.md' },
     ],
   },
diff --git a/docs/guide/in-depth/bundling-cjs.md b/docs/guide/in-depth/bundling-cjs.md
new file mode 100644
index 000000000000..a08d0c0429af
--- /dev/null
+++ b/docs/guide/in-depth/bundling-cjs.md
@@ -0,0 +1,101 @@
+# Bundling CJS
+
+Rolldown provides first-class support for CommonJS modules. This document explains how Rolldown handles CJS modules and their interoperability with ES modules.
+
+## Key Features
+
+### Native CJS Support
+
+Rolldown automatically recognizes and processes CommonJS modules without requiring any additional plugins or packages. This native support means:
+
+- No need to install extra dependencies
+- Better performance compared to plugin-based solutions
+
+### On-demand Execution
+
+Rolldown preserves the on-demand execution semantics of CommonJS modules, which is a key feature of the CommonJS module system. This means modules are only executed when they are actually required.
+
+Here's an example:
+
+```js
+// index.js
+import { value } from './foo.js';
+
+const getFooExports = () => require('./foo.js');
+
+// foo.js
+module.exports = { value: 'foo' };
+```
+
+When bundled, it produces:
+
+```js
+// #region rolldown:runtime
+// ...runtime code
+// #endregion
+
+// #region foo.js
+var require_foo = __commonJS({
+  'foo.js'(exports, module) {
+    module.exports = { value: 'foo' };
+  },
+});
+
+// #endregion
+// #region index.js
+const getFooExports = () => require_foo();
+// #endregion
+```
+
+In this example, the `foo.js` module won't be executed until `getFooExports()` is called, maintaining the lazy-loading behavior of CommonJS.
+
+### ESM/CJS Interoperability
+
+Rolldown provides seamless interoperability between ES modules and CommonJS modules.
+
+Example of ESM importing from CJS:
+
+```js
+// index.js
+import { value } from './foo.js';
+
+console.log(value);
+
+// foo.js
+module.exports = { value: 'foo' };
+```
+
+Bundled output:
+
+```js
+// #region rolldown:runtime
+// ...runtime code
+// #endregion
+
+// #region foo.js
+var require_foo = __commonJS({
+  'foo.js'(exports, module) {
+    module.exports = { value: 'foo' };
+  },
+});
+
+// #endregion
+// #region index.js
+var import_foo = __toESM(require_foo());
+console.log(import_foo.value);
+
+// #endregion
+```
+
+The `__toESM` helper ensures that CommonJS exports are properly converted to ES module format, allowing seamless access to the exported values.
+
+## `require` external modules
+
+Rolldown will leave it as it is. You need to ensure the running environment will provide a `require` function.
+
+## Future Plans
+
+Rolldown's first-class support for CommonJS modules enables several potential optimizations:
+
+- Advanced tree-shaking capabilities for CommonJS modules
+- Better dead code elimination


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR adds documentation outlining how Rolldown bundles CommonJS modules and updates the sidebar configuration to reference the new guide.  
- Added a new guide in docs/guide/in-depth/bundling-cjs.md covering native CJS support, on-demand execution, and ESM/CJS interoperability.  
- Updated docs/.vitepress/config.ts to include a new sidebar link for Bundling CJS.

### Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

| File                                 | Description                                                        |
| ------------------------------------ | ------------------------------------------------------------------ |
| docs/guide/in-depth/bundling-cjs.md  | New documentation guide explaining bundling of CommonJS modules.   |
| docs/.vitepress/config.ts             | Added a sidebar entry pointing to the Bundling CJS documentation.  |




state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T13:56:54Z
user: login: graphite-app[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T13:57:59Z
user: login: IWANABETHATGUY
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-16T16:41:08Z
user: login: IWANABETHATGUY
state: APPROVED
author_association: MEMBER
submitted_at: 2025-05-16T16:43:10Z
user: login: hyf0
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-05-16T18:43:11Z

Review comments:
diff_hunk: @@ -34,6 +34,7 @@ const sharedSidebar = [
       { text: 'Bundler API', link: '/reference/bundler-api.md' },
       { text: 'Plugin API', link: '/reference/plugin-api.md' },
       { text: 'Command Line Interface', link: '/reference/cli.md' },
+      { text: 'Bundling CJS', link: '/reference/bundling-cjs.md' },
path: docs/.vitepress/config.ts
user: login: Copilot
body: The link path in the sidebar appears inconsistent with the file location. If the new guide is in 'docs/guide/in-depth/bundling-cjs.md', consider updating the link to '/guide/in-depth/bundling-cjs.md'.
created_at: 2025-05-16T13:56:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,98 @@
+# Bundling CJS
+
+Rolldown provides first-class support for CommonJS modules. This document explains how Rolldown handles CJS modules and their interoperability with ES modules.
+
+## Key Features
+
+### Native CJS Support
+
+Rolldown automatically recognizes and processes CommonJS modules without requiring any additional plugins or packages. This native support means:
+
+- No need to install extra dependencies
+- Better performance compared to plugin-based solutions
+
+### On-demand Execution
+
+Rolldown preserves the on-demand execution semantics of CommonJS modules, which is a key feature of the CommonJS module system. This means modules are only executed when they are actually required.
+
+Here's an example:
+
+```js
+// index.js
+import { value } from './foo.js';
+
+const getFooExports = () => require('./foo.js');
+
+// foo.js
+module.exports = { value: 'foo' };
+```
+
+When bundled, it produces:
+
+```js
+// #region rolldown:runtime
+// ...runtime code
+// #endregion
+
+// #region foo.js
+var require_foo = __commonJS({
+  'foo.js'(exports, module) {
+    module.exports = { value: 'foo' };
+  },
+});
+
+// #endregion
+// #region index.js
+const getFooExports = () => require_foo();
+// #endregion
+```
+
+In this example, the `foo.js` module won't be executed until `getFooExports()` is called, maintaining the lazy-loading behavior of CommonJS.
+
+### ESM/CJS Interoperability
+
+Rolldown provides seamless interoperability between ES modules and CommonJS modules.
+
+Example of ESM importing from CJS:
+
+```js
+// index.js
+import { value } from './foo.js';
+
+console.log(value);
+
+// foo.js
+module.exports = { value: 'foo' };
+```
+
+Bundled output:
+
+```js
+// #region rolldown:runtime
+// ...runtime code
+// #endregion
+
+// #region foo.js
+var require_foo = __commonJS({
+  'foo.js'(exports, module) {
+    module.exports = { value: 'foo' };
+  },
+});
+
+// #endregion
+// #region index.ts
path: docs/guide/in-depth/bundling-cjs.md
user: login: graphite-app[bot]
body: There's a mismatch between the file extension in the example and the region comment. The example shows `index.js` (line 59), but the region comment refers to `index.ts`. For consistency, please update the region comment to `// #region index.js`.
```suggestion
// #region index.js
```
  

*Spotted by [Diamond](https://app.graphite.dev/diamond/?org=rolldown&ref=ai-review-comment)*<i class='graphite__hidden'><br /><br />Is this helpful? React 👍 or 👎 to let us know.</i>
created_at: 2025-05-16T13:57:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,98 @@
+# Bundling CJS
+
+Rolldown provides first-class support for CommonJS modules. This document explains how Rolldown handles CJS modules and their interoperability with ES modules.
+
+## Key Features
+
+### Native CJS Support
+
+Rolldown automatically recognizes and processes CommonJS modules without requiring any additional plugins or packages. This native support means:
+
+- No need to install extra dependencies
+- Better performance compared to plugin-based solutions
+
+### On-demand Execution
+
+Rolldown preserves the on-demand execution semantics of CommonJS modules, which is a key feature of the CommonJS module system. This means modules are only executed when they are actually required.
+
+Here's an example:
+
+```js
+// index.js
+import { value } from './foo.js';
+
+const getFooExports = () => require('./foo.js');
+
+// foo.js
+module.exports = { value: 'foo' };
+```
+
+When bundled, it produces:
+
+```js
+// #region rolldown:runtime
+// ...runtime code
+// #endregion
+
+// #region foo.js
+var require_foo = __commonJS({
+  'foo.js'(exports, module) {
+    module.exports = { value: 'foo' };
+  },
+});
+
+// #endregion
+// #region index.js
+const getFooExports = () => require_foo();
+// #endregion
+```
+
+In this example, the `foo.js` module won't be executed until `getFooExports()` is called, maintaining the lazy-loading behavior of CommonJS.
+
+### ESM/CJS Interoperability
+
+Rolldown provides seamless interoperability between ES modules and CommonJS modules.
+
+Example of ESM importing from CJS:
+
+```js
+// index.js
+import { value } from './foo.js';
+
+console.log(value);
+
+// foo.js
+module.exports = { value: 'foo' };
+```
+
+Bundled output:
+
+```js
+// #region rolldown:runtime
+// ...runtime code
+// #endregion
+
+// #region foo.js
+var require_foo = __commonJS({
+  'foo.js'(exports, module) {
+    module.exports = { value: 'foo' };
+  },
+});
+
+// #endregion
+// #region index.js
+var import_foo = __toESM(require_foo());
+console.log(import_foo.value);
+
+// #endregion
+```
+
+The `__toESM` helper ensures that CommonJS exports are properly converted to ES module format, allowing seamless access to the exported values.
+
+## Future Plans
+
+Rolldown's first-class support for CommonJS modules enables several potential optimizations:
+
+- Advanced tree-shaking capabilities for CommonJS modules
+- Better dead code elimination
+- Improved static analysis opportunities
path: docs/guide/in-depth/bundling-cjs.md
user: login: IWANABETHATGUY
body: > Improved static analysis opportunities 

This sentence sounds a little weird, maybe `Improve static analysis of CommonJS ` or even ` Improve static analysis`?
created_at: 2025-05-16T16:41:07Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,98 @@
+# Bundling CJS
+
+Rolldown provides first-class support for CommonJS modules. This document explains how Rolldown handles CJS modules and their interoperability with ES modules.
+
+## Key Features
+
+### Native CJS Support
+
+Rolldown automatically recognizes and processes CommonJS modules without requiring any additional plugins or packages. This native support means:
+
+- No need to install extra dependencies
+- Better performance compared to plugin-based solutions
+
+### On-demand Execution
+
+Rolldown preserves the on-demand execution semantics of CommonJS modules, which is a key feature of the CommonJS module system. This means modules are only executed when they are actually required.
+
+Here's an example:
+
+```js
+// index.js
+import { value } from './foo.js';
+
+const getFooExports = () => require('./foo.js');
+
+// foo.js
+module.exports = { value: 'foo' };
+```
+
+When bundled, it produces:
+
+```js
+// #region rolldown:runtime
+// ...runtime code
+// #endregion
+
+// #region foo.js
+var require_foo = __commonJS({
+  'foo.js'(exports, module) {
+    module.exports = { value: 'foo' };
+  },
+});
+
+// #endregion
+// #region index.js
+const getFooExports = () => require_foo();
+// #endregion
+```
+
+In this example, the `foo.js` module won't be executed until `getFooExports()` is called, maintaining the lazy-loading behavior of CommonJS.
+
+### ESM/CJS Interoperability
+
+Rolldown provides seamless interoperability between ES modules and CommonJS modules.
+
+Example of ESM importing from CJS:
+
+```js
+// index.js
+import { value } from './foo.js';
+
+console.log(value);
+
+// foo.js
+module.exports = { value: 'foo' };
+```
+
+Bundled output:
+
+```js
+// #region rolldown:runtime
+// ...runtime code
+// #endregion
+
+// #region foo.js
+var require_foo = __commonJS({
+  'foo.js'(exports, module) {
+    module.exports = { value: 'foo' };
+  },
+});
+
+// #endregion
+// #region index.js
+var import_foo = __toESM(require_foo());
+console.log(import_foo.value);
+
+// #endregion
+```
+
+The `__toESM` helper ensures that CommonJS exports are properly converted to ES module format, allowing seamless access to the exported values.
+
+## Future Plans
+
+Rolldown's first-class support for CommonJS modules enables several potential optimizations:
+
+- Advanced tree-shaking capabilities for CommonJS modules
+- Better dead code elimination
+- Improved static analysis opportunities
path: docs/guide/in-depth/bundling-cjs.md
user: login: hyf0
body: It sounds indeed weird, I just removed it.
created_at: 2025-05-16T18:43:10Z
author_association: MEMBER

PR comments:
user: login: hyf0
created_at: 2025-05-16T13:56:47Z
body: * **#4574** <a href="https://app.graphite.dev/github/pr/rolldown/rolldown/4574?utm_source=stack-comment-icon" target="_blank"><img src="https://static.graphite.dev/graphite-32x32-black.png" alt="Graphite" width="10px" height="10px"/></a> 👈 <a href="https://app.graphite.dev/github/pr/rolldown/rolldown/4574?utm_source=stack-comment-view-in-graphite" target="_blank">(View in Graphite)</a>
* `main`


<h2></h2>

This stack of pull requests is managed by <a href="https://graphite.dev?utm-source=stack-comment"><b>Graphite</b></a>. Learn more about <a href="https://stacking.dev/?utm_source=stack-comment">stacking</a>.
<!-- Current dependencies on/for this PR: -->
author_association: MEMBER
user: login: netlify[bot]
created_at: 2025-05-16T13:57:17Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *rolldown-rs* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 16e534ab112f4b4557e40049df40ac74989d306d |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/projects/rolldown-rs/deploys/68277872dc0f2a00086587ad |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-4574--rolldown-rs.netlify.app](https://deploy-preview-4574--rolldown-rs.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTQ1NzQtLXJvbGxkb3duLXJzLm5ldGxpZnkuYXBwIn0.Jl58BwH62OgEjdyiPbyovo5oSGQPp10EWmvrLntllHc)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [rolldown-rs Preview](https://deploy-preview-4574--rolldown-rs.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify project configuration](https://app.netlify.com/projects/rolldown-rs/configuration/notifications#deploy-notifications)._
author_association: NONE
user: login: pi0
created_at: 2025-05-16T14:17:55Z
body: Thank you for initiating docs for clarity dear @hyf0 ❤️ 

Would it be good idea to mention https://github.com/rolldown/rolldown/issues/4575 as a current limitation perhaps until fixed?
author_association: NONE
user: login: hyf0
created_at: 2025-05-16T14:27:07Z
body: > Thank you for initiating docs for clarity dear @hyf0 ❤️
> 
> Would it be good idea to mention #4575 as a current limitation perhaps until fixed?

With hoisting, you can't keep the original on-demand excution semantic of `require`. So I wouldn't say the current behavior of rolldown is bug, that's also how esbuild did it.

Despite that, I guess we could come up with some solutions.
author_association: MEMBER
user: login: pi0
created_at: 2025-05-16T14:52:45Z
body: Thanks for answer.  (from https://github.com/rolldown/rolldown/issues/4575#issuecomment-2886949753) i think it would be worth to still note two things:
- `platform: node` is required for `require("node:")` being properly hoisted
- behavior is close to esbuild rather than rollup/cjs for now at least


author_association: NONE