diff --git a/.gitignore b/.gitignore index 12a252a120..22345efbcd 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ dist /.package.json /packages/babel-runtime/core-js /packages/babel-runtime/helpers/*.js +/packages/babel-runtime/helpers/builtin/*.js +/packages/babel-runtime/helpers/builtin/es6/*.js +/packages/babel-runtime/helpers/es6/*.js /packages/babel-register/test/.babel /packages/*/lib _babel.github.io diff --git a/packages/babel-plugin-transform-runtime/src/index.js b/packages/babel-plugin-transform-runtime/src/index.js index 98976b52af..c9737af8a4 100644 --- a/packages/babel-plugin-transform-runtime/src/index.js +++ b/packages/babel-plugin-transform-runtime/src/index.js @@ -16,13 +16,19 @@ export default function ({ types: t }) { const moduleName = getRuntimeModuleName(this.opts); if (this.opts.helpers !== false) { + const baseHelpersDir = this.opts.useBuiltIns ? "helpers/builtin" : "helpers"; + const helpersDir = this.opts.useESModules ? `${baseHelpersDir}/es6` : baseHelpersDir; file.set("helperGenerator", function (name) { if (HELPER_BLACKLIST.indexOf(name) < 0) { - return file.addImport(`${moduleName}/helpers/${name}`, "default", name); + return file.addImport(`${moduleName}/${helpersDir}/${name}`, "default", name); } }); } + if (this.opts.polyfill && this.opts.useBuiltIns) { + throw new Error("The polyfill option conflicts with useBuiltIns; use one or the other"); + } + this.setDynamic("regeneratorIdentifier", function () { return file.addImport(`${moduleName}/regenerator`, "default", "regeneratorRuntime"); }); @@ -37,7 +43,7 @@ export default function ({ types: t }) { return; } - if (state.opts.polyfill === false) return; + if (state.opts.polyfill === false || state.opts.useBuiltIns) return; if (t.isMemberExpression(parent)) return; if (!has(definitions.builtins, node.name)) return; @@ -54,7 +60,7 @@ export default function ({ types: t }) { // arr[Symbol.iterator]() -> _core.$for.getIterator(arr) CallExpression(path, state) { - if (state.opts.polyfill === false) return; + if (state.opts.polyfill === false || state.opts.useBuiltIns) return; // we can't compile this if (path.node.arguments.length) return; @@ -77,7 +83,7 @@ export default function ({ types: t }) { // Symbol.iterator in arr -> core.$for.isIterable(arr) BinaryExpression(path, state) { - if (state.opts.polyfill === false) return; + if (state.opts.polyfill === false || state.opts.useBuiltIns) return; if (path.node.operator !== "in") return; if (!path.get("left").matchesPattern("Symbol.iterator")) return; @@ -96,7 +102,7 @@ export default function ({ types: t }) { // Array.from -> _core.Array.from MemberExpression: { enter(path, state) { - if (state.opts.polyfill === false) return; + if (state.opts.polyfill === false || state.opts.useBuiltIns) return; if (!path.isReferenced()) return; const { node } = path; @@ -128,7 +134,7 @@ export default function ({ types: t }) { }, exit(path, state) { - if (state.opts.polyfill === false) return; + if (state.opts.polyfill === false || state.opts.useBuiltIns) return; if (!path.isReferenced()) return; const { node } = path; diff --git a/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns-useESModules/actual.js b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns-useESModules/actual.js new file mode 100644 index 0000000000..dcde0c7bf9 --- /dev/null +++ b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns-useESModules/actual.js @@ -0,0 +1 @@ +class Foo extends Bar {} diff --git a/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns-useESModules/expected.js b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns-useESModules/expected.js new file mode 100644 index 0000000000..4a14d5a038 --- /dev/null +++ b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns-useESModules/expected.js @@ -0,0 +1,15 @@ +import _classCallCheck from "babel-runtime/helpers/builtin/es6/classCallCheck"; +import _possibleConstructorReturn from "babel-runtime/helpers/builtin/es6/possibleConstructorReturn"; +import _inherits from "babel-runtime/helpers/builtin/es6/inherits"; + +let Foo = function (_Bar) { + _inherits(Foo, _Bar); + + function Foo() { + _classCallCheck(this, Foo); + + return _possibleConstructorReturn(this, (Foo.__proto__ || Object.getPrototypeOf(Foo)).apply(this, arguments)); + } + + return Foo; +}(Bar); diff --git a/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns-useESModules/options.json b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns-useESModules/options.json new file mode 100644 index 0000000000..9780ad32b9 --- /dev/null +++ b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns-useESModules/options.json @@ -0,0 +1,3 @@ +{ + "plugins": [["transform-runtime", { "useBuiltIns": true, "useESModules": true }], "transform-es2015-classes"] +} diff --git a/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns/actual.js b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns/actual.js new file mode 100644 index 0000000000..dcde0c7bf9 --- /dev/null +++ b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns/actual.js @@ -0,0 +1 @@ +class Foo extends Bar {} diff --git a/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns/expected.js b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns/expected.js new file mode 100644 index 0000000000..69aca59986 --- /dev/null +++ b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns/expected.js @@ -0,0 +1,15 @@ +import _classCallCheck from "babel-runtime/helpers/builtin/classCallCheck"; +import _possibleConstructorReturn from "babel-runtime/helpers/builtin/possibleConstructorReturn"; +import _inherits from "babel-runtime/helpers/builtin/inherits"; + +let Foo = function (_Bar) { + _inherits(Foo, _Bar); + + function Foo() { + _classCallCheck(this, Foo); + + return _possibleConstructorReturn(this, (Foo.__proto__ || Object.getPrototypeOf(Foo)).apply(this, arguments)); + } + + return Foo; +}(Bar); \ No newline at end of file diff --git a/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns/options.json b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns/options.json new file mode 100644 index 0000000000..a3df912d0a --- /dev/null +++ b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useBuiltIns/options.json @@ -0,0 +1,3 @@ +{ + "plugins": [["transform-runtime", { "useBuiltIns": true }], "transform-es2015-classes"] +} diff --git a/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useESModules/actual.js b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useESModules/actual.js new file mode 100644 index 0000000000..dcde0c7bf9 --- /dev/null +++ b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useESModules/actual.js @@ -0,0 +1 @@ +class Foo extends Bar {} diff --git a/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useESModules/expected.js b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useESModules/expected.js new file mode 100644 index 0000000000..6673b09141 --- /dev/null +++ b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useESModules/expected.js @@ -0,0 +1,16 @@ +import _Object$getPrototypeOf from "babel-runtime/core-js/object/get-prototype-of"; +import _classCallCheck from "babel-runtime/helpers/es6/classCallCheck"; +import _possibleConstructorReturn from "babel-runtime/helpers/es6/possibleConstructorReturn"; +import _inherits from "babel-runtime/helpers/es6/inherits"; + +let Foo = function (_Bar) { + _inherits(Foo, _Bar); + + function Foo() { + _classCallCheck(this, Foo); + + return _possibleConstructorReturn(this, (Foo.__proto__ || _Object$getPrototypeOf(Foo)).apply(this, arguments)); + } + + return Foo; +}(Bar); \ No newline at end of file diff --git a/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useESModules/options.json b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useESModules/options.json new file mode 100644 index 0000000000..11076eaa11 --- /dev/null +++ b/packages/babel-plugin-transform-runtime/test/fixtures/use-options/useESModules/options.json @@ -0,0 +1,3 @@ +{ + "plugins": [["transform-runtime", { "useESModules": true }], "transform-es2015-classes"] +} diff --git a/packages/babel-runtime/scripts/build-dist.js b/packages/babel-runtime/scripts/build-dist.js index 050aa73b21..3aee330207 100644 --- a/packages/babel-runtime/scripts/build-dist.js +++ b/packages/babel-runtime/scripts/build-dist.js @@ -46,16 +46,23 @@ function writeFile(filename, content) { return writeRootFile(filename, content); } -var transformOpts = { - presets: [ - require("../../babel-preset-es2015") - ], +function makeTransformOpts(modules, useBuiltIns) { + const opts = { + presets: [ + [require("../../babel-preset-es2015"), { modules: false }] + ], - plugins: [ - require("../../babel-plugin-transform-runtime"), - [require("../../babel-plugin-transform-es2015-modules-commonjs"), { loose: true, strict: false }] - ] -}; + plugins: [ + [require("../../babel-plugin-transform-runtime"), { useBuiltIns: useBuiltIns, useESModules: modules === false }] + ] + } + if (modules === 'commonjs') { + opts.plugins.push([require("../../babel-plugin-transform-es2015-modules-commonjs"), { loose: true, strict: false }]) + } else if (modules !== false) { + throw new Error('Unsupported module type') + } + return opts +} function buildRuntimeRewritePlugin(relativePath, helperName) { return { @@ -63,12 +70,16 @@ function buildRuntimeRewritePlugin(relativePath, helperName) { var original = file.get("helperGenerator"); file.set("helperGenerator", function(name) { // make sure that helpers won't insert circular references to themselves - if (name === helperName) return; + if (name === helperName) return false; return original(name); }); }, visitor: { + ImportDeclaration: function(path){ + path.get("source").node.value = path.get("source").node.value + .replace(/^babel-runtime/, relativePath); + }, CallExpression: function(path){ if (!path.get("callee").isIdentifier({name: "require"}) || path.get("arguments").length !== 1 || @@ -82,23 +93,40 @@ function buildRuntimeRewritePlugin(relativePath, helperName) { }; } -function buildHelper(helperName) { +function buildHelper(helperName, modules, useBuiltIns) { + const helper = helpers.get(helperName) + // avoid an unneccessary TDZ in the easy case + if (helper.type === "FunctionExpression") { + helper.type = "FunctionDeclaration" + } var tree = t.program([ - t.exportDefaultDeclaration(helpers.get(helperName)) + t.exportDefaultDeclaration(helper) ]); + const transformOpts = makeTransformOpts(modules, useBuiltIns) + + const relative = useBuiltIns ? "../.." : ".." + return babel.transformFromAst(tree, null, { presets: transformOpts.presets, - plugins: transformOpts.plugins.concat([buildRuntimeRewritePlugin("..", helperName)]) + plugins: transformOpts.plugins.concat([buildRuntimeRewritePlugin(modules === false ? `../${relative}` : relative, helperName)]) }).code; } -helpers.list.forEach(function (helperName) { - writeFile("helpers/" + helperName + ".js", buildHelper(helperName)); +for (const modules of ["commonjs", false]) { + for (const builtin of [false, true]) { + const dirname = `helpers/${builtin ? 'builtin/' : ''}${!modules ? 'es6/' : ''}` - // compat - var helperAlias = kebabCase(helperName); - var content = "module.exports = require(\"./" + helperName + ".js\");"; - writeFile("helpers/_" + helperAlias + ".js", content); - if (helperAlias !== helperName) writeFile("helpers/" + helperAlias + ".js", content); -}); + for (const helperName of helpers.list) { + writeFile(`${dirname}${helperName}.js`, buildHelper(helperName, modules, builtin)); + + // compat + var helperAlias = kebabCase(helperName); + var content = !modules + ? `export { default } from \"./${helperName}.js\";` + : "module.exports = require(\"./" + helperName + ".js\");"; + writeFile(`${dirname}_${helperAlias}.js`, content); + if (helperAlias !== helperName) writeFile(`${dirname}${helperAlias}.js`, content); + } + } +}