[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.
This project has been done in the spirit of soundiness. When building practical program analyses, it is often necessary to cut corners. In order to be open about language features that we do not support or support only partially, we are attaching this soundiness statement.
Our analysis does not have a fully sound handling of the following features:
- eval
- implicit conversions (==, valueOf, toString)
- exceptions and flow related to that
- prototype semantics
We have determined that the unsoundness in our handling of these features has minimal effect on analysis output and the validity of our experimental evaluation. To the best of our knowledge, our analysis has a sound handling of all language features other than those listed above.
This statement has been produced with the Soundiness Statement Generator from http://soundiness.org.