Commit graph

124 commits

Author SHA1 Message Date
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
Boshen
746f7b3a02 refactor(minifier): align code with closure compiler (#5717)
also fixes #4341
2024-09-12 04:18:41 +00:00
Boshen
21e2df57a0 refactor(minifier): replace VisitMut with Traverse for inject and define plugins (#5705)
closes #5704
2024-09-11 16:13:10 +00:00
Boshen
b8f8dd6612 fix(minifier/replace_global_defines): do not replace shadowed identifiers (#5691) 2024-09-11 07:17:17 +00:00
Boshen
68c3cf544f feat(minifier): fold void 1 -> void 0 (#5670) 2024-09-10 08:11:10 +00:00
Boshen
c6bbf94f4c feat(minifier): constant fold unary expression (#5669) 2024-09-10 07:18:54 +00:00
Boshen
86256ea238 feat(minifier): constant fold typeof (#5666)
closes #5628
2024-09-10 04:43:00 +00:00
Boshen
4a8aec1605 feat(span)!: change SourceType::js to SourceType::cjs and SourceType::mjs (#5606) 2024-09-08 14:11:02 +00:00
Boshen
b06052501a refactor(semantic)!: remove source_type argument from SemanticBuilder::new (#5553)
Realized we can get the source type from the AST.

The next PR will introduce `unambiguous` to `SourceType` and directly set `Program::source_type` to either `script` or `module`.
2024-09-06 16:40:10 +00:00
Boshen
ba4b68cf63 feat(minifier): remove parenthesized expression for dce (#5439)
relates #5436
2024-09-04 12:58:39 +00:00
IWANABETHATGUY
2b21be31ff feat(oxc_minifier): define plugin with postfix wildcard (#4979)
1. support `import.meta.env.*` in `ReplaceGlobalDefine`
2024-08-21 13:27:09 +00:00
Boshen
b4407c4e9a
refactor(oxc,mangler): oxc crate add mangler; mangler use options API 2024-08-20 22:58:59 +08:00
Boshen
ce4d4698b4 feat(codegen)!: remove const generic MINIFY (#5001)
This is a premature optimization, makes the code complicated, and bloats the final binary size.

The minify option is moved to `CodegenOptions`
2024-08-20 08:13:27 +00:00
Boshen
46cb1c1b7b fix(minifier): handle Object.definedPropert(exports for @babel/types/lib/index.js (#4933)
Discoverd in `monitor-oxc`

```javascript
Object.keys(_index6).forEach(function(key) {
	if (key === "default" || key === "__esModule") return;
	if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
	if (key in exports && exports[key] === _index6[key]) return;
	Object.defineProperty(exports, key, {
		enumerable: true,
		get: function() {
			return _index6[key];
		}
	});
});
```

🙃
2024-08-16 06:48:15 +00:00
Boshen
81fd6379e8 fix(minifier): do not fold 0 && (module.exports = {}) for cjs-module-lexer (#4878) 2024-08-13 12:54:59 +00:00
Boshen
879a271d47 fix(minifier): do not join require calls for cjs-module-lexer (#4875) 2024-08-13 12:21:37 +00:00
Boshen
fae8a62a62 fix(minifier): temporarily bail constant folding for tagged template (#4842)
closes #4341
2024-08-12 07:38:25 +00:00
Boshen
c51929558d feat(minifier): add InjectGlobalVariables plugin (@rollup/plugin-inject) (#4759) 2024-08-10 01:12:31 +00:00
Boshen
fbfd852cf8 refactor(minifier): add NodeUtil trait for accessing symbols on ast nodes (#4734) 2024-08-08 02:48:25 +00:00
Boshen
17602db6de
refactor(minifier): move tests and files around 2024-08-07 20:01:55 +08:00
Boshen
3289477197 refactor(minifier): clean up tests (#4724) 2024-08-07 06:29:41 +00:00
Boshen
94d3c31933 fix(minifier): avoid removing function declaration from KeepVar (#4722) 2024-08-07 05:09:32 +00:00
Boshen
bf48c7f02a fix(minifier): fix keep_var keeping vars from arrow functions (#4680) 2024-08-06 07:04:25 +00:00
Boshen
451ac4d0e3
chore: s/elimintation/elimination/ 2024-08-06 14:18:44 +08:00
Boshen
9be29af9d4 fix(minifier): temporarily fix shadowed undefined variable (#4678) 2024-08-06 04:58:39 +00:00
Boshen
e8b662a314 fix(minifier): various fixes to pass minifier conformance (#4667) 2024-08-05 16:31:19 +00:00
camc314
229a0e9c45 feat(minifier): implement dot define for member expressions (#3959)
adds the ability to transform

`process.env.NODE_ENV`

into

`"development"`

when that config is passed in
2024-08-05 12:04:13 +00:00
Boshen
e78cba6464 refactor(minifier): ast passes infrastructure (#4625)
After studying google closure compiler, I'm leaning towards a multi-ast-pass infrastructure for the minifier.

This is one of the few places where we are going to trade maintainability over performance, given the goal of the minifier is compression size not performance.

All of the terminologies and separation of concerns are aligned with google closure compiler.

Infrastructure of `terser` and `esbuild` are not suitable for us to study nor pursuit. Their code are so tightly coupled - I failed to comprehend any of them every time I try to walk through a piece of optmization. Google closure compiler despite being written in Java, it's actually the most readable minifier out there.

To improve performance between ast passes, I envision a change detection system over a portion of the code.

The benchmark will demonstrate the performance regression of running 5 ast passes instead of 2.

To complete this PR, I need to figure out "fix-point" and order of these ast passes.
2024-08-04 11:58:39 +00:00
Boshen
a558492bf9 feat(codegen): implement BinaryExpressionVisitor (#4548)
part of https://github.com/oxc-project/backlog/issues/58

`monitor-oxc` run: https://github.com/oxc-project/monitor-oxc/actions/runs/10179047831
binary expression stack length tally using `counts` in top 100 npm packages from monitor-oxc:

```
29772 counts
(  1)    17652 (59.3%, 59.3%): 0
(  2)     5772 (19.4%, 78.7%): 1
(  3)     3204 (10.8%, 89.4%): 2
(  4)     1276 ( 4.3%, 93.7%): 3
(  5)      616 ( 2.1%, 95.8%): 4
(  6)      308 ( 1.0%, 96.8%): 5
(  7)      202 ( 0.7%, 97.5%): 6
(  8)      168 ( 0.6%, 98.1%): 7
(  9)      114 ( 0.4%, 98.5%): 9
( 10)       90 ( 0.3%, 98.8%): 8
( 11)       84 ( 0.3%, 99.0%): 13
( 12)       58 ( 0.2%, 99.2%): 10
( 13)       48 ( 0.2%, 99.4%): 12
( 14)       32 ( 0.1%, 99.5%): 11
( 15)       20 ( 0.1%, 99.6%): 134
( 16)       16 ( 0.1%, 99.6%): 18
( 17)       16 ( 0.1%, 99.7%): 20
( 18)       12 ( 0.0%, 99.7%): 19
( 19)       12 ( 0.0%, 99.8%): 35
( 20)       12 ( 0.0%, 99.8%): 51
( 21)       10 ( 0.0%, 99.8%): 15
( 22)        6 ( 0.0%, 99.9%): 17
( 23)        6 ( 0.0%, 99.9%): 21
( 24)        6 ( 0.0%, 99.9%): 45
( 25)        4 ( 0.0%, 99.9%): 14
( 26)        4 ( 0.0%, 99.9%): 26
( 27)        4 ( 0.0%, 99.9%): 53
( 28)        2 ( 0.0%, 99.9%): 172
( 29)        2 ( 0.0%, 99.9%): 214
( 30)        2 ( 0.0%,100.0%): 22
( 31)        2 ( 0.0%,100.0%): 27
( 32)        2 ( 0.0%,100.0%): 28
( 33)        2 ( 0.0%,100.0%): 29
( 34)        2 ( 0.0%,100.0%): 31
( 35)        2 ( 0.0%,100.0%): 36
( 36)        2 ( 0.0%,100.0%): 46
( 37)        2 ( 0.0%,100.0%): 55
```
2024-07-31 12:44:19 +00:00
Boshen
6a94e3f573 fix(codegen): fixes for esbuild test cases (#4503) 2024-07-28 08:57:15 +00:00
Boshen
e1ca4122d8 chore(codegen): port over esbuild codegen tests (all ignored right now) (#4457) 2024-07-25 10:00:34 +00:00
Boshen
fb15a19040
chore: fix cyclic dependencies by moving mangler tests to oxc_minifier 2024-07-23 22:13:13 +08:00
Boshen
e33ec18d78 feat(minifier): compress typeof foo == "undefined" into typeof foo > "u" (#4412)
```
> monitor-oxc@ test /home/runner/work/monitor-oxc/monitor-oxc
> node src/main.test.mjs

/home/runner/work/monitor-oxc/monitor-oxc/node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.development.js:2375
		if (void 0 === __REACT_DEVTOOLS_GLOBAL_HOOK__) return !1;
		^

ReferenceError: __REACT_DEVTOOLS_GLOBAL_HOOK__ is not defined
    at injectInternals (/home/runner/work/monitor-oxc/monitor-oxc/node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.development.js:2375:3)
```
2024-07-22 12:44:33 +00:00
Boshen
267f7c4398
fix(minifier): skip Object.defineProperty(exports, ...) for cjs-module-lexer (#4409)
```
> monitor-oxc@ test /home/runner/work/monitor-oxc/monitor-oxc
> node src/main.test.mjs

file:///home/runner/work/monitor-oxc/monitor-oxc/node_modules/.pnpm/inquirer@[10](https://github.com/oxc-project/monitor-oxc/actions/runs/10038139357/job/27739464680#step:8:11).0.1/node_modules/inquirer/dist/esm/ui/prompt.mjs:2
import { defer, EMPTY, from, of, concatMap, filter, reduce, isObservable, lastValueFrom } from "rxjs";
                ^^^^^
SyntaxError: Named export 'EMPTY' not found. The requested module 'rxjs' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'rxjs';
const { defer, EMPTY, from, of, concatMap, filter, reduce, isObservable, lastValueFrom } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:134:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:217:5)
    at async ModuleLoader.import (node:internal/modules/esm/loader:316:24)
    at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:[12](https://github.com/oxc-project/monitor-oxc/actions/runs/10038139357/job/27739464680#step:8:13)3:5)

Node.js v20.15.1
```

Export is undefined when `enumerable` is "!0".
See `https://github.com/nodejs/cjs-module-lexer/issues/64`
2024-07-22 20:32:15 +08:00
Boshen
0deb027e6b
feat(minfier): dce if (xxx) else if (false) { REMOVE } (#4407) 2024-07-22 19:57:35 +08:00
underfin
f144082a60
fix(minifier): RemoveDeadCode should visit nested expression (#4268) 2024-07-15 19:37:12 +08:00
Boshen
ab41c52485
chore(minifier): add two || test cases to dce_if_statement 2024-07-13 11:41:43 +08:00
Boshen
83c2c62f7b feat(codegen): add option for choosing quotes; remove slow choose_quot method (#4219) 2024-07-12 03:08:22 +00:00
Boshen
c8184723f4 feat(minifier): dce conditional expression && or || (#4190) 2024-07-11 04:40:23 +00:00
Boshen
54cd04aead feat(minifier): implement dce with var hoisting (#4160) 2024-07-10 11:03:21 +00:00
Boshen
44a894a2f9 feat(minifier): implement return statement dce (#4155)
This isn't complete, I need to figure out `var` hoisting 🙃
2024-07-10 11:03:14 +00:00
Boshen
c6c16a5fc9
feat(minifier): dce all conditional expressions (#4135) 2024-07-09 18:28:33 +08:00
Boshen
719fb9672b
fix(minifier): omit dce undefined which can be a shadowed variable (#4073)
```
 // Shadowed `undefined` as a variable should not be erased.
    test(
        "function foo(undefined) { if (!undefined) { } }",
        "function foo(undefined){if(!undefined){}}",
    );
```

I'm not using the cheap `ident.reference_id.get().is_some()` here yet
because I don't know what I'm doing - how should minifier consume
`Semantic`?
2024-07-07 01:25:54 +08:00
Boshen
0da9dfbf09
feat(minifier): add constant folding to remove dead code (#4058) 2024-07-05 15:44:01 +08:00
Boshen
aaac2d8775
fix(codegen): preserve parentheses from AST instead calculating from operator precedence (#4055)
…operator precedence

Calculating from operator precedence is currently unsafe and will result
incorrect semantics.
2024-07-05 14:01:17 +08:00
camc314
77a4a0b77c feat(minifier) minify conditional expressions (#3907) 2024-06-26 05:06:53 +00:00
Boshen
dd540c8f0f feat(minifier): add skeleton for ReplaceGlobalDefines ast pass (#3803) 2024-06-21 13:53:59 +00:00
Boshen
f3c3970131 feat(minifier): add skeleton for RemoveDeadCode ast pass (#3802) 2024-06-21 07:18:14 +00:00
Boshen
5c38a0fd69 feat(codegen)!: new code gen API (#3740)
This PR introduces two type alias to avoid the confusing const generic `pub struct Codegen<'a, const MINIFY: bool>`

* CodeGenerator - Code generator without whitespace removal.
* WhitespaceRemover - Code generator with whitespace removal.

Usage is changed to a builder pattern:

```rust
CodeGenerator::new()
  .enable_comment(...)
  .enable_sourcemap(...)
  .build(&program);
```
2024-06-18 15:50:12 +00:00
Boshen
38a75e559b feat(coverage): improve codegen (#3729) 2024-06-18 02:53:09 +00:00