oxc/crates/oxc_minifier/tests/ast_passes/dead_code_elimination.rs
Boshen 6bc13f6cd4 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.");
```
2024-09-13 08:31:45 +00:00

221 lines
5.7 KiB
Rust

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);
}
fn test_same(source_text: &str) {
test(source_text, source_text);
}
#[test]
fn dce_if_statement() {
test("if (true) { foo }", "{ foo }");
test("if (true) { foo } else { bar }", "{ foo }");
test("if (false) { foo } else { bar }", "{ bar }");
test("if (xxx) { foo } else if (false) { bar }", "if (xxx) { foo }");
test("if (xxx) { foo } else if (false) { bar } else { baz }", "if (xxx) { foo } else { baz }");
test("if (xxx) { foo } else if (false) { bar } else if (false) { baz }", "if (xxx) { foo }");
test(
"if (xxx) { foo } else if (false) { bar } else if (false) { baz } else { quaz }",
"if (xxx) { foo } else { quaz }",
);
test(
"if (xxx) { foo } else if (true) { bar } else if (false) { baz }",
"if (xxx) { foo } else { bar }",
);
test(
"if (xxx) { foo } else if (false) { bar } else if (true) { baz }",
"if (xxx) { foo } else { baz }",
);
test(
"if (xxx) { foo } else if (true) { bar } else if (true) { baz }",
"if (xxx) { foo } else { bar }",
);
test(
"if (xxx) { foo } else if (false) { var a; var b; } else if (false) { var c; var d; }",
"if (xxx) { foo } else var c, d;",
);
test("if (!false) { foo }", "{ foo }");
test("if (!true) { foo } else { bar }", "{ bar }");
test("if (!false && xxx) { foo }", "if (xxx) { foo; }");
test("if (!true && yyy) { foo } else { bar }", "{ bar }");
test("if (true || xxx) { foo }", "{ foo }");
test("if (false || xxx) { foo }", "if (xxx) { foo }");
test("if ('production' == 'production') { foo } else { bar }", "{ foo }");
test("if ('development' == 'production') { foo } else { bar }", "{ bar }");
test("if ('production' === 'production') { foo } else { bar }", "{ foo }");
test("if ('development' === 'production') { foo } else { bar }", "{ bar }");
// Shadowed `undefined` as a variable should not be erased.
// This is a rollup test.
test_same("function foo(undefined) { if (!undefined) { } }");
test("function foo() { if (undefined) { bar } }", "function foo() { }");
test_same("function foo() { { bar } }");
test("if (true) { foo; } if (true) { foo; }", "{ foo; } { foo; }");
test(
"
if (true) { foo; return }
foo;
if (true) { bar; return }
bar;
",
"{foo; return }",
);
// nested expression
test(
"const a = { fn: function() { if (true) { foo; } } }",
"const a = { fn: function() { { foo; } } }",
);
// parenthesized
test("if (!!(false)) { REMOVE; } else { KEEP; }", "{ KEEP }");
// typeof
test("if (typeof 1 !== 'number') { REMOVE; }", "");
test("if (typeof false !== 'boolean') { REMOVE; }", "");
test("if (typeof 1 === 'string') { REMOVE; }", "");
}
#[test]
fn dce_conditional_expression() {
test("false ? foo : bar;", "bar");
test("true ? foo : bar;", "foo");
test("!true ? foo : bar;", "bar");
test("!false ? foo : bar;", "foo");
test("!!false ? foo : bar;", "bar");
test("!!true ? foo : bar;", "foo");
test("const foo = true ? A : B", "const foo = A");
test("const foo = false ? A : B", "const foo = B");
}
#[test]
fn dce_logical_expression() {
test("false && bar()", "false");
test("true && bar()", "bar()");
test("const foo = false && bar()", "const foo = false");
test("const foo = true && bar()", "const foo = bar()");
}
#[test]
fn dce_var_hoisting() {
test(
"function f() {
return () => {
var x;
}
REMOVE;
function KEEP() {}
REMOVE;
}",
"function f() {
return () => {
var x;
}
function KEEP() {}
}",
);
test(
"function f() {
return function g() {
var x;
}
REMOVE;
function KEEP() {}
REMOVE;
}",
"function f() {
return function g() {
var x;
}
function KEEP() {}
}",
);
}
// https://github.com/terser/terser/blob/master/test/compress/dead-code.js
#[test]
fn dce_from_terser() {
test(
"function f() {
a();
b();
x = 10;
return;
if (x) {
y();
}
}",
"function f() {
a();
b();
x = 10;
return;
}",
);
// NOTE: `if (x)` is changed to `if (true)` because const inlining is not implemented yet.
test(
r#"function f() {
g();
x = 10;
throw new Error("foo");
if (true) {
y();
var x;
function g(){};
(function(){
var q;
function y(){};
})();
}
}
f();
"#,
r#"function f() {
g();
x = 10;
throw new Error("foo");
var x;
}
f();
"#,
);
test(
"if (0) {
let foo = 6;
const bar = 12;
class Baz {};
var qux;
}
console.log(foo, bar, Baz);
",
"
var qux;
console.log(foo, bar, Baz);
",
);
}