Skip to content

Conversation

@hyf0
Copy link
Member

@hyf0 hyf0 commented Nov 8, 2025

Several reasons I want to introduce BundleMode:

A mental model footgun

BundleMode is used to emphasize there are 3 states when we integrate incremental build into rolldown.

For exmaple, for watch mode:

  1. if incremental_build: false, file changes should result to full rebuild every time
  2. if incremental_build: true, the first build is not a plain full build, it needs to be done with saving data into cache.
  3. if incremental_build: true, files changes should result to incremental rebuilds.

The footgun:

let is_incremental = bundler.options.experimental.is_incremental_build_enabled();
let start_time = Instant::now();
self.emitter.emit(WatcherEvent::Event(BundleEvent::BundleStart))?;
bundler.reset_closed_for_watch_mode();
bundler.plugin_driver.clear();
let result = {
let scan_mode = if is_incremental && !changed_files.is_empty() {
rolldown_common::ScanMode::Partial(changed_files.to_vec())
} else {
rolldown_common::ScanMode::Full
};

When incremental_build: false, the watch mode will use ScanMode:Full in order to trigger full build, which seems to satisfy the above 1., but it's not!

  1. if incremental_build: false, file changes should result to full rebuild every time

watch mode triggers a full build with extra unnecessary overhead of saving cache! And for each file change.

This PR doesn't fix the behavior but make it clear to be recognized. See https://github.com/rolldown/rolldown/pull/6894/files#r2507176596.

Reduce bug prone patterns:

Misc

Inside of the Bundle logic, we're using ScanMode + is_incremental_build_enabled to achieve the same effect as BundleMode. I restrict the refactor area of this PR, so they are not touched.

The previous design for the bundler hides many issues and cause advance features vulnerable. #6891 (comment) probably won't be the last issue.

Copy link
Member Author

hyf0 commented Nov 8, 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.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions
Copy link
Contributor

github-actions bot commented Nov 8, 2025

Benchmarks Rust

  • target: 11-09-refactor_rust_rename_bundlecontext_to_bundlehandle_(179baf3)
  • pr: 11-09-feat_rust_use_bundlemode_to_handle_incremental_build_exhaustively(c099a6e)
group                                                        pr                                     target
-----                                                        --                                     ------
bundle/bundle@multi-duplicated-top-level-symbol              1.01     65.4±3.08ms        ? ?/sec    1.00     65.0±3.50ms        ? ?/sec
bundle/bundle@multi-duplicated-top-level-symbol-sourcemap    1.00     70.4±2.11ms        ? ?/sec    1.00     70.6±2.00ms        ? ?/sec
bundle/bundle@rome_ts                                        1.01    109.0±2.84ms        ? ?/sec    1.00    108.2±1.85ms        ? ?/sec
bundle/bundle@rome_ts-sourcemap                              1.00    121.1±2.04ms        ? ?/sec    1.00    120.5±1.55ms        ? ?/sec
bundle/bundle@threejs                                        1.00     40.0±3.24ms        ? ?/sec    1.00     40.1±1.26ms        ? ?/sec
bundle/bundle@threejs-sourcemap                              1.00     43.2±0.96ms        ? ?/sec    1.00     43.2±1.18ms        ? ?/sec
bundle/bundle@threejs10x                                     1.00    396.9±5.37ms        ? ?/sec    1.02   403.0±10.76ms        ? ?/sec
bundle/bundle@threejs10x-sourcemap                           1.02    462.0±8.73ms        ? ?/sec    1.00    454.3±5.26ms        ? ?/sec
scan/scan@rome_ts                                            1.04     89.0±2.72ms        ? ?/sec    1.00     86.0±1.29ms        ? ?/sec
scan/scan@threejs                                            1.00     29.7±1.27ms        ? ?/sec    1.00     29.5±2.02ms        ? ?/sec
scan/scan@threejs10x                                         1.00    300.9±4.32ms        ? ?/sec    1.00    301.2±3.32ms        ? ?/sec

@hyf0 hyf0 marked this pull request as ready for review November 8, 2025 21:26
@hyf0 hyf0 force-pushed the 11-09-feat_rust_use_bundlemode_to_handle_incremental_build_exhaustively branch from e529b13 to 0039e6a Compare November 8, 2025 21:37
@hyf0 hyf0 force-pushed the 11-09-feat_rust_use_bundlemode_to_handle_incremental_build_exhaustively branch from 0039e6a to 016aac6 Compare November 9, 2025 08:16
@hyf0 hyf0 force-pushed the 11-09-feat_rust_use_bundlemode_to_handle_incremental_build_exhaustively branch from 016aac6 to 73da1a0 Compare November 9, 2025 08:21
@graphite-app
Copy link
Contributor

graphite-app bot commented Nov 9, 2025

Merge activity

…#6894)

Several reasons I want to introduce `BundleMode`:

## A mental model footgun

`BundleMode` is used to emphasize there are 3 states when we integrate incremental build into rolldown.

For exmaple, for watch mode:

1. if `incremental_build: false`, file changes should result to full rebuild every time
2. if `incremental_build: true`, the first build is not a plain full build, it needs to be done with saving data into cache.
3. if `incremental_build: true`, files changes should result to incremental rebuilds.

The footgun:

https://github.com/rolldown/rolldown/blob/c19666e8309871688d997729d32f49108604249a/crates/rolldown/src/watch/watcher_task.rs#L53-L67

When `incremental_build: false`, the watch mode will use `ScanMode:Full` in order to trigger full build, which seems to satisfy the above `1.`, but it's not!

> 1. if `incremental_build: false`, file changes should result to full rebuild every time

`watch mode` triggers a full build with extra unnecessary overhead of saving cache! And for each file change.

This PR doesn't fix the behavior but make it clear to be recognized. See https://github.com/rolldown/rolldown/pull/6894/files#r2507176596.

## Reduce bug prone patterns:

- https://github.com/rolldown/rolldown/pull/6894/files#r2507167806
- https://github.com/rolldown/rolldown/pull/6894/files#r2507174518

## Misc

Inside of the `Bundle` logic, we're using `ScanMode` + `is_incremental_build_enabled` to achieve the same effect as `BundleMode`. I restrict the refactor area of this PR, so they are not touched.

The previous design for the bundler hides many issues and cause advance features vulnerable. #6891 (comment) probably won't be the last issue.
@graphite-app graphite-app bot force-pushed the 11-09-refactor_rust_rename_bundlecontext_to_bundlehandle_ branch from 1524d13 to 179baf3 Compare November 9, 2025 09:54
@graphite-app graphite-app bot force-pushed the 11-09-feat_rust_use_bundlemode_to_handle_incremental_build_exhaustively branch from 73da1a0 to c099a6e Compare November 9, 2025 09:55
Base automatically changed from 11-09-refactor_rust_rename_bundlecontext_to_bundlehandle_ to main November 9, 2025 10:08
@graphite-app graphite-app bot merged commit c099a6e into main Nov 9, 2025
24 checks passed
@graphite-app graphite-app bot deleted the 11-09-feat_rust_use_bundlemode_to_handle_incremental_build_exhaustively branch November 9, 2025 10:12
shulaoda added a commit that referenced this pull request Nov 10, 2025
## [1.0.0-beta.48] - 2025-11-10

:boom: Breaking Changes

- `this.emitFile` now respects `chunkFileNames` for chunk type
```js
// rolldown.config.js
export default {
  output: {
    chunkFileNames: 'chunks/[name]-[hash].js'
  }
}

// In plugin
this.emitFile({
  type: 'chunk',
  id: './my-module.js'
});

// Before: Output might not follow chunkFileNames pattern
// After: Output follows 'chunks/[name]-[hash].js' pattern
```

- Deprecated top-level options removed
  - `define` → `transform.define`
  - `inject` → `transform.inject`
  - `dropLabels` → `transform.dropLabels`
  - `keepNames` → `output.keepNames`
  - `profilerNames` → `output.generatedCode.profilerNames`

- Stable plugins moved from experimental
```js
// Before
import { replacePlugin, esmExternalRequirePlugin } from 'rolldown/experimental';

// After
import { replacePlugin, esmExternalRequirePlugin } from 'rolldown/plugins';
```

- `RolldownBuild#scan` is removed, now only available from `rolldown/experimental`
```js
// Before: scan was a method on RolldownBuild
const build = await rolldown(config);
await build.scan();

// After: import scan from rolldown/experimental
import { scan } from 'rolldown/experimental';
await scan(config);
```

### 💥 BREAKING CHANGES

- `this.emitFile` does not respect `chunkFileNames` (#6868) by @Copilot
- remove deprecated top-level `dropLabels` option (#6915) by @sapphi-red
- remove deprecated top-level `keepNames` option (#6914) by @sapphi-red
- remove deprecated top-level `profilerNames` option (#6913) by @sapphi-red
- remove deprecated top-level `define` and `inject` options (#6912) by @sapphi-red
- move stable plugins from experimental to `rolldown/plugins` (#6303) by @shulaoda
- node: remove experimental `RolldownBuild#scan`, only expose it from `rolldown/experimental` (#6889) by @hyf0

### 🚀 Features

- add side-effect detection for global constructors with primitive arguments (#6898) by @IWANABETHATGUY
- rust: use `BundleMode` to handle incremental build exhaustively (#6894) by @hyf0
- detect side-effect-free global function calls (#6897) by @IWANABETHATGUY
- expose `parseSync` / `parseAsync` function (#6866) by @sapphi-red
- skip `__toESM` helper when only named imports are used from CJS modules (#6850) by @Copilot
- rolldown_binding: expose `htmlInlineProxyPlugin` (#6856) by @shulaoda
- rolldown_plugin_html_inline_proxy: align `load` hook logic (#6855) by @shulaoda
- rolldown_plugin_html_inline_proxy: align `resolveId` hook logic (#6854) by @shulaoda
- rolldown_plugin_html_inline_proxy: initialize (#6853) by @shulaoda

### 🐛 Bug Fixes

- cli: support nested options in CLI properly (#6911) by @sapphi-red
- debug: ensure injecting `hook_resolve_id_trigger` correctly (#6908) by @hyf0
- use chunk-specific exports for entry module export detection (#6904) by @IWANABETHATGUY
- debug: ensure build get injected and add tests (#6896) by @hyf0
- error: return friendly error for bundler already closed scenario (#6878) by @hyf0
- improve dynamic entry processing with iterative approach (#6869) by @IWANABETHATGUY
- handle tsconfig option resolve error (#6871) by @sapphi-red
- handle error when creating output chunk directories (#6870) by @sapphi-red
- node: `NormalizedOutputOptionsImpl` and `NormalizedInputOptionsImpl` enumerable (#6861) by @hyf0
- node: keys of `RolldownOutput` should be enumerable (#6852) by @Copilot

### 🚜 Refactor

- rust: rename `BundleContext` to `BundleHandle` (#6893) by @hyf0
- rust: rename `build_span` to `bundle_span` (#6892) by @hyf0
- rust: introduce `PluginDriverFactory` to manage creation of `PluginDriver` (#6891) by @hyf0
- crates/rolldown_binding: remove useless `BindingBundlerImpl` (#6888) by @hyf0
- crates/rolldown_binding: rename `Bundler` to `ClassicBundler` and clarify the purpose (#6887) by @hyf0
- rust: rename `BuildFactory/Build` to `BundleFactory/Bundle` (#6886) by @hyf0
- rust: tweak incremental build related methods of `Bundler` (#6884) by @hyf0
- rust: manage build via `BuildFactory` for `Bundler` (#6883) by @hyf0
- node: implement a new `Bundler` that satisfy the usage of `RolldownBuild` (#6877) by @hyf0
- node: remove useless `nonEnumerable` decorator (#6862) by @hyf0

### 📚 Documentation

- add documentation for native replace plugin (#6315) by @shulaoda
- in-depth/directives: expand description of how directives are handled (#6882) by @sapphi-red
- in-depth/bundling-cjs: clarify the condition of `default` export interop (#6875) by @sapphi-red
- add troubleshooting section for `this` in exported functions (#6865) by @sapphi-red
- update prebuilt binaries list based on Node 24 platform support tier (#6864) by @sapphi-red
- remove unsupported [ext] placeholder from entryFileNames and chunkFileNames (#6859) by @Copilot

### ⚡ Performance

- rolldown: improve sourcemap chain processing (#6858) by @Brooooooklyn

### 🧪 Testing

- add test case for issue #6881 with scientific notation (#6906) by @IWANABETHATGUY

### ⚙️ Miscellaneous Tasks

- deps: update npm packages (#6918) by @renovate[bot]
- deps: update dependency rolldown-plugin-dts to v0.17.5 (#6917) by @renovate[bot]
- deps: lock file maintenance (#6907) by @renovate[bot]
- deps: update rust crates (#6905) by @renovate[bot]
- deps: update npm packages (#6903) by @renovate[bot]
- deps: update github-actions (#6902) by @renovate[bot]
- deps: update `oxc_resolver` and `oxc_resolver_napi` (#6901) by @shulaoda
- deps: update dependency rolldown-plugin-dts to v0.17.4 (#6895) by @renovate[bot]
- deps: update dependency tsdown to v0.16.1 (#6885) by @renovate[bot]
- deps: upgrade napi to remove linker args that skip missing symbols (#6867) by @Boshen

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.

3 participants