"use strict"; const plumber = require("gulp-plumber"); const through = require("through2"); const chalk = require("chalk"); const newer = require("gulp-newer"); const babel = require("gulp-babel"); const camelCase = require("lodash/camelCase"); const fancyLog = require("fancy-log"); const filter = require("gulp-filter"); const gulp = require("gulp"); const path = require("path"); const fs = require("fs"); const rollup = require("rollup"); const rollupBabel = require("@rollup/plugin-babel").default; const rollupBabelSource = require("./scripts/rollup-plugin-babel-source"); const rollupCommonJs = require("@rollup/plugin-commonjs"); const rollupJson = require("@rollup/plugin-json"); const rollupNodePolyfills = require("rollup-plugin-node-polyfills"); const rollupNodeResolve = require("@rollup/plugin-node-resolve").default; const rollupReplace = require("@rollup/plugin-replace"); const { terser: rollupTerser } = require("rollup-plugin-terser"); const { default: rollupDts } = require("rollup-plugin-dts"); const defaultPackagesGlob = "./@(codemods|packages|eslint)/*"; const defaultSourcesGlob = `${defaultPackagesGlob}/src/**/{*.js,!(*.d).ts}`; const defaultDtsGlob = `${defaultPackagesGlob}/lib/**/*.d.ts{,.map}`; const babelStandalonePluginConfigGlob = "./packages/babel-standalone/scripts/pluginConfig.json"; const buildTypingsWatchGlob = [ "./packages/babel-types/lib/definitions/**/*.js", "./packages/babel-types/scripts/generators/*.js", ]; /** * map source code path to the generated artifacts path * @example * mapSrcToLib("packages/babel-core/src/index.js") * // returns "packages/babel-core/lib/index.js" * @example * mapSrcToLib("packages/babel-template/src/index.ts") * // returns "packages/babel-template/lib/index.js" * @example * mapSrcToLib("packages/babel-template/src/index.d.ts") * // returns "packages/babel-template/lib/index.d.ts" * @param {string} srcPath * @returns {string} */ function mapSrcToLib(srcPath) { const parts = srcPath.replace(/(? { fancyLog("Generating @babel/standalone files"); const pluginConfig = JSON.parse(file.contents); let imports = ""; let list = ""; let allList = ""; for (const plugin of pluginConfig) { const camelPlugin = camelCase(plugin); imports += `import ${camelPlugin} from "@babel/plugin-${plugin}";`; list += `${camelPlugin},`; allList += `"${plugin}": ${camelPlugin},`; } const fileContents = `/* * This file is auto-generated! Do not modify it directly. * To re-generate run 'yarn gulp generate-standalone' */ ${imports} export {${list}}; export const all = {${allList}};`; file.path = "plugins.js"; file.contents = Buffer.from(formatCode(fileContents, dest)); callback(null, file); }) ) .pipe(gulp.dest(dest)); } function unlink() { return through.obj(function (file, enc, callback) { fs.unlink(file.path, () => callback()); }); } function finish(stream) { return new Promise((resolve, reject) => { stream.on("end", resolve); stream.on("finish", resolve); stream.on("error", reject); }); } function getFiles(glob, { include, exclude }) { let stream = gulp.src(glob, { base: __dirname }); if (exclude) { const filters = exclude.map(p => `!**/${p}/**`); filters.unshift("**"); stream = stream.pipe(filter(filters)); } if (include) { const filters = include.map(p => `**/${p}/**`); stream = stream.pipe(filter(filters)); } return stream; } function buildBabel(exclude) { const base = __dirname; return getFiles(defaultSourcesGlob, { exclude: exclude && exclude.map(p => p.src), }) .pipe(errorsLogger()) .pipe(newer({ dest: base, map: mapSrcToLib })) .pipe(compilationLogger()) .pipe( babel({ caller: { // We have wrapped packages/babel-core/src/config/files/configuration.js with feature detection supportsDynamicImport: true, }, }) ) .pipe( // Passing 'file.relative' because newer() above uses a relative // path and this keeps it consistent. rename(file => path.resolve(file.base, mapSrcToLib(file.relative))) ) .pipe(gulp.dest(base)); } // If this build is part of a pull request, include the pull request number in // the version number. let versionSuffix = ""; if (process.env.CIRCLE_PR_NUMBER) { versionSuffix = "+pr." + process.env.CIRCLE_PR_NUMBER; } const babelVersion = require("./packages/babel-core/package.json").version + versionSuffix; function buildRollup(packages, targetBrowsers) { const sourcemap = process.env.NODE_ENV === "production"; return Promise.all( packages.map(async ({ src, format, dest, name, filename }) => { const pkgJSON = require("./" + src + "/package.json"); const version = pkgJSON.version + versionSuffix; const { dependencies = {}, peerDependencies = {} } = pkgJSON; const external = Object.keys(dependencies).concat( Object.keys(peerDependencies) ); let nodeResolveBrowser = false, babelEnvName = "rollup"; switch (src) { case "packages/babel-standalone": nodeResolveBrowser = true; babelEnvName = "standalone"; break; } const input = getIndexFromPackage(src); fancyLog(`Compiling '${chalk.cyan(input)}' with rollup ...`); const bundle = await rollup.rollup({ input, external, onwarn(warning, warn) { if (warning.code !== "CIRCULAR_DEPENDENCY") { warn(warning); // https://github.com/babel/babel/pull/12011#discussion_r540434534 throw new Error("Rollup aborted due to warnings above"); } }, plugins: [ rollupBabelSource(), rollupReplace({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV), BABEL_VERSION: JSON.stringify(babelVersion), VERSION: JSON.stringify(version), }), rollupBabel({ envName: babelEnvName, babelrc: false, babelHelpers: "bundled", extends: "./babel.config.js", extensions: [".mjs", ".cjs", ".ts", ".js"], }), rollupNodeResolve({ extensions: [".mjs", ".cjs", ".ts", ".js", ".json"], browser: nodeResolveBrowser, preferBuiltins: true, }), rollupCommonJs({ include: [ /node_modules/, "packages/babel-runtime/regenerator/**", "packages/babel-preset-env/data/*.js", // Rollup doesn't read export maps, so it loads the cjs fallback "packages/babel-compat-data/*.js", ], }), rollupJson(), targetBrowsers && rollupNodePolyfills({ sourceMap: sourcemap, include: "**/*.{js,ts}", }), ].filter(Boolean), }); const outputFile = path.join(src, dest, filename || "index.js"); await bundle.write({ file: outputFile, format, name, sourcemap: sourcemap, exports: "named", }); if (!process.env.IS_PUBLISH) { fancyLog( chalk.yellow( `Skipped minification of '${chalk.cyan( outputFile )}' because not publishing` ) ); return undefined; } fancyLog(`Minifying '${chalk.cyan(outputFile)}'...`); await bundle.write({ file: outputFile.replace(/\.js$/, ".min.js"), format, name, sourcemap: sourcemap, exports: "named", plugins: [ rollupTerser({ // workaround https://bugs.webkit.org/show_bug.cgi?id=212725 output: { ascii_only: true, }, }), ], }); }) ); } function buildRollupDts(packages) { const sourcemap = process.env.NODE_ENV === "production"; return Promise.all( packages.map(async packageName => { const input = `${packageName}/lib/index.d.ts`; fancyLog(`Bundling '${chalk.cyan(input)}' with rollup ...`); const bundle = await rollup.rollup({ input, plugins: [rollupDts()], }); await finish( gulp.src(`${packageName}/lib/**/*.d.ts{,.map}`).pipe(unlink()) ); await bundle.write({ file: `${packageName}/lib/index.d.ts`, format: "es", sourcemap: sourcemap, exports: "named", }); }) ); } function removeDts(exclude) { return getFiles(defaultDtsGlob, { exclude }).pipe(unlink()); } function copyDts(packages) { return getFiles(`${defaultPackagesGlob}/src/**/*.d.ts`, { include: packages }) .pipe(rename(file => path.resolve(file.base, mapSrcToLib(file.relative)))) .pipe(gulp.dest(__dirname)); } const libBundles = [ "packages/babel-parser", "packages/babel-plugin-proposal-optional-chaining", "packages/babel-preset-typescript", "packages/babel-helper-member-expression-to-functions", ].map(src => ({ src, format: "cjs", dest: "lib", })); const dtsBundles = ["packages/babel-types"]; const standaloneBundle = [ { src: "packages/babel-standalone", format: "umd", name: "Babel", filename: "babel.js", dest: "", version: babelVersion, }, ]; gulp.task("generate-type-helpers", () => { fancyLog("Generating @babel/types dynamic functions"); return Promise.all([ generateTypeHelpers("asserts"), generateTypeHelpers("builders"), generateTypeHelpers("builders", "uppercase.js"), generateTypeHelpers("constants"), generateTypeHelpers("validators"), generateTypeHelpers("ast-types"), ]); }); gulp.task("generate-standalone", () => generateStandalone()); gulp.task("build-rollup", () => buildRollup(libBundles)); gulp.task("rollup-babel-standalone", () => buildRollup(standaloneBundle, true)); gulp.task( "build-babel-standalone", gulp.series("generate-standalone", "rollup-babel-standalone") ); gulp.task("copy-dts", () => copyDts(dtsBundles)); gulp.task( "bundle-dts", gulp.series("copy-dts", () => buildRollupDts(dtsBundles)) ); gulp.task("clean-dts", () => removeDts(/* exclude */ dtsBundles)); gulp.task("build-babel", () => buildBabel(/* exclude */ libBundles)); gulp.task( "build", gulp.series( gulp.parallel("build-rollup", "build-babel"), gulp.parallel( "generate-standalone", gulp.series( "generate-type-helpers", // rebuild @babel/types since type-helpers may be changed "build-babel" ) ) ) ); gulp.task("default", gulp.series("build")); gulp.task("build-no-bundle", () => buildBabel()); gulp.task( "build-dev", gulp.series( "build-no-bundle", gulp.parallel( "generate-standalone", gulp.series( "generate-type-helpers", // rebuild @babel/types since type-helpers may be changed "build-no-bundle" ) ) ) ); gulp.task( "watch", gulp.series("build-dev", function watch() { gulp.watch(defaultSourcesGlob, gulp.task("build-no-bundle")); gulp.watch( babelStandalonePluginConfigGlob, gulp.task("generate-standalone") ); gulp.watch(buildTypingsWatchGlob, gulp.task("generate-type-helpers")); }) );