Unused import bundled unless define test is on same line for class methods only
When given a define, esbuild is able to treeshake some known dead code. This does not work for class methods unless the define is tested on the same line.
esbuild --bundle --format=esm --minify --tree-shaking --define:debug=false entry.ts
import {a, b} from './file.ts'
class C {
init(): void {
if (!debug) return
if (debug) a()
b()
}
}
new C()
export function a(): void {
console.log('a')
}
export function b(): void {
console.log('b')
}
Produces function i(){console.log("b")}var o=class{init(){}};new o; when no logs were expected var o=class{init(){}};new o;.
It seems to be method specific. This code shakes fine:
import {a, b} from './file.ts'
function init(): void {
if (!debug) return
if (debug) a()
b()
}
init()
The difference you noticed is because esbuild doesn't attempt to tree-shake class methods, but it does attempt to tree-shake function declarations. Tree-shaking class methods is technically possible in a very limited number of situations, but the analysis would quickly fall apart as JavaScript is a very dynamic language, methods are properties on objects, and guaranteed analysis of dead properties is not generally possible (e.g. if you do console.log(new C) that could access init).
That said, you're right that this should work. Right now the dead code recognizer eliminates code inside of if/else branches that are known to not be taken, but does not currently consider the code past an if+return to be dead code. A full solution here would require control flow analysis that esbuild doesn't currently do, but special-casing if+return in the same block is easy enough.