From 42a2430d2c966fee27694cf015721e71916bd3f8 Mon Sep 17 00:00:00 2001 From: Sebastian McKenzie Date: Mon, 30 Mar 2015 03:38:14 +1100 Subject: [PATCH] add support for export extensions https://github.com/leebyron/ecmascript-more-export-from - closes #1091 --- src/acorn/src/statement.js | 19 +++++-- src/babel/generation/generators/modules.js | 15 +++++- src/babel/transformation/modules/_default.js | 2 +- .../transformers/es7/export-extensions.js | 25 +++++++++ .../transformation/transformers/index.js | 1 + src/babel/types/alias-keys.json | 10 ++-- src/babel/types/visitor-keys.json | 10 ++-- test/acorn/tests-babel.js | 52 +++++++++++++++++++ .../default-commonjs/actual.js | 1 + .../default-commonjs/expected.js | 5 ++ .../default-es6/actual.js | 1 + .../default-es6/expected.js | 4 ++ .../default-es6/options.json | 3 ++ .../namespace-commonjs/actual.js | 1 + .../namespace-commonjs/expected.js | 9 ++++ .../namespace-es6/actual.js | 1 + .../namespace-es6/expected.js | 4 ++ .../namespace-es6/options.json | 3 ++ .../es7-export-extensions/options.json | 5 ++ 19 files changed, 157 insertions(+), 14 deletions(-) create mode 100644 src/babel/transformation/transformers/es7/export-extensions.js create mode 100644 test/core/fixtures/transformation/es7-export-extensions/default-commonjs/actual.js create mode 100644 test/core/fixtures/transformation/es7-export-extensions/default-commonjs/expected.js create mode 100644 test/core/fixtures/transformation/es7-export-extensions/default-es6/actual.js create mode 100644 test/core/fixtures/transformation/es7-export-extensions/default-es6/expected.js create mode 100644 test/core/fixtures/transformation/es7-export-extensions/default-es6/options.json create mode 100644 test/core/fixtures/transformation/es7-export-extensions/namespace-commonjs/actual.js create mode 100644 test/core/fixtures/transformation/es7-export-extensions/namespace-commonjs/expected.js create mode 100644 test/core/fixtures/transformation/es7-export-extensions/namespace-es6/actual.js create mode 100644 test/core/fixtures/transformation/es7-export-extensions/namespace-es6/expected.js create mode 100644 test/core/fixtures/transformation/es7-export-extensions/namespace-es6/options.json create mode 100644 test/core/fixtures/transformation/es7-export-extensions/options.json diff --git a/src/acorn/src/statement.js b/src/acorn/src/statement.js index 9ca10174b9..be8e33786f 100755 --- a/src/acorn/src/statement.js +++ b/src/acorn/src/statement.js @@ -559,10 +559,10 @@ pp.parseExport = function(node) { this.next() // export * from '...' if (this.eat(tt.star)) { - this.expectContextual("from") - node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected() - this.semicolon() - this.checkExport(node) + if (this.options.features["es7.exportExtensions"] && this.eatContextual("as")) { + node.exported = this.parseIdent() + } + this.parseExportFrom(node) return this.finishNode(node, "ExportAllDeclaration") } if (this.eat(tt._default)) { // export default ... @@ -587,6 +587,10 @@ pp.parseExport = function(node) { node.declaration = this.parseStatement(true) node.specifiers = [] node.source = null + } else if (this.type === tt.name) { + node.exported = this.parseIdent() + this.parseExportFrom(node) + return this.finishNode(node, "ExportNamespaceDeclaration") } else { // export { x, y as z } [from '...'] node.declaration = null node.specifiers = this.parseExportSpecifiers() @@ -601,6 +605,13 @@ pp.parseExport = function(node) { return this.finishNode(node, "ExportNamedDeclaration") } +pp.parseExportFrom = function(node) { + this.expectContextual("from") + node.source = this.type === tt.string ? this.parseExprAtom() : this.unexpected() + this.semicolon() + this.checkExport(node) +} + pp.shouldParseExportDeclaration = function() { return this.options.features["es7.asyncFunctions"] && this.isContextual("async") } diff --git a/src/babel/generation/generators/modules.js b/src/babel/generation/generators/modules.js index 7f71826343..cb7de0c36e 100644 --- a/src/babel/generation/generators/modules.js +++ b/src/babel/generation/generators/modules.js @@ -21,8 +21,21 @@ export function ExportSpecifier(node, print) { } } +export function ExportNamespaceDeclaration(node, print) { + this.push("export "); + print(node.exported); + this.push(" from "); + print(node.source); + this.semicolon(); +} + export function ExportAllDeclaration(node, print) { - this.push("export * from "); + this.push("export *"); + if (node.exported) { + this.push(" as "); + print(node.exported); + } + this.push(" from "); print(node.source); this.semicolon(); } diff --git a/src/babel/transformation/modules/_default.js b/src/babel/transformation/modules/_default.js index 7d2555275d..534af2333d 100644 --- a/src/babel/transformation/modules/_default.js +++ b/src/babel/transformation/modules/_default.js @@ -62,7 +62,7 @@ var exportsVisitor = traverse.explode({ extend(formatter.localExports, declar.getBindingIdentifiers()); } - if (!t.isExportDefaultDeclaration(node)) { + if (!t.isExportDefaultDeclaration(node) && !t.isExportNamespaceDeclaration(node)) { formatter.hasNonDefaultExports = true; } diff --git a/src/babel/transformation/transformers/es7/export-extensions.js b/src/babel/transformation/transformers/es7/export-extensions.js new file mode 100644 index 0000000000..0d6caa3c06 --- /dev/null +++ b/src/babel/transformation/transformers/es7/export-extensions.js @@ -0,0 +1,25 @@ +// https://github.com/leebyron/ecmascript-more-export-from + +import * as t from "../../../types"; + +export function check(node) { + return t.isExportNamespaceDeclaration(node) || (t.isExportAllDeclaration(node) && node.exported); +} + +export function ExportNamespaceDeclaration(node, parent, scope) { + var uid = scope.generateUidIdentifier("default"); + return [ + t.importDeclaration([t.importDefaultSpecifier(uid)], node.source), + t.exportDefaultDeclaration(uid) + ]; +} + +export function ExportAllDeclaration(node, parent, scope) { + if (node.exported) { + var uid = scope.generateUidIdentifier(node.exported.name); + return [ + t.importDeclaration([t.importNamespaceSpecifier(uid)], node.source), + t.exportNamedDeclaration(null, [t.exportSpecifier(uid, node.exported)]) + ]; + } +} diff --git a/src/babel/transformation/transformers/index.js b/src/babel/transformation/transformers/index.js index 13254736e9..31810b952b 100644 --- a/src/babel/transformation/transformers/index.js +++ b/src/babel/transformation/transformers/index.js @@ -84,6 +84,7 @@ export default { runtime: require("./other/runtime"), // needs to be before `_blockHoist` due to function hoisting etc + "es7.exportExtensions": require("./es7/export-extensions"), "es6.modules": require("./es6/modules"), _blockHoist: require("./internal/block-hoist"), diff --git a/src/babel/types/alias-keys.json b/src/babel/types/alias-keys.json index df6bdecf4b..4a36b511f5 100644 --- a/src/babel/types/alias-keys.json +++ b/src/babel/types/alias-keys.json @@ -14,10 +14,12 @@ "EmptyStatement": ["Statement"], "LabeledStatement": ["Statement"], "VariableDeclaration": ["Statement", "Declaration"], - "ExportAllDeclaration": ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], - "ExportDefaultDeclaration": ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], - "ExportNamedDeclaration": ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], - "ImportDeclaration": ["Statement", "Declaration", "ModuleDeclaration"], + + "ExportNamespaceDeclaration": ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], + "ExportAllDeclaration": ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], + "ExportDefaultDeclaration": ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], + "ExportNamedDeclaration": ["Statement", "Declaration", "ModuleDeclaration", "ExportDeclaration"], + "ImportDeclaration": ["Statement", "Declaration", "ModuleDeclaration"], "ArrowFunctionExpression": ["Scopable", "Function", "Expression"], "FunctionDeclaration": ["Scopable", "Function", "Statement", "Declaration"], diff --git a/src/babel/types/visitor-keys.json b/src/babel/types/visitor-keys.json index 6df9c31634..96e9396751 100644 --- a/src/babel/types/visitor-keys.json +++ b/src/babel/types/visitor-keys.json @@ -22,10 +22,6 @@ "DoWhileStatement": ["body", "test"], "DoExpression": ["body"], "EmptyStatement": [], - "ExportAllDeclaration": ["source"], - "ExportDefaultDeclaration": ["declaration"], - "ExportNamedDeclaration": ["declaration", "specifiers", "source"], - "ExportSpecifier": ["local", "exported"], "ExpressionStatement": ["expression"], "File": ["program"], "ForInStatement": ["left", "right", "body"], @@ -72,6 +68,12 @@ "WithStatement": ["object", "body"], "YieldExpression": ["argument"], + "ExportAllDeclaration": ["source", "exported"], + "ExportNamespaceDeclaration": ["exported"], + "ExportDefaultDeclaration": ["declaration"], + "ExportNamedDeclaration": ["declaration", "specifiers", "source"], + "ExportSpecifier": ["local", "exported"], + "AnyTypeAnnotation": [], "ArrayTypeAnnotation": ["elementType"], "BooleanTypeAnnotation": [], diff --git a/test/acorn/tests-babel.js b/test/acorn/tests-babel.js index c0d2a26e3e..1d4baaca90 100644 --- a/test/acorn/tests-babel.js +++ b/test/acorn/tests-babel.js @@ -2058,3 +2058,55 @@ test('export async function foo(){}', { }); // ES7 decorators + +// ES7 export extensions - https://github.com/leebyron/ecmascript-more-export-from + +test('export foo from "bar";', { + type: "Program", + body: [{ + type: "ExportNamespaceDeclaration", + start: 0, + end: 22, + exported: { + type: "Identifier", + name: "foo", + start: 7, + end: 10, + }, + source: { + type: "Literal", + value: "bar", + start: 16, + end: 21 + } + }] +}, { + ecmaVersion: 7, + sourceType: "module", + features: { "es7.exportExtensions": true } +}); + +test('export * as foo from "bar";', { + type: "Program", + body: [{ + type: "ExportAllDeclaration", + start: 0, + end: 27, + exported: { + type: "Identifier", + name: "foo", + start: 12, + end: 15, + }, + source: { + type: "Literal", + value: "bar", + start: 21, + end: 26 + } + }] +}, { + ecmaVersion: 7, + sourceType: "module", + features: { "es7.exportExtensions": true } +}); diff --git a/test/core/fixtures/transformation/es7-export-extensions/default-commonjs/actual.js b/test/core/fixtures/transformation/es7-export-extensions/default-commonjs/actual.js new file mode 100644 index 0000000000..48783b0466 --- /dev/null +++ b/test/core/fixtures/transformation/es7-export-extensions/default-commonjs/actual.js @@ -0,0 +1 @@ +export foo from "bar"; diff --git a/test/core/fixtures/transformation/es7-export-extensions/default-commonjs/expected.js b/test/core/fixtures/transformation/es7-export-extensions/default-commonjs/expected.js new file mode 100644 index 0000000000..dee58d49c3 --- /dev/null +++ b/test/core/fixtures/transformation/es7-export-extensions/default-commonjs/expected.js @@ -0,0 +1,5 @@ +"use strict"; + +var _default = babelHelpers.interopRequire(require("bar")); + +module.exports = _default; diff --git a/test/core/fixtures/transformation/es7-export-extensions/default-es6/actual.js b/test/core/fixtures/transformation/es7-export-extensions/default-es6/actual.js new file mode 100644 index 0000000000..48783b0466 --- /dev/null +++ b/test/core/fixtures/transformation/es7-export-extensions/default-es6/actual.js @@ -0,0 +1 @@ +export foo from "bar"; diff --git a/test/core/fixtures/transformation/es7-export-extensions/default-es6/expected.js b/test/core/fixtures/transformation/es7-export-extensions/default-es6/expected.js new file mode 100644 index 0000000000..5557329cbb --- /dev/null +++ b/test/core/fixtures/transformation/es7-export-extensions/default-es6/expected.js @@ -0,0 +1,4 @@ +"use strict"; + +import _default from "bar"; +export default _default; diff --git a/test/core/fixtures/transformation/es7-export-extensions/default-es6/options.json b/test/core/fixtures/transformation/es7-export-extensions/default-es6/options.json new file mode 100644 index 0000000000..896d86d463 --- /dev/null +++ b/test/core/fixtures/transformation/es7-export-extensions/default-es6/options.json @@ -0,0 +1,3 @@ +{ + "blacklist": ["es6.modules"] +} diff --git a/test/core/fixtures/transformation/es7-export-extensions/namespace-commonjs/actual.js b/test/core/fixtures/transformation/es7-export-extensions/namespace-commonjs/actual.js new file mode 100644 index 0000000000..d9d78f9ccd --- /dev/null +++ b/test/core/fixtures/transformation/es7-export-extensions/namespace-commonjs/actual.js @@ -0,0 +1 @@ +export * as foo from "bar"; diff --git a/test/core/fixtures/transformation/es7-export-extensions/namespace-commonjs/expected.js b/test/core/fixtures/transformation/es7-export-extensions/namespace-commonjs/expected.js new file mode 100644 index 0000000000..81a8ec84a9 --- /dev/null +++ b/test/core/fixtures/transformation/es7-export-extensions/namespace-commonjs/expected.js @@ -0,0 +1,9 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _foo = babelHelpers.interopRequireWildcard(require("bar")); + +exports.foo = _foo; diff --git a/test/core/fixtures/transformation/es7-export-extensions/namespace-es6/actual.js b/test/core/fixtures/transformation/es7-export-extensions/namespace-es6/actual.js new file mode 100644 index 0000000000..d9d78f9ccd --- /dev/null +++ b/test/core/fixtures/transformation/es7-export-extensions/namespace-es6/actual.js @@ -0,0 +1 @@ +export * as foo from "bar"; diff --git a/test/core/fixtures/transformation/es7-export-extensions/namespace-es6/expected.js b/test/core/fixtures/transformation/es7-export-extensions/namespace-es6/expected.js new file mode 100644 index 0000000000..58aec8dcdf --- /dev/null +++ b/test/core/fixtures/transformation/es7-export-extensions/namespace-es6/expected.js @@ -0,0 +1,4 @@ +"use strict"; + +import * as _foo from "bar"; +export { _foo as foo }; diff --git a/test/core/fixtures/transformation/es7-export-extensions/namespace-es6/options.json b/test/core/fixtures/transformation/es7-export-extensions/namespace-es6/options.json new file mode 100644 index 0000000000..896d86d463 --- /dev/null +++ b/test/core/fixtures/transformation/es7-export-extensions/namespace-es6/options.json @@ -0,0 +1,3 @@ +{ + "blacklist": ["es6.modules"] +} diff --git a/test/core/fixtures/transformation/es7-export-extensions/options.json b/test/core/fixtures/transformation/es7-export-extensions/options.json new file mode 100644 index 0000000000..e5a4490293 --- /dev/null +++ b/test/core/fixtures/transformation/es7-export-extensions/options.json @@ -0,0 +1,5 @@ +{ + "noCheckAst": true, + "externalHelpers": true, + "optional": "es7.exportExtensions" +}