Skip to content

Commit

Permalink
feat(mangler): mangle top level variables (#7907)
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
sapphi-red and autofix-ci[bot] authored Dec 15, 2024
1 parent 075bd16 commit db9e93b
Show file tree
Hide file tree
Showing 10 changed files with 53 additions and 24 deletions.
10 changes: 6 additions & 4 deletions crates/oxc_mangler/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand Down Expand Up @@ -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());
Expand All @@ -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;
}
Expand Down Expand Up @@ -201,6 +202,7 @@ impl Mangler {
}

fn tally_slot_frequencies(
&self,
symbol_table: &SymbolTable,
scope_tree: &ScopeTree,
total_number_of_slots: usize,
Expand All @@ -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)) {
Expand Down
4 changes: 3 additions & 1 deletion crates/oxc_minifier/examples/mangler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
6 changes: 5 additions & 1 deletion crates/oxc_minifier/examples/minifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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() })
Expand Down
10 changes: 7 additions & 3 deletions crates/oxc_minifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<MangleOptions>,
pub compress: CompressOptions,
}

impl Default for MinifierOptions {
fn default() -> Self {
Self { mangle: true, compress: CompressOptions::default() }
Self { mangle: Some(MangleOptions::default()), compress: CompressOptions::default() }
}
}

Expand All @@ -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 }
}
}
17 changes: 12 additions & 5 deletions crates/oxc_minifier/tests/mangler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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
});

Expand Down
5 changes: 5 additions & 0 deletions crates/oxc_minifier/tests/mangler/snapshots/mangler.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
4 changes: 2 additions & 2 deletions crates/oxc_wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 7 additions & 5 deletions napi/minify/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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() })
Expand Down
2 changes: 1 addition & 1 deletion tasks/coverage/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 5 additions & 2 deletions tasks/minsize/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit db9e93b

Please sign in to comment.