[WIP] This is an experimental tree shaker (code size optimizer) for JavaScript based on the Oxc compiler.
- Simulate the runtime behavior of the code, instead of applying rules.
- Single AST pass - Analyzer as much information as possible.
- As accurate as possible. test262 is used for testing.
- May not be the fastest. (But I will try my best)
This is a simple example, but it's a good start.
Input | Output |
export function f() {
function g(a) {
if (a) console.log("effect");
else return "str";
}
let { ["x"]: y = 1 } = { x: g("") ? undefined : g(1) };
return y;
} |
export function f() {
return 1;
} |
The core of tree-shaking. The execution is simulated to know which code is useless.
And don't worry about the
&& true
in the output, minifier will remove it.
Input | Output |
function f(value) {
if (value) console.log(`${value} is truthy`);
}
f(1);
f(0);
function g(t1, t2) {
if (t1 && t2) console.log(2);
else if (t1 || t2) console.log(1);
else console.log(0);
}
g(true, true);
g(false, false); |
function f() {
{
console.log("1 is truthy");
}
}
f();
function g(t1) {
if (t1 && true) console.log(2);
else {
console.log(0);
}
}
g(true);
g(false); |
This is beyond the scope of tree-shaking, we need a new name for this project π.
Input | Output |
export function main() {
const obj = {
foo: v1,
[t1 ? "bar" : "baz"]: v2,
};
const key = t2 ? "foo" : "bar";
console.log(obj[key]);
} |
export function main() {
const obj = {
a: v1,
[t1 ? "b" : "c"]: v2,
};
const key = t2 ? "a" : "b";
console.log(obj[key]);
} |
createElement
also works, if it is directly imported fromreact
.
Input | Output |
function Name({ name, info }) {
return (
<span>
{name}
{info && <sub> Lots of things never rendered </sub>}
</span>
);
}
export function Main() {
return <Name name={"world"} />;
} |
function Name() {
return (
<span>
{"world"}
{}
</span>
);
}
export function Main() {
return <Name />;
} |
We also have special handling for some React.js APIs. For example, React Context,
memo
,forwardRef
,useMemo
, etc.
Input | Output |
import React from "react";
const MyContext = React.createContext("default");
function Inner() {
const value = React.useContext(MyContext);
return <div>{value}</div>;
}
export function main() {
return (
<MyContext.Provider value="hello">
<Inner />
</MyContext.Provider>
);
} |
import React from "react";
const MyContext = React.createContext();
function Inner() {
return <div>{"hello"}</div>;
}
export function main() {
return (
<MyContext.Provider>
<Inner />
</MyContext.Provider>
);
} |
- Rollup: Rollup tree-shakes the code in a multi-module context, while this project is focused on a single module. For some cases, this project can remove 10% more code than Rollup.
- Closure Compiler: Closure Compiler can be considered as a tree shaker + minifier, while this project is only a tree shaker (for the minifier, we have
oxc_minifier
). Theoretically, we can shake more than Closure Compiler, but we cannot compare them directly because we don't have a equivalent minifier. Also, it's written in Java, which is hard to be integrated into the JS ecosystem. - swc: swc can also be considered as a tree shaker + minifier. TBH, currently swc is much faster and more complete. It is rule-based, which is a different approach from this project. It's also not compatible with the Oxc project, thus a new tree shaker is needed.
- Performance!
- Type narrowing
- Pure annotation
- Complete JS Builtins metadata
- Test against fixtures from other tree shakers like Rollup
- Rollup-like try-scope optimization/de-optimization
- Reuse code with oxc_minifier for JS computation logics
- Parse the code via
oxc_parser
. - Build the semantic information via
oxc_semantic
. - Tree shake the code.
- Emulate the runtime behavior of the code. (Control flow, Side effects, ...)
- Analyze the possible runtime values of the variables.
- Remove the dead code.
- Minify the code via
oxc_minifier
. (Optional)
Entity
: Represents the analyzed information of a JS value.Consumable
: Entity or AST Nodes or some other things that the runtime value ofEntity
depends on.- Scopes:
- Call Scope: Function call scope.
- Cf Scope: Control flow scope.
- Variable Scope: Variable scope.
- Try Scope: Try statement or function.