Skip to content

Conversation

@aprosail
Copy link
Contributor

@aprosail aprosail commented Oct 12, 2025

Provide an output option (OutputOptions) to empty output directory (emptyOutDir): When both emptyOutDir and dir in the output options are configured, it will empty the configured output directory (remove all files and directories recursively inside). You may configure like this:

import {defineConfig} from "rolldown"

export default defineConfig({
  ...,
  output: {
    dir: './dist',
    cleanDir: true,
  }
})

Inspired by vite.

@graphite-app
Copy link
Contributor

graphite-app bot commented Oct 12, 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.

@aprosail aprosail marked this pull request as draft October 12, 2025 06:36
@aprosail
Copy link
Contributor Author

aprosail commented Oct 12, 2025

Need to resolve security risks: #6485 (comment)
Use such API:

export interface OutputOptions {
  ...
  /**
   * Empty output directory before writing.
   *
   * - `'force'`: clean the out dir even if it's not inside project root.
   * - `true`: only clean the out dir inside project root.
   * - `false` or `undefined`: don't do anything about the out dir.
   */
  emptyOutDir?: boolean | 'force';
}

- `'force'`: clean the out dir even if it's not inside project root.
- `true`: only clean the out dir inside project root (cwd).
- `false` or `undefined`: don't do anything about the out dir.
@aprosail aprosail marked this pull request as ready for review October 12, 2025 09:32
Optimize ts API to satisfy current code style.

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
Signed-off-by: James Aprosail <[email protected]>
@aprosail
Copy link
Contributor Author

There might still something to improve: Currently it uses cwd rather than the root option configured inside input.

@aprosail aprosail marked this pull request as draft October 15, 2025 05:49
@aprosail
Copy link
Contributor Author

Currently I don't have mode free time to focus on such PR (diving down into rolldown's source code is rather difficult for a beginner like me)... I've made a plugin to satisfy such demand.

Copy link
Member

@hyf0 hyf0 left a comment

Choose a reason for hiding this comment

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

Could you also change the option name to cleanDir?

@hyf0 hyf0 self-assigned this Oct 15, 2025
@sapphi-red sapphi-red linked an issue Oct 15, 2025 that may be closed by this pull request
@aprosail aprosail requested review from hyf0 and sapphi-red October 15, 2025 11:37
@aprosail aprosail marked this pull request as ready for review October 15, 2025 11:38
@aprosail
Copy link
Contributor Author

Could you also change the option name to cleanDir?

Done.

@hyf0 hyf0 changed the title feat(rolldown): empty out dir feat(rolldown): support output.clearDir to clean up dir before build Oct 15, 2025
@aprosail aprosail requested a review from hyf0 October 16, 2025 09:10
@aprosail
Copy link
Contributor Author

aprosail commented Oct 16, 2025

Changes had been compressed from 500+ lines into 150+ lines 😂

@hyf0
Copy link
Member

hyf0 commented Oct 16, 2025

Could you run just lint locally and fix related errors.

Replace match statement on boolean expression with if/else to comply with clippy::match_bool lint.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@netlify
Copy link

netlify bot commented Oct 16, 2025

Deploy Preview for rolldown-rs canceled.

Name Link
🔨 Latest commit ef87650
🔍 Latest deploy log https://app.netlify.com/projects/rolldown-rs/deploys/68f0cb6eab9d270008c9f684

Copy link
Member

@hyf0 hyf0 left a comment

Choose a reason for hiding this comment

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

Thank you for contributing such an amazing feature!

@hyf0 hyf0 enabled auto-merge (squash) October 16, 2025 10:41
@hyf0 hyf0 merged commit 64825a8 into rolldown:main Oct 16, 2025
28 checks passed
@aprosail
Copy link
Contributor Author

Thank you for so detailed guidance to a newcomer like me contributing to Rolldown for the first time 😄

@aprosail
Copy link
Contributor Author

Could you run just lint locally and fix related errors.

This error might be something about timeout configuration: sometimes it failed, but mostly it will pass. Sorry that I still cannot have a deep understand about the workspace code workflow structures, that maybe I cannot resolve such bug yet...

shulaoda added a commit that referenced this pull request Oct 20, 2025
## [1.0.0-beta.44] - 2025-10-20

:boom: Breaking Changes
- Top-level `jsx` removed, use `transform.jsx` instead
- `output.minifyInternalExports` now enabled by default

