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

Leaking global 'exports' variable #165

Closed
jessewmc opened this issue Jun 1, 2020 · 3 comments
Closed

Leaking global 'exports' variable #165

jessewmc opened this issue Jun 1, 2020 · 3 comments

Comments

@jessewmc
Copy link

jessewmc commented Jun 1, 2020

If I declare const exports = ... in an entry point or any included modules I get the following error:

index2.js:1:6: error: "exports" has already been declared
const exports = "Hi";
      ~~~~~~~
1 error

Is this intentional for one of the non-es module specs? It looks like it might have something to do with the runtime.

(As an aside, it would be nice to have the option to build without the runtime if it's not needed.)

Heres a trivial example of the runtime that gets generated when I remove the collision by hand. It seems like exports = module.exports; could be related.

((modules, entryPoint) => {
  let global = function() {
    return this;
  }();
  let cache = {};
  let require = (target, arg) => {
    if (typeof target === "number") {
      let module = cache[target], exports;
      if (!module) {
        module = cache[target] = {
          exports: {}
        };
        modules[target].call(global, require, module.exports, module);
      }
      exports = module.exports;
      if (arg && (!exports || !exports.__esModule)) {
        if (!exports || typeof exports !== "object") {
          exports = {};
        }
        if (!("default" in exports)) {
          Object.defineProperty(exports, "default", {
            get: () => module.exports,
            enumerable: true
          });
        }
      }
      return exports;
    }
    arg.__esModule = () => true;
    for (let name in arg) {
      Object.defineProperty(target, name, {
        get: arg[name],
        enumerable: true
      });
    }
  };
  return require(entryPoint);
})({
  0() {
    // index2.js
    const exports1 = "Hi";
    console.log(exports1);
  }
}, 0);
@jessewmc
Copy link
Author

jessewmc commented Jun 2, 2020

I should clarify that unlike the other issue I filed, I have actually confirmed this with 0.4.3!

Also, to reproduce:

esbuild --bundle --outfile=index-built.js index.js

With index.js as:

const exports = "Hi";
console.log(exports);

Only happens when passing --bundle.

@evanw
Copy link
Owner

evanw commented Jun 5, 2020

This happens because esbuild supports CommonJS, which declares an implicit variable called exports in the same scope. You'll get the exact same behavior with node, which supports CommonJS:

$ cat example.js
const exports = "Hi";
console.log(exports);
$ node example.js
/Users/evan/dev/esbuild/example.js:1
const exports = "Hi";
      ^

SyntaxError: Identifier 'exports' has already been declared
    at wrapSafe (internal/modules/cjs/loader.js:1070:16)
    at Module._compile (internal/modules/cjs/loader.js:1120:27)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1176:10)
    at Module.load (internal/modules/cjs/loader.js:1000:32)
    at Function.Module._load (internal/modules/cjs/loader.js:899:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)
    at internal/main/run_main_module.js:18:47

Can you share more information about what you are trying to do? What does your full file look like?

@jessewmc
Copy link
Author

jessewmc commented Jun 5, 2020

I'm building a browser bundle, everything is is an es6 module--no commonjs or amd. I'm targeting esnext. There are a few hundred modules, it just so happened that there were a couple declaring const exports at their top level scopes (for no good reason).

It was no problem for me to change once I realized what was going on, just surprising to see a collision with a non-reserved word (at least in ecmascript) that is private to a module.

I normally use rollup (& terser), but esbuild gets me from ~1 minute build time to less than a second, which is amazing.

@evanw evanw closed this as completed in b6e86a6 Jun 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants