diff --git a/crates/oxc_minifier/src/ast_passes/mod.rs b/crates/oxc_minifier/src/ast_passes/mod.rs index 9fc44e9b4e3a5..892167151f248 100644 --- a/crates/oxc_minifier/src/ast_passes/mod.rs +++ b/crates/oxc_minifier/src/ast_passes/mod.rs @@ -1,3 +1,7 @@ +#![allow(clippy::wildcard_imports)] + +mod remove_dead_code; mod remove_parens; +pub use remove_dead_code::RemoveDeadCode; pub use remove_parens::RemoveParens; diff --git a/crates/oxc_minifier/src/ast_passes/remove_dead_code.rs b/crates/oxc_minifier/src/ast_passes/remove_dead_code.rs new file mode 100644 index 0000000000000..b7147d177fbbc --- /dev/null +++ b/crates/oxc_minifier/src/ast_passes/remove_dead_code.rs @@ -0,0 +1,45 @@ +use oxc_allocator::Allocator; +use oxc_ast::{ast::*, visit::walk_mut, AstBuilder, VisitMut}; +use oxc_span::SPAN; + +/// Remove Dead Code from the AST. +/// +/// Terser option: `dead_code: true`. +#[derive(Clone, Copy)] +pub struct RemoveDeadCode<'a> { + ast: AstBuilder<'a>, +} + +impl<'a> RemoveDeadCode<'a> { + pub fn new(allocator: &'a Allocator) -> Self { + Self { ast: AstBuilder::new(allocator) } + } + + pub fn build(&mut self, program: &mut Program<'a>) { + self.visit_program(program); + } + + pub fn remove_if(&mut self, stmt: &mut Statement<'a>) { + let Statement::IfStatement(if_stmt) = stmt else { return }; + match if_stmt.test.get_boolean_value() { + Some(true) => { + *stmt = self.ast.move_statement(&mut if_stmt.consequent); + } + Some(false) => { + *stmt = if let Some(alternate) = &mut if_stmt.alternate { + self.ast.move_statement(alternate) + } else { + self.ast.empty_statement(SPAN) + }; + } + _ => {} + } + } +} + +impl<'a> VisitMut<'a> for RemoveDeadCode<'a> { + fn visit_statement(&mut self, stmt: &mut Statement<'a>) { + self.remove_if(stmt); + walk_mut::walk_statement_mut(self, stmt); + } +} diff --git a/crates/oxc_minifier/src/ast_passes/remove_parens.rs b/crates/oxc_minifier/src/ast_passes/remove_parens.rs index 3dd3f2288c64a..09a36289f380e 100644 --- a/crates/oxc_minifier/src/ast_passes/remove_parens.rs +++ b/crates/oxc_minifier/src/ast_passes/remove_parens.rs @@ -1,7 +1,9 @@ use oxc_allocator::{Allocator, Vec}; -use oxc_ast::visit::walk_mut::{walk_expression_mut, walk_statements_mut}; -#[allow(clippy::wildcard_imports)] -use oxc_ast::{ast::*, AstBuilder, VisitMut}; +use oxc_ast::{ + ast::*, + visit::walk_mut::{walk_expression_mut, walk_statements_mut}, + AstBuilder, VisitMut, +}; /// Remove Parenthesized Expression from the AST. #[derive(Clone, Copy)] diff --git a/crates/oxc_minifier/src/lib.rs b/crates/oxc_minifier/src/lib.rs index 60280baea0b74..b6206ea04d7e8 100644 --- a/crates/oxc_minifier/src/lib.rs +++ b/crates/oxc_minifier/src/lib.rs @@ -8,7 +8,7 @@ use oxc_allocator::Allocator; use oxc_ast::ast::Program; pub use crate::{ - ast_passes::RemoveParens, + ast_passes::{RemoveDeadCode, RemoveParens}, compressor::{CompressOptions, Compressor}, mangler::ManglerBuilder, }; diff --git a/crates/oxc_minifier/tests/oxc/mod.rs b/crates/oxc_minifier/tests/oxc/mod.rs index 4be69df14db41..f148ba3a16805 100644 --- a/crates/oxc_minifier/tests/oxc/mod.rs +++ b/crates/oxc_minifier/tests/oxc/mod.rs @@ -1,3 +1,4 @@ mod code_removal; mod folding; mod precedence; +mod remove_dead_code; diff --git a/crates/oxc_minifier/tests/oxc/remove_dead_code.rs b/crates/oxc_minifier/tests/oxc/remove_dead_code.rs new file mode 100644 index 0000000000000..689e06b306690 --- /dev/null +++ b/crates/oxc_minifier/tests/oxc/remove_dead_code.rs @@ -0,0 +1,26 @@ +use oxc_allocator::Allocator; +use oxc_codegen::WhitespaceRemover; +use oxc_minifier::RemoveDeadCode; +use oxc_parser::Parser; +use oxc_span::SourceType; + +fn minify(source_text: &str) -> String { + let source_type = SourceType::default(); + let allocator = Allocator::default(); + let ret = Parser::new(&allocator, source_text, source_type).parse(); + let program = allocator.alloc(ret.program); + RemoveDeadCode::new(&allocator).build(program); + WhitespaceRemover::new().build(program).source_text +} + +pub(crate) fn test(source_text: &str, expected: &str) { + let minified = minify(source_text); + assert_eq!(minified, expected, "for source {source_text}"); +} + +#[test] +fn remove_dead_code() { + test("if (true) { foo }", "{foo}"); + test("if (true) { foo } else { bar }", "{foo}"); + test("if (false) { foo } else { bar }", "{bar}"); +}