Static class fields can’t be tree-shaken away on subsequent compilation
Hey Evan,
Thank you for creating and maintaining esbuild!
We @ Framer stumbled upon an issue around static class fields. When you use them, and you’re compiling for ES2021 and below, esbuild produces code that’s not tree-shakeable on a subsequent compilation. This is relevant e.g. when you’re a library author and are distributing the library as a bundle.
Steps to reproduce
-
Create
my-library.jswith the following code:export class Navigation { state = defaultState() static defaultProps = { enabled: true, } static contextType = NavigationCallbackContext } -
Bundle the library with
{ target: "es2019" }intomy-library-compiled.js. Observe that the library is compiled down to:var __defProp = Object.defineProperty; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); export class Navigation { constructor() { __publicField(this, "state", defaultState()); } } __publicField(Navigation, "defaultProps", { enabled: true }); __publicField(Navigation, "contextType", NavigationCallbackContext); -
Now, import the compiled library from another file (say,
my-app.js):import {} from "./my-library-compiled.js" -
Bundle that file (using esbuild, webpack, etc – doesn’t matter). Observe that the app bundle includes
Navigation, even though it’s not used.
Actual result
Any classes that use static fields in the library can’t be tree-shaken away due to top-level __publicField setters.
Expected result
esbuild compiles classes with static fields down to something like this:
export class Navigation {
state = defaultState()
static defaultProps = {
enabled: true,
}
static contextType = NavigationCallbackContext
}
↓
export var Navigation = /* @__PURE__ */ (() => {
class Navigation {
constructor() {
__publicField(this, "state", defaultState());
}
}
__publicField(Navigation, "defaultProps", {
enabled: true
});
__publicField(Navigation, "contextType", NavigationCallbackContext);
return Navigation;
})()
which allows Navigation to be tree-shaken away.
It'd be great to support this. I'm surprised to see little fanfare around this issue. Is there another issue that already pointed out this deficiency?