Skip to content

Commit db9e93b

Browse files
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>
1 parent 075bd16 commit db9e93b

File tree

10 files changed

+53
-24
lines changed

10 files changed

+53
-24
lines changed

crates/oxc_mangler/src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ use oxc_span::CompactStr;
66

77
type Slot = usize;
88

9-
#[derive(Default)]
9+
#[derive(Default, Debug, Clone, Copy)]
1010
pub struct MangleOptions {
11+
pub top_level: bool,
1112
pub debug: bool,
1213
}
1314

@@ -124,7 +125,7 @@ impl Mangler {
124125
}
125126

126127
let frequencies =
127-
Self::tally_slot_frequencies(&symbol_table, &scope_tree, total_number_of_slots, &slots);
128+
self.tally_slot_frequencies(&symbol_table, &scope_tree, total_number_of_slots, &slots);
128129

129130
let root_unresolved_references = scope_tree.root_unresolved_references();
130131
let root_bindings = scope_tree.get_bindings(scope_tree.root_scope_id());
@@ -142,7 +143,7 @@ impl Mangler {
142143
if !is_keyword(n)
143144
&& !is_special_name(n)
144145
&& !root_unresolved_references.contains_key(n)
145-
&& !root_bindings.contains_key(n)
146+
&& (self.options.top_level || !root_bindings.contains_key(n))
146147
{
147148
break name;
148149
}
@@ -201,6 +202,7 @@ impl Mangler {
201202
}
202203

203204
fn tally_slot_frequencies(
205+
&self,
204206
symbol_table: &SymbolTable,
205207
scope_tree: &ScopeTree,
206208
total_number_of_slots: usize,
@@ -209,7 +211,7 @@ impl Mangler {
209211
let root_scope_id = scope_tree.root_scope_id();
210212
let mut frequencies = vec![SlotFrequency::default(); total_number_of_slots];
211213
for (symbol_id, slot) in slots.iter_enumerated() {
212-
if symbol_table.get_scope_id(symbol_id) == root_scope_id {
214+
if !self.options.top_level && symbol_table.get_scope_id(symbol_id) == root_scope_id {
213215
continue;
214216
}
215217
if is_special_name(symbol_table.get_name(symbol_id)) {

crates/oxc_minifier/examples/mangler.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ fn main() -> std::io::Result<()> {
3838
fn mangler(source_text: &str, source_type: SourceType, debug: bool) -> String {
3939
let allocator = Allocator::default();
4040
let ret = Parser::new(&allocator, source_text, source_type).parse();
41-
let mangler = Mangler::new().with_options(MangleOptions { debug }).build(&ret.program);
41+
let mangler = Mangler::new()
42+
.with_options(MangleOptions { debug, top_level: source_type.is_module() })
43+
.build(&ret.program);
4244
CodeGenerator::new().with_mangler(Some(mangler)).build(&ret.program).code
4345
}

crates/oxc_minifier/examples/minifier.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::path::Path;
33

44
use oxc_allocator::Allocator;
55
use oxc_codegen::{CodeGenerator, CodegenOptions};
6+
use oxc_mangler::MangleOptions;
67
use oxc_minifier::{CompressOptions, Minifier, MinifierOptions};
78
use oxc_parser::Parser;
89
use oxc_span::SourceType;
@@ -47,7 +48,10 @@ fn minify(
4748
) -> String {
4849
let ret = Parser::new(allocator, source_text, source_type).parse();
4950
let mut program = ret.program;
50-
let options = MinifierOptions { mangle, compress: CompressOptions::default() };
51+
let options = MinifierOptions {
52+
mangle: mangle.then(MangleOptions::default),
53+
compress: CompressOptions::default(),
54+
};
5155
let ret = Minifier::new(options).build(allocator, &mut program);
5256
CodeGenerator::new()
5357
.with_options(CodegenOptions { minify: nospace, ..CodegenOptions::default() })

crates/oxc_minifier/src/lib.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,17 @@ use oxc_ast::ast::Program;
1414
use oxc_mangler::Mangler;
1515

1616
pub use crate::{ast_passes::CompressorPass, compressor::Compressor, options::CompressOptions};
17+
pub use oxc_mangler::MangleOptions;
1718

1819
#[derive(Debug, Clone, Copy)]
1920
pub struct MinifierOptions {
20-
pub mangle: bool,
21+
pub mangle: Option<MangleOptions>,
2122
pub compress: CompressOptions,
2223
}
2324

2425
impl Default for MinifierOptions {
2526
fn default() -> Self {
26-
Self { mangle: true, compress: CompressOptions::default() }
27+
Self { mangle: Some(MangleOptions::default()), compress: CompressOptions::default() }
2728
}
2829
}
2930

@@ -42,7 +43,10 @@ impl Minifier {
4243

4344
pub fn build<'a>(self, allocator: &'a Allocator, program: &mut Program<'a>) -> MinifierReturn {
4445
Compressor::new(allocator, self.options.compress).build(program);
45-
let mangler = self.options.mangle.then(|| Mangler::default().build(program));
46+
let mangler = self
47+
.options
48+
.mangle
49+
.map(|options| Mangler::default().with_options(options).build(program));
4650
MinifierReturn { mangler }
4751
}
4852
}

crates/oxc_minifier/tests/mangler/mod.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ use std::fmt::Write;
22

33
use oxc_allocator::Allocator;
44
use oxc_codegen::CodeGenerator;
5-
use oxc_mangler::Mangler;
5+
use oxc_mangler::{MangleOptions, Mangler};
66
use oxc_parser::Parser;
77
use oxc_span::SourceType;
88

9-
fn mangle(source_text: &str) -> String {
9+
fn mangle(source_text: &str, top_level: bool) -> String {
1010
let allocator = Allocator::default();
1111
let source_type = SourceType::mjs();
1212
let ret = Parser::new(&allocator, source_text, source_type).parse();
1313
let program = ret.program;
14-
let mangler = Mangler::new().build(&program);
14+
let mangler =
15+
Mangler::new().with_options(MangleOptions { debug: false, top_level }).build(&program);
1516
CodeGenerator::new().with_mangler(Some(mangler)).build(&program).code
1617
}
1718

@@ -25,9 +26,15 @@ fn mangler() {
2526
"import { x } from 's'; export { x }",
2627
"function _ (exports) { Object.defineProperty(exports, '__esModule', { value: true }) }",
2728
];
29+
let top_level_cases = ["function foo(a) {a}"];
2830

29-
let snapshot = cases.into_iter().fold(String::new(), |mut w, case| {
30-
write!(w, "{case}\n{}\n", mangle(case)).unwrap();
31+
let mut snapshot = String::new();
32+
cases.into_iter().fold(&mut snapshot, |w, case| {
33+
write!(w, "{case}\n{}\n", mangle(case, false)).unwrap();
34+
w
35+
});
36+
top_level_cases.into_iter().fold(&mut snapshot, |w, case| {
37+
write!(w, "{case}\n{}\n", mangle(case, true)).unwrap();
3138
w
3239
});
3340

crates/oxc_minifier/tests/mangler/snapshots/mangler.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,8 @@ function _ (exports) { Object.defineProperty(exports, '__esModule', { value: tru
3030
function _(exports) {
3131
Object.defineProperty(exports, "__esModule", { value: true });
3232
}
33+
34+
function foo(a) {a}
35+
function a(b) {
36+
b;
37+
}

crates/oxc_wasm/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use oxc::{
1313
allocator::Allocator,
1414
ast::{ast::Program, Comment as OxcComment, CommentKind, Visit},
1515
codegen::{CodeGenerator, CodegenOptions},
16-
minifier::{CompressOptions, Minifier, MinifierOptions},
16+
minifier::{CompressOptions, MangleOptions, Minifier, MinifierOptions},
1717
parser::{ParseOptions, Parser, ParserReturn},
1818
semantic::{
1919
dot::{DebugDot, DebugDotContext},
@@ -269,7 +269,7 @@ impl Oxc {
269269
{
270270
let compress_options = minifier_options.compress_options.unwrap_or_default();
271271
let options = MinifierOptions {
272-
mangle: minifier_options.mangle.unwrap_or_default(),
272+
mangle: minifier_options.mangle.unwrap_or_default().then(MangleOptions::default),
273273
compress: if minifier_options.compress.unwrap_or_default() {
274274
CompressOptions {
275275
drop_console: compress_options.drop_console,

napi/minify/src/lib.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use napi_derive::napi;
22

33
use oxc_allocator::Allocator;
44
use oxc_codegen::{Codegen, CodegenOptions};
5-
use oxc_minifier::{CompressOptions, Minifier, MinifierOptions};
5+
use oxc_minifier::{CompressOptions, MangleOptions, Minifier, MinifierOptions};
66
use oxc_parser::Parser;
77
use oxc_span::SourceType;
88

@@ -14,10 +14,12 @@ pub fn minify(filename: String, source_text: String) -> String {
1414

1515
let mut program = Parser::new(&allocator, &source_text, source_type).parse().program;
1616

17-
let mangler =
18-
Minifier::new(MinifierOptions { mangle: true, compress: CompressOptions::default() })
19-
.build(&allocator, &mut program)
20-
.mangler;
17+
let mangler = Minifier::new(MinifierOptions {
18+
mangle: Some(MangleOptions::default()),
19+
compress: CompressOptions::default(),
20+
})
21+
.build(&allocator, &mut program)
22+
.mangler;
2123

2224
Codegen::new()
2325
.with_options(CodegenOptions { minify: true, ..CodegenOptions::default() })

tasks/coverage/src/runtime/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ impl Test262RuntimeCase {
183183
}
184184

185185
let mangler = if minify {
186-
Minifier::new(MinifierOptions { mangle: false, ..MinifierOptions::default() })
186+
Minifier::new(MinifierOptions { mangle: None, ..MinifierOptions::default() })
187187
.build(&allocator, &mut program)
188188
.mangler
189189
} else {

tasks/minsize/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use flate2::{write::GzEncoder, Compression};
88
use humansize::{format_size, DECIMAL};
99
use oxc_allocator::Allocator;
1010
use oxc_codegen::{CodeGenerator, CodegenOptions};
11-
use oxc_minifier::{CompressOptions, Minifier, MinifierOptions};
11+
use oxc_minifier::{CompressOptions, MangleOptions, Minifier, MinifierOptions};
1212
use oxc_parser::Parser;
1313
use oxc_span::SourceType;
1414
use oxc_tasks_common::{project_root, TestFile, TestFiles};
@@ -115,7 +115,10 @@ pub fn run() -> Result<(), io::Error> {
115115

116116
fn minify_twice(file: &TestFile) -> String {
117117
let source_type = SourceType::from_path(&file.file_name).unwrap();
118-
let options = MinifierOptions { mangle: true, compress: CompressOptions::default() };
118+
let options = MinifierOptions {
119+
mangle: Some(MangleOptions::default()),
120+
compress: CompressOptions::default(),
121+
};
119122
let source_text1 = minify(&file.source_text, source_type, options);
120123
let source_text2 = minify(&source_text1, source_type, options);
121124
assert!(source_text1 == source_text2, "Minification failed for {}", &file.file_name);

0 commit comments

Comments
 (0)