diff --git a/scripts/build/build.mjs b/scripts/build/build.mjs index cc7a6c4af..2c718da65 100644 --- a/scripts/build/build.mjs +++ b/scripts/build/build.mjs @@ -4,12 +4,19 @@ import path from "node:path"; import fs from "node:fs/promises"; import readline from "node:readline"; import chalk from "chalk"; -import execa from "execa"; import minimist from "minimist"; import prettyBytes from "pretty-bytes"; +import rimraf from "rimraf"; import bundler from "./bundler.mjs"; import bundleConfigs from "./config.mjs"; -import * as utils from "./utils.mjs"; +import { + PROJECT_ROOT, + CACHE_DIR, + DIST_DIR, + readJson, + writeJson, + copyFile, +} from "./utils.mjs"; import Cache from "./cache.mjs"; // Errors in promises should be fatal. @@ -67,7 +74,7 @@ async function createBundle(bundleConfig, cache, options) { return; } - const file = path.join("dist", output); + const file = path.join(DIST_DIR, output); // Files including U+FFEE can't load in Chrome Extension // `prettier-chrome-extension` https://github.com/prettier/prettier-chrome-extension @@ -93,7 +100,11 @@ async function createBundle(bundleConfig, cache, options) { bundleConfig.bundler !== "webpack" && target === "universal" ) { - const esmFile = path.join("dist/esm", output.replace(".js", ".mjs")); + const esmFile = path.join( + DIST_DIR, + "esm", + output.replace(".js", ".mjs") + ); sizeTexts.push(`esm ${await getSizeText(esmFile)}`); } process.stdout.write(fitTerminal(output, `${sizeTexts.join(", ")} `)); @@ -112,41 +123,23 @@ function handleError(error) { throw error; } -async function cacheFiles(cache) { - // Copy built files to .cache - try { - await execa("rm", ["-rf", path.join(".cache", "files")]); - await execa("mkdir", ["-p", path.join(".cache", "files")]); - await execa("mkdir", ["-p", path.join(".cache", "files", "esm")]); - const manifest = cache.updated; - - for (const file of Object.keys(manifest.files)) { - await execa("cp", [ - file, - path.join(".cache", file.replace("dist", "files")), - ]); - } - } catch { - // Don't fail the build - } -} - async function preparePackage() { - const pkg = await utils.readJson("package.json"); - pkg.bin = "./bin-prettier.js"; - pkg.engines.node = ">=10.13.0"; - delete pkg.dependencies; - delete pkg.devDependencies; - delete pkg.browserslist; - pkg.scripts = { + const packageJson = await readJson(path.join(PROJECT_ROOT, "package.json")); + packageJson.bin = "./bin-prettier.js"; + packageJson.engines.node = ">=10.13.0"; + delete packageJson.dependencies; + delete packageJson.devDependencies; + delete packageJson.browserslist; + packageJson.scripts = { prepublishOnly: "node -e \"assert.equal(require('.').version, require('..').version)\"", }; - pkg.files = ["*.js", "esm/*.mjs"]; - await utils.writeJson("dist/package.json", pkg); + packageJson.files = ["*.js", "esm/*.mjs"]; + await writeJson(path.join(DIST_DIR, "package.json"), packageJson); - await utils.copyFile("./README.md", "./dist/README.md"); - await utils.copyFile("./LICENSE", "./dist/LICENSE"); + for (const file of ["README.md", "LICENSE"]) { + await copyFile(path.join(PROJECT_ROOT, file), path.join(DIST_DIR, file)); + } } async function run(params) { @@ -156,19 +149,20 @@ async function run(params) { if (params.file) { configs = configs.filter(({ output }) => output === params.file); } else { - await execa("rm", ["-rf", "dist"]); + rimraf.sync(DIST_DIR); } - await execa("mkdir", ["-p", "dist"]); - await execa("mkdir", ["-p", "dist/esm"]); - if (params["purge-cache"]) { - await execa("rm", ["-rf", ".cache"]); + rimraf.sync(CACHE_DIR); } let bundleCache; if (shouldUseCache) { - bundleCache = new Cache(".cache/", CACHE_VERSION); + bundleCache = new Cache({ + cacheDir: CACHE_DIR, + distDir: DIST_DIR, + version: CACHE_VERSION, + }); await bundleCache.load(); } @@ -179,7 +173,6 @@ async function run(params) { } if (shouldUseCache) { - await cacheFiles(bundleCache); await bundleCache.save(); } diff --git a/scripts/build/bundler.mjs b/scripts/build/bundler.mjs index 05a228428..97a1e1a53 100644 --- a/scripts/build/bundler.mjs +++ b/scripts/build/bundler.mjs @@ -17,9 +17,9 @@ import rollupPluginExecutable from "./rollup-plugins/executable.mjs"; import rollupPluginEvaluate from "./rollup-plugins/evaluate.mjs"; import rollupPluginReplaceModule from "./rollup-plugins/replace-module.mjs"; import bundles from "./config.mjs"; +import { PROJECT_ROOT, DIST_DIR } from "./utils.mjs"; const { __dirname, require, json } = createEsmUtils(import.meta); -const PROJECT_ROOT = path.join(__dirname, "../.."); const packageJson = json.loadSync("../../package.json"); const entries = [ @@ -114,7 +114,7 @@ function getBabelConfig(bundle) { function getRollupConfig(bundle) { const config = { - input: bundle.input, + input: path.join(PROJECT_ROOT, bundle.input), onwarn(warning) { if ( // ignore `MIXED_EXPORTS` warn @@ -255,7 +255,7 @@ function getRollupOutputOptions(bundle, buildOptions) { const options = { // Avoid warning form #8797 exports: "auto", - file: `dist/${bundle.output}`, + file: path.join(DIST_DIR, bundle.output), name: bundle.name, plugins: [ bundle.minify !== false && @@ -280,7 +280,10 @@ function getRollupOutputOptions(bundle, buildOptions) { !buildOptions.playground && { ...options, format: "esm", - file: `dist/esm/${bundle.output.replace(".js", ".mjs")}`, + file: path.join( + DIST_DIR, + `esm/${bundle.output.replace(".js", ".mjs")}` + ), }, ].filter(Boolean); } @@ -290,6 +293,7 @@ function getRollupOutputOptions(bundle, buildOptions) { if (buildOptions.playground && bundle.bundler !== "webpack") { return { skipped: true }; } + return [options]; } @@ -314,7 +318,7 @@ function getWebpackConfig(bundle) { ], }, output: { - path: path.resolve(PROJECT_ROOT, "dist"), + path: DIST_DIR, filename: bundle.output, library: { type: "umd", diff --git a/scripts/build/cache.mjs b/scripts/build/cache.mjs index 6da2afb74..3e1a80c31 100644 --- a/scripts/build/cache.mjs +++ b/scripts/build/cache.mjs @@ -2,17 +2,14 @@ import { strict as assert } from "node:assert"; import crypto from "node:crypto"; import fs from "node:fs/promises"; import path from "node:path"; -import execa from "execa"; import { rollup } from "rollup"; -import createEsmUtils from "esm-utils"; - -const { __dirname } = createEsmUtils(import.meta); -const ROOT = path.join(__dirname, "..", ".."); +import { PROJECT_ROOT, readJson, writeJson, copyFile } from "./utils.mjs"; class Cache { - constructor(cacheDir, version) { - this.cacheDir = path.resolve(cacheDir || required("cacheDir")); - this.manifest = path.join(this.cacheDir, "manifest.json"); + constructor({ cacheDir, distDir, version }) { + this.cacheDir = cacheDir || required("cacheDir"); + this.distDir = distDir || required("distDir"); + this.manifest = path.join(cacheDir, "manifest.json"); this.version = version || required("version"); this.checksums = {}; this.files = {}; @@ -26,13 +23,15 @@ class Cache { // Loads the manifest.json file with the information from the last build async load() { // This should never throw, if it does, let it fail the build - const lockfile = await fs.readFile("yarn.lock", "utf-8"); + const lockfile = await fs.readFile( + path.join(PROJECT_ROOT, "yarn.lock"), + "utf8" + ); const lockfileHash = hashString(lockfile); this.updated.checksums["yarn.lock"] = lockfileHash; try { - const manifest = await fs.readFile(this.manifest, "utf-8"); - const { version, checksums, files } = JSON.parse(manifest); + const { version, checksums, files } = await readJson(this.manifest); // Ignore the cache if the version changed assert.strictEqual(this.version, version); @@ -40,17 +39,17 @@ class Cache { assert.ok(typeof checksums === "object"); // If yarn.lock changed, rebuild everything assert.strictEqual(lockfileHash, checksums["yarn.lock"]); - this.checksums = checksums; assert.ok(typeof files === "object"); - this.files = files; - for (const files of Object.values(this.files)) { - assert.ok(Array.isArray(files)); + for (const modules of Object.values(files)) { + assert.ok(Array.isArray(modules)); } + + this.checksums = checksums; + this.files = files; } catch { - this.checksums = {}; - this.files = {}; + // noop } } @@ -69,7 +68,10 @@ class Cache { const modules = output .filter((mod) => !/\0/.test(mod.facadeModuleId)) - .map((mod) => [path.relative(ROOT, mod.facadeModuleId), mod.code]); + .map((mod) => [ + path.relative(PROJECT_ROOT, mod.facadeModuleId), + mod.code, + ]); for (const [id, code] of modules) { newFiles.push(id); @@ -95,28 +97,32 @@ class Cache { } async save() { - try { - await fs.writeFile(this.manifest, JSON.stringify(this.updated, null, 2)); - } catch { - // Don't fail the build + const { manifest, updated } = this; + + await writeJson(manifest, updated); + + const files = Object.keys(updated.files); + for (const file of files) { + await copyFile( + path.join(this.distDir, file), + path.join(this.cacheDir, "files", file) + ); } } async isCached(inputOptions, outputOption) { - const useCache = await this.checkBundle( - outputOption.file, - inputOptions, - outputOption - ); + const file = path.relative(this.distDir, outputOption.file); + const useCache = await this.checkBundle(file, inputOptions, outputOption); + if (useCache) { try { - await execa("cp", [ - path.join(this.cacheDir, outputOption.file.replace("dist", "files")), - outputOption.file, - ]); + await copyFile( + path.join(this.cacheDir, "files", file), + path.join(this.distDir, file) + ); + return true; - } catch (err) { - console.log(err); + } catch { // Proceed to build } } diff --git a/scripts/build/utils.mjs b/scripts/build/utils.mjs index b474be33f..1c4c6d992 100644 --- a/scripts/build/utils.mjs +++ b/scripts/build/utils.mjs @@ -1,4 +1,6 @@ import fs from "node:fs/promises"; +import url from "node:url"; +import path from "node:path"; async function readJson(file) { const data = await fs.readFile(file); @@ -7,12 +9,26 @@ async function readJson(file) { function writeJson(file, content) { content = JSON.stringify(content, null, 2); - return fs.writeFile(file, content); + return writeFile(file, content); } async function copyFile(from, to) { const data = await fs.readFile(from); - return fs.writeFile(to, data); + return writeFile(to, data); } -export { readJson, writeJson, copyFile }; +async function writeFile(file, content) { + const directory = path.dirname(file); + try { + await fs.mkdir(directory, { recursive: true }); + } catch { + // noop + } + return fs.writeFile(file, content); +} + +const PROJECT_ROOT = url.fileURLToPath(new URL("../../", import.meta.url)); +const CACHE_DIR = path.join(PROJECT_ROOT, ".cache"); +const DIST_DIR = path.join(PROJECT_ROOT, "dist"); + +export { PROJECT_ROOT, DIST_DIR, CACHE_DIR, readJson, writeJson, copyFile };