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>
This commit is contained in:
翠 / green 2024-12-15 22:31:41 +09:00 committed by GitHub
parent 075bd165a8
commit db9e93b554
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 53 additions and 24 deletions

View file

@ -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)) {

View file

@ -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
}

View file

@ -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() })

View file

@ -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() }
}
}
@ -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 }
}
}

View file

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

View file

@ -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;
}

View file

@ -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,

View file

@ -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() })

View file

@ -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 {

View file

@ -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);