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
2 changes: 2 additions & 0 deletions crates/oxc_minifier/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ The compressor is responsible for rewriting statements and expressions for minim
- [Properties of the global object defined in the ECMAScript spec](https://tc39.es/ecma262/multipage/global-object.html#sec-global-object) behaves the same as in the spec
- Examples of properties: `Infinity`, `parseInt`, `Object`, `Promise.resolve`
- Examples that breaks this assumption: `globalThis.Object = class MyObject {}`
- The code does not rely on the `name` property of `Function` or `Class`
- Examples that breaks this assumption: `function fn() {}; console.log(f.name === 'fn')`
- [`document.all`](https://tc39.es/ecma262/multipage/additional-ecmascript-features-for-web-browsers.html#sec-IsHTMLDDA-internal-slot) is not used or behaves as a normal object
- Examples that breaks this assumption: `console.log(typeof document.all === 'undefined')`
- TDZ violation does not happen
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,9 +426,10 @@ impl<'a> PeepholeOptimizations {
if !matches!(consequent.left, AssignmentTarget::AssignmentTargetIdentifier(_)) {
return None;
}
if consequent.right.is_anonymous_function_definition() {
return None;
}
// TODO: need this condition when `keep_fnames` is introduced
// if consequent.right.is_anonymous_function_definition() {
// return None;
// }
if consequent.operator != AssignmentOperator::Assign
|| consequent.operator != alternate.operator
|| consequent.left.content_ne(&alternate.left)
Expand Down
9 changes: 5 additions & 4 deletions crates/oxc_minifier/src/peephole/minimize_conditions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1558,15 +1558,16 @@ mod test {
test("x ? a = 0 : a = 1", "a = x ? 0 : 1");
test(
"x ? a = function foo() { return 'a' } : a = function bar() { return 'b' }",
"a = x ? function foo() { return 'a' } : function bar() { return 'b' }",
"a = x ? function () { return 'a' } : function () { return 'b' }",
);

// a.b might have a side effect
test_same("x ? a.b = 0 : a.b = 1");
// `a = x ? () => 'a' : () => 'b'` does not set the name property of the function
test_same("x ? a = () => 'a' : a = () => 'b'");
test_same("x ? a = function () { return 'a' } : a = function () { return 'b' }");
test_same("x ? a = class { foo = 'a' } : a = class { foo = 'b' }");
// TODO: need to pass these tests when `keep_fnames` are introduced
// test_same("x ? a = () => 'a' : a = () => 'b'");
// test_same("x ? a = function () { return 'a' } : a = function () { return 'b' }");
// test_same("x ? a = class { foo = 'a' } : a = class { foo = 'b' }");

// for non `=` operators, `GetValue(lref)` is called before `Evaluation of AssignmentExpression`
// so cannot be fold to `a += x ? 0 : 1`
Expand Down
39 changes: 36 additions & 3 deletions crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,7 @@ impl<'a> LatePeepholeOptimizations {
if let Expression::NewExpression(e) = expr {
Self::try_compress_typed_array_constructor(e, ctx);
}
Self::remove_name_from_expressions(expr, ctx);

if let Some(folded_expr) = match expr {
Expression::BooleanLiteral(_) => Self::try_compress_boolean(expr, ctx),
Expand Down Expand Up @@ -1085,6 +1086,30 @@ impl<'a> LatePeepholeOptimizations {
}
}

/// Remove name from function / class expressions if it is not used.
///
/// - `var a = function f() {}` -> `var a = function () {}`
/// - `var a = class C {}` -> `var a = class {}`
///
/// This compression is not safe if the code relies on `Function::name`.
fn remove_name_from_expressions(expr: &mut Expression<'a>, ctx: Ctx<'a, '_>) {
match expr {
Expression::FunctionExpression(func) => {
if func.id.as_ref().is_some_and(|id| !ctx.symbols().symbol_is_used(id.symbol_id()))
{
func.id = None;
}
}
Expression::ClassExpression(class) => {
if class.id.as_ref().is_some_and(|id| !ctx.symbols().symbol_is_used(id.symbol_id()))
{
class.id = None;
}
}
_ => {}
}
}

/// Whether the name matches any TypedArray name.
///
/// See <https://tc39.es/ecma262/multipage/indexed-collections.html#sec-typedarray-objects> for the list of TypedArrays.
Expand Down Expand Up @@ -1259,7 +1284,7 @@ mod test {
test("x = new Object()", "x = ({})");
test("x = Object()", "x = ({})");

test_same("x = (function f(){function Object(){this.x=4}return new Object();})();");
test_same("x = (function (){function Object(){this.x=4}return new Object();})();");

test("x = new window.Object", "x = ({})");
test("x = new window.Object()", "x = ({})");
Expand All @@ -1269,8 +1294,8 @@ mod test {
test("x = window.Object?.()", "x = Object?.()");

test(
"x = (function f(){function Object(){this.x=4};return new window.Object;})();",
"x = (function f(){function Object(){this.x=4}return {};})();",
"x = (function (){function Object(){this.x=4};return new window.Object;})();",
"x = (function (){function Object(){this.x=4}return {};})();",
);
}

Expand Down Expand Up @@ -1699,6 +1724,14 @@ mod test {
);
}

#[test]
fn test_remove_name_from_expressions() {
test("var a = function f() {}", "var a = function () {}");
test_same("var a = function f() { return f; }");
test("var a = class C {}", "var a = class {}");
test_same("var a = class C { foo() { return C } }");
}

#[test]
fn test_compress_is_null_and_to_nullish_coalescing() {
test("x == null && y", "x ?? y");
Expand Down
16 changes: 8 additions & 8 deletions tasks/minsize/minsize.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@ Original | minified | minified | gzip | gzip | Fixture

173.90 kB | 59.54 kB | 59.82 kB | 19.18 kB | 19.33 kB | moment.js

287.63 kB | 89.45 kB | 90.07 kB | 30.97 kB | 31.95 kB | jquery.js
287.63 kB | 89.44 kB | 90.07 kB | 30.96 kB | 31.95 kB | jquery.js

342.15 kB | 117.61 kB | 118.14 kB | 43.45 kB | 44.37 kB | vue.js
342.15 kB | 117.46 kB | 118.14 kB | 43.33 kB | 44.37 kB | vue.js

544.10 kB | 71.40 kB | 72.48 kB | 25.86 kB | 26.20 kB | lodash.js

555.77 kB | 271.11 kB | 270.13 kB | 88.26 kB | 90.80 kB | d3.js

1.01 MB | 440.89 kB | 458.89 kB | 122.52 kB | 126.71 kB | bundle.min.js
1.01 MB | 440.48 kB | 458.89 kB | 122.41 kB | 126.71 kB | bundle.min.js

1.25 MB | 650.30 kB | 646.76 kB | 160.95 kB | 163.73 kB | three.js
1.25 MB | 647.42 kB | 646.76 kB | 160.30 kB | 163.73 kB | three.js

2.14 MB | 716.92 kB | 724.14 kB | 161.98 kB | 181.07 kB | victory.js
2.14 MB | 716.51 kB | 724.14 kB | 161.81 kB | 181.07 kB | victory.js

3.20 MB | 1.01 MB | 1.01 MB | 324.32 kB | 331.56 kB | echarts.js
3.20 MB | 1.01 MB | 1.01 MB | 324.27 kB | 331.56 kB | echarts.js

6.69 MB | 2.28 MB | 2.31 MB | 467.77 kB | 488.28 kB | antd.js
6.69 MB | 2.28 MB | 2.31 MB | 466.21 kB | 488.28 kB | antd.js

10.95 MB | 3.36 MB | 3.49 MB | 861.77 kB | 915.50 kB | typescript.js
10.95 MB | 3.35 MB | 3.49 MB | 861.48 kB | 915.50 kB | typescript.js

Loading