Consider this example:
// util.cjs
module.exports.sum = (x, y) => x + y;
// star.mjs
export * from './util.cjs';
// main.mjs
import {sum} from `./star.mjs`
console.log(sum(2,4));
When main.mjs tries to import {sum} from './star.mjs', it will fail. SyntaxError: The requested module './star.mjs' does not provide an export named 'sum'".
That's because the spec'd behavior when doing a star export from a module with only a default export is to export no names at all. If you import * as star from './star.mjs', you'll find that star is an empty module; there's no way for main.mjs to get sum back out of star.mjs without modifying star.mjs.
(That spec'd behavior seems kinda silly to me; I tip my hat to @guybedford's proposal to include the default export in export * from 'module')
But as long as "export no names" is the behavior, and as long as CJS only generates default exports and not named exports, I think it would be helpful to throw a clear error in this case. export * from 'util.cjs' can't really do anything useful, and we could serve users better by doing something more friendly.
Maybe something like this?
[CJS_STAR_EXPORT]: "export *" from a CommonJS module is not allowed. (CommonJS exports are computed at runtime, but ES module exports must be computed earlier, during the parse phase.)
There is certainly a way; add export { default } from './util.cjs' to star.mjs. This is because in ESM, export * doesn't include the default, and CJS modules only have a default export.
Yes, I realize that, and I recognize that it is possible to re-export a default export, but that doesn't change the fact that export * from 'util.cjs' is not going to do anything useful; Node could support the user with better error handling when someone writes a line like that.
I've updated my proposal to clarify that there's no way for main.mjs to get sum out of star.mjs, as written.
I think this could be generalized to "warn if export star is used on something with (only) a default export". It might be worth asking V8 if that's something they'd be interested in. It seems like something worth printing to the console in the browser as well, even without CJS being involved?
How would you suggest going about that? Post an email on the V8 mailing list? (Surely not filing an issue on the V8 issue tracker…?)
FWIW, I can't help but notice that TC39 has had multiple opportunities to fix this. The committee rejected Guy's proposal, knowing full well that star exports from default-only modules don't work and have never worked.
And they also rejected dynamic modules _because_ they would break star exports, among other reasons. ("Syntax poisoning," I believe they called it.)
I guess folks on the committee must actually _approve_ of the fact that star exports from default-only modules fail silently? For theoretical purity reasons?? Does anybody here know _why_ this (IMO buggy) behavior has slain every attempt to fix it?
@dfabulich this should be resolved on node@>=14.15.0 thanks to @guybedford's work with cjs-module-lexer
Most helpful comment
Yes, I realize that, and I recognize that it is possible to re-export a default export, but that doesn't change the fact that
export * from 'util.cjs'is not going to do anything useful; Node could support the user with better error handling when someone writes a line like that.I've updated my proposal to clarify that there's no way for
main.mjsto getsumout ofstar.mjs, as written.