javascriptnode.jses6-modules

Why are export conflicts silently removed in ES6 modules?


Let's suppose we have 3 files

// a.mjs
export * from "./b.mjs";
export * from "./c.mjs";

// b.mjs
export const foo = 1;
export const bar = 2;

// c.mjs
export const bar = 3;

Both b.mjs and c.mjs export bar, so they create a conflict when they're re-exported from a.mjs.

I was expecting either one of the two to "shadow" the other one or the whole thing to throw, but instead it turns out that a.mjs only exports foo.

Why is JavaScript pretending that the conflicting exports don't exist at all?

(Tested in both Chromium and Node)


Solution

  • As per ES 2015 ("ES6") module specification

    First, it collects all the names exported by b.mjs and all the names exported by c.mjs, then merges them into a single "export map."

    When the same export name appears more than once in that merge, duplicate names do not shadow one another or cause a compile‐time error—they simply vanish from the final export list.


    TLDR:

    That’s why, in your example, a.mjs ends up exporting only foo->bar was dropped because it appeared twice, per the ES module.


    export * from "./b.mjs"; // exports { foo, bar }
    export { bar as barFromC } from "./c.mjs"; // explicitly re-export c.mjs's bar under a different name
    
    # If you want c.mjs's bar to "shadow" b.mjs's bar:
    export { foo, bar } from "./b.mjs"; // single‐source re-export of b.mjs
    export { bar } from "./c.mjs";      // re-export c.mjs's bar under the same name, replacing it