diff --git a/src/babel/transformation/modules/system.js b/src/babel/transformation/modules/system.js index ae5cbb57a0..7223c4f9ba 100644 --- a/src/babel/transformation/modules/system.js +++ b/src/babel/transformation/modules/system.js @@ -78,6 +78,7 @@ export default class SystemFormatter extends AMDFormatter { constructor(file) { super(file); + this._setters = null; this.exportIdentifier = file.scope.generateUidIdentifier("export"); this.noInteropRequireExport = true; this.noInteropRequireImport = true; @@ -178,11 +179,14 @@ export default class SystemFormatter extends AMDFormatter { var block = t.blockStatement(program.body); + var setterListNode = this._buildRunnerSetters(block, hoistDeclarators); + this._setters = setterListNode; + var runner = util.template("system", { MODULE_DEPENDENCIES: t.arrayExpression(this.buildDependencyLiterals()), EXPORT_IDENTIFIER: this.exportIdentifier, MODULE_NAME: moduleNameLiteral, - SETTERS: this._buildRunnerSetters(block, hoistDeclarators), + SETTERS: setterListNode, EXECUTE: t.functionExpression(null, [], block) }, true); diff --git a/src/babel/transformation/transformers/index.js b/src/babel/transformation/transformers/index.js index 31e1bde216..9dacb52d1e 100644 --- a/src/babel/transformation/transformers/index.js +++ b/src/babel/transformation/transformers/index.js @@ -84,4 +84,5 @@ export default { _blockHoist: require("./internal/block-hoist"), jscript: require("babel-plugin-jscript"), flow: require("./other/flow"), + "optimisation.modules.system": require("./optimisation/modules.system"), }; diff --git a/src/babel/transformation/transformers/optimisation/modules.system.js b/src/babel/transformation/transformers/optimisation/modules.system.js new file mode 100644 index 0000000000..9c2388eb2c --- /dev/null +++ b/src/babel/transformation/transformers/optimisation/modules.system.js @@ -0,0 +1,50 @@ +import * as t from "../../../types"; + +export var metadata = { + optional: true, + group: "builtin-trailing" +}; + +export var visitor = { + Program(node, parent, scope, file){ + if (file.moduleFormatter._setters){ + scope.traverse(file.moduleFormatter._setters, optimizeSettersVisitor, { + exportFunctionIdentifier: file.moduleFormatter.exportIdentifier + }); + } + } +}; + +/** + * Setters are optimized to avoid slow export behavior in modules that rely on deep hierarchies + * of export-from declarations. + * More info in https://github.com/babel/babel/pull/1722 and + * https://github.com/ModuleLoader/es6-module-loader/issues/386. + * + * TODO: Ideally this would be optimized during construction of the setters, but the current + * architecture of the module formatters make that difficult. + */ +var optimizeSettersVisitor = { + FunctionExpression: { + enter: (node, parent, scope, state) => { + state.hasExports = false; + state.exportObjectIdentifier = scope.generateUidIdentifier("exportObj"); + }, + exit: (node, parent, scope, state) => { + if (!state.hasExports) return; + + node.body.body.unshift(t.variableDeclaration("var", [ + t.variableDeclarator(t.cloneDeep(state.exportObjectIdentifier), t.objectExpression([])) + ])); + node.body.body.push(t.expressionStatement(t.callExpression( + t.cloneDeep(state.exportFunctionIdentifier), [t.cloneDeep(state.exportObjectIdentifier)]))); + } + }, + CallExpression: (node, parent, scope, state) => { + if (!t.isIdentifier(node.callee, {name: state.exportFunctionIdentifier.name})) return; + + state.hasExports = true; + var memberNode = t.memberExpression(t.cloneDeep(state.exportObjectIdentifier), node.arguments[0], true); + return t.assignmentExpression("=", memberNode, node.arguments[1]); + } +}; diff --git a/test/core/fixtures/transformation/optimisation.modules.system/options.json b/test/core/fixtures/transformation/optimisation.modules.system/options.json new file mode 100644 index 0000000000..4609d2a157 --- /dev/null +++ b/test/core/fixtures/transformation/optimisation.modules.system/options.json @@ -0,0 +1,4 @@ +{ + "modules": "system", + "optional": ["optimisation.modules.system"] +} diff --git a/test/core/fixtures/transformation/optimisation.modules.system/setters/actual.js b/test/core/fixtures/transformation/optimisation.modules.system/setters/actual.js new file mode 100644 index 0000000000..9dcc9094ea --- /dev/null +++ b/test/core/fixtures/transformation/optimisation.modules.system/setters/actual.js @@ -0,0 +1,8 @@ +export * from "foo"; +export {foo} from "foo"; +export {foo, bar} from "foo"; +export {foo as bar} from "bar"; +export {foo as default} from "bar"; +export {foo as default, bar} from "bar"; + +export var foo, bar; diff --git a/test/core/fixtures/transformation/optimisation.modules.system/setters/expected.js b/test/core/fixtures/transformation/optimisation.modules.system/setters/expected.js new file mode 100644 index 0000000000..24f9c92628 --- /dev/null +++ b/test/core/fixtures/transformation/optimisation.modules.system/setters/expected.js @@ -0,0 +1,33 @@ +System.register(["foo", "bar"], function (_export) { + "use strict"; + + var foo, bar; + return { + setters: [function (_foo) { + var _exportObj = {}; + + for (var _key in _foo) { + _exportObj[_key] = _foo[_key]; + } + + _exportObj["foo"] = _foo.foo; + _exportObj["foo"] = _foo.foo; + _exportObj["bar"] = _foo.bar; + + _export(_exportObj); + }, function (_bar) { + var _exportObj2 = {}; + _exportObj2["bar"] = _bar.foo; + _exportObj2["default"] = _bar.foo; + _exportObj2["default"] = _bar.foo; + _exportObj2["bar"] = _bar.bar; + + _export(_exportObj2); + }], + execute: function () { + _export("foo", foo); + + _export("bar", bar); + } + }; +});