mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(minifier): implement collapse-variable-declarations (#6464)
This commit is contained in:
parent
f960e9ea83
commit
97c8a3608f
3 changed files with 71 additions and 66 deletions
|
|
@ -36,65 +36,75 @@ impl<'a> CollapseVariableDeclarations {
|
|||
Self { options, changed: false }
|
||||
}
|
||||
|
||||
/// Join consecutive var statements
|
||||
fn is_require_call(var_decl: &VariableDeclaration) -> bool {
|
||||
var_decl
|
||||
.declarations
|
||||
.first()
|
||||
.and_then(|d| d.init.as_ref())
|
||||
.is_some_and(Expression::is_require_call)
|
||||
}
|
||||
|
||||
fn is_valid_var_decl(
|
||||
stmt: &Statement,
|
||||
kind: Option<VariableDeclarationKind>,
|
||||
) -> Option<VariableDeclarationKind> {
|
||||
if let Statement::VariableDeclaration(cur_decl) = stmt {
|
||||
let is_not_require_call = !Self::is_require_call(cur_decl);
|
||||
if kind.map_or(true, |k| cur_decl.kind == k) && is_not_require_call {
|
||||
return Some(cur_decl.kind);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn join_vars(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.join_vars {
|
||||
if !self.options.join_vars || stmts.len() < 2 {
|
||||
return;
|
||||
}
|
||||
// Collect all the consecutive ranges that contain joinable vars.
|
||||
// This is required because Rust prevents in-place vec mutation.
|
||||
let mut ranges = vec![];
|
||||
let mut range = 0..0;
|
||||
let mut i = 1usize;
|
||||
let mut capacity = 0usize;
|
||||
for window in stmts.windows(2) {
|
||||
let [prev, cur] = window else { unreachable!() };
|
||||
if let (
|
||||
Statement::VariableDeclaration(cur_decl),
|
||||
Statement::VariableDeclaration(prev_decl),
|
||||
) = (cur, prev)
|
||||
|
||||
let mut prev: usize = stmts.len() - 1;
|
||||
let mut items = std::vec::Vec::<usize>::new();
|
||||
|
||||
while prev > 0 {
|
||||
prev -= 1;
|
||||
|
||||
let cur: usize = prev + 1;
|
||||
|
||||
if !Self::is_valid_var_decl(&stmts[cur], None)
|
||||
.is_some_and(|kind| Self::is_valid_var_decl(&stmts[prev], Some(kind)).is_some())
|
||||
{
|
||||
// Do not join `require` calls for cjs-module-lexer.
|
||||
if cur_decl
|
||||
.declarations
|
||||
.first()
|
||||
.and_then(|d| d.init.as_ref())
|
||||
.is_some_and(Expression::is_require_call)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if cur_decl.kind == prev_decl.kind {
|
||||
if i - 1 != range.end {
|
||||
range.start = i - 1;
|
||||
}
|
||||
range.end = i + 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (range.end != i || i == stmts.len() - 1) && range.start < range.end {
|
||||
capacity += range.end - range.start - 1;
|
||||
ranges.push(range.clone());
|
||||
range = 0..0;
|
||||
let Some(Statement::VariableDeclaration(cur_decl)) = stmts.get_mut(cur) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut decls = ctx.ast.move_vec(&mut cur_decl.declarations);
|
||||
if let Some(Statement::VariableDeclaration(prev_decl)) = stmts.get_mut(prev) {
|
||||
items.push(cur);
|
||||
prev_decl.declarations.append(&mut decls);
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if ranges.is_empty() {
|
||||
if items.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Reconstruct the stmts array by joining consecutive ranges
|
||||
let mut new_stmts = ctx.ast.vec_with_capacity(stmts.len() - capacity);
|
||||
for (i, stmt) in stmts.drain(..).enumerate() {
|
||||
if i > 0 && ranges.iter().any(|range| range.contains(&(i - 1)) && range.contains(&i)) {
|
||||
if let Statement::VariableDeclaration(prev_decl) = new_stmts.last_mut().unwrap() {
|
||||
if let Statement::VariableDeclaration(mut cur_decl) = stmt {
|
||||
prev_decl.declarations.append(&mut cur_decl.declarations);
|
||||
}
|
||||
let mut item_iter = items.iter().rev();
|
||||
let mut next_item = item_iter.next();
|
||||
|
||||
let mut new_stmts = ctx.ast.vec_with_capacity(stmts.len() - items.len());
|
||||
|
||||
for (index, stmt) in stmts.drain(..).enumerate() {
|
||||
if let Some(item) = next_item {
|
||||
if *item == index {
|
||||
next_item = item_iter.next();
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
new_stmts.push(stmt);
|
||||
}
|
||||
new_stmts.push(stmt);
|
||||
}
|
||||
|
||||
*stmts = new_stmts;
|
||||
self.changed = true;
|
||||
}
|
||||
|
|
@ -131,7 +141,6 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_collapsing() {
|
||||
// Basic collapsing
|
||||
test("var a;var b;", "var a,b;");
|
||||
|
|
@ -177,7 +186,6 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_issue397() {
|
||||
test_same("var x; x = 5; var z = 7;");
|
||||
test("var x; var y = 3; x = 5;", "var x, y = 3; x = 5;");
|
||||
|
|
@ -192,7 +200,6 @@ mod test {
|
|||
|
||||
// ES6 Tests
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_collapsing_let_const() {
|
||||
// Basic collapsing
|
||||
test("let a;let b;", "let a,b;");
|
||||
|
|
@ -229,7 +236,6 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_redeclaration_let_in_function() {
|
||||
test(
|
||||
"function f() { let x = 1; let y = 2; let z = 3; x + y + z; }",
|
||||
|
|
@ -248,7 +254,6 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_arrow_function() {
|
||||
test("() => {let x = 1; let y = 2; x + y; }", "() => {let x = 1, y = 2; x + y; }");
|
||||
|
||||
|
|
@ -264,7 +269,6 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_mixed_declaration_types() {
|
||||
// lets, vars, const declarations consecutive
|
||||
test("let x = 1; let z = 3; var y = 2;", "let x = 1, z = 3; var y = 2;");
|
||||
|
|
|
|||
|
|
@ -42,9 +42,6 @@ impl<'a> Compressor<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
ExploitAssigns::new().build(program, &mut ctx);
|
||||
CollapseVariableDeclarations::new(self.options).build(program, &mut ctx);
|
||||
|
||||
// See `latePeepholeOptimizations`
|
||||
let mut passes: [&mut dyn CompressorPass; 6] = [
|
||||
&mut StatementFusion::new(),
|
||||
|
|
@ -74,6 +71,10 @@ impl<'a> Compressor<'a> {
|
|||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
// Passes listed in `getFinalization` in `DefaultPassConfig`
|
||||
ExploitAssigns::new().build(program, &mut ctx);
|
||||
CollapseVariableDeclarations::new(self.options).build(program, &mut ctx);
|
||||
}
|
||||
|
||||
fn dead_code_elimination(program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
Original | Minified | esbuild | Gzip | esbuild
|
||||
|
||||
72.14 kB | 24.46 kB | 23.70 kB | 8.65 kB | 8.54 kB | react.development.js
|
||||
72.14 kB | 24.12 kB | 23.70 kB | 8.62 kB | 8.54 kB | react.development.js
|
||||
|
||||
173.90 kB | 61.68 kB | 59.82 kB | 19.53 kB | 19.33 kB | moment.js
|
||||
173.90 kB | 61.67 kB | 59.82 kB | 19.53 kB | 19.33 kB | moment.js
|
||||
|
||||
287.63 kB | 92.83 kB | 90.07 kB | 32.29 kB | 31.95 kB | jquery.js
|
||||
287.63 kB | 92.70 kB | 90.07 kB | 32.27 kB | 31.95 kB | jquery.js
|
||||
|
||||
342.15 kB | 124.06 kB | 118.14 kB | 44.79 kB | 44.37 kB | vue.js
|
||||
342.15 kB | 121.90 kB | 118.14 kB | 44.59 kB | 44.37 kB | vue.js
|
||||
|
||||
544.10 kB | 74.13 kB | 72.48 kB | 26.23 kB | 26.20 kB | lodash.js
|
||||
544.10 kB | 73.49 kB | 72.48 kB | 26.13 kB | 26.20 kB | lodash.js
|
||||
|
||||
555.77 kB | 278.22 kB | 270.13 kB | 91.36 kB | 90.80 kB | d3.js
|
||||
555.77 kB | 276.31 kB | 270.13 kB | 91.08 kB | 90.80 kB | d3.js
|
||||
|
||||
1.01 MB | 470.07 kB | 458.89 kB | 126.95 kB | 126.71 kB | bundle.min.js
|
||||
1.01 MB | 467.63 kB | 458.89 kB | 126.75 kB | 126.71 kB | bundle.min.js
|
||||
|
||||
1.25 MB | 670.94 kB | 646.76 kB | 164.72 kB | 163.73 kB | three.js
|
||||
1.25 MB | 662.90 kB | 646.76 kB | 164.00 kB | 163.73 kB | three.js
|
||||
|
||||
2.14 MB | 756.31 kB | 724.14 kB | 182.74 kB | 181.07 kB | victory.js
|
||||
2.14 MB | 741.41 kB | 724.14 kB | 181.41 kB | 181.07 kB | victory.js
|
||||
|
||||
3.20 MB | 1.05 MB | 1.01 MB | 334.08 kB | 331.56 kB | echarts.js
|
||||
3.20 MB | 1.02 MB | 1.01 MB | 331.95 kB | 331.56 kB | echarts.js
|
||||
|
||||
6.69 MB | 2.44 MB | 2.31 MB | 498.86 kB | 488.28 kB | antd.js
|
||||
6.69 MB | 2.39 MB | 2.31 MB | 496.10 kB | 488.28 kB | antd.js
|
||||
|
||||
10.95 MB | 3.59 MB | 3.49 MB | 913.91 kB | 915.50 kB | typescript.js
|
||||
10.95 MB | 3.56 MB | 3.49 MB | 911.24 kB | 915.50 kB | typescript.js
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue