Add useBuiltIns and useESModules options to transform-runtime

useBuiltIns uses versions of the helpers that do not import even
internal polyfills from core-js.

useESModules uses versions of the helpers that do not go through
transform-es2015-modules-commonjs. This allows for smaller builds in
module systems like webpack, as it doesn't need to preserve commonjs
semantics.

This includes changes to the babel-runtime build-dist script, which
will build the versions of the runtime helpers to be used by
combinations of useBuiltIns and useESModules.
This commit is contained in:
Diogo Franco (Kovensky)
2017-03-10 16:27:35 +09:00
parent 12eb25c06c
commit cf5ab16ff9
2 changed files with 61 additions and 27 deletions

View File

@@ -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;

View File

@@ -46,16 +46,23 @@ function writeFile(filename, content) {
return writeRootFile(filename, content);
}
var transformOpts = {
presets: [
require("../../babel-preset-es2015")
],
function makeTransformOpts(modules = "commonjs", useBuiltIns = false) {
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 = "commonjs", useBuiltIns = false) {
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);
}
}
}