Stage: 1
Author: Lee Byron
Reviewers: Caridy Patiño
Specification: https://tc39.github.io/proposal-export-ns-from/
AST: ESTree.md
Transpiler: See Babel's export-namespace-from plugin.
NOTE: Closely related to the export-default-from proposal.
The export ___ from "module"
statements are a very useful mechanism for
building up "package" modules in a declarative way. In the ECMAScript 2015 spec,
we can:
- export through a single export with
export {x} from "mod"
- ...optionally renaming it with
export {x as v} from "mod"
. - We can also spread all exports with
export * from "mod"
.
These three export-from statements are easy to understand if you understand the semantics of the similar looking import statements.
However there is an import statement which does not have corresponding export-from statement, exporting the ModuleNameSpace object as a named export.
Example:
export * as someIdentifier from "someModule";
Import Statement Form | [[ModuleRequest]] | [[ImportName]] | [[LocalName]] |
---|---|---|---|
import v from "mod"; |
"mod" |
"default" |
"v" |
import * as ns from "mod"; |
"mod" |
"*" |
"ns" |
import {x} from "mod"; |
"mod" |
"x" |
"x" |
import {x as v} from "mod"; |
"mod" |
"x" |
"v" |
import "mod"; |
Export Statement Form | [[ModuleRequest]] | [[ImportName]] | [[LocalName]] | [[ExportName]] |
---|---|---|---|---|
export var v; |
null | null | "v" |
"v" |
export default function f(){}; |
null | null | "f" |
"default" |
export default function(){}; |
null | null | "*default*" |
"default" |
export default 42; |
null | null | "*default*" |
"default" |
export {x} ; |
null | null | "x" |
"x" |
export {x as v} ; |
null | null | "x" |
"v" |
export {x} from "mod" ; |
"mod" |
"x" |
null | "x" |
export {x as v} from "mod" ; |
"mod" |
"x" |
null | "v" |
export * from "mod" ; |
"mod" |
"*" |
null | null |
Export Statement Form | [[ModuleRequest]] | [[ImportName]] | [[LocalName]] | [[ExportName]] |
---|---|---|---|---|
export * as ns from "mod"; |
"mod" |
"*" |
null | "ns" |
There's a syntactic symmetry between the export-from statements and the import statements they resemble. There is also a semantic symmetry; where import creates a locally named binding, export-from creates an export entry.
As an existing example:
import {v} from "mod";
If then v
should be exported, this can be followed by an export
. However, if
v
is unused in the local scope, then it has introduced a name to the local
scope unnecessarily.
import {v} from "mod";
export {v};
A single "export from" line directly creates an export entry, and does not alter the local scope. It is symmetric to the similar "import from" statement.
export {v} from "mod";
This presents a developer experience where it is expected that replacing the
word import
with export
will always provide this symmetric behavior.
However, when there is a gap in this symmetry, it can lead to confusing behavior:
"I would like to chime in with use-case evidence. When I began using ES6 (via babel) and discovered imports could be re-exported I assumed the feature set would be symmetrical only to find out quite surprisingly that it was not. I would love to see this spec round out what I believe is a slightly incomplete feature set in ES6."
- @jasonkuhrt
"I also bumped into this and even thought this was a bug in Babel."
- @gaearon
The proposed addition follows this same symmetric pattern:
Importing a namespace exotic object (existing):
import * as ns from "mod";
Exporting that name (existing):
import * as ns from "mod";
export {ns};
Symmetric "export from" (proposed):
export * as ns from "mod";
Using the terminology of Table 40 and Table 42 in ECMAScript 2015, the export-from form can be created from the symmetric import form by setting export-from's [[ExportName]] to import's [[LocalName]] and export-from's [[LocalName]] to null.
Statement Form | [[ModuleRequest]] | [[ImportName]] | [[LocalName]] | [[ExportName]] |
---|---|---|---|---|
import v from "mod"; |
"mod" |
"default" |
"v" |
|
import {x} from "mod"; |
"mod" |
"x" |
"x" |
|
export {x} from "mod"; |
"mod" |
"x" |
null | "x" |
import {x as v} from "mod"; |
"mod" |
"x" |
"v" |
|
export {x as v} from "mod"; |
"mod" |
"x" |
null | "v" |
import * as ns from "mod"; |
"mod" |
"*" |
"ns" |
|
export * as ns from "mod"; |
"mod" |
"*" |
null | "ns" |
export * from "mod"; |
"mod" |
"*" |
null | null (many) |