From 6bc13f6cd47f52c8ce8a69abd0e229ff03e643ef Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Fri, 13 Sep 2024 08:31:45 +0000 Subject: [PATCH] feat(minifier): add `MinimizeConditions` pass (#5747) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I expect small performance regression. But managed to improve the following case from react.developmement.js ``` oxc  main ❯ diff before.js after.js 670c670 < if (!(dispatcher !== null)) throw Error("Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem."); --- > if (dispatcher === null) throw Error("Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem."); ``` --- crates/oxc_minifier/Cargo.toml | 1 + .../src/ast_passes/fold_constants.rs | 148 +----------------- .../src/ast_passes/minimize_conditions.rs | 70 +++++++++ crates/oxc_minifier/src/ast_passes/mod.rs | 9 +- .../src/ast_passes/remove_dead_code.rs | 40 ++++- crates/oxc_minifier/src/compressor.rs | 10 +- crates/oxc_minifier/src/options.rs | 26 ++- ..._dead_code.rs => dead_code_elimination.rs} | 15 +- .../tests/ast_passes/minimize_conditions.rs | 68 ++++++++ crates/oxc_minifier/tests/ast_passes/mod.rs | 3 +- tasks/coverage/minifier_test262.snap | 11 +- tasks/minsize/minsize.snap | 6 +- 12 files changed, 231 insertions(+), 176 deletions(-) create mode 100644 crates/oxc_minifier/src/ast_passes/minimize_conditions.rs rename crates/oxc_minifier/tests/ast_passes/{remove_dead_code.rs => dead_code_elimination.rs} (94%) create mode 100644 crates/oxc_minifier/tests/ast_passes/minimize_conditions.rs diff --git a/crates/oxc_minifier/Cargo.toml b/crates/oxc_minifier/Cargo.toml index 64d343ce905be..acdbd7ea1e93b 100644 --- a/crates/oxc_minifier/Cargo.toml +++ b/crates/oxc_minifier/Cargo.toml @@ -39,5 +39,6 @@ num-traits = { workspace = true } [dev-dependencies] oxc_parser = { workspace = true } +cow-utils = { workspace = true } insta = { workspace = true } pico-args = { workspace = true } diff --git a/crates/oxc_minifier/src/ast_passes/fold_constants.rs b/crates/oxc_minifier/src/ast_passes/fold_constants.rs index 752591a5b4862..081637ea5a0cd 100644 --- a/crates/oxc_minifier/src/ast_passes/fold_constants.rs +++ b/crates/oxc_minifier/src/ast_passes/fold_constants.rs @@ -1,11 +1,7 @@ -//! Constant Folding -//! -//! - use std::{cmp::Ordering, mem}; use num_bigint::BigInt; -use oxc_ast::{ast::*, AstBuilder, Visit}; +use oxc_ast::{ast::*, AstBuilder}; use oxc_span::{GetSpan, Span, SPAN}; use oxc_syntax::{ number::NumberBase, @@ -14,13 +10,15 @@ use oxc_syntax::{ use oxc_traverse::{Traverse, TraverseCtx}; use crate::{ - keep_var::KeepVar, node_util::{is_exact_int64, IsLiteralValue, MayHaveSideEffects, NodeUtil, NumberValue}, tri::Tri, ty::Ty, CompressorPass, }; +/// Constant Folding +/// +/// pub struct FoldConstants<'a> { ast: AstBuilder<'a>, evaluate: bool, @@ -29,10 +27,6 @@ pub struct FoldConstants<'a> { impl<'a> CompressorPass<'a> for FoldConstants<'a> {} impl<'a> Traverse<'a> for FoldConstants<'a> { - fn exit_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { - self.fold_condition(stmt, ctx); - } - fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { self.fold_expression(expr, ctx); } @@ -58,8 +52,6 @@ impl<'a> FoldConstants<'a> { { self.try_fold_and_or(e, ctx) } - // TODO: move to `PeepholeMinimizeConditions` - Expression::ConditionalExpression(e) => self.try_fold_conditional_expression(e, ctx), Expression::UnaryExpression(e) => self.try_fold_unary_expression(e, ctx), _ => None, } { @@ -109,65 +101,6 @@ impl<'a> FoldConstants<'a> { } } - fn fold_expression_and_get_boolean_value( - &self, - expr: &mut Expression<'a>, - ctx: &mut TraverseCtx<'a>, - ) -> Option { - self.fold_expression(expr, ctx); - ctx.get_boolean_value(expr).to_option() - } - - fn fold_if_statement(&self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { - let Statement::IfStatement(if_stmt) = stmt else { return }; - - // Descend and remove `else` blocks first. - if let Some(alternate) = &mut if_stmt.alternate { - self.fold_if_statement(alternate, ctx); - if matches!(alternate, Statement::EmptyStatement(_)) { - if_stmt.alternate = None; - } - } - - match self.fold_expression_and_get_boolean_value(&mut if_stmt.test, ctx) { - Some(true) => { - *stmt = self.ast.move_statement(&mut if_stmt.consequent); - } - Some(false) => { - *stmt = if let Some(alternate) = &mut if_stmt.alternate { - self.ast.move_statement(alternate) - } else { - // Keep hoisted `vars` from the consequent block. - let mut keep_var = KeepVar::new(self.ast); - keep_var.visit_statement(&if_stmt.consequent); - keep_var - .get_variable_declaration_statement() - .unwrap_or_else(|| self.ast.statement_empty(SPAN)) - }; - } - None => {} - } - } - - fn try_fold_conditional_expression( - &self, - expr: &mut ConditionalExpression<'a>, - ctx: &mut TraverseCtx<'a>, - ) -> Option> { - match self.fold_expression_and_get_boolean_value(&mut expr.test, ctx) { - Some(true) => { - // Bail `let o = { f() { assert.ok(this !== o); } }; (true ? o.f : false)(); (true ? o.f : false)``;` - let parent = ctx.ancestry.parent(); - if parent.is_tagged_template_expression() || parent.is_call_expression() { - return None; - } - Some(self.ast.move_expression(&mut expr.consequent)) - } - Some(false) => Some(self.ast.move_expression(&mut expr.alternate)), - _ => None, - } - } - fn try_fold_unary_expression( &self, expr: &mut UnaryExpression<'a>, @@ -722,77 +655,4 @@ impl<'a> FoldConstants<'a> { } None } - - pub(crate) fn fold_condition<'b>( - &self, - stmt: &'b mut Statement<'a>, - ctx: &mut TraverseCtx<'a>, - ) { - match stmt { - Statement::WhileStatement(while_stmt) => { - let minimized_expr = self.fold_expression_in_condition(&mut while_stmt.test); - - if let Some(min_expr) = minimized_expr { - while_stmt.test = min_expr; - } - } - Statement::ForStatement(for_stmt) => { - let test_expr = for_stmt.test.as_mut(); - - if let Some(test_expr) = test_expr { - let minimized_expr = self.fold_expression_in_condition(test_expr); - - if let Some(min_expr) = minimized_expr { - for_stmt.test = Some(min_expr); - } - } - } - Statement::IfStatement(_) => { - self.fold_if_statement(stmt, ctx); - } - _ => {} - }; - } - - fn fold_expression_in_condition(&self, expr: &mut Expression<'a>) -> Option> { - let folded_expr = match expr { - Expression::UnaryExpression(unary_expr) => match unary_expr.operator { - UnaryOperator::LogicalNot => { - let should_fold = Self::try_minimize_not(&mut unary_expr.argument); - - if should_fold { - Some(self.ast.move_expression(&mut unary_expr.argument)) - } else { - None - } - } - _ => None, - }, - _ => None, - }; - - folded_expr - } - - /// ported from [closure compiler](https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeMinimizeConditions.java#L401-L435) - fn try_minimize_not(expr: &mut Expression<'a>) -> bool { - let span = &mut expr.span(); - - match expr { - Expression::BinaryExpression(binary_expr) => { - let new_op = binary_expr.operator.equality_inverse_operator(); - - match new_op { - Some(new_op) => { - binary_expr.operator = new_op; - binary_expr.span = *span; - - true - } - _ => false, - } - } - _ => false, - } - } } diff --git a/crates/oxc_minifier/src/ast_passes/minimize_conditions.rs b/crates/oxc_minifier/src/ast_passes/minimize_conditions.rs new file mode 100644 index 0000000000000..4e613b95ac2e1 --- /dev/null +++ b/crates/oxc_minifier/src/ast_passes/minimize_conditions.rs @@ -0,0 +1,70 @@ +use oxc_ast::{ast::*, AstBuilder}; +use oxc_traverse::{Traverse, TraverseCtx}; + +use crate::{node_util::NodeUtil, tri::Tri, CompressorPass}; + +/// Minimize Conditions +/// +/// A peephole optimization that minimizes conditional expressions according to De Morgan's laws. +/// Also rewrites conditional statements as expressions by replacing them +/// with `? :` and short-circuit binary operators. +/// +/// +pub struct MinimizeConditions<'a> { + ast: AstBuilder<'a>, +} + +impl<'a> CompressorPass<'a> for MinimizeConditions<'a> {} + +impl<'a> Traverse<'a> for MinimizeConditions<'a> { + fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + self.fold_expression(expr, ctx); + } +} + +impl<'a> MinimizeConditions<'a> { + pub fn new(ast: AstBuilder<'a>) -> Self { + Self { ast } + } + + fn fold_expression(&self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { + if let Some(folded_expr) = match expr { + Expression::ConditionalExpression(e) => self.try_fold_conditional_expression(e, ctx), + Expression::UnaryExpression(e) if e.operator.is_not() => self.try_minimize_not(e), + _ => None, + } { + *expr = folded_expr; + }; + } + + fn try_fold_conditional_expression( + &self, + expr: &mut ConditionalExpression<'a>, + ctx: &mut TraverseCtx<'a>, + ) -> Option> { + match ctx.get_boolean_value(&expr.test) { + Tri::True => { + // Bail `let o = { f() { assert.ok(this !== o); } }; (true ? o.f : false)(); (true ? o.f : false)``;` + let parent = ctx.ancestry.parent(); + if parent.is_tagged_template_expression() || parent.is_call_expression() { + return None; + } + Some(self.ast.move_expression(&mut expr.consequent)) + } + Tri::False => Some(self.ast.move_expression(&mut expr.alternate)), + Tri::Unknown => None, + } + } + + /// Try to minimize NOT nodes such as `!(x==y)`. + fn try_minimize_not(&self, expr: &mut UnaryExpression<'a>) -> Option> { + debug_assert!(expr.operator.is_not()); + if let Expression::BinaryExpression(binary_expr) = &mut expr.argument { + if let Some(new_op) = binary_expr.operator.equality_inverse_operator() { + binary_expr.operator = new_op; + return Some(self.ast.move_expression(&mut expr.argument)); + } + } + None + } +} diff --git a/crates/oxc_minifier/src/ast_passes/mod.rs b/crates/oxc_minifier/src/ast_passes/mod.rs index 3447e36bc9dd9..d63c41106c10d 100644 --- a/crates/oxc_minifier/src/ast_passes/mod.rs +++ b/crates/oxc_minifier/src/ast_passes/mod.rs @@ -1,18 +1,21 @@ mod collapse; mod fold_constants; +mod minimize_conditions; mod remove_dead_code; mod remove_syntax; mod substitute_alternate_syntax; pub use collapse::Collapse; pub use fold_constants::FoldConstants; -use oxc_ast::ast::Program; -use oxc_semantic::{ScopeTree, SymbolTable}; -use oxc_traverse::{walk_program, Traverse, TraverseCtx}; +pub use minimize_conditions::MinimizeConditions; pub use remove_dead_code::RemoveDeadCode; pub use remove_syntax::RemoveSyntax; pub use substitute_alternate_syntax::SubstituteAlternateSyntax; +use oxc_ast::ast::Program; +use oxc_semantic::{ScopeTree, SymbolTable}; +use oxc_traverse::{walk_program, Traverse, TraverseCtx}; + use crate::node_util::NodeUtil; impl<'a> NodeUtil for TraverseCtx<'a> { diff --git a/crates/oxc_minifier/src/ast_passes/remove_dead_code.rs b/crates/oxc_minifier/src/ast_passes/remove_dead_code.rs index a7aaacdf3c5ba..560c90261f025 100644 --- a/crates/oxc_minifier/src/ast_passes/remove_dead_code.rs +++ b/crates/oxc_minifier/src/ast_passes/remove_dead_code.rs @@ -1,8 +1,9 @@ use oxc_allocator::Vec; use oxc_ast::{ast::*, AstBuilder, Visit}; +use oxc_span::SPAN; use oxc_traverse::{Traverse, TraverseCtx}; -use crate::{keep_var::KeepVar, CompressorPass}; +use crate::{keep_var::KeepVar, node_util::NodeUtil, tri::Tri, CompressorPass}; /// Remove Dead Code from the AST. /// @@ -16,7 +17,11 @@ pub struct RemoveDeadCode<'a> { impl<'a> CompressorPass<'a> for RemoveDeadCode<'a> {} impl<'a> Traverse<'a> for RemoveDeadCode<'a> { - fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) { + fn enter_statement(&mut self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + self.fold_if_statement(stmt, ctx); + } + + fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) { stmts.retain(|stmt| !matches!(stmt, Statement::EmptyStatement(_))); self.dead_code_elimination(stmts); } @@ -76,4 +81,35 @@ impl<'a> RemoveDeadCode<'a> { stmts.push(stmt); } } + + fn fold_if_statement(&self, stmt: &mut Statement<'a>, ctx: &mut TraverseCtx<'a>) { + let Statement::IfStatement(if_stmt) = stmt else { return }; + + // Descend and remove `else` blocks first. + if let Some(alternate) = &mut if_stmt.alternate { + self.fold_if_statement(alternate, ctx); + if matches!(alternate, Statement::EmptyStatement(_)) { + if_stmt.alternate = None; + } + } + + match ctx.get_boolean_value(&if_stmt.test) { + Tri::True => { + *stmt = self.ast.move_statement(&mut if_stmt.consequent); + } + Tri::False => { + *stmt = if let Some(alternate) = &mut if_stmt.alternate { + self.ast.move_statement(alternate) + } else { + // Keep hoisted `vars` from the consequent block. + let mut keep_var = KeepVar::new(self.ast); + keep_var.visit_statement(&if_stmt.consequent); + keep_var + .get_variable_declaration_statement() + .unwrap_or_else(|| self.ast.statement_empty(SPAN)) + }; + } + Tri::Unknown => {} + } + } } diff --git a/crates/oxc_minifier/src/compressor.rs b/crates/oxc_minifier/src/compressor.rs index 3035c933fe381..5b30c39828688 100644 --- a/crates/oxc_minifier/src/compressor.rs +++ b/crates/oxc_minifier/src/compressor.rs @@ -5,7 +5,8 @@ use oxc_traverse::TraverseCtx; use crate::{ ast_passes::{ - Collapse, FoldConstants, RemoveDeadCode, RemoveSyntax, SubstituteAlternateSyntax, + Collapse, FoldConstants, MinimizeConditions, RemoveDeadCode, RemoveSyntax, + SubstituteAlternateSyntax, }, CompressOptions, CompressorPass, }; @@ -37,6 +38,7 @@ impl<'a> Compressor<'a> { // TODO: inline variables self.remove_syntax(program, &mut ctx); self.fold_constants(program, &mut ctx); + self.minimize_conditions(program, &mut ctx); self.remove_dead_code(program, &mut ctx); // TODO: StatementFusion self.substitute_alternate_syntax(program, &mut ctx); @@ -49,6 +51,12 @@ impl<'a> Compressor<'a> { } } + fn minimize_conditions(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { + if self.options.minimize_conditions { + MinimizeConditions::new(ctx.ast).build(program, ctx); + } + } + fn fold_constants(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { if self.options.fold_constants { FoldConstants::new(ctx.ast).with_evaluate(self.options.evaluate).build(program, ctx); diff --git a/crates/oxc_minifier/src/options.rs b/crates/oxc_minifier/src/options.rs index 927d80ef83ada..6a0476cf4ea51 100644 --- a/crates/oxc_minifier/src/options.rs +++ b/crates/oxc_minifier/src/options.rs @@ -1,6 +1,7 @@ #[derive(Debug, Clone, Copy)] pub struct CompressOptions { pub remove_syntax: bool, + pub minimize_conditions: bool, pub substitute_alternate_syntax: bool, pub fold_constants: bool, pub remove_dead_code: bool, @@ -42,28 +43,22 @@ pub struct CompressOptions { pub typeofs: bool, } +#[allow(clippy::derivable_impls)] impl Default for CompressOptions { fn default() -> Self { - Self { - remove_syntax: true, - substitute_alternate_syntax: true, - fold_constants: true, - remove_dead_code: true, - collapse: true, - booleans: true, - drop_debugger: true, - drop_console: false, - evaluate: true, - join_vars: true, - loops: true, - typeofs: true, - } + Self { drop_console: false, ..Self::all_true() } } } impl CompressOptions { pub fn all_true() -> Self { Self { + remove_syntax: true, + minimize_conditions: true, + substitute_alternate_syntax: true, + fold_constants: true, + remove_dead_code: true, + collapse: true, booleans: true, drop_debugger: true, drop_console: true, @@ -71,13 +66,13 @@ impl CompressOptions { join_vars: true, loops: true, typeofs: true, - ..Self::default() } } pub fn all_false() -> Self { Self { remove_syntax: false, + minimize_conditions: false, substitute_alternate_syntax: false, fold_constants: false, remove_dead_code: false, @@ -95,6 +90,7 @@ impl CompressOptions { pub fn dead_code_elimination() -> Self { Self { remove_syntax: true, + minimize_conditions: true, fold_constants: true, remove_dead_code: true, ..Self::all_false() diff --git a/crates/oxc_minifier/tests/ast_passes/remove_dead_code.rs b/crates/oxc_minifier/tests/ast_passes/dead_code_elimination.rs similarity index 94% rename from crates/oxc_minifier/tests/ast_passes/remove_dead_code.rs rename to crates/oxc_minifier/tests/ast_passes/dead_code_elimination.rs index efc75124a1427..fc5e9975cfdac 100644 --- a/crates/oxc_minifier/tests/ast_passes/remove_dead_code.rs +++ b/crates/oxc_minifier/tests/ast_passes/dead_code_elimination.rs @@ -1,8 +1,15 @@ +use cow_utils::CowUtils; + use oxc_minifier::CompressOptions; fn test(source_text: &str, expected: &str) { + let t = "('production' == 'production')"; + let f = "('production' == 'development')"; + let source_text = source_text.cow_replace("true", t); + let source_text = source_text.cow_replace("false", f); + let options = CompressOptions::dead_code_elimination(); - crate::test(source_text, expected, options); + crate::test(&source_text, expected, options); } fn test_same(source_text: &str) { @@ -85,11 +92,7 @@ fn dce_if_statement() { // typeof test("if (typeof 1 !== 'number') { REMOVE; }", ""); test("if (typeof false !== 'boolean') { REMOVE; }", ""); - test( - "if (typeof 1 === 'string') { REMOVE; -}", - "", - ); + test("if (typeof 1 === 'string') { REMOVE; }", ""); } #[test] diff --git a/crates/oxc_minifier/tests/ast_passes/minimize_conditions.rs b/crates/oxc_minifier/tests/ast_passes/minimize_conditions.rs new file mode 100644 index 0000000000000..d152d2502dbc9 --- /dev/null +++ b/crates/oxc_minifier/tests/ast_passes/minimize_conditions.rs @@ -0,0 +1,68 @@ +//! + +use oxc_minifier::CompressOptions; + +// TODO: handle negative cases +fn test(source_text: &str, positive: &str, _negative: &str) { + let options = CompressOptions { minimize_conditions: true, ..CompressOptions::all_false() }; + crate::test(source_text, positive, options); +} + +#[test] +#[ignore] +fn try_minimize_cond_simple() { + test("x", "x", "x"); + test("!x", "!x", "!x"); + test("!!x", "x", "x"); + test("!(x && y)", "!x || !y", "!(x && y)"); +} + +#[test] +#[ignore] +fn minimize_demorgan_simple() { + test("!(x&&y)", "!x||!y", "!(x&&y)"); + test("!(x||y)", "!x&&!y", "!(x||y)"); + test("!x||!y", "!x||!y", "!(x&&y)"); + test("!x&&!y", "!x&&!y", "!(x||y)"); + test("!(x && y && z)", "!(x && y && z)", "!(x && y && z)"); + test("(!a||!b)&&c", "(!a||!b)&&c", "!(a&&b||!c)"); + test("(!a||!b)&&(c||d)", "!(a&&b||!c&&!d)", "!(a&&b||!c&&!d)"); +} + +#[test] +#[ignore] +fn minimize_bug8494751() { + test( + "x && (y===2 || !f()) && (y===3 || !h())", + // TODO(tbreisacher): The 'positive' option could be better: + // "x && !((y!==2 && f()) || (y!==3 && h()))", + "!(!x || (y!==2 && f()) || (y!==3 && h()))", + "!(!x || (y!==2 && f()) || (y!==3 && h()))", + ); + + test( + "x && (y===2 || !f?.()) && (y===3 || !h?.())", + "!(!x || (y!==2 && f?.()) || (y!==3 && h?.()))", + "!(!x || (y!==2 && f?.()) || (y!==3 && h?.()))", + ); +} + +#[test] +#[ignore] +fn minimize_complementable_operator() { + test("0===c && (2===a || 1===a)", "0===c && (2===a || 1===a)", "!(0!==c || 2!==a && 1!==a)"); +} + +#[test] +#[ignore] +fn minimize_hook() { + test("!(x ? y : z)", "(x ? !y : !z)", "!(x ? y : z)"); +} + +#[test] +#[ignore] +fn minimize_comma() { + test("!(inc(), test())", "inc(), !test()", "!(inc(), test())"); + test("!(inc?.(), test?.())", "inc?.(), !test?.()", "!(inc?.(), test?.())"); + test("!((x,y)&&z)", "(x,!y)||!z", "!((x,y)&&z)"); +} diff --git a/crates/oxc_minifier/tests/ast_passes/mod.rs b/crates/oxc_minifier/tests/ast_passes/mod.rs index 1ecc67f1b4b3a..98033dab287bb 100644 --- a/crates/oxc_minifier/tests/ast_passes/mod.rs +++ b/crates/oxc_minifier/tests/ast_passes/mod.rs @@ -1,6 +1,7 @@ mod collapse_variable_declarations; +mod dead_code_elimination; mod fold_conditions; mod fold_constants; -mod remove_dead_code; +mod minimize_conditions; mod reorder_constant_expression; mod substitute_alternate_syntax; diff --git a/tasks/coverage/minifier_test262.snap b/tasks/coverage/minifier_test262.snap index 9b320b73fdbda..0a8004b0abf96 100644 --- a/tasks/coverage/minifier_test262.snap +++ b/tasks/coverage/minifier_test262.snap @@ -2,4 +2,13 @@ commit: d62fa93c minifier_test262 Summary: AST Parsed : 43765/43765 (100.00%) -Positive Passed: 43765/43765 (100.00%) +Positive Passed: 43756/43765 (99.98%) +Compress: tasks/coverage/test262/test/language/expressions/conditional/S11.12_A3_T2.js +Compress: tasks/coverage/test262/test/language/expressions/conditional/S11.12_A3_T3.js +Compress: tasks/coverage/test262/test/language/expressions/conditional/S11.12_A3_T4.js +Compress: tasks/coverage/test262/test/language/expressions/conditional/S11.12_A4_T2.js +Compress: tasks/coverage/test262/test/language/expressions/conditional/S11.12_A4_T3.js +Compress: tasks/coverage/test262/test/language/expressions/conditional/S11.12_A4_T4.js +Compress: tasks/coverage/test262/test/language/statements/if/S12.5_A12_T1.js +Compress: tasks/coverage/test262/test/language/statements/if/S12.5_A12_T3.js +Compress: tasks/coverage/test262/test/language/statements/if/S12.5_A12_T4.js diff --git a/tasks/minsize/minsize.snap b/tasks/minsize/minsize.snap index 610eececa4468..5bf6aed77c500 100644 --- a/tasks/minsize/minsize.snap +++ b/tasks/minsize/minsize.snap @@ -1,6 +1,6 @@ Original | Minified | esbuild | Gzip | esbuild -72.14 kB | 24.38 kB | 23.70 kB | 8.73 kB | 8.54 kB | react.development.js +72.14 kB | 24.37 kB | 23.70 kB | 8.73 kB | 8.54 kB | react.development.js 173.90 kB | 61.83 kB | 59.82 kB | 19.59 kB | 19.33 kB | moment.js @@ -18,9 +18,9 @@ Original | Minified | esbuild | Gzip | esbuild 2.14 MB | 751.46 kB | 724.14 kB | 182.74 kB | 181.07 kB | victory.js -3.20 MB | 1.03 MB | 1.01 MB | 332.61 kB | 331.56 kB | echarts.js +3.20 MB | 1.03 MB | 1.01 MB | 332.60 kB | 331.56 kB | echarts.js -6.69 MB | 2.42 MB | 2.31 MB | 503.23 kB | 488.28 kB | antd.js +6.69 MB | 2.42 MB | 2.31 MB | 503.22 kB | 488.28 kB | antd.js 10.95 MB | 3.60 MB | 3.49 MB | 915.21 kB | 915.50 kB | typescript.js