Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

re-exporting generates unneeded code #3032

Closed
dekdevy opened this issue Mar 31, 2023 · 4 comments
Closed

re-exporting generates unneeded code #3032

dekdevy opened this issue Mar 31, 2023 · 4 comments

Comments

@dekdevy
Copy link

dekdevy commented Mar 31, 2023

when using any kind of export of code that has been loaded via import, some code that is in some cases unused will be generated. is this avoidable?

example:

// vec.js
export const add = (a, b) => a+b
// math.js
export * as vec from './vec.js'
// test.js
import { vec } from './math.js'
console.log(vec.add(1,2))

The generated bundle code is:

var __defProp = Object.defineProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};

// vec.js
var vec_exports = {};
__export(vec_exports, {
  add: () => add
});
var add = (a, b) => a + b;

// test.js
console.log(vec_exports.add(1, 2));

however, the code rollup will produce is:

var add = (a, b) => a + b;
console.log(add(1, 2));

Question: Is there any way I can rewrite my code to end up with the result i get from rollup without changing the file structure?

@hyrious
Copy link

hyrious commented Mar 31, 2023

Simple rule: when you're capturing the namespace object (* as vec), do not pass it outside of "current file", in which case esbuild cannot analyze too deep to "de-construct" it.

For each file, esbuild is smart enough to treat namespace references as named imports:

import * as vec from './vec'
vec.add(3, 5)

// is converted to
import {add} from './vec'
add(3, 5)

..but not cross files.

@dekdevy
Copy link
Author

dekdevy commented Mar 31, 2023

@hyrious thank you for the reply. does this mean that any kind of "flattening" files like this: https://github.com/toji/gl-matrix/blob/master/src/index.js will pretty much break treeshaking and cause hickups unless you rewrite code to avoid the flatten file completely?

@hyrious
Copy link

hyrious commented Apr 1, 2023

Yes but no --
Talking about tree-shaking, the namespace object is known to be pure so that it will be shaked if no one is using it even though being exported. see it live

And yes the whole module "captured" by the namespace object is always bundled in if been used.

Note that you can export * across modules, in which case esbuild will shake the final reference correctly.

// lib.js
export * from './foo'
export * from './bar'

// main.js
import * as lib from './lib'
lib.foo()

This trick is also been used in TypeScript's source code.

@evanw
Copy link
Owner

evanw commented Apr 1, 2023

The reason why esbuild does this is that the "module namespace exotic object" (the thing you are exporting with export * as vec from './vec.js') must prevent mutating the exports through the imported object. So in that sense this code isn't "unneeded".

There's an existing request for tree shaking to be able to unwrap multiple levels of indirection like this: #1420. So I'm closing this issue as a duplicate of that issue. The workaround for now is to either not write code like this or to use another tool instead of esbuild.

@evanw evanw closed this as completed Apr 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants