Skip to content
This repository was archived by the owner on Mar 29, 2018. It is now read-only.
This repository was archived by the owner on Mar 29, 2018. It is now read-only.

Node interopability with default exports  #85

@joliss

Description

@joliss

This is not a bug in the transpiler, but I thought this repo might be a good place to have this discussion:

I want ES6 modules to succeed, because it offers some clear technical advantages. But if we write our modules in ES6, we'll generally want to also transpile them to Node's CommonJS to publish them on npm, so good CJS interopability will be important for ES6 adoption.

But there's an interop problem: Say you have module foo, and it has a single export (export default Foo, to be used like import Foo from 'foo'). Right now, this transpiles to exports["default"] = Foo, to be used like var Foo = require('foo').default. This extra .default is clearly suboptimal, in that it breaks the convention of the existing Node ecosystem. I worry that having .default will be unappealing and make our transpiled modules look like "second-class Node citizens".

"Oh," you say, "but we can simply wrap our transpiled modules in a bit of helper code to get rid of the .default." (See the handlebars wrapper for Node for an example.) Sadly, I believe this actually makes things worse: Say another package "bar", written in ES6 as well, has import Foo from 'foo'. When bar is transpiled to CJS, it will say (approximately) var Foo = require("foo").default. The transpiler cannot know that "foo" is specially wrapped on Node and doesn't need .default, so now we need need to manually remove the .default in bar's CJS output. (Am I missing something here?)

I've also heard people suggest that Node could simply adopt ES6, so that these troubles would be irrelevant. But switching Node to ES6 is probably not a matter of just enabling the syntax in V8. Rather, the big hurdle is interopability with the existing ecosystem. (Also keep in mind that Node doesn't have a pressing need to switch to ES6.) So if Node is to adopt ES6 modules at all, figuring out a good interop story is probably a prerequisite.

So here are some possible solutions, as I see them:

  1. Accept that .default will be all over the place on Node.
  2. Or, try to wrap modules manually to remove the .default on CJS. As I point out above, this might get troublesome once we have an ecosystem of packages written in ES6 that need to also play together on Node.
  3. Or, a daring suggestion: Change the CJS transpilation output to omit .default. (Or add a "Node" mode in addition to CJS that omits .default.) So export default Foo would transpile to module.exports = Foo, and import Foo from 'foo' and module Foo from 'foo' would both transpile to var Foo = require('foo'). (If, transpiling to CJS, a module has both default and named exports, we might throw an error, or require some kind of directive for the transpiler, [update:] or tack the named exports onto the default object, see @caridy's comment below.) This change would acknowledge that .default is really something you never want on Node. It falls short when modules have default and named exports. (Does this happen much at all?) I believe it also makes circular default imports impossible to support. This is fairly easy to work around though - intra-package cycles can use named imports, and inter-package cycles are very rare.
  4. [Update:] Or make Node's module loading ES6 aware somehow. See @domenic's comment below.
  5. [Update:] Export default as the root-level object, and tack a property like ._es6_module_exports onto it for named exports. See @stefanpenner's comment below.
  6. [Update:] Export default as the root-level object only if there are no named exports. Use an ._es6Module property to preserve ES6 semantics. See my comment way below.

What do you think about those? Any other ideas?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions