esbuild
esbuild copied to clipboard
Comparisons of heterogenous primitive types are not considered for dead code elimination
echo "if ('foo'=='bar') foo(); else bar();" | yarn esbuild --minify outputs bar(); as expected.
echo "if ('foo'==null) foo(); else bar();" | yarn esbuild --minify outputs "foo"==null?foo():bar(); when we expect it to output bar();, because 'foo'==null is always false.
This occurs when the LHS and RHS of an equality operator are different types, or arrays, or objects. Additionally, only ==, ===, the logical and ternary operators, and property accessors of new object expressions seem to be considered; arithmetic and comparison operators are excluded. While some of these are probably out of scope, it would be helpful to understand which constructs are eliminated and which are not.
The code for this is here:
https://github.com/evanw/esbuild/blob/f730c03dc7be43c3c908fbd6e106120a99ebb4a0/internal/js_ast/js_ast_helpers.go#L840-L878
Code folding for loose comparisons hasn't been implemented yet. Unlike JavaScript minifiers written in JavaScript, doing this in Go is non-trivial because you have to carefully and manually implement JavaScript type coercion rules. Right now constant folding for loose comparisons is only done in cases where both sides are primitives and there is no type coercion.
I'm open to implementing some additional rules. The specific algorithm to use as a guide would be this: https://tc39.es/ecma262/#sec-islooselyequal. Which rules are you looking for? Just equality with null?
Loose equality is often discouraged except for null/undefined checks. It seems fine to implement loose equality for these cases. It is pretty straightforward since null/undefined are only loosely equal to null/undefined.