From 82969651c5d2e049d89e384bf8f04ff2f0e4fd9a Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 00:12:40 +0900 Subject: [PATCH 001/103] Add a spec mode to transform-es2015-modules-commonjs --- packages/babel-helpers/src/helpers.js | 31 ++ .../src/index.js | 347 ++++++++++++++---- .../fixtures/spec/commonjs-shadow/actual.js | 7 + .../fixtures/spec/commonjs-shadow/expected.js | 22 ++ .../fixtures/spec/export-default/actual.js | 3 + .../fixtures/spec/export-default/expected.js | 24 ++ .../test/fixtures/spec/exports/actual.js | 13 + .../test/fixtures/spec/exports/expected.js | 62 ++++ .../test/fixtures/spec/import/actual.js | 23 ++ .../test/fixtures/spec/import/expected.js | 23 ++ .../test/fixtures/spec/options.json | 3 + .../test/fixtures/spec/reexports/actual.js | 11 + .../test/fixtures/spec/reexports/expected.js | 58 +++ 13 files changed, 554 insertions(+), 73 deletions(-) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/options.json create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 87a58c3dc957..b5733159978a 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -396,6 +396,9 @@ helpers.interopRequireDefault = template(` }) `); +// interopRequireDefault doesn't technically match spec, +// but where it does not match is not observable + helpers.interopRequireWildcard = template(` (function (obj) { if (obj && obj.__esModule) { @@ -413,6 +416,34 @@ helpers.interopRequireWildcard = template(` }) `); +helpers.specRequireInterop = template(` + (function (obj) { + if (obj && obj.__esModule) { + return obj; + } else { + var newObj = Object.create + ? Object.create(null, { + default: { + value: obj, + writable: true, + enumerable: true + }, + __esModule: { + value: true + } + }) + : { + default: obj, + __esModule: true + }; + if (Symbol && Symbol.toStringTag) { + Object.defineProperty(newObj, Symbol.toStringTag, { value: "Module" }) + } + return (Object.freeze || Object)(newObj); + } + }) +`); + helpers.newArrowCheck = template(` (function (innerThis, boundThis) { if (innerThis !== boundThis) { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 8d3351bfa92e..17235e77e280 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -43,20 +43,91 @@ let buildExportAll = template(` }); `); +const specBuildNamespace = template(` + $0 = $1 = Object.create(null, { __esModule: { value: true } }); +`); + +const specBuildFunctionNameWrapper = template(` + ({ default: $0 }).default +`); + +const specBuildExportDefault = template(` + Object.defineProperty(EXPORTS, "default", { enumerable: true, value: VALUE }); +`); + +const specBuildTempExportDescriptor = template(` + const $0 = { enumerable: true, configurable: true, value: undefined }; +`); + +const specBuildTempExportProperty = (id, descriptorId) => t.objectProperty(id, descriptorId); + +// Being configurable is not spec compliant; but not doing this would prevent updating +// the export with the true value later. +const specBuildTempExport = template(` + Object.defineProperties($0, $1); +`); + +const specBuildExport = template(` + Object.defineProperty(EXPORTS, NAME, { enumerable: true, get() { return VALUE; } }); +`); + +const specBuildNamespaceReexport = template(` + Object.defineProperty(EXPORTS, NAME, { enumerable: true, value: VALUE }); +`); + +const specBuildNamespaceSpread = template(` + Object.keys(OBJECT).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + Object.defineProperty(EXPORTS, key, { + enumerable: true, + get: function () { + return OBJECT[key]; + } + }); + }); +`); + +const specFinishNamespaceExport = template(` + Object.freeze($0); +`); + const THIS_BREAK_KEYS = ["FunctionExpression", "FunctionDeclaration", "ClassProperty", "ClassMethod", "ObjectMethod"]; export default function () { let REASSIGN_REMAP_SKIP = Symbol(); + const moduleExports = t.memberExpression(t.identifier("module"), t.identifier("exports")); + moduleExports[REASSIGN_REMAP_SKIP] = true; + const exportsObj = t.identifier("exports"); + exportsObj[REASSIGN_REMAP_SKIP] = true; + + const isModuleExports = t.buildMatchMemberExpression("module.exports", true); + let reassignmentVisitor = { - ReferencedIdentifier(path) { + ReferencedIdentifier(path, state) { + const spec = isSpec(state); let name = path.node.name; let remap = this.remaps[name]; - if (!remap) return; + if (!spec && !remap) return; // redeclared in this scope if (this.scope.getBinding(name) !== path.scope.getBinding(name)) return; + if (spec) { + if (name === "exports" && !path.node[REASSIGN_REMAP_SKIP]) { + remap = this.remaps[".exports"] = this.remaps[".exports"] || path.scope.generateUidIdentifier("exports"); + path.replaceWith(remap); + return; + } + if (isModuleExports(path.parent) && !path.parent[REASSIGN_REMAP_SKIP]) { + remap = this.remaps[".module.exports"] = this.remaps[".module.exports"] || path.scope.generateUidIdentifier("module.exports"); + path.parentPath.replaceWith(remap); + return; + } + + if (!remap) return; + } + if (path.parentPath.isCallExpression({ callee: path.node })) { path.replaceWith(t.sequenceExpression([t.numericLiteral(0), remap])); } else if (path.isJSXIdentifier() && t.isMemberExpression(remap)) { @@ -68,7 +139,11 @@ export default function () { this.requeueInParent(path); }, - AssignmentExpression(path) { + AssignmentExpression(path, state) { + if (isSpec(state)) { + return; + } + let node = path.node; if (node[REASSIGN_REMAP_SKIP]) return; @@ -92,7 +167,11 @@ export default function () { this.requeueInParent(path); }, - UpdateExpression(path) { + UpdateExpression(path, state) { + if (isSpec(state)) { + return; + } + let arg = path.get("argument"); if (!arg.isIdentifier()) return; @@ -146,10 +225,11 @@ export default function () { }, Program: { - exit(path) { + exit(path, state) { this.ranCommonJS = true; let strict = !!this.opts.strict; + const spec = isSpec(state); let { scope } = path; @@ -169,19 +249,26 @@ export default function () { let topNodes = []; let remaps = Object.create(null); + const namespaceImports = new Set(); let requires = Object.create(null); - function addRequire(source, blockHoist) { + const addRequire = (source, spec, blockHoist) => { let cached = requires[source]; if (cached) return cached; let ref = path.scope.generateUidIdentifier(basename(source, extname(source))); + const req = buildRequire( + t.stringLiteral(source) + ).expression; + let varDecl = t.variableDeclaration("var", [ - t.variableDeclarator(ref, buildRequire( - t.stringLiteral(source) - ).expression) + t.variableDeclarator(ref, + spec + ? t.callExpression(this.addHelper("specRequireInterop"), [req]) + : req + ) ]); // Copy location from the original import statement for sourcemap @@ -197,7 +284,7 @@ export default function () { topNodes.push(varDecl); return requires[source] = ref; - } + }; function addTo(obj, key, arr) { let existing = obj[key] || []; @@ -229,6 +316,13 @@ export default function () { importsEntry.specifiers.push(...path.node.specifiers); + if (spec) { + path.node.specifiers + .filter((s) => t.isImportNamespaceSpecifier(s)) + .map((specifier) => specifier.local.name) + .forEach((name) => { namespaceImports.add(name); }); + } + if (typeof path.node._blockHoist === "number") { importsEntry.maxBlockHoist = Math.max( path.node._blockHoist, @@ -246,10 +340,15 @@ export default function () { let defNode = t.identifier("default"); if (id) { addTo(exports, id.name, defNode); - topNodes.push(buildExportsAssignment(defNode, id)); + topNodes.push(spec ? specBuildExportDefault({ EXPORTS: exportsObj, VALUE: id }) : buildExportsAssignment(defNode, id)); path.replaceWith(declaration.node); + } else if (spec) { + const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; + topNodes.push(specBuildExportDefault({ EXPORTS: exportsObj, VALUE: expr })); + path.remove(); } else { - topNodes.push(buildExportsAssignment(defNode, t.toExpression(declaration.node))); + const expr = t.toExpression(declaration.node); + topNodes.push(buildExportsAssignment(defNode, expr)); path.remove(); } } else if (declaration.isClassDeclaration()) { @@ -259,10 +358,19 @@ export default function () { addTo(exports, id.name, defNode); path.replaceWithMultiple([ declaration.node, - buildExportsAssignment(defNode, id) + spec ? specBuildExportDefault({ EXPORTS: exportsObj, VALUE: id }) : buildExportsAssignment(defNode, id) ]); + } else if (spec) { + const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; + path.replaceWith(specBuildExportDefault({ EXPORTS: exportsObj, VALUE: expr })); + + // Manually re-queue the expression so other transforms can get to it. + // Ideally this would happen automatically from the replaceWith above. + // See #4140 for more info. + path.parentPath.requeue(path.get("expression.arguments.2")); } else { - path.replaceWith(buildExportsAssignment(defNode, t.toExpression(declaration.node))); + const expr = t.toExpression(declaration.node); + path.replaceWith(buildExportsAssignment(defNode, expr)); // Manualy re-queue `export default class {}` expressions so that the ES3 transform // has an opportunity to convert them. Ideally this would happen automatically from the @@ -270,12 +378,22 @@ export default function () { path.parentPath.requeue(path.get("expression.left")); } } else { - path.replaceWith(buildExportsAssignment(t.identifier("default"), declaration.node)); + const defNode = t.identifier("default"); + if (spec) { + path.replaceWith(specBuildExportDefault({ EXPORTS: exportsObj, VALUE: declaration.node })); + + // Manually re-queue the expression so other transforms can get to it. + // Ideally this would happen automatically from the replaceWith above. + // See #4140 for more info. + path.parentPath.requeue(path.get("expression.arguments.2")); + } else { + path.replaceWith(buildExportsAssignment(defNode, declaration.node)); - // Manualy re-queue `export default foo;` expressions so that the ES3 transform - // has an opportunity to convert them. Ideally this would happen automatically from the - // replaceWith above. See #4140 for more info. - path.parentPath.requeue(path.get("expression.left")); + // Manualy re-queue `export default foo;` expressions so that the ES3 transform + // has an opportunity to convert them. Ideally this would happen automatically from the + // replaceWith above. See #4140 for more info. + path.parentPath.requeue(path.get("expression.left")); + } } } else if (path.isExportNamedDeclaration()) { let declaration = path.get("declaration"); @@ -283,33 +401,57 @@ export default function () { if (declaration.isFunctionDeclaration()) { let id = declaration.node.id; addTo(exports, id.name, id); - topNodes.push(buildExportsAssignment(id, id)); + topNodes.push(spec + ? specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral(id.name), VALUE: id }) + : buildExportsAssignment(id, id)); path.replaceWith(declaration.node); } else if (declaration.isClassDeclaration()) { let id = declaration.node.id; addTo(exports, id.name, id); path.replaceWithMultiple([ declaration.node, - buildExportsAssignment(id, id) + spec + ? specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral(id.name), VALUE: id }) + : buildExportsAssignment(id, id) ]); nonHoistedExportNames[id.name] = true; } else if (declaration.isVariableDeclaration()) { let declarators = declaration.get("declarations"); + const toExport = spec && new Set(); for (let decl of declarators) { let id = decl.get("id"); let init = decl.get("init"); if (!init.node) init.replaceWith(t.identifier("undefined")); + if (spec) { + toExport.add(id.node); + nonHoistedExportNames[id.node.name] = true; + continue; + } + if (id.isIdentifier()) { addTo(exports, id.node.name, id.node); - init.replaceWith(buildExportsAssignment(id.node, init.node).expression); + if (spec) { + } else { + init.replaceWith(buildExportsAssignment(id.node, init.node).expression); + } nonHoistedExportNames[id.node.name] = true; } else { // todo } } - path.replaceWith(declaration.node); + if (spec) { + path.replaceWithMultiple([ + declaration.node + ].concat( + Array.from(toExport) + .map((id) => specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral(id.name), VALUE: id})) + ) + ); + } else { + path.replaceWith(declaration.node); + } } continue; } @@ -318,7 +460,7 @@ export default function () { let nodes = []; let source = path.node.source; if (source) { - let ref = addRequire(source.value, path.node._blockHoist); + let ref = addRequire(source.value, spec, path.node._blockHoist); for (let specifier of specifiers) { if (specifier.isExportNamespaceSpecifier()) { @@ -326,7 +468,9 @@ export default function () { } else if (specifier.isExportDefaultSpecifier()) { // todo } else if (specifier.isExportSpecifier()) { - if (specifier.node.local.name === "default") { + if (spec) { + topNodes.push(specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral(specifier.node.exported.name), VALUE: t.memberExpression(ref, specifier.node.local) })); + } else if (specifier.node.local.name === "default") { topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name), t.memberExpression(t.callExpression(this.addHelper("interopRequireDefault"), [ref]), specifier.node.local))); } else { topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name), t.memberExpression(ref, specifier.node.local))); @@ -339,15 +483,30 @@ export default function () { if (specifier.isExportSpecifier()) { addTo(exports, specifier.node.local.name, specifier.node.exported); nonHoistedExportNames[specifier.node.exported.name] = true; - nodes.push(buildExportsAssignment(specifier.node.exported, specifier.node.local)); + + if (spec) { + if (namespaceImports.has(specifier.node.local.name)) { + // it's a namespace, doesn't go through a getter, safe to reexport as a value + nodes.push(specBuildNamespaceReexport({ EXPORTS: exportsObj, NAME: t.stringLiteral(specifier.node.exported.name), VALUE: specifier.node.local })); + } else { + nodes.push(specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral(specifier.node.exported.name), VALUE: specifier.node.local })); + } + } else { + nodes.push(buildExportsAssignment(specifier.node.exported, specifier.node.local)); + } } } } path.replaceWithMultiple(nodes); } else if (path.isExportAllDeclaration()) { - let exportNode = buildExportAll({ - OBJECT: addRequire(path.node.source.value, path.node._blockHoist) - }); + let exportNode = spec + ? specBuildNamespaceSpread({ + EXPORTS: exportsObj, + OBJECT: addRequire(path.node.source.value, spec, path.node._blockHoist) + }) + : buildExportAll({ + OBJECT: addRequire(path.node.source.value, spec, path.node._blockHoist) + }); exportNode.loc = path.node.loc; topNodes.push(exportNode); path.remove(); @@ -357,51 +516,32 @@ export default function () { for (let source in imports) { let {specifiers, maxBlockHoist} = imports[source]; if (specifiers.length) { - let uid = addRequire(source, maxBlockHoist); + const uid = addRequire(source, spec, maxBlockHoist); - let wildcard; - - for (let i = 0; i < specifiers.length; i++) { - let specifier = specifiers[i]; - if (t.isImportNamespaceSpecifier(specifier)) { - if (strict) { + if (spec) { + for (const specifier of specifiers) { + if (t.isImportNamespaceSpecifier(specifier)) { remaps[specifier.local.name] = uid; + } else if (t.isImportDefaultSpecifier(specifier)) { + remaps[specifier.local.name] = t.memberExpression(uid, t.identifier("default")); } else { - const varDecl = t.variableDeclaration("var", [ - t.variableDeclarator( - specifier.local, - t.callExpression( - this.addHelper("interopRequireWildcard"), - [uid] - ) - ) - ]); - - if (maxBlockHoist > 0) { - varDecl._blockHoist = maxBlockHoist; - } - - topNodes.push(varDecl); + remaps[specifier.local.name] = t.memberExpression(uid, t.cloneWithoutLoc(specifier.imported)); } - wildcard = specifier.local; - } else if (t.isImportDefaultSpecifier(specifier)) { - specifiers[i] = t.importSpecifier(specifier.local, t.identifier("default")); } - } + } else { + let wildcard; - for (let specifier of specifiers) { - if (t.isImportSpecifier(specifier)) { - let target = uid; - if (specifier.imported.name === "default") { - if (wildcard) { - target = wildcard; + for (let i = 0; i < specifiers.length; i++) { + let specifier = specifiers[i]; + if (t.isImportNamespaceSpecifier(specifier)) { + if (strict) { + remaps[specifier.local.name] = uid; } else { - target = wildcard = path.scope.generateUidIdentifier(uid.name); const varDecl = t.variableDeclaration("var", [ t.variableDeclarator( - target, + specifier.local, t.callExpression( - this.addHelper("interopRequireDefault"), + this.addHelper("interopRequireWildcard"), [uid] ) ) @@ -413,8 +553,39 @@ export default function () { topNodes.push(varDecl); } + wildcard = specifier.local; + } else if (t.isImportDefaultSpecifier(specifier)) { + specifiers[i] = t.importSpecifier(specifier.local, t.identifier("default")); + } + } + + for (let specifier of specifiers) { + if (t.isImportSpecifier(specifier)) { + let target = uid; + if (specifier.imported.name === "default") { + if (wildcard) { + target = wildcard; + } else { + target = wildcard = path.scope.generateUidIdentifier(uid.name); + const varDecl = t.variableDeclaration("var", [ + t.variableDeclarator( + target, + t.callExpression( + this.addHelper("interopRequireDefault"), + [uid] + ) + ) + ]); + + if (maxBlockHoist > 0) { + varDecl._blockHoist = maxBlockHoist; + } + + topNodes.push(varDecl); + } + } + remaps[specifier.local.name] = t.memberExpression(target, t.cloneWithoutLoc(specifier.imported)); } - remaps[specifier.local.name] = t.memberExpression(target, t.cloneWithoutLoc(specifier.imported)); } } } else { @@ -428,20 +599,37 @@ export default function () { if (hasImports && Object.keys(nonHoistedExportNames).length) { let hoistedExportsNode = t.identifier("undefined"); - for (let name in nonHoistedExportNames) { - hoistedExportsNode = buildExportsAssignment(t.identifier(name), hoistedExportsNode).expression; - } + if (spec) { + const descId = path.scope.generateUidIdentifier("undefined"); + const desc = specBuildTempExportDescriptor(descId); + desc._blockHoist = 3; + + const expr = t.objectExpression( + Object.keys(nonHoistedExportNames).map((name) => + specBuildTempExportProperty(t.identifier(name), descId) + ) + ); + const node = specBuildTempExport(exportsObj, expr); + node._blockHoist = 3; + + topNodes.unshift(node); + topNodes.unshift(desc); + } else { + for (let name in nonHoistedExportNames) { + hoistedExportsNode = buildExportsAssignment(t.identifier(name), hoistedExportsNode).expression; + } - const node = t.expressionStatement(hoistedExportsNode); - node._blockHoist = 3; + const node = t.expressionStatement(hoistedExportsNode); + node._blockHoist = 3; - topNodes.unshift(node); + topNodes.unshift(node); + } } // add __esModule declaration if this file has any exports - if (hasExports && !strict) { + if (hasExports && !strict && !spec) { let buildTemplate = buildExportsModuleDeclaration; - if (this.opts.loose) buildTemplate = buildLooseExportsModuleDeclaration; + if (state.opts.loose) buildTemplate = buildLooseExportsModuleDeclaration; const declar = buildTemplate(); declar._blockHoist = 3; @@ -449,11 +637,20 @@ export default function () { topNodes.unshift(declar); } + if (hasExports && spec) { + const decl = specBuildNamespace(moduleExports, exportsObj); + decl._blockHoist = 3; + topNodes.unshift(decl); + + path.pushContainer("body", [specFinishNamespaceExport(exportsObj)]); + } + path.unshiftContainer("body", topNodes); path.traverse(reassignmentVisitor, { remaps, scope, exports, + opts: state.opts, requeueInParent: (newPath) => path.requeue(newPath), }); } @@ -461,3 +658,7 @@ export default function () { } }; } + +function isSpec (state) { + return state && state.opts && !!state.opts.spec; +} diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/actual.js new file mode 100644 index 000000000000..6b02231af737 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/actual.js @@ -0,0 +1,7 @@ +import * as foo from 'bar'; + +export default class {}; + +// neither of these should be able to use or affect the real exports +new exports.default(); +module.exports = {}; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js new file mode 100644 index 000000000000..019b8da432cc --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js @@ -0,0 +1,22 @@ +'use strict'; + +module.exports = exports = Object.create(null, { + __esModule: { + value: true + } +}); + +var _bar = babelHelpers.specRequireInterop(require('bar')); + +Object.defineProperty(exports, "default", { + enumerable: true, + value: { + default: class {} + }.default +}); +; + +// neither of these should be able to use or affect the real exports +new _exports.default(); +_moduleExports = {}; +Object.freeze(exports); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/actual.js new file mode 100644 index 000000000000..4d818cf550fb --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/actual.js @@ -0,0 +1,3 @@ +export default function () {} +export var unrelated; +unrelated = "changed"; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js new file mode 100644 index 000000000000..2ae6c4e2a48d --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -0,0 +1,24 @@ +"use strict"; + +module.exports = exports = Object.create(null, { + __esModule: { + value: true + } +}); +Object.defineProperty(exports, "default", { + enumerable: true, + value: { + default: function () {} + }.default +}); +var unrelated = undefined; +Object.defineProperty(exports, "unrelated", { + enumerable: true, + + get() { + return unrelated; + } + +}); +unrelated = "changed"; +Object.freeze(exports); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/actual.js new file mode 100644 index 000000000000..4b5d9f387a6c --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/actual.js @@ -0,0 +1,13 @@ +import { anything } from "outside" + +var def; +export { def as default } + +export function foo () { + def = "export mutation"; + return "foo"; +} + +export class Bar {} + +export const baz = foo(); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js new file mode 100644 index 000000000000..32839eed43e3 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -0,0 +1,62 @@ +"use strict"; + +module.exports = exports = Object.create(null, { + __esModule: { + value: true + } +}); +const _undefined = { + enumerable: true, + configurable: true, + value: undefined +}; +Object.defineProperties(exports, { + default: _undefined, + Bar: _undefined, + baz: _undefined +}); +Object.defineProperty(exports, "foo", { + enumerable: true, + + get() { + return foo; + } + +}); + +var _outside = babelHelpers.specRequireInterop(require("outside")); + +var def; +Object.defineProperty(exports, "default", { + enumerable: true, + + get() { + return def; + } + +}); +function foo() { + def = "export mutation"; + return "foo"; +} + +class Bar {} + +Object.defineProperty(exports, "Bar", { + enumerable: true, + + get() { + return Bar; + } + +}); +const baz = foo(); +Object.defineProperty(exports, "baz", { + enumerable: true, + + get() { + return baz; + } + +}); +Object.freeze(exports); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/actual.js new file mode 100644 index 000000000000..461824804461 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/actual.js @@ -0,0 +1,23 @@ +import * as namespace from './elsewhere'; + +import { default as outside, obj } from './outside'; + +outside(obj.key); + +import something from './anywhere'; + +something(namespace); + +import {} from './empty'; + +import './imperative'; + +import who, { what } from './i-dont-know'; + +import why, * as because from './naturally'; + +who[what](why, because.naturally); + +import { who as naturally } from './i-dont-know'; + +naturally(because); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js new file mode 100644 index 000000000000..7287f81c3b9e --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js @@ -0,0 +1,23 @@ +'use strict'; + +var _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); + +var _outside = babelHelpers.specRequireInterop(require('./outside')); + +var _anywhere = babelHelpers.specRequireInterop(require('./anywhere')); + +require('./empty'); + +require('./imperative'); + +var _iDontKnow = babelHelpers.specRequireInterop(require('./i-dont-know')); + +var _naturally = babelHelpers.specRequireInterop(require('./naturally')); + +(0, _outside.default)(_outside.obj.key); + +(0, _anywhere.default)(_elsewhere); + +_iDontKnow.default[_iDontKnow.what](_naturally.default, _naturally.naturally); + +(0, _iDontKnow.who)(_naturally); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/options.json b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/options.json new file mode 100644 index 000000000000..b213374a9332 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["external-helpers", ["transform-es2015-modules-commonjs", {"spec": true}]] +} diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/actual.js new file mode 100644 index 000000000000..97b540e2e809 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/actual.js @@ -0,0 +1,11 @@ +import * as namespace from './somewhere'; + +import { stuff } from './elsewhere'; + +export { namespace }; + +export { stuff as default }; + +export * from './i-dont-know'; + +export { default as why } from './because'; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js new file mode 100644 index 000000000000..be4b9bb2e183 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -0,0 +1,58 @@ +'use strict'; + +module.exports = exports = Object.create(null, { + __esModule: { + value: true + } +}); +const _undefined = { + enumerable: true, + configurable: true, + value: undefined +}; +Object.defineProperties(exports, { + namespace: _undefined, + default: _undefined, + why: _undefined +}); + +var _iDontKnow = babelHelpers.specRequireInterop(require('./i-dont-know')); + +Object.keys(_iDontKnow).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _iDontKnow[key]; + } + }); +}); + +var _because = babelHelpers.specRequireInterop(require('./because')); + +Object.defineProperty(exports, 'why', { + enumerable: true, + + get() { + return _because.default; + } + +}); + +var _somewhere = babelHelpers.specRequireInterop(require('./somewhere')); + +var _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); + +Object.defineProperty(exports, 'namespace', { + enumerable: true, + value: _somewhere +}); +Object.defineProperty(exports, 'default', { + enumerable: true, + + get() { + return _elsewhere.stuff; + } + +}); +Object.freeze(exports); \ No newline at end of file From 4a746eebc7050c4afe8b6800bfa098acfeabca23 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 00:42:12 +0900 Subject: [PATCH 002/103] Correct the descriptors to have writable: true --- .../src/index.js | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 17235e77e280..8b3cd98da754 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -51,28 +51,19 @@ const specBuildFunctionNameWrapper = template(` ({ default: $0 }).default `); +// The descriptors are as specified in https://tc39.github.io/ecma262/#sec-module-namespace-exotic-objects-getownproperty-p const specBuildExportDefault = template(` - Object.defineProperty(EXPORTS, "default", { enumerable: true, value: VALUE }); -`); - -const specBuildTempExportDescriptor = template(` - const $0 = { enumerable: true, configurable: true, value: undefined }; -`); - -const specBuildTempExportProperty = (id, descriptorId) => t.objectProperty(id, descriptorId); - -// Being configurable is not spec compliant; but not doing this would prevent updating -// the export with the true value later. -const specBuildTempExport = template(` - Object.defineProperties($0, $1); + Object.defineProperty(EXPORTS, "default", { enumerable: true, writable: true, value: VALUE }); `); +// Unfortunately, regular objects can't synthesize a value descriptor every time they're read, +// so a getter needs to be used for live bindings. const specBuildExport = template(` - Object.defineProperty(EXPORTS, NAME, { enumerable: true, get() { return VALUE; } }); + Object.defineProperty(EXPORTS, NAME, { enumerable: true, writable: true, get() { return VALUE; } }); `); const specBuildNamespaceReexport = template(` - Object.defineProperty(EXPORTS, NAME, { enumerable: true, value: VALUE }); + Object.defineProperty(EXPORTS, NAME, { enumerable: true, writable: true, value: VALUE }); `); const specBuildNamespaceSpread = template(` @@ -80,13 +71,24 @@ const specBuildNamespaceSpread = template(` if (key === "default" || key === "__esModule") return; Object.defineProperty(EXPORTS, key, { enumerable: true, - get: function () { + writable: true, + get() { return OBJECT[key]; } }); }); `); +const specBuildTempExportDescriptor = template(` + const $0 = { enumerable: true, writable: true, value: undefined }; +`); + +const specBuildTempExportProperty = (id, descriptorId) => t.objectProperty(id, descriptorId); + +const specBuildTempExport = template(` + Object.defineProperties($0, $1); +`); + const specFinishNamespaceExport = template(` Object.freeze($0); `); From 43bd0e92a143e8b81a5f47484373cf37c3b5668c Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 00:45:09 +0900 Subject: [PATCH 003/103] Update tests --- .../test/fixtures/spec/commonjs-shadow/expected.js | 1 + .../test/fixtures/spec/export-default/expected.js | 2 ++ .../test/fixtures/spec/exports/expected.js | 6 +++++- .../test/fixtures/spec/reexports/expected.js | 10 ++++++++-- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js index 019b8da432cc..2c310eae0a0c 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js @@ -10,6 +10,7 @@ var _bar = babelHelpers.specRequireInterop(require('bar')); Object.defineProperty(exports, "default", { enumerable: true, + writable: true, value: { default: class {} }.default diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js index 2ae6c4e2a48d..3f66bbedf1ca 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -7,6 +7,7 @@ module.exports = exports = Object.create(null, { }); Object.defineProperty(exports, "default", { enumerable: true, + writable: true, value: { default: function () {} }.default @@ -14,6 +15,7 @@ Object.defineProperty(exports, "default", { var unrelated = undefined; Object.defineProperty(exports, "unrelated", { enumerable: true, + writable: true, get() { return unrelated; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js index 32839eed43e3..58ad9e96f22a 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -7,7 +7,7 @@ module.exports = exports = Object.create(null, { }); const _undefined = { enumerable: true, - configurable: true, + writable: true, value: undefined }; Object.defineProperties(exports, { @@ -17,6 +17,7 @@ Object.defineProperties(exports, { }); Object.defineProperty(exports, "foo", { enumerable: true, + writable: true, get() { return foo; @@ -29,6 +30,7 @@ var _outside = babelHelpers.specRequireInterop(require("outside")); var def; Object.defineProperty(exports, "default", { enumerable: true, + writable: true, get() { return def; @@ -44,6 +46,7 @@ class Bar {} Object.defineProperty(exports, "Bar", { enumerable: true, + writable: true, get() { return Bar; @@ -53,6 +56,7 @@ Object.defineProperty(exports, "Bar", { const baz = foo(); Object.defineProperty(exports, "baz", { enumerable: true, + writable: true, get() { return baz; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index be4b9bb2e183..c6309cc1cb46 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -7,7 +7,7 @@ module.exports = exports = Object.create(null, { }); const _undefined = { enumerable: true, - configurable: true, + writable: true, value: undefined }; Object.defineProperties(exports, { @@ -22,9 +22,12 @@ Object.keys(_iDontKnow).forEach(function (key) { if (key === "default" || key === "__esModule") return; Object.defineProperty(exports, key, { enumerable: true, - get: function () { + writable: true, + + get() { return _iDontKnow[key]; } + }); }); @@ -32,6 +35,7 @@ var _because = babelHelpers.specRequireInterop(require('./because')); Object.defineProperty(exports, 'why', { enumerable: true, + writable: true, get() { return _because.default; @@ -45,10 +49,12 @@ var _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); Object.defineProperty(exports, 'namespace', { enumerable: true, + writable: true, value: _somewhere }); Object.defineProperty(exports, 'default', { enumerable: true, + writable: true, get() { return _elsewhere.stuff; From 3389e9faf90588bbf7a0e5d19bd6bc7d0df1ecc8 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 00:47:47 +0900 Subject: [PATCH 004/103] Simplify the specRequireInterop helper; spec isn't ES3 compatible --- packages/babel-helpers/src/helpers.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index b5733159978a..6bce30779825 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -421,8 +421,7 @@ helpers.specRequireInterop = template(` if (obj && obj.__esModule) { return obj; } else { - var newObj = Object.create - ? Object.create(null, { + var newObj = Object.create(null, { default: { value: obj, writable: true, @@ -431,15 +430,11 @@ helpers.specRequireInterop = template(` __esModule: { value: true } - }) - : { - default: obj, - __esModule: true - }; + }); if (Symbol && Symbol.toStringTag) { Object.defineProperty(newObj, Symbol.toStringTag, { value: "Module" }) } - return (Object.freeze || Object)(newObj); + return Object.freeze(newObj); } }) `); From 17cd0b985d30254d43737641a94b1a800e80dab9 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 00:48:55 +0900 Subject: [PATCH 005/103] Add missing @@toStringTag definition --- .../src/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 8b3cd98da754..fd617b7ee7ec 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -45,6 +45,9 @@ let buildExportAll = template(` const specBuildNamespace = template(` $0 = $1 = Object.create(null, { __esModule: { value: true } }); + if (Symbol && Symbol.toStringTag) { + Object.defineProperty($0, Symbol.toStringTag, { value: "Module" }) + } `); const specBuildFunctionNameWrapper = template(` From 42d870e1938cf89f944608ece0c38b520f6ff2eb Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 00:59:46 +0900 Subject: [PATCH 006/103] Fix broken AST attachment; update tests --- .../src/index.js | 11 ++++++----- .../test/fixtures/spec/commonjs-shadow/expected.js | 6 ++++++ .../test/fixtures/spec/export-default/expected.js | 7 +++++++ .../test/fixtures/spec/exports/expected.js | 7 +++++++ .../test/fixtures/spec/reexports/expected.js | 7 +++++++ 5 files changed, 33 insertions(+), 5 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index fd617b7ee7ec..a0a5ab43ee34 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -46,7 +46,7 @@ let buildExportAll = template(` const specBuildNamespace = template(` $0 = $1 = Object.create(null, { __esModule: { value: true } }); if (Symbol && Symbol.toStringTag) { - Object.defineProperty($0, Symbol.toStringTag, { value: "Module" }) + Object.defineProperty($0, Symbol.toStringTag, { value: "Module" }); } `); @@ -642,15 +642,16 @@ export default function () { topNodes.unshift(declar); } + path.unshiftContainer("body", topNodes); + if (hasExports && spec) { - const decl = specBuildNamespace(moduleExports, exportsObj); - decl._blockHoist = 3; - topNodes.unshift(decl); + const decls = specBuildNamespace(moduleExports, exportsObj); + decls.forEach((decl) => { decl._blockHoist = 3; }); + path.unshiftContainer("body", decls); path.pushContainer("body", [specFinishNamespaceExport(exportsObj)]); } - path.unshiftContainer("body", topNodes); path.traverse(reassignmentVisitor, { remaps, scope, diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js index 2c310eae0a0c..ca184036462f 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js @@ -6,6 +6,12 @@ module.exports = exports = Object.create(null, { } }); +if (Symbol && Symbol.toStringTag) { + Object.defineProperty(module.exports, Symbol.toStringTag, { + value: "Module" + }); +} + var _bar = babelHelpers.specRequireInterop(require('bar')); Object.defineProperty(exports, "default", { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js index 3f66bbedf1ca..fe82b92852e7 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -5,6 +5,13 @@ module.exports = exports = Object.create(null, { value: true } }); + +if (Symbol && Symbol.toStringTag) { + Object.defineProperty(module.exports, Symbol.toStringTag, { + value: "Module" + }); +} + Object.defineProperty(exports, "default", { enumerable: true, writable: true, diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js index 58ad9e96f22a..cacf97e5ddae 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -5,6 +5,13 @@ module.exports = exports = Object.create(null, { value: true } }); + +if (Symbol && Symbol.toStringTag) { + Object.defineProperty(module.exports, Symbol.toStringTag, { + value: "Module" + }); +} + const _undefined = { enumerable: true, writable: true, diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index c6309cc1cb46..e1e20a3a6a06 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -5,6 +5,13 @@ module.exports = exports = Object.create(null, { value: true } }); + +if (Symbol && Symbol.toStringTag) { + Object.defineProperty(module.exports, Symbol.toStringTag, { + value: "Module" + }); +} + const _undefined = { enumerable: true, writable: true, From 477cb99a13128232f358038651d299ab2f60909b Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 01:02:03 +0900 Subject: [PATCH 007/103] Use a check that won't actually throw in ES5 strict mode --- packages/babel-helpers/src/helpers.js | 2 +- .../babel-plugin-transform-es2015-modules-commonjs/src/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 6bce30779825..ee7696ac41ea 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -431,7 +431,7 @@ helpers.specRequireInterop = template(` value: true } }); - if (Symbol && Symbol.toStringTag) { + if (typeof Symbol !== "undefined" && Symbol.toStringTag) { Object.defineProperty(newObj, Symbol.toStringTag, { value: "Module" }) } return Object.freeze(newObj); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index a0a5ab43ee34..905059a35546 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -45,7 +45,7 @@ let buildExportAll = template(` const specBuildNamespace = template(` $0 = $1 = Object.create(null, { __esModule: { value: true } }); - if (Symbol && Symbol.toStringTag) { + if (typeof Symbol !== "undefined" && Symbol.toStringTag) { Object.defineProperty($0, Symbol.toStringTag, { value: "Module" }); } `); From 6fbf2ac824ca190bfa42124c274a85e05fc29bc2 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 01:02:50 +0900 Subject: [PATCH 008/103] Update fixtures --- .../test/fixtures/spec/commonjs-shadow/expected.js | 2 +- .../test/fixtures/spec/export-default/expected.js | 2 +- .../test/fixtures/spec/exports/expected.js | 2 +- .../test/fixtures/spec/reexports/expected.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js index ca184036462f..124eacdf2078 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js @@ -6,7 +6,7 @@ module.exports = exports = Object.create(null, { } }); -if (Symbol && Symbol.toStringTag) { +if (typeof Symbol !== "undefined" && Symbol.toStringTag) { Object.defineProperty(module.exports, Symbol.toStringTag, { value: "Module" }); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js index fe82b92852e7..cdb801b00157 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -6,7 +6,7 @@ module.exports = exports = Object.create(null, { } }); -if (Symbol && Symbol.toStringTag) { +if (typeof Symbol !== "undefined" && Symbol.toStringTag) { Object.defineProperty(module.exports, Symbol.toStringTag, { value: "Module" }); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js index cacf97e5ddae..cae265e1f14b 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -6,7 +6,7 @@ module.exports = exports = Object.create(null, { } }); -if (Symbol && Symbol.toStringTag) { +if (typeof Symbol !== "undefined" && Symbol.toStringTag) { Object.defineProperty(module.exports, Symbol.toStringTag, { value: "Module" }); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index e1e20a3a6a06..c6f4b0eaed9e 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -6,7 +6,7 @@ module.exports = exports = Object.create(null, { } }); -if (Symbol && Symbol.toStringTag) { +if (typeof Symbol !== "undefined" && Symbol.toStringTag) { Object.defineProperty(module.exports, Symbol.toStringTag, { value: "Module" }); From 906e8c12791d82f48b5d17dfa2e766c428452258 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 01:14:28 +0900 Subject: [PATCH 009/103] Don't set writable alongside getters Throws in v8 with "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute" I guess this is why the spec defines "magic changing values" instead of getters :sweat_smile: --- .../src/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 905059a35546..0c8b9249e04a 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -62,7 +62,7 @@ const specBuildExportDefault = template(` // Unfortunately, regular objects can't synthesize a value descriptor every time they're read, // so a getter needs to be used for live bindings. const specBuildExport = template(` - Object.defineProperty(EXPORTS, NAME, { enumerable: true, writable: true, get() { return VALUE; } }); + Object.defineProperty(EXPORTS, NAME, { enumerable: true, get() { return VALUE; } }); `); const specBuildNamespaceReexport = template(` @@ -74,7 +74,6 @@ const specBuildNamespaceSpread = template(` if (key === "default" || key === "__esModule") return; Object.defineProperty(EXPORTS, key, { enumerable: true, - writable: true, get() { return OBJECT[key]; } From b88c6c54955839b72f6c78f8d4cf7f3826a79355 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 01:15:00 +0900 Subject: [PATCH 010/103] Shorten generated code Repeat the shorter identifier in the block. --- .../src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 0c8b9249e04a..347a52c07173 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -44,7 +44,7 @@ let buildExportAll = template(` `); const specBuildNamespace = template(` - $0 = $1 = Object.create(null, { __esModule: { value: true } }); + $1 = $0 = Object.create(null, { __esModule: { value: true } }); if (typeof Symbol !== "undefined" && Symbol.toStringTag) { Object.defineProperty($0, Symbol.toStringTag, { value: "Module" }); } @@ -644,7 +644,7 @@ export default function () { path.unshiftContainer("body", topNodes); if (hasExports && spec) { - const decls = specBuildNamespace(moduleExports, exportsObj); + const decls = specBuildNamespace(exportsObj, moduleExports); decls.forEach((decl) => { decl._blockHoist = 3; }); path.unshiftContainer("body", decls); From 8f18792789f10f5795202b1e9cdb0992e0f2206b Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 01:16:07 +0900 Subject: [PATCH 011/103] Update fixtures --- .../test/fixtures/spec/commonjs-shadow/expected.js | 2 +- .../test/fixtures/spec/export-default/expected.js | 3 +-- .../test/fixtures/spec/exports/expected.js | 6 +----- .../test/fixtures/spec/reexports/expected.js | 5 +---- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js index 124eacdf2078..2d61482992b3 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js @@ -7,7 +7,7 @@ module.exports = exports = Object.create(null, { }); if (typeof Symbol !== "undefined" && Symbol.toStringTag) { - Object.defineProperty(module.exports, Symbol.toStringTag, { + Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); } diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js index cdb801b00157..2e9f228fb494 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -7,7 +7,7 @@ module.exports = exports = Object.create(null, { }); if (typeof Symbol !== "undefined" && Symbol.toStringTag) { - Object.defineProperty(module.exports, Symbol.toStringTag, { + Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); } @@ -22,7 +22,6 @@ Object.defineProperty(exports, "default", { var unrelated = undefined; Object.defineProperty(exports, "unrelated", { enumerable: true, - writable: true, get() { return unrelated; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js index cae265e1f14b..2a757b715dc2 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -7,7 +7,7 @@ module.exports = exports = Object.create(null, { }); if (typeof Symbol !== "undefined" && Symbol.toStringTag) { - Object.defineProperty(module.exports, Symbol.toStringTag, { + Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); } @@ -24,7 +24,6 @@ Object.defineProperties(exports, { }); Object.defineProperty(exports, "foo", { enumerable: true, - writable: true, get() { return foo; @@ -37,7 +36,6 @@ var _outside = babelHelpers.specRequireInterop(require("outside")); var def; Object.defineProperty(exports, "default", { enumerable: true, - writable: true, get() { return def; @@ -53,7 +51,6 @@ class Bar {} Object.defineProperty(exports, "Bar", { enumerable: true, - writable: true, get() { return Bar; @@ -63,7 +60,6 @@ Object.defineProperty(exports, "Bar", { const baz = foo(); Object.defineProperty(exports, "baz", { enumerable: true, - writable: true, get() { return baz; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index c6f4b0eaed9e..375a1da63039 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -7,7 +7,7 @@ module.exports = exports = Object.create(null, { }); if (typeof Symbol !== "undefined" && Symbol.toStringTag) { - Object.defineProperty(module.exports, Symbol.toStringTag, { + Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); } @@ -29,7 +29,6 @@ Object.keys(_iDontKnow).forEach(function (key) { if (key === "default" || key === "__esModule") return; Object.defineProperty(exports, key, { enumerable: true, - writable: true, get() { return _iDontKnow[key]; @@ -42,7 +41,6 @@ var _because = babelHelpers.specRequireInterop(require('./because')); Object.defineProperty(exports, 'why', { enumerable: true, - writable: true, get() { return _because.default; @@ -61,7 +59,6 @@ Object.defineProperty(exports, 'namespace', { }); Object.defineProperty(exports, 'default', { enumerable: true, - writable: true, get() { return _elsewhere.stuff; From 60900bdc05adb8ac6ab141e34b848592449a9edf Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 02:09:05 +0900 Subject: [PATCH 012/103] Fix several corner cases in the commons shadowing --- .../src/index.js | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 347a52c07173..ccad4ff942b6 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -100,12 +100,14 @@ const THIS_BREAK_KEYS = ["FunctionExpression", "FunctionDeclaration", "ClassProp export default function () { let REASSIGN_REMAP_SKIP = Symbol(); - const moduleExports = t.memberExpression(t.identifier("module"), t.identifier("exports")); - moduleExports[REASSIGN_REMAP_SKIP] = true; const exportsObj = t.identifier("exports"); exportsObj[REASSIGN_REMAP_SKIP] = true; + const moduleExports = t.memberExpression(t.identifier("module"), exportsObj); + moduleExports[REASSIGN_REMAP_SKIP] = true; const isModuleExports = t.buildMatchMemberExpression("module.exports", true); + const isExports = t.buildMatchMemberExpression("exports", true); + let commonjsExportsMasked = null; let reassignmentVisitor = { ReferencedIdentifier(path, state) { @@ -118,14 +120,20 @@ export default function () { if (this.scope.getBinding(name) !== path.scope.getBinding(name)) return; if (spec) { - if (name === "exports" && !path.node[REASSIGN_REMAP_SKIP]) { + if (name === "exports" && !path.node[REASSIGN_REMAP_SKIP] && + // Apparently replacing "module.exports" still visits the ".exports" here + !(this.remaps[".module.exports"] && t.isIdentifier(path.parent, this.remaps[".module.exports"])) && + // Avoid entering when it's just some other object key named export + !(isExports(path.parent) && t.isMemberExpression(path.parent.parent))) { remap = this.remaps[".exports"] = this.remaps[".exports"] || path.scope.generateUidIdentifier("exports"); path.replaceWith(remap); + commonjsExportsMasked.exports = remap; return; } - if (isModuleExports(path.parent) && !path.parent[REASSIGN_REMAP_SKIP]) { + if (isModuleExports(path.parent) && !path.parent[REASSIGN_REMAP_SKIP] && !t.isMemberExpression(path.parent.parent)) { remap = this.remaps[".module.exports"] = this.remaps[".module.exports"] || path.scope.generateUidIdentifier("module.exports"); path.parentPath.replaceWith(remap); + commonjsExportsMasked.moduleExports = remap; return; } @@ -145,6 +153,12 @@ export default function () { AssignmentExpression(path, state) { if (isSpec(state)) { + if (!path.node[REASSIGN_REMAP_SKIP] && t.isIdentifier(path.node.left, { name: "exports" }) && + !path.node.left[REASSIGN_REMAP_SKIP] && this.scope.getBinding(name) === path.scope.getBinding(name)) { + let remap = this.remaps[".exports"] = this.remaps[".exports"] || path.scope.generateUidIdentifier("exports"); + path.get("left").replaceWith(remap); + commonjsExportsMasked.exports = remap; + } return; } @@ -651,6 +665,8 @@ export default function () { path.pushContainer("body", [specFinishNamespaceExport(exportsObj)]); } + commonjsExportsMasked = {}; + path.traverse(reassignmentVisitor, { remaps, scope, @@ -658,6 +674,18 @@ export default function () { opts: state.opts, requeueInParent: (newPath) => path.requeue(newPath), }); + + if (spec && (commonjsExportsMasked.exports || commonjsExportsMasked.moduleExports)) { + path.unshiftContainer("body", [ + t.variableDeclaration("var", [ + commonjsExportsMasked.exports && t.variableDeclarator(commonjsExportsMasked.exports, t.objectExpression([])), + commonjsExportsMasked.moduleExports && commonjsExportsMasked.exports && + t.variableDeclarator(commonjsExportsMasked.moduleExports, commonjsExportsMasked.exports), + commonjsExportsMasked.moduleExports && !commonjsExportsMasked.exports && + t.variableDeclarator(commonjsExportsMasked.moduleExports, t.objectExpression([])) + ].filter(Boolean)) + ]); + } } } } From a57449f6db0082c13c67b67661bde480933f4994 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 02:09:17 +0900 Subject: [PATCH 013/103] Add more fixtures, update fixtures --- .../fixtures/spec/commonjs-shadow-1/actual.js | 3 +++ .../spec/commonjs-shadow-1/expected.js | 26 +++++++++++++++++++ .../fixtures/spec/commonjs-shadow-2/actual.js | 3 +++ .../spec/commonjs-shadow-2/expected.js | 19 ++++++++++++++ .../fixtures/spec/commonjs-shadow/expected.js | 3 +++ 5 files changed, 54 insertions(+) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/actual.js new file mode 100644 index 000000000000..7e4fee02a3e1 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/actual.js @@ -0,0 +1,3 @@ +export default function () {} + +exports = function () {}; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js new file mode 100644 index 000000000000..cda8436c1f19 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js @@ -0,0 +1,26 @@ +"use strict"; + +module.exports = exports = Object.create(null, { + __esModule: { + value: true + } +}); + +if (typeof Symbol !== "undefined" && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { + value: "Module" + }); +} + +var _exports = {}; +Object.defineProperty(exports, "default", { + enumerable: true, + writable: true, + value: { + default: function () {} + }.default +}); + + +_exports = function () {}; +Object.freeze(exports); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/actual.js new file mode 100644 index 000000000000..1a6e3c5624ab --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/actual.js @@ -0,0 +1,3 @@ +export {} + +module.exports = class {}; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js new file mode 100644 index 000000000000..2fce7f7d11f0 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js @@ -0,0 +1,19 @@ +"use strict"; + +module.exports = exports = Object.create(null, { + __esModule: { + value: true + } +}); + +if (typeof Symbol !== "undefined" && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { + value: "Module" + }); +} + +var _moduleExports = {}; + + +_moduleExports = class {}; +Object.freeze(exports); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js index 2d61482992b3..4d4f2991cca6 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js @@ -12,6 +12,9 @@ if (typeof Symbol !== "undefined" && Symbol.toStringTag) { }); } +var _exports = {}, + _moduleExports = _exports; + var _bar = babelHelpers.specRequireInterop(require('bar')); Object.defineProperty(exports, "default", { From d09ac54a7ea93763c521174ba92c1c89cc2a5ac1 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 02:18:21 +0900 Subject: [PATCH 014/103] Better check for typeof Symbol --- packages/babel-helpers/src/helpers.js | 2 +- .../babel-plugin-transform-es2015-modules-commonjs/src/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index ee7696ac41ea..dc272edb4e6e 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -431,7 +431,7 @@ helpers.specRequireInterop = template(` value: true } }); - if (typeof Symbol !== "undefined" && Symbol.toStringTag) { + if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(newObj, Symbol.toStringTag, { value: "Module" }) } return Object.freeze(newObj); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index ccad4ff942b6..42ccc9666525 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -45,7 +45,7 @@ let buildExportAll = template(` const specBuildNamespace = template(` $1 = $0 = Object.create(null, { __esModule: { value: true } }); - if (typeof Symbol !== "undefined" && Symbol.toStringTag) { + if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty($0, Symbol.toStringTag, { value: "Module" }); } `); From cf5e102fd5c120b4d31d4a71b0b261ac2f5fb016 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 02:18:37 +0900 Subject: [PATCH 015/103] Avoid allocation on non-spec --- .../src/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 42ccc9666525..d9051b8c1f01 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -665,7 +665,9 @@ export default function () { path.pushContainer("body", [specFinishNamespaceExport(exportsObj)]); } - commonjsExportsMasked = {}; + if (spec) { + commonjsExportsMasked = {}; + } path.traverse(reassignmentVisitor, { remaps, From 538a5859bab966b8361607671e13ca840ca47d09 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 02:19:21 +0900 Subject: [PATCH 016/103] Update fixtures --- .../test/fixtures/spec/commonjs-shadow-1/expected.js | 2 +- .../test/fixtures/spec/commonjs-shadow-2/expected.js | 2 +- .../test/fixtures/spec/commonjs-shadow/expected.js | 2 +- .../test/fixtures/spec/export-default/expected.js | 2 +- .../test/fixtures/spec/exports/expected.js | 2 +- .../test/fixtures/spec/reexports/expected.js | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js index cda8436c1f19..c38f1b8a5117 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js @@ -6,7 +6,7 @@ module.exports = exports = Object.create(null, { } }); -if (typeof Symbol !== "undefined" && Symbol.toStringTag) { +if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js index 2fce7f7d11f0..15eea8814872 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js @@ -6,7 +6,7 @@ module.exports = exports = Object.create(null, { } }); -if (typeof Symbol !== "undefined" && Symbol.toStringTag) { +if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js index 4d4f2991cca6..bdc244ee83da 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js @@ -6,7 +6,7 @@ module.exports = exports = Object.create(null, { } }); -if (typeof Symbol !== "undefined" && Symbol.toStringTag) { +if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js index 2e9f228fb494..39985281d780 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -6,7 +6,7 @@ module.exports = exports = Object.create(null, { } }); -if (typeof Symbol !== "undefined" && Symbol.toStringTag) { +if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js index 2a757b715dc2..1acc32f9801e 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -6,7 +6,7 @@ module.exports = exports = Object.create(null, { } }); -if (typeof Symbol !== "undefined" && Symbol.toStringTag) { +if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index 375a1da63039..05394ca5d497 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -6,7 +6,7 @@ module.exports = exports = Object.create(null, { } }); -if (typeof Symbol !== "undefined" && Symbol.toStringTag) { +if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); From ba377a991b6256d5ddd1d2e4d76aa688194e4fc6 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 16:37:26 +0900 Subject: [PATCH 017/103] Try to be nicer to es5-sham --- packages/babel-helpers/src/helpers.js | 9 ++++++--- .../src/index.js | 5 +++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index dc272edb4e6e..ab33858da99b 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -421,7 +421,7 @@ helpers.specRequireInterop = template(` if (obj && obj.__esModule) { return obj; } else { - var newObj = Object.create(null, { + var newObj = Object.create ? Object.create(null, { default: { value: obj, writable: true, @@ -430,11 +430,14 @@ helpers.specRequireInterop = template(` __esModule: { value: true } - }); + }) : { + default: obj, + __esModule: true + }; if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(newObj, Symbol.toStringTag, { value: "Module" }) } - return Object.freeze(newObj); + return (Object.freeze || Object)(newObj); } }) `); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index d9051b8c1f01..2fcec7e83373 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -44,7 +44,7 @@ let buildExportAll = template(` `); const specBuildNamespace = template(` - $1 = $0 = Object.create(null, { __esModule: { value: true } }); + $1 = $0 = Object.create ? Object.create(null, { __esModule: { value: true } }) : { __esModule: true }; if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty($0, Symbol.toStringTag, { value: "Module" }); } @@ -61,6 +61,7 @@ const specBuildExportDefault = template(` // Unfortunately, regular objects can't synthesize a value descriptor every time they're read, // so a getter needs to be used for live bindings. +// It's also not allowed to specify writable when using getters/setters. const specBuildExport = template(` Object.defineProperty(EXPORTS, NAME, { enumerable: true, get() { return VALUE; } }); `); @@ -92,7 +93,7 @@ const specBuildTempExport = template(` `); const specFinishNamespaceExport = template(` - Object.freeze($0); + !(Object.freeze || Object)($0); `); const THIS_BREAK_KEYS = ["FunctionExpression", "FunctionDeclaration", "ClassProperty", "ClassMethod", "ObjectMethod"]; From adea1878d462cccd871f4c31eb8c324bfd6d5396 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 16:59:02 +0900 Subject: [PATCH 018/103] Remove unneeded unary negation --- .../babel-plugin-transform-es2015-modules-commonjs/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 2fcec7e83373..545c740310de 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -93,7 +93,7 @@ const specBuildTempExport = template(` `); const specFinishNamespaceExport = template(` - !(Object.freeze || Object)($0); + (Object.freeze || Object)($0); `); const THIS_BREAK_KEYS = ["FunctionExpression", "FunctionDeclaration", "ClassProperty", "ClassMethod", "ObjectMethod"]; From 62ade301ebc69378a6fe0d944b7b333275d68c78 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 16:59:36 +0900 Subject: [PATCH 019/103] Try to convert access to "global" exports / module into TDZ violations --- .../src/index.js | 67 +++++++++++++------ 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 545c740310de..3a6e2947e343 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -103,10 +103,12 @@ export default function () { const exportsObj = t.identifier("exports"); exportsObj[REASSIGN_REMAP_SKIP] = true; - const moduleExports = t.memberExpression(t.identifier("module"), exportsObj); - moduleExports[REASSIGN_REMAP_SKIP] = true; + const moduleObj = t.identifier("module"); + moduleObj[REASSIGN_REMAP_SKIP] = true; + const module = t.memberExpression(moduleObj, exportsObj); + module[REASSIGN_REMAP_SKIP] = true; - const isModuleExports = t.buildMatchMemberExpression("module.exports", true); + const isModuleObj = t.buildMatchMemberExpression("module", true); const isExports = t.buildMatchMemberExpression("exports", true); let commonjsExportsMasked = null; @@ -123,7 +125,7 @@ export default function () { if (spec) { if (name === "exports" && !path.node[REASSIGN_REMAP_SKIP] && // Apparently replacing "module.exports" still visits the ".exports" here - !(this.remaps[".module.exports"] && t.isIdentifier(path.parent, this.remaps[".module.exports"])) && + !(this.remaps[".module"] && t.isIdentifier(path.parent, this.remaps[".module"])) && // Avoid entering when it's just some other object key named export !(isExports(path.parent) && t.isMemberExpression(path.parent.parent))) { remap = this.remaps[".exports"] = this.remaps[".exports"] || path.scope.generateUidIdentifier("exports"); @@ -131,10 +133,11 @@ export default function () { commonjsExportsMasked.exports = remap; return; } - if (isModuleExports(path.parent) && !path.parent[REASSIGN_REMAP_SKIP] && !t.isMemberExpression(path.parent.parent)) { - remap = this.remaps[".module.exports"] = this.remaps[".module.exports"] || path.scope.generateUidIdentifier("module.exports"); - path.parentPath.replaceWith(remap); - commonjsExportsMasked.moduleExports = remap; + if (name === "module" && !path.node[REASSIGN_REMAP_SKIP] && + !(isModuleObj(path.parent) && !path.parent[REASSIGN_REMAP_SKIP] && !t.isMemberExpression(path.parent.parent))) { + remap = this.remaps[".module"] = this.remaps[".module"] || path.scope.generateUidIdentifier("module"); + path.replaceWith(remap); + commonjsExportsMasked.module = remap; return; } @@ -154,11 +157,34 @@ export default function () { AssignmentExpression(path, state) { if (isSpec(state)) { - if (!path.node[REASSIGN_REMAP_SKIP] && t.isIdentifier(path.node.left, { name: "exports" }) && - !path.node.left[REASSIGN_REMAP_SKIP] && this.scope.getBinding(name) === path.scope.getBinding(name)) { - let remap = this.remaps[".exports"] = this.remaps[".exports"] || path.scope.generateUidIdentifier("exports"); - path.get("left").replaceWith(remap); - commonjsExportsMasked.exports = remap; + if (!path.node[REASSIGN_REMAP_SKIP]) { + const target = t.isIdentifier(path.node.left) + ? path.node.left + : t.isMemberExpression(path.node.left) && t.isIdentifier(path.node.left.object) + ? path.node.left.object : null; + const name = target && target.name; + + if (target[REASSIGN_REMAP_SKIP] || this.scope.getBinding(name) !== path.scope.getBinding(name)) { + return; + } + + let remap = null; + if (name === "exports") { + remap = this.remaps[".exports"] = this.remaps[".exports"] || path.scope.generateUidIdentifier("exports"); + commonjsExportsMasked.exports = remap; + } + if (name === "module") { + remap = this.remaps[".module"] = this.remaps[".module"] || path.scope.generateUidIdentifier("module"); + commonjsExportsMasked.module = remap; + } + + if (remap) { + if (t.isIdentifier(path.node.left)) { + path.get("left").replaceWith(remap); + } else { + path.get("left.object").replaceWith(remap); + } + } } return; } @@ -659,7 +685,7 @@ export default function () { path.unshiftContainer("body", topNodes); if (hasExports && spec) { - const decls = specBuildNamespace(exportsObj, moduleExports); + const decls = specBuildNamespace(exportsObj, module); decls.forEach((decl) => { decl._blockHoist = 3; }); path.unshiftContainer("body", decls); @@ -678,14 +704,11 @@ export default function () { requeueInParent: (newPath) => path.requeue(newPath), }); - if (spec && (commonjsExportsMasked.exports || commonjsExportsMasked.moduleExports)) { - path.unshiftContainer("body", [ - t.variableDeclaration("var", [ - commonjsExportsMasked.exports && t.variableDeclarator(commonjsExportsMasked.exports, t.objectExpression([])), - commonjsExportsMasked.moduleExports && commonjsExportsMasked.exports && - t.variableDeclarator(commonjsExportsMasked.moduleExports, commonjsExportsMasked.exports), - commonjsExportsMasked.moduleExports && !commonjsExportsMasked.exports && - t.variableDeclarator(commonjsExportsMasked.moduleExports, t.objectExpression([])) + if (spec && (commonjsExportsMasked.exports || commonjsExportsMasked.module)) { + path.pushContainer("body", [ + t.variableDeclaration("let", [ + commonjsExportsMasked.exports && t.variableDeclarator(commonjsExportsMasked.exports), + commonjsExportsMasked.module && t.variableDeclarator(commonjsExportsMasked.module) ].filter(Boolean)) ]); } From 418ee6b0b0fdf8ffc164ba47c927bcde11520941 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 17:00:10 +0900 Subject: [PATCH 020/103] Update fixtures --- .../fixtures/spec/commonjs-shadow-1/expected.js | 11 +++++++---- .../fixtures/spec/commonjs-shadow-2/expected.js | 13 +++++++------ .../fixtures/spec/commonjs-shadow/expected.js | 15 ++++++++------- .../test/fixtures/spec/export-default/expected.js | 8 +++++--- .../test/fixtures/spec/exports/expected.js | 8 +++++--- .../test/fixtures/spec/reexports/expected.js | 8 +++++--- 6 files changed, 37 insertions(+), 26 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js index c38f1b8a5117..5094128117c9 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js @@ -1,10 +1,12 @@ "use strict"; -module.exports = exports = Object.create(null, { +module.exports = exports = Object.create ? Object.create(null, { __esModule: { value: true } -}); +}) : { + __esModule: true +}; if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { @@ -12,7 +14,6 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { }); } -var _exports = {}; Object.defineProperty(exports, "default", { enumerable: true, writable: true, @@ -23,4 +24,6 @@ Object.defineProperty(exports, "default", { _exports = function () {}; -Object.freeze(exports); \ No newline at end of file +(Object.freeze || Object)(exports); + +let _exports; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js index 15eea8814872..24da5d4fa9eb 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js @@ -1,10 +1,12 @@ "use strict"; -module.exports = exports = Object.create(null, { +module.exports = exports = Object.create ? Object.create(null, { __esModule: { value: true } -}); +}) : { + __esModule: true +}; if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { @@ -12,8 +14,7 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { }); } -var _moduleExports = {}; +_module.exports = class {}; +(Object.freeze || Object)(exports); - -_moduleExports = class {}; -Object.freeze(exports); \ No newline at end of file +let _module; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js index bdc244ee83da..b982216b3c58 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js @@ -1,10 +1,12 @@ 'use strict'; -module.exports = exports = Object.create(null, { +module.exports = exports = Object.create ? Object.create(null, { __esModule: { value: true } -}); +}) : { + __esModule: true +}; if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { @@ -12,9 +14,6 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { }); } -var _exports = {}, - _moduleExports = _exports; - var _bar = babelHelpers.specRequireInterop(require('bar')); Object.defineProperty(exports, "default", { @@ -28,5 +27,7 @@ Object.defineProperty(exports, "default", { // neither of these should be able to use or affect the real exports new _exports.default(); -_moduleExports = {}; -Object.freeze(exports); \ No newline at end of file +_module.exports = {}; +(Object.freeze || Object)(exports); + +let _exports, _module; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js index 39985281d780..f720708f0bc5 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -1,10 +1,12 @@ "use strict"; -module.exports = exports = Object.create(null, { +module.exports = exports = Object.create ? Object.create(null, { __esModule: { value: true } -}); +}) : { + __esModule: true +}; if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { @@ -29,4 +31,4 @@ Object.defineProperty(exports, "unrelated", { }); unrelated = "changed"; -Object.freeze(exports); \ No newline at end of file +(Object.freeze || Object)(exports); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js index 1acc32f9801e..ede8a080c456 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -1,10 +1,12 @@ "use strict"; -module.exports = exports = Object.create(null, { +module.exports = exports = Object.create ? Object.create(null, { __esModule: { value: true } -}); +}) : { + __esModule: true +}; if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { @@ -66,4 +68,4 @@ Object.defineProperty(exports, "baz", { } }); -Object.freeze(exports); \ No newline at end of file +(Object.freeze || Object)(exports); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index 05394ca5d497..77cad80b471b 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -1,10 +1,12 @@ 'use strict'; -module.exports = exports = Object.create(null, { +module.exports = exports = Object.create ? Object.create(null, { __esModule: { value: true } -}); +}) : { + __esModule: true +}; if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(exports, Symbol.toStringTag, { @@ -65,4 +67,4 @@ Object.defineProperty(exports, 'default', { } }); -Object.freeze(exports); \ No newline at end of file +(Object.freeze || Object)(exports); \ No newline at end of file From 4afaba85da28e9e3237eba05001856cdbed033e8 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Fri, 9 Dec 2016 17:18:57 +0900 Subject: [PATCH 021/103] Add moduleSpec option to preset-es2015 Mostly for testing interaction of the transform with other es2015 plugins. --- packages/babel-preset-es2015/src/index.js | 21 +++++++------ .../modules-commonjs-spec/actual.js | 2 ++ .../modules-commonjs-spec/expected.js | 31 +++++++++++++++++++ .../modules-commonjs-spec/options.json | 5 +++ 4 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/actual.js create mode 100644 packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js create mode 100644 packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/options.json diff --git a/packages/babel-preset-es2015/src/index.js b/packages/babel-preset-es2015/src/index.js index 57d96082d120..721ec43d291c 100644 --- a/packages/babel-preset-es2015/src/index.js +++ b/packages/babel-preset-es2015/src/index.js @@ -33,22 +33,23 @@ import transformRegenerator from "babel-plugin-transform-regenerator"; function preset(context, opts = {}) { const moduleTypes = ["commonjs", "amd", "umd", "systemjs"]; - let loose = false; - let modules = "commonjs"; - let spec = false; - - if (opts !== undefined) { - if (opts.loose !== undefined) loose = opts.loose; - if (opts.modules !== undefined) modules = opts.modules; - if (opts.spec !== undefined) spec = opts.spec; - } + const { + loose = false, + modules = "commonjs", + spec = false, + moduleSpec = false + } = opts; if (typeof loose !== "boolean") throw new Error("Preset es2015 'loose' option must be a boolean."); if (typeof spec !== "boolean") throw new Error("Preset es2015 'spec' option must be a boolean."); + if (typeof moduleSpec !== "boolean") throw new Error("Preset es2015 'moduleSpec' option must be a boolean."); if (modules !== false && moduleTypes.indexOf(modules) === -1) { throw new Error("Preset es2015 'modules' option must be 'false' to indicate no modules\n" + "or a module type which be be one of: 'commonjs' (default), 'amd', 'umd', 'systemjs'"); } + if (moduleSpec !== false && (modules !== false || modules !== "commonjs")) { + throw new Error("The 'moduleSpec' option is only supported with 'commonjs' modules"); + } // be DRY const optsLoose = { loose }; @@ -74,7 +75,7 @@ function preset(context, opts = {}) { [transformES2015Destructuring, optsLoose], transformES2015BlockScoping, transformES2015TypeofSymbol, - modules === "commonjs" && [transformES2015ModulesCommonJS, optsLoose], + modules === "commonjs" && [transformES2015ModulesCommonJS, { loose, spec: moduleSpec }], modules === "systemjs" && [transformES2015ModulesSystemJS, optsLoose], modules === "amd" && [transformES2015ModulesAMD, optsLoose], modules === "umd" && [transformES2015ModulesUMD, optsLoose], diff --git a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/actual.js b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/actual.js new file mode 100644 index 000000000000..c3c69a6e6961 --- /dev/null +++ b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/actual.js @@ -0,0 +1,2 @@ +export default function () {} +export function b () {} diff --git a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js new file mode 100644 index 000000000000..7f834879d42a --- /dev/null +++ b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js @@ -0,0 +1,31 @@ +"use strict"; + +module.exports = exports = Object.create ? Object.create(null, { + __esModule: { + value: true + } +}) : { + __esModule: true +}; + +if (typeof Symbol === "function" && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { + value: "Module" + }); +} + +Object.defineProperty(exports, "default", { + enumerable: true, + writable: true, + value: { + default: function _default() {} + }.default +}); +Object.defineProperty(exports, "b", { + enumerable: true, + get: function get() { + return b; + } +}); +function b() {} +(Object.freeze || Object)(exports); \ No newline at end of file diff --git a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/options.json b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/options.json new file mode 100644 index 000000000000..68b24a409796 --- /dev/null +++ b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/options.json @@ -0,0 +1,5 @@ +{ + "presets": [ + ["es2015", { "modules": "commonjs", "moduleSpec": true }] + ] +} From ad9fc26553c343a38cc7660a8c7e791ad581c914 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 00:48:53 +0900 Subject: [PATCH 022/103] Correct logic error --- packages/babel-preset-es2015/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-preset-es2015/src/index.js b/packages/babel-preset-es2015/src/index.js index 721ec43d291c..e7892f970a8d 100644 --- a/packages/babel-preset-es2015/src/index.js +++ b/packages/babel-preset-es2015/src/index.js @@ -47,7 +47,7 @@ function preset(context, opts = {}) { throw new Error("Preset es2015 'modules' option must be 'false' to indicate no modules\n" + "or a module type which be be one of: 'commonjs' (default), 'amd', 'umd', 'systemjs'"); } - if (moduleSpec !== false && (modules !== false || modules !== "commonjs")) { + if (moduleSpec !== false && (modules !== false && modules !== "commonjs")) { throw new Error("The 'moduleSpec' option is only supported with 'commonjs' modules"); } From b37066feac0c0457166c6c98cb416cdbec8d1b74 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 02:22:09 +0900 Subject: [PATCH 023/103] Use getter on default export of named class or function If they're named, they can be modified, so the binding has to be kept live. --- .../src/index.js | 9 ++++-- .../spec/export-default-named/actual.js | 1 + .../spec/export-default-named/expected.js | 28 +++++++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 3a6e2947e343..b18865a963d5 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -385,7 +385,10 @@ export default function () { let defNode = t.identifier("default"); if (id) { addTo(exports, id.name, defNode); - topNodes.push(spec ? specBuildExportDefault({ EXPORTS: exportsObj, VALUE: id }) : buildExportsAssignment(defNode, id)); + topNodes.push(spec + ? specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral("default"), VALUE: id }) + : buildExportsAssignment(defNode, id) + ); path.replaceWith(declaration.node); } else if (spec) { const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; @@ -403,7 +406,9 @@ export default function () { addTo(exports, id.name, defNode); path.replaceWithMultiple([ declaration.node, - spec ? specBuildExportDefault({ EXPORTS: exportsObj, VALUE: id }) : buildExportsAssignment(defNode, id) + spec + ? specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral("default"), VALUE: id }) + : buildExportsAssignment(defNode, id) ]); } else if (spec) { const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/actual.js new file mode 100644 index 000000000000..9cecd735acaf --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/actual.js @@ -0,0 +1 @@ +export default function named () { named = function () {} } \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js new file mode 100644 index 000000000000..828c2c40e315 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js @@ -0,0 +1,28 @@ +"use strict"; + +module.exports = exports = Object.create ? Object.create(null, { + __esModule: { + value: true + } +}) : { + __esModule: true +}; + +if (typeof Symbol === "function" && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { + value: "Module" + }); +} + +Object.defineProperty(exports, "default", { + enumerable: true, + + get() { + return named; + } + +}); +function named() { + named = function () {}; +} +(Object.freeze || Object)(exports); \ No newline at end of file From cf7a7e4e422b83e54e67db4062fb812de13883d8 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 02:23:11 +0900 Subject: [PATCH 024/103] Add an easy to read import test case with all import forms --- .../test/fixtures/spec/import-simple/actual.js | 9 +++++++++ .../test/fixtures/spec/import-simple/expected.js | 15 +++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/actual.js new file mode 100644 index 000000000000..c9e476c2a46e --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/actual.js @@ -0,0 +1,9 @@ +import 'a'; +import {} from 'b'; +import * as ns from 'c'; +import d from 'd' +import {name} from 'e'; + +ns.name; +d; +name; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js new file mode 100644 index 000000000000..3aba3f2fdee8 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js @@ -0,0 +1,15 @@ +'use strict'; + +require('a'); + +require('b'); + +var _c = babelHelpers.specRequireInterop(require('c')); + +var _d = babelHelpers.specRequireInterop(require('d')); + +var _e = babelHelpers.specRequireInterop(require('e')); + +_c.name; +_d.default; +_e.name; \ No newline at end of file From 0efc4386866198c211f83e0bbcf73482117ea67a Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 03:36:24 +0900 Subject: [PATCH 025/103] Add a test that actually executes the module body and inspects the exprots --- .../test/spec-import.js | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import.js new file mode 100644 index 000000000000..2cf16b41ac81 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import.js @@ -0,0 +1,93 @@ +const assert = require("assert"); +const babel = require("../../babel-core"); +const vm = require("vm"); + +test("spec Interop import", function () { + const src = "import * as ns from 'fs'\nimport fs from 'fs'\nexport { fs, ns }\n"; + + const fakeMod = { "fakeFs": true }; + const context = { + module: { + exports: {} + }, + require: function (id) { + if (id === "fs") return fakeMod; + throw new Error("Unmocked module " + id + " required"); + } + }; + context.exports = context.module.exports; + + const code = babel.transform(src, { + "plugins": [ + [require("../"), {spec: true}], + ], + "ast": false, + }).code; + + vm.runInNewContext(code, context); + + const exports = context.module.exports; + + assert(Object.isFrozen(exports), "exports is frozen"); + assert.strictEqual(Object.getPrototypeOf(exports), null, "exports has null prototype"); + + if (typeof Symbol === "function" && Symbol.toStringTag) { + assert.strictEqual(exports[Symbol.toStringTag], "Module", "exports is tagged as Module"); + } + + assert.deepStrictEqual( + Object.getOwnPropertyDescriptor(exports, "__esModule"), + { value: true, configurable: false, writable: false, enumerable: false }, + "__esModule in top level Module" + ); + + const nsDesc = Object.getOwnPropertyDescriptor(exports, "ns"); + + assert(nsDesc.enumerable, "ns export is enumerable"); + // This should be the actual observable behavior, but freezing the export + // makes this change to false. + // // assert(nsDesc.writable, "ns export is writable"); + assert(!nsDesc.writable, "ns export happens to not be writable"); + assert(!nsDesc.configurable,"ns export is not configurable"); + + const fsDesc = Object.getOwnPropertyDescriptor(exports, "fs"); + + assert(fsDesc.enumerable, "fs export is enumerable"); + // fsDesc.writable cannot be true like the spec asks because... + // // assert.strictEqual(fsDesc.writable, true); + // ...fsDesc.get must be provided (and be a function) + assert.strictEqual(typeof fsDesc.get, "function", "fs export is a getter"); + assert(!fsDesc.configurable, "fs export is not configurable"); + + const ns = exports.ns; + + assert.deepStrictEqual( + Object.getOwnPropertyDescriptor(ns, "__esModule"), + { value: true, configurable: false, writable: false, enumerable: false }, + "__esModule in the imported ns Module" + ); + + assert(Object.isFrozen(ns), "ns export is frozen"); + assert.strictEqual(Object.getPrototypeOf(ns), null, "ns export has null prototype"); + + if (typeof Symbol === "function" && Symbol.toStringTag) { + assert.strictEqual(ns[Symbol.toStringTag], "Module", "ns export is tagged as Module"); + } + + const imported = Object.getOwnPropertyNames(ns).filter(function (name) { return name !== "__esModule"; }); + + assert.deepStrictEqual(imported, [ "default" ], "No exports other than 'default' in ns Module"); + + const defaultDesc = Object.getOwnPropertyDescriptor(ns, "default"); + + assert.strictEqual(defaultDesc.enumerable, true, "ns.default export is enumerable"); + // This is also not writable for the same reason nsDesc.writable was false + // // assert.strictEqual(defaultDesc.writable, true, "ns.default export is writable"); + assert.strictEqual(defaultDesc.writable, false, "ns export happens to not be writable"); + assert.strictEqual(defaultDesc.configurable, false, "ns.default export is configurable"); + assert.strictEqual("value" in defaultDesc, true, "ns.default export has a value"); + + assert(!("__esModule" in ns.default), "ns.default export is not a Module"); + assert.strictEqual(ns.default, fakeMod, "ns.default export is the commonjs namespace"); + assert.strictEqual(exports.fs, fakeMod, "fs reexport is the commonjs namespace"); +}); From ebfb27487e7df8ab9ec10c8e245f1682a47cfaed Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 03:37:43 +0900 Subject: [PATCH 026/103] Correct bugs found by the runtime test --- .../src/index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index b18865a963d5..abbb27b21f59 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -44,7 +44,7 @@ let buildExportAll = template(` `); const specBuildNamespace = template(` - $1 = $0 = Object.create ? Object.create(null, { __esModule: { value: true } }) : { __esModule: true }; + const $0 = $1 = Object.create ? Object.create(null, { __esModule: { value: true } }) : { __esModule: true }; if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty($0, Symbol.toStringTag, { value: "Module" }); } @@ -82,8 +82,11 @@ const specBuildNamespaceSpread = template(` }); `); +// It should _not_ be configurable, but this is needed as referring to +// the real export, even with a getter, may cause DMZ errors with circular +// references. const specBuildTempExportDescriptor = template(` - const $0 = { enumerable: true, writable: true, value: undefined }; + const $0 = { enumerable: true, writable: true, configurable: true, value: undefined }; `); const specBuildTempExportProperty = (id, descriptorId) => t.objectProperty(id, descriptorId); From c083987e89966e5c6a0613ee8846266eb4cc5d34 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 03:38:56 +0900 Subject: [PATCH 027/103] Update fixtures --- .../test/fixtures/spec/commonjs-shadow-1/expected.js | 2 +- .../test/fixtures/spec/commonjs-shadow-2/expected.js | 2 +- .../test/fixtures/spec/commonjs-shadow/expected.js | 2 +- .../test/fixtures/spec/export-default-named/expected.js | 2 +- .../test/fixtures/spec/export-default/expected.js | 2 +- .../test/fixtures/spec/exports/expected.js | 3 ++- .../test/fixtures/spec/reexports/expected.js | 3 ++- 7 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js index 5094128117c9..619d54bcfa6b 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js @@ -1,6 +1,6 @@ "use strict"; -module.exports = exports = Object.create ? Object.create(null, { +const exports = module.exports = Object.create ? Object.create(null, { __esModule: { value: true } diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js index 24da5d4fa9eb..d208a7611eab 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js @@ -1,6 +1,6 @@ "use strict"; -module.exports = exports = Object.create ? Object.create(null, { +const exports = module.exports = Object.create ? Object.create(null, { __esModule: { value: true } diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js index b982216b3c58..c3bad22fa1ff 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js @@ -1,6 +1,6 @@ 'use strict'; -module.exports = exports = Object.create ? Object.create(null, { +const exports = module.exports = Object.create ? Object.create(null, { __esModule: { value: true } diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js index 828c2c40e315..45dc5d88fb26 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js @@ -1,6 +1,6 @@ "use strict"; -module.exports = exports = Object.create ? Object.create(null, { +const exports = module.exports = Object.create ? Object.create(null, { __esModule: { value: true } diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js index f720708f0bc5..3deadbfc2cd6 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -1,6 +1,6 @@ "use strict"; -module.exports = exports = Object.create ? Object.create(null, { +const exports = module.exports = Object.create ? Object.create(null, { __esModule: { value: true } diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js index ede8a080c456..7813011a89a6 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -1,6 +1,6 @@ "use strict"; -module.exports = exports = Object.create ? Object.create(null, { +const exports = module.exports = Object.create ? Object.create(null, { __esModule: { value: true } @@ -17,6 +17,7 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { const _undefined = { enumerable: true, writable: true, + configurable: true, value: undefined }; Object.defineProperties(exports, { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index 77cad80b471b..633ea62e6220 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -1,6 +1,6 @@ 'use strict'; -module.exports = exports = Object.create ? Object.create(null, { +const exports = module.exports = Object.create ? Object.create(null, { __esModule: { value: true } @@ -17,6 +17,7 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { const _undefined = { enumerable: true, writable: true, + configurable: true, value: undefined }; Object.defineProperties(exports, { From 0c2bd667d60f83eb0a0993986d6cdac1406475db Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 16:04:38 +0900 Subject: [PATCH 028/103] Missing fixture update --- .../fixtures/preset-options/modules-commonjs-spec/expected.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js index 7f834879d42a..e2156380ac58 100644 --- a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js +++ b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js @@ -1,6 +1,6 @@ "use strict"; -module.exports = exports = Object.create ? Object.create(null, { +var exports = module.exports = Object.create ? Object.create(null, { __esModule: { value: true } From 6fd580d0862b8a61ac07bcc4850948e3e0620bed Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 16:33:26 +0900 Subject: [PATCH 029/103] Add (failing) reexport runtime tests --- .../test/spec-reexport.js | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js new file mode 100644 index 000000000000..db90935dc033 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js @@ -0,0 +1,63 @@ +"use strict"; + +const assert = require("assert"); +const babel = require("../../babel-core"); +const vm = require("vm"); + +describe("spec star reexport", function () { + const modules = { + a: "export const name = {key: 'value'}", + b: "export const name = {key: 'value'}", + aSame: "export const name = 'name'", + bSame: "export const name = 'name'" + }; + + const cached = {}; + + function makeContext () { + const context = { module: { exports: {} }, require: contextRequire }; + context.exports = context.module.exports; + return context; + } + + function transformAndRunInNewContext (code, context) { + if (typeof context === "undefined") { + context = makeContext(); + } + + code = babel.transform(code, { + "plugins": [ + [require("../"), {spec: true}], + ], + "ast": false, + }).code; + vm.runInNewContext(code, context); + + return context.module.exports; + } + + function contextRequire (id) { + if (id in modules) { + if (!cached[id]) { + cached[id] = makeContext(); + transformAndRunInNewContext(modules[id], cached[id]); + } + return cached[id].module.exports; + } + throw new Error("Unmocked module " + id + " required"); + } + + it("throws when not shadowed and duplicate is not SameValue", function () { + assert.throws(function () { + transformAndRunInNewContext("export * from 'a'\nexport * from 'b'"); + }); + }); + + it("does not throw when not shadowed but is SameValue", function () { + transformAndRunInNewContext("export * from 'aSame'\nexport * from 'bSame'"); + }); + + it("does not throw when shadowed even if it is not SameValue", function () { + transformAndRunInNewContext("export * from 'a'\nexport * from 'b'\nexport const name = {}"); + }); +}); From 2e0f7402784ac4b358a15cb06059f62d02690e3d Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 18:01:44 +0900 Subject: [PATCH 030/103] Hoist all exports in spec mode, avoid name clashes on star reexport --- .../src/index.js | 187 ++++++++++-------- 1 file changed, 108 insertions(+), 79 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index abbb27b21f59..6e789b51f120 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -66,13 +66,14 @@ const specBuildExport = template(` Object.defineProperty(EXPORTS, NAME, { enumerable: true, get() { return VALUE; } }); `); -const specBuildNamespaceReexport = template(` - Object.defineProperty(EXPORTS, NAME, { enumerable: true, writable: true, value: VALUE }); +const specBuildOwnExports = template(` + const $0 = Object.keys($1) `); const specBuildNamespaceSpread = template(` Object.keys(OBJECT).forEach(function (key) { - if (key === "default" || key === "__esModule") return; + if (key === "__esModule" || key === "default" || OWN_EXPORTS.indexOf(key) >= 0) return; + if (key in EXPORTS && (EXPORTS[key] === OBJECT[key] || typeof EXPORTS[key] === 'number' && isNaN(EXPORTS[key]))) return; Object.defineProperty(EXPORTS, key, { enumerable: true, get() { @@ -85,13 +86,14 @@ const specBuildNamespaceSpread = template(` // It should _not_ be configurable, but this is needed as referring to // the real export, even with a getter, may cause DMZ errors with circular // references. -const specBuildTempExportDescriptor = template(` - const $0 = { enumerable: true, writable: true, configurable: true, value: undefined }; +const specBuildHoistedExportDescriptor = template(` + ({ enumerable: true, get() { return $0; } }) `); -const specBuildTempExportProperty = (id, descriptorId) => t.objectProperty(id, descriptorId); +const specBuildHoistedExportProperty = (id, descriptorId) => + t.objectProperty(id, specBuildHoistedExportDescriptor(descriptorId).expression); -const specBuildTempExport = template(` +const specBuildHoistedExport = template(` Object.defineProperties($0, $1); `); @@ -297,7 +299,9 @@ export default function () { let topNodes = []; let remaps = Object.create(null); - const namespaceImports = new Set(); + + const hoistedExports = spec && new Map(); + let ownExportsUid = null; let requires = Object.create(null); @@ -364,13 +368,6 @@ export default function () { importsEntry.specifiers.push(...path.node.specifiers); - if (spec) { - path.node.specifiers - .filter((s) => t.isImportNamespaceSpecifier(s)) - .map((specifier) => specifier.local.name) - .forEach((name) => { namespaceImports.add(name); }); - } - if (typeof path.node._blockHoist === "number") { importsEntry.maxBlockHoist = Math.max( path.node._blockHoist, @@ -382,18 +379,24 @@ export default function () { path.remove(); } else if (path.isExportDefaultDeclaration()) { + if (spec && hoistedExports.has("default")) { + let todo; + } + let declaration = path.get("declaration"); if (declaration.isFunctionDeclaration()) { let id = declaration.node.id; let defNode = t.identifier("default"); if (id) { addTo(exports, id.name, defNode); - topNodes.push(spec - ? specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral("default"), VALUE: id }) - : buildExportsAssignment(defNode, id) - ); + if (spec) { + hoistedExports.set("default", id); + } else { + topNodes.push(spec, buildExportsAssignment(defNode, id)); + } path.replaceWith(declaration.node); } else if (spec) { + hoistedExports.set("default", null); const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; topNodes.push(specBuildExportDefault({ EXPORTS: exportsObj, VALUE: expr })); path.remove(); @@ -407,13 +410,18 @@ export default function () { let defNode = t.identifier("default"); if (id) { addTo(exports, id.name, defNode); - path.replaceWithMultiple([ - declaration.node, - spec - ? specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral("default"), VALUE: id }) - : buildExportsAssignment(defNode, id) - ]); + if (spec) { + hoistedExports.set("default", id); + path.replaceWith(declaration.node); + } else { + path.replaceWithMultiple([ + declaration.node, + buildExportsAssignment(defNode, id) + ]); + } } else if (spec) { + hoistedExports.set("default", null); + const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; path.replaceWith(specBuildExportDefault({ EXPORTS: exportsObj, VALUE: expr })); @@ -433,6 +441,7 @@ export default function () { } else { const defNode = t.identifier("default"); if (spec) { + hoistedExports.set("default", null); path.replaceWith(specBuildExportDefault({ EXPORTS: exportsObj, VALUE: declaration.node })); // Manually re-queue the expression so other transforms can get to it. @@ -454,35 +463,45 @@ export default function () { if (declaration.isFunctionDeclaration()) { let id = declaration.node.id; addTo(exports, id.name, id); - topNodes.push(spec - ? specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral(id.name), VALUE: id }) - : buildExportsAssignment(id, id)); + if (spec) { + if (hoistedExports.has(id.name)) { + let todo; + } + hoistedExports.set(id.name, id); + } else { + topNodes.push(buildExportsAssignment(id, id)); + } path.replaceWith(declaration.node); } else if (declaration.isClassDeclaration()) { let id = declaration.node.id; addTo(exports, id.name, id); - path.replaceWithMultiple([ - declaration.node, - spec - ? specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral(id.name), VALUE: id }) - : buildExportsAssignment(id, id) - ]); - nonHoistedExportNames[id.name] = true; + if (spec) { + if (hoistedExports.has(id.name)) { + let todo; + } + hoistedExports.set(id.name, id); + path.replaceWith(declaration.node); + } else { + path.replaceWithMultiple([ + declaration.node, + buildExportsAssignment(id, id) + ]); + nonHoistedExportNames[id.name] = true; + } } else if (declaration.isVariableDeclaration()) { let declarators = declaration.get("declarations"); const toExport = spec && new Set(); for (let decl of declarators) { let id = decl.get("id"); - let init = decl.get("init"); - if (!init.node) init.replaceWith(t.identifier("undefined")); - if (spec) { toExport.add(id.node); - nonHoistedExportNames[id.node.name] = true; continue; } + let init = decl.get("init"); + if (!init.node) init.replaceWith(t.identifier("undefined")); + if (id.isIdentifier()) { addTo(exports, id.node.name, id.node); if (spec) { @@ -495,16 +514,14 @@ export default function () { } } if (spec) { - path.replaceWithMultiple([ - declaration.node - ].concat( - Array.from(toExport) - .map((id) => specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral(id.name), VALUE: id})) - ) - ); - } else { - path.replaceWith(declaration.node); + Array.from(toExport).forEach((id) => { + if (hoistedExports.has(id.name)) { + let todo; + } + hoistedExports.set(id.name, id); + }); } + path.replaceWith(declaration.node); } continue; } @@ -522,7 +539,10 @@ export default function () { // todo } else if (specifier.isExportSpecifier()) { if (spec) { - topNodes.push(specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral(specifier.node.exported.name), VALUE: t.memberExpression(ref, specifier.node.local) })); + if (hoistedExports.has(specifier.node.exported.name)) { + let todo; + } + hoistedExports.set(specifier.node.exported.name, t.memberExpression(ref, specifier.node.local)); } else if (specifier.node.local.name === "default") { topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name), t.memberExpression(t.callExpression(this.addHelper("interopRequireDefault"), [ref]), specifier.node.local))); } else { @@ -538,12 +558,10 @@ export default function () { nonHoistedExportNames[specifier.node.exported.name] = true; if (spec) { - if (namespaceImports.has(specifier.node.local.name)) { - // it's a namespace, doesn't go through a getter, safe to reexport as a value - nodes.push(specBuildNamespaceReexport({ EXPORTS: exportsObj, NAME: t.stringLiteral(specifier.node.exported.name), VALUE: specifier.node.local })); - } else { - nodes.push(specBuildExport({ EXPORTS: exportsObj, NAME: t.stringLiteral(specifier.node.exported.name), VALUE: specifier.node.local })); + if (hoistedExports.has(specifier.node.exported.name)) { + let todo; } + hoistedExports.set(specifier.node.exported.name, specifier.node.local); } else { nodes.push(buildExportsAssignment(specifier.node.exported, specifier.node.local)); } @@ -552,14 +570,24 @@ export default function () { } path.replaceWithMultiple(nodes); } else if (path.isExportAllDeclaration()) { - let exportNode = spec - ? specBuildNamespaceSpread({ + let exportNode; + if (spec) { + if (ownExportsUid == null) { + ownExportsUid = path.scope.generateUidIdentifier("ownExports"); + const ownExportsNode = specBuildOwnExports(ownExportsUid, exportsObj); + topNodes.push(ownExportsNode); + } + + exportNode = specBuildNamespaceSpread({ EXPORTS: exportsObj, + OWN_EXPORTS: ownExportsUid, OBJECT: addRequire(path.node.source.value, spec, path.node._blockHoist) - }) - : buildExportAll({ + }); + } else { + exportNode = buildExportAll({ OBJECT: addRequire(path.node.source.value, spec, path.node._blockHoist) }); + } exportNode.loc = path.node.loc; topNodes.push(exportNode); path.remove(); @@ -649,34 +677,35 @@ export default function () { } } - if (hasImports && Object.keys(nonHoistedExportNames).length) { - let hoistedExportsNode = t.identifier("undefined"); + if (spec) { + const entries = Array.from(hoistedExports.entries()).map(([name, id]) => { + if (name === "default" && id === null) { + // explicit export is generated + return; + } + return specBuildHoistedExportProperty(t.identifier(name), id); + }).filter(Boolean); - if (spec) { - const descId = path.scope.generateUidIdentifier("undefined"); - const desc = specBuildTempExportDescriptor(descId); - desc._blockHoist = 3; - - const expr = t.objectExpression( - Object.keys(nonHoistedExportNames).map((name) => - specBuildTempExportProperty(t.identifier(name), descId) - ) - ); - const node = specBuildTempExport(exportsObj, expr); + if (entries.length > 0) { + const expr = t.objectExpression(entries); + const node = specBuildHoistedExport(exportsObj, expr); node._blockHoist = 3; topNodes.unshift(node); - topNodes.unshift(desc); - } else { - for (let name in nonHoistedExportNames) { - hoistedExportsNode = buildExportsAssignment(t.identifier(name), hoistedExportsNode).expression; - } + } + } - const node = t.expressionStatement(hoistedExportsNode); - node._blockHoist = 3; + if (!spec && hasImports && Object.keys(nonHoistedExportNames).length) { + let hoistedExportsNode = t.identifier("undefined"); - topNodes.unshift(node); + for (let name in nonHoistedExportNames) { + hoistedExportsNode = buildExportsAssignment(t.identifier(name), hoistedExportsNode).expression; } + + const node = t.expressionStatement(hoistedExportsNode); + node._blockHoist = 3; + + topNodes.unshift(node); } // add __esModule declaration if this file has any exports From c428c522dfab74c220fa7ba8359d5c8d2b0ee5b8 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 18:02:48 +0900 Subject: [PATCH 031/103] Update fixtures --- .../spec/export-default-named/expected.js | 12 ++-- .../fixtures/spec/export-default/expected.js | 20 +++--- .../test/fixtures/spec/exports/expected.js | 69 ++++++++----------- .../test/fixtures/spec/reexports/expected.js | 60 ++++++++-------- 4 files changed, 76 insertions(+), 85 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js index 45dc5d88fb26..9bd50067ab04 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js @@ -14,13 +14,15 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { }); } -Object.defineProperty(exports, "default", { - enumerable: true, +Object.defineProperties(exports, { + default: { + enumerable: true, - get() { - return named; - } + get() { + return named; + } + } }); function named() { named = function () {}; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js index 3deadbfc2cd6..3e2fb6e77433 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -14,6 +14,16 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { }); } +Object.defineProperties(exports, { + unrelated: { + enumerable: true, + + get() { + return unrelated; + } + + } +}); Object.defineProperty(exports, "default", { enumerable: true, writable: true, @@ -21,14 +31,6 @@ Object.defineProperty(exports, "default", { default: function () {} }.default }); -var unrelated = undefined; -Object.defineProperty(exports, "unrelated", { - enumerable: true, - - get() { - return unrelated; - } - -}); +var unrelated; unrelated = "changed"; (Object.freeze || Object)(exports); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js index 7813011a89a6..c85c8a9232cb 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -14,37 +14,44 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { }); } -const _undefined = { - enumerable: true, - writable: true, - configurable: true, - value: undefined -}; Object.defineProperties(exports, { - default: _undefined, - Bar: _undefined, - baz: _undefined -}); -Object.defineProperty(exports, "foo", { - enumerable: true, + default: { + enumerable: true, - get() { - return foo; - } + get() { + return def; + } -}); + }, + foo: { + enumerable: true, -var _outside = babelHelpers.specRequireInterop(require("outside")); + get() { + return foo; + } -var def; -Object.defineProperty(exports, "default", { - enumerable: true, + }, + Bar: { + enumerable: true, - get() { - return def; - } + get() { + return Bar; + } + + }, + baz: { + enumerable: true, + + get() { + return baz; + } + } }); + +var _outside = babelHelpers.specRequireInterop(require("outside")); + +var def; function foo() { def = "export mutation"; return "foo"; @@ -52,21 +59,5 @@ function foo() { class Bar {} -Object.defineProperty(exports, "Bar", { - enumerable: true, - - get() { - return Bar; - } - -}); const baz = foo(); -Object.defineProperty(exports, "baz", { - enumerable: true, - - get() { - return baz; - } - -}); (Object.freeze || Object)(exports); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index 633ea62e6220..8191934da8bf 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -14,22 +14,40 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { }); } -const _undefined = { - enumerable: true, - writable: true, - configurable: true, - value: undefined -}; Object.defineProperties(exports, { - namespace: _undefined, - default: _undefined, - why: _undefined + namespace: { + enumerable: true, + + get() { + return _somewhere; + } + + }, + default: { + enumerable: true, + + get() { + return _elsewhere.stuff; + } + + }, + why: { + enumerable: true, + + get() { + return _because.default; + } + + } }); +const _ownExports = Object.keys(exports); + var _iDontKnow = babelHelpers.specRequireInterop(require('./i-dont-know')); Object.keys(_iDontKnow).forEach(function (key) { - if (key === "default" || key === "__esModule") return; + if (key === "__esModule" || key === "default" || _ownExports.indexOf(key) >= 0) return; + if (key in exports && (exports[key] === _iDontKnow[key] || typeof exports[key] === 'number' && isNaN(exports[key]))) return; Object.defineProperty(exports, key, { enumerable: true, @@ -42,30 +60,8 @@ Object.keys(_iDontKnow).forEach(function (key) { var _because = babelHelpers.specRequireInterop(require('./because')); -Object.defineProperty(exports, 'why', { - enumerable: true, - - get() { - return _because.default; - } - -}); - var _somewhere = babelHelpers.specRequireInterop(require('./somewhere')); var _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); -Object.defineProperty(exports, 'namespace', { - enumerable: true, - writable: true, - value: _somewhere -}); -Object.defineProperty(exports, 'default', { - enumerable: true, - - get() { - return _elsewhere.stuff; - } - -}); (Object.freeze || Object)(exports); \ No newline at end of file From f3b8e118dd0c95986536d5ee900e6eba96dbf756 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 18:03:12 +0900 Subject: [PATCH 032/103] Refactor runtime tests --- .../test/spec-import.js | 31 ++------- .../test/spec-reexport.js | 48 ++----------- .../test/spec-test-helpers.js | 69 +++++++++++++++++++ 3 files changed, 82 insertions(+), 66 deletions(-) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import.js index 2cf16b41ac81..388f6ecf6093 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import.js @@ -1,32 +1,15 @@ const assert = require("assert"); -const babel = require("../../babel-core"); -const vm = require("vm"); +const helpers = require("./spec-test-helpers"); test("spec Interop import", function () { - const src = "import * as ns from 'fs'\nimport fs from 'fs'\nexport { fs, ns }\n"; + const runner = new helpers.Runner(); const fakeMod = { "fakeFs": true }; - const context = { - module: { - exports: {} - }, - require: function (id) { - if (id === "fs") return fakeMod; - throw new Error("Unmocked module " + id + " required"); - } - }; - context.exports = context.module.exports; - - const code = babel.transform(src, { - "plugins": [ - [require("../"), {spec: true}], - ], - "ast": false, - }).code; - - vm.runInNewContext(code, context); - - const exports = context.module.exports; + runner.addToCache("fs", { module: { exports: fakeMod } }); + + const exports = runner.transformAndRun( + "import * as ns from 'fs'\nimport fs from 'fs'\nexport { fs, ns }\n" + ); assert(Object.isFrozen(exports), "exports is frozen"); assert.strictEqual(Object.getPrototypeOf(exports), null, "exports has null prototype"); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js index db90935dc033..67b46af3b9a3 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js @@ -1,63 +1,27 @@ "use strict"; const assert = require("assert"); -const babel = require("../../babel-core"); -const vm = require("vm"); +const helpers = require("./spec-test-helpers"); describe("spec star reexport", function () { - const modules = { + const runner = new helpers.Runner({ a: "export const name = {key: 'value'}", b: "export const name = {key: 'value'}", aSame: "export const name = 'name'", bSame: "export const name = 'name'" - }; - - const cached = {}; - - function makeContext () { - const context = { module: { exports: {} }, require: contextRequire }; - context.exports = context.module.exports; - return context; - } - - function transformAndRunInNewContext (code, context) { - if (typeof context === "undefined") { - context = makeContext(); - } - - code = babel.transform(code, { - "plugins": [ - [require("../"), {spec: true}], - ], - "ast": false, - }).code; - vm.runInNewContext(code, context); - - return context.module.exports; - } - - function contextRequire (id) { - if (id in modules) { - if (!cached[id]) { - cached[id] = makeContext(); - transformAndRunInNewContext(modules[id], cached[id]); - } - return cached[id].module.exports; - } - throw new Error("Unmocked module " + id + " required"); - } + }); it("throws when not shadowed and duplicate is not SameValue", function () { assert.throws(function () { - transformAndRunInNewContext("export * from 'a'\nexport * from 'b'"); + runner.transformAndRun("export * from 'a'\nexport * from 'b'"); }); }); it("does not throw when not shadowed but is SameValue", function () { - transformAndRunInNewContext("export * from 'aSame'\nexport * from 'bSame'"); + runner.transformAndRun("export * from 'aSame'\nexport * from 'bSame'"); }); it("does not throw when shadowed even if it is not SameValue", function () { - transformAndRunInNewContext("export * from 'a'\nexport * from 'b'\nexport const name = {}"); + runner.transformAndRun("export * from 'a'\nexport * from 'b'\nexport const name = {}"); }); }); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js new file mode 100644 index 000000000000..ef2f6cdd2b0b --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js @@ -0,0 +1,69 @@ +const babel = require("../../babel-core"); +const vm = require("vm"); + +module.exports.Runner = function Runner (initialModules) { + if (!(this instanceof Runner)) { + throw new Error("Runner can only be instantiated"); + } + + this.modules = {}; + this.cache = {}; + + if (initialModules != null) { + this.addModules(initialModules); + } +}; + +module.exports.Runner.prototype = { + addModule: function (name, code) { + this.modules[name] = code; + }, + + addModules: function (dict) { + for (const key in dict) { + if (!Object.prototype.hasOwnProperty.call(dict, key)) continue; + this.addModule(key, dict[key]); + } + }, + + addToCache: function (name, context) { + if (! (context && context.module && "exports" in context.module)) { + throw new Error("The context to cache must have a module.exports"); + } + this.cache[name] = context; + }, + + transformAndRun: function (code) { + return this.transformAndRunInNewContext(code, this.makeContext()); + }, + + transformAndRunInNewContext: function (code, context) { + code = babel.transform(code, { + "plugins": [ + [require("../"), {spec: true}], + ], + "ast": false, + }).code; + vm.runInNewContext(code, context); + + return context.module.exports; + }, + + makeContext: function () { + const context = { module: { exports: {} }, require: this.contextRequire.bind(this) }; + context.exports = context.module.exports; + return context; + }, + + contextRequire: function (id) { + if (id in this.cache) { + return this.cache[id].module.exports; + } + if (id in this.modules) { + this.cache[id] = this.makeContext(); + this.transformAndRunInNewContext(this.modules[id], this.cache[id]); + return this.cache[id].module.exports; + } + throw new Error("Unmocked module " + id + " required"); + } +}; From f18e5dc48c1429742b5e786fd2deb6ddacce307a Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 18:04:42 +0900 Subject: [PATCH 033/103] Remove unused template, update comments --- .../src/index.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 6e789b51f120..26c32a279c2f 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -59,13 +59,6 @@ const specBuildExportDefault = template(` Object.defineProperty(EXPORTS, "default", { enumerable: true, writable: true, value: VALUE }); `); -// Unfortunately, regular objects can't synthesize a value descriptor every time they're read, -// so a getter needs to be used for live bindings. -// It's also not allowed to specify writable when using getters/setters. -const specBuildExport = template(` - Object.defineProperty(EXPORTS, NAME, { enumerable: true, get() { return VALUE; } }); -`); - const specBuildOwnExports = template(` const $0 = Object.keys($1) `); @@ -83,9 +76,12 @@ const specBuildNamespaceSpread = template(` }); `); -// It should _not_ be configurable, but this is needed as referring to -// the real export, even with a getter, may cause DMZ errors with circular -// references. +// Unfortunately, regular objects can't synthesize a value descriptor every time they're read, +// so a getter needs to be used for live bindings. +// It's also not allowed to specify writable when using getters/setters. +// +// Accessing a non-hoisted export should cause a DMZ error instead of just +// returning undefined, so a getter is also needed for that. const specBuildHoistedExportDescriptor = template(` ({ enumerable: true, get() { return $0; } }) `); From 56164e7cde6629bb407b3960ea11d2fbdde721dc Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 18:41:17 +0900 Subject: [PATCH 034/103] Remove the hoistedExports.has checks; babylon already checks for this --- .../src/index.js | 27 +------------------ 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 26c32a279c2f..7747b6f302db 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -375,10 +375,6 @@ export default function () { path.remove(); } else if (path.isExportDefaultDeclaration()) { - if (spec && hoistedExports.has("default")) { - let todo; - } - let declaration = path.get("declaration"); if (declaration.isFunctionDeclaration()) { let id = declaration.node.id; @@ -460,9 +456,6 @@ export default function () { let id = declaration.node.id; addTo(exports, id.name, id); if (spec) { - if (hoistedExports.has(id.name)) { - let todo; - } hoistedExports.set(id.name, id); } else { topNodes.push(buildExportsAssignment(id, id)); @@ -472,9 +465,6 @@ export default function () { let id = declaration.node.id; addTo(exports, id.name, id); if (spec) { - if (hoistedExports.has(id.name)) { - let todo; - } hoistedExports.set(id.name, id); path.replaceWith(declaration.node); } else { @@ -486,12 +476,11 @@ export default function () { } } else if (declaration.isVariableDeclaration()) { let declarators = declaration.get("declarations"); - const toExport = spec && new Set(); for (let decl of declarators) { let id = decl.get("id"); if (spec) { - toExport.add(id.node); + hoistedExports.set(id.node.name, id.node); continue; } @@ -509,14 +498,6 @@ export default function () { // todo } } - if (spec) { - Array.from(toExport).forEach((id) => { - if (hoistedExports.has(id.name)) { - let todo; - } - hoistedExports.set(id.name, id); - }); - } path.replaceWith(declaration.node); } continue; @@ -535,9 +516,6 @@ export default function () { // todo } else if (specifier.isExportSpecifier()) { if (spec) { - if (hoistedExports.has(specifier.node.exported.name)) { - let todo; - } hoistedExports.set(specifier.node.exported.name, t.memberExpression(ref, specifier.node.local)); } else if (specifier.node.local.name === "default") { topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name), t.memberExpression(t.callExpression(this.addHelper("interopRequireDefault"), [ref]), specifier.node.local))); @@ -554,9 +532,6 @@ export default function () { nonHoistedExportNames[specifier.node.exported.name] = true; if (spec) { - if (hoistedExports.has(specifier.node.exported.name)) { - let todo; - } hoistedExports.set(specifier.node.exported.name, specifier.node.local); } else { nodes.push(buildExportsAssignment(specifier.node.exported, specifier.node.local)); From 6340b24bb65e563c25885d1a74808f0d94911a43 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 18:41:50 +0900 Subject: [PATCH 035/103] Add more specific runtime tests for the export shape --- .../test/spec-export.js | 140 ++++++++++++++++++ ...{spec-import.js => spec-interop-import.js} | 0 2 files changed, 140 insertions(+) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js rename packages/babel-plugin-transform-es2015-modules-commonjs/test/{spec-import.js => spec-interop-import.js} (100%) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js new file mode 100644 index 000000000000..eca863b31330 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js @@ -0,0 +1,140 @@ +"use strict"; + +const assert = require("assert"); +const helpers = require("./spec-test-helpers"); + +describe("spec export", function () { + const runner = new helpers.Runner(); + + describe("basic shape", function () { + const exports = runner.transformAndRun("export {}"); + + it("is frozen", function () { + assert(Object.isFrozen(exports)); + }); + + it("has a null prototype", function () { + assert.strictEqual(Object.getPrototypeOf(exports), null); + }); + + it("is tagged as Module", function () { + if (typeof Symbol === "function" && Symbol.toStringTag) { + assert.strictEqual(exports[Symbol.toStringTag], "Module"); + } else { + this.skip(); + } + }); + + it("has the __esModule flag", function () { + assert.deepStrictEqual( + Object.getOwnPropertyDescriptor(exports, "__esModule"), + { value: true, configurable: false, writable: false, enumerable: false } + ); + }); + + it("has no exports", function () { + assert.deepStrictEqual(Object.keys(exports), []); + }); + }); + + describe("default export", function () { + describe("of single value", function () { + const exports = runner.transformAndRun("const foo = 'foo';\nexport default foo"); + + it("has no exports other than 'default'", function () { + assert.deepStrictEqual(Object.keys(exports), ["default"]); + }); + + it("has the correct value", function () { + assert.strictEqual(exports.default, "foo"); + }); + }); + + describe("of anonymous function", function () { + const exports = runner.transformAndRun("export default function () {}"); + + it("has Function.name of 'default'", function () { + assert.strictEqual(exports.default.name, "default"); + }); + }); + + describe("of anonymous class", function () { + const exports = runner.transformAndRun("export default class {}"); + + it("has Function.name of 'default'", function () { + assert.strictEqual(exports.default.name, "default"); + }); + }); + }); + + describe("named export", function () { + describe("of single value declaration", function () { + const exports = runner.transformAndRun("export const a = 'a'"); + + it("has no exports other than 'a'", function () { + assert.deepStrictEqual(Object.keys(exports), ["a"]); + }); + + it("has the correct value", function () { + assert.strictEqual(exports.a, "a"); + }); + }); + + describe("of multiple value declaration", function () { + const exports = runner.transformAndRun("export const a = 'a', b = 'b'"); + + it("has no exports other than 'a' and 'b'", function () { + const keys = Object.keys(exports); + assert.strictEqual(keys.length, 2); + assert(keys.indexOf("a") >= 0); + assert(keys.indexOf("b") >= 0); + }); + + it("has the correct values", function () { + assert.strictEqual(exports.a, "a"); + assert.strictEqual(exports.b, "b"); + }); + }); + }); + + describe("early errors", function () { + // it turns out that babylon already throws for us, but test just to make sure + it("throws when generating duplicate default exports", function () { + assert.throws(function () { + runner.transformAndRun("export default class {}\nexport default class {}"); + }); + }); + + it("throws when generating duplicate default export via renaming", function () { + assert.throws(function () { + runner.transformAndRun("var foo\nexport default foo\nexport { foo as default }"); + }); + }); + + it("throws when generating duplicate exports in the same specifier", function () { + assert.throws(function () { + runner.transformAndRun("export var foo, foo"); + }); + }); + + it("throws when generating duplicate named exports in the same specifier", function () { + assert.throws(function () { + runner.transformAndRun("const foo = 'foo'\nexport { foo, foo }"); + }); + }); + + it("throws when generating duplicate renamed exports", function () { + assert.throws(function () { + runner.transformAndRun("const foo = 'foo'\nconst bar = 'bar'\nexport { foo, bar as foo }"); + }); + }); + + // babel extension; check for the flag used to distinguish + // babel ES modules from regular commonjs modules + it("throws when attempting to export __esModule", function () { + assert.throws(function () { + runner.transformAndRun("const __esModule = false\nexport { __esModule }"); + }); + }); + }); +}); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js similarity index 100% rename from packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import.js rename to packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js From f6c014a052bb4ef106e2ceadb0420bd12918a8fb Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 19:00:50 +0900 Subject: [PATCH 036/103] Improve runtime tests --- .../test/spec-export.js | 77 +++++++++++++++++++ .../test/spec-reexport.js | 18 +++-- 2 files changed, 90 insertions(+), 5 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js index eca863b31330..37c8f2864f88 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js @@ -95,6 +95,83 @@ describe("spec export", function () { assert.strictEqual(exports.b, "b"); }); }); + + describe("of function", function () { + const exports = runner.transformAndRun("export function foo () { return 'bar'; }"); + + it("has no exports other than 'foo'", function () { + assert.deepStrictEqual(Object.keys(exports), ["foo"]); + }); + + it("hass the correct Function.name", function () { + assert.strictEqual(exports.foo.name, "foo"); + }); + + it("has the correct value", function () { + assert.strictEqual(typeof exports.foo, "function"); + assert.strictEqual((0, exports).foo(), "bar"); + }); + }); + + describe("of class", function () { + const exports = runner.transformAndRun("export class Foo {}"); + + it("has no exports other than 'Foo'", function () { + assert.deepStrictEqual(Object.keys(exports), ["Foo"]); + }); + + it("has the correct Function.name", function () { + assert.strictEqual(exports.Foo.name, "Foo"); + }); + }); + + describe("of identifier", function () { + const exports = runner.transformAndRun("var foo\nexport { foo }"); + + it("has no exports other than 'foo'", function () { + assert.deepStrictEqual(Object.keys(exports), ["foo"]); + }); + }); + + describe("of renamed identifier", function () { + const exports = runner.transformAndRun("function foo () {}\nexport { foo as bar }"); + + it("has no exports other than 'bar'", function () { + assert.deepStrictEqual(Object.keys(exports), ["bar"]); + }); + + it("has the correct Function.name", function () { + assert.strictEqual(exports.bar.name, "foo"); + }); + }); + }); + + describe("live binding", function () { + describe("of default export", function () { + const exports = runner.transformAndRun("export default function foo () { foo = ':scream:' }"); + + it("has the correct initial value", function () { + assert.strictEqual(typeof exports.default, "function"); + }); + + it("correctly updates when executed", function () { + (0, exports).default(); + assert.strictEqual(exports.default, ":scream:"); + }); + }); + + describe("of named export", function () { + const exports = runner.transformAndRun("export let count = 0\nexport default function up () { count += 1 }"); + + it("has the correct initial value", function () { + assert.strictEqual(exports.count, 0); + }); + + it("correctly updates", function () { + (0, exports).default(); + assert.strictEqual(exports.count, 1); + }); + }); }); describe("early errors", function () { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js index 67b46af3b9a3..3f4a9287a360 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js @@ -14,14 +14,22 @@ describe("spec star reexport", function () { it("throws when not shadowed and duplicate is not SameValue", function () { assert.throws(function () { runner.transformAndRun("export * from 'a'\nexport * from 'b'"); - }); + }, "Cannot redefine property: name"); }); - it("does not throw when not shadowed but is SameValue", function () { - runner.transformAndRun("export * from 'aSame'\nexport * from 'bSame'"); + describe("SameValue duplicate reexport", function () { + const exports = runner.transformAndRun("export * from 'aSame'\nexport * from 'bSame'"); + + it("has the correct value", function () { + assert.strictEqual(exports.name, "name"); + }); }); - it("does not throw when shadowed even if it is not SameValue", function () { - runner.transformAndRun("export * from 'a'\nexport * from 'b'\nexport const name = {}"); + describe("shadowed reexport", function () { + const exports = runner.transformAndRun("export * from 'a'\nexport * from 'b'\nexport const name = 'foo'"); + + it("has the correct value", function () { + assert.strictEqual(exports.name, "foo"); + }); }); }); From a861f64c78b54782345f7d263dd2804147ea5b4d Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 19:17:08 +0900 Subject: [PATCH 037/103] Add reexport tests --- .../test/spec-reexport.js | 127 +++++++++++++++++- .../test/spec-test-helpers.js | 7 + 2 files changed, 132 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js index 3f4a9287a360..028f91f79322 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js @@ -3,7 +3,130 @@ const assert = require("assert"); const helpers = require("./spec-test-helpers"); -describe("spec star reexport", function () { +describe("spec reexport", function () { + const runner = new helpers.Runner({ + a: "export const a = 'a'", + b: "export const b = 'b'", + ab: "export { a } from 'a'\nexport { b } from 'b'", + ns: "import * as ab from 'ab'\nexport { ab }", + star: "export * from 'ab'\nexport * from 'ns'" + }); + + describe("individual reexports", function () { + const exports = runner.getExportsOf("ab"); + + it("is frozen", function () { + assert(Object.isFrozen(exports)); + }); + + it("has a null prototype", function () { + assert.strictEqual(Object.getPrototypeOf(exports), null); + }); + + it("is tagged as Module", function () { + if (typeof Symbol === "function" && Symbol.toStringTag) { + assert.strictEqual(exports[Symbol.toStringTag], "Module"); + } else { + this.skip(); + } + }); + + it("has the __esModule flag", function () { + assert.deepStrictEqual( + Object.getOwnPropertyDescriptor(exports, "__esModule"), + { value: true, configurable: false, writable: false, enumerable: false } + ); + }); + + it("has no exports other than 'a' and 'b'", function () { + const keys = Object.keys(exports); + assert.strictEqual(keys.length, 2); + assert(keys.indexOf("a") >= 0); + assert(keys.indexOf("b") >= 0); + }); + + it("has the correct values", function () { + assert.strictEqual(exports.a, "a"); + assert.strictEqual(exports.b, "b"); + }); + }); + + describe("namespace reexport", function () { + const exports = runner.getExportsOf("ns"); + + it("the reexport is frozen", function () { + assert(Object.isFrozen(exports.ab)); + }); + + it("the reexport has a null prototype", function () { + assert.strictEqual(Object.getPrototypeOf(exports.ab), null); + }); + + it("the reexport is tagged as Module", function () { + if (typeof Symbol === "function" && Symbol.toStringTag) { + assert.strictEqual(exports.ab[Symbol.toStringTag], "Module"); + } else { + this.skip(); + } + }); + + it("the reexport has the __esModule flag", function () { + assert.deepStrictEqual( + Object.getOwnPropertyDescriptor(exports.ab, "__esModule"), + { value: true, configurable: false, writable: false, enumerable: false } + ); + }); + + it("the reexport has the correct values", function () { + assert.strictEqual(exports.ab.a, "a"); + assert.strictEqual(exports.ab.b, "b"); + }); + }); + + describe("star reexport", function () { + const exports = runner.getExportsOf("star"); + + + it("is frozen", function () { + assert(Object.isFrozen(exports)); + }); + + it("has a null prototype", function () { + assert.strictEqual(Object.getPrototypeOf(exports), null); + }); + + it("is tagged as Module", function () { + if (typeof Symbol === "function" && Symbol.toStringTag) { + assert.strictEqual(exports[Symbol.toStringTag], "Module"); + } else { + this.skip(); + } + }); + + it("has the __esModule flag", function () { + assert.deepStrictEqual( + Object.getOwnPropertyDescriptor(exports, "__esModule"), + { value: true, configurable: false, writable: false, enumerable: false } + ); + }); + + it("has no exports other than 'a', 'b' and 'ab'", function () { + const keys = Object.keys(exports); + assert.strictEqual(keys.length, 3); + assert(keys.indexOf("a") >= 0); + assert(keys.indexOf("b") >= 0); + assert(keys.indexOf("ab") >= 0); + }); + + it("has the correct values", function () { + assert.strictEqual(exports.a, "a"); + assert.strictEqual(exports.b, "b"); + assert.strictEqual(exports.ab, runner.getExportsOf("ab")); + }); + }); +}); + +describe("spec reexport star with duplicates", function () { const runner = new helpers.Runner({ a: "export const name = {key: 'value'}", b: "export const name = {key: 'value'}", @@ -17,7 +140,7 @@ describe("spec star reexport", function () { }, "Cannot redefine property: name"); }); - describe("SameValue duplicate reexport", function () { + describe("SameValue", function () { const exports = runner.transformAndRun("export * from 'aSame'\nexport * from 'bSame'"); it("has the correct value", function () { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js index ef2f6cdd2b0b..f46b1c0b40d1 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js @@ -33,6 +33,13 @@ module.exports.Runner.prototype = { this.cache[name] = context; }, + getExportsOf: function (name) { + if (! (name in this.cache || name in this.modules)) { + throw new Error("Unknown module " + name + " requested"); + } + return this.contextRequire(name); + }, + transformAndRun: function (code) { return this.transformAndRunInNewContext(code, this.makeContext()); }, From 9ec0147ccfbeccf925dfb8fb6975e61428acfe1f Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 19:18:16 +0900 Subject: [PATCH 038/103] Update fixtures --- .../modules-commonjs-spec/expected.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js index e2156380ac58..3fafd2eeabdf 100644 --- a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js +++ b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js @@ -14,6 +14,14 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { }); } +Object.defineProperties(exports, { + b: { + enumerable: true, + get: function get() { + return b; + } + } +}); Object.defineProperty(exports, "default", { enumerable: true, writable: true, @@ -21,11 +29,5 @@ Object.defineProperty(exports, "default", { default: function _default() {} }.default }); -Object.defineProperty(exports, "b", { - enumerable: true, - get: function get() { - return b; - } -}); function b() {} (Object.freeze || Object)(exports); \ No newline at end of file From de10efb5dd891e212f128aa55fd932b1144fe7b9 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 19:28:16 +0900 Subject: [PATCH 039/103] Try to detect extra transforms needed for node 0.12 --- .../test/spec-test-helpers.js | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js index f46b1c0b40d1..ca9688f1484d 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js @@ -1,6 +1,24 @@ const babel = require("../../babel-core"); const vm = require("vm"); +const extraPlugins = []; + +function prepareExtraPlugins () { + try { + eval("'use strict'; const foo = 'bar'"); + } catch (e) { + extraPlugins.push(require("../../babel-plugin-transform-es2015-block-scoping")); + } + + try { + eval("'use strict'; ({ get () {} })"); + } catch (e) { + extraPlugins.push(require("../../babel-plugin-transform-es2015-shorthand-properties")); + } +} + +prepareExtraPlugins(); + module.exports.Runner = function Runner (initialModules) { if (!(this instanceof Runner)) { throw new Error("Runner can only be instantiated"); @@ -8,6 +26,12 @@ module.exports.Runner = function Runner (initialModules) { this.modules = {}; this.cache = {}; + this.babelConfig = { + "plugins": [ + [require("../"), {spec: true}].concat(extraPlugins), + ], + "ast": false, + }; if (initialModules != null) { this.addModules(initialModules); @@ -45,12 +69,7 @@ module.exports.Runner.prototype = { }, transformAndRunInNewContext: function (code, context) { - code = babel.transform(code, { - "plugins": [ - [require("../"), {spec: true}], - ], - "ast": false, - }).code; + code = babel.transform(code, this.babelConfig).code; vm.runInNewContext(code, context); return context.module.exports; From 23382524dbf98215c51aab0a767524c58c028b3f Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 20:08:56 +0900 Subject: [PATCH 040/103] Actually fix tests to run in node 0.12 --- .../test/spec-export.js | 35 +++++++++++++------ .../test/spec-interop-import.js | 14 +++++--- .../test/spec-reexport.js | 12 +++---- .../test/spec-test-helpers.js | 22 ++++++++++-- 4 files changed, 59 insertions(+), 24 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js index 37c8f2864f88..7a3910e1f376 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js @@ -18,7 +18,7 @@ describe("spec export", function () { }); it("is tagged as Module", function () { - if (typeof Symbol === "function" && Symbol.toStringTag) { + if (helpers.hasToStringTag()) { assert.strictEqual(exports[Symbol.toStringTag], "Module"); } else { this.skip(); @@ -26,14 +26,14 @@ describe("spec export", function () { }); it("has the __esModule flag", function () { - assert.deepStrictEqual( + assert.deepEqual( Object.getOwnPropertyDescriptor(exports, "__esModule"), { value: true, configurable: false, writable: false, enumerable: false } ); }); it("has no exports", function () { - assert.deepStrictEqual(Object.keys(exports), []); + assert.deepEqual(Object.keys(exports), []); }); }); @@ -42,7 +42,7 @@ describe("spec export", function () { const exports = runner.transformAndRun("const foo = 'foo';\nexport default foo"); it("has no exports other than 'default'", function () { - assert.deepStrictEqual(Object.keys(exports), ["default"]); + assert.deepEqual(Object.keys(exports), ["default"]); }); it("has the correct value", function () { @@ -50,11 +50,20 @@ describe("spec export", function () { }); }); + // Use Function to make sure it is not transformed by babel-register + // Even with the function name transform, it would not be possible to get the correct + // Function.name to be generated, so use this to decide whether to skip tests + const hasFunctionName = Function("return { foo: function () {} }.foo")().name === "foo"; + describe("of anonymous function", function () { const exports = runner.transformAndRun("export default function () {}"); it("has Function.name of 'default'", function () { - assert.strictEqual(exports.default.name, "default"); + if (hasFunctionName) { + assert.strictEqual(exports.default.name, "default"); + } else { + this.skip(); + } }); }); @@ -62,7 +71,11 @@ describe("spec export", function () { const exports = runner.transformAndRun("export default class {}"); it("has Function.name of 'default'", function () { - assert.strictEqual(exports.default.name, "default"); + if (hasFunctionName) { + assert.strictEqual(exports.default.name, "default"); + } else { + this.skip(); + } }); }); }); @@ -72,7 +85,7 @@ describe("spec export", function () { const exports = runner.transformAndRun("export const a = 'a'"); it("has no exports other than 'a'", function () { - assert.deepStrictEqual(Object.keys(exports), ["a"]); + assert.deepEqual(Object.keys(exports), ["a"]); }); it("has the correct value", function () { @@ -100,7 +113,7 @@ describe("spec export", function () { const exports = runner.transformAndRun("export function foo () { return 'bar'; }"); it("has no exports other than 'foo'", function () { - assert.deepStrictEqual(Object.keys(exports), ["foo"]); + assert.deepEqual(Object.keys(exports), ["foo"]); }); it("hass the correct Function.name", function () { @@ -117,7 +130,7 @@ describe("spec export", function () { const exports = runner.transformAndRun("export class Foo {}"); it("has no exports other than 'Foo'", function () { - assert.deepStrictEqual(Object.keys(exports), ["Foo"]); + assert.deepEqual(Object.keys(exports), ["Foo"]); }); it("has the correct Function.name", function () { @@ -129,7 +142,7 @@ describe("spec export", function () { const exports = runner.transformAndRun("var foo\nexport { foo }"); it("has no exports other than 'foo'", function () { - assert.deepStrictEqual(Object.keys(exports), ["foo"]); + assert.deepEqual(Object.keys(exports), ["foo"]); }); }); @@ -137,7 +150,7 @@ describe("spec export", function () { const exports = runner.transformAndRun("function foo () {}\nexport { foo as bar }"); it("has no exports other than 'bar'", function () { - assert.deepStrictEqual(Object.keys(exports), ["bar"]); + assert.deepEqual(Object.keys(exports), ["bar"]); }); it("has the correct Function.name", function () { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js index 388f6ecf6093..6bc71fc28756 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js @@ -14,11 +14,13 @@ test("spec Interop import", function () { assert(Object.isFrozen(exports), "exports is frozen"); assert.strictEqual(Object.getPrototypeOf(exports), null, "exports has null prototype"); - if (typeof Symbol === "function" && Symbol.toStringTag) { + if (helpers.hasToStringTag()) { assert.strictEqual(exports[Symbol.toStringTag], "Module", "exports is tagged as Module"); + } else { + it.skip("exports is tagged as Module"); } - assert.deepStrictEqual( + assert.deepEqual( Object.getOwnPropertyDescriptor(exports, "__esModule"), { value: true, configurable: false, writable: false, enumerable: false }, "__esModule in top level Module" @@ -44,7 +46,7 @@ test("spec Interop import", function () { const ns = exports.ns; - assert.deepStrictEqual( + assert.deepEqual( Object.getOwnPropertyDescriptor(ns, "__esModule"), { value: true, configurable: false, writable: false, enumerable: false }, "__esModule in the imported ns Module" @@ -53,13 +55,15 @@ test("spec Interop import", function () { assert(Object.isFrozen(ns), "ns export is frozen"); assert.strictEqual(Object.getPrototypeOf(ns), null, "ns export has null prototype"); - if (typeof Symbol === "function" && Symbol.toStringTag) { + if (helpers.hasToStringTag()) { assert.strictEqual(ns[Symbol.toStringTag], "Module", "ns export is tagged as Module"); + } else { + it.skip("ns export is tagged as Module"); } const imported = Object.getOwnPropertyNames(ns).filter(function (name) { return name !== "__esModule"; }); - assert.deepStrictEqual(imported, [ "default" ], "No exports other than 'default' in ns Module"); + assert.deepEqual(imported, [ "default" ], "No exports other than 'default' in ns Module"); const defaultDesc = Object.getOwnPropertyDescriptor(ns, "default"); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js index 028f91f79322..bc8170a2d5e5 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js @@ -24,7 +24,7 @@ describe("spec reexport", function () { }); it("is tagged as Module", function () { - if (typeof Symbol === "function" && Symbol.toStringTag) { + if (helpers.hasToStringTag()) { assert.strictEqual(exports[Symbol.toStringTag], "Module"); } else { this.skip(); @@ -32,7 +32,7 @@ describe("spec reexport", function () { }); it("has the __esModule flag", function () { - assert.deepStrictEqual( + assert.deepEqual( Object.getOwnPropertyDescriptor(exports, "__esModule"), { value: true, configurable: false, writable: false, enumerable: false } ); @@ -63,7 +63,7 @@ describe("spec reexport", function () { }); it("the reexport is tagged as Module", function () { - if (typeof Symbol === "function" && Symbol.toStringTag) { + if (helpers.hasToStringTag()) { assert.strictEqual(exports.ab[Symbol.toStringTag], "Module"); } else { this.skip(); @@ -71,7 +71,7 @@ describe("spec reexport", function () { }); it("the reexport has the __esModule flag", function () { - assert.deepStrictEqual( + assert.deepEqual( Object.getOwnPropertyDescriptor(exports.ab, "__esModule"), { value: true, configurable: false, writable: false, enumerable: false } ); @@ -96,7 +96,7 @@ describe("spec reexport", function () { }); it("is tagged as Module", function () { - if (typeof Symbol === "function" && Symbol.toStringTag) { + if (helpers.hasToStringTag()) { assert.strictEqual(exports[Symbol.toStringTag], "Module"); } else { this.skip(); @@ -104,7 +104,7 @@ describe("spec reexport", function () { }); it("has the __esModule flag", function () { - assert.deepStrictEqual( + assert.deepEqual( Object.getOwnPropertyDescriptor(exports, "__esModule"), { value: true, configurable: false, writable: false, enumerable: false } ); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js index ca9688f1484d..57238b7de7eb 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js @@ -15,6 +15,12 @@ function prepareExtraPlugins () { } catch (e) { extraPlugins.push(require("../../babel-plugin-transform-es2015-shorthand-properties")); } + + try { + eval("'use strict'; class Foo {}"); + } catch (e) { + extraPlugins.push(require("../../babel-plugin-transform-es2015-classes")); + } } prepareExtraPlugins(); @@ -28,8 +34,8 @@ module.exports.Runner = function Runner (initialModules) { this.cache = {}; this.babelConfig = { "plugins": [ - [require("../"), {spec: true}].concat(extraPlugins), - ], + [require("../"), {spec: true}], + ].concat(extraPlugins), "ast": false, }; @@ -93,3 +99,15 @@ module.exports.Runner.prototype = { throw new Error("Unmocked module " + id + " required"); } }; + +let hasToStringTag = null; +module.exports.hasToStringTag = function () { + if (hasToStringTag != null) { + return hasToStringTag; + } + + const context = { module: { exports : {} } }; + vm.runInNewContext("module.exports = typeof Symbol === 'function' && Symbol.toStringTag", context); + hasToStringTag = typeof context.module.exports === "symbol"; + return hasToStringTag; +}; From cd388c9fcefd9c90eb27f8a318d5d9bf5e037447 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 20:12:22 +0900 Subject: [PATCH 041/103] Fix incorrect arguments to array push --- .../babel-plugin-transform-es2015-modules-commonjs/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 7747b6f302db..846d432b8ebb 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -384,7 +384,7 @@ export default function () { if (spec) { hoistedExports.set("default", id); } else { - topNodes.push(spec, buildExportsAssignment(defNode, id)); + topNodes.push(buildExportsAssignment(defNode, id)); } path.replaceWith(declaration.node); } else if (spec) { From 3f2f1a7f21acbfcfb2fc18a1b34d0ae5f314e5b9 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 20:37:02 +0900 Subject: [PATCH 042/103] Refactor test/spec-interop-import --- .../test/spec-interop-import.js | 181 +++++++++++------- 1 file changed, 114 insertions(+), 67 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js index 6bc71fc28756..f91db6b728d0 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js @@ -1,7 +1,7 @@ const assert = require("assert"); const helpers = require("./spec-test-helpers"); -test("spec Interop import", function () { +describe("spec Interop import", function () { const runner = new helpers.Runner(); const fakeMod = { "fakeFs": true }; @@ -11,70 +11,117 @@ test("spec Interop import", function () { "import * as ns from 'fs'\nimport fs from 'fs'\nexport { fs, ns }\n" ); - assert(Object.isFrozen(exports), "exports is frozen"); - assert.strictEqual(Object.getPrototypeOf(exports), null, "exports has null prototype"); - - if (helpers.hasToStringTag()) { - assert.strictEqual(exports[Symbol.toStringTag], "Module", "exports is tagged as Module"); - } else { - it.skip("exports is tagged as Module"); - } - - assert.deepEqual( - Object.getOwnPropertyDescriptor(exports, "__esModule"), - { value: true, configurable: false, writable: false, enumerable: false }, - "__esModule in top level Module" - ); - - const nsDesc = Object.getOwnPropertyDescriptor(exports, "ns"); - - assert(nsDesc.enumerable, "ns export is enumerable"); - // This should be the actual observable behavior, but freezing the export - // makes this change to false. - // // assert(nsDesc.writable, "ns export is writable"); - assert(!nsDesc.writable, "ns export happens to not be writable"); - assert(!nsDesc.configurable,"ns export is not configurable"); - - const fsDesc = Object.getOwnPropertyDescriptor(exports, "fs"); - - assert(fsDesc.enumerable, "fs export is enumerable"); - // fsDesc.writable cannot be true like the spec asks because... - // // assert.strictEqual(fsDesc.writable, true); - // ...fsDesc.get must be provided (and be a function) - assert.strictEqual(typeof fsDesc.get, "function", "fs export is a getter"); - assert(!fsDesc.configurable, "fs export is not configurable"); - - const ns = exports.ns; - - assert.deepEqual( - Object.getOwnPropertyDescriptor(ns, "__esModule"), - { value: true, configurable: false, writable: false, enumerable: false }, - "__esModule in the imported ns Module" - ); - - assert(Object.isFrozen(ns), "ns export is frozen"); - assert.strictEqual(Object.getPrototypeOf(ns), null, "ns export has null prototype"); - - if (helpers.hasToStringTag()) { - assert.strictEqual(ns[Symbol.toStringTag], "Module", "ns export is tagged as Module"); - } else { - it.skip("ns export is tagged as Module"); - } - - const imported = Object.getOwnPropertyNames(ns).filter(function (name) { return name !== "__esModule"; }); - - assert.deepEqual(imported, [ "default" ], "No exports other than 'default' in ns Module"); - - const defaultDesc = Object.getOwnPropertyDescriptor(ns, "default"); - - assert.strictEqual(defaultDesc.enumerable, true, "ns.default export is enumerable"); - // This is also not writable for the same reason nsDesc.writable was false - // // assert.strictEqual(defaultDesc.writable, true, "ns.default export is writable"); - assert.strictEqual(defaultDesc.writable, false, "ns export happens to not be writable"); - assert.strictEqual(defaultDesc.configurable, false, "ns.default export is configurable"); - assert.strictEqual("value" in defaultDesc, true, "ns.default export has a value"); - - assert(!("__esModule" in ns.default), "ns.default export is not a Module"); - assert.strictEqual(ns.default, fakeMod, "ns.default export is the commonjs namespace"); - assert.strictEqual(exports.fs, fakeMod, "fs reexport is the commonjs namespace"); + describe("export descriptors", function () { + describe("ns", function () { + const nsDesc = Object.getOwnPropertyDescriptor(exports, "ns"); + + it("is enumerable", function () { + assert(nsDesc.enumerable); + }); + + it("is writable", function () { + // This should be the actual observable behavior, but getters + // are being used, which forbids setting writable. + // Even if it was a value descriptor, freezing the export + // forces the result of getOwnPropertyDescriptor to have writable: false + // // assert(nsDesc.writable); + this.skip(); + }); + + it("happens to be a getter", function () { + assert.strictEqual(typeof nsDesc.get, "function"); + }); + + it("is not configurable", function () { + assert(!nsDesc.configurable); + }); + }); + + describe("fs", function () { + const fsDesc = Object.getOwnPropertyDescriptor(exports, "fs"); + + it("is enumerable", function () { + assert(fsDesc.enumerable); + }); + + it("is writable", function () { + // fsDesc.writable cannot be true for the same reason nsDesc.writable could not + // // assert(fsDesc.writable); + }); + + it("happens to be a getter", function () { + assert.strictEqual(typeof fsDesc.get, "function"); + }); + + it("is not configurable", function () { + assert(!fsDesc.configurable); + }); + }); + }); + + describe("synthetic namespace", function () { + const ns = exports.ns; + + it("is frozen", function () { + assert(Object.isFrozen(ns)); + }); + + it("has a null prototype", function () { + assert.strictEqual(Object.getPrototypeOf(ns), null); + }); + + it("is tagged as Module", function () { + if (helpers.hasToStringTag()) { + assert.strictEqual(ns[Symbol.toStringTag], "Module"); + } else { + this.skip(); + } + }); + + it("has the __esModule flag", function () { + assert.deepEqual( + Object.getOwnPropertyDescriptor(ns, "__esModule"), + { value: true, configurable: false, writable: false, enumerable: false } + ); + }); + + it("has no exports other than 'default'", function () { + assert.deepEqual(Object.keys(ns), ["default"]); + }); + + describe("'default' descriptor", function () { + const defaultDesc = Object.getOwnPropertyDescriptor(ns, "default"); + + it("is enumerable", function () { + assert(defaultDesc.enumerable); + }); + it("is writable", function () { + // Unfortunately, when the namespace is frozen, all value descriptors + // returned by getOwnPropertyDescriptors have writable: false + // // assert(defaultDesc.writable) + this.skip(); + }); + it("happens to not be writable", function () { + assert(!defaultDesc.writable); + }); + it("has a value", function () { + assert("value" in defaultDesc); + }); + }); + }); + + describe("ns export", function () { + it("is not a Module", function () { + assert(!("__esModule" in exports.ns.default)); + }); + it("is the original commonjs namespace", function () { + assert.strictEqual(exports.ns.default, fakeMod); + }); + }); + + describe("fs reexport", function () { + it("is the original commonjs namespace", function() { + assert.strictEqual(exports.fs, fakeMod); + }); + }); }); From f9dd48504bcc20f43d2b1e14ab1a33558f5d81e1 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 20:37:47 +0900 Subject: [PATCH 043/103] Add missing this.skip() --- .../test/spec-interop-import.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js index f91db6b728d0..284589bc0063 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js @@ -47,6 +47,7 @@ describe("spec Interop import", function () { it("is writable", function () { // fsDesc.writable cannot be true for the same reason nsDesc.writable could not // // assert(fsDesc.writable); + this.skip(); }); it("happens to be a getter", function () { From f27fe0883942e05568c42645c2c4f9431a9f9053 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 20:51:57 +0900 Subject: [PATCH 044/103] Also hoist the declaration of the default export Use `let` to ensure it will be in TDZ if something goes horribly wrong and the initialization doesn't happen. --- .../src/index.js | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 846d432b8ebb..55aeb240ea8f 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -54,11 +54,6 @@ const specBuildFunctionNameWrapper = template(` ({ default: $0 }).default `); -// The descriptors are as specified in https://tc39.github.io/ecma262/#sec-module-namespace-exotic-objects-getownproperty-p -const specBuildExportDefault = template(` - Object.defineProperty(EXPORTS, "default", { enumerable: true, writable: true, value: VALUE }); -`); - const specBuildOwnExports = template(` const $0 = Object.keys($1) `); @@ -79,9 +74,13 @@ const specBuildNamespaceSpread = template(` // Unfortunately, regular objects can't synthesize a value descriptor every time they're read, // so a getter needs to be used for live bindings. // It's also not allowed to specify writable when using getters/setters. +// Even if it was a value, getOwnPropertyDescriptor would return writable: false after +// the exports are frozen by specFinishNamespaceExport. // // Accessing a non-hoisted export should cause a DMZ error instead of just // returning undefined, so a getter is also needed for that. +// +// This is as close as we can get to https://tc39.github.io/ecma262/#sec-module-namespace-exotic-objects-getownproperty-p const specBuildHoistedExportDescriptor = template(` ({ enumerable: true, get() { return $0; } }) `); @@ -297,6 +296,7 @@ export default function () { let remaps = Object.create(null); const hoistedExports = spec && new Map(); + let defaultExportUid = null; let ownExportsUid = null; let requires = Object.create(null); @@ -388,9 +388,12 @@ export default function () { } path.replaceWith(declaration.node); } else if (spec) { - hoistedExports.set("default", null); + defaultExportUid = path.scope.generateUidIdentifier("default"); + hoistedExports.set("default", defaultExportUid); const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; - topNodes.push(specBuildExportDefault({ EXPORTS: exportsObj, VALUE: expr })); + topNodes.push(t.variableDeclaration("let", [ + t.variableDeclarator(defaultExportUid, expr) + ])); path.remove(); } else { const expr = t.toExpression(declaration.node); @@ -412,15 +415,19 @@ export default function () { ]); } } else if (spec) { - hoistedExports.set("default", null); + defaultExportUid = path.scope.generateUidIdentifier("default"); + hoistedExports.set("default", defaultExportUid); const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; - path.replaceWith(specBuildExportDefault({ EXPORTS: exportsObj, VALUE: expr })); + + path.replaceWith(t.variableDeclaration("let", [ + t.variableDeclarator(defaultExportUid, expr) + ])); // Manually re-queue the expression so other transforms can get to it. // Ideally this would happen automatically from the replaceWith above. // See #4140 for more info. - path.parentPath.requeue(path.get("expression.arguments.2")); + path.parentPath.requeue(path.get("declarations.init")); } else { const expr = t.toExpression(declaration.node); path.replaceWith(buildExportsAssignment(defNode, expr)); @@ -433,13 +440,17 @@ export default function () { } else { const defNode = t.identifier("default"); if (spec) { - hoistedExports.set("default", null); - path.replaceWith(specBuildExportDefault({ EXPORTS: exportsObj, VALUE: declaration.node })); + defaultExportUid = path.scope.generateUidIdentifier("default"); + hoistedExports.set("default", defaultExportUid); + + path.replaceWith(t.variableDeclaration("let", [ + t.variableDeclarator(defaultExportUid, declaration.node) + ])); // Manually re-queue the expression so other transforms can get to it. // Ideally this would happen automatically from the replaceWith above. // See #4140 for more info. - path.parentPath.requeue(path.get("expression.arguments.2")); + path.parentPath.requeue(path.get("declarations.init")); } else { path.replaceWith(buildExportsAssignment(defNode, declaration.node)); From 2fddb7af3d9aaa8b36fe0dd673abf4d5d64b4852 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 20:53:52 +0900 Subject: [PATCH 045/103] Update fixtures --- .../spec/commonjs-shadow-1/expected.js | 18 ++++++++++------ .../fixtures/spec/commonjs-shadow/expected.js | 21 ++++++++++++------- .../fixtures/spec/export-default/expected.js | 18 +++++++++------- .../modules-commonjs-spec/expected.js | 16 +++++++------- 4 files changed, 46 insertions(+), 27 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js index 619d54bcfa6b..f81646fd5ef6 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js @@ -14,13 +14,19 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { }); } -Object.defineProperty(exports, "default", { - enumerable: true, - writable: true, - value: { - default: function () {} - }.default +Object.defineProperties(exports, { + default: { + enumerable: true, + + get() { + return _default; + } + + } }); +let _default = { + default: function () {} +}.default; _exports = function () {}; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js index c3bad22fa1ff..e3c410416a4c 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js @@ -14,15 +14,22 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { }); } -var _bar = babelHelpers.specRequireInterop(require('bar')); +Object.defineProperties(exports, { + default: { + enumerable: true, + + get() { + return _default; + } -Object.defineProperty(exports, "default", { - enumerable: true, - writable: true, - value: { - default: class {} - }.default + } }); + +var _bar = babelHelpers.specRequireInterop(require('bar')); + +let _default = { + default: class {} +}.default; ; // neither of these should be able to use or affect the real exports diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js index 3e2fb6e77433..7e62ec78167b 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -15,6 +15,14 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { } Object.defineProperties(exports, { + default: { + enumerable: true, + + get() { + return _default; + } + + }, unrelated: { enumerable: true, @@ -24,13 +32,9 @@ Object.defineProperties(exports, { } }); -Object.defineProperty(exports, "default", { - enumerable: true, - writable: true, - value: { - default: function () {} - }.default -}); +let _default = { + default: function () {} +}.default; var unrelated; unrelated = "changed"; (Object.freeze || Object)(exports); \ No newline at end of file diff --git a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js index 3fafd2eeabdf..263730f3babc 100644 --- a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js +++ b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js @@ -15,6 +15,12 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { } Object.defineProperties(exports, { + default: { + enumerable: true, + get: function get() { + return _default; + } + }, b: { enumerable: true, get: function get() { @@ -22,12 +28,8 @@ Object.defineProperties(exports, { } } }); -Object.defineProperty(exports, "default", { - enumerable: true, - writable: true, - value: { - default: function _default() {} - }.default -}); +var _default = { + default: function _default() {} +}.default; function b() {} (Object.freeze || Object)(exports); \ No newline at end of file From ba4d3d64328646594c85284a85f1686ab2169a5d Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 21:30:57 +0900 Subject: [PATCH 046/103] Also hoist the Object.freeze call --- .../src/index.js | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 55aeb240ea8f..9e02bc732540 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -557,14 +557,21 @@ export default function () { if (ownExportsUid == null) { ownExportsUid = path.scope.generateUidIdentifier("ownExports"); const ownExportsNode = specBuildOwnExports(ownExportsUid, exportsObj); + ownExportsNode._blockHoist = 3; topNodes.push(ownExportsNode); } + // Unfortunately, the namespace spread needs to happen _before_ + // the namespace is frozen. This _will_ result in out-of-order + // imports, with the 'export * from' imports all running before + // any of the other imports. + exportNode = specBuildNamespaceSpread({ EXPORTS: exportsObj, OWN_EXPORTS: ownExportsUid, - OBJECT: addRequire(path.node.source.value, spec, path.node._blockHoist) + OBJECT: addRequire(path.node.source.value, spec, 3) }); + exportNode._blockHoist = 3; } else { exportNode = buildExportAll({ OBJECT: addRequire(path.node.source.value, spec, path.node._blockHoist) @@ -675,30 +682,36 @@ export default function () { topNodes.unshift(node); } - } - if (!spec && hasImports && Object.keys(nonHoistedExportNames).length) { - let hoistedExportsNode = t.identifier("undefined"); - - for (let name in nonHoistedExportNames) { - hoistedExportsNode = buildExportsAssignment(t.identifier(name), hoistedExportsNode).expression; + if (hasExports) { + const node = specFinishNamespaceExport(exportsObj); + node._blockHoist = 3; + topNodes.push(node); } + } else { + if (hasImports && Object.keys(nonHoistedExportNames).length) { + let hoistedExportsNode = t.identifier("undefined"); + + for (let name in nonHoistedExportNames) { + hoistedExportsNode = buildExportsAssignment(t.identifier(name), hoistedExportsNode).expression; + } - const node = t.expressionStatement(hoistedExportsNode); - node._blockHoist = 3; + const node = t.expressionStatement(hoistedExportsNode); + node._blockHoist = 3; - topNodes.unshift(node); - } + topNodes.unshift(node); + } - // add __esModule declaration if this file has any exports - if (hasExports && !strict && !spec) { - let buildTemplate = buildExportsModuleDeclaration; - if (state.opts.loose) buildTemplate = buildLooseExportsModuleDeclaration; + // add __esModule declaration if this file has any exports + if (hasExports && !strict) { + let buildTemplate = buildExportsModuleDeclaration; + if (state.opts.loose) buildTemplate = buildLooseExportsModuleDeclaration; - const declar = buildTemplate(); - declar._blockHoist = 3; + const declar = buildTemplate(); + declar._blockHoist = 3; - topNodes.unshift(declar); + topNodes.unshift(declar); + } } path.unshiftContainer("body", topNodes); @@ -708,7 +721,6 @@ export default function () { decls.forEach((decl) => { decl._blockHoist = 3; }); path.unshiftContainer("body", decls); - path.pushContainer("body", [specFinishNamespaceExport(exportsObj)]); } if (spec) { From 82455bc9bab98f44a3cf887c01527fa5bf52cf6f Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 21:31:04 +0900 Subject: [PATCH 047/103] Update fixtures --- .../test/fixtures/spec/commonjs-shadow-1/expected.js | 2 +- .../test/fixtures/spec/commonjs-shadow-2/expected.js | 4 +++- .../test/fixtures/spec/commonjs-shadow/expected.js | 2 +- .../test/fixtures/spec/export-default-named/expected.js | 4 ++-- .../test/fixtures/spec/export-default/expected.js | 4 ++-- .../test/fixtures/spec/exports/expected.js | 4 ++-- .../test/fixtures/spec/reexports/expected.js | 5 ++--- .../preset-options/modules-commonjs-spec/expected.js | 4 ++-- 8 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js index f81646fd5ef6..f1248c6fc7a5 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js @@ -24,12 +24,12 @@ Object.defineProperties(exports, { } }); +(Object.freeze || Object)(exports); let _default = { default: function () {} }.default; _exports = function () {}; -(Object.freeze || Object)(exports); let _exports; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js index d208a7611eab..9c127fe38a3e 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-2/expected.js @@ -14,7 +14,9 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { }); } -_module.exports = class {}; (Object.freeze || Object)(exports); + +_module.exports = class {}; + let _module; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js index e3c410416a4c..a63986d16f52 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js @@ -24,6 +24,7 @@ Object.defineProperties(exports, { } }); +(Object.freeze || Object)(exports); var _bar = babelHelpers.specRequireInterop(require('bar')); @@ -35,6 +36,5 @@ let _default = { // neither of these should be able to use or affect the real exports new _exports.default(); _module.exports = {}; -(Object.freeze || Object)(exports); let _exports, _module; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js index 9bd50067ab04..dd98228d8edb 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-named/expected.js @@ -24,7 +24,7 @@ Object.defineProperties(exports, { } }); +(Object.freeze || Object)(exports); function named() { named = function () {}; -} -(Object.freeze || Object)(exports); \ No newline at end of file +} \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js index 7e62ec78167b..6809a8bcf691 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -32,9 +32,9 @@ Object.defineProperties(exports, { } }); +(Object.freeze || Object)(exports); let _default = { default: function () {} }.default; var unrelated; -unrelated = "changed"; -(Object.freeze || Object)(exports); \ No newline at end of file +unrelated = "changed"; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js index c85c8a9232cb..3a3d5d1beee0 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -48,6 +48,7 @@ Object.defineProperties(exports, { } }); +(Object.freeze || Object)(exports); var _outside = babelHelpers.specRequireInterop(require("outside")); @@ -59,5 +60,4 @@ function foo() { class Bar {} -const baz = foo(); -(Object.freeze || Object)(exports); \ No newline at end of file +const baz = foo(); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index 8191934da8bf..c48b9a418f7b 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -57,11 +57,10 @@ Object.keys(_iDontKnow).forEach(function (key) { }); }); +(Object.freeze || Object)(exports); var _because = babelHelpers.specRequireInterop(require('./because')); var _somewhere = babelHelpers.specRequireInterop(require('./somewhere')); -var _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); - -(Object.freeze || Object)(exports); \ No newline at end of file +var _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); \ No newline at end of file diff --git a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js index 263730f3babc..5da0ef4e0201 100644 --- a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js +++ b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js @@ -28,8 +28,8 @@ Object.defineProperties(exports, { } } }); +(Object.freeze || Object)(exports); var _default = { default: function _default() {} }.default; -function b() {} -(Object.freeze || Object)(exports); \ No newline at end of file +function b() {} \ No newline at end of file From 33a956d6202fbb36761f46c3bf4004d602c78c82 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 21:31:33 +0900 Subject: [PATCH 048/103] Use before hooks to run less code during TEST_GREP --- .../test/spec-export.js | 72 +++++++++++++++---- .../test/spec-interop-import.js | 34 +++++++-- .../test/spec-reexport.js | 17 ++++- 3 files changed, 101 insertions(+), 22 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js index 7a3910e1f376..f14059dcbd3b 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js @@ -7,7 +7,11 @@ describe("spec export", function () { const runner = new helpers.Runner(); describe("basic shape", function () { - const exports = runner.transformAndRun("export {}"); + let exports; + + before(function () { + exports = runner.transformAndRun("export {}"); + }); it("is frozen", function () { assert(Object.isFrozen(exports)); @@ -39,7 +43,11 @@ describe("spec export", function () { describe("default export", function () { describe("of single value", function () { - const exports = runner.transformAndRun("const foo = 'foo';\nexport default foo"); + let exports; + + before(function () { + exports = runner.transformAndRun("const foo = 'foo';\nexport default foo"); + }); it("has no exports other than 'default'", function () { assert.deepEqual(Object.keys(exports), ["default"]); @@ -56,7 +64,11 @@ describe("spec export", function () { const hasFunctionName = Function("return { foo: function () {} }.foo")().name === "foo"; describe("of anonymous function", function () { - const exports = runner.transformAndRun("export default function () {}"); + let exports; + + before(function () { + exports = runner.transformAndRun("export default function () {}"); + }); it("has Function.name of 'default'", function () { if (hasFunctionName) { @@ -68,7 +80,11 @@ describe("spec export", function () { }); describe("of anonymous class", function () { - const exports = runner.transformAndRun("export default class {}"); + let exports; + + before(function () { + exports = runner.transformAndRun("export default class {}"); + }); it("has Function.name of 'default'", function () { if (hasFunctionName) { @@ -82,7 +98,11 @@ describe("spec export", function () { describe("named export", function () { describe("of single value declaration", function () { - const exports = runner.transformAndRun("export const a = 'a'"); + let exports; + + before(function () { + exports = runner.transformAndRun("export const a = 'a'"); + }); it("has no exports other than 'a'", function () { assert.deepEqual(Object.keys(exports), ["a"]); @@ -94,7 +114,11 @@ describe("spec export", function () { }); describe("of multiple value declaration", function () { - const exports = runner.transformAndRun("export const a = 'a', b = 'b'"); + let exports; + + before(function () { + exports = runner.transformAndRun("export const a = 'a', b = 'b'"); + }); it("has no exports other than 'a' and 'b'", function () { const keys = Object.keys(exports); @@ -110,7 +134,11 @@ describe("spec export", function () { }); describe("of function", function () { - const exports = runner.transformAndRun("export function foo () { return 'bar'; }"); + let exports; + + before(function () { + exports = runner.transformAndRun("export function foo () { return 'bar'; }"); + }); it("has no exports other than 'foo'", function () { assert.deepEqual(Object.keys(exports), ["foo"]); @@ -127,7 +155,11 @@ describe("spec export", function () { }); describe("of class", function () { - const exports = runner.transformAndRun("export class Foo {}"); + let exports; + + before(function () { + exports = runner.transformAndRun("export class Foo {}"); + }); it("has no exports other than 'Foo'", function () { assert.deepEqual(Object.keys(exports), ["Foo"]); @@ -139,7 +171,11 @@ describe("spec export", function () { }); describe("of identifier", function () { - const exports = runner.transformAndRun("var foo\nexport { foo }"); + let exports; + + before(function () { + exports = runner.transformAndRun("var foo\nexport { foo }"); + }); it("has no exports other than 'foo'", function () { assert.deepEqual(Object.keys(exports), ["foo"]); @@ -147,7 +183,11 @@ describe("spec export", function () { }); describe("of renamed identifier", function () { - const exports = runner.transformAndRun("function foo () {}\nexport { foo as bar }"); + let exports; + + before(function () { + exports = runner.transformAndRun("function foo () {}\nexport { foo as bar }"); + }); it("has no exports other than 'bar'", function () { assert.deepEqual(Object.keys(exports), ["bar"]); @@ -161,7 +201,11 @@ describe("spec export", function () { describe("live binding", function () { describe("of default export", function () { - const exports = runner.transformAndRun("export default function foo () { foo = ':scream:' }"); + let exports; + + before(function () { + exports = runner.transformAndRun("export default function foo () { foo = ':scream:' }"); + }); it("has the correct initial value", function () { assert.strictEqual(typeof exports.default, "function"); @@ -174,7 +218,11 @@ describe("spec export", function () { }); describe("of named export", function () { - const exports = runner.transformAndRun("export let count = 0\nexport default function up () { count += 1 }"); + let exports; + + before(function () { + exports = runner.transformAndRun("export let count = 0\nexport default function up () { count += 1 }"); + }); it("has the correct initial value", function () { assert.strictEqual(exports.count, 0); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js index 284589bc0063..c3dd85e83e46 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-interop-import.js @@ -7,13 +7,21 @@ describe("spec Interop import", function () { const fakeMod = { "fakeFs": true }; runner.addToCache("fs", { module: { exports: fakeMod } }); - const exports = runner.transformAndRun( - "import * as ns from 'fs'\nimport fs from 'fs'\nexport { fs, ns }\n" - ); + let exports; + + before(function () { + exports = runner.transformAndRun( + "import * as ns from 'fs'\nimport fs from 'fs'\nexport { fs, ns }\n" + ); + }); describe("export descriptors", function () { describe("ns", function () { - const nsDesc = Object.getOwnPropertyDescriptor(exports, "ns"); + let nsDesc; + + before(function () { + nsDesc = Object.getOwnPropertyDescriptor(exports, "ns"); + }); it("is enumerable", function () { assert(nsDesc.enumerable); @@ -38,7 +46,11 @@ describe("spec Interop import", function () { }); describe("fs", function () { - const fsDesc = Object.getOwnPropertyDescriptor(exports, "fs"); + let fsDesc; + + before(function () { + fsDesc = Object.getOwnPropertyDescriptor(exports, "fs"); + }); it("is enumerable", function () { assert(fsDesc.enumerable); @@ -61,7 +73,11 @@ describe("spec Interop import", function () { }); describe("synthetic namespace", function () { - const ns = exports.ns; + let ns; + + before(function () { + ns = exports.ns; + }); it("is frozen", function () { assert(Object.isFrozen(ns)); @@ -91,7 +107,11 @@ describe("spec Interop import", function () { }); describe("'default' descriptor", function () { - const defaultDesc = Object.getOwnPropertyDescriptor(ns, "default"); + let defaultDesc; + + before(function () { + defaultDesc = Object.getOwnPropertyDescriptor(ns, "default"); + }); it("is enumerable", function () { assert(defaultDesc.enumerable); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js index bc8170a2d5e5..c924a2026fc8 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js @@ -13,7 +13,11 @@ describe("spec reexport", function () { }); describe("individual reexports", function () { - const exports = runner.getExportsOf("ab"); + let exports; + + before(function () { + exports = runner.getExportsOf("ab"); + }); it("is frozen", function () { assert(Object.isFrozen(exports)); @@ -52,7 +56,11 @@ describe("spec reexport", function () { }); describe("namespace reexport", function () { - const exports = runner.getExportsOf("ns"); + let exports; + + before(function () { + exports = runner.getExportsOf("ns"); + }); it("the reexport is frozen", function () { assert(Object.isFrozen(exports.ab)); @@ -84,8 +92,11 @@ describe("spec reexport", function () { }); describe("star reexport", function () { - const exports = runner.getExportsOf("star"); + let exports; + before(function () { + exports = runner.getExportsOf("star"); + }); it("is frozen", function () { assert(Object.isFrozen(exports)); From db72c8719f88bfe73a7913ea66c1716a0f84d145 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 21:51:15 +0900 Subject: [PATCH 049/103] Hoist the initialization of the default func if it's inside the function name wrapper --- .../src/index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 9e02bc732540..6aa48f974424 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -391,9 +391,12 @@ export default function () { defaultExportUid = path.scope.generateUidIdentifier("default"); hoistedExports.set("default", defaultExportUid); const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; - topNodes.push(t.variableDeclaration("let", [ + const decl = t.variableDeclaration("let", [ t.variableDeclarator(defaultExportUid, expr) - ])); + ]); + decl.loc = declaration.node.loc; + decl._blockHoist = 3; + topNodes.unshift(decl); path.remove(); } else { const expr = t.toExpression(declaration.node); From 8ba476d670276d3f719f7ac023c984fd133d827f Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sat, 10 Dec 2016 21:51:39 +0900 Subject: [PATCH 050/103] Add default func hoisting fixture, update fixtures --- .../spec/commonjs-shadow-1/expected.js | 2 +- .../export-default-func-hoisting/actual.js | 3 ++ .../export-default-func-hoisting/expected.js | 51 +++++++++++++++++++ .../fixtures/spec/export-default/expected.js | 2 +- .../modules-commonjs-spec/expected.js | 2 +- 5 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js index f1248c6fc7a5..d89d44cb86c1 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js @@ -24,10 +24,10 @@ Object.defineProperties(exports, { } }); -(Object.freeze || Object)(exports); let _default = { default: function () {} }.default; +(Object.freeze || Object)(exports); _exports = function () {}; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/actual.js new file mode 100644 index 000000000000..cbbd0b626777 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/actual.js @@ -0,0 +1,3 @@ +import * as foo from 'foo' +export * from 'bar' +export default function () { return foo } \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js new file mode 100644 index 000000000000..dafc334fb2be --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js @@ -0,0 +1,51 @@ +'use strict'; + +const exports = module.exports = Object.create ? Object.create(null, { + __esModule: { + value: true + } +}) : { + __esModule: true +}; + +if (typeof Symbol === "function" && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { + value: "Module" + }); +} + +Object.defineProperties(exports, { + default: { + enumerable: true, + + get() { + return _default; + } + + } +}); +let _default = { + default: function () { + return _foo; + } +}.default; + +const _ownExports = Object.keys(exports); + +var _bar = babelHelpers.specRequireInterop(require('bar')); + +Object.keys(_bar).forEach(function (key) { + if (key === "__esModule" || key === "default" || _ownExports.indexOf(key) >= 0) return; + if (key in exports && (exports[key] === _bar[key] || typeof exports[key] === 'number' && isNaN(exports[key]))) return; + Object.defineProperty(exports, key, { + enumerable: true, + + get() { + return _bar[key]; + } + + }); +}); +(Object.freeze || Object)(exports); + +var _foo = babelHelpers.specRequireInterop(require('foo')); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js index 6809a8bcf691..721df9030725 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -32,9 +32,9 @@ Object.defineProperties(exports, { } }); -(Object.freeze || Object)(exports); let _default = { default: function () {} }.default; +(Object.freeze || Object)(exports); var unrelated; unrelated = "changed"; \ No newline at end of file diff --git a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js index 5da0ef4e0201..57432694d213 100644 --- a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js +++ b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js @@ -28,8 +28,8 @@ Object.defineProperties(exports, { } } }); -(Object.freeze || Object)(exports); var _default = { default: function _default() {} }.default; +(Object.freeze || Object)(exports); function b() {} \ No newline at end of file From 97db049030d8cd36017dee262fe26343daca6ddc Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 16:01:02 +0900 Subject: [PATCH 051/103] Add documentation for spec mode --- .../README.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md index 0693b91f07d6..8415cabc2901 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md @@ -62,6 +62,69 @@ require("babel-core").transform("code", { }); ``` +## Options `spec` + +By default, `babel` actually implements importing very loosely, by +supporting treating a commonjs export as if it was a namespace export. +The exported namespace is also not frozen and has an incorrect prototype. + +The `spec` option, when set to `true`, tries to generate code that is as +close as possible to what is required by the ECMA262 spec without relying +on `Proxy`. The exports will be frozen, and imports will always be treated +like ES modules. + +Importing a commonjs module (say, the standard `fs` module) will always +wrap it in an ES module that has a single `default` export. This means that +some imports that work in non-`spec` mode, like `import { readFile } from 'fs'`, +will result in `undefined` in `spec` mode. + +Note that exports, under this mode, always require runtime support for +getters. It also is not possible to access or write to the commonjs +`exports` or `module` objects; attempts to access them will result in +TDZ errors at runtime. + +```javascript +import 'module1'; +import defaultImport from 'module2'; +import * as namespace from 'module3'; +import { pick } from 'module4'; + +defaultImport(namespace, pick); + +export { pick } +export default function () {} +``` + +```javascript +const exports = module.exports = Object.create ? Object.create(null, { + __esModule: { value: true } +}) : { __esModule: true } +Object.defineProperties(exports, { + default: { + enumerable: true, + get() { return _default; } + }, + pick: { + enumerable: true, + get() { return _module4.pick; } + } +}) +let _default = { + default: function () {} +}.default; +(Object.freeze || Object)(exports); + +require('module1'); + +const _module2 = babelHelpers._specInteropImport(require('module2')); + +const _module3 = babelHelpers._specInteropImport(require('module3')); + +const _module4 = babelHelpers._specInteropImport(require('module4')); + +_module2.default(_module3, _module4.pick); +``` + ## Options `loose` As per the spec, `import` and `export` are only allowed to be used at the top @@ -85,3 +148,5 @@ and instead of using `Object.defineProperty` an assignment will be used instead. var foo = exports.foo = 5; exports.__esModule = true; ``` + +The `loose` option is **ignored** if used in combination with `spec`. \ No newline at end of file From 31205a8b07bd09fd78eccd99f5190ed42052a73c Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 16:14:22 +0900 Subject: [PATCH 052/103] Add a 'specImport' option to only enable the import half of spec mode --- .../src/index.js | 9 +++++++-- .../specImport/commonjs-no-shadow/actual.js | 5 +++++ .../specImport/commonjs-no-shadow/exec.js | 3 +++ .../specImport/commonjs-no-shadow/expected.js | 9 +++++++++ .../specImport/import-and-export/actual.js | 8 ++++++++ .../specImport/import-and-export/expected.js | 19 +++++++++++++++++++ .../test/fixtures/specImport/options.json | 3 +++ 7 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/exec.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/options.json diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 6aa48f974424..b1fd0c02a22f 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -275,6 +275,7 @@ export default function () { let strict = !!this.opts.strict; const spec = isSpec(state); + const specImport = isSpecImport(state); let { scope } = path; @@ -313,7 +314,7 @@ export default function () { let varDecl = t.variableDeclaration("var", [ t.variableDeclarator(ref, - spec + specImport ? t.callExpression(this.addHelper("specRequireInterop"), [req]) : req ) @@ -591,7 +592,7 @@ export default function () { if (specifiers.length) { const uid = addRequire(source, spec, maxBlockHoist); - if (spec) { + if (specImport) { for (const specifier of specifiers) { if (t.isImportNamespaceSpecifier(specifier)) { remaps[specifier.local.name] = uid; @@ -755,3 +756,7 @@ export default function () { function isSpec (state) { return state && state.opts && !!state.opts.spec; } + +function isSpecImport (state) { + return state && state.opts && (!!state.opts.spec || !!state.opts.specImport); +} \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/actual.js new file mode 100644 index 000000000000..6551aeb215ec --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/actual.js @@ -0,0 +1,5 @@ +import foo from 'bar' + +export {} + +module.exports = exports = {}; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/exec.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/exec.js new file mode 100644 index 000000000000..80b3abf6ebdb --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/exec.js @@ -0,0 +1,3 @@ +export {} + +assert.notStrictEqual(typeof exports, 'undefined') diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js new file mode 100644 index 000000000000..ebf14257e902 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js @@ -0,0 +1,9 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _bar = babelHelpers.specRequireInterop(require('bar')); + +module.exports = exports = {}; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/actual.js new file mode 100644 index 000000000000..b2823a779ea6 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/actual.js @@ -0,0 +1,8 @@ +import 'module1' +import * as module2 from 'module2' +import module3 from 'module3' +import { default as module4 } from 'module4' + +module2.default(module3, module4) + +export { module2, module3 } diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js new file mode 100644 index 000000000000..a3e60ebba4cd --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js @@ -0,0 +1,19 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.module3 = exports.module2 = undefined; + +require('module1'); + +var _module = babelHelpers.specRequireInterop(require('module2')); + +var _module2 = babelHelpers.specRequireInterop(require('module3')); + +var _module3 = babelHelpers.specRequireInterop(require('module4')); + +_module.default(_module2.default, _module3.default); + +exports.module2 = _module; +exports.module3 = _module2.default; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/options.json b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/options.json new file mode 100644 index 000000000000..47949d470d56 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["external-helpers", ["transform-es2015-modules-commonjs", {"spec": false, "specImport": true}]] +} From f6f291f3845e1288187451d22fdcf6cc1ff74144 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 16:30:05 +0900 Subject: [PATCH 053/103] Improve README --- .../README.md | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md index 8415cabc2901..e0114486344f 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md @@ -83,6 +83,8 @@ getters. It also is not possible to access or write to the commonjs `exports` or `module` objects; attempts to access them will result in TDZ errors at runtime. +### Input + ```javascript import 'module1'; import defaultImport from 'module2'; @@ -95,10 +97,14 @@ export { pick } export default function () {} ``` +### Output + ```javascript +'use strict'; + const exports = module.exports = Object.create ? Object.create(null, { __esModule: { value: true } -}) : { __esModule: true } +}) : { __esModule: true }; Object.defineProperties(exports, { default: { enumerable: true, @@ -108,7 +114,7 @@ Object.defineProperties(exports, { enumerable: true, get() { return _module4.pick; } } -}) +}); let _default = { default: function () {} }.default; @@ -125,6 +131,40 @@ const _module4 = babelHelpers._specInteropImport(require('module4')); _module2.default(_module3, _module4.pick); ``` +## Options `specImport` + +This option enables only the half of `spec` mode that affects the imports, without +changing how exports are generated. This would allow the generation of code that +may still be compatible with engines that do not support getters. + +Note that the require helper does use `Object.defineProperty`, so when running on +an old engine that does not support it, a polyfill like `es5-sham` is still required. + +This option is **ignored** if `spec` is enabled. Enabling `spec` implies that this +option is also enabled. + +### Input + +```javascript +import { pick } from 'module' + +export default pick() +``` + +### Output + +```javascript +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _module = babelHelpers.specRequireInterop(require('module')); + +exports.default = (0, _module.pick)(); +``` + ## Options `loose` As per the spec, `import` and `export` are only allowed to be used at the top From 1861e4d37e187a66b56c9f28c907b9e8026689dc Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 16:32:26 +0900 Subject: [PATCH 054/103] Use const for specImport namespaces to ensure TDZ in circular references --- .../babel-plugin-transform-es2015-modules-commonjs/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index b1fd0c02a22f..c2d65695adb7 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -312,7 +312,7 @@ export default function () { t.stringLiteral(source) ).expression; - let varDecl = t.variableDeclaration("var", [ + let varDecl = t.variableDeclaration(specImport ? "const" : "var", [ t.variableDeclarator(ref, specImport ? t.callExpression(this.addHelper("specRequireInterop"), [req]) From a371bb17ce361e79e359345d59f2d262802fc42a Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 16:32:39 +0900 Subject: [PATCH 055/103] Add more specImport tests, update fixtures --- .../fixtures/spec/commonjs-shadow/expected.js | 2 +- .../export-default-func-hoisting/expected.js | 4 +-- .../test/fixtures/spec/exports/expected.js | 2 +- .../fixtures/spec/import-simple/expected.js | 6 ++-- .../test/fixtures/spec/import/expected.js | 10 +++---- .../test/fixtures/spec/reexports/expected.js | 8 ++--- .../specImport/commonjs-no-shadow/expected.js | 2 +- .../specImport/export-default/actual.js | 3 ++ .../specImport/export-default/expected.js | 9 ++++++ .../specImport/import-and-export/expected.js | 6 ++-- .../fixtures/specImport/reexport/actual.js | 7 +++++ .../fixtures/specImport/reexport/expected.js | 29 +++++++++++++++++++ 12 files changed, 68 insertions(+), 20 deletions(-) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js index a63986d16f52..7168f20e7137 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow/expected.js @@ -26,7 +26,7 @@ Object.defineProperties(exports, { }); (Object.freeze || Object)(exports); -var _bar = babelHelpers.specRequireInterop(require('bar')); +const _bar = babelHelpers.specRequireInterop(require('bar')); let _default = { default: class {} diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js index dafc334fb2be..926ec213203a 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js @@ -32,7 +32,7 @@ let _default = { const _ownExports = Object.keys(exports); -var _bar = babelHelpers.specRequireInterop(require('bar')); +const _bar = babelHelpers.specRequireInterop(require('bar')); Object.keys(_bar).forEach(function (key) { if (key === "__esModule" || key === "default" || _ownExports.indexOf(key) >= 0) return; @@ -48,4 +48,4 @@ Object.keys(_bar).forEach(function (key) { }); (Object.freeze || Object)(exports); -var _foo = babelHelpers.specRequireInterop(require('foo')); \ No newline at end of file +const _foo = babelHelpers.specRequireInterop(require('foo')); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js index 3a3d5d1beee0..62390aa3d09d 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -50,7 +50,7 @@ Object.defineProperties(exports, { }); (Object.freeze || Object)(exports); -var _outside = babelHelpers.specRequireInterop(require("outside")); +const _outside = babelHelpers.specRequireInterop(require("outside")); var def; function foo() { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js index 3aba3f2fdee8..a2207ce5af09 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js @@ -4,11 +4,11 @@ require('a'); require('b'); -var _c = babelHelpers.specRequireInterop(require('c')); +const _c = babelHelpers.specRequireInterop(require('c')); -var _d = babelHelpers.specRequireInterop(require('d')); +const _d = babelHelpers.specRequireInterop(require('d')); -var _e = babelHelpers.specRequireInterop(require('e')); +const _e = babelHelpers.specRequireInterop(require('e')); _c.name; _d.default; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js index 7287f81c3b9e..2b2633b12fe1 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js @@ -1,18 +1,18 @@ 'use strict'; -var _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); +const _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); -var _outside = babelHelpers.specRequireInterop(require('./outside')); +const _outside = babelHelpers.specRequireInterop(require('./outside')); -var _anywhere = babelHelpers.specRequireInterop(require('./anywhere')); +const _anywhere = babelHelpers.specRequireInterop(require('./anywhere')); require('./empty'); require('./imperative'); -var _iDontKnow = babelHelpers.specRequireInterop(require('./i-dont-know')); +const _iDontKnow = babelHelpers.specRequireInterop(require('./i-dont-know')); -var _naturally = babelHelpers.specRequireInterop(require('./naturally')); +const _naturally = babelHelpers.specRequireInterop(require('./naturally')); (0, _outside.default)(_outside.obj.key); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index c48b9a418f7b..96571bc6f667 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -43,7 +43,7 @@ Object.defineProperties(exports, { const _ownExports = Object.keys(exports); -var _iDontKnow = babelHelpers.specRequireInterop(require('./i-dont-know')); +const _iDontKnow = babelHelpers.specRequireInterop(require('./i-dont-know')); Object.keys(_iDontKnow).forEach(function (key) { if (key === "__esModule" || key === "default" || _ownExports.indexOf(key) >= 0) return; @@ -59,8 +59,8 @@ Object.keys(_iDontKnow).forEach(function (key) { }); (Object.freeze || Object)(exports); -var _because = babelHelpers.specRequireInterop(require('./because')); +const _because = babelHelpers.specRequireInterop(require('./because')); -var _somewhere = babelHelpers.specRequireInterop(require('./somewhere')); +const _somewhere = babelHelpers.specRequireInterop(require('./somewhere')); -var _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); \ No newline at end of file +const _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js index ebf14257e902..7b49a4b2a3f2 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js @@ -4,6 +4,6 @@ Object.defineProperty(exports, "__esModule", { value: true }); -var _bar = babelHelpers.specRequireInterop(require('bar')); +const _bar = babelHelpers.specRequireInterop(require('bar')); module.exports = exports = {}; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/actual.js new file mode 100644 index 000000000000..b50c286043a4 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/actual.js @@ -0,0 +1,3 @@ +import { pick } from 'module'; + +export default pick() \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/expected.js new file mode 100644 index 000000000000..290cb9600aae --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/expected.js @@ -0,0 +1,9 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +const _module = babelHelpers.specRequireInterop(require('module')); + +exports.default = (0, _module.pick)(); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js index a3e60ebba4cd..417861debc50 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js @@ -7,11 +7,11 @@ exports.module3 = exports.module2 = undefined; require('module1'); -var _module = babelHelpers.specRequireInterop(require('module2')); +const _module = babelHelpers.specRequireInterop(require('module2')); -var _module2 = babelHelpers.specRequireInterop(require('module3')); +const _module2 = babelHelpers.specRequireInterop(require('module3')); -var _module3 = babelHelpers.specRequireInterop(require('module4')); +const _module3 = babelHelpers.specRequireInterop(require('module4')); _module.default(_module2.default, _module3.default); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/actual.js new file mode 100644 index 000000000000..d5df151b640f --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/actual.js @@ -0,0 +1,7 @@ +import * as ns from 'module1' +import def from 'module2' +import { pick } from 'module3' + +export default ns +export { ns, def, pick } +export * from 'module4' \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js new file mode 100644 index 000000000000..b2c80d773501 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js @@ -0,0 +1,29 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.pick = exports.def = exports.ns = undefined; + +const _module = babelHelpers.specRequireInterop(require('module4')); + +Object.keys(_module).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _module[key]; + } + }); +}); + +const _module2 = babelHelpers.specRequireInterop(require('module1')); + +const _module3 = babelHelpers.specRequireInterop(require('module2')); + +const _module4 = babelHelpers.specRequireInterop(require('module3')); + +exports.default = _module2; +exports.ns = _module2; +exports.def = _module3.default; +exports.pick = _module4.pick; \ No newline at end of file From d8c8e8609fbff2f3fc191d486cc98d80abc60b71 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 18:59:19 +0900 Subject: [PATCH 056/103] Keep track of used import names; assert that they exist at import time --- packages/babel-helpers/src/helpers.js | 15 ++ .../src/index.js | 178 +++++++++++++++++- 2 files changed, 184 insertions(+), 9 deletions(-) diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index ab33858da99b..f728134fe796 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -442,6 +442,21 @@ helpers.specRequireInterop = template(` }) `); +helpers.specImportCheck = template(` + (function (module, moduleName, imports) { + if (!module.__esModule) throw new Error("Only ES modules can be checked"); + const realImports = Object.keys(module); + const invalid = imports.filter(function (i) { return realImports.indexOf(i) < 0; }); + if (invalid.length > 0) { + throw new Error( + "Unknown imports (" + + invalid.map(function (i) { return "'" + i + "'"; }).join(', ') + + ") used from module '" + moduleName + "'" + ); + } + }) +`); + helpers.newArrowCheck = template(` (function (innerThis, boundThis) { if (innerThis !== boundThis) { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index c2d65695adb7..183935649a9c 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -110,6 +110,7 @@ export default function () { const isModuleObj = t.buildMatchMemberExpression("module", true); const isExports = t.buildMatchMemberExpression("exports", true); + let commonjsExportsMasked = null; let reassignmentVisitor = { @@ -150,6 +151,29 @@ export default function () { const { object, property } = remap; path.replaceWith(t.JSXMemberExpression(t.JSXIdentifier(object.name), t.JSXIdentifier(property.name))); } else { + if (remap.nameSet) { + if (t.isMemberExpression(remap)) { + const name = t.isIdentifier(remap.property) + ? remap.property.name + : t.isStringLiteral(remap.property) + ? remap.property.value + : undefined; + if (name == null) { + throw new Error(`Unexpected ${remap.type}`); + } + remap.nameSet.add(name); + } else if (t.isIdentifier(remap)) { + if (t.isMemberExpression(path.parent) && !path.parent.computed) { + const name = t.isIdentifier(path.parent.property) + ? path.parent.property.name + : t.isStringLiteral(path.parent.property) + ? path.parent.property.value + : undefined; + + remap.nameSet.add(name); + } + } + } path.replaceWith(remap); } this.requeueInParent(path); @@ -302,7 +326,7 @@ export default function () { let requires = Object.create(null); - const addRequire = (source, spec, blockHoist) => { + const addRequire = (source, specImport, blockHoist) => { let cached = requires[source]; if (cached) return cached; @@ -326,10 +350,16 @@ export default function () { varDecl.loc = imports[source].loc; } + if (specImport) { + blockHoist = Math.max( + imports[source] && imports[source].maxBlockHoist || 0, + blockHoist || 0 + ); + } + if (typeof blockHoist === "number" && blockHoist > 0) { varDecl._blockHoist = blockHoist; } - topNodes.push(varDecl); return requires[source] = ref; @@ -359,7 +389,7 @@ export default function () { let key = path.node.source.value; let importsEntry = imports[key] || { specifiers: [], - maxBlockHoist: 0, + maxBlockHoist: specImport ? 2 : 0, loc: path.node.loc, }; @@ -522,7 +552,29 @@ export default function () { let nodes = []; let source = path.node.source; if (source) { - let ref = addRequire(source.value, spec, path.node._blockHoist); + let ref, importsEntry; + if (specImport) { + let key = path.node.source.value; + importsEntry = imports[key] || { + specifiers: [], + maxBlockHoist: 2, + loc: path.node.loc, + }; + importsEntry.reexports = importsEntry.reexports || new Map(), + + importsEntry.specifiers.push(...path.node.specifiers); + + if (typeof path.node._blockHoist === "number") { + importsEntry.maxBlockHoist = Math.max( + path.node._blockHoist, + importsEntry.maxBlockHoist + ); + } + + imports[key] = importsEntry; + } else { + ref = addRequire(source.value, specImport, path.node._blockHoist); + } for (let specifier of specifiers) { if (specifier.isExportNamespaceSpecifier()) { @@ -530,8 +582,8 @@ export default function () { } else if (specifier.isExportDefaultSpecifier()) { // todo } else if (specifier.isExportSpecifier()) { - if (spec) { - hoistedExports.set(specifier.node.exported.name, t.memberExpression(ref, specifier.node.local)); + if (specImport) { + importsEntry.reexports.set(specifier.node.exported.name, specifier.node.local); } else if (specifier.node.local.name === "default") { topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name), t.memberExpression(t.callExpression(this.addHelper("interopRequireDefault"), [ref]), specifier.node.local))); } else { @@ -573,12 +625,12 @@ export default function () { exportNode = specBuildNamespaceSpread({ EXPORTS: exportsObj, OWN_EXPORTS: ownExportsUid, - OBJECT: addRequire(path.node.source.value, spec, 3) + OBJECT: addRequire(path.node.source.value, specImport, 3) }); exportNode._blockHoist = 3; } else { exportNode = buildExportAll({ - OBJECT: addRequire(path.node.source.value, spec, path.node._blockHoist) + OBJECT: addRequire(path.node.source.value, specImport, path.node._blockHoist) }); } exportNode.loc = path.node.loc; @@ -587,20 +639,83 @@ export default function () { } } + const checkedImportMap = specImport && new Map(); for (let source in imports) { let {specifiers, maxBlockHoist} = imports[source]; if (specifiers.length) { - const uid = addRequire(source, spec, maxBlockHoist); + const uid = addRequire(source, specImport, maxBlockHoist); + + if (imports[source].reexports) { + if (!spec) { + for (const [name, local] of imports[source].reexports.entries()) { + if (name === "default") { + topNodes.push(buildExportsFrom(t.stringLiteral(name), t.memberExpression(t.callExpression(this.addHelper("interopRequireDefault"), [uid]), local))); + } else { + topNodes.push(buildExportsFrom(t.stringLiteral(name), t.memberExpression(uid, local))); + } + } + } else { + for (const [name, local] of imports[source].reexports.entries()) { + hoistedExports.set(name, t.memberExpression(uid, local)); + } + } + } if (specImport) { + if (!checkedImportMap.has(uid)) { + const nameSet = new Set( + Array.from(new Set(specifiers)) + .filter((specifier) => !t.isImportNamespaceSpecifier(specifier)) + .map((specifier) => { + if (t.isImportDefaultSpecifier(specifier)) { + return "default"; + } + if (t.isExportSpecifier(specifier)) { + return specifier.local.name; + } + return specifier.imported.name; + }) + ); + nameSet.source = source; + + checkedImportMap.set(uid, nameSet); + + if (nameSet.size > 0) { + nameSet.generated = Array.from(nameSet).map((name) => t.stringLiteral(name)); + const names = t.arrayExpression(nameSet.generated); + + const stmt = + t.expressionStatement( + t.callExpression( + this.addHelper("specImportCheck"), + [ + uid, + t.stringLiteral(nameSet.source), + names + ] + ) + ); + + stmt.loc = imports[source].loc; + stmt._blockHoist = Math.max(imports[source].maxBlockHoist, 2); + + topNodes.push(stmt); + } + } + for (const specifier of specifiers) { if (t.isImportNamespaceSpecifier(specifier)) { remaps[specifier.local.name] = uid; } else if (t.isImportDefaultSpecifier(specifier)) { remaps[specifier.local.name] = t.memberExpression(uid, t.identifier("default")); + } else if (specImport && t.isExportSpecifier(specifier)) { + // do nothing } else { remaps[specifier.local.name] = t.memberExpression(uid, t.cloneWithoutLoc(specifier.imported)); } + if (specImport && !t.isExportSpecifier(specifier)) { + remaps[specifier.local.name].nameSet = checkedImportMap.get(uid); + } } } else { let wildcard; @@ -665,6 +780,9 @@ export default function () { } else { // bare import let requireNode = buildRequire(t.stringLiteral(source)); + if (specImport) { + requireNode._blockHoist = 2; + } requireNode.loc = imports[source].loc; topNodes.push(requireNode); } @@ -739,6 +857,48 @@ export default function () { requeueInParent: (newPath) => path.requeue(newPath), }); + if (specImport && hasImports) { + const checks = Array.from(checkedImportMap.entries()) + .map(([uid, nameSet]) => { + if (nameSet.size < 1) return; + + if (nameSet.generated) { + if (nameSet.generated.length === nameSet.generated.size) return; + // nameSet.generated must be modified in-place + // to update the nodes of the pre-existing nameSet check + for (const name of nameSet) { + if (!nameSet.generated.some((existing) => existing.value === name)) { + nameSet.generated.push(t.stringLiteral(name)); + } + } + return; + } + + const names = t.arrayExpression(Array.from(nameSet).map((name) => t.stringLiteral(name))); + + const stmt = + t.expressionStatement( + t.callExpression( + this.addHelper("specImportCheck"), + [ + uid, + t.stringLiteral(nameSet.source), + names + ] + ) + ); + + stmt.loc = imports[nameSet.source].loc; + stmt._blockHoist = Math.max(imports[nameSet.source].maxBlockHoist, 1); + + return stmt; + }).filter(Boolean); + + if (checks.length > 0) { + path.pushContainer("body", checks); + } + } + if (spec && (commonjsExportsMasked.exports || commonjsExportsMasked.module)) { path.pushContainer("body", [ t.variableDeclaration("let", [ From c920f266dbf6d4180238f755a7ec6d1446f6d6d4 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 18:59:32 +0900 Subject: [PATCH 057/103] Update tests and fixtures --- .../test/fixtures/spec/exports/expected.js | 3 ++ .../fixtures/spec/import-simple/expected.js | 6 +++ .../test/fixtures/spec/import/expected.js | 9 +++++ .../test/fixtures/spec/reexports/actual.js | 4 +- .../test/fixtures/spec/reexports/expected.js | 22 +++++++++-- .../specImport/commonjs-no-shadow/expected.js | 3 ++ .../specImport/export-default/expected.js | 1 + .../specImport/import-and-export/expected.js | 6 +++ .../fixtures/specImport/reexport/actual.js | 6 ++- .../fixtures/specImport/reexport/expected.js | 37 +++++++++++++++---- 10 files changed, 84 insertions(+), 13 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js index 62390aa3d09d..49dba7c4e42a 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -52,6 +52,9 @@ Object.defineProperties(exports, { const _outside = babelHelpers.specRequireInterop(require("outside")); +babelHelpers.specImportCheck(_outside, "outside", ["anything"]); + + var def; function foo() { def = "export mutation"; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js index a2207ce5af09..56c8efaefc9f 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js @@ -8,8 +8,14 @@ const _c = babelHelpers.specRequireInterop(require('c')); const _d = babelHelpers.specRequireInterop(require('d')); +babelHelpers.specImportCheck(_d, 'd', ['default']); + const _e = babelHelpers.specRequireInterop(require('e')); +babelHelpers.specImportCheck(_e, 'e', ['name']); +babelHelpers.specImportCheck(_c, 'c', ['name']); + + _c.name; _d.default; _e.name; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js index 2b2633b12fe1..73c02875303a 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js @@ -4,16 +4,25 @@ const _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); const _outside = babelHelpers.specRequireInterop(require('./outside')); +babelHelpers.specImportCheck(_outside, './outside', ['default', 'obj']); + const _anywhere = babelHelpers.specRequireInterop(require('./anywhere')); +babelHelpers.specImportCheck(_anywhere, './anywhere', ['default']); + require('./empty'); require('./imperative'); const _iDontKnow = babelHelpers.specRequireInterop(require('./i-dont-know')); +babelHelpers.specImportCheck(_iDontKnow, './i-dont-know', ['default', 'what', 'who']); + const _naturally = babelHelpers.specRequireInterop(require('./naturally')); +babelHelpers.specImportCheck(_naturally, './naturally', ['default', 'naturally']); + + (0, _outside.default)(_outside.obj.key); (0, _anywhere.default)(_elsewhere); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/actual.js index 97b540e2e809..78e00fcd9f4d 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/actual.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/actual.js @@ -1,6 +1,8 @@ import * as namespace from './somewhere'; -import { stuff } from './elsewhere'; +export { stuff } from './elsewhere' + +import { stuff } from './somewhereElse'; export { namespace }; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index 96571bc6f667..6b78725db960 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -26,6 +26,14 @@ Object.defineProperties(exports, { default: { enumerable: true, + get() { + return _somewhereElse.stuff; + } + + }, + stuff: { + enumerable: true, + get() { return _elsewhere.stuff; } @@ -59,8 +67,16 @@ Object.keys(_iDontKnow).forEach(function (key) { }); (Object.freeze || Object)(exports); -const _because = babelHelpers.specRequireInterop(require('./because')); - const _somewhere = babelHelpers.specRequireInterop(require('./somewhere')); -const _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); \ No newline at end of file +const _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); + +babelHelpers.specImportCheck(_elsewhere, './elsewhere', ['stuff']); + +const _somewhereElse = babelHelpers.specRequireInterop(require('./somewhereElse')); + +babelHelpers.specImportCheck(_somewhereElse, './somewhereElse', ['stuff']); + +const _because = babelHelpers.specRequireInterop(require('./because')); + +babelHelpers.specImportCheck(_because, './because', ['default']); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js index 7b49a4b2a3f2..b63f73a1e786 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js @@ -6,4 +6,7 @@ Object.defineProperty(exports, "__esModule", { const _bar = babelHelpers.specRequireInterop(require('bar')); +babelHelpers.specImportCheck(_bar, 'bar', ['default']); + + module.exports = exports = {}; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/expected.js index 290cb9600aae..dee5129ff66b 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/expected.js @@ -6,4 +6,5 @@ Object.defineProperty(exports, "__esModule", { const _module = babelHelpers.specRequireInterop(require('module')); +babelHelpers.specImportCheck(_module, 'module', ['pick']); exports.default = (0, _module.pick)(); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js index 417861debc50..43acc9a1dcf5 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js @@ -11,8 +11,14 @@ const _module = babelHelpers.specRequireInterop(require('module2')); const _module2 = babelHelpers.specRequireInterop(require('module3')); +babelHelpers.specImportCheck(_module2, 'module3', ['default']); + const _module3 = babelHelpers.specRequireInterop(require('module4')); +babelHelpers.specImportCheck(_module3, 'module4', ['default']); +babelHelpers.specImportCheck(_module, 'module2', ['default']); + + _module.default(_module2.default, _module3.default); exports.module2 = _module; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/actual.js index d5df151b640f..04d951f02101 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/actual.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/actual.js @@ -4,4 +4,8 @@ import { pick } from 'module3' export default ns export { ns, def, pick } -export * from 'module4' \ No newline at end of file +export * from 'module4' + +export { foo, bar as baz } from 'module5' + +import 'module6' \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js index b2c80d773501..19fff6a4ff6e 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js @@ -3,7 +3,23 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.pick = exports.def = exports.ns = undefined; +exports.baz = exports.foo = exports.pick = exports.def = exports.ns = undefined; + +const _module2 = babelHelpers.specRequireInterop(require('module1')); + +const _module3 = babelHelpers.specRequireInterop(require('module2')); + +babelHelpers.specImportCheck(_module3, 'module2', ['default']); + +const _module4 = babelHelpers.specRequireInterop(require('module3')); + +babelHelpers.specImportCheck(_module4, 'module3', ['pick']); + +const _module5 = babelHelpers.specRequireInterop(require('module5')); + +babelHelpers.specImportCheck(_module5, 'module5', ['foo', 'bar']); + +require('module6'); const _module = babelHelpers.specRequireInterop(require('module4')); @@ -16,13 +32,18 @@ Object.keys(_module).forEach(function (key) { } }); }); - -const _module2 = babelHelpers.specRequireInterop(require('module1')); - -const _module3 = babelHelpers.specRequireInterop(require('module2')); - -const _module4 = babelHelpers.specRequireInterop(require('module3')); - +Object.defineProperty(exports, 'foo', { + enumerable: true, + get: function () { + return _module5.foo; + } +}); +Object.defineProperty(exports, 'baz', { + enumerable: true, + get: function () { + return _module5.bar; + } +}); exports.default = _module2; exports.ns = _module2; exports.def = _module3.default; From 03c6ef996e70d35cd766cd9b86b7d965be5f3697 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 18:59:39 +0900 Subject: [PATCH 058/103] Add missing before handlers --- .../test/spec-reexport.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js index c924a2026fc8..6dc40a68bd90 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js @@ -152,7 +152,11 @@ describe("spec reexport star with duplicates", function () { }); describe("SameValue", function () { - const exports = runner.transformAndRun("export * from 'aSame'\nexport * from 'bSame'"); + let exports; + + before(function () { + exports = runner.transformAndRun("export * from 'aSame'\nexport * from 'bSame'"); + }); it("has the correct value", function () { assert.strictEqual(exports.name, "name"); @@ -160,7 +164,11 @@ describe("spec reexport star with duplicates", function () { }); describe("shadowed reexport", function () { - const exports = runner.transformAndRun("export * from 'a'\nexport * from 'b'\nexport const name = 'foo'"); + let exports; + + before(function () { + exports = runner.transformAndRun("export * from 'a'\nexport * from 'b'\nexport const name = 'foo'"); + }); it("has the correct value", function () { assert.strictEqual(exports.name, "foo"); From 4d54fdc703631165b7d20c046b36b3c317de6cbb Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 19:11:25 +0900 Subject: [PATCH 059/103] Don't give the module name as argument to helper; would have prevented minification --- packages/babel-helpers/src/helpers.js | 10 ++++++---- .../src/index.js | 2 -- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index f728134fe796..31a1615bb5aa 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -443,16 +443,18 @@ helpers.specRequireInterop = template(` `); helpers.specImportCheck = template(` - (function (module, moduleName, imports) { + (function (module, imports) { if (!module.__esModule) throw new Error("Only ES modules can be checked"); const realImports = Object.keys(module); const invalid = imports.filter(function (i) { return realImports.indexOf(i) < 0; }); if (invalid.length > 0) { - throw new Error( - "Unknown imports (" + + const error = new Error( + "Unknown exports" + (invalid.length > 1 ? "s " : " ") + invalid.map(function (i) { return "'" + i + "'"; }).join(', ') + - ") used from module '" + moduleName + "'" + " imported" ); + error.module = module; + throw error; } }) `); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 183935649a9c..a3b2c863e842 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -690,7 +690,6 @@ export default function () { this.addHelper("specImportCheck"), [ uid, - t.stringLiteral(nameSet.source), names ] ) @@ -882,7 +881,6 @@ export default function () { this.addHelper("specImportCheck"), [ uid, - t.stringLiteral(nameSet.source), names ] ) From 76eea108f74a4a56d2145c332b3011c147f88917 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 19:11:46 +0900 Subject: [PATCH 060/103] Update README --- .../README.md | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md index e0114486344f..21c2ca8db813 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md @@ -71,12 +71,13 @@ The exported namespace is also not frozen and has an incorrect prototype. The `spec` option, when set to `true`, tries to generate code that is as close as possible to what is required by the ECMA262 spec without relying on `Proxy`. The exports will be frozen, and imports will always be treated -like ES modules. +like ES modules. All imported names will be checked, at import time, if +they exist in the exports of the imported module. Importing a commonjs module (say, the standard `fs` module) will always wrap it in an ES module that has a single `default` export. This means that some imports that work in non-`spec` mode, like `import { readFile } from 'fs'`, -will result in `undefined` in `spec` mode. +will result errors in `spec` mode. Note that exports, under this mode, always require runtime support for getters. It also is not possible to access or write to the commonjs @@ -91,7 +92,7 @@ import defaultImport from 'module2'; import * as namespace from 'module3'; import { pick } from 'module4'; -defaultImport(namespace, pick); +defaultImport(namespace.foo, pick); export { pick } export default function () {} @@ -122,13 +123,18 @@ let _default = { require('module1'); -const _module2 = babelHelpers._specInteropImport(require('module2')); +const _module2 = babelHelpers.specInteropImport(require('module2')); -const _module3 = babelHelpers._specInteropImport(require('module3')); +babelHelpers.specImportCheck(_module2, [ 'default' ]); -const _module4 = babelHelpers._specInteropImport(require('module4')); +const _module3 = babelHelpers.specInteropImport(require('module3')); -_module2.default(_module3, _module4.pick); +const _module4 = babelHelpers.specInteropImport(require('module4')); + +babelHelpers.specImportCheck(_module4, [ 'pick' ]); +babelHelpers.specImportCheck(_module3, [ 'foo' ]); + +_module2.default(_module3.foo, _module4.pick); ``` ## Options `specImport` @@ -137,8 +143,9 @@ This option enables only the half of `spec` mode that affects the imports, witho changing how exports are generated. This would allow the generation of code that may still be compatible with engines that do not support getters. -Note that the require helper does use `Object.defineProperty`, so when running on -an old engine that does not support it, a polyfill like `es5-sham` is still required. +Note that the require helpers do use `Object.keys` and `Object.defineProperty`, so +ES5 polyfills may still be required. When running on an old engine that does not support +`Object.defineProperty`, a polyfill to fake it like `es5-sham` is still required. This option is **ignored** if `spec` is enabled. Enabling `spec` implies that this option is also enabled. @@ -162,6 +169,8 @@ Object.defineProperty(exports, "__esModule", { var _module = babelHelpers.specRequireInterop(require('module')); +babelHelpers.specImportCheck(_module, [ 'pick' ]); + exports.default = (0, _module.pick)(); ``` From 8aa75b51289e20d345717501a1ae3238bdc1f003 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 19:27:49 +0900 Subject: [PATCH 061/103] Fix typo in helper --- packages/babel-helpers/src/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 31a1615bb5aa..cb7a369405da 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -449,7 +449,7 @@ helpers.specImportCheck = template(` const invalid = imports.filter(function (i) { return realImports.indexOf(i) < 0; }); if (invalid.length > 0) { const error = new Error( - "Unknown exports" + (invalid.length > 1 ? "s " : " ") + + "Unknown export" + (invalid.length > 1 ? "s " : " ") + invalid.map(function (i) { return "'" + i + "'"; }).join(', ') + " imported" ); From a8b094cd9f8d9cc44bd760571fa492f423dd732c Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 19:28:53 +0900 Subject: [PATCH 062/103] Add runtime tests for the import name checker --- .../test/spec-import-check.js | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js new file mode 100644 index 000000000000..0c777b39f73f --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js @@ -0,0 +1,70 @@ +"use strict"; + +const assert = require("assert"); +const helpers = require("./spec-test-helpers"); + +describe("spec import", function () { + const runner = new helpers.Runner({ + a: "export const a = 'a'", + b: "export const b = 'b'" + }); + runner.addToCache("cjs", { module: { exports: { cjs: true } } }); + + describe("error checking", function () { + it("throws when directly importing unknown name", function () { + assert.throws(function () { + runner.transformAndRun("import { b } from 'a'"); + }, "Unknown export 'b' imported"); + }); + + it("throws when importing renamed unknown name", function () { + assert.throws(function () { + runner.transformAndRun("import { b as a } from 'a'"); + }, "Unknown export 'b' imported"); + }); + + it("throws when using unknown name from namespace", function () { + assert.throws(function () { + runner.transformAndRun("import * as b from 'b'\nb.a"); + }, "Unknown export 'a' imported"); + }); + + it("throws when using multiple unknown names", function () { + assert.throws(function () { + runner.transformAndRun("import { a, b, c, d } from 'a'"); + }, "Unknown exports 'b', 'c', 'd' imported"); + }); + + it("throws when using default export of module without default export", function () { + assert.throws(function () { + runner.transformAndRun("import a from 'a'"); + }, "Unknown export 'default' imported"); + }); + + it("throws when namespace and name imports are combined", function () { + assert.throws(function () { + runner.transformAndRun("import * as a, { b } from 'a'\na.c"); + }, "Unknown exports 'b', 'c' imported"); + }); + + it("does not throw when only known imports are used", function () { + runner.transformAndRun("import * as a from 'a'\nimport { b } from 'b'\na.a"); + }); + }); + + describe("error checking with commonjs", function () { + it("throws when any name other than default is used", function () { + assert.throws(function () { + runner.transformAndRun("import { cjs } from 'cjs'"); + }, "Unknown export 'cjs' imported"); + }); + + it("does not throw when default import of commonjs module is used", function () { + runner.transformAndRun("import cjs from 'cjs'"); + }); + + it("does not throw when only default is used from namespace import of commonjs module", function () { + runner.transformAndRun("import * as ns from 'cjs'\nns.default"); + }); + }); +}); From 157ab451ac23ef5888717dfff9e31609bc5e421b Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 19:29:34 +0900 Subject: [PATCH 063/103] Update fixtures --- .../test/fixtures/spec/exports/expected.js | 2 +- .../test/fixtures/spec/import-simple/expected.js | 6 +++--- .../test/fixtures/spec/import/expected.js | 8 ++++---- .../test/fixtures/spec/reexports/expected.js | 6 +++--- .../fixtures/specImport/commonjs-no-shadow/expected.js | 2 +- .../test/fixtures/specImport/export-default/expected.js | 2 +- .../fixtures/specImport/import-and-export/expected.js | 6 +++--- .../test/fixtures/specImport/reexport/expected.js | 6 +++--- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js index 49dba7c4e42a..f7370da00c39 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/exports/expected.js @@ -52,7 +52,7 @@ Object.defineProperties(exports, { const _outside = babelHelpers.specRequireInterop(require("outside")); -babelHelpers.specImportCheck(_outside, "outside", ["anything"]); +babelHelpers.specImportCheck(_outside, ["anything"]); var def; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js index 56c8efaefc9f..386d7c9fd916 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js @@ -8,12 +8,12 @@ const _c = babelHelpers.specRequireInterop(require('c')); const _d = babelHelpers.specRequireInterop(require('d')); -babelHelpers.specImportCheck(_d, 'd', ['default']); +babelHelpers.specImportCheck(_d, ['default']); const _e = babelHelpers.specRequireInterop(require('e')); -babelHelpers.specImportCheck(_e, 'e', ['name']); -babelHelpers.specImportCheck(_c, 'c', ['name']); +babelHelpers.specImportCheck(_e, ['name']); +babelHelpers.specImportCheck(_c, ['name']); _c.name; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js index 73c02875303a..6e85d8f8b66f 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js @@ -4,11 +4,11 @@ const _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); const _outside = babelHelpers.specRequireInterop(require('./outside')); -babelHelpers.specImportCheck(_outside, './outside', ['default', 'obj']); +babelHelpers.specImportCheck(_outside, ['default', 'obj']); const _anywhere = babelHelpers.specRequireInterop(require('./anywhere')); -babelHelpers.specImportCheck(_anywhere, './anywhere', ['default']); +babelHelpers.specImportCheck(_anywhere, ['default']); require('./empty'); @@ -16,11 +16,11 @@ require('./imperative'); const _iDontKnow = babelHelpers.specRequireInterop(require('./i-dont-know')); -babelHelpers.specImportCheck(_iDontKnow, './i-dont-know', ['default', 'what', 'who']); +babelHelpers.specImportCheck(_iDontKnow, ['default', 'what', 'who']); const _naturally = babelHelpers.specRequireInterop(require('./naturally')); -babelHelpers.specImportCheck(_naturally, './naturally', ['default', 'naturally']); +babelHelpers.specImportCheck(_naturally, ['default', 'naturally']); (0, _outside.default)(_outside.obj.key); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index 6b78725db960..d2e82b2f1836 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -71,12 +71,12 @@ const _somewhere = babelHelpers.specRequireInterop(require('./somewhere')); const _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); -babelHelpers.specImportCheck(_elsewhere, './elsewhere', ['stuff']); +babelHelpers.specImportCheck(_elsewhere, ['stuff']); const _somewhereElse = babelHelpers.specRequireInterop(require('./somewhereElse')); -babelHelpers.specImportCheck(_somewhereElse, './somewhereElse', ['stuff']); +babelHelpers.specImportCheck(_somewhereElse, ['stuff']); const _because = babelHelpers.specRequireInterop(require('./because')); -babelHelpers.specImportCheck(_because, './because', ['default']); \ No newline at end of file +babelHelpers.specImportCheck(_because, ['default']); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js index b63f73a1e786..47f7ba1314e6 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/commonjs-no-shadow/expected.js @@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { const _bar = babelHelpers.specRequireInterop(require('bar')); -babelHelpers.specImportCheck(_bar, 'bar', ['default']); +babelHelpers.specImportCheck(_bar, ['default']); module.exports = exports = {}; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/expected.js index dee5129ff66b..a3a5a635d46d 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/export-default/expected.js @@ -6,5 +6,5 @@ Object.defineProperty(exports, "__esModule", { const _module = babelHelpers.specRequireInterop(require('module')); -babelHelpers.specImportCheck(_module, 'module', ['pick']); +babelHelpers.specImportCheck(_module, ['pick']); exports.default = (0, _module.pick)(); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js index 43acc9a1dcf5..19fe13456355 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js @@ -11,12 +11,12 @@ const _module = babelHelpers.specRequireInterop(require('module2')); const _module2 = babelHelpers.specRequireInterop(require('module3')); -babelHelpers.specImportCheck(_module2, 'module3', ['default']); +babelHelpers.specImportCheck(_module2, ['default']); const _module3 = babelHelpers.specRequireInterop(require('module4')); -babelHelpers.specImportCheck(_module3, 'module4', ['default']); -babelHelpers.specImportCheck(_module, 'module2', ['default']); +babelHelpers.specImportCheck(_module3, ['default']); +babelHelpers.specImportCheck(_module, ['default']); _module.default(_module2.default, _module3.default); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js index 19fff6a4ff6e..2abcf7d0eed1 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js @@ -9,15 +9,15 @@ const _module2 = babelHelpers.specRequireInterop(require('module1')); const _module3 = babelHelpers.specRequireInterop(require('module2')); -babelHelpers.specImportCheck(_module3, 'module2', ['default']); +babelHelpers.specImportCheck(_module3, ['default']); const _module4 = babelHelpers.specRequireInterop(require('module3')); -babelHelpers.specImportCheck(_module4, 'module3', ['pick']); +babelHelpers.specImportCheck(_module4, ['pick']); const _module5 = babelHelpers.specRequireInterop(require('module5')); -babelHelpers.specImportCheck(_module5, 'module5', ['foo', 'bar']); +babelHelpers.specImportCheck(_module5, ['foo', 'bar']); require('module6'); From 79a87ca6ba293a14956075ced2816a6fe215313f Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 19:34:26 +0900 Subject: [PATCH 064/103] Copy the _blockHoist in non-spec specImport mode, just to make sure --- .../src/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index a3b2c863e842..477ce749fe9c 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -629,9 +629,13 @@ export default function () { }); exportNode._blockHoist = 3; } else { + const ref = addRequire(path.node.source.value, specImport, path.node._blockHoist); exportNode = buildExportAll({ - OBJECT: addRequire(path.node.source.value, specImport, path.node._blockHoist) + OBJECT: ref }); + if (specImport) { + exportNode._blockHoist = ref._blockHoist; + } } exportNode.loc = path.node.loc; topNodes.push(exportNode); From 0a6ead9d6c0fc16cf022bb82440142f2907e4725 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 19:42:24 +0900 Subject: [PATCH 065/103] Helpers can't use const --- packages/babel-helpers/src/helpers.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index cb7a369405da..ed3226c6d14d 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -445,10 +445,10 @@ helpers.specRequireInterop = template(` helpers.specImportCheck = template(` (function (module, imports) { if (!module.__esModule) throw new Error("Only ES modules can be checked"); - const realImports = Object.keys(module); - const invalid = imports.filter(function (i) { return realImports.indexOf(i) < 0; }); + var realImports = Object.keys(module); + var invalid = imports.filter(function (i) { return realImports.indexOf(i) < 0; }); if (invalid.length > 0) { - const error = new Error( + var error = new Error( "Unknown export" + (invalid.length > 1 ? "s " : " ") + invalid.map(function (i) { return "'" + i + "'"; }).join(', ') + " imported" From b2cb5294e7b00ed1aa44c4a79b98d1848b2e58f7 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 19:55:46 +0900 Subject: [PATCH 066/103] Add a section about star reexports to the README --- .../README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md index 21c2ca8db813..8cb5bfaebfc5 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md @@ -198,4 +198,15 @@ var foo = exports.foo = 5; exports.__esModule = true; ``` -The `loose` option is **ignored** if used in combination with `spec`. \ No newline at end of file +The `loose` option is **ignored** if used in combination with `spec`. + +## Caveats + +Star reexports (`export * from 'module'`) always run before other imports or +reexports, as this module's exports must be known before other modules execute. +That is, they behave as if there was an `import 'module'` that runs before any +of the other imports. + +This is particularly hard to avoid in the `spec` / `specImport` mode, as the +exports must be frozen before importing other modules. Star reexports are the +only exception. From 5eb912f5cfa2ee471c86a77ae76e0c2f4b360862 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 20:05:35 +0900 Subject: [PATCH 067/103] Add the README snippets as fixtures; use (reformatted) output in the README --- .../README.md | 44 ++++++++------- .../test/fixtures/spec/readme/actual.js | 9 ++++ .../test/fixtures/spec/readme/expected.js | 54 +++++++++++++++++++ .../test/fixtures/specImport/readme/actual.js | 3 ++ .../fixtures/specImport/readme/expected.js | 10 ++++ 5 files changed, 100 insertions(+), 20 deletions(-) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/readme/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/readme/expected.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md index 8cb5bfaebfc5..12375e6e47c5 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md @@ -104,17 +104,21 @@ export default function () {} 'use strict'; const exports = module.exports = Object.create ? Object.create(null, { - __esModule: { value: true } -}) : { __esModule: true }; -Object.defineProperties(exports, { - default: { - enumerable: true, - get() { return _default; } - }, - pick: { - enumerable: true, - get() { return _module4.pick; } + __esModule: { + value: true } +}) : { + __esModule: true +}; +if (typeof Symbol === "function" && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { + value: "Module" + }); +} + +Object.defineProperties(exports, { + pick: { enumerable: true, get() { return _module4.pick; } }, + default: { enumerable: true, get() { return _default; } } }); let _default = { default: function () {} @@ -123,18 +127,19 @@ let _default = { require('module1'); -const _module2 = babelHelpers.specInteropImport(require('module2')); +const _module2 = babelHelpers.specRequireInterop(require('module2')); + +babelHelpers.specImportCheck(_module2, ['default']); -babelHelpers.specImportCheck(_module2, [ 'default' ]); +const _module3 = babelHelpers.specRequireInterop(require('module3')); -const _module3 = babelHelpers.specInteropImport(require('module3')); +const _module4 = babelHelpers.specRequireInterop(require('module4')); -const _module4 = babelHelpers.specInteropImport(require('module4')); +babelHelpers.specImportCheck(_module4, ['pick']); +babelHelpers.specImportCheck(_module3, ['foo']); -babelHelpers.specImportCheck(_module4, [ 'pick' ]); -babelHelpers.specImportCheck(_module3, [ 'foo' ]); -_module2.default(_module3.foo, _module4.pick); +(0, _module2.default)(_module3.foo, _module4.pick); ``` ## Options `specImport` @@ -167,10 +172,9 @@ Object.defineProperty(exports, "__esModule", { value: true }); -var _module = babelHelpers.specRequireInterop(require('module')); - -babelHelpers.specImportCheck(_module, [ 'pick' ]); +const _module = babelHelpers.specRequireInterop(require('module')); +babelHelpers.specImportCheck(_module, ['pick']); exports.default = (0, _module.pick)(); ``` diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/actual.js new file mode 100644 index 000000000000..7da03f672c52 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/actual.js @@ -0,0 +1,9 @@ +import 'module1'; +import defaultImport from 'module2'; +import * as namespace from 'module3'; +import { pick } from 'module4'; + +defaultImport(namespace.foo, pick); + +export { pick } +export default function () {} \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js new file mode 100644 index 000000000000..7d906d6c96ac --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js @@ -0,0 +1,54 @@ +'use strict'; + +const exports = module.exports = Object.create ? Object.create(null, { + __esModule: { + value: true + } +}) : { + __esModule: true +}; + +if (typeof Symbol === "function" && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { + value: "Module" + }); +} + +Object.defineProperties(exports, { + pick: { + enumerable: true, + + get() { + return _module3.pick; + } + + }, + default: { + enumerable: true, + + get() { + return _default; + } + + } +}); +let _default = { + default: function () {} +}.default; +(Object.freeze || Object)(exports); + +require('module1'); + +const _module = babelHelpers.specRequireInterop(require('module2')); + +babelHelpers.specImportCheck(_module, ['default']); + +const _module2 = babelHelpers.specRequireInterop(require('module3')); + +const _module3 = babelHelpers.specRequireInterop(require('module4')); + +babelHelpers.specImportCheck(_module3, ['pick']); +babelHelpers.specImportCheck(_module2, ['foo']); + + +(0, _module.default)(_module2.foo, _module3.pick); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/readme/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/readme/actual.js new file mode 100644 index 000000000000..03d53e791329 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/readme/actual.js @@ -0,0 +1,3 @@ +import { pick } from 'module' + +export default pick() \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/readme/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/readme/expected.js new file mode 100644 index 000000000000..a3a5a635d46d --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/readme/expected.js @@ -0,0 +1,10 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +const _module = babelHelpers.specRequireInterop(require('module')); + +babelHelpers.specImportCheck(_module, ['pick']); +exports.default = (0, _module.pick)(); \ No newline at end of file From 52f9b5b2710e75754944fce0a810fc0b50bbedaa Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 20:11:26 +0900 Subject: [PATCH 068/103] Give a better example for the options :blush: --- .../babel-plugin-transform-es2015-modules-commonjs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md index 12375e6e47c5..25ae2f39a536 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md @@ -42,7 +42,7 @@ npm install --save-dev babel-plugin-transform-es2015-modules-commonjs { "plugins": [ ["transform-es2015-modules-commonjs", { - "allowTopLevelThis": true + "spec": true }] ] } From 60c0e9c73c58dadd127df28e78a6d19b0d7adf48 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Sun, 11 Dec 2016 20:17:45 +0900 Subject: [PATCH 069/103] Add note about TDZ --- .../babel-plugin-transform-es2015-modules-commonjs/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md index 25ae2f39a536..393b65dec433 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md @@ -72,7 +72,8 @@ The `spec` option, when set to `true`, tries to generate code that is as close as possible to what is required by the ECMA262 spec without relying on `Proxy`. The exports will be frozen, and imports will always be treated like ES modules. All imported names will be checked, at import time, if -they exist in the exports of the imported module. +they exist in the exports of the imported module. If `let` and `const` are +not transformed to `var`, the exports will also implement TDZ. Importing a commonjs module (say, the standard `fs` module) will always wrap it in an ES module that has a single `default` export. This means that From 79941d842d090566e8a426e7d70132cebceebbc1 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 07:17:05 +0900 Subject: [PATCH 070/103] Use faster filter, use JSON.stringify for the list of invalid imports --- packages/babel-helpers/src/helpers.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index ed3226c6d14d..8c948c6cb7a4 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -445,12 +445,11 @@ helpers.specRequireInterop = template(` helpers.specImportCheck = template(` (function (module, imports) { if (!module.__esModule) throw new Error("Only ES modules can be checked"); - var realImports = Object.keys(module); - var invalid = imports.filter(function (i) { return realImports.indexOf(i) < 0; }); + var invalid = imports.filter(function (i) { var d = Object.getOwnPropertyDescriptor(module, i); return !d || !d.enumerable; }); if (invalid.length > 0) { var error = new Error( "Unknown export" + (invalid.length > 1 ? "s " : " ") + - invalid.map(function (i) { return "'" + i + "'"; }).join(', ') + + JSON.stringify(invalid) + " imported" ); error.module = module; From a95a651b8f403bd09945b0a55c74dddc1315ff05 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 07:24:47 +0900 Subject: [PATCH 071/103] Update tests, correct invalid second argument --- .../test/spec-import-check.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js index 0c777b39f73f..29323b6302f0 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js @@ -14,31 +14,31 @@ describe("spec import", function () { it("throws when directly importing unknown name", function () { assert.throws(function () { runner.transformAndRun("import { b } from 'a'"); - }, "Unknown export 'b' imported"); + }, /Unknown export \["b"] imported$/); }); it("throws when importing renamed unknown name", function () { assert.throws(function () { runner.transformAndRun("import { b as a } from 'a'"); - }, "Unknown export 'b' imported"); + }, /Unknown export \["b"] imported$/); }); it("throws when using unknown name from namespace", function () { assert.throws(function () { runner.transformAndRun("import * as b from 'b'\nb.a"); - }, "Unknown export 'a' imported"); + }, /Unknown export \["a"] imported$/); }); it("throws when using multiple unknown names", function () { assert.throws(function () { runner.transformAndRun("import { a, b, c, d } from 'a'"); - }, "Unknown exports 'b', 'c', 'd' imported"); + }, /Unknown exports \["b","c","d"] imported$/); }); it("throws when using default export of module without default export", function () { assert.throws(function () { runner.transformAndRun("import a from 'a'"); - }, "Unknown export 'default' imported"); + }, /Unknown export \["default"] imported$/); }); it("throws when namespace and name imports are combined", function () { From a77935c69e6270a57d5a4a017f0b17eb9dd81cd7 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 07:28:12 +0900 Subject: [PATCH 072/103] Fix incorrect second argument to assert.throws in tests --- .../test/spec-export.js | 12 ++++++------ .../test/spec-import-check.js | 4 ++-- .../test/spec-reexport.js | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js index f14059dcbd3b..bca03e5af020 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-export.js @@ -240,31 +240,31 @@ describe("spec export", function () { it("throws when generating duplicate default exports", function () { assert.throws(function () { runner.transformAndRun("export default class {}\nexport default class {}"); - }); + }, SyntaxError); }); it("throws when generating duplicate default export via renaming", function () { assert.throws(function () { runner.transformAndRun("var foo\nexport default foo\nexport { foo as default }"); - }); + }, SyntaxError); }); it("throws when generating duplicate exports in the same specifier", function () { assert.throws(function () { runner.transformAndRun("export var foo, foo"); - }); + }, SyntaxError); }); it("throws when generating duplicate named exports in the same specifier", function () { assert.throws(function () { runner.transformAndRun("const foo = 'foo'\nexport { foo, foo }"); - }); + }, SyntaxError); }); it("throws when generating duplicate renamed exports", function () { assert.throws(function () { runner.transformAndRun("const foo = 'foo'\nconst bar = 'bar'\nexport { foo, bar as foo }"); - }); + }, SyntaxError); }); // babel extension; check for the flag used to distinguish @@ -272,7 +272,7 @@ describe("spec export", function () { it("throws when attempting to export __esModule", function () { assert.throws(function () { runner.transformAndRun("const __esModule = false\nexport { __esModule }"); - }); + }, SyntaxError); }); }); }); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js index 29323b6302f0..0d29513874f3 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js @@ -43,8 +43,8 @@ describe("spec import", function () { it("throws when namespace and name imports are combined", function () { assert.throws(function () { - runner.transformAndRun("import * as a, { b } from 'a'\na.c"); - }, "Unknown exports 'b', 'c' imported"); + runner.transformAndRun("import * as a from 'a'\nimport { b } from 'a'\na.c"); + }, /Unknown exports \["b","c"] imported$/); }); it("does not throw when only known imports are used", function () { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js index 6dc40a68bd90..50dba486d498 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-reexport.js @@ -148,7 +148,7 @@ describe("spec reexport star with duplicates", function () { it("throws when not shadowed and duplicate is not SameValue", function () { assert.throws(function () { runner.transformAndRun("export * from 'a'\nexport * from 'b'"); - }, "Cannot redefine property: name"); + }, /Cannot redefine property: name$/); }); describe("SameValue", function () { From 6dbd22897146d9100a24714fceb0e63681e484bb Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 08:26:49 +0900 Subject: [PATCH 073/103] Add specNamespaceGet helper, use when doing computed access to namespace --- packages/babel-helpers/src/helpers.js | 18 +++++++++++++ .../src/index.js | 27 ++++++++++++++----- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 8c948c6cb7a4..7065d31fb2ff 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -458,6 +458,24 @@ helpers.specImportCheck = template(` }) `); +// The name && typeof Symbol === "function" && ... line is from helpers.typeof, which may be needed +// when polyfilled. Helpers might not be transformed when using external-helpers, so can't rely on +// the typeof being augmented when targeting ES5. +helpers.specNamespaceGet = template(` + (function (module, name) { + if (!module.__esModule) throw new Error("Only ES modules can be checked"); + if ( + typeof name === "symbol" || + name && typeof Symbol === "function" && name.constructor === Symbol && name !== Symbol.prototype + ) { + return module[name]; + } + var d = Object.getOwnPropertyDescriptor(module, name); + if (!d || !d.enumerable) throw new Error("Unknown export " + JSON.stringify(name) + " imported"); + return module[name]; + }) +`); + helpers.newArrowCheck = template(` (function (innerThis, boundThis) { if (innerThis !== boundThis) { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 477ce749fe9c..b2c58236e31d 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -163,15 +163,29 @@ export default function () { } remap.nameSet.add(name); } else if (t.isIdentifier(remap)) { - if (t.isMemberExpression(path.parent) && !path.parent.computed) { - const name = t.isIdentifier(path.parent.property) - ? path.parent.property.name - : t.isStringLiteral(path.parent.property) - ? path.parent.property.value + if (t.isMemberExpression(path.parent)) { + const { property, computed } = path.parent; + + const name = !computed && t.isIdentifier(property) + ? property.name + : t.isStringLiteral(property) + ? property.value : undefined; - remap.nameSet.add(name); + if (name !== undefined) { + remap.nameSet.add(name); + } else { + path.parentPath.replaceWith( + t.callExpression( + this.addHelper("specNamespaceGet"), + [ remap, property ] + ) + ); + return; + } } + } else { + throw new Error(`Unexpected ${remap.type}`); } } path.replaceWith(remap); @@ -857,6 +871,7 @@ export default function () { scope, exports, opts: state.opts, + addHelper: this.addHelper.bind(this), requeueInParent: (newPath) => path.requeue(newPath), }); From 789aeae70431c9a39360f09bd97819616bb5107c Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 08:28:22 +0900 Subject: [PATCH 074/103] Add fixture and runtime tests --- .../fixtures/spec/namespace-get/actual.js | 9 +++ .../fixtures/spec/namespace-get/expected.js | 14 +++++ .../test/spec-import-check.js | 58 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/namespace-get/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/namespace-get/expected.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/namespace-get/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/namespace-get/actual.js new file mode 100644 index 000000000000..2ea7e0a33e24 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/namespace-get/actual.js @@ -0,0 +1,9 @@ +import * as a from 'a' + +const name = 'a' + +const ident = a.a +const literal = a['a'] +const computed = a[name] +const recursive = a[a[name]] +const symbol = a[Symbol.toStringTag] \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/namespace-get/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/namespace-get/expected.js new file mode 100644 index 000000000000..b1970e9da86e --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/namespace-get/expected.js @@ -0,0 +1,14 @@ +'use strict'; + +const _a = babelHelpers.specRequireInterop(require('a')); + +babelHelpers.specImportCheck(_a, ['a']); + + +const name = 'a'; + +const ident = _a.a; +const literal = _a['a']; +const computed = babelHelpers.specNamespaceGet(_a, name); +const recursive = babelHelpers.specNamespaceGet(_a, babelHelpers.specNamespaceGet(_a, name)); +const symbol = babelHelpers.specNamespaceGet(_a, Symbol.toStringTag); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js index 0d29513874f3..fecf5de8b9b3 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js @@ -47,9 +47,67 @@ describe("spec import", function () { }, /Unknown exports \["b","c"] imported$/); }); + it("throws when using indexed access with constant string", function () { + assert.throws(function () { + runner.transformAndRun("import * as a from 'a'; a['b']"); + }, /Unknown export \["b"] imported$/); + }); + + it("throws when using indexed access with null", function () { + assert.throws(function () { + runner.transformAndRun("import * as a from 'a'; a[null]"); + }, /Unknown export null imported$/); + }); + + it("throws when using indexed access with variable", function () { + assert.throws(function () { + exports = runner.transformAndRun("import * as a from 'a'; const name = 'b'; a[name]"); + }, /Unknown export "b" imported$/); + }); + it("does not throw when only known imports are used", function () { runner.transformAndRun("import * as a from 'a'\nimport { b } from 'b'\na.a"); }); + + describe("with variable", function () { + runner.addModule("variable", "import * as a from 'a'\nconst name = 'a'\nexport const simple = a[name]\nexport const recursive = a[a[name]]"); + + it("does not throw", function () { + runner.getExportsOf("variable"); + }); + + describe("has the correct result", function () { + const exports = runner.getExportsOf("variable"); + + it("simple", function () { + assert.strictEqual(exports.simple, "a"); + }); + + it("recursive", function () { + assert.strictEqual(exports.recursive, "a"); + }); + }); + }); + + describe("with Symbol", function () { + runner.addModule("symbol", "import * as a from 'a'\nexport default a[Symbol.toStringTag]"); + if (!helpers.hasToStringTag()) { + this.skip(); + } + + it("does not throw", function () { + runner.getExportsOf("symbol"); + }); + + it("has the correct result", function () { + const exports = runner.getExportsOf("symbol"); + assert.strictEqual(exports.default, "Module"); + }); + + it("does not throw with unknown symbols either", function () { + runner.transformAndRun("import * as a from 'a'\na[Symbol('foo')]"); + }); + }); }); describe("error checking with commonjs", function () { From 5f6206199d5228e8328c02162c29fd3773364598 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 08:41:34 +0900 Subject: [PATCH 075/103] Add test to verify __esModule is considered an unknown export --- .../test/spec-import-check.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js index fecf5de8b9b3..9eb387639d80 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js @@ -47,6 +47,13 @@ describe("spec import", function () { }, /Unknown exports \["b","c"] imported$/); }); + it("throws when attempting to import __esModule", function () { + // This one unfortunately won't work with { spec: false, specImport: true, loose: true } :cry: + assert.throws(function () { + runner.transformAndRun("import { __esModule } from 'b'"); + }, /Unknown export \["__esModule"] imported$/); + }); + it("throws when using indexed access with constant string", function () { assert.throws(function () { runner.transformAndRun("import * as a from 'a'; a['b']"); From 3b7f44351e2509762135cabff24f9506cf20c997 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 08:48:47 +0900 Subject: [PATCH 076/103] Add test to document the namespace get wrapping can't be perfect --- .../test/spec-import-check.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js index 9eb387639d80..7fb64b5885f7 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js @@ -72,6 +72,14 @@ describe("spec import", function () { }, /Unknown export "b" imported$/); }); + it("throws when accessing unknown import when aliased", function () { + this.skip("would require a Proxy-based implementation to work"); + + assert.throws(function () { + runner.transformAndRun("import * as a from 'a'; function get (ns, key) { return ns[key]; }; get(a, 'b')"); + }, /Unknown export "b" imported$/); + }); + it("does not throw when only known imports are used", function () { runner.transformAndRun("import * as a from 'a'\nimport { b } from 'b'\na.a"); }); From c6aec5f25831950004fa95edf59c331ac770bf4a Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 10:17:28 +0900 Subject: [PATCH 077/103] Add non-spec test to verify export * from ordering behavior --- .../fixtures/interop/export-from-8/actual.js | 3 ++ .../interop/export-from-8/expected.js | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-8/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-8/expected.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-8/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-8/actual.js new file mode 100644 index 000000000000..c7dddff6227d --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-8/actual.js @@ -0,0 +1,3 @@ +export * from 'a' +import 'b' +export * from 'c' \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-8/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-8/expected.js new file mode 100644 index 000000000000..d007f4c2829d --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-8/expected.js @@ -0,0 +1,31 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _a = require('a'); + +Object.keys(_a).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _a[key]; + } + }); +}); + +var _c = require('c'); + +Object.keys(_c).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _c[key]; + } + }); +}); + +require('b'); \ No newline at end of file From d212a375c88fd38cb46b81a9e84c9e805fe1756b Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 10:27:52 +0900 Subject: [PATCH 078/103] Remove _blockHoist hack for imports; just remove check later if it's empty --- .../src/index.js | 143 +++++++++--------- 1 file changed, 69 insertions(+), 74 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index b2c58236e31d..04cd8a6e2a5a 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -403,7 +403,7 @@ export default function () { let key = path.node.source.value; let importsEntry = imports[key] || { specifiers: [], - maxBlockHoist: specImport ? 2 : 0, + maxBlockHoist: 0, loc: path.node.loc, }; @@ -571,7 +571,7 @@ export default function () { let key = path.node.source.value; importsEntry = imports[key] || { specifiers: [], - maxBlockHoist: 2, + maxBlockHoist: 0, loc: path.node.loc, }; importsEntry.reexports = importsEntry.reexports || new Map(), @@ -663,22 +663,6 @@ export default function () { if (specifiers.length) { const uid = addRequire(source, specImport, maxBlockHoist); - if (imports[source].reexports) { - if (!spec) { - for (const [name, local] of imports[source].reexports.entries()) { - if (name === "default") { - topNodes.push(buildExportsFrom(t.stringLiteral(name), t.memberExpression(t.callExpression(this.addHelper("interopRequireDefault"), [uid]), local))); - } else { - topNodes.push(buildExportsFrom(t.stringLiteral(name), t.memberExpression(uid, local))); - } - } - } else { - for (const [name, local] of imports[source].reexports.entries()) { - hoistedExports.set(name, t.memberExpression(uid, local)); - } - } - } - if (specImport) { if (!checkedImportMap.has(uid)) { const nameSet = new Set( @@ -696,30 +680,62 @@ export default function () { ); nameSet.source = source; - checkedImportMap.set(uid, nameSet); + const names = Array.from(nameSet).map((name) => t.stringLiteral(name)); + const namesExpr = t.arrayExpression(names); + + const node = + t.expressionStatement( + t.callExpression( + this.addHelper("specImportCheck"), + [ + uid, + namesExpr + ] + ) + ); - if (nameSet.size > 0) { - nameSet.generated = Array.from(nameSet).map((name) => t.stringLiteral(name)); - const names = t.arrayExpression(nameSet.generated); + node.loc = imports[source].loc; - const stmt = - t.expressionStatement( - t.callExpression( - this.addHelper("specImportCheck"), - [ - uid, - names - ] - ) - ); + checkedImportMap.set(uid, { + nameSet, + node, + updateNode: () => { + if (nameSet.size === names.length) return; + + const current = new Set(names.map((name) => name.value)); + + for (const name of nameSet) { + if (!current.has(name)) { + names.push(t.stringLiteral(name)); + } + } + }, + isEmpty: () => { + return names.length < 1; + } + }); - stmt.loc = imports[source].loc; - stmt._blockHoist = Math.max(imports[source].maxBlockHoist, 2); + topNodes.push(node); + } + } - topNodes.push(stmt); + if (imports[source].reexports) { + if (!spec) { + for (const [name, local] of imports[source].reexports.entries()) { + if (name === "default") { + topNodes.push(buildExportsFrom(t.stringLiteral(name), t.memberExpression(t.callExpression(this.addHelper("interopRequireDefault"), [uid]), local))); + } else { + topNodes.push(buildExportsFrom(t.stringLiteral(name), t.memberExpression(uid, local))); + } + } + } else { + for (const [name, local] of imports[source].reexports.entries()) { + hoistedExports.set(name, t.memberExpression(uid, local)); } } + } + if (specImport) { for (const specifier of specifiers) { if (t.isImportNamespaceSpecifier(specifier)) { remaps[specifier.local.name] = uid; @@ -731,7 +747,7 @@ export default function () { remaps[specifier.local.name] = t.memberExpression(uid, t.cloneWithoutLoc(specifier.imported)); } if (specImport && !t.isExportSpecifier(specifier)) { - remaps[specifier.local.name].nameSet = checkedImportMap.get(uid); + remaps[specifier.local.name].nameSet = checkedImportMap.get(uid).nameSet; } } } else { @@ -797,9 +813,6 @@ export default function () { } else { // bare import let requireNode = buildRequire(t.stringLiteral(source)); - if (specImport) { - requireNode._blockHoist = 2; - } requireNode.loc = imports[source].loc; topNodes.push(requireNode); } @@ -876,43 +889,25 @@ export default function () { }); if (specImport && hasImports) { - const checks = Array.from(checkedImportMap.entries()) - .map(([uid, nameSet]) => { - if (nameSet.size < 1) return; - - if (nameSet.generated) { - if (nameSet.generated.length === nameSet.generated.size) return; - // nameSet.generated must be modified in-place - // to update the nodes of the pre-existing nameSet check - for (const name of nameSet) { - if (!nameSet.generated.some((existing) => existing.value === name)) { - nameSet.generated.push(t.stringLiteral(name)); - } - } - return; - } - - const names = t.arrayExpression(Array.from(nameSet).map((name) => t.stringLiteral(name))); - - const stmt = - t.expressionStatement( - t.callExpression( - this.addHelper("specImportCheck"), - [ - uid, - names - ] - ) - ); - - stmt.loc = imports[nameSet.source].loc; - stmt._blockHoist = Math.max(imports[nameSet.source].maxBlockHoist, 1); + const toRemove = new Set(); + for (const check of checkedImportMap.values()) { + check.updateNode(); - return stmt; - }).filter(Boolean); + if (check.isEmpty()) { + toRemove.add(check.node); + } + } - if (checks.length > 0) { - path.pushContainer("body", checks); + if (toRemove.size) { + path.traverse({ + ExpressionStatement (path) { + if (toRemove.has(path.node)) { + path.remove(); + return; + } + path.skip(); + } + }); } } From 77ae14188b71577a77640fe73ad1d6a3299a9fac Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 10:28:06 +0900 Subject: [PATCH 079/103] Update fixtures --- .../fixtures/spec/import-simple/expected.js | 3 +- .../test/fixtures/spec/readme/expected.js | 3 +- .../specImport/import-and-export/expected.js | 3 +- .../fixtures/specImport/reexport/expected.js | 29 ++++++++++--------- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js index 386d7c9fd916..5f03bec21402 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js @@ -6,6 +6,8 @@ require('b'); const _c = babelHelpers.specRequireInterop(require('c')); +babelHelpers.specImportCheck(_c, ['name']); + const _d = babelHelpers.specRequireInterop(require('d')); babelHelpers.specImportCheck(_d, ['default']); @@ -13,7 +15,6 @@ babelHelpers.specImportCheck(_d, ['default']); const _e = babelHelpers.specRequireInterop(require('e')); babelHelpers.specImportCheck(_e, ['name']); -babelHelpers.specImportCheck(_c, ['name']); _c.name; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js index 7d906d6c96ac..719eeff3ab04 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js @@ -45,10 +45,11 @@ babelHelpers.specImportCheck(_module, ['default']); const _module2 = babelHelpers.specRequireInterop(require('module3')); +babelHelpers.specImportCheck(_module2, ['foo']); + const _module3 = babelHelpers.specRequireInterop(require('module4')); babelHelpers.specImportCheck(_module3, ['pick']); -babelHelpers.specImportCheck(_module2, ['foo']); (0, _module.default)(_module2.foo, _module3.pick); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js index 19fe13456355..00405de335a1 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/import-and-export/expected.js @@ -9,6 +9,8 @@ require('module1'); const _module = babelHelpers.specRequireInterop(require('module2')); +babelHelpers.specImportCheck(_module, ['default']); + const _module2 = babelHelpers.specRequireInterop(require('module3')); babelHelpers.specImportCheck(_module2, ['default']); @@ -16,7 +18,6 @@ babelHelpers.specImportCheck(_module2, ['default']); const _module3 = babelHelpers.specRequireInterop(require('module4')); babelHelpers.specImportCheck(_module3, ['default']); -babelHelpers.specImportCheck(_module, ['default']); _module.default(_module2.default, _module3.default); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js index 2abcf7d0eed1..a3dc349ed815 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/reexport/expected.js @@ -5,6 +5,18 @@ Object.defineProperty(exports, "__esModule", { }); exports.baz = exports.foo = exports.pick = exports.def = exports.ns = undefined; +const _module = babelHelpers.specRequireInterop(require('module4')); + +Object.keys(_module).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + Object.defineProperty(exports, key, { + enumerable: true, + get: function () { + return _module[key]; + } + }); +}); + const _module2 = babelHelpers.specRequireInterop(require('module1')); const _module3 = babelHelpers.specRequireInterop(require('module2')); @@ -18,20 +30,6 @@ babelHelpers.specImportCheck(_module4, ['pick']); const _module5 = babelHelpers.specRequireInterop(require('module5')); babelHelpers.specImportCheck(_module5, ['foo', 'bar']); - -require('module6'); - -const _module = babelHelpers.specRequireInterop(require('module4')); - -Object.keys(_module).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function () { - return _module[key]; - } - }); -}); Object.defineProperty(exports, 'foo', { enumerable: true, get: function () { @@ -44,6 +42,9 @@ Object.defineProperty(exports, 'baz', { return _module5.bar; } }); + +require('module6'); + exports.default = _module2; exports.ns = _module2; exports.def = _module3.default; From fe39f70d76d30552d4d7664e49180271d2043a0a Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 10:29:22 +0900 Subject: [PATCH 080/103] Use names in readme snippet that generateUidIdentifier likes better --- .../test/fixtures/spec/readme/actual.js | 8 ++++---- .../test/fixtures/spec/readme/expected.js | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/actual.js index 7da03f672c52..03c18c5535b4 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/actual.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/actual.js @@ -1,7 +1,7 @@ -import 'module1'; -import defaultImport from 'module2'; -import * as namespace from 'module3'; -import { pick } from 'module4'; +import 'a'; +import defaultImport from 'b'; +import * as namespace from 'c'; +import { pick } from 'd'; defaultImport(namespace.foo, pick); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js index 719eeff3ab04..628f4f5986d8 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js @@ -19,7 +19,7 @@ Object.defineProperties(exports, { enumerable: true, get() { - return _module3.pick; + return _d.pick; } }, @@ -37,19 +37,19 @@ let _default = { }.default; (Object.freeze || Object)(exports); -require('module1'); +require('a'); -const _module = babelHelpers.specRequireInterop(require('module2')); +const _b = babelHelpers.specRequireInterop(require('b')); -babelHelpers.specImportCheck(_module, ['default']); +babelHelpers.specImportCheck(_b, ['default']); -const _module2 = babelHelpers.specRequireInterop(require('module3')); +const _c = babelHelpers.specRequireInterop(require('c')); -babelHelpers.specImportCheck(_module2, ['foo']); +babelHelpers.specImportCheck(_c, ['foo']); -const _module3 = babelHelpers.specRequireInterop(require('module4')); +const _d = babelHelpers.specRequireInterop(require('d')); -babelHelpers.specImportCheck(_module3, ['pick']); +babelHelpers.specImportCheck(_d, ['pick']); -(0, _module.default)(_module2.foo, _module3.pick); \ No newline at end of file +(0, _b.default)(_c.foo, _d.pick); \ No newline at end of file From d6ce6ab5758508981d8396907759522d856bb30e Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 10:33:07 +0900 Subject: [PATCH 081/103] Update README --- .../README.md | 34 ++++++++++--------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md index 393b65dec433..af35ee7d3007 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/README.md +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/README.md @@ -88,10 +88,10 @@ TDZ errors at runtime. ### Input ```javascript -import 'module1'; -import defaultImport from 'module2'; -import * as namespace from 'module3'; -import { pick } from 'module4'; +import 'a'; +import defaultImport from 'b'; +import * as namespace from 'c'; +import { pick } from 'd'; defaultImport(namespace.foo, pick); @@ -118,7 +118,7 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { } Object.defineProperties(exports, { - pick: { enumerable: true, get() { return _module4.pick; } }, + pick: { enumerable: true, get() { return _d.pick; } }, default: { enumerable: true, get() { return _default; } } }); let _default = { @@ -126,21 +126,22 @@ let _default = { }.default; (Object.freeze || Object)(exports); -require('module1'); +require('a'); -const _module2 = babelHelpers.specRequireInterop(require('module2')); +const _b = babelHelpers.specRequireInterop(require('b')); -babelHelpers.specImportCheck(_module2, ['default']); +babelHelpers.specImportCheck(_b, ['default']); -const _module3 = babelHelpers.specRequireInterop(require('module3')); +const _c = babelHelpers.specRequireInterop(require('c')); -const _module4 = babelHelpers.specRequireInterop(require('module4')); +babelHelpers.specImportCheck(_c, ['foo']); -babelHelpers.specImportCheck(_module4, ['pick']); -babelHelpers.specImportCheck(_module3, ['foo']); +const _d = babelHelpers.specRequireInterop(require('d')); +babelHelpers.specImportCheck(_d, ['pick']); -(0, _module2.default)(_module3.foo, _module4.pick); + +(0, _b.default)(_c.foo, _d.pick); ``` ## Options `specImport` @@ -149,9 +150,10 @@ This option enables only the half of `spec` mode that affects the imports, witho changing how exports are generated. This would allow the generation of code that may still be compatible with engines that do not support getters. -Note that the require helpers do use `Object.keys` and `Object.defineProperty`, so -ES5 polyfills may still be required. When running on an old engine that does not support -`Object.defineProperty`, a polyfill to fake it like `es5-sham` is still required. +Note that the require helpers do use `Object.getOwnPropertyDescriptor` and +`Object.defineProperty`, so ES5 polyfills may still be required. When running on an +old engine that does not support `Object.defineProperty`, a polyfill to fake it like +`es5-sham` is required. This option is **ignored** if `spec` is enabled. Enabling `spec` implies that this option is also enabled. From df80908e3ab7ee9490883d27bce62682a1329b07 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 10:47:54 +0900 Subject: [PATCH 082/103] Remove unneeded argument --- .../src/index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 04cd8a6e2a5a..4cab15faeafc 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -340,7 +340,7 @@ export default function () { let requires = Object.create(null); - const addRequire = (source, specImport, blockHoist) => { + const addRequire = (source, blockHoist) => { let cached = requires[source]; if (cached) return cached; @@ -587,7 +587,7 @@ export default function () { imports[key] = importsEntry; } else { - ref = addRequire(source.value, specImport, path.node._blockHoist); + ref = addRequire(source.value, path.node._blockHoist); } for (let specifier of specifiers) { @@ -639,11 +639,11 @@ export default function () { exportNode = specBuildNamespaceSpread({ EXPORTS: exportsObj, OWN_EXPORTS: ownExportsUid, - OBJECT: addRequire(path.node.source.value, specImport, 3) + OBJECT: addRequire(path.node.source.value, 3) }); exportNode._blockHoist = 3; } else { - const ref = addRequire(path.node.source.value, specImport, path.node._blockHoist); + const ref = addRequire(path.node.source.value, path.node._blockHoist); exportNode = buildExportAll({ OBJECT: ref }); @@ -661,7 +661,7 @@ export default function () { for (let source in imports) { let {specifiers, maxBlockHoist} = imports[source]; if (specifiers.length) { - const uid = addRequire(source, specImport, maxBlockHoist); + const uid = addRequire(source, maxBlockHoist); if (specImport) { if (!checkedImportMap.has(uid)) { From 2241a880a0b69cf1524e51d1dbbb85ae3603e000 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 11:04:03 +0900 Subject: [PATCH 083/103] Check if renamed reexports are correctly exported --- .../test/fixtures/spec/reexports/actual.js | 2 +- .../test/fixtures/spec/reexports/expected.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/actual.js index 78e00fcd9f4d..ce30528464c2 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/actual.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/actual.js @@ -1,6 +1,6 @@ import * as namespace from './somewhere'; -export { stuff } from './elsewhere' +export { stuff as renamed } from './elsewhere' import { stuff } from './somewhereElse'; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index d2e82b2f1836..985698f907d1 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -31,7 +31,7 @@ Object.defineProperties(exports, { } }, - stuff: { + renamed: { enumerable: true, get() { From 9989e97a126846ab2535c3eb14ad101a84586172 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 11:19:27 +0900 Subject: [PATCH 084/103] Add missing before hooks; fix tests failing on Node < 6 --- .../test/spec-import-check.js | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js index 7fb64b5885f7..6e2eb8820c09 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-import-check.js @@ -85,14 +85,20 @@ describe("spec import", function () { }); describe("with variable", function () { - runner.addModule("variable", "import * as a from 'a'\nconst name = 'a'\nexport const simple = a[name]\nexport const recursive = a[a[name]]"); + before(function () { + runner.addModule("variable", "import * as a from 'a'\nconst name = 'a'\nexport const simple = a[name]\nexport const recursive = a[a[name]]"); - it("does not throw", function () { - runner.getExportsOf("variable"); + it("does not throw", function () { + runner.getExportsOf("variable"); + }); }); describe("has the correct result", function () { - const exports = runner.getExportsOf("variable"); + let exports; + + before(function () { + exports = runner.getExportsOf("variable"); + }); it("simple", function () { assert.strictEqual(exports.simple, "a"); @@ -105,10 +111,13 @@ describe("spec import", function () { }); describe("with Symbol", function () { - runner.addModule("symbol", "import * as a from 'a'\nexport default a[Symbol.toStringTag]"); - if (!helpers.hasToStringTag()) { - this.skip(); - } + before(function () { + runner.addModule("symbol", "import * as a from 'a'\nexport default a[Symbol.toStringTag]"); + + if (!helpers.hasToStringTag()) { + this.skip(); + } + }); it("does not throw", function () { runner.getExportsOf("symbol"); From 33f2325360c9dfa710517ddf5e262071a5d5dab8 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 11:44:21 +0900 Subject: [PATCH 085/103] Use propertyIsEnumerable instead of getOwnPropertyDescriptor --- packages/babel-helpers/src/helpers.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 7065d31fb2ff..e5b566a36e26 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -445,7 +445,7 @@ helpers.specRequireInterop = template(` helpers.specImportCheck = template(` (function (module, imports) { if (!module.__esModule) throw new Error("Only ES modules can be checked"); - var invalid = imports.filter(function (i) { var d = Object.getOwnPropertyDescriptor(module, i); return !d || !d.enumerable; }); + var invalid = imports.filter(function (i) { return !Object.prototype.propertyIsEnumerable.call(module, i) }); if (invalid.length > 0) { var error = new Error( "Unknown export" + (invalid.length > 1 ? "s " : " ") + @@ -470,8 +470,9 @@ helpers.specNamespaceGet = template(` ) { return module[name]; } - var d = Object.getOwnPropertyDescriptor(module, name); - if (!d || !d.enumerable) throw new Error("Unknown export " + JSON.stringify(name) + " imported"); + if (!Object.prototype.propertyIsEnumerable.call(module, name)) { + throw new Error("Unknown export " + JSON.stringify(name) + " imported"); + } return module[name]; }) `); From ca4b63b33205ff3f13a4561b8bea83b677b61aa9 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 11:44:56 +0900 Subject: [PATCH 086/103] Use better SameValue algorithm --- .../src/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 4cab15faeafc..1d6fa1b4b143 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -58,10 +58,16 @@ const specBuildOwnExports = template(` const $0 = Object.keys($1) `); +// The SameValue check between own and other was adapted from core-js's +// Object.is implementation. +// https://github.com/zloirock/core-js/blob/master/modules/_same-value.js const specBuildNamespaceSpread = template(` Object.keys(OBJECT).forEach(function (key) { if (key === "__esModule" || key === "default" || OWN_EXPORTS.indexOf(key) >= 0) return; - if (key in EXPORTS && (EXPORTS[key] === OBJECT[key] || typeof EXPORTS[key] === 'number' && isNaN(EXPORTS[key]))) return; + if (key in EXPORTS) { + var own = EXPORTS[key], other = OBJECT[key] + if (own === other ? own !== 0 || 1 / own === 1 / other : own != own && other != other) return; + } Object.defineProperty(EXPORTS, key, { enumerable: true, get() { From 65680c9b8036e74c07572839d6250e47e38b88b4 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 11:46:04 +0900 Subject: [PATCH 087/103] Update fixtures --- .../spec/export-default-func-hoisting/expected.js | 8 +++++++- .../test/fixtures/spec/reexports/expected.js | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js index 926ec213203a..d24d24791e39 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js @@ -36,7 +36,13 @@ const _bar = babelHelpers.specRequireInterop(require('bar')); Object.keys(_bar).forEach(function (key) { if (key === "__esModule" || key === "default" || _ownExports.indexOf(key) >= 0) return; - if (key in exports && (exports[key] === _bar[key] || typeof exports[key] === 'number' && isNaN(exports[key]))) return; + + if (key in exports) { + var own = exports[key], + other = _bar[key]; + if (own === other ? own !== 0 || 1 / own === 1 / other : own != own && other != other) return; + } + Object.defineProperty(exports, key, { enumerable: true, diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index 985698f907d1..f15a403de471 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -55,7 +55,13 @@ const _iDontKnow = babelHelpers.specRequireInterop(require('./i-dont-know')); Object.keys(_iDontKnow).forEach(function (key) { if (key === "__esModule" || key === "default" || _ownExports.indexOf(key) >= 0) return; - if (key in exports && (exports[key] === _iDontKnow[key] || typeof exports[key] === 'number' && isNaN(exports[key]))) return; + + if (key in exports) { + var own = exports[key], + other = _iDontKnow[key]; + if (own === other ? own !== 0 || 1 / own === 1 / other : own != own && other != other) return; + } + Object.defineProperty(exports, key, { enumerable: true, From 70d0f9aec0f4d8f49c0481b82673765da24e22ce Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 11:52:11 +0900 Subject: [PATCH 088/103] Use a helper for the SameValue check --- packages/babel-helpers/src/helpers.js | 8 ++++++++ .../src/index.js | 9 ++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index e5b566a36e26..00c81c2d03aa 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -416,6 +416,14 @@ helpers.interopRequireWildcard = template(` }) `); +// based on core-js's SameValue implementation +// https://github.com/zloirock/core-js/blob/693767b/modules/_same-value.js +helpers.sameValue = template(` + (function (x, y) { + return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y; + }) +`); + helpers.specRequireInterop = template(` (function (obj) { if (obj && obj.__esModule) { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 1d6fa1b4b143..c71aee758b2a 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -58,16 +58,10 @@ const specBuildOwnExports = template(` const $0 = Object.keys($1) `); -// The SameValue check between own and other was adapted from core-js's -// Object.is implementation. -// https://github.com/zloirock/core-js/blob/master/modules/_same-value.js const specBuildNamespaceSpread = template(` Object.keys(OBJECT).forEach(function (key) { if (key === "__esModule" || key === "default" || OWN_EXPORTS.indexOf(key) >= 0) return; - if (key in EXPORTS) { - var own = EXPORTS[key], other = OBJECT[key] - if (own === other ? own !== 0 || 1 / own === 1 / other : own != own && other != other) return; - } + if (key in EXPORTS && SAME_VALUE(EXPORTS[key], OBJECT[key])) return; Object.defineProperty(EXPORTS, key, { enumerable: true, get() { @@ -645,6 +639,7 @@ export default function () { exportNode = specBuildNamespaceSpread({ EXPORTS: exportsObj, OWN_EXPORTS: ownExportsUid, + SAME_VALUE: this.addHelper("sameValue"), OBJECT: addRequire(path.node.source.value, 3) }); exportNode._blockHoist = 3; From e233d40c3a8cc05fdff3b76f0032eadc6ba44d93 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 11:52:22 +0900 Subject: [PATCH 089/103] Update fixtures --- .../spec/export-default-func-hoisting/expected.js | 8 +------- .../test/fixtures/spec/reexports/expected.js | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js index d24d24791e39..2bacdc7c2267 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js @@ -36,13 +36,7 @@ const _bar = babelHelpers.specRequireInterop(require('bar')); Object.keys(_bar).forEach(function (key) { if (key === "__esModule" || key === "default" || _ownExports.indexOf(key) >= 0) return; - - if (key in exports) { - var own = exports[key], - other = _bar[key]; - if (own === other ? own !== 0 || 1 / own === 1 / other : own != own && other != other) return; - } - + if (key in exports && babelHelpers.sameValue(exports[key], _bar[key])) return; Object.defineProperty(exports, key, { enumerable: true, diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index f15a403de471..11c61e6ebbf7 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -55,13 +55,7 @@ const _iDontKnow = babelHelpers.specRequireInterop(require('./i-dont-know')); Object.keys(_iDontKnow).forEach(function (key) { if (key === "__esModule" || key === "default" || _ownExports.indexOf(key) >= 0) return; - - if (key in exports) { - var own = exports[key], - other = _iDontKnow[key]; - if (own === other ? own !== 0 || 1 / own === 1 / other : own != own && other != other) return; - } - + if (key in exports && babelHelpers.sameValue(exports[key], _iDontKnow[key])) return; Object.defineProperty(exports, key, { enumerable: true, From 1d63392554bf352ac6dbdb610d15ab8c10ff25df Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 14:41:16 +0900 Subject: [PATCH 090/103] Fix crash when processing ThisExpression --- .../src/index.js | 18 ++++----- .../fixtures/spec/commonjs-shadow-3/actual.js | 9 +++++ .../spec/commonjs-shadow-3/expected.js | 40 +++++++++++++++++++ 3 files changed, 56 insertions(+), 11 deletions(-) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-3/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-3/expected.js diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index c71aee758b2a..03b2804eeb3e 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -196,13 +196,13 @@ export default function () { AssignmentExpression(path, state) { if (isSpec(state)) { if (!path.node[REASSIGN_REMAP_SKIP]) { - const target = t.isIdentifier(path.node.left) - ? path.node.left - : t.isMemberExpression(path.node.left) && t.isIdentifier(path.node.left.object) - ? path.node.left.object : null; - const name = target && target.name; + const left = path.get("left"); + if (!left.isIdentifier() && !left.isMemberExpression()) return; + const target = left.isIdentifier() ? left : left.get("object"); + if (!target.isIdentifier()) return; + const name = target.node.name; - if (target[REASSIGN_REMAP_SKIP] || this.scope.getBinding(name) !== path.scope.getBinding(name)) { + if (target.node[REASSIGN_REMAP_SKIP] || this.scope.getBinding(name) !== path.scope.getBinding(name)) { return; } @@ -217,11 +217,7 @@ export default function () { } if (remap) { - if (t.isIdentifier(path.node.left)) { - path.get("left").replaceWith(remap); - } else { - path.get("left.object").replaceWith(remap); - } + target.replaceWith(remap); } } return; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-3/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-3/actual.js new file mode 100644 index 000000000000..4c4bb8a3ff02 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-3/actual.js @@ -0,0 +1,9 @@ +export default class { + noShadow (exports) { + this.shadow(exports) + } + + shadow () { + exports + } +} diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-3/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-3/expected.js new file mode 100644 index 000000000000..1529431091e9 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-3/expected.js @@ -0,0 +1,40 @@ +"use strict"; + +const exports = module.exports = Object.create ? Object.create(null, { + __esModule: { + value: true + } +}) : { + __esModule: true +}; + +if (typeof Symbol === "function" && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { + value: "Module" + }); +} + +Object.defineProperties(exports, { + default: { + enumerable: true, + + get() { + return _default; + } + + } +}); +(Object.freeze || Object)(exports); +let _default = { + default: class { + noShadow(exports) { + this.shadow(exports); + } + + shadow() { + _exports; + } + } +}.default; + +let _exports; \ No newline at end of file From ccd3895fd82bc7d94eea14a9d518e2b74e4213b8 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 14:41:44 +0900 Subject: [PATCH 091/103] Add failing test (fix TODO) --- .../specImport/transform-runtime/actual.js | 5 +++ .../specImport/transform-runtime/expected.js | 32 +++++++++++++++++++ .../specImport/transform-runtime/options.json | 3 ++ 3 files changed, 40 insertions(+) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/options.json diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/actual.js new file mode 100644 index 000000000000..ff1f7dfa1590 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/actual.js @@ -0,0 +1,5 @@ +import { foo } from 'foo' +import * as ns from 'bar' +export * from 'baz' + +ns[true && 'bar'] diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/expected.js new file mode 100644 index 000000000000..d0a9f34abfba --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/expected.js @@ -0,0 +1,32 @@ +'use strict'; + +// definitely not this +import _Object$defineProperty from 'babel-runtime/core-js/object/define-property'; +import _Object$keys from 'babel-runtime/core-js/object/keys'; +import _specNamespaceGet from 'babel-runtime/helpers/specNamespaceGet'; +Object.defineProperty(exports, "__esModule", { + value: true +}); +import _specImportCheck from 'babel-runtime/helpers/specImportCheck'; +import _specRequireInterop from 'babel-runtime/helpers/specRequireInterop'; + +const _baz = _specRequireInterop(require('baz')); + +_Object$keys(_baz).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + + _Object$defineProperty(exports, key, { + enumerable: true, + get: function () { + return _baz[key]; + } + }); +}); + +const _foo = _specRequireInterop(require('foo')); + +_specImportCheck(_foo, ['foo']); + +const _bar = _specRequireInterop(require('bar')); + +_specNamespaceGet(_bar, true && 'bar'); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/options.json b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/options.json new file mode 100644 index 000000000000..71386e610d89 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/options.json @@ -0,0 +1,3 @@ +{ + "plugins": [["transform-es2015-modules-commonjs", {"spec": false, "specImport": true}], "transform-runtime"] +} From ba417599aa880905f74a4abf47090bdd2d635220 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Mon, 12 Dec 2016 14:43:42 +0900 Subject: [PATCH 092/103] Fix order in options.json; test still failing --- .../test/fixtures/specImport/transform-runtime/options.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/options.json b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/options.json index 71386e610d89..74e1870801be 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/options.json +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/options.json @@ -1,3 +1,3 @@ { - "plugins": [["transform-es2015-modules-commonjs", {"spec": false, "specImport": true}], "transform-runtime"] + "plugins": ["transform-runtime", ["transform-es2015-modules-commonjs", {"spec": false, "specImport": true}]] } From 838fb7b8ef68d022cf5e0b2d771f36d8fe7efbd2 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Tue, 13 Dec 2016 02:46:19 +0900 Subject: [PATCH 093/103] Allow the helperGenerator hook to return the helper itself --- packages/babel-core/src/transformation/file/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/babel-core/src/transformation/file/index.js b/packages/babel-core/src/transformation/file/index.js index 01d5b7efc9d3..88abbf288585 100644 --- a/packages/babel-core/src/transformation/file/index.js +++ b/packages/babel-core/src/transformation/file/index.js @@ -288,14 +288,15 @@ export default class File extends Store { let generator = this.get("helperGenerator"); let runtime = this.get("helpersNamespace"); + let res = null; if (generator) { - let res = generator(name); - if (res) return res; + res = generator(name); + if (res && t.isIdentifier(res)) return res; } else if (runtime) { return t.memberExpression(runtime, t.identifier(name)); } - let ref = getHelper(name); + let ref = res || getHelper(name); let uid = this.declarations[name] = this.scope.generateUidIdentifier(name); if (t.isFunctionExpression(ref) && !ref.id) { From 60973721938d5d889ffd3ae14379d504e1133046 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Tue, 13 Dec 2016 02:47:02 +0900 Subject: [PATCH 094/103] transform-runtime: flag blacklisted helpers for skipping transform --- .../package.json | 3 ++- .../src/index.js | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-runtime/package.json b/packages/babel-plugin-transform-runtime/package.json index eb6ef08fbfcf..ad5c9a9c8d26 100644 --- a/packages/babel-plugin-transform-runtime/package.json +++ b/packages/babel-plugin-transform-runtime/package.json @@ -9,7 +9,8 @@ "babel-plugin" ], "dependencies": { - "babel-runtime": "^6.9.0" + "babel-runtime": "^6.9.0", + "babel-helpers": "^6.16.0" }, "devDependencies": { "babel-helper-plugin-test-runner": "^6.8.0" diff --git a/packages/babel-plugin-transform-runtime/src/index.js b/packages/babel-plugin-transform-runtime/src/index.js index 805df686ae10..25e525f47f04 100644 --- a/packages/babel-plugin-transform-runtime/src/index.js +++ b/packages/babel-plugin-transform-runtime/src/index.js @@ -1,4 +1,5 @@ import definitions from "./definitions"; +import getHelper from "babel-helpers"; export default function ({ types: t }) { function getRuntimeModuleName(opts) { @@ -9,7 +10,14 @@ export default function ({ types: t }) { return Object.prototype.hasOwnProperty.call(obj, key); } - let HELPER_BLACKLIST = ["interopRequireWildcard", "interopRequireDefault"]; + let HELPER_BLACKLIST = [ + "interopRequireWildcard", "interopRequireDefault", + "specRequireInterop", "specImportCheck" + ]; + + function shouldSkipTransform(path) { + return !!path.find((p) => p.node._noTransform); + } return { pre(file) { @@ -19,6 +27,10 @@ export default function ({ types: t }) { file.set("helperGenerator", function (name) { if (HELPER_BLACKLIST.indexOf(name) < 0) { return file.addImport(`${moduleName}/helpers/${name}`, "default", name); + } else { + const node = getHelper(name); + node._noTransform = true; + return node; } }); } @@ -42,6 +54,7 @@ export default function ({ types: t }) { if (t.isMemberExpression(parent)) return; if (!has(definitions.builtins, node.name)) return; if (scope.getBindingIdentifier(node.name)) return; + if (shouldSkipTransform(path)) return; // Symbol() -> _core.Symbol(); new Promise -> new _core.Promise const moduleName = getRuntimeModuleName(state.opts); @@ -63,6 +76,7 @@ export default function ({ types: t }) { if (!t.isMemberExpression(callee)) return; if (!callee.computed) return; if (!path.get("callee.property").matchesPattern("Symbol.iterator")) return; + if (shouldSkipTransform(path)) return; const moduleName = getRuntimeModuleName(state.opts); path.replaceWith(t.callExpression( @@ -81,6 +95,7 @@ export default function ({ types: t }) { if (path.node.operator !== "in") return; if (!path.get("left").matchesPattern("Symbol.iterator")) return; + if (shouldSkipTransform(path)) return; const moduleName = getRuntimeModuleName(state.opts); path.replaceWith(t.callExpression( @@ -118,6 +133,7 @@ export default function ({ types: t }) { let call = path.parentPath.node; if (call.arguments.length === 3 && t.isLiteral(call.arguments[1])) return; } + if (shouldSkipTransform(path)) return; const moduleName = getRuntimeModuleName(state.opts); path.replaceWith(state.addImport( @@ -136,6 +152,7 @@ export default function ({ types: t }) { if (!has(definitions.builtins, obj.name)) return; if (path.scope.getBindingIdentifier(obj.name)) return; + if (shouldSkipTransform(path)) return; const moduleName = getRuntimeModuleName(state.opts); path.replaceWith(t.memberExpression( From ba6dda001f75fb7e5dc6cada5b0eb0cab22567ca Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Tue, 13 Dec 2016 02:48:16 +0900 Subject: [PATCH 095/103] Build the babel-runtime dist scripts from current code --- packages/babel-runtime/scripts/build-dist.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-runtime/scripts/build-dist.js b/packages/babel-runtime/scripts/build-dist.js index 25799b68c613..baa27038a378 100644 --- a/packages/babel-runtime/scripts/build-dist.js +++ b/packages/babel-runtime/scripts/build-dist.js @@ -31,7 +31,7 @@ each(legacy, function (value, key) { writeFile("core-js/" + key + ".js", defaultify('require("core-js/library/fn/' + value + '")')); }); -var helpers = require("babel-helpers"); +var helpers = require("../../babel-helpers"); var babel = require("../../babel-core"); var util = require("../../babel-core/lib/util"); var t = require("../../babel-types"); From 1c5bbcc21e51be7eeac839b0a7a042fd106c0e27 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Tue, 13 Dec 2016 02:50:42 +0900 Subject: [PATCH 096/103] Huge refactoring; reimplement spec / specImport modes with visitors --- packages/babel-helpers/src/helpers.js | 34 +- .../src/index.js | 836 +++++++++++------- .../actual.js | 5 + .../expected.js | 35 + .../options.json | 3 + .../spec/commonjs-shadow-1/expected.js | 2 +- .../export-default-func-hoisting/expected.js | 23 +- .../fixtures/spec/export-default/expected.js | 2 +- .../fixtures/spec/import-simple/expected.js | 16 + .../test/fixtures/spec/import/expected.js | 16 + .../fixtures/spec/namespace-get/expected.js | 16 + .../test/fixtures/spec/readme/expected.js | 2 +- .../test/fixtures/spec/reexports/expected.js | 25 +- .../fixtures/spec/transform-runtime/actual.js | 5 + .../spec/transform-runtime/expected.js | 70 ++ .../spec/transform-runtime/options.json | 3 + .../specImport/transform-runtime/actual.js | 5 +- .../specImport/transform-runtime/expected.js | 33 +- .../test/spec-test-helpers.js | 12 +- .../test/spec-with-transform-runtime.js | 54 ++ 20 files changed, 802 insertions(+), 395 deletions(-) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-with-transform-runtime/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-with-transform-runtime/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-with-transform-runtime/options.json create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/transform-runtime/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/transform-runtime/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/transform-runtime/options.json create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-with-transform-runtime.js diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 00c81c2d03aa..921c700eba85 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -416,14 +416,6 @@ helpers.interopRequireWildcard = template(` }) `); -// based on core-js's SameValue implementation -// https://github.com/zloirock/core-js/blob/693767b/modules/_same-value.js -helpers.sameValue = template(` - (function (x, y) { - return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y; - }) -`); - helpers.specRequireInterop = template(` (function (obj) { if (obj && obj.__esModule) { @@ -485,6 +477,32 @@ helpers.specNamespaceGet = template(` }) `); +// sameValue function based on core-js's SameValue implementation +// https://github.com/zloirock/core-js/blob/693767b/modules/_same-value.js +helpers.specNamespaceSpread = template(` + (function (exports, ownExports, module) { + if (!module.__esModule) throw new Error("Only ES modules can be spread"); + for (var key in module) { + if (!Object.prototype.hasOwnProperty.call(module, key)) continue; + if (key === "__esModule" || key === "default" || ownExports.indexOf(key) >= 0) continue; + if (key in exports && sameValue(exports[key], module[key])) continue; + + Object.defineProperty(exports, key, { + enumerable: true, + get: (function (key) { + return function () { + return module[key]; + } + })(key) + }); + } + + function sameValue(x, y) { + return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y; + } + }) +`); + helpers.newArrowCheck = template(` (function (innerThis, boundThis) { if (innerThis !== boundThis) { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 03b2804eeb3e..9b6c3089c418 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -58,19 +58,6 @@ const specBuildOwnExports = template(` const $0 = Object.keys($1) `); -const specBuildNamespaceSpread = template(` - Object.keys(OBJECT).forEach(function (key) { - if (key === "__esModule" || key === "default" || OWN_EXPORTS.indexOf(key) >= 0) return; - if (key in EXPORTS && SAME_VALUE(EXPORTS[key], OBJECT[key])) return; - Object.defineProperty(EXPORTS, key, { - enumerable: true, - get() { - return OBJECT[key]; - } - }); - }); -`); - // Unfortunately, regular objects can't synthesize a value descriptor every time they're read, // so a getter needs to be used for live bindings. // It's also not allowed to specify writable when using getters/setters. @@ -284,6 +271,67 @@ export default function () { } }; + function addImportDependency(path, state) { + const { dependencySet, importMap, exportAllSet } = state; + const source = path.node.source.value; + dependencySet.add(source); + if (!importMap.has(source)) { + importMap.set(source, { + specifiers: new Set(), + reexports: new Map(), + maxBlockHoist: 0, + loc: path.node.loc + }); + } + const entry = importMap.get(source); + + if (path.isExportAllDeclaration()) { + exportAllSet.add(source); + } else { + for (const specifier of path.node.specifiers) { + if (t.isExportDefaultSpecifier(specifier)) { + // not ES2015 + } else if (t.isExportNamespaceSpecifier(specifier)) { + // not ES2015 + } else if (t.isExportSpecifier(specifier)) { + entry.reexports.set(specifier.exported.name, specifier.local); + } + entry.specifiers.add(specifier); + } + } + + if (typeof path.node._blockHoist === "number") { + entry.maxBlockHoist = Math.max( + path.node._blockHoist, + entry.maxBlockHoist + ); + } + + return entry; + } + + function addExportDeclaration(path, state) { + if (isSpec(state)) { + if (!path.isExportAllDeclaration()) { + const specifiers = [].concat(path.get("declaration"), path.get("specifiers")); + for (const specifier of specifiers) { + const ids = specifier.getBindingIdentifiers(); + if (ids.__esModule) { + throw specifier.buildCodeFrameError("Illegal export \"__esModule\""); + } + } + } + } + + if (isSpecImport(state)) { + if (path.node.source) { + state.addHelper("specRequireInterop"); + state.addHelper("specImportCheck"); + addImportDependency(path, state); + } + } + } + return { inherits: require("babel-plugin-transform-strict-mode"), @@ -303,7 +351,209 @@ export default function () { } }, + ImportDeclaration(path, state) { + if (isSpecImport(state)) { + this.addHelper("specRequireInterop"); + this.addHelper("specImportCheck"); + + addImportDependency(path, state); + + try { + path.remove(); + } catch (e) { + throw new Error( + `Invalid import ${JSON.stringify(path.node.source.value)} created after commonjs transform finished executing` + ); + } + } + }, + + ExportDefaultDeclaration(path, state) { + addExportDeclaration(path, state); + + if (isSpec(state)) { + const { exportMap } = state; + const declaration = path.get("declaration"); + if (declaration.isFunctionDeclaration()) { + const id = declaration.node.id; + if (id) { + exportMap.set("default", id); + path.replaceWith(declaration.node); + } else { + const defaultExportUid = path.scope.generateUidIdentifier("default"); + exportMap.set("default", defaultExportUid); + + const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; + const decl = t.variableDeclaration("let", [ + t.variableDeclarator(defaultExportUid, expr) + ]); + decl.loc = declaration.node.loc; + decl._blockHoist = 3; + + path.parentPath.pushContainer("body", [decl]); + path.remove(); + } + } else if (declaration.isClassDeclaration()) { + const id = declaration.node.id; + if (id) { + exportMap.set("default", id); + path.replaceWith(declaration.node); + } else { + const defaultExportUid = path.scope.generateUidIdentifier("default"); + exportMap.set("default", defaultExportUid); + + const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; + path.replaceWith(t.variableDeclaration("let", [ + t.variableDeclarator(defaultExportUid, expr) + ])); + } + } else { + const defaultExportUid = path.scope.generateUidIdentifier("default"); + exportMap.set("default", defaultExportUid); + + path.replaceWith(t.variableDeclaration("let", [ + t.variableDeclarator(defaultExportUid, declaration.node) + ])); + } + } + }, + + ExportAllDeclaration(path, state) { + addExportDeclaration(path, state); + + if (isSpecImport(state)) { + this.addHelper("specRequireInterop"); + this.addHelper("specImportCheck"); + path.remove(); + } + if (isSpec(state)) { + this.addHelper("specNamespaceSpread"); + } + }, + + ExportNamedDeclaration(path, state) { + addExportDeclaration(path, state); + + if (isSpec(state)) { + const { exportMap } = state; + const declaration = path.get("declaration"); + if (declaration.node) { + if (declaration.isFunctionDeclaration() || declaration.isClassDeclaration()) { + const id = declaration.node.id; + exportMap.set(id.name, id); + } else if (declaration.isVariableDeclaration()) { + const declarations = declaration.get("declarations"); + for (const decl of declarations) { + const id = decl.node.id; + exportMap.set(id.name, id); + } + } + path.replaceWith(declaration.node); + } else { + for (const specifier of path.node.specifiers) { + exportMap.set(specifier.exported.name, specifier.local); + } + path.remove(); + } + } + }, + Program: { + enter(path, state) { + if (isSpec(state)) { + state.exportMap = new Map(); + + const decls = specBuildNamespace(exportsObj, module); + const exportPropertyList = []; + const node = specBuildHoistedExport(exportsObj, t.objectExpression(exportPropertyList)); + decls.push(node); + const freeze = specFinishNamespaceExport(exportsObj); + decls.push(freeze); + + decls.forEach((decl) => { decl._blockHoist = 3; }); + + path.unshiftContainer("body", decls); + + state.exportPath = path.get("body").find((path) => path.node === node); + state.freezePath = path.get("body").find((path) => path.node === freeze); + + state.updateExports = () => { + const { exportMap } = state; + + const current = new Set(exportPropertyList.map((prop) => prop.key.name)); + + const entries = Array.from(exportMap.entries()) + .filter(([name]) => !current.has(name)) + .map(([name, id]) => + specBuildHoistedExportProperty(t.identifier(name), id) + ) + .filter(Boolean); + + for (const entry of entries) { + exportPropertyList.push(entry); + } + }; + + state.removeHoistedExports = () => { + path.get("body").find((path) => path.node === node).remove(); + }; + } + + if (isSpecImport(state)) { + state.dependencySet = new Set(); + state.importMap = new Map(); + state.exportAllSet = new Set(); + + const namespaceImportSet = new Set(); + + // two fast traversals to check whether the specNamespaceGet + // or the specNamespaceSpread helpers will be needed and request + // them beforehand + + const { scope } = path; + const addHelper = this.addHelper.bind(this); + + path.traverse({ + ExportAllDeclaration() { + if (isSpec(state)) { + addHelper("specNamespaceSpread"); + if (!state.ownExportsUid) { + state.ownExportsUid = path.scope.generateUidIdentifier("ownExports"); + const node = specBuildOwnExports(state.ownExportsUid, exportsObj); + node._blockHoist = 3; + state.freezePath.insertBefore([node]); + state.ownExportsPath = state.exportPath.parentPath.get("body").find((path) => path.node === node); + } + } + }, + ImportNamespaceSpecifier(path) { + namespaceImportSet.add(path.node.local.name); + } + }); + if (namespaceImportSet.size > 0) { + path.traverse({ + ReferencedIdentifier(path) { + const name = path.node.name; + + // redeclared in this scope + if (scope.getBinding(name) !== path.scope.getBinding(name)) return; + + if ( + t.isMemberExpression(path.parent) && + t.isIdentifier(path.parent.object) && + namespaceImportSet.has(path.parent.object.name) + ) { + const { property, computed } = path.parent; + + if (computed && !t.isStringLiteral(property)) { + addHelper("specNamespaceGet"); + } + } + } + }); + } + } + }, exit(path, state) { this.ranCommonJS = true; @@ -330,10 +580,6 @@ export default function () { let topNodes = []; let remaps = Object.create(null); - const hoistedExports = spec && new Map(); - let defaultExportUid = null; - let ownExportsUid = null; - let requires = Object.create(null); const addRequire = (source, blockHoist) => { @@ -393,7 +639,7 @@ export default function () { } } - if (path.isImportDeclaration()) { + if (!specImport && path.isImportDeclaration()) { hasImports = true; let key = path.node.source.value; @@ -415,87 +661,42 @@ export default function () { imports[key] = importsEntry; path.remove(); - } else if (path.isExportDefaultDeclaration()) { - let declaration = path.get("declaration"); - if (declaration.isFunctionDeclaration()) { - let id = declaration.node.id; - let defNode = t.identifier("default"); - if (id) { - addTo(exports, id.name, defNode); - if (spec) { - hoistedExports.set("default", id); - } else { + } else if (!spec) { + if (path.isExportDefaultDeclaration()) { + let declaration = path.get("declaration"); + if (declaration.isFunctionDeclaration()) { + let id = declaration.node.id; + let defNode = t.identifier("default"); + if (id) { + addTo(exports, id.name, defNode); topNodes.push(buildExportsAssignment(defNode, id)); - } - path.replaceWith(declaration.node); - } else if (spec) { - defaultExportUid = path.scope.generateUidIdentifier("default"); - hoistedExports.set("default", defaultExportUid); - const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; - const decl = t.variableDeclaration("let", [ - t.variableDeclarator(defaultExportUid, expr) - ]); - decl.loc = declaration.node.loc; - decl._blockHoist = 3; - topNodes.unshift(decl); - path.remove(); - } else { - const expr = t.toExpression(declaration.node); - topNodes.push(buildExportsAssignment(defNode, expr)); - path.remove(); - } - } else if (declaration.isClassDeclaration()) { - let id = declaration.node.id; - let defNode = t.identifier("default"); - if (id) { - addTo(exports, id.name, defNode); - if (spec) { - hoistedExports.set("default", id); path.replaceWith(declaration.node); } else { + const expr = t.toExpression(declaration.node); + topNodes.push(buildExportsAssignment(defNode, expr)); + path.remove(); + } + } else if (declaration.isClassDeclaration()) { + let id = declaration.node.id; + let defNode = t.identifier("default"); + if (id) { + addTo(exports, id.name, defNode); path.replaceWithMultiple([ declaration.node, buildExportsAssignment(defNode, id) ]); - } - } else if (spec) { - defaultExportUid = path.scope.generateUidIdentifier("default"); - hoistedExports.set("default", defaultExportUid); - - const expr = specBuildFunctionNameWrapper(t.toExpression(declaration.node)).expression; - - path.replaceWith(t.variableDeclaration("let", [ - t.variableDeclarator(defaultExportUid, expr) - ])); + } else { + const expr = t.toExpression(declaration.node); + path.replaceWith(buildExportsAssignment(defNode, expr)); - // Manually re-queue the expression so other transforms can get to it. - // Ideally this would happen automatically from the replaceWith above. - // See #4140 for more info. - path.parentPath.requeue(path.get("declarations.init")); + // Manualy re-queue `export default class {}` expressions so that the ES3 transform + // has an opportunity to convert them. Ideally this would happen automatically from the + // replaceWith above. See #4140 for more info. + path.parentPath.requeue(path.get("expression.left")); + } } else { - const expr = t.toExpression(declaration.node); - path.replaceWith(buildExportsAssignment(defNode, expr)); + const defNode = t.identifier("default"); - // Manualy re-queue `export default class {}` expressions so that the ES3 transform - // has an opportunity to convert them. Ideally this would happen automatically from the - // replaceWith above. See #4140 for more info. - path.parentPath.requeue(path.get("expression.left")); - } - } else { - const defNode = t.identifier("default"); - if (spec) { - defaultExportUid = path.scope.generateUidIdentifier("default"); - hoistedExports.set("default", defaultExportUid); - - path.replaceWith(t.variableDeclaration("let", [ - t.variableDeclarator(defaultExportUid, declaration.node) - ])); - - // Manually re-queue the expression so other transforms can get to it. - // Ideally this would happen automatically from the replaceWith above. - // See #4140 for more info. - path.parentPath.requeue(path.get("declarations.init")); - } else { path.replaceWith(buildExportsAssignment(defNode, declaration.node)); // Manualy re-queue `export default foo;` expressions so that the ES3 transform @@ -503,251 +704,209 @@ export default function () { // replaceWith above. See #4140 for more info. path.parentPath.requeue(path.get("expression.left")); } - } - } else if (path.isExportNamedDeclaration()) { - let declaration = path.get("declaration"); - if (declaration.node) { - if (declaration.isFunctionDeclaration()) { - let id = declaration.node.id; - addTo(exports, id.name, id); - if (spec) { - hoistedExports.set(id.name, id); - } else { + } else if (path.isExportNamedDeclaration()) { + let declaration = path.get("declaration"); + if (declaration.node) { + if (declaration.isFunctionDeclaration()) { + let id = declaration.node.id; + addTo(exports, id.name, id); topNodes.push(buildExportsAssignment(id, id)); - } - path.replaceWith(declaration.node); - } else if (declaration.isClassDeclaration()) { - let id = declaration.node.id; - addTo(exports, id.name, id); - if (spec) { - hoistedExports.set(id.name, id); path.replaceWith(declaration.node); - } else { + } else if (declaration.isClassDeclaration()) { + let id = declaration.node.id; + addTo(exports, id.name, id); path.replaceWithMultiple([ declaration.node, buildExportsAssignment(id, id) ]); nonHoistedExportNames[id.name] = true; - } - } else if (declaration.isVariableDeclaration()) { - let declarators = declaration.get("declarations"); - for (let decl of declarators) { - let id = decl.get("id"); - - if (spec) { - hoistedExports.set(id.node.name, id.node); - continue; - } - - let init = decl.get("init"); - if (!init.node) init.replaceWith(t.identifier("undefined")); - - if (id.isIdentifier()) { - addTo(exports, id.node.name, id.node); - if (spec) { - } else { + } else if (declaration.isVariableDeclaration()) { + let declarators = declaration.get("declarations"); + for (let decl of declarators) { + let id = decl.get("id"); + let init = decl.get("init"); + if (!init.node) init.replaceWith(t.identifier("undefined")); + + if (id.isIdentifier()) { + addTo(exports, id.node.name, id.node); init.replaceWith(buildExportsAssignment(id.node, init.node).expression); + nonHoistedExportNames[id.node.name] = true; + } else { + // todo } - nonHoistedExportNames[id.node.name] = true; - } else { - // todo } + path.replaceWith(declaration.node); } - path.replaceWith(declaration.node); + continue; } - continue; - } - let specifiers = path.get("specifiers"); - let nodes = []; - let source = path.node.source; - if (source) { - let ref, importsEntry; - if (specImport) { - let key = path.node.source.value; - importsEntry = imports[key] || { - specifiers: [], - maxBlockHoist: 0, - loc: path.node.loc, - }; - importsEntry.reexports = importsEntry.reexports || new Map(), - - importsEntry.specifiers.push(...path.node.specifiers); - - if (typeof path.node._blockHoist === "number") { - importsEntry.maxBlockHoist = Math.max( - path.node._blockHoist, - importsEntry.maxBlockHoist - ); - } + let specifiers = path.get("specifiers"); + let nodes = []; + let source = path.node.source; + if (source && !specImport) { + let ref = addRequire(source.value, path.node._blockHoist); - imports[key] = importsEntry; - } else { - ref = addRequire(source.value, path.node._blockHoist); - } - - for (let specifier of specifiers) { - if (specifier.isExportNamespaceSpecifier()) { - // todo - } else if (specifier.isExportDefaultSpecifier()) { - // todo - } else if (specifier.isExportSpecifier()) { - if (specImport) { - importsEntry.reexports.set(specifier.node.exported.name, specifier.node.local); - } else if (specifier.node.local.name === "default") { - topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name), t.memberExpression(t.callExpression(this.addHelper("interopRequireDefault"), [ref]), specifier.node.local))); - } else { - topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name), t.memberExpression(ref, specifier.node.local))); + for (let specifier of specifiers) { + if (specifier.isExportNamespaceSpecifier()) { + // todo + } else if (specifier.isExportDefaultSpecifier()) { + // todo + } else if (specifier.isExportSpecifier()) { + if (specifier.node.local.name === "default") { + topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name), t.memberExpression(t.callExpression(this.addHelper("interopRequireDefault"), [ref]), specifier.node.local))); + } else { + topNodes.push(buildExportsFrom(t.stringLiteral(specifier.node.exported.name), t.memberExpression(ref, specifier.node.local))); + } + nonHoistedExportNames[specifier.node.exported.name] = true; } - nonHoistedExportNames[specifier.node.exported.name] = true; } - } - } else { - for (let specifier of specifiers) { - if (specifier.isExportSpecifier()) { - addTo(exports, specifier.node.local.name, specifier.node.exported); - nonHoistedExportNames[specifier.node.exported.name] = true; + } else if (!source) { + for (let specifier of specifiers) { + if (specifier.isExportSpecifier()) { + addTo(exports, specifier.node.local.name, specifier.node.exported); + nonHoistedExportNames[specifier.node.exported.name] = true; - if (spec) { - hoistedExports.set(specifier.node.exported.name, specifier.node.local); - } else { nodes.push(buildExportsAssignment(specifier.node.exported, specifier.node.local)); } } } - } - path.replaceWithMultiple(nodes); - } else if (path.isExportAllDeclaration()) { - let exportNode; - if (spec) { - if (ownExportsUid == null) { - ownExportsUid = path.scope.generateUidIdentifier("ownExports"); - const ownExportsNode = specBuildOwnExports(ownExportsUid, exportsObj); - ownExportsNode._blockHoist = 3; - topNodes.push(ownExportsNode); - } - - // Unfortunately, the namespace spread needs to happen _before_ - // the namespace is frozen. This _will_ result in out-of-order - // imports, with the 'export * from' imports all running before - // any of the other imports. - - exportNode = specBuildNamespaceSpread({ - EXPORTS: exportsObj, - OWN_EXPORTS: ownExportsUid, - SAME_VALUE: this.addHelper("sameValue"), - OBJECT: addRequire(path.node.source.value, 3) - }); - exportNode._blockHoist = 3; - } else { + path.replaceWithMultiple(nodes); + } else if (path.isExportAllDeclaration() && !specImport) { const ref = addRequire(path.node.source.value, path.node._blockHoist); - exportNode = buildExportAll({ + const exportNode = buildExportAll({ OBJECT: ref }); - if (specImport) { - exportNode._blockHoist = ref._blockHoist; - } + exportNode.loc = path.node.loc; + topNodes.push(exportNode); + path.remove(); } - exportNode.loc = path.node.loc; - topNodes.push(exportNode); - path.remove(); } } const checkedImportMap = specImport && new Map(); - for (let source in imports) { - let {specifiers, maxBlockHoist} = imports[source]; - if (specifiers.length) { - const uid = addRequire(source, maxBlockHoist); - - if (specImport) { - if (!checkedImportMap.has(uid)) { - const nameSet = new Set( - Array.from(new Set(specifiers)) - .filter((specifier) => !t.isImportNamespaceSpecifier(specifier)) - .map((specifier) => { - if (t.isImportDefaultSpecifier(specifier)) { - return "default"; - } - if (t.isExportSpecifier(specifier)) { - return specifier.local.name; - } - return specifier.imported.name; - }) - ); - nameSet.source = source; - - const names = Array.from(nameSet).map((name) => t.stringLiteral(name)); - const namesExpr = t.arrayExpression(names); - - const node = - t.expressionStatement( - t.callExpression( - this.addHelper("specImportCheck"), - [ - uid, - namesExpr - ] - ) - ); - - node.loc = imports[source].loc; - - checkedImportMap.set(uid, { - nameSet, - node, - updateNode: () => { - if (nameSet.size === names.length) return; - - const current = new Set(names.map((name) => name.value)); - - for (const name of nameSet) { - if (!current.has(name)) { - names.push(t.stringLiteral(name)); - } - } - }, - isEmpty: () => { - return names.length < 1; - } - }); - topNodes.push(node); - } + if (specImport) { + const { importMap, exportAllSet, dependencySet } = state; + + if (!spec) { + for (const star of exportAllSet) { + const entry = importMap.get(star); + const maxBlockHoist = Math.max(entry.maxBlockHoist, 3); + const ref = addRequire(star, maxBlockHoist); + const exportNode = buildExportAll({ + OBJECT: ref + }); + exportNode._blockHoist = maxBlockHoist; + exportNode.loc = entry.loc; + topNodes.push(exportNode); } + } + for (const dep of dependencySet) { + if (exportAllSet.has(dep)) continue; - if (imports[source].reexports) { - if (!spec) { - for (const [name, local] of imports[source].reexports.entries()) { - if (name === "default") { - topNodes.push(buildExportsFrom(t.stringLiteral(name), t.memberExpression(t.callExpression(this.addHelper("interopRequireDefault"), [uid]), local))); - } else { - topNodes.push(buildExportsFrom(t.stringLiteral(name), t.memberExpression(uid, local))); + const entry = importMap.get(dep); + const { specifiers } = entry; + + if (specifiers.size == 0) { + const node = buildRequire(t.stringLiteral(dep)); + node.loc = entry.loc; + topNodes.push(node); + continue; + } + + const uid = addRequire(dep, entry.maxBlockHoist || undefined); + + if (!checkedImportMap.has(uid)) { + const nameSet = new Set( + Array.from(new Set(specifiers)) + .filter((specifier) => !t.isImportNamespaceSpecifier(specifier)) + .map((specifier) => { + if (t.isImportDefaultSpecifier(specifier)) { + return "default"; } + if (t.isExportSpecifier(specifier)) { + return specifier.local.name; + } + return specifier.imported.name; + }) + ); + nameSet.source = dep; + + const names = Array.from(nameSet).map((name) => t.stringLiteral(name)); + const namesExpr = t.arrayExpression(names); + + const node = + t.expressionStatement( + t.callExpression( + this.addHelper("specImportCheck"), + [ + uid, + namesExpr + ] + ) + ); + + node.loc = entry.loc; + node._blockHoist = entry.maxBlockHoist || undefined; + + checkedImportMap.set(uid, { + nameSet, + node, + updateNode: () => { + if (nameSet.size === names.length) return; + + const current = new Set(names.map((name) => name.value)); + + for (const name of nameSet) { + if (!current.has(name)) { + names.push(t.stringLiteral(name)); + } + } + }, + isEmpty: () => { + return names.length < 1; } - } else { - for (const [name, local] of imports[source].reexports.entries()) { - hoistedExports.set(name, t.memberExpression(uid, local)); - } - } + }); + + topNodes.push(node); } - if (specImport) { - for (const specifier of specifiers) { - if (t.isImportNamespaceSpecifier(specifier)) { - remaps[specifier.local.name] = uid; - } else if (t.isImportDefaultSpecifier(specifier)) { - remaps[specifier.local.name] = t.memberExpression(uid, t.identifier("default")); - } else if (specImport && t.isExportSpecifier(specifier)) { - // do nothing + if (!spec) { + for (const [name, local] of entry.reexports.entries()) { + if (name === "default") { + topNodes.push(buildExportsFrom(t.stringLiteral(name), t.memberExpression(t.callExpression(this.addHelper("interopRequireDefault"), [uid]), local))); } else { - remaps[specifier.local.name] = t.memberExpression(uid, t.cloneWithoutLoc(specifier.imported)); - } - if (specImport && !t.isExportSpecifier(specifier)) { - remaps[specifier.local.name].nameSet = checkedImportMap.get(uid).nameSet; + topNodes.push(buildExportsFrom(t.stringLiteral(name), t.memberExpression(uid, local))); } } } else { + const { exportMap } = state; + for (const [name, local] of entry.reexports.entries()) { + exportMap.set(name, t.memberExpression(uid, local)); + } + } + + // all specifiers + for (const specifier of specifiers) { + if (t.isExportSpecifier(specifier)) { + continue; + } if (t.isImportNamespaceSpecifier(specifier)) { + remaps[specifier.local.name] = uid; + } else if (t.isImportDefaultSpecifier(specifier)) { + remaps[specifier.local.name] = t.memberExpression(uid, t.identifier("default")); + } else { + remaps[specifier.local.name] = t.memberExpression(uid, t.cloneWithoutLoc(specifier.imported)); + } + + remaps[specifier.local.name].nameSet = checkedImportMap.get(uid).nameSet; + } + } + } else { + for (let source in imports) { + let {specifiers, maxBlockHoist} = imports[source]; + if (specifiers.length) { + const uid = addRequire(source, maxBlockHoist); + let wildcard; for (let i = 0; i < specifiers.length; i++) { @@ -806,38 +965,27 @@ export default function () { remaps[specifier.local.name] = t.memberExpression(target, t.cloneWithoutLoc(specifier.imported)); } } + } else { + // bare import + let requireNode = buildRequire(t.stringLiteral(source)); + requireNode.loc = imports[source].loc; + topNodes.push(requireNode); } - } else { - // bare import - let requireNode = buildRequire(t.stringLiteral(source)); - requireNode.loc = imports[source].loc; - topNodes.push(requireNode); } } - if (spec) { - const entries = Array.from(hoistedExports.entries()).map(([name, id]) => { - if (name === "default" && id === null) { - // explicit export is generated - return; - } - return specBuildHoistedExportProperty(t.identifier(name), id); - }).filter(Boolean); + if (!spec) { + const { importMap } = state; + hasImports = specImport ? importMap.size > 0 : hasImports; - if (entries.length > 0) { - const expr = t.objectExpression(entries); - const node = specBuildHoistedExport(exportsObj, expr); - node._blockHoist = 3; - - topNodes.unshift(node); + if (specImport) { + for (const entry of importMap.values()) { + for (const name of entry.reexports.keys()) { + nonHoistedExportNames[name] = true; + } + } } - if (hasExports) { - const node = specFinishNamespaceExport(exportsObj); - node._blockHoist = 3; - topNodes.push(node); - } - } else { if (hasImports && Object.keys(nonHoistedExportNames).length) { let hoistedExportsNode = t.identifier("undefined"); @@ -865,14 +1013,50 @@ export default function () { path.unshiftContainer("body", topNodes); - if (hasExports && spec) { - const decls = specBuildNamespace(exportsObj, module); - decls.forEach((decl) => { decl._blockHoist = 3; }); + if (spec) { + const { exportAllSet } = state; + let tail = state.exportPath; - path.unshiftContainer("body", decls); - } + if (exportAllSet.size > 0) { + const toAdd = new Set(); + + for (const star of exportAllSet) { + const ref = path.scope.generateUidIdentifier(basename(star, extname(star))); + + const req = buildRequire( + t.stringLiteral(star) + ).expression; + + const decl = t.variableDeclaration("const", [ + t.variableDeclarator(ref, + t.callExpression(this.addHelper("specRequireInterop"), [req]) + ) + ]); + decl._blockHoist = 3; + toAdd.add(decl); + + const node = t.expressionStatement( + t.callExpression( + this.addHelper("specNamespaceSpread"), + [ + exportsObj, state.ownExportsUid, ref + ] + ) + ); + node._blockHoist = 3; + toAdd.add(node); + } + + state.ownExportsPath.insertAfter(Array.from(toAdd)); + tail = state.ownExportsPath.getSibling(state.ownExportsPath.key + toAdd.size); + } + + if (state.exportMap.size > 0) { + state.updateExports(); + } else { + state.removeHoistedExports(); + } - if (spec) { commonjsExportsMasked = {}; } @@ -885,7 +1069,7 @@ export default function () { requeueInParent: (newPath) => path.requeue(newPath), }); - if (specImport && hasImports) { + if (specImport && checkedImportMap.size > 0) { const toRemove = new Set(); for (const check of checkedImportMap.values()) { check.updateNode(); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-with-transform-runtime/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-with-transform-runtime/actual.js new file mode 100644 index 000000000000..d36f3711f1c6 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-with-transform-runtime/actual.js @@ -0,0 +1,5 @@ +// NOTE: this is _broken_ + +export * from 'a' +import 'b' +export * from 'c' \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-with-transform-runtime/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-with-transform-runtime/expected.js new file mode 100644 index 000000000000..3f5bff0264f9 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-with-transform-runtime/expected.js @@ -0,0 +1,35 @@ +'use strict'; + +import _Object$defineProperty from 'babel-runtime/core-js/object/define-property'; +import _Object$keys from 'babel-runtime/core-js/object/keys'; +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _a = require('a'); + +_Object$keys(_a).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + + _Object$defineProperty(exports, key, { + enumerable: true, + get: function () { + return _a[key]; + } + }); +}); + +var _c = require('c'); + +_Object$keys(_c).forEach(function (key) { + if (key === "default" || key === "__esModule") return; + + _Object$defineProperty(exports, key, { + enumerable: true, + get: function () { + return _c[key]; + } + }); +}); + +require('b'); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-with-transform-runtime/options.json b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-with-transform-runtime/options.json new file mode 100644 index 000000000000..50123bb50eb4 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/interop/export-from-with-transform-runtime/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["external-helpers", "transform-es2015-modules-commonjs", "transform-runtime"] +} diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js index d89d44cb86c1..f1248c6fc7a5 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/commonjs-shadow-1/expected.js @@ -24,10 +24,10 @@ Object.defineProperties(exports, { } }); +(Object.freeze || Object)(exports); let _default = { default: function () {} }.default; -(Object.freeze || Object)(exports); _exports = function () {}; diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js index 2bacdc7c2267..576e822e9a42 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default-func-hoisting/expected.js @@ -24,28 +24,17 @@ Object.defineProperties(exports, { } }); -let _default = { - default: function () { - return _foo; - } -}.default; const _ownExports = Object.keys(exports); const _bar = babelHelpers.specRequireInterop(require('bar')); -Object.keys(_bar).forEach(function (key) { - if (key === "__esModule" || key === "default" || _ownExports.indexOf(key) >= 0) return; - if (key in exports && babelHelpers.sameValue(exports[key], _bar[key])) return; - Object.defineProperty(exports, key, { - enumerable: true, - - get() { - return _bar[key]; - } - - }); -}); +babelHelpers.specNamespaceSpread(exports, _ownExports, _bar); (Object.freeze || Object)(exports); +let _default = { + default: function () { + return _foo; + } +}.default; const _foo = babelHelpers.specRequireInterop(require('foo')); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js index 721df9030725..6809a8bcf691 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/export-default/expected.js @@ -32,9 +32,9 @@ Object.defineProperties(exports, { } }); +(Object.freeze || Object)(exports); let _default = { default: function () {} }.default; -(Object.freeze || Object)(exports); var unrelated; unrelated = "changed"; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js index 5f03bec21402..11c6b4718e83 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import-simple/expected.js @@ -1,5 +1,21 @@ 'use strict'; +const exports = module.exports = Object.create ? Object.create(null, { + __esModule: { + value: true + } +}) : { + __esModule: true +}; + +if (typeof Symbol === "function" && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { + value: "Module" + }); +} + +(Object.freeze || Object)(exports); + require('a'); require('b'); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js index 6e85d8f8b66f..13d212d3381b 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/import/expected.js @@ -1,5 +1,21 @@ 'use strict'; +const exports = module.exports = Object.create ? Object.create(null, { + __esModule: { + value: true + } +}) : { + __esModule: true +}; + +if (typeof Symbol === "function" && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { + value: "Module" + }); +} + +(Object.freeze || Object)(exports); + const _elsewhere = babelHelpers.specRequireInterop(require('./elsewhere')); const _outside = babelHelpers.specRequireInterop(require('./outside')); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/namespace-get/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/namespace-get/expected.js index b1970e9da86e..d1e02c1eba49 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/namespace-get/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/namespace-get/expected.js @@ -1,5 +1,21 @@ 'use strict'; +const exports = module.exports = Object.create ? Object.create(null, { + __esModule: { + value: true + } +}) : { + __esModule: true +}; + +if (typeof Symbol === "function" && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { + value: "Module" + }); +} + +(Object.freeze || Object)(exports); + const _a = babelHelpers.specRequireInterop(require('a')); babelHelpers.specImportCheck(_a, ['a']); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js index 628f4f5986d8..e37be1862631 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/readme/expected.js @@ -32,10 +32,10 @@ Object.defineProperties(exports, { } }); +(Object.freeze || Object)(exports); let _default = { default: function () {} }.default; -(Object.freeze || Object)(exports); require('a'); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js index 11c61e6ebbf7..a1510c85f98a 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/reexports/expected.js @@ -15,27 +15,27 @@ if (typeof Symbol === "function" && Symbol.toStringTag) { } Object.defineProperties(exports, { - namespace: { + renamed: { enumerable: true, get() { - return _somewhere; + return _elsewhere.stuff; } }, - default: { + namespace: { enumerable: true, get() { - return _somewhereElse.stuff; + return _somewhere; } }, - renamed: { + default: { enumerable: true, get() { - return _elsewhere.stuff; + return _somewhereElse.stuff; } }, @@ -53,18 +53,7 @@ const _ownExports = Object.keys(exports); const _iDontKnow = babelHelpers.specRequireInterop(require('./i-dont-know')); -Object.keys(_iDontKnow).forEach(function (key) { - if (key === "__esModule" || key === "default" || _ownExports.indexOf(key) >= 0) return; - if (key in exports && babelHelpers.sameValue(exports[key], _iDontKnow[key])) return; - Object.defineProperty(exports, key, { - enumerable: true, - - get() { - return _iDontKnow[key]; - } - - }); -}); +babelHelpers.specNamespaceSpread(exports, _ownExports, _iDontKnow); (Object.freeze || Object)(exports); const _somewhere = babelHelpers.specRequireInterop(require('./somewhere')); diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/transform-runtime/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/transform-runtime/actual.js new file mode 100644 index 000000000000..ff1f7dfa1590 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/transform-runtime/actual.js @@ -0,0 +1,5 @@ +import { foo } from 'foo' +import * as ns from 'bar' +export * from 'baz' + +ns[true && 'bar'] diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/transform-runtime/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/transform-runtime/expected.js new file mode 100644 index 000000000000..4099ee1003eb --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/transform-runtime/expected.js @@ -0,0 +1,70 @@ +'use strict'; + +const _specNamespaceGet2 = _specRequireInterop(require('babel-runtime/helpers/specNamespaceGet')); + +_specImportCheck(_specNamespaceGet2, ['default']); + +const _specNamespaceSpread2 = _specRequireInterop(require('babel-runtime/helpers/specNamespaceSpread')); + +_specImportCheck(_specNamespaceSpread2, ['default']); + +const _create = _specRequireInterop(require('babel-runtime/core-js/object/create')); + +_specImportCheck(_create, ['default']); + +const _symbol = _specRequireInterop(require('babel-runtime/core-js/symbol')); + +_specImportCheck(_symbol, ['default']); + +const _toStringTag = _specRequireInterop(require('babel-runtime/core-js/symbol/to-string-tag')); + +_specImportCheck(_toStringTag, ['default']); + +const _defineProperty = _specRequireInterop(require('babel-runtime/core-js/object/define-property')); + +_specImportCheck(_defineProperty, ['default']); + +const _defineProperties = _specRequireInterop(require('babel-runtime/core-js/object/define-properties')); + +_specImportCheck(_defineProperties, ['default']); + +const _keys = _specRequireInterop(require('babel-runtime/core-js/object/keys')); + +_specImportCheck(_keys, ['default']); + +const _freeze = _specRequireInterop(require('babel-runtime/core-js/object/freeze')); + +_specImportCheck(_freeze, ['default']); + +const exports = module.exports = _create.default ? (0, _create.default)(null, { + __esModule: { + value: true + } +}) : { + __esModule: true +}; + +if (typeof _symbol.default === "function" && _toStringTag.default) { + (0, _defineProperty.default)(exports, _toStringTag.default, { + value: "Module" + }); +} + +const _ownExports = (0, _keys.default)(exports); + +const _baz = _specRequireInterop(require('baz')); + +(0, _specNamespaceSpread2.default)(exports, _ownExports, _baz); +(_freeze.default || Object)(exports); + +const _foo = _specRequireInterop(require('foo')); + +_specImportCheck(_foo, ['foo']); + +const _bar = _specRequireInterop(require('bar')); + +function _specImportCheck(module, imports) { if (!module.__esModule) throw new Error("Only ES modules can be checked"); var invalid = imports.filter(function (i) { return !Object.prototype.propertyIsEnumerable.call(module, i); }); if (invalid.length > 0) { var error = new Error("Unknown export" + (invalid.length > 1 ? "s " : " ") + JSON.stringify(invalid) + " imported"); error.module = module; throw error; } } + +function _specRequireInterop(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = Object.create ? Object.create(null, { default: { value: obj, writable: true, enumerable: true }, __esModule: { value: true } }) : { default: obj, __esModule: true }; if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(newObj, Symbol.toStringTag, { value: "Module" }); } return (Object.freeze || Object)(newObj); } } + +(0, _specNamespaceGet2.default)(_bar, true && 'bar'); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/transform-runtime/options.json b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/transform-runtime/options.json new file mode 100644 index 000000000000..f0f17f52ca96 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/transform-runtime/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["transform-runtime", ["transform-es2015-modules-commonjs", {"spec": true}]] +} diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/actual.js index ff1f7dfa1590..7bf6c93f17b7 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/actual.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/actual.js @@ -1,5 +1,6 @@ -import { foo } from 'foo' +// NOTE: star reexports are completely broken, but they also are broken in non-spec mode + +export { foo } from 'foo' import * as ns from 'bar' -export * from 'baz' ns[true && 'bar'] diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/expected.js index d0a9f34abfba..b427a9e1c055 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/expected.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/specImport/transform-runtime/expected.js @@ -1,32 +1,29 @@ 'use strict'; -// definitely not this -import _Object$defineProperty from 'babel-runtime/core-js/object/define-property'; -import _Object$keys from 'babel-runtime/core-js/object/keys'; -import _specNamespaceGet from 'babel-runtime/helpers/specNamespaceGet'; Object.defineProperty(exports, "__esModule", { value: true }); -import _specImportCheck from 'babel-runtime/helpers/specImportCheck'; -import _specRequireInterop from 'babel-runtime/helpers/specRequireInterop'; +exports.foo = undefined; -const _baz = _specRequireInterop(require('baz')); +const _specNamespaceGet2 = _specRequireInterop(require('babel-runtime/helpers/specNamespaceGet')); -_Object$keys(_baz).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - - _Object$defineProperty(exports, key, { - enumerable: true, - get: function () { - return _baz[key]; - } - }); -}); +_specImportCheck(_specNamespaceGet2, ['default']); const _foo = _specRequireInterop(require('foo')); _specImportCheck(_foo, ['foo']); +Object.defineProperty(exports, 'foo', { + enumerable: true, + get: function () { + return _foo.foo; + } +}); + const _bar = _specRequireInterop(require('bar')); -_specNamespaceGet(_bar, true && 'bar'); \ No newline at end of file +function _specImportCheck(module, imports) { if (!module.__esModule) throw new Error("Only ES modules can be checked"); var invalid = imports.filter(function (i) { return !Object.prototype.propertyIsEnumerable.call(module, i); }); if (invalid.length > 0) { var error = new Error("Unknown export" + (invalid.length > 1 ? "s " : " ") + JSON.stringify(invalid) + " imported"); error.module = module; throw error; } } + +function _specRequireInterop(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = Object.create ? Object.create(null, { default: { value: obj, writable: true, enumerable: true }, __esModule: { value: true } }) : { default: obj, __esModule: true }; if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(newObj, Symbol.toStringTag, { value: "Module" }); } return (Object.freeze || Object)(newObj); } } + +(0, _specNamespaceGet2.default)(_bar, true && 'bar'); \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js index 57238b7de7eb..ee0aa1f6d928 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js @@ -38,6 +38,7 @@ module.exports.Runner = function Runner (initialModules) { ].concat(extraPlugins), "ast": false, }; + this.fallbackRequire = null; if (initialModules != null) { this.addModules(initialModules); @@ -92,9 +93,14 @@ module.exports.Runner.prototype = { return this.cache[id].module.exports; } if (id in this.modules) { - this.cache[id] = this.makeContext(); - this.transformAndRunInNewContext(this.modules[id], this.cache[id]); - return this.cache[id].module.exports; + const cache = this.makeContext(); + this.transformAndRunInNewContext(this.modules[id], cache); + this.cache[id] = cache; + return cache.module.exports; + } + if (this.fallbackRequire) { + const res = this.fallbackRequire(id); + if (res) return res; } throw new Error("Unmocked module " + id + " required"); } diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-with-transform-runtime.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-with-transform-runtime.js new file mode 100644 index 000000000000..cc77247ccbfc --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-with-transform-runtime.js @@ -0,0 +1,54 @@ +"use strict"; + +const path = require("path"); +const assert = require("assert"); +const helpers = require("./spec-test-helpers"); + +describe("spec with transform runtime", function () { + const runner = new helpers.Runner({ + a: "export default 'a'", + b: "export const b = 'b'", + ab: "export { default as a } from 'a'\nexport { b } from 'b'", + star: "export * from 'a'\nexport * from 'b'" + }); + runner.babelConfig.plugins.push(require("../../babel-plugin-transform-runtime")); + runner.fallbackRequire = function req(id) { + if (id.startsWith("babel-runtime")) { + id = path.resolve(__dirname, "../../", id); + } + return require(id); + }; + + it("throws without fallbackRequire", function () { + const fallback = runner.fallbackRequire; + try { + runner.fallbackRequire = null; + assert.throws(function () { + runner.getExportsOf("a"); + }, /: Unmocked module.*\bbabel-runtime\b/); + } finally { + runner.fallbackRequire = fallback; + } + }); + + it("has correct default exports", function () { + assert.strictEqual(runner.getExportsOf("a").default, "a"); + }); + + it("has correct named exports", function () { + assert.strictEqual(runner.getExportsOf("b").b, "b"); + }); + + it("has correct named reexports", function () { + const exports = runner.getExportsOf("ab"); + assert.strictEqual(exports.a, "a"); + assert.strictEqual(exports.b, "b"); + }); + + it("has correct star reexports", function () { + const exports = runner.getExportsOf("star"); + + assert.deepEqual(Object.keys(exports), [ "b" ]); + assert.strictEqual(exports.b, "b"); + }); +}); From d9336f7fc45b6ada6988938b3e491045b393ddf0 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Tue, 13 Dec 2016 03:01:35 +0900 Subject: [PATCH 097/103] Remove unused variable, update fixture --- .../babel-plugin-transform-es2015-modules-commonjs/src/index.js | 2 -- .../fixtures/preset-options/modules-commonjs-spec/expected.js | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 9b6c3089c418..78892809fd41 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -1015,7 +1015,6 @@ export default function () { if (spec) { const { exportAllSet } = state; - let tail = state.exportPath; if (exportAllSet.size > 0) { const toAdd = new Set(); @@ -1048,7 +1047,6 @@ export default function () { } state.ownExportsPath.insertAfter(Array.from(toAdd)); - tail = state.ownExportsPath.getSibling(state.ownExportsPath.key + toAdd.size); } if (state.exportMap.size > 0) { diff --git a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js index 57432694d213..5da0ef4e0201 100644 --- a/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js +++ b/packages/babel-preset-es2015/test/fixtures/preset-options/modules-commonjs-spec/expected.js @@ -28,8 +28,8 @@ Object.defineProperties(exports, { } } }); +(Object.freeze || Object)(exports); var _default = { default: function _default() {} }.default; -(Object.freeze || Object)(exports); function b() {} \ No newline at end of file From 6066a222f5f4ec61fa3aa4537c7d06f105b1537a Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Wed, 14 Dec 2016 17:01:53 +0900 Subject: [PATCH 098/103] Avoid calling scope.getBinding on Flow types --- .../src/index.js | 24 +++++++----- .../test/fixtures/spec/flow/actual.js | 13 +++++++ .../test/fixtures/spec/flow/expected.js | 38 +++++++++++++++++++ .../test/fixtures/spec/flow/options.json | 6 +++ 4 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/flow/actual.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/flow/expected.js create mode 100644 packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/flow/options.json diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index 78892809fd41..b14380040c61 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -107,22 +107,23 @@ export default function () { let remap = this.remaps[name]; if (!spec && !remap) return; - // redeclared in this scope - if (this.scope.getBinding(name) !== path.scope.getBinding(name)) return; - if (spec) { if (name === "exports" && !path.node[REASSIGN_REMAP_SKIP] && // Apparently replacing "module.exports" still visits the ".exports" here !(this.remaps[".module"] && t.isIdentifier(path.parent, this.remaps[".module"])) && // Avoid entering when it's just some other object key named export - !(isExports(path.parent) && t.isMemberExpression(path.parent.parent))) { + !(isExports(path.parent) && t.isMemberExpression(path.parent.parent)) && + // not shadowed + this.scope.getBinding(name) === path.scope.getBinding(name) + ) { remap = this.remaps[".exports"] = this.remaps[".exports"] || path.scope.generateUidIdentifier("exports"); path.replaceWith(remap); commonjsExportsMasked.exports = remap; return; } if (name === "module" && !path.node[REASSIGN_REMAP_SKIP] && - !(isModuleObj(path.parent) && !path.parent[REASSIGN_REMAP_SKIP] && !t.isMemberExpression(path.parent.parent))) { + !(isModuleObj(path.parent) && !path.parent[REASSIGN_REMAP_SKIP] && !t.isMemberExpression(path.parent.parent)) && + this.scope.getBinding(name) === path.scope.getBinding(name)) { remap = this.remaps[".module"] = this.remaps[".module"] || path.scope.generateUidIdentifier("module"); path.replaceWith(remap); commonjsExportsMasked.module = remap; @@ -132,6 +133,9 @@ export default function () { if (!remap) return; } + // redeclared in this scope + if (this.scope.getBinding(name) !== path.scope.getBinding(name)) return; + if (path.parentPath.isCallExpression({ callee: path.node })) { path.replaceWith(t.sequenceExpression([t.numericLiteral(0), remap])); } else if (path.isJSXIdentifier() && t.isMemberExpression(remap)) { @@ -533,16 +537,16 @@ export default function () { if (namespaceImportSet.size > 0) { path.traverse({ ReferencedIdentifier(path) { - const name = path.node.name; - - // redeclared in this scope - if (scope.getBinding(name) !== path.scope.getBinding(name)) return; - if ( t.isMemberExpression(path.parent) && t.isIdentifier(path.parent.object) && namespaceImportSet.has(path.parent.object.name) ) { + const name = path.parent.object.name; + + // redeclared in this scope + if (!scope.getBinding(name) !== path.scope.getBinding(name)) return; + const { property, computed } = path.parent; if (computed && !t.isStringLiteral(property)) { diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/flow/actual.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/flow/actual.js new file mode 100644 index 000000000000..f1e526aa2a60 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/flow/actual.js @@ -0,0 +1,13 @@ +export type ElementState = { + tagExpr: Object; // tag node + tagName: string; // raw string tag name + args: Array; // array of call arguments + call?: Object; // optional call property that can be set to override the call expression returned + pre?: Function; // function called with (state: ElementState) before building attribs + post?: Function; // function called with (state: ElementState) after building attribs +}; + +import * as types from 'foo' +export type Foo = types.Foo + +exports diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/flow/expected.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/flow/expected.js new file mode 100644 index 000000000000..be9475e0b67b --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/flow/expected.js @@ -0,0 +1,38 @@ +'use strict'; + +const exports = module.exports = Object.create ? Object.create(null, { + __esModule: { + value: true + } +}) : { + __esModule: true +}; + +if (typeof Symbol === "function" && Symbol.toStringTag) { + Object.defineProperty(exports, Symbol.toStringTag, { + value: "Module" + }); +} + +(Object.freeze || Object)(exports); + +const _foo = _specRequireInterop(require('foo')); + +function _specImportCheck(module, imports) { if (!module.__esModule) throw new Error("Only ES modules can be checked"); var invalid = imports.filter(function (i) { return !Object.prototype.propertyIsEnumerable.call(module, i); }); if (invalid.length > 0) { var error = new Error("Unknown export" + (invalid.length > 1 ? "s " : " ") + JSON.stringify(invalid) + " imported"); error.module = module; throw error; } } + +function _specRequireInterop(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = Object.create ? Object.create(null, { default: { value: obj, writable: true, enumerable: true }, __esModule: { value: true } }) : { default: obj, __esModule: true }; if (typeof Symbol === "function" && Symbol.toStringTag) { Object.defineProperty(newObj, Symbol.toStringTag, { value: "Module" }); } return (Object.freeze || Object)(newObj); } } + +type ElementState = { + tagExpr: Object // tag node + ; tagName: string // raw string tag name + ; args: Array // array of call arguments + ; call?: Object // optional call property that can be set to override the call expression returned + ; pre?: Function // function called with (state: ElementState) before building attribs + ; post?: Function // function called with (state: ElementState) after building attribs + ; }; + +type Foo = _foo.Foo; + +_exports; + +let _exports; \ No newline at end of file diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/flow/options.json b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/flow/options.json new file mode 100644 index 000000000000..634c4c8da959 --- /dev/null +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/fixtures/spec/flow/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + "syntax-flow", + [ "transform-es2015-modules-commonjs", { "spec": true } ] + ] +} From 6fa02a8ed9f1267d82322aa130381ea25cb5ef49 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Wed, 14 Dec 2016 17:02:50 +0900 Subject: [PATCH 099/103] Convert spec-test-helpers to ES6 --- .../test/spec-test-helpers.js | 85 +++++++++---------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js index ee0aa1f6d928..b4e753fd86dd 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/test/spec-test-helpers.js @@ -1,5 +1,5 @@ -const babel = require("../../babel-core"); -const vm = require("vm"); +import * as babel from "../../babel-core"; +import vm from "vm"; const extraPlugins = []; @@ -25,70 +25,67 @@ function prepareExtraPlugins () { prepareExtraPlugins(); -module.exports.Runner = function Runner (initialModules) { - if (!(this instanceof Runner)) { - throw new Error("Runner can only be instantiated"); - } - - this.modules = {}; - this.cache = {}; - this.babelConfig = { - "plugins": [ - [require("../"), {spec: true}], - ].concat(extraPlugins), - "ast": false, - }; - this.fallbackRequire = null; - - if (initialModules != null) { - this.addModules(initialModules); +export class Runner { + + constructor (initialModules) { + this.modules = {}; + this.cache = {}; + this.babelConfig = { + "plugins": [ + [require("../"), {spec: true}], + ].concat(extraPlugins), + "ast": false, + }; + this.fallbackRequire = null; + + if (initialModules != null) { + this.addModules(initialModules); + } } -}; -module.exports.Runner.prototype = { - addModule: function (name, code) { + addModule (name, code) { this.modules[name] = code; - }, + } - addModules: function (dict) { + addModules (dict) { for (const key in dict) { if (!Object.prototype.hasOwnProperty.call(dict, key)) continue; this.addModule(key, dict[key]); } - }, + } - addToCache: function (name, context) { + addToCache (name, context) { if (! (context && context.module && "exports" in context.module)) { throw new Error("The context to cache must have a module.exports"); } this.cache[name] = context; - }, + } - getExportsOf: function (name) { + getExportsOf (name) { if (! (name in this.cache || name in this.modules)) { throw new Error("Unknown module " + name + " requested"); } return this.contextRequire(name); - }, + } - transformAndRun: function (code) { + transformAndRun (code) { return this.transformAndRunInNewContext(code, this.makeContext()); - }, + } - transformAndRunInNewContext: function (code, context) { + transformAndRunInNewContext (code, context) { code = babel.transform(code, this.babelConfig).code; vm.runInNewContext(code, context); return context.module.exports; - }, + } - makeContext: function () { + makeContext () { const context = { module: { exports: {} }, require: this.contextRequire.bind(this) }; context.exports = context.module.exports; return context; - }, + } - contextRequire: function (id) { + contextRequire (id) { if (id in this.cache) { return this.cache[id].module.exports; } @@ -104,16 +101,16 @@ module.exports.Runner.prototype = { } throw new Error("Unmocked module " + id + " required"); } -}; +} -let hasToStringTag = null; -module.exports.hasToStringTag = function () { - if (hasToStringTag != null) { - return hasToStringTag; +let hasToStringTagResult = null; +export function hasToStringTag () { + if (hasToStringTagResult != null) { + return hasToStringTagResult; } const context = { module: { exports : {} } }; vm.runInNewContext("module.exports = typeof Symbol === 'function' && Symbol.toStringTag", context); - hasToStringTag = typeof context.module.exports === "symbol"; - return hasToStringTag; -}; + hasToStringTagResult = typeof context.module.exports === "symbol"; + return hasToStringTagResult; +} From d50b59d3840ec72c3000ab8e9e74adb4b54f58ea Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Wed, 14 Dec 2016 20:32:05 +0900 Subject: [PATCH 100/103] Be more proactive with the Invalid import throw Even if the path.remove did work, the import would have been deleted without generating the corresponding require. --- .../src/index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index b14380040c61..fce5305f9f7d 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -360,15 +360,15 @@ export default function () { this.addHelper("specRequireInterop"); this.addHelper("specImportCheck"); - addImportDependency(path, state); - - try { - path.remove(); - } catch (e) { + if (state.ranCommonJS && !state.importMap.has(path.node.source.value)) { throw new Error( `Invalid import ${JSON.stringify(path.node.source.value)} created after commonjs transform finished executing` ); } + + addImportDependency(path, state); + + path.remove(); } }, From 4d9671926c4543a65f05306f7f169cc1c2158549 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Thu, 15 Dec 2016 10:12:55 +0900 Subject: [PATCH 101/103] Remove accidental ! --- .../babel-plugin-transform-es2015-modules-commonjs/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index fce5305f9f7d..4f04588ff7af 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -545,7 +545,7 @@ export default function () { const name = path.parent.object.name; // redeclared in this scope - if (!scope.getBinding(name) !== path.scope.getBinding(name)) return; + if (scope.getBinding(name) !== path.scope.getBinding(name)) return; const { property, computed } = path.parent; From 7b222ac1cb9fe11d0229f2695d3715d1b1ead40e Mon Sep 17 00:00:00 2001 From: Diogo Franco Date: Thu, 19 Jan 2017 11:54:18 +0900 Subject: [PATCH 102/103] Fix SyntaxError --- .../src/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index bc6a40232bec..d992db23c047 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -104,7 +104,7 @@ export default function () { ReferencedIdentifier(path, state) { const spec = isSpec(state); const name = path.node.name; - const remap = this.remaps[name]; + let remap = this.remaps[name]; if (!spec && !remap) return; if (spec) { @@ -1113,4 +1113,4 @@ function isSpec (state) { function isSpecImport (state) { return state && state.opts && (!!state.opts.spec || !!state.opts.specImport); -} \ No newline at end of file +} From 195d824e8cd5a571bb1479333b901b24d6501711 Mon Sep 17 00:00:00 2001 From: Diogo Franco Date: Thu, 19 Jan 2017 12:10:14 +0900 Subject: [PATCH 103/103] Fix invalid this --- .../babel-plugin-transform-es2015-modules-commonjs/src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js index d992db23c047..7d5bef81f6c9 100644 --- a/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js +++ b/packages/babel-plugin-transform-es2015-modules-commonjs/src/index.js @@ -599,7 +599,7 @@ export default function () { const varDecl = t.variableDeclaration(specImport ? "const" : "var", [ t.variableDeclarator(ref, specImport - ? t.callExpression(this.addHelper("specRequireInterop"), [req]) + ? t.callExpression(state.addHelper("specRequireInterop"), [req]) : req ) ]);