Commit Graph

377 Commits

Author SHA1 Message Date
camc314 76b1dc7a9e fix(minifier): avoid incorrect logical assignment transformation when base object may be mutated (#16802)
Fixes #16647

Use `symbol_is_mutated()` to check if the member expression base object may be reassigned, preventing incorrect transformations like `x.y || (x = {}, x.y = 3)` to `x.y ||= (x = {}, 3)`.

🤖 generated with help from Claude Opus 4.5
2025-12-21 02:09:53 +00:00
Armano 3d2b492def feat(minifier): fold iife arrow functions in call expressions (#16477)
Improve folding of iife 

## details

- check all `CallExpression` if they can be collapsed
  eg. passed as arguments or in assignment
- functions without arguments and body can be collapsed to to `void 0`
 `(() => {})()` or `(function () { })()` to `void 0`
- with one statement and without return will be collapsed to `(c, void
0)`
  `(() => { fn() })()` to `(fn(), void 0)`
- `FunctionExpression` is not supported
  `(function () { ... })()`

## notes

- function with arguments could be handled by first in-lining them
- empty function with arguments could be also if all arguments do not
have defaults
- we should consider optimizing function definitions by removing not
used arguments
- initially i added support for `FunctionExpression` but there is no way
to track `this` usage, and thus we are unable to minify this

## change example

code
```js
{
  let AOT = true;
  const getAOT = /* @__NO_SIDE_EFFECTS__ */ () => AOT;
  const fn = getAOT() ? () => console.log("Hi") : () => {};
  fn();
}
```

currently is collapsed to 

```js
(/* @__PURE__ */ /* @__NO_SIDE_EFFECTS__ */ (() => !0)() ? () => console.log("Hi") : () => {})();
```

and with this change

```js
console.log('Hi');
```

------------------

from what i can see @sapphi-red worked on this file

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2025-12-18 12:19:46 +09:00
sapphi-red 9cc20a1de7 fix(minifier): avoid merging side effectful expressions to next assignment statement if the side effect may change the left hand side reference (#16165)
fixes https://github.com/rolldown/rolldown/issues/7093

```js
function wrapper() { let foo; const bar = (() => { foo = {} })(); foo.bar = bar; return foo }
```
cannot be compressed to
```js
function wrapper() { let foo; return foo.bar = (() => { foo = {} })(), foo }
```
because `foo` is read on left hand side.
2025-11-27 07:35:43 +00:00
sapphi-red b3d6f45d37 test(all): check parse errors existence (#15865)
Added `assert!(ret.errors.is_empty());` in many places so that invalid inputs are caught properly.
2025-11-19 13:56:55 +00:00
sapphi-red 68703b9fac feat(minifier): rotate binary expressions to remove parentheses (#15473)
Compress `a | (b | c)` to `a | b | c` and `a * (b % c)` to `b % c * a`.
2025-11-11 16:17:53 +00:00
sapphi-red 5d3114cf11 fix(mangler): allow using typescript keywords as variable names (#14315)
`as`, `any` should be allowed to use as a variable name.
2025-10-03 15:40:28 +00:00
sapphi-red 6b2daa80d1 fix(minifier): don't inline single use variable in conditional logical expressions (#14185)
https://github.com/oxc-project/oxc/blob/358f2fccd3183b7987332cb321b946fe31a85691/crates/oxc_minifier/src/peephole/minimize_statements.rs#L1427-L1429
This should be handled in logical expressions as well, but was not handled.
2025-10-01 05:12:53 +00:00
sapphi-red 6123684e50 feat(minifier): inline single-use variable past read-only variables (#14184)
fixes #13912

As long as we know the variable is not re-assigned, the value is same even if we reordered the expression.
By using that fact, we can compress
```js
function wrapper() { var x = foo, y = bar; return [x, y] }
```
to
```js
function wrapper() { return [foo, bar] }
```
.
2025-10-01 05:12:52 +00:00
夕舞八弦 358f2fccd3 fix(minifier): remove continue in for-in / for-of (#14151)
fix: #14063
2025-09-27 21:02:58 +09:00
sapphi-red 402e6c7602 feat(minifier): inline single use variables within the same variable declarations (#13959)
Compress
```js
var a = foo, b = a;
```
into
```js
var b = foo;
```
refs #13912
2025-09-27 08:48:19 +00:00
sapphi-red d02d750ab8 fix(mangler): mangle non top-level exports variable (#14169)
```js
var a = function(exports) {
  exports.foo = 'foo'
}
a(exports)
```
The `exports` parameter was not mangled. But it is fine to mangle this.
We need to keep the global `exports` variable as-is, but that is handled by keeping the global variable references.

refs #5965
2025-09-27 06:38:36 +00:00
sapphi-red 1337811da6 feat(minifier): evaluate .concat calls that has subsequent method calls (#14074)
```js
x = ''.concat('a', ' ').concat('b').split(/[\\s\\n]+/)
```
was not compressed to
```js
x = 'a b'.split(/[\\s\\n]+/)
```
. This PR changes the code to allow that.
2025-09-25 15:31:04 +00:00
sapphi-red c364ad195a feat(minifier): support ForStatements for single use variable inlining (#13755)
Inline single use variables in for statements:
```js
function wrapper1() { var x = foo; for (var i = x; i < 10; i++) console.log(i) }
function wrapper2() { var i, x = foo; for (i = x; i < 10; i++) console.log(i) }
// to
function wrapper1() { for (var i = foo; i < 10; i++) console.log(i) }
function wrapper2() { var i; for (i = foo; i < 10; i++) console.log(i) }
```
```js
function wrapper() { var x = {}; for (var a in x) console.log(a) }
// to
function wrapper() { for (var a in {}) console.log(a) }
```
```js
function wrapper() { var x = []; for (var a of x) console.log(a) }
// to
function wrapper() { for (var a of []) console.log(a) }
```

refs #13277
2025-09-14 12:55:38 +00:00
sapphi-red 52f3e8977e feat(minifier): remove unused variables in for init (#13508)
Remove unused variables in for initializer.
2025-09-01 14:56:32 +00:00
sapphi-red b4dfddd677 feat(minifier): store symbol information for for init variables (#13507)
Store the symbol information for "for init variables" so that the transformation that relies on it can use it.
2025-09-01 14:56:32 +00:00
sapphi-red 05def8cd1e feat(minifier): constant fold RegExp.prototype.source (#13472)
Fold `/foo/.source` to `"foo"`.
2025-08-31 15:15:50 +00:00
sapphi-red 2931356a4a feat(minifier): inline single use functions past sideeffectful expressions (#13426)
Allowed literal functions to be inlined past sideeffectful expressions as it's in the same scope.
https://github.com/oxc-project/oxc/blob/b30bff56717c54028833a4a3cba0336be50ab60b/crates/oxc_ecmascript/src/constant_evaluation/is_literal_value.rs#L5-L18

close #13109
2025-08-30 20:39:12 +09:00
sapphi-red a40140aa21 feat(minifier): use is_literal_value in substitute_single_use_symbol_in_expression (#13364)
Use `is_literal_value` instead of `is_literal` to allow more cases to be inlined.
2025-08-29 02:55:34 +00:00
sapphi-red a19e84f40a feat(minifier): merge assignment to variable declaration (#13269)
Compress `var a; a = 0` into `var a = 0`.
2025-08-24 10:48:50 +00:00
sapphi-red 07afa7051d feat(minifier): support remaining expressions for single use variable inlining (#13280)
Improved `substitute_single_use_symbol_in_expression` to support the remaining expressions.
2025-08-24 08:42:53 +00:00
sapphi-red a951eeed0c feat(minifier): support more statements for single use variable inlining (#13277)
Added `substitute_single_use_symbol_in_statement` calls to other statements like esbuild does.
2025-08-24 08:42:53 +00:00
sapphi-red dcda3a2a2d feat(minifier): support more expressions for single use variable inlining (#13276)
Improved `substitute_single_use_symbol_in_expression` to support more expressions by porting more code from esbuild.
2025-08-24 08:42:52 +00:00
sapphi-red 66d6005bb5 feat(minifier): implement basic single use variable inlining (#13275)
Ported the basic parts of esbuild's `substituteSingleUseSymbolInStmt`.
This would allow compressing
```js
let x = fn();
return x;
```
into
```js
return fn();
```
2025-08-24 08:42:52 +00:00
sapphi-red 53f55a454f feat(minifier): remove unnecessary parenthesis from nested optional chaining (#13268)
Flatten nested chain expressions like `(a?.b)?.c` to `a?.b?.c`.
2025-08-24 08:42:51 +00:00
sapphi-red 8ca9909035 feat(minifier): remove unused assignments for vars (#13231)
I found that the temporary variable are kept. This change would remove them.
Variables that were declared with `var` did not have `SymbolValue` populated and that caused those variables to bail out at this line.
https://github.com/oxc-project/oxc/blob/2141c18540803218889aa3c3f1bca5f51cdb5daa/crates/oxc_minifier/src/peephole/remove_unused_expression.rs#L624-L626

I think TDZ needs to be considered when inlining the values, but does not affect removing the unused assignments.
2025-08-24 08:42:51 +00:00
sapphi-red cae222c206 feat(minifier): compress return void foo() => foo(); return (#13271)
Compress `return void foo()` => `foo(); return`.
`return void 0` and `return undefined` was handled but this wasn't.
2025-08-24 04:31:52 +00:00
Boshen fbe6663518 feat(minifier): mark more known global methods as side-effect free (#13086)
closes #13067
2025-08-16 03:42:22 +00:00
sapphi-red 5dfb40ec07 feat(minifier): drop var r = [...arguments] if r is not used (#13115)
Avoid generating `var r = [...arguments]` if `r` is not used to reduce the number of iterations.
This would also remove `[...arguments].slice()`, which would not be removed currently because we don't treat `Array::slice` as sideeffect free.
2025-08-15 13:04:09 +00:00
sapphi-red 3d0d31aa81 feat(minifier): rewrite arguments copy loops to spread syntax (#13114)
Reverse-transpile rest argument code generated by TypeScript and babel.

Example input:
```js
for (var e = arguments.length, r = Array(e), a = 0; a < e; a++)
  r[a] = arguments[a];
```
output:
```js
var r = [...arguments];
```

It's a quite specific transform though.
2025-08-15 13:04:09 +00:00
sapphi-red dea41dc132 feat(minifier): compress Object(expr)(args) to (0, expr)(args) (#13092)
Compress `Object(expr)(args)` to `(0, expr)(args)`.

This can be done because
- the function call throws an error if the callee is not a function.
- `Object(expr)` would output a function if and only if `expr` is a function.

---

**References**

- [spec of `Object()`](https://tc39.es/ecma262/2025/multipage/fundamental-objects.html#sec-object-value)
2025-08-15 11:26:25 +00:00
Boshen 73a6f25d94 refactor(minifier): inline statement methods (#13044) 2025-08-13 11:50:40 +08:00
Boshen 993db8915c feat(minifier): .minify and .dce methods; run dce in loop (#13026) 2025-08-12 14:58:41 +00:00
Boshen 7b9a781047 feat(minifier): return total number of iterations for debugging (#12995) 2025-08-11 15:51:26 +00:00
sapphi-red 577d742e09 feat(minifier): logical assignment with sequential (#12957)
Compress `x || (a, x = 3)` into `x ||= (a, 3)`.

refs #8315
2025-08-10 08:59:02 +00:00
Boshen a286dd4a5b feat(minifier): remove unnecessary 'use strict' directive (#12642) 2025-07-31 05:07:44 +00:00
Boshen 763a6189b8 feat(minifier): inline small constant values (#12639)
closes #2646
2025-07-30 14:17:44 +00:00
Boshen b877039e43 feat(minifier): inline const variables that are only used once (#12488)
Just to get the infrastructure ready.

Other "inline"s are more complicated due to TDZ.
2025-07-24 08:08:10 +00:00
sapphi-red c135beb1b5 feat(codegen): keep function expression PIFEs (#12470)
Made function expression PIFEs to be kept. Similar to #12353 but for function expressions.

The minsize for victory is bigger because webpack's output contains many PIFEs (some shouldn't be written as PIFEs though).

refs #12353
2025-07-24 00:49:59 +00:00
Boshen cc3bea44f7 refactor(minifier): do not remove unused assignment expression yet (#12367)
it's broken for

```js
export let foo;
foo = 1;
```

closes #12340
2025-07-17 14:13:02 +00:00
Boshen ed696b5725 fix(minifier): remove more unused assignment expressions (#12364) 2025-07-17 14:04:57 +00:00
Boshen 8777839117 fix(minifier): improve remove unused variable declaration (#12351)
In script mode:

```js
var x;
```

must be preserved.

fixes #12341

I took the opportunity to refactor these test cases, and also apply "remove unused assignment expression" to all expressions instead of lone assignment expression.

```js
function foo() {
  var x = 1;
  return x = 2;
}
```
2025-07-17 12:45:50 +00:00
Boshen 1b80633434 feat(minifier): remove unused function declaration (#12318) 2025-07-16 10:29:07 +00:00
Boshen 3f33e8c8b5 feat(minifier): remove unused assignment expression (#12314)
```
var x = 1;
x = 2; // remove
```

Remove the entier thing if the binding has no read references.
2025-07-16 07:37:21 +00:00
Boshen fb8289c5d3 feat(minifier): remove unused variable declaration (#11796)
closes #10033
2025-07-16 13:31:44 +08:00
Boshen cd90549955 feat(tasks/minsize): generate a quick diff for inspection (#12238) 2025-07-12 10:05:04 +00:00
Boshen 2cdf722e6c feat(minifier): constant fold small integer multiplication (n <= 255) (#12236)
ref: https://github.com/evanw/esbuild/commit/492e299ce6fa15ee237234887711e3f461fff415
2025-07-12 03:46:46 +00:00
Boshen 50fd11eec1 fix(minifier): always escape $ when concatenating template literals (#12029)
fixes #11840
2025-07-02 09:29:03 +00:00
Boshen b01cb45578 feat(codegen): a way to keep legal comments after minification (#10689)
fixes https://github.com/rolldown/rolldown/issues/4118
fixes #10626

At present, there is no option to keep legal comments when minification is turn on.

This PR allows Codegen to set legal comments with minfication turned on, with the following behavior:

* Inline: only if the attached code is not removed
* Eof: move all legal comments to the end of file, regardless of where they appear
* Linked and External: saved into `CodegenReturn::legal_comments` for the caller to use, regardless of where they appear

Code gets moved around or removed if minifier is turned on, so some legal comments may disappear from the final output.

The recommended legal comment option is Eof.

---

For Rolldown to keep legal comments, it should set `LegalComment::Inline` for chunk codegen and `LegalComment::Eof` for minification codegen.
2025-05-01 06:31:34 +00:00
Boshen 315143ae42 refactor(codegen)!: remove useless CodeGenerator type alias (#10702) 2025-04-29 17:01:07 +00:00
Boshen 1962bc67d4 feat(transformer_plugins): split out oxc_transformer_plugins crate (#10617) 2025-04-25 08:39:56 +00:00