import path from "path"; import fs from "fs"; import { createRequire } from "module"; import helpers from "@babel/helpers"; import babel from "@babel/core"; import template from "@babel/template"; import t from "@babel/types"; import { fileURLToPath } from "url"; import transformRuntime from "../lib/index.js"; import buildCorejs2Definitions from "../lib/runtime-corejs2-definitions.js"; import buildCorejs3Definitions from "../lib/runtime-corejs3-definitions.js"; const require = createRequire(import.meta.url); const runtimeVersion = require("@babel/runtime/package.json").version; const corejs2Definitions = buildCorejs2Definitions.default(); const corejs3Definitions = buildCorejs3Definitions.default(); function outputFile(filePath, data) { fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, data); } writeHelpers("@babel/runtime"); writeHelpers("@babel/runtime-corejs2", { corejs: 2 }); writeHelpers("@babel/runtime-corejs3", { corejs: { version: 3, proposals: true }, }); writeCoreJS({ corejs: 2, proposals: true, definitions: corejs2Definitions, paths: [ "is-iterable", "get-iterator", // This was previously in definitions, but was removed to work around // zloirock/core-js#262. We need to keep it in @babel/runtime-corejs2 to // avoid a breaking change there. "symbol/async-iterator", ], corejsRoot: "core-js/library/fn", }); writeCoreJS({ corejs: 3, proposals: false, definitions: corejs3Definitions, paths: [], corejsRoot: "core-js-pure/stable", }); writeCoreJS({ corejs: 3, proposals: true, definitions: corejs3Definitions, paths: ["is-iterable", "get-iterator", "get-iterator-method"], corejsRoot: "core-js-pure/features", }); function writeCoreJS({ corejs, proposals, definitions: { BuiltIns, StaticProperties, InstanceProperties }, paths, corejsRoot, }) { const pkgDirname = getRuntimeRoot(`@babel/runtime-corejs${corejs}`); Object.keys(BuiltIns).forEach(name => { const { stable, path } = BuiltIns[name]; if (stable || proposals) paths.push(path); }); Object.keys(StaticProperties).forEach(builtin => { const props = StaticProperties[builtin]; Object.keys(props).forEach(name => { const { stable, path } = props[name]; if (stable || proposals) paths.push(path); }); }); if (InstanceProperties) { Object.keys(InstanceProperties).forEach(name => { const { stable, path } = InstanceProperties[name]; if (stable || proposals) paths.push(`instance/${path}`); }); } const runtimeRoot = proposals ? "core-js" : "core-js-stable"; paths.forEach(function (corejsPath) { outputFile( path.join(pkgDirname, runtimeRoot, `${corejsPath}.js`), `module.exports = require("${corejsRoot}/${corejsPath}");` ); }); writeCorejsExports(pkgDirname, runtimeRoot, paths); } function writeCorejsExports(pkgDirname, runtimeRoot, paths) { const pkgJsonPath = require.resolve(`${pkgDirname}/package.json`); const pkgJson = require(pkgJsonPath); const exports = pkgJson.exports; // Export `./core-js/` so `import "@babel/runtime-corejs3/core-js/some-feature.js"` works exports[`./${runtimeRoot}/`] = `./${runtimeRoot}/`; for (const corejsPath of paths) { // Export `./core-js/some-feature` so `import "@babel/runtime-corejs3/core-js/some-feature"` also works const corejsExportPath = `./${runtimeRoot}/${corejsPath}`; exports[corejsExportPath] = corejsExportPath + ".js"; } pkgJson.exports = exports; outputFile(pkgJsonPath, JSON.stringify(pkgJson, undefined, 2) + "\n"); } function writeHelpers(runtimeName, { corejs } = {}) { const helperPaths = writeHelperFiles(runtimeName, { corejs, esm: false }); const helperESMPaths = writeHelperFiles(runtimeName, { corejs, esm: true }); writeHelperExports(runtimeName, helperPaths.concat(helperESMPaths)); } function writeHelperExports(runtimeName, helperPaths) { const helperSubExports = {}; for (const helperPath of helperPaths) { helperSubExports[helperPath.replace(".js", "")] = helperPath; } const exports = { "./helpers/": "./helpers/", ...helperSubExports, "./package": "./package.json", "./package.json": "./package.json", "./regenerator": "./regenerator/index.js", "./regenerator/": "./regenerator/", }; const pkgDirname = getRuntimeRoot(runtimeName); const pkgJsonPath = require.resolve(`${pkgDirname}/package.json`); const pkgJson = require(pkgJsonPath); pkgJson.exports = exports; outputFile(pkgJsonPath, JSON.stringify(pkgJson, undefined, 2) + "\n"); } function writeHelperFiles(runtimeName, { esm, corejs }) { const pkgDirname = getRuntimeRoot(runtimeName); const helperPaths = []; for (const helperName of helpers.list) { const helperPath = "./" + path.join("helpers", esm ? "esm" : "", `${helperName}.js`); const helperFilename = path.join(pkgDirname, helperPath); outputFile( helperFilename, buildHelper(runtimeName, pkgDirname, helperFilename, helperName, { esm, corejs, }) ); helperPaths.push(helperPath); } return helperPaths; } function getRuntimeRoot(runtimeName) { return path.resolve( path.dirname(fileURLToPath(import.meta.url)), "..", "..", runtimeName.replace(/^@babel\//, "babel-") ); } function buildHelper( runtimeName, pkgDirname, helperFilename, helperName, { esm, corejs } ) { const tree = t.program([], [], esm ? "module" : "script"); const dependencies = {}; let bindings = null; if (!esm) { bindings = []; helpers.ensure(helperName, babel.File); for (const dep of helpers.getDependencies(helperName)) { const id = (dependencies[dep] = t.identifier(t.toIdentifier(dep))); tree.body.push(template.statement.ast` var ${id} = require("${`./${dep}`}"); `); bindings.push(id.name); } } const helper = helpers.get( helperName, dep => dependencies[dep], esm ? null : template.expression.ast`module.exports`, bindings ); tree.body.push(...helper.nodes); return babel.transformFromAst(tree, null, { filename: helperFilename, presets: [ [ "@babel/preset-env", { modules: false, exclude: ["@babel/plugin-transform-typeof-symbol"] }, ], ], plugins: [ [ transformRuntime, { corejs, useESModules: esm, version: runtimeVersion }, ], buildRuntimeRewritePlugin(runtimeName, helperName, esm), ], overrides: [ { exclude: /typeof/, plugins: ["@babel/plugin-transform-typeof-symbol"], }, ], }).code; } function buildRuntimeRewritePlugin(runtimeName, helperName, esm) { const helperPath = esm ? "helpers/esm" : "helpers"; /** * rewrite helpers imports to runtime imports * @example * adjustImportPath(ast`"setPrototypeOf"`) * // returns ast`"@babel/runtime/helpers/esm/setPrototypeOf"` * @param {*} node The string literal contains import path */ function adjustImportPath(node) { if (helpers.list.includes(node.value)) { node.value = `${runtimeName}/${helperPath}/${node.value}`; } } return { pre(file) { const original = file.get("helperGenerator"); file.set("helperGenerator", name => { // make sure that helpers won't insert circular references to themselves if (name === helperName) return false; return original(name); }); }, visitor: { ImportDeclaration(path) { adjustImportPath(path.get("source").node); }, CallExpression(path) { if ( !path.get("callee").isIdentifier({ name: "require" }) || path.get("arguments").length !== 1 || !path.get("arguments")[0].isStringLiteral() ) { return; } // replace reference to internal helpers with @babel/runtime import path adjustImportPath(path.get("arguments")[0].node); }, }, }; }