Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions crates/oxc_minifier/src/ast_passes/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_syntax::es_target::ESTarget;
use oxc_traverse::{traverse_mut_with_ctx, ReusableTraverseCtx, Traverse, TraverseCtx};

mod collapse_variable_declarations;
Expand Down Expand Up @@ -53,15 +54,15 @@ pub struct PeepholeOptimizations {
impl PeepholeOptimizations {
/// `in_fixed_loop`: Do not compress syntaxes that are hard to analyze inside the fixed loop.
/// Opposite of `late` in Closure Compiler.
pub fn new(in_fixed_loop: bool, options: CompressOptions) -> Self {
pub fn new(target: ESTarget, in_fixed_loop: bool, options: CompressOptions) -> Self {
Self {
x0_statement_fusion: StatementFusion::new(),
x1_minimize_exit_points: MinimizeExitPoints::new(),
x2_exploit_assigns: ExploitAssigns::new(),
x3_collapse_variable_declarations: CollapseVariableDeclarations::new(),
x4_peephole_fold_constants: PeepholeFoldConstants::new(),
x5_peephole_remove_dead_code: PeepholeRemoveDeadCode::new(),
x6_peephole_minimize_conditions: PeepholeMinimizeConditions::new(),
x6_peephole_minimize_conditions: PeepholeMinimizeConditions::new(target),
x7_peephole_replace_known_methods: PeepholeReplaceKnownMethods::new(),
x8_convert_to_dotted_properties: ConvertToDottedProperties::new(in_fixed_loop),
x9_peephole_substitute_alternate_syntax: PeepholeSubstituteAlternateSyntax::new(
Expand Down
49 changes: 43 additions & 6 deletions crates/oxc_minifier/src/ast_passes/peephole_minimize_conditions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use oxc_allocator::Vec;
use oxc_ast::{ast::*, NONE};
use oxc_ecmascript::constant_evaluation::ValueType;
use oxc_span::{cmp::ContentEq, GetSpan, SPAN};
use oxc_syntax::es_target::ESTarget;
use oxc_traverse::{traverse_mut_with_ctx, Ancestor, ReusableTraverseCtx, Traverse, TraverseCtx};

use crate::CompressorPass;
Expand All @@ -14,6 +15,7 @@ use crate::CompressorPass;
///
/// <https://github.com/google/closure-compiler/blob/v20240609/src/com/google/javascript/jscomp/PeepholeMinimizeConditions.java>
pub struct PeepholeMinimizeConditions {
target: ESTarget,
pub(crate) changed: bool,
}

Expand Down Expand Up @@ -56,7 +58,7 @@ impl<'a> Traverse<'a> for PeepholeMinimizeConditions {
Expression::UnaryExpression(e) => Self::try_minimize_not(e, ctx),
Expression::LogicalExpression(e) => Self::try_minimize_logical(e, ctx),
Expression::BinaryExpression(e) => Self::try_minimize_binary(e, ctx),
Expression::ConditionalExpression(e) => Self::try_minimize_conditional(e, ctx),
Expression::ConditionalExpression(e) => self.try_minimize_conditional(e, ctx),
_ => None,
} {
*expr = folded_expr;
Expand All @@ -66,8 +68,8 @@ impl<'a> Traverse<'a> for PeepholeMinimizeConditions {
}

impl<'a> PeepholeMinimizeConditions {
pub fn new() -> Self {
Self { changed: false }
pub fn new(target: ESTarget) -> Self {
Self { target, changed: false }
}

/// Try to minimize NOT nodes such as `!(x==y)`.
Expand Down Expand Up @@ -344,6 +346,7 @@ impl<'a> PeepholeMinimizeConditions {

// based on https://github.com/evanw/esbuild/blob/df815ac27b84f8b34374c9182a93c94718f8a630/internal/js_ast/js_ast_helpers.go#L2745
fn try_minimize_conditional(
&self,
expr: &mut ConditionalExpression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<Expression<'a>> {
Expand Down Expand Up @@ -633,7 +636,39 @@ impl<'a> PeepholeMinimizeConditions {
}
}

// TODO: Try using the "??" or "?." operators
// Try using the "??" or "?." operators
if let Expression::BinaryExpression(bin_expr) = &mut expr.test {
if bin_expr.operator == BinaryOperator::Equality
|| bin_expr.operator == BinaryOperator::Inequality
{
if let Some(check) = {
if bin_expr.left.is_null() {
Some(&bin_expr.right)
} else {
Some(&bin_expr.left)
}
} {
// `a != null ? a : b` -> `a ?? b``
if check.content_eq(if bin_expr.operator == BinaryOperator::Equality {
&expr.alternate
} else {
&expr.consequent
}) && self.target >= ESTarget::ES2020
// TODO: this is probably a but too aggressive
&& matches!(check, Expression::Identifier(_))
{
return Some(ctx.ast.expression_logical(
SPAN,
ctx.ast.move_expression(&mut expr.consequent),
LogicalOperator::Coalesce,
ctx.ast.move_expression(&mut expr.alternate),
));
}

// TODO: `a != null ? a.b.c[d](e) : undefined` -> `a?.b.c[d](e)``
}
}
}

// Non esbuild optimizations

Expand Down Expand Up @@ -812,12 +847,13 @@ impl<'a> PeepholeMinimizeConditions {
#[cfg(test)]
mod test {
use oxc_allocator::Allocator;
use oxc_syntax::es_target::ESTarget;

use crate::tester;

fn test(source_text: &str, positive: &str) {
let allocator = Allocator::default();
let mut pass = super::PeepholeMinimizeConditions::new();
let mut pass = super::PeepholeMinimizeConditions::new(ESTarget::ES2025);
tester::test(&allocator, source_text, positive, &mut pass);
}

Expand Down Expand Up @@ -2003,7 +2039,8 @@ mod test {
test("var a; a ? b(c, d) : b(e, d)", "var a; b(a ? c : e, d)");
test("var a; a ? b(...c) : b(...e)", "var a; b(...a ? c : e)");
test("var a; a ? b(c) : b(e)", "var a; b(a ? c : e)");
// test("a != null ? a : b", "a ?? b");
test("a != null ? a : b", "a ?? b");
test_same("a() != null ? a() : b");
// test("a != null ? a.b.c[d](e) : undefined", "a?.b.c[d](e)");
}
}
6 changes: 4 additions & 2 deletions crates/oxc_minifier/src/compressor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ impl<'a> Compressor<'a> {
RemoveSyntax::new(self.options).build(program, &mut ctx);
// RemoveUnusedCode::new(self.options).build(program, &mut ctx);
Normalize::new().build(program, &mut ctx);
PeepholeOptimizations::new(true, self.options).run_in_loop(program, &mut ctx);
PeepholeOptimizations::new(false, self.options).build(program, &mut ctx);
PeepholeOptimizations::new(self.options.target, true, self.options)
.run_in_loop(program, &mut ctx);
PeepholeOptimizations::new(self.options.target, false, self.options)
.build(program, &mut ctx);
}

pub fn dead_code_elimination(self, program: &mut Program<'a>) {
Expand Down
20 changes: 10 additions & 10 deletions tasks/minsize/minsize.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@ Original | minified | minified | gzip | gzip | Fixture
-------------------------------------------------------------------------------------
72.14 kB | 23.70 kB | 23.70 kB | 8.61 kB | 8.54 kB | react.development.js

173.90 kB | 59.79 kB | 59.82 kB | 19.40 kB | 19.33 kB | moment.js
173.90 kB | 59.77 kB | 59.82 kB | 19.39 kB | 19.33 kB | moment.js

287.63 kB | 90.08 kB | 90.07 kB | 32.02 kB | 31.95 kB | jquery.js
287.63 kB | 90.02 kB | 90.07 kB | 32.01 kB | 31.95 kB | jquery.js

342.15 kB | 118.09 kB | 118.14 kB | 44.43 kB | 44.37 kB | vue.js
342.15 kB | 118.08 kB | 118.14 kB | 44.43 kB | 44.37 kB | vue.js

544.10 kB | 71.74 kB | 72.48 kB | 26.14 kB | 26.20 kB | lodash.js
544.10 kB | 71.72 kB | 72.48 kB | 26.15 kB | 26.20 kB | lodash.js

555.77 kB | 273.19 kB | 270.13 kB | 90.91 kB | 90.80 kB | d3.js
555.77 kB | 272.89 kB | 270.13 kB | 90.85 kB | 90.80 kB | d3.js

1.01 MB | 460.17 kB | 458.89 kB | 126.75 kB | 126.71 kB | bundle.min.js
1.01 MB | 460.13 kB | 458.89 kB | 126.74 kB | 126.71 kB | bundle.min.js

1.25 MB | 652.81 kB | 646.76 kB | 163.50 kB | 163.73 kB | three.js

2.14 MB | 726.23 kB | 724.14 kB | 180.12 kB | 181.07 kB | victory.js
2.14 MB | 725.99 kB | 724.14 kB | 180.05 kB | 181.07 kB | victory.js

3.20 MB | 1.01 MB | 1.01 MB | 331.78 kB | 331.56 kB | echarts.js
3.20 MB | 1.01 MB | 1.01 MB | 331.68 kB | 331.56 kB | echarts.js

6.69 MB | 2.32 MB | 2.31 MB | 492.64 kB | 488.28 kB | antd.js
6.69 MB | 2.32 MB | 2.31 MB | 492.60 kB | 488.28 kB | antd.js

10.95 MB | 3.49 MB | 3.49 MB | 907.46 kB | 915.50 kB | typescript.js
10.95 MB | 3.49 MB | 3.49 MB | 907.25 kB | 915.50 kB | typescript.js

Loading