docs(minifier): explain what dce mode means (#23994)

Comments only. `MinifierState::dce` is easy to misread as "the pass that removes dead code", but it selects tree-shaking-only mode (rolldown's per-module tree-shaking) versus full minify. This documents it on the `dce` field, the two `Compressor` entry points (`build` / `dead_code_elimination`), and the `exit_expression` branch that splits the two modes — including why the constant folds must stay on in tree-shaking mode.
This commit is contained in:
Dunqing committed 2026-07-01 04:01:59 +00:00
1 parent 60e716087d
commit e4c30e6d19
3 files changed
+28 -1

No files matched your search

+6
View File
@@ -17,6 +17,9 @@ impl<'a> Compressor<'a> {
Self { allocator }
}
/// Full minify: removes dead code and shrinks the output (`dce = false`).
/// For tree-shaking only, see [`Self::dead_code_elimination`] and the
/// `MinifierState::dce` docs.
pub fn build(self, program: &mut Program<'a>, options: CompressOptions) {
let scoping = SemanticBuilder::new().build(program).semantic.into_scoping();
self.build_with_scoping(program, scoping, options);
@@ -63,6 +66,9 @@ impl<'a> Compressor<'a> {
Self::run_in_loop(max_iterations, program, &mut ctx)
}
/// Tree-shaking only: removes dead and unused code, but does not shrink the
/// output like [`Self::build`] (`dce = true`). Rolldown runs this on its
/// own. See the `MinifierState::dce` docs.
pub fn dead_code_elimination(self, program: &mut Program<'a>, options: CompressOptions) -> u8 {
let scoping = SemanticBuilder::new().build(program).semantic.into_scoping();
self.dead_code_elimination_with_scoping(program, scoping, options)
+7
View File
@@ -606,6 +606,13 @@ impl<'a> Traverse<'a> for PeepholeOptimizations {
}
fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) {
// Tree-shaking mode: fewer passes than full minify below. Only the ones
// that remove code, plus the constant folds those removals need. The
// folds stay on because the removal passes don't evaluate compound
// conditions themselves: `if ('production' === 'production')` must fold
// to `true` before the dead branch can be dropped. Passes that only
// shrink code (`substitute_*`, `minimize_*`) are left out. See the `dce`
// docs in `state.rs`.
if ctx.state.dce {
match expr {
Expression::TemplateLiteral(t) => {
+15 -1
View File
@@ -50,7 +50,21 @@ pub struct MinifierState<'a> {
pub options: CompressOptions,
/// When true, only run dead code elimination passes (subset of full peephole optimizations).
/// Two modes: tree-shaking only (`true`), or full minify (`false`).
/// `Compressor::dead_code_elimination` uses `true`; `Compressor::build`
/// uses `false`.
///
/// "DCE" here does not mean "removes dead code" (full minify does that
/// too). It means tree-shaking: remove code that nothing imports, without
/// making the rest smaller. Rolldown runs this on every tree-shaking build,
/// with or without `minify`, so users can see this output directly.
///
/// In this mode `exit_*` only runs the passes that remove code, plus the
/// constant folds those removals need. For example, `fold_binary_expr`
/// folds `'production' === 'production'` to `true` so the dead `else`
/// branch can be dropped (this is how `define` values remove branches). The
/// passes that only shrink code (`substitute_*`, `minimize_*`) are left
/// out. See the `if ctx.state.dce` branch in `peephole/mod.rs`.
pub dce: bool,
/// The return value of function declarations that are pure