From 81a4d455f3e1d689852289bf562dae26c33e893a Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Sun, 25 Sep 2022 11:50:59 +0800 Subject: [PATCH] Build: Refactor build script (#13513) --- ...bundler.mjs => build-javascript-module.js} | 164 ++++------- .../{save-licenses.mjs => build-license.js} | 75 +++-- scripts/build/build-package-json.js | 40 +++ scripts/build/build.mjs | 255 +++++++--------- scripts/build/config.mjs | 278 ++++++++++++------ 5 files changed, 451 insertions(+), 361 deletions(-) rename scripts/build/{bundler.mjs => build-javascript-module.js} (60%) rename scripts/build/{save-licenses.mjs => build-license.js} (50%) create mode 100644 scripts/build/build-package-json.js diff --git a/scripts/build/bundler.mjs b/scripts/build/build-javascript-module.js similarity index 60% rename from scripts/build/bundler.mjs rename to scripts/build/build-javascript-module.js index 4f97b737c..c81526a99 100644 --- a/scripts/build/bundler.mjs +++ b/scripts/build/build-javascript-module.js @@ -13,23 +13,17 @@ import esbuildPluginVisualizer from "./esbuild-plugins/visualizer.mjs"; import esbuildPluginStripNodeProtocol from "./esbuild-plugins/strip-node-protocol.mjs"; import esbuildPluginThrowWarnings from "./esbuild-plugins/throw-warnings.mjs"; import esbuildPluginShimCommonjsObjects from "./esbuild-plugins/shim-commonjs-objects.mjs"; -import bundles from "./config.mjs"; import transform from "./transform/index.js"; const { dirname, readJsonSync, require } = createEsmUtils(import.meta); const packageJson = readJsonSync("../../package.json"); -const umdTarget = browserslistToEsbuild(packageJson.browserslist); +const universalTarget = browserslistToEsbuild(packageJson.browserslist); -const bundledFiles = [ - ...bundles, - { input: "package.json", output: "package.json" }, -].map(({ input, output }) => ({ - input: path.join(PROJECT_ROOT, input), - output: `./${output}`, -})); +function getEsbuildOptions({ file, files, shouldCollectLicenses, cliOptions }) { + // Save dependencies to file + file.dependencies = []; -function* getEsbuildOptions(bundle, buildOptions) { const replaceModule = [ // Use `require` directly { @@ -70,11 +64,11 @@ function* getEsbuildOptions(bundle, buildOptions) { ]; const define = { - "process.env.PRETTIER_TARGET": JSON.stringify(bundle.target), + "process.env.PRETTIER_TARGET": JSON.stringify(file.platform), "process.env.NODE_ENV": JSON.stringify("production"), }; - if (bundle.target === "universal") { + if (file.platform === "universal") { // We can't reference `process` in UMD bundles and this is // an undocumented "feature" replaceModule.push({ @@ -94,24 +88,30 @@ function* getEsbuildOptions(bundle, buildOptions) { define.__dirname = JSON.stringify("/prettier-security-dirname-placeholder"); } - // Replace other bundled files - if (bundle.target === "node") { - // Replace other bundled files and `package.json` with dynamic `require()` - for (let { input, output } of bundledFiles) { - if (input === path.join(PROJECT_ROOT, bundle.input)) { - continue; - } + if (file.platform === "node") { + // External other bundled files + replaceModule.push( + ...files + .filter( + (bundle) => + bundle.input === "package.json" || + (file.input !== bundle.input && bundle.output.format === "esm") + ) + .map((bundle) => { + let output = bundle.output.file; + if ( + file.output.file === "index.cjs" && + bundle.output.file === "esm/doc.mjs" + ) { + output = "doc.js"; + } - if (output === "./doc.js" && bundle.output !== "index.cjs") { - output = "./esm/doc.mjs"; - } - - if (output.startsWith("./parser-")) { - output = `./esm/${output.slice(2).replace(".js", ".mjs")}`; - } - - replaceModule.push({ module: input, external: output }); - } + return { + module: path.join(PROJECT_ROOT, bundle.input), + external: `./${output}`, + }; + }) + ); } else { replaceModule.push( // When running build script with `--no-minify`, `esbuildPluginNodeModulePolyfills` shim `module` module incorrectly @@ -151,16 +151,12 @@ function* getEsbuildOptions(bundle, buildOptions) { } } - let shouldMinify = buildOptions.minify; - if (typeof shouldMinify !== "boolean") { - shouldMinify = bundle.minify !== false && bundle.target === "universal"; - } - - const interopDefault = - !bundle.input.endsWith(".cjs") && bundle.interopDefault !== false; + const { buildOptions } = file; + const shouldMinify = + cliOptions.minify ?? buildOptions.minify ?? file.platform === "universal"; const esbuildOptions = { - entryPoints: [path.join(PROJECT_ROOT, bundle.input)], + entryPoints: [path.join(PROJECT_ROOT, file.input)], define, bundle: true, metafile: true, @@ -168,102 +164,68 @@ function* getEsbuildOptions(bundle, buildOptions) { esbuildPluginEvaluate(), esbuildPluginStripNodeProtocol(), esbuildPluginReplaceModule({ - replacements: [...replaceModule, ...(bundle.replaceModule ?? [])], + replacements: [...replaceModule, ...(buildOptions.replaceModule ?? [])], }), - bundle.target === "universal" && esbuildPluginNodeModulePolyfills(), - buildOptions.onLicenseFound && + file.platform === "universal" && esbuildPluginNodeModulePolyfills(), + shouldCollectLicenses && esbuildPluginLicense({ cwd: PROJECT_ROOT, thirdParty: { includePrivate: true, - output: buildOptions.onLicenseFound, + output: (dependencies) => file.dependencies.push(...dependencies), }, }), - buildOptions.reports && - esbuildPluginVisualizer({ formats: buildOptions.reports }), + cliOptions.reports && + esbuildPluginVisualizer({ formats: cliOptions.reports }), esbuildPluginThrowWarnings({ - allowDynamicRequire: bundle.target === "node", - allowDynamicImport: bundle.target === "node", + allowDynamicRequire: file.platform === "node", + allowDynamicImport: file.platform === "node", }), ].filter(Boolean), minify: shouldMinify, legalComments: "none", - external: ["pnpapi", ...(bundle.external ?? [])], + external: ["pnpapi", ...(buildOptions.external ?? [])], // Disable esbuild auto discover `tsconfig.json` file tsconfig: path.join(dirname, "empty-tsconfig.json"), - target: [...(bundle.esbuildTarget ?? ["node14"])], + target: [...(buildOptions.target ?? ["node14"])], logLevel: "error", + format: file.output.format, + outfile: path.join(DIST_DIR, cliOptions.saveAs ?? file.output.file), }; - if (bundle.target === "universal") { - if (!bundle.esbuildTarget) { - esbuildOptions.target.push(...umdTarget); + if (file.platform === "universal") { + if (!buildOptions.target) { + esbuildOptions.target.push(...universalTarget); } - yield { - ...esbuildOptions, - outfile: bundle.output, - plugins: [ + if (file.output.format === "umd") { + esbuildOptions.plugins.push( esbuildPluginUmd({ - name: bundle.name, - interopDefault, - }), - ...esbuildOptions.plugins, - ], - format: "umd", - }; - - yield { - ...esbuildOptions, - outfile: `esm/${bundle.output.replace(".js", ".mjs")}`, - format: "esm", - }; + name: file.output.umdVariableName, + interopDefault: buildOptions.interopDefault ?? true, + }) + ); + } } else { esbuildOptions.platform = "node"; - esbuildOptions.external.push(...bundledFiles.map(({ output }) => output)); + esbuildOptions.external.push(...files.map((file) => file.output.file)); - if (bundle.format !== "esm" && interopDefault) { + if (file.output.format !== "esm" && buildOptions.interopDefault) { esbuildOptions.plugins.push(esbuildPluginInteropDefault()); } // https://github.com/evanw/esbuild/issues/1921 - if (bundle.format === "esm") { + if (file.output.format === "esm") { esbuildOptions.plugins.push(esbuildPluginShimCommonjsObjects()); } - - yield { - ...esbuildOptions, - outfile: bundle.output, - format: bundle.format ?? "cjs", - }; } + + return esbuildOptions; } -async function runBuild(bundle, esbuildOptions) { +async function runEsbuild(options) { + const esbuildOptions = getEsbuildOptions(options); await esbuild.build(esbuildOptions); } -async function* createBundle(bundle, buildOptions) { - for (const esbuildOptions of getEsbuildOptions(bundle, buildOptions)) { - const { outfile: file } = esbuildOptions; - - if ( - (buildOptions.files && !buildOptions.files.has(file)) || - (buildOptions.playground && esbuildOptions.format !== "umd") - ) { - yield { name: file, skipped: true }; - continue; - } - - const relativePath = buildOptions.saveAs || file; - const absolutePath = path.join(DIST_DIR, relativePath); - - esbuildOptions.outfile = absolutePath; - - yield { name: file, started: true }; - await runBuild(bundle, esbuildOptions, buildOptions); - yield { name: file, relativePath, absolutePath }; - } -} - -export default createBundle; +export default runEsbuild; diff --git a/scripts/build/save-licenses.mjs b/scripts/build/build-license.js similarity index 50% rename from scripts/build/save-licenses.mjs rename to scripts/build/build-license.js index 12f323fc5..97bfa6069 100644 --- a/scripts/build/save-licenses.mjs +++ b/scripts/build/build-license.js @@ -1,20 +1,25 @@ import path from "node:path"; import { promises as fs } from "node:fs"; import { outdent } from "outdent"; -import { DIST_DIR } from "../utils/index.mjs"; +import { DIST_DIR, PROJECT_ROOT } from "../utils/index.mjs"; -const file = path.join(DIST_DIR, "LICENSE"); +const PROJECT_LICENSE_FILE = path.join(PROJECT_ROOT, "LICENSE"); +const LICENSE_FILE = path.join(DIST_DIR, "LICENSE"); const separator = `\n${"-".repeat(40)}\n\n`; -async function saveLicenses(dependencies) { - // Unique by `name` and `version` +async function getLicenseText(files) { + let dependencies = files.flatMap((file) => file.dependencies); + dependencies = dependencies.filter( (dependency, index) => + // Exclude ourself + dependency.name !== "prettier" && + // Unique by `name` and `version` index === - dependencies.findIndex( - ({ name, version }) => - dependency.name === name && dependency.version === version - ) + dependencies.findIndex( + ({ name, version }) => + dependency.name === name && dependency.version === version + ) ); dependencies.sort( @@ -23,7 +28,7 @@ async function saveLicenses(dependencies) { dependencyA.version.localeCompare(dependencyB.version) ); - const prettierLicense = await fs.readFile(file, "utf8"); + const prettierLicense = await fs.readFile(PROJECT_LICENSE_FILE, "utf8"); const licenses = [ ...new Set( @@ -32,21 +37,29 @@ async function saveLicenses(dependencies) { .map(({ license }) => license) ), ]; + const text = outdent` # Prettier license Prettier is released under the MIT license: ${prettierLicense.trim()} - - ## Licenses of bundled dependencies - - The published Prettier artifact additionally contains code with the following licenses: - ${licenses.join(", ")} - - ## Bundled dependencies `; + if (licenses.length === 0) { + return text; + } + + const parts = [ + text, + outdent` + ## Licenses of bundled dependencies + + The published Prettier artifact additionally contains code with the following licenses: + ${licenses.join(", ")} + `, + ]; + const content = dependencies .map((dependency) => { let text = `### ${dependency.name}@v${dependency.version}\n`; @@ -81,7 +94,33 @@ async function saveLicenses(dependencies) { }) .join(separator); - await fs.writeFile(file, text + "\n\n" + content); + return [ + ...parts, + outdent` + ## Bundled dependencies + + ${content} + `, + ].join("\n\n"); } -export default saveLicenses; +async function buildLicense({ file, files, shouldCollectLicenses }) { + if (files.indexOf(file) !== files.length - 1) { + throw new Error("license should be last file to build."); + } + + if (!shouldCollectLicenses) { + return; + } + + const javascriptFiles = files.filter((file) => !file.isMetaFile); + if (javascriptFiles.some((file) => !Array.isArray(file.dependencies))) { + return { skipped: true }; + } + + const text = await getLicenseText(javascriptFiles); + + await fs.writeFile(LICENSE_FILE, text); +} + +export default buildLicense; diff --git a/scripts/build/build-package-json.js b/scripts/build/build-package-json.js new file mode 100644 index 000000000..af6f518a9 --- /dev/null +++ b/scripts/build/build-package-json.js @@ -0,0 +1,40 @@ +import path from "node:path"; +import { + PROJECT_ROOT, + DIST_DIR, + readJson, + writeJson, +} from "../utils/index.mjs"; + +async function buildPackageJson({ files }) { + const packageJson = await readJson(path.join(PROJECT_ROOT, "package.json")); + + const bin = files.find( + (file) => + path.join(PROJECT_ROOT, packageJson.bin) === + path.join(PROJECT_ROOT, file.input) + ).output.file; + + packageJson.bin = `./${bin}`; + packageJson.main = "./index.cjs"; + // TODO: Add `exports` + + // https://github.com/prettier/prettier/pull/13118#discussion_r922708068 + packageJson.engines.node = ">=14"; + delete packageJson.dependencies; + delete packageJson.devDependencies; + delete packageJson.browserslist; + delete packageJson.type; + delete packageJson.c8; + delete packageJson.packageManager; + delete packageJson.resolutions; + packageJson.scripts = { + prepublishOnly: + "node -e \"assert.equal(require('.').version, require('..').version)\"", + }; + packageJson.files = files.map(({ output: { file } }) => file).sort(); + + await writeJson(path.join(DIST_DIR, "package.json"), packageJson); +} + +export default buildPackageJson; diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index 1be99779b..2d6612f17 100644 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -7,16 +7,8 @@ import chalk from "chalk"; import minimist from "minimist"; import prettyBytes from "pretty-bytes"; import createEsmUtils from "esm-utils"; -import { - PROJECT_ROOT, - DIST_DIR, - readJson, - writeJson, - copyFile, -} from "../utils/index.mjs"; -import bundler from "./bundler.mjs"; -import bundleConfigs from "./config.mjs"; -import saveLicenses from "./save-licenses.mjs"; +import { DIST_DIR } from "../utils/index.mjs"; +import files from "./config.mjs"; const { require } = createEsmUtils(import.meta); @@ -52,141 +44,122 @@ const clear = () => { readline.cursorTo(process.stdout, 0, null); }; -async function createBundle(bundleConfig, options) { +async function buildFile({ file, files, shouldCollectLicenses, cliOptions }) { + let displayName = file.output.file; + if ( + (file.platform === "universal" && file.output.format !== "esm") || + (file.output.file.startsWith("index.") && file.output.format !== "esm") + ) { + displayName = ` ${displayName}`; + } + + process.stdout.write(fitTerminal(displayName)); + + if ( + (cliOptions.files && !cliOptions.files.has(file.output.file)) || + (cliOptions.playground && file.output.format !== "umd") + ) { + console.log(status.SKIPPED); + return; + } + + let result; try { - for await (const { - name, - started, - skipped, - relativePath, - absolutePath, - } of bundler(bundleConfig, options)) { - const displayName = name.startsWith("esm/") ? ` ${name}` : name; - - if (started) { - process.stdout.write(fitTerminal(displayName)); - continue; - } - - if (skipped) { - if (!options.files) { - process.stdout.write(fitTerminal(displayName)); - console.log(status.SKIPPED); - } - - continue; - } - - const sizeMessages = []; - - if (options.printSize) { - const { size } = await fs.stat(absolutePath); - sizeMessages.push(prettyBytes(size)); - } - - if (options.compareSize) { - // TODO: Use `import.meta.resolve` when Node.js support - const stablePrettierDirectory = path.dirname( - require.resolve("prettier") - ); - const stableVersionFile = path.join( - stablePrettierDirectory, - relativePath - ); - let stableSize; - try { - ({ size: stableSize } = await fs.stat(stableVersionFile)); - } catch { - // No op - } - - if (stableSize) { - const { size } = await fs.stat(absolutePath); - const sizeDiff = size - stableSize; - const message = chalk[sizeDiff > 0 ? "yellow" : "green"]( - prettyBytes(sizeDiff) - ); - - sizeMessages.push(`${message}`); - } else { - sizeMessages.push(chalk.blue("[NEW FILE]")); - } - } - - if (sizeMessages.length > 0) { - // Clear previous line - clear(); - process.stdout.write( - fitTerminal(displayName, `${sizeMessages.join(", ")} `) - ); - } - - console.log(status.DONE); - } + result = (await file.build({ + file, + files, + shouldCollectLicenses, + cliOptions, + })) ?? { + file: cliOptions.saveAs ?? file.output.file, + }; } catch (error) { console.log(status.FAIL + "\n"); console.error(error); throw error; } + + if (result.skipped) { + console.log(status.SKIPPED); + return; + } + + const sizeMessages = []; + if (cliOptions.printSize) { + const { size } = await fs.stat(path.join(DIST_DIR, result.file)); + sizeMessages.push(prettyBytes(size)); + } + + if (cliOptions.compareSize) { + // TODO: Use `import.meta.resolve` when Node.js support + const stablePrettierDirectory = path.dirname(require.resolve("prettier")); + const stableVersionFile = path.join(stablePrettierDirectory, result.file); + let stableSize; + try { + ({ size: stableSize } = await fs.stat(stableVersionFile)); + } catch { + // No op + } + + if (stableSize) { + const { size } = await fs.stat(path.join(DIST_DIR, result.file)); + const sizeDiff = size - stableSize; + const message = chalk[sizeDiff > 0 ? "yellow" : "green"]( + prettyBytes(sizeDiff) + ); + + sizeMessages.push(`${message}`); + } else { + sizeMessages.push(chalk.blue("[NEW FILE]")); + } + } + + if (sizeMessages.length > 0) { + // Clear previous line + clear(); + process.stdout.write( + fitTerminal(displayName, `${sizeMessages.join(", ")} `) + ); + } + + console.log(status.DONE); } -async function preparePackage() { - const packageJson = await readJson(path.join(PROJECT_ROOT, "package.json")); - packageJson.bin = "./bin-prettier.cjs"; - packageJson.main = "./index.cjs"; - // TODO: Add `exports` +async function run(cliOptions) { + cliOptions.files = cliOptions.file + ? new Set([cliOptions.file].flat()) + : cliOptions.file; + delete cliOptions.file; - // https://github.com/prettier/prettier/pull/13118#discussion_r922708068 - packageJson.engines.node = ">=14"; - delete packageJson.dependencies; - delete packageJson.devDependencies; - delete packageJson.browserslist; - delete packageJson.type; - delete packageJson.c8; - delete packageJson.packageManager; - packageJson.scripts = { - prepublishOnly: - "node -e \"assert.equal(require('.').version, require('..').version)\"", - }; - packageJson.files = ["*.js", "*.mjs", "esm/*.mjs"]; - await writeJson(path.join(DIST_DIR, "package.json"), packageJson); + cliOptions.saveAs = cliOptions["save-as"]; + delete cliOptions["save-as"]; - for (const file of ["README.md", "LICENSE"]) { - await copyFile(path.join(PROJECT_ROOT, file), path.join(DIST_DIR, file)); + cliOptions.printSize = cliOptions["print-size"]; + delete cliOptions["print-size"]; + + cliOptions.compareSize = cliOptions["compare-size"]; + delete cliOptions["compare-size"]; + + if (cliOptions.report === "") { + cliOptions.report = ["html"]; } -} + cliOptions.reports = cliOptions.report + ? [cliOptions.report].flat() + : cliOptions.report; + delete cliOptions.report; -async function run(params) { - params.files = params.file ? new Set([params.file].flat()) : params.file; - delete params.file; - - params.saveAs = params["save-as"]; - delete params["save-as"]; - - params.printSize = params["print-size"]; - delete params["print-size"]; - - params.compareSize = params["compare-size"]; - delete params["compare-size"]; - - if (params.report === "") { - params.report = ["html"]; - } - params.reports = params.report ? [params.report].flat() : params.report; - delete params.report; - - if (params.saveAs && !(params.files && params.files.size === 1)) { + if (cliOptions.saveAs && !(cliOptions.files && cliOptions.files.size === 1)) { throw new Error("'--save-as' can only use together with one '--file' flag"); } if ( - params.saveAs && - !path.join(DIST_DIR, params.saveAs).startsWith(DIST_DIR) + cliOptions.saveAs && + !path.join(DIST_DIR, cliOptions.saveAs).startsWith(DIST_DIR) ) { throw new Error("'--save-as' can only relative path"); } - if (params.clean) { + if (cliOptions.clean) { let stat; try { stat = await fs.stat(DIST_DIR); @@ -203,45 +176,27 @@ async function run(params) { } } - if (params.compareSize) { - if (params.minify === false) { + if (cliOptions.compareSize) { + if (cliOptions.minify === false) { throw new Error( "'--compare-size' can not use together with '--no-minify' flag" ); } - if (params.saveAs) { + if (cliOptions.saveAs) { throw new Error( "'--compare-size' can not use together with '--save-as' flag" ); } } - const shouldPreparePackage = - !params.playground && !params.files && params.minify === null; - const shouldSaveBundledPackagesLicenses = shouldPreparePackage; - - const licenses = []; - if (shouldSaveBundledPackagesLicenses) { - params.onLicenseFound = (dependencies) => licenses.push(...dependencies); - } + const shouldCollectLicenses = + !cliOptions.playground && !cliOptions.files && cliOptions.minify === null; console.log(chalk.inverse(" Building packages ")); - for (const bundleConfig of bundleConfigs) { - await createBundle(bundleConfig, params); - } - - if (shouldPreparePackage) { - await preparePackage(); - } - - if (shouldSaveBundledPackagesLicenses) { - await saveLicenses(licenses.filter(({ name }) => name !== "prettier")); - } else { - console.warn( - chalk.red("Bundled packages licenses not included in `dist/LICENSE`.") - ); + for (const file of files) { + await buildFile({ file, files, shouldCollectLicenses, cliOptions }); } } diff --git a/scripts/build/config.mjs b/scripts/build/config.mjs index 444b116ce..11e33e012 100644 --- a/scripts/build/config.mjs +++ b/scripts/build/config.mjs @@ -1,27 +1,35 @@ import path from "node:path"; import { createRequire } from "node:module"; import createEsmUtils from "esm-utils"; -import { PROJECT_ROOT } from "../utils/index.mjs"; +import { PROJECT_ROOT, DIST_DIR, copyFile } from "../utils/index.mjs"; +import buildJavascriptModule from "./build-javascript-module.js"; +import buildPackageJson from "./build-package-json.js"; +import buildLicense from "./build-license.js"; import modifyTypescriptModule from "./modify-typescript-module.mjs"; const { require, dirname } = createEsmUtils(import.meta); /** - * @typedef {Object} Bundle - * @property {string} input - input of the bundle - * @property {string?} output - path of the output file in the `dist/` folder - * @property {string?} name - name for the UMD bundle (for plugins, it'll be `prettierPlugins.${name}`) - * @property {'node' | 'universal'} target - should generate a CJS only for node or universal bundle - * @property {'core' | 'plugin'} type - it's a plugin bundle or core part of prettier - * @property {string[]} external - array of paths that should not be included in the final bundle - * @property {object[]} replaceModule - Module replacements - * @property {string[]} babelPlugins - babel plugins - * @property {boolean?} minify - minify - * @property {string[]?} esbuildTarget - ESBuild target - * @property {boolean?} interopDefault - Should export the ESM default export - - * @typedef {Object} CommonJSConfig - * @property {string[]} ignore - paths of CJS modules to ignore + * @typedef {Object} BuildOptions + * @property {object[]?} replaceModule - Module replacements + * @property {string[]?} target - ESBuild targets + * @property {string[]?} external - array of paths that should not be included in the final bundle + * @property {boolean?} interopDefault - interop default export + * @property {boolean?} minify - disable code minification + * + * @typedef {Object} Output + * @property {'esm' | 'umd' | 'cjs' | 'text' | 'json'} format - File format + * @property {string} file - path of the output file in the `dist/` folder + * @property {string?} umdVariableName - name for the UMD file (for plugins, it'll be `prettierPlugins.${name}`) + * + * @typedef {Object} File + * @property {string?} input - input of the file + * @property {Output} output - output of the file + * @property {function} build - file generate function + * @property {'node' | 'universal'} platform - ESBuild platform + * @property {BuildOptions} buildOptions - ESBuild options + * @property {boolean?} isPlugin - file is a plugin + * @property {boolean?} isMeta - file is a meta file (package.json, LICENSE README.md) */ /* @@ -37,14 +45,15 @@ const replaceDiffPackageEntry = (file) => ({ path: path.join(diffPackageDirectory, file), }); -/** @type {Bundle[]} */ -const parsers = [ - { - input: "src/language-js/parse/babel.js", - }, - { - input: "src/language-js/parse/flow.js", - }, +const extensions = { + esm: ".mjs", + umd: ".js", + cjs: ".cjs", +}; + +const pluginFiles = [ + "src/language-js/parse/babel.js", + "src/language-js/parse/flow.js", { input: "src/language-js/parse/typescript.js", replaceModule: [ @@ -105,9 +114,9 @@ const parsers = [ }, { input: "src/language-js/parse/acorn-and-espree.js", - name: "prettierPlugins.espree", // TODO: Rename this file to `parser-acorn-and-espree.js` or find a better way - output: "parser-espree.js", + outputBaseName: "espree", + umdPropertyName: "espree", replaceModule: [ { module: require.resolve("espree"), @@ -121,9 +130,7 @@ const parsers = [ }, ], }, - { - input: "src/language-js/parse/meriyah.js", - }, + "src/language-js/parse/meriyah.js", { input: "src/language-js/parse/angular.js", replaceModule: [ @@ -155,9 +162,7 @@ const parsers = [ }, ], }, - { - input: "src/language-graphql/parser-graphql.js", - }, + "src/language-graphql/parser-graphql.js", { input: "src/language-markdown/parser-markdown.js", replaceModule: [ @@ -181,33 +186,104 @@ const parsers = [ }, ], }, - { - input: "src/language-html/parser-html.js", - }, - { - input: "src/language-yaml/parser-yaml.js", - }, -].map((bundle) => { - const { name } = bundle.input.match( - /(?:parser-|parse\/)(?.*?)\.js$/ - ).groups; + "src/language-html/parser-html.js", + "src/language-yaml/parser-yaml.js", +].map((file) => { + if (typeof file === "string") { + file = { input: file }; + } + + let { input, umdPropertyName, outputBaseName, ...buildOptions } = file; + + outputBaseName ??= input.match( + /(?:parser-|parse\/)(?.*?)\.js$/ + ).groups.outputBaseName; + const umdVariableName = `prettierPlugins.${ + umdPropertyName ?? outputBaseName + }`; return { - type: "plugin", - target: "universal", - name: `prettierPlugins.${name}`, - output: `parser-${name}.js`, - ...bundle, + input, + outputBaseName, + umdVariableName, + buildOptions, + isPlugin: true, }; }); -/** @type {Bundle[]} */ -const coreBundles = [ +const nonPluginUniversalFiles = [ + { + input: "src/document/index.js", + outputBaseName: "doc", + umdVariableName: "doc", + interopDefault: false, + minify: false, + }, + { + input: "src/standalone.js", + umdVariableName: "prettier", + interopDefault: false, + replaceModule: [ + { + module: require.resolve("@babel/highlight"), + path: path.join(dirname, "./shims/babel-highlight.js"), + }, + { + module: createRequire(require.resolve("vnopts")).resolve("chalk"), + path: path.join(dirname, "./shims/chalk.js"), + }, + replaceDiffPackageEntry("lib/diff/array.js"), + ], + }, +].map((file) => { + const { + input, + outputBaseName = path.basename(input, ".js"), + umdVariableName, + ...buildOptions + } = file; + + return { + input, + outputBaseName, + umdVariableName, + buildOptions, + }; +}); + +const universalFiles = [...nonPluginUniversalFiles, ...pluginFiles].flatMap( + (file) => { + let { input, outputBaseName, umdVariableName, buildOptions, isPlugin } = + file; + + outputBaseName ??= path.basename(input); + if (file.isPlugin) { + outputBaseName = `parser-${outputBaseName}`; + } + + return [ + { + format: "esm", + file: `esm/${outputBaseName}${extensions.esm}`, + }, + { + format: "umd", + file: `${outputBaseName}${extensions.umd}`, + umdVariableName, + }, + ].map((output) => ({ + input, + output, + platform: "universal", + buildOptions, + isPlugin, + })); + } +); + +const nodejsFiles = [ { input: "src/index.js", - output: "index.mjs", - format: "esm", - interopDefault: false, replaceModule: [ { module: require.resolve("@iarna/toml/lib/toml-parser.js"), @@ -237,36 +313,10 @@ const coreBundles = [ { input: "src/index.cjs", }, - { - input: "src/document/index.js", - interopDefault: false, - name: "doc", - output: "doc.js", - target: "universal", - format: "umd", - minify: false, - }, - { - input: "src/standalone.js", - interopDefault: false, - name: "prettier", - target: "universal", - replaceModule: [ - { - module: require.resolve("@babel/highlight"), - path: path.join(dirname, "./shims/babel-highlight.js"), - }, - { - module: createRequire(require.resolve("vnopts")).resolve("chalk"), - path: path.join(dirname, "./shims/chalk.js"), - }, - replaceDiffPackageEntry("lib/diff/array.js"), - ], - }, { input: "bin/prettier.cjs", - output: "bin-prettier.cjs", - esbuildTarget: ["node0.10"], + outputBaseName: "bin-prettier", + target: ["node0.10"], replaceModule: [ { module: path.join(PROJECT_ROOT, "bin/prettier.cjs"), @@ -276,15 +326,12 @@ const coreBundles = [ }, { input: "src/cli/index.js", - output: "cli.mjs", - format: "esm", + outputBaseName: "cli", external: ["benchmark"], replaceModule: [replaceDiffPackageEntry("lib/patch/create.js")], }, { input: "src/common/third-party.js", - output: "third-party.mjs", - format: "esm", replaceModule: [ // cosmiconfig@6 -> import-fresh can't find parentModule, since module is bundled { @@ -293,12 +340,59 @@ const coreBundles = [ }, ], }, -].map((bundle) => ({ - type: "core", - target: "node", - output: path.basename(bundle.input), - ...bundle, -})); +].map((file) => { + let { input, output, outputBaseName, ...buildOptions } = file; -const configs = [...coreBundles, ...parsers]; -export default configs; + const format = input.endsWith(".cjs") ? "cjs" : "esm"; + outputBaseName ??= path.basename(input).split(".").slice(0, -1).join("."); + + return { + input, + output: { + format, + file: `${outputBaseName}${extensions[format]}`, + }, + platform: "node", + buildOptions, + }; +}); + +const metaFiles = [ + { + input: "package.json", + output: { + format: "json", + file: "package.json", + }, + build: buildPackageJson, + }, + { + output: { + format: "text", + file: "README.md", + }, + async build() { + await copyFile( + path.join(PROJECT_ROOT, "README.md"), + path.join(DIST_DIR, "README.md") + ); + }, + }, + { + output: { + format: "text", + file: "LICENSE", + }, + build: buildLicense, + }, +].map((file) => ({ ...file, isMetaFile: true })); + +/** @type {Files[]} */ +const files = [ + ...[...nodejsFiles, ...universalFiles].map((file) => ({ + ...file, + build: buildJavascriptModule, + })), + ...metaFiles, +]; +export default files;