Open fabiospampinato opened 1 year ago
Are you saying if you do import Lib from 'lib'
and then lib.foo()
that esbuild would remove bar
and baz
? One problem is that lib.foo()
hands foo
the entire value of lib
via this
, which it could then use to access bar
and baz
. So this is not "trivially analyzable" because removing bar
and baz
would be unsafe and incorrect in some cases.
There will be cases where analyzing the code would be complicated enough, if not impossible, but I'd argue it would still be useful to implement this, if there's a reasonable mechanism for opting into this that doesn't break the world. Maybe somebody should just make a userland plugin for this.
In you example specifically if foo
is an arrow function that problem doesn't exist immediately, and at least I almost always use arrow functions, though I understand most people don't, in which case one could check if this
is ever used inside the body of the function, or if the function contains some runtime code generation (eval/Function/setInterval...).
If you only supported a strict form for this feature, where export * as
is used to build the namespace [1], would that be a useful signal from the developer to say "I'm opting into this", could that also guarantee that the namespace doesn't get modified and does it make the feature any easier to implement if you did that?
For example, if I built the namespace like this:
lib/public.ts
import foo from './foo';
import bar from './bar';
import baz from './baz';
export {foo, bar, baz};
// export * as qux from "./qux"; // [1] and maybe also if the file only contains imports/exports?
lib/index.ts
export * as Lib from "./public";
main.ts
import { Lib } from "./lib";
Lib.foo();
Lib.baz();
// Lib.qux.quux();
I don't think that would make this easier to implement as that's pretty much a completely separate feature from what is being requested here. The export
statement may look like an object literal because its syntax also uses the {
and }
tokens, but no object is involved and it does something pretty different. The request for the feature you are asking about is already tracked here: #1420
It'd be interesting if default exports could be tree-shaken too, perhaps on a global or per-module opt-in basis.
The problem is that, for example, I want the DX that the following allows, where I can then just do a super clean
import Lib from 'lib'
, rather than having to write* as
or having to import each little thing individually.But I want the tree-shaking that this allows:
Basically I think there should be some mechanism for treating simple trivially analyzable default exports as tree-shakeable. Maybe if an ESM package says explicitly that it has
"sideEffects": false
this optimization could be performed? Maybe some other flag or#PURE
-like annotation could be introduced?Alternatively the simplest workaround I can think about is to have two entry points of your library, one exports things individually and the other imports everything from that and exports them together as the default export, then you import the default export, but rewrite the code with a simple transform to actually import everything from the other entry point with
* as
. It should work, and it's simple enough to be practical as a one-off, but if I need to modify every library entry point and account for each of them with a transform it gets impractical pretty quick, it'd be nice if this Just Worked™️