From db9e93b554b48500e6d9d9091204c5fff0a1aadc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BF=A0=20/=20green?= Date: Sun, 15 Dec 2024 22:31:41 +0900 Subject: [PATCH] feat(mangler): mangle top level variables (#7907) Adds `top_level` option which is similar to [terser's `toplevel` option](https://terser.org/docs/cli-usage/#cli-mangle-options). --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- crates/oxc_mangler/src/lib.rs | 10 ++++++---- crates/oxc_minifier/examples/mangler.rs | 4 +++- crates/oxc_minifier/examples/minifier.rs | 6 +++++- crates/oxc_minifier/src/lib.rs | 10 +++++++--- crates/oxc_minifier/tests/mangler/mod.rs | 17 ++++++++++++----- .../tests/mangler/snapshots/mangler.snap | 5 +++++ crates/oxc_wasm/src/lib.rs | 4 ++-- napi/minify/src/lib.rs | 12 +++++++----- tasks/coverage/src/runtime/mod.rs | 2 +- tasks/minsize/src/lib.rs | 7 +++++-- 10 files changed, 53 insertions(+), 24 deletions(-) diff --git a/crates/oxc_mangler/src/lib.rs b/crates/oxc_mangler/src/lib.rs index cb6b085cb14be..1eac2ef473696 100644 --- a/crates/oxc_mangler/src/lib.rs +++ b/crates/oxc_mangler/src/lib.rs @@ -6,8 +6,9 @@ use oxc_span::CompactStr; type Slot = usize; -#[derive(Default)] +#[derive(Default, Debug, Clone, Copy)] pub struct MangleOptions { + pub top_level: bool, pub debug: bool, } @@ -124,7 +125,7 @@ impl Mangler { } let frequencies = - Self::tally_slot_frequencies(&symbol_table, &scope_tree, total_number_of_slots, &slots); + self.tally_slot_frequencies(&symbol_table, &scope_tree, total_number_of_slots, &slots); let root_unresolved_references = scope_tree.root_unresolved_references(); let root_bindings = scope_tree.get_bindings(scope_tree.root_scope_id()); @@ -142,7 +143,7 @@ impl Mangler { if !is_keyword(n) && !is_special_name(n) && !root_unresolved_references.contains_key(n) - && !root_bindings.contains_key(n) + && (self.options.top_level || !root_bindings.contains_key(n)) { break name; } @@ -201,6 +202,7 @@ impl Mangler { } fn tally_slot_frequencies( + &self, symbol_table: &SymbolTable, scope_tree: &ScopeTree, total_number_of_slots: usize, @@ -209,7 +211,7 @@ impl Mangler { let root_scope_id = scope_tree.root_scope_id(); let mut frequencies = vec![SlotFrequency::default(); total_number_of_slots]; for (symbol_id, slot) in slots.iter_enumerated() { - if symbol_table.get_scope_id(symbol_id) == root_scope_id { + if !self.options.top_level && symbol_table.get_scope_id(symbol_id) == root_scope_id { continue; } if is_special_name(symbol_table.get_name(symbol_id)) { diff --git a/crates/oxc_minifier/examples/mangler.rs b/crates/oxc_minifier/examples/mangler.rs index e6a5251e1d782..23e60618061f6 100644 --- a/crates/oxc_minifier/examples/mangler.rs +++ b/crates/oxc_minifier/examples/mangler.rs @@ -38,6 +38,8 @@ fn main() -> std::io::Result<()> { fn mangler(source_text: &str, source_type: SourceType, debug: bool) -> String { let allocator = Allocator::default(); let ret = Parser::new(&allocator, source_text, source_type).parse(); - let mangler = Mangler::new().with_options(MangleOptions { debug }).build(&ret.program); + let mangler = Mangler::new() + .with_options(MangleOptions { debug, top_level: source_type.is_module() }) + .build(&ret.program); CodeGenerator::new().with_mangler(Some(mangler)).build(&ret.program).code } diff --git a/crates/oxc_minifier/examples/minifier.rs b/crates/oxc_minifier/examples/minifier.rs index 76c30e3a4061a..b3b52b2c62f30 100644 --- a/crates/oxc_minifier/examples/minifier.rs +++ b/crates/oxc_minifier/examples/minifier.rs @@ -3,6 +3,7 @@ use std::path::Path; use oxc_allocator::Allocator; use oxc_codegen::{CodeGenerator, CodegenOptions}; +use oxc_mangler::MangleOptions; use oxc_minifier::{CompressOptions, Minifier, MinifierOptions}; use oxc_parser::Parser; use oxc_span::SourceType; @@ -47,7 +48,10 @@ fn minify( ) -> String { let ret = Parser::new(allocator, source_text, source_type).parse(); let mut program = ret.program; - let options = MinifierOptions { mangle, compress: CompressOptions::default() }; + let options = MinifierOptions { + mangle: mangle.then(MangleOptions::default), + compress: CompressOptions::default(), + }; let ret = Minifier::new(options).build(allocator, &mut program); CodeGenerator::new() .with_options(CodegenOptions { minify: nospace, ..CodegenOptions::default() }) diff --git a/crates/oxc_minifier/src/lib.rs b/crates/oxc_minifier/src/lib.rs index 82dbb2b27a3d3..67f92ea9fe22a 100644 --- a/crates/oxc_minifier/src/lib.rs +++ b/crates/oxc_minifier/src/lib.rs @@ -14,16 +14,17 @@ use oxc_ast::ast::Program; use oxc_mangler::Mangler; pub use crate::{ast_passes::CompressorPass, compressor::Compressor, options::CompressOptions}; +pub use oxc_mangler::MangleOptions; #[derive(Debug, Clone, Copy)] pub struct MinifierOptions { - pub mangle: bool, + pub mangle: Option, pub compress: CompressOptions, } impl Default for MinifierOptions { fn default() -> Self { - Self { mangle: true, compress: CompressOptions::default() } + Self { mangle: Some(MangleOptions::default()), compress: CompressOptions::default() } } } @@ -42,7 +43,10 @@ impl Minifier { pub fn build<'a>(self, allocator: &'a Allocator, program: &mut Program<'a>) -> MinifierReturn { Compressor::new(allocator, self.options.compress).build(program); - let mangler = self.options.mangle.then(|| Mangler::default().build(program)); + let mangler = self + .options + .mangle + .map(|options| Mangler::default().with_options(options).build(program)); MinifierReturn { mangler } } } diff --git a/crates/oxc_minifier/tests/mangler/mod.rs b/crates/oxc_minifier/tests/mangler/mod.rs index e264e2075d132..e8a1001d42d76 100644 --- a/crates/oxc_minifier/tests/mangler/mod.rs +++ b/crates/oxc_minifier/tests/mangler/mod.rs @@ -2,16 +2,17 @@ use std::fmt::Write; use oxc_allocator::Allocator; use oxc_codegen::CodeGenerator; -use oxc_mangler::Mangler; +use oxc_mangler::{MangleOptions, Mangler}; use oxc_parser::Parser; use oxc_span::SourceType; -fn mangle(source_text: &str) -> String { +fn mangle(source_text: &str, top_level: bool) -> String { let allocator = Allocator::default(); let source_type = SourceType::mjs(); let ret = Parser::new(&allocator, source_text, source_type).parse(); let program = ret.program; - let mangler = Mangler::new().build(&program); + let mangler = + Mangler::new().with_options(MangleOptions { debug: false, top_level }).build(&program); CodeGenerator::new().with_mangler(Some(mangler)).build(&program).code } @@ -25,9 +26,15 @@ fn mangler() { "import { x } from 's'; export { x }", "function _ (exports) { Object.defineProperty(exports, '__esModule', { value: true }) }", ]; + let top_level_cases = ["function foo(a) {a}"]; - let snapshot = cases.into_iter().fold(String::new(), |mut w, case| { - write!(w, "{case}\n{}\n", mangle(case)).unwrap(); + let mut snapshot = String::new(); + cases.into_iter().fold(&mut snapshot, |w, case| { + write!(w, "{case}\n{}\n", mangle(case, false)).unwrap(); + w + }); + top_level_cases.into_iter().fold(&mut snapshot, |w, case| { + write!(w, "{case}\n{}\n", mangle(case, true)).unwrap(); w }); diff --git a/crates/oxc_minifier/tests/mangler/snapshots/mangler.snap b/crates/oxc_minifier/tests/mangler/snapshots/mangler.snap index 6e28a492caec8..fd36999045d8d 100644 --- a/crates/oxc_minifier/tests/mangler/snapshots/mangler.snap +++ b/crates/oxc_minifier/tests/mangler/snapshots/mangler.snap @@ -30,3 +30,8 @@ function _ (exports) { Object.defineProperty(exports, '__esModule', { value: tru function _(exports) { Object.defineProperty(exports, "__esModule", { value: true }); } + +function foo(a) {a} +function a(b) { + b; +} diff --git a/crates/oxc_wasm/src/lib.rs b/crates/oxc_wasm/src/lib.rs index 5417a514ba461..a37962b9a08ef 100644 --- a/crates/oxc_wasm/src/lib.rs +++ b/crates/oxc_wasm/src/lib.rs @@ -13,7 +13,7 @@ use oxc::{ allocator::Allocator, ast::{ast::Program, Comment as OxcComment, CommentKind, Visit}, codegen::{CodeGenerator, CodegenOptions}, - minifier::{CompressOptions, Minifier, MinifierOptions}, + minifier::{CompressOptions, MangleOptions, Minifier, MinifierOptions}, parser::{ParseOptions, Parser, ParserReturn}, semantic::{ dot::{DebugDot, DebugDotContext}, @@ -269,7 +269,7 @@ impl Oxc { { let compress_options = minifier_options.compress_options.unwrap_or_default(); let options = MinifierOptions { - mangle: minifier_options.mangle.unwrap_or_default(), + mangle: minifier_options.mangle.unwrap_or_default().then(MangleOptions::default), compress: if minifier_options.compress.unwrap_or_default() { CompressOptions { drop_console: compress_options.drop_console, diff --git a/napi/minify/src/lib.rs b/napi/minify/src/lib.rs index 61156f360f351..6ddeecfa90af0 100644 --- a/napi/minify/src/lib.rs +++ b/napi/minify/src/lib.rs @@ -2,7 +2,7 @@ use napi_derive::napi; use oxc_allocator::Allocator; use oxc_codegen::{Codegen, CodegenOptions}; -use oxc_minifier::{CompressOptions, Minifier, MinifierOptions}; +use oxc_minifier::{CompressOptions, MangleOptions, Minifier, MinifierOptions}; use oxc_parser::Parser; use oxc_span::SourceType; @@ -14,10 +14,12 @@ pub fn minify(filename: String, source_text: String) -> String { let mut program = Parser::new(&allocator, &source_text, source_type).parse().program; - let mangler = - Minifier::new(MinifierOptions { mangle: true, compress: CompressOptions::default() }) - .build(&allocator, &mut program) - .mangler; + let mangler = Minifier::new(MinifierOptions { + mangle: Some(MangleOptions::default()), + compress: CompressOptions::default(), + }) + .build(&allocator, &mut program) + .mangler; Codegen::new() .with_options(CodegenOptions { minify: true, ..CodegenOptions::default() }) diff --git a/tasks/coverage/src/runtime/mod.rs b/tasks/coverage/src/runtime/mod.rs index efa10670fbc1e..222485a2f1351 100644 --- a/tasks/coverage/src/runtime/mod.rs +++ b/tasks/coverage/src/runtime/mod.rs @@ -183,7 +183,7 @@ impl Test262RuntimeCase { } let mangler = if minify { - Minifier::new(MinifierOptions { mangle: false, ..MinifierOptions::default() }) + Minifier::new(MinifierOptions { mangle: None, ..MinifierOptions::default() }) .build(&allocator, &mut program) .mangler } else { diff --git a/tasks/minsize/src/lib.rs b/tasks/minsize/src/lib.rs index cbeb291356513..955b31712bc04 100644 --- a/tasks/minsize/src/lib.rs +++ b/tasks/minsize/src/lib.rs @@ -8,7 +8,7 @@ use flate2::{write::GzEncoder, Compression}; use humansize::{format_size, DECIMAL}; use oxc_allocator::Allocator; use oxc_codegen::{CodeGenerator, CodegenOptions}; -use oxc_minifier::{CompressOptions, Minifier, MinifierOptions}; +use oxc_minifier::{CompressOptions, MangleOptions, Minifier, MinifierOptions}; use oxc_parser::Parser; use oxc_span::SourceType; use oxc_tasks_common::{project_root, TestFile, TestFiles}; @@ -115,7 +115,10 @@ pub fn run() -> Result<(), io::Error> { fn minify_twice(file: &TestFile) -> String { let source_type = SourceType::from_path(&file.file_name).unwrap(); - let options = MinifierOptions { mangle: true, compress: CompressOptions::default() }; + let options = MinifierOptions { + mangle: Some(MangleOptions::default()), + compress: CompressOptions::default(), + }; let source_text1 = minify(&file.source_text, source_type, options); let source_text2 = minify(&source_text1, source_type, options); assert!(source_text1 == source_text2, "Minification failed for {}", &file.file_name);