:dart: Enhanced Tree-Shaking
- JSON default imports are now tree-shakeable
```js
// config.json
{
"apiUrl": "[https://api.example.com](https://api.example.com/)",
"timeout": 5000,
"retries": 3,
"debug": false
}

// main.js
// Before: Entire JSON was included in bundle
import config from './config.json';
console.log(config.apiUrl);

// After: Only the used property is included
// Bundle now contains just the apiUrl value
```
- Built-in typed array constructors (`new Uint8Array()`, `new Int32Array()`, etc.) marked as pure

:gear: Configuration Improvements
- Support `output.cleanDir` option
- Deprecated some top-level  options
  - `define` → `transform.define`
  - `inject` → `transform.inject`
  - `dropLabels` → `transform.dropLabels`
  - `keepNames` → `output.keepNames`
  - `profilerNames` → `output.generatedCode.profilerNames`

### 💥 BREAKING CHANGES

- enable `output.minifyInternalExports` for `format: 'es'` or `minify: true` (#6594) by @sapphi-red
- node/options: remove `InputOptions.jsx`, prefer `transform.jsx` always (#6548) by @hyf0

### 🚀 Features

- support jsx preset for `transform.jsx` (#6630) by @shulaoda
- rolldown_plugin_vite_html: align `inject_to_body` function (#6622) by @shulaoda
- rolldown_vite_css_post_plugin: tweak `is_legacy` field (#6620) by @shulaoda
- native-plugin: expose `viteHtmlPlugin` (#6609) by @shulaoda
- native-plugin: expose `viteCSSPostPlugin` (#6606) by @shulaoda
- builtin-plugin: expose `viteCSSPlugin` (#6605) by @shulaoda
- rolldown_plugin_vite_html: complete plugin binding (#6604) by @shulaoda
- rolldown_plugin_vite_css: support plugin binding (#6598) by @shulaoda
- add TypedArray constructors to side-effect free globals (#6592) by @IWANABETHATGUY
- rolldown_plugin_vite_html: align process src attr logic (#6572) by @shulaoda
- rolldown: support `output.clearDir` to clean up `dir` before build (#6486) by @aprosail
- dev: return `RolldownOutput` instead of `BindingOutputs` from `onOutput` (#6563) by @sapphi-red
- rolldown_plugin_vite_html: align process srcset logic (#6560) by @shulaoda
- rolldown_plugin_vite_html: align process src function (#6559) by @shulaoda
- rolldown_plugin_vite_html: align parse secset function (#6558) by @shulaoda
- node/options: deprecate `dropLabels` and add `transform.dropLabels` (#6557) by @hyf0
- node/options: deprecate `keepNames` and add `output.keepNames` (#6556) by @hyf0
- node/options: deprecate `profilerNames` and add `output.generatedCode.profilerNames` (#6555) by @hyf0
- node/options: deprecate top level `inject` and `define` and add `transform.define`, `transform.inject` (#6544) by @hyf0
- rolldown_plugin_vite_html: initialize attributes handle logic (#6543) by @shulaoda
- rolldown: oxc v0.95.0 (#6541) by @Boshen
- rolldown_plugin_vite_html: align inject css logic (#6528) by @shulaoda
- rolldown_plugin_vite_html: align modulepreload inject logic (#6526) by @shulaoda
- rolldown_plugin_vite_html: align css bundle output inject logic (#6524) by @shulaoda
- rolldown_plugin_vite_html: support inject_to_head function (#6522) by @shulaoda
- rolldown_plugin_vite_html: align emit and delete logic (#6521) by @shulaoda
- rolldown_vite_html: align handle html asset url logic (#6518) by @shulaoda
- add an example how to transform code and generate sourcemap with `experimental.nativeMagicString` (#6514) by @IWANABETHATGUY
- rolldown_plugin_vite_html: align handle inline css logic (#6517) by @shulaoda

### 🐛 Bug Fixes

- native-plugin: use correct config for assetPlugin (#6638) by @shulaoda
- no_color not being respected on reporter (#6615) by @H4ad
- improve tree shaking for JSON default imports (#6626) by @IWANABETHATGUY
- cjs treeshaking removes used code (#6597) by @IWANABETHATGUY
- plugin/vite-resolve: return `package_json_path` from resolveId hook (#6434) by @sapphi-red
- dev: `should have idx` error happens with `deferSyncScanData` + incremental rebuild (#6568) by @sapphi-red
- rolldown: sync scoping properly in pre_process_ecma_ast (#6537) by @Boshen
- process CJS tree shaking bailout modules before eliminating unused dynamic entries (#6549) by @IWANABETHATGUY
- rust/dev: error of `bundler.scan`doesn't get handled (#6547) by @Copilot
- inline only self referenced json module properties (#6519) by @IWANABETHATGUY
- avoid processing native MagicString sourcemap when `sourcemap` is disabled (#6510) by @IWANABETHATGUY

### 🚜 Refactor

- rolldown_binding: unify `BindingAssetInlineLimit` (#6625) by @shulaoda
- rolldown_binding: tweak `BindingAssetPluginConfig` (#6624) by @shulaoda
- tweak `viteCSSPlugin` config (#6610) by @shulaoda
- rust/dev: remove unnecessary bundler cache management of dev engine (#6576) by @hyf0
- rust/dev: refactor `TaskInput` into enum to reduce invalid states (#6575) by @hyf0
- improve the error message of `ScanStageCache#merge` (#6564) by @IWANABETHATGUY
- rust/dev: use `client_id` to check if module is executed for test environment (#6566) by @hyf0
- rust/binding: mark napi object that won't be received from js side (#6531) by @hyf0
- rust/binding: mark napi object struct that won't be passed to js explicitly (#6525) by @hyf0
- rust/binding: use `&str` or `JsString` to avoid unnecessary clone (#6523) by @hyf0
- rust/binding: use `&str` to avoid unnecessary clone (#6520) by @hyf0
- rust: replace unwrap with expect for better error handling in message sending (#6509) by @hyf0

### 📚 Documentation

- add redirect from `/guide/in-depth/` to `/in-depth/` (#6554) by @Copilot
- guide: polish notable features page (#6535) by @sapphi-red
- builtin-plugins: add replace plugin docs (#6534) by @sapphi-red
- add `Additional Thanks` in `acknowledgements` page (#6507) by @hyf0
- polish doc for `experimental.nativeMagicString` (#6504) by @IWANABETHATGUY

### ⚡ Performance

- cli: advance `createTokioRuntime` (#6618) by @hyf0
- rolldown: reduce unnecessary Vec allocate (#6601) by @Brooooooklyn
- rolldown: make derived assets generation more parallel (#6600) by @Brooooooklyn
- rolldown: make chunks generation more parallel (#6599) by @Brooooooklyn
- rolldown_utils: only allocate HASH_PLACEHOLDER_LEFT once (#6595) by @Brooooooklyn
- rolldown: use oxc-resolver with simd-json (#6591) by @Boshen
- rolldown: avoid allocate the sourcemap sources on non Windows os (#6590) by @Brooooooklyn
- resolver: use optimized fs.read_to_string impl (#6589) by @Brooooooklyn
- deps: upgrade json-escape-simd and add simdutf8 dependency (#6579) by @Brooooooklyn
- rust/dev: remove unncessary clone for `ClientInput` (#6565) by @hyf0
- visit ast in `cross_module_optimization` stage parallel (#6552) by @IWANABETHATGUY

### 🧪 Testing

- json module prop conflict with normal module declaration binding (#6540) by @IWANABETHATGUY
- vite-tests: fix rolldown overrides (#6532) by @sapphi-red

### ⚙️ Miscellaneous Tasks

- remove string conversion warning in `replacePlugin` to compatible with rollup (#6639) by @IWANABETHATGUY
- deps: update dependency dprint-json to v0.21.0 (#6637) by @renovate[bot]
- deps: lock file maintenance (#6635) by @renovate[bot]
- deps: lock file maintenance npm packages (#6632) by @renovate[bot]
- node: use built-in styleText (#6340) by @dumbmatter
- deps: update actions/setup-node action to v6 (#6629) by @renovate[bot]
- deps: update github-actions (#6628) by @renovate[bot]
- deps: update dependency dprint-markdown to v0.20.0 (#6627) by @renovate[bot]
- Switch color dependencies (ansis and picocolors) to built-in styleText (#6619) by @IWANABETHATGUY
- native-plugin: tweak assetPlugin (#6623) by @shulaoda
- remove deprecated warning in `build.ts` (#6621) by @IWANABETHATGUY
- deps: update dependency tsdown to v0.15.8 (#6617) by @renovate[bot]
- rolldown_binding: rename vite css plugin config field (#6611) by @shulaoda
- add colored deprecation warnings for top-level options (#6612) by @IWANABETHATGUY
- rust: make `cargo publish --workspace` work (#6287) by @Boshen
- deps: update dependency rolldown-plugin-dts to v0.16.12 (#6608) by @renovate[bot]
- test: add `opposite_minify_internal_exports` and remove `minify_internal_exports` extend test (#6596) by @hyf0
- test: render snapshot for build and dev independently (#6584) by @hyf0
- test: split test logic for normal build and dev engine (#6580) by @hyf0
- fix rolldown build self warning (#6578) by @IWANABETHATGUY
- tweak `BindingBuiltinPluginName` (#6574) by @shulaoda
- rust: use forked `notify` crate (#6573) by @Boshen
- add `.len()` and `.is_empty()` to `HybridIndexVec` (#6570) by @sapphi-red
- ai: improve `AGENT.md` with verified prompts (#6533) by @hyf0
- clippy: set `too-many-lines-threshold` to 200 (#6530) by @shulaoda
- clean up examples/basic-vue (#6515) by @IWANABETHATGUY
- deps: update dependency tsdown to v0.15.7 (#6512) by @renovate[bot]
- add shulaoda to release PR assignees (#6505) by @shulaoda

### ❤️ New Contributors

* @dumbmatter made their first contribution in [#6340](#6340)
* @H4ad made their first contribution in [#6615](#6615)
* @aprosail made their first contribution in [#6486](#6486)

Co-authored-by: shulaoda <[email protected]>
@lazka
Copy link

lazka commented Oct 20, 2025

It seems this is being run after generateBundle and before writeBundle (based on my testing), which results in all files emitted by plugins before writeBundle being deleted too. Is that on purpose?

@sapphi-red
Copy link
Member

That is an expected behavior for me.

@hyf0
Copy link
Member

hyf0 commented Oct 20, 2025

It seems this is being run after generateBundle and before writeBundle (based on my testing), which results in all files emitted by plugins before writeBundle being deleted too. Is that on purpose?

If this's common community plugin pattern, It's ok to advance the timing.

@lazka
Copy link

lazka commented Oct 20, 2025

I've only had a problem with rollup-plugin-copy, but that allows setting target hook to run in, so easily worked around. I noticed that other plugins that create files in generateBundle via emitFile seem to work fine too. So no big problem for me at least, I was just curious since vite seems to delete in renderStart.

@lazka
Copy link

lazka commented Oct 20, 2025

Ah, @rollup/plugin-url is broken too. It copies files manually in generateBundle.

@aprosail
Copy link
Contributor Author

aprosail commented Oct 21, 2025

@hyf0 #6647 how about a fix like this?

This PR already clean dir before the generateBundle hook,
that all files generated inside the generateBundle hook will not be deleted.

@aprosail aprosail deleted the feat/empty-outdir branch October 21, 2025 08:47
hyf0 pushed a commit that referenced this pull request Oct 24, 2025
#6647)

See:
#6486 (comment)

Advance the "clean dir" code before the `bundle_up` is called inside the
`bundle_write` function: `fn generate_bundle` is called in `fn
bundle_up`, and the "clean dir" code is advanced before the call of `fn
bundle_up` in `fn bundle_write`.

- [x] Fix the source code and pass all current tests.
- [x] Create new behavior tests about the `generateBundle` and
`writeBundle` hook.

I also fix another bug about testing timeout configuration, which
prevents the previous random failure when testing spent more time than
the default 20s (20_000ms).
delino bot pushed a commit to hyf0/rolldown that referenced this pull request Oct 24, 2025
Add detailed JSDoc documentation for the `cleanDir` option in OutputOptions,
including:
- Clear description of functionality
- Timing behavior explanation (cleans before generateBundle hook)
- Safety warnings about recursive directory deletion
- Usage example

This documentation follows the established patterns in the codebase and
will be automatically included in the generated documentation.

Related PRs:
- rolldown#6486: Initial implementation of output.cleanDir
- rolldown#6647: Fixed timing to execute before bundle_up is called

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
graphite-app bot pushed a commit that referenced this pull request Oct 24, 2025
…ity (#6682)

## Summary

Updates the `output.cleanDir` documentation to clarify when directory cleanup occurs and how it affects plugin-generated files.

## Context

PR #6486 introduced the `output.cleanDir` option to clean the output directory before emitting output. However, PR #6647 fixed an important timing issue where the cleanup was initially happening between `generateBundle` and `writeBundle` hooks, causing files created by plugins (like `@rollup/plugin-url` and `rollup-plugin-copy`) to be deleted.

The fix moved the cleanup to occur **before** the `generateBundle` hook, ensuring all plugin-generated files are preserved. This documentation update clarifies this critical behavior for users.

## Changes

### Updated main description
Changed from the vague "before `writeBundle` hooks are called" to explicitly state:
> The cleanup happens before the `generateBundle` hook is called. This ensures that files created by plugins during `generateBundle` or `writeBundle` hooks are preserved.

### Added "In-depth" section
Added a new section explaining the timing importance:
- The cleanup occurs **before** the `generateBundle` hook
- Files created by plugins in `generateBundle` or `writeBundle` hooks are **not** deleted
- Guidance for advanced use cases with multiple outputs

## Why this matters

Many plugins create files during the `generateBundle` hook (e.g., copying assets, generating additional files). Without clear documentation, users might be confused about why their plugin-generated files are or aren't being cleaned, leading to unexpected behavior in their build process.

This update provides clarity on the execution order and helps users understand the plugin compatibility guarantees.

## References

- Original feature: #6486
- Timing fix: #6647
- Issue comment: #6486 (comment)

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

<details>

<summary>Original prompt</summary>

> Read #6486 (comment) and #6647. Use concise words to describe the behavior and update related docs. Just point out new added behavior that #6647 emphasized and upadate the description in `/docs/options`

</details>

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

✨ Let Copilot coding agent [set things up for you](https://github.com/rolldown/rolldown/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.
@hyf0
Copy link
Member

hyf0 commented Oct 24, 2025

@aprosail Thank you for your work. @lazka we had changed the behavior and doc. We'll release a new version Monday including that change.

IWANABETHATGUY pushed a commit that referenced this pull request Oct 27, 2025
#6647)

See:
#6486 (comment)

Advance the "clean dir" code before the `bundle_up` is called inside the
`bundle_write` function: `fn generate_bundle` is called in `fn
bundle_up`, and the "clean dir" code is advanced before the call of `fn
bundle_up` in `fn bundle_write`.

- [x] Fix the source code and pass all current tests.
- [x] Create new behavior tests about the `generateBundle` and
`writeBundle` hook.

I also fix another bug about testing timeout configuration, which
prevents the previous random failure when testing spent more time than
the default 20s (20_000ms).
IWANABETHATGUY pushed a commit that referenced this pull request Oct 27, 2025
…ity (#6682)

## Summary

Updates the `output.cleanDir` documentation to clarify when directory cleanup occurs and how it affects plugin-generated files.

## Context

PR #6486 introduced the `output.cleanDir` option to clean the output directory before emitting output. However, PR #6647 fixed an important timing issue where the cleanup was initially happening between `generateBundle` and `writeBundle` hooks, causing files created by plugins (like `@rollup/plugin-url` and `rollup-plugin-copy`) to be deleted.

The fix moved the cleanup to occur **before** the `generateBundle` hook, ensuring all plugin-generated files are preserved. This documentation update clarifies this critical behavior for users.

## Changes

### Updated main description
Changed from the vague "before `writeBundle` hooks are called" to explicitly state:
> The cleanup happens before the `generateBundle` hook is called. This ensures that files created by plugins during `generateBundle` or `writeBundle` hooks are preserved.

### Added "In-depth" section
Added a new section explaining the timing importance:
- The cleanup occurs **before** the `generateBundle` hook
- Files created by plugins in `generateBundle` or `writeBundle` hooks are **not** deleted
- Guidance for advanced use cases with multiple outputs

## Why this matters

Many plugins create files during the `generateBundle` hook (e.g., copying assets, generating additional files). Without clear documentation, users might be confused about why their plugin-generated files are or aren't being cleaned, leading to unexpected behavior in their build process.

This update provides clarity on the execution order and helps users understand the plugin compatibility guarantees.

## References

- Original feature: #6486
- Timing fix: #6647
- Issue comment: #6486 (comment)

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

<details>

<summary>Original prompt</summary>

> Read #6486 (comment) and #6647. Use concise words to describe the behavior and update related docs. Just point out new added behavior that #6647 emphasized and upadate the description in `/docs/options`

</details>

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

✨ Let Copilot coding agent [set things up for you](https://github.com/rolldown/rolldown/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.
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.

[Feature Request]: Empty Output Directory

4 participants