mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
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:
parent
075bd165a8
commit
db9e93b554
10 changed files with 53 additions and 24 deletions
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() })
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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() })
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue