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/es6/destructuring.js b/src/babel/transformation/transformers/es6/destructuring.js index ebf7751d7a..c87c5f8214 100644 --- a/src/babel/transformation/transformers/es6/destructuring.js +++ b/src/babel/transformation/transformers/es6/destructuring.js @@ -316,9 +316,12 @@ class DestructuringTransformer { var left = pattern.left; if (t.isPattern(left)) { - this.nodes.push(t.expressionStatement( + var tempValueDefault = t.expressionStatement( t.assignmentExpression("=", tempValueRef, tempConditional) - )); + ); + tempValueDefault._blockHoist = this.blockHoist; + + this.nodes.push(tempValueDefault); this.push(left, tempValueRef); } else { this.nodes.push(this.buildVariableAssignment(left, tempConditional)); 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/es6.destructuring/parameters/actual.js b/test/core/fixtures/transformation/es6.destructuring/parameters/actual.js index 4446360a01..23e062af36 100644 --- a/test/core/fixtures/transformation/es6.destructuring/parameters/actual.js +++ b/test/core/fixtures/transformation/es6.destructuring/parameters/actual.js @@ -1,4 +1,4 @@ -function somethingAdvanced({topLeft: {x: x1, y: y1}, bottomRight: {x: x2, y: y2}}){ +function somethingAdvanced({topLeft: {x: x1, y: y1} = {}, bottomRight: {x: x2, y: y2} = {}}, p2, p3){ } diff --git a/test/core/fixtures/transformation/es6.destructuring/parameters/expected.js b/test/core/fixtures/transformation/es6.destructuring/parameters/expected.js index 50365469a1..dc5d19e9ef 100644 --- a/test/core/fixtures/transformation/es6.destructuring/parameters/expected.js +++ b/test/core/fixtures/transformation/es6.destructuring/parameters/expected.js @@ -1,10 +1,12 @@ "use strict"; -function somethingAdvanced(_ref) { +function somethingAdvanced(_ref, p2, p3) { var _ref$topLeft = _ref.topLeft; + _ref$topLeft = _ref$topLeft === undefined ? {} : _ref$topLeft; var x1 = _ref$topLeft.x; var y1 = _ref$topLeft.y; var _ref$bottomRight = _ref.bottomRight; + _ref$bottomRight = _ref$bottomRight === undefined ? {} : _ref$bottomRight; var x2 = _ref$bottomRight.x; var y2 = _ref$bottomRight.y; } 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); + } + }; +});