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)
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:
export *
operation builds a union of all exported names from its source modules.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