mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(minifier): add MinimizeConditions pass (#5747)
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.");
```
This commit is contained in:
parent
b4ed564a35
commit
6bc13f6cd4
12 changed files with 231 additions and 176 deletions
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
//! Constant Folding
|
||||
//!
|
||||
//! <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeFoldConstants.java>
|
||||
|
||||
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
|
||||
///
|
||||
/// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeFoldConstants.java>
|
||||
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<bool> {
|
||||
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<Expression<'a>> {
|
||||
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<Expression<'a>> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
70
crates/oxc_minifier/src/ast_passes/minimize_conditions.rs
Normal file
70
crates/oxc_minifier/src/ast_passes/minimize_conditions.rs
Normal file
|
|
@ -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.
|
||||
///
|
||||
/// <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeMinimizeConditions.java>
|
||||
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<Expression<'a>> {
|
||||
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<Expression<'a>> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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 => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
68
crates/oxc_minifier/tests/ast_passes/minimize_conditions.rs
Normal file
68
crates/oxc_minifier/tests/ast_passes/minimize_conditions.rs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
//! <https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/PeepholeMinimizeConditionsTest.java>
|
||||
|
||||
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)");
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue