From be32c6401568dc6fce6e740c98e07eb7d06e97f5 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 04:23:00 +0900 Subject: [PATCH 01/53] Install `file-entry-cache` and types --- package.json | 2 ++ yarn.lock | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 22e3f492a434..91d2faa671b3 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "esutils": "2.0.3", "fast-glob": "3.2.11", "fast-json-stable-stringify": "2.1.0", + "file-entry-cache": "6.0.1", "find-parent-dir": "0.3.1", "flow-parser": "0.180.0", "get-stdin": "8.0.0", @@ -96,6 +97,7 @@ "@esbuild-plugins/node-modules-polyfill": "0.1.4", "@glimmer/reference": "0.84.2", "@types/estree": "0.0.51", + "@types/file-entry-cache": "5.0.2", "@types/jest": "27.4.1", "@typescript-eslint/eslint-plugin": "5.20.0", "babel-jest": "27.5.1", diff --git a/yarn.lock b/yarn.lock index 7e9a6ff89975..a7e2d3e7c434 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1638,6 +1638,13 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== +"@types/file-entry-cache@5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@types/file-entry-cache/-/file-entry-cache-5.0.2.tgz#3d31097d34fb5ff6bd9951f80d4082bda52feece" + integrity sha512-6uLb9gNrW+e4JivzglLQ2eJSyd7xvu5gSkwKIlOZOmuFgz8U7O9ddTwWjmWgUaIeukdQhoWefI5fQ5/MRTw8XA== + dependencies: + "@types/node" "*" + "@types/graceful-fs@^4.1.2": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -3367,7 +3374,7 @@ fetch-blob@^3.1.2, fetch-blob@^3.1.4: node-domexception "^1.0.0" web-streams-polyfill "^3.0.3" -file-entry-cache@^6.0.1: +file-entry-cache@6.0.1, file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== From 94e27ff3fb9a7181bc8a7fa2ff22225e8e2b6315 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 04:23:40 +0900 Subject: [PATCH 02/53] Implement `FormatResultsCache` --- src/cli/format-results-cache.js | 87 +++++++++++++++++++++++++++++++++ src/cli/utils.js | 12 ++++- 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/cli/format-results-cache.js diff --git a/src/cli/format-results-cache.js b/src/cli/format-results-cache.js new file mode 100644 index 000000000000..a68c04a66556 --- /dev/null +++ b/src/cli/format-results-cache.js @@ -0,0 +1,87 @@ +"use strict"; + +// Inspired by LintResultsCache from ESLint +// https://github.com/eslint/eslint/blob/c2d0a830754b6099a3325e6d3348c3ba983a677a/lib/cli-engine/lint-result-cache.js + +const fileEntryCache = require("file-entry-cache"); +const stringify = require("fast-json-stable-stringify"); +// eslint-disable-next-line no-restricted-modules +const { version: prettierVersion } = require("../index.js"); +const { createHash } = require("./utils.js"); + +const configHashCache = new WeakMap(); +const nodeVersion = process && process.version; + +/** + * @param {*} options + * @returns {string} + */ +function getHashOfOptions(options) { + if (configHashCache.has(options)) { + return configHashCache.get(options); + } + return createHash(`${prettierVersion}_${nodeVersion}_${stringify(options)}`); +} + +/** + * @typedef {{ hashOfOptions?: string }} OurMeta + * @typedef {import("file-entry-cache").FileDescriptor} FileDescriptor + * + * @param {import("file-entry-cache").FileDescriptor} fileDescriptor + * @returns {FileDescriptor["meta"] & OurMeta} + */ +function getMetadataFromFileDescriptor(fileDescriptor) { + return fileDescriptor.meta; +} + +class FormatResultsCache { + /** + * @param {string} cacheFileLocation The path of cache file location. (default: `.prettiercache`) + */ + constructor(cacheFileLocation) { + this.cacheFileLocation = cacheFileLocation; + this.fileEntryCache = fileEntryCache.create( + /* cacheId */ cacheFileLocation + ); + } + + /** + * @param {string} filePath + * @param {any} options + */ + existsAvailableFormatResultsCache(filePath, options) { + const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath); + const hashOfOptions = getHashOfOptions(options); + const meta = getMetadataFromFileDescriptor(fileDescriptor); + const changed = + fileDescriptor.changed || meta.hashOfOptions !== hashOfOptions; + + if (fileDescriptor.notFound) { + return false; + } + + if (changed) { + return false; + } + + return true; + } + + /** + * @param {string} filePath + * @param {any} options + */ + setFormatResultsCache(filePath, options) { + const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath); + const meta = getMetadataFromFileDescriptor(fileDescriptor); + if (fileDescriptor && !fileDescriptor.notFound) { + meta.hashOfOptions = getHashOfOptions(options); + } + } + + reconcile() { + this.fileEntryCache.reconcile(); + } +} + +module.exports = FormatResultsCache; diff --git a/src/cli/utils.js b/src/cli/utils.js index 3fc2637dde39..2a45a8ec2c5a 100644 --- a/src/cli/utils.js +++ b/src/cli/utils.js @@ -1,5 +1,7 @@ "use strict"; +const crypto = require("crypto"); + // eslint-disable-next-line no-console const printToScreen = console.log.bind(console); @@ -38,4 +40,12 @@ function pick(object, keys) { return Object.fromEntries(entries); } -module.exports = { printToScreen, groupBy, pick }; +/** + * @param {string} source + * @returns {string} + */ +function createHash(source) { + return crypto.createHash("md5").update(source).digest("hex"); +} + +module.exports = { printToScreen, groupBy, pick, createHash }; From b1173f1fa8b60b69216460e5b3aba8ec451a499c Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 04:24:08 +0900 Subject: [PATCH 03/53] Add `--cache` and `--cache-location` option --- src/cli/constant.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cli/constant.js b/src/cli/constant.js index 5755a06e0e6e..d4952669e1cb 100644 --- a/src/cli/constant.js +++ b/src/cli/constant.js @@ -71,6 +71,16 @@ const categoryOrder = [ */ /* eslint sort-keys: "error" */ const options = { + cache: { + type: "boolean", + default: false, + description: "Only format changed files", + }, + "cache-location": { + type: "path", + description: "Path to the cache file or directory", + default: ".prettiercache", + }, check: { alias: "c", category: coreOptions.CATEGORY_OUTPUT, From ebd577f89b7d5699cb45a02d20f1b4a45c72ba61 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 04:24:38 +0900 Subject: [PATCH 04/53] Implement `--cache` and `--cache-location` --- src/cli/format.js | 62 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/src/cli/format.js b/src/cli/format.js index 0f89481eb2f1..cd8deaf60094 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -15,6 +15,8 @@ const { createIgnorer, errors } = require("./prettier-internal.js"); const { expandPatterns, fixWindowsSlashes } = require("./expand-patterns.js"); const getOptionsForFile = require("./options/get-options-for-file.js"); const isTTY = require("./is-tty.js"); +const FormatResultsCache = require("./format-results-cache.js"); +const { createHash } = require("./utils.js"); function diff(a, b) { return require("diff").createTwoFilesPatch("", "", a, b, "", "", { @@ -283,6 +285,38 @@ async function formatStdin(context) { } } +/** + * Inspired by `getCacheFile` function from ESLint + * https://github.com/eslint/eslint/blob/c2d0a830754b6099a3325e6d3348c3ba983a677a/lib/cli-engine/cli-engine.js#L424-L485 + * + * @param {string} cacheLocation + * @returns {string} + */ +async function getCacheFile(cacheLocation) { + const cwd = process.cwd(); + const normalizedCacheLocation = path.normalize(cacheLocation); + const looksLikeADirectory = normalizedCacheLocation.slice(-1) === path.sep; + const resolvedCacheLocation = path.resolve(cwd, normalizedCacheLocation); + const getCacheFileForDirectory = () => + path.join(resolvedCacheLocation, `.cache_${createHash(cwd)}`); + let fileStats; + try { + fileStats = await fs.lstat(resolvedCacheLocation); + } catch { + fileStats = null; + } + if (fileStats) { + if (fileStats.isDirectory() || looksLikeADirectory) { + return getCacheFileForDirectory(); + } + return resolvedCacheLocation; + } + if (looksLikeADirectory) { + return getCacheFileForDirectory(); + } + return resolvedCacheLocation; +} + async function formatFiles(context) { // The ignorer will be used to filter file paths after the glob is checked, // before any files are actually written @@ -295,6 +329,10 @@ async function formatFiles(context) { context.logger.log("Checking formatting..."); } + const formatResultsCache = context.argv.cache + ? new FormatResultsCache(await getCacheFile(context.argv.cacheLocation)) + : undefined; + for await (const pathOrError of expandPatterns(context)) { if (typeof pathOrError === "object") { context.logger.error(pathOrError.error); @@ -362,17 +400,28 @@ async function formatFiles(context) { const start = Date.now(); + const existsCache = formatResultsCache?.existsAvailableFormatResultsCache( + filename, + options + ); + let result; let output; try { - result = format(context, input, options); + if (existsCache) { + result = { formatted: input }; + } else { + result = format(context, input, options); + } output = result.formatted; } catch (error) { handleError(context, filename, error, printedFilename); continue; } + formatResultsCache?.setFormatResultsCache(filename, options); + const isDifferent = output !== input; if (printedFilename) { @@ -408,7 +457,12 @@ async function formatFiles(context) { process.exitCode = 2; } } else if (!context.argv.check && !context.argv.listDifferent) { - context.logger.log(`${chalk.grey(filename)} ${Date.now() - start}ms`); + const message = `${chalk.grey(filename)} ${Date.now() - start}ms`; + if (existsCache) { + context.logger.log(`${message} (cached)`); + } else { + context.logger.log(message); + } } } else if (context.argv.debugCheck) { /* istanbul ignore else */ @@ -431,6 +485,10 @@ async function formatFiles(context) { } } + if (formatResultsCache) { + formatResultsCache.reconcile(); + } + // Print check summary based on expected exit code if (context.argv.check) { if (numberOfUnformattedFilesFound === 0) { From 56f432cb8134c02e51dd2ed4ae56626b80e540fd Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 04:24:57 +0900 Subject: [PATCH 05/53] Add tests for `--cache` --- tests/integration/__tests__/cache.js | 123 +++++++++++++++++++++++++++ tests/integration/cli/cache/a.js | 3 + tests/integration/cli/cache/b.js | 3 + 3 files changed, 129 insertions(+) create mode 100644 tests/integration/__tests__/cache.js create mode 100644 tests/integration/cli/cache/a.js create mode 100644 tests/integration/cli/cache/b.js diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js new file mode 100644 index 000000000000..8711fbc59d59 --- /dev/null +++ b/tests/integration/__tests__/cache.js @@ -0,0 +1,123 @@ +"use strict"; + +const path = require("path"); +const fs = require("fs/promises"); +const { default: stripAnsi } = require("../../../vendors/strip-ansi.js"); + +const runPrettier = require("../run-prettier.js"); + +function resolveDir(dir) { + return path.join(__dirname, "..", dir); +} + +describe("--cache option", () => { + const dir = resolveDir("cli/cache"); + const defaultCacheFile = path.join(dir, ".prettiercache"); + + let contentA; + let contentB; + + beforeAll(async () => { + contentA = await fs.readFile(path.join(dir, "a.js"), "utf8"); + contentB = await fs.readFile(path.join(dir, "b.js"), "utf8"); + }); + + afterEach(async () => { + await fs.rm(defaultCacheFile); + await fs.writeFile(path.join(dir, "a.js"), contentA); + await fs.writeFile(path.join(dir, "b.js"), contentB); + }); + + it("creates default cache file named `.prettiercache`", async () => { + await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty( + "code", + "ENOENT" + ); + await runPrettier(dir, ["--cache", "."]); + await expect(fs.stat(defaultCacheFile)).resolves.not.toThrowError(); + }); + + it("does'nt format when cache is available", async () => { + const { stdout: firstStdout } = await runPrettier(dir, [ + "--cache", + "--write", + ".", + ]); + expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + + const { stdout: secondStdout } = await runPrettier(dir, [ + "--cache", + "--write", + ".", + ]); + expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms \(cached\)$/), + expect.stringMatching(/^b\.js .+ms \(cached\)$/), + ]) + ); + }); + + it("re-formats when a file has been updated.", async () => { + const { stdout: firstStdout } = await runPrettier(dir, [ + "--cache", + "--write", + ".", + ]); + expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + + // Update `a.js` + await fs.writeFile(path.join(dir, "a.js"), "const a = `a`;"); + + const { stdout: secondStdout } = await runPrettier(dir, [ + "--cache", + "--write", + ".", + ]); + expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( + // the cache of `b.js` is only available. + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms \(cached\)$/), + ]) + ); + }); + + it("re-formats when options has been updated.", async () => { + const { stdout: firstStdout } = await runPrettier(dir, [ + "--cache", + "--write", + ".", + ]); + expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + + const { stdout: secondStdout } = await runPrettier(dir, [ + "--cache", + "--write", + "--trailing-comma", + "all", + ".", + ]); + expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + }); +}); diff --git a/tests/integration/cli/cache/a.js b/tests/integration/cli/cache/a.js new file mode 100644 index 000000000000..b824ea0f9278 --- /dev/null +++ b/tests/integration/cli/cache/a.js @@ -0,0 +1,3 @@ +function a() { + console.log("this is a.js") +} diff --git a/tests/integration/cli/cache/b.js b/tests/integration/cli/cache/b.js new file mode 100644 index 000000000000..4bc80d22e7a9 --- /dev/null +++ b/tests/integration/cli/cache/b.js @@ -0,0 +1,3 @@ +function b() { + console.log("this is b.js"); +} From 7b03c76fd9cc1c7637abfc920bc8e744b54d04f9 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 04:25:06 +0900 Subject: [PATCH 06/53] Add tests for `--cache-location` --- tests/integration/__tests__/cache-location.js | 127 ++++++++++++++++++ tests/integration/cli/cache-location/a.js | 3 + tests/integration/cli/cache-location/b.js | 3 + 3 files changed, 133 insertions(+) create mode 100644 tests/integration/__tests__/cache-location.js create mode 100644 tests/integration/cli/cache-location/a.js create mode 100644 tests/integration/cli/cache-location/b.js diff --git a/tests/integration/__tests__/cache-location.js b/tests/integration/__tests__/cache-location.js new file mode 100644 index 000000000000..b6d69f88f3a9 --- /dev/null +++ b/tests/integration/__tests__/cache-location.js @@ -0,0 +1,127 @@ +"use strict"; + +const path = require("path"); +const fs = require("fs/promises"); + +const { default: stripAnsi } = require("../../../vendors/strip-ansi.js"); + +const runPrettier = require("../run-prettier.js"); + +function resolveDir(dir) { + return path.join(__dirname, "..", dir); +} + +function isJson(str) { + try { + JSON.parse(str); + return true; + } catch { + return false; + } +} + +async function rm(file) { + try { + await fs.rm(file); + } catch { + // noop + } +} + +async function rmdir(dirPath) { + try { + await await fs.rm(dirPath, { recursive: true, force: true }); + } catch { + // noop + } +} + +describe("--cache-location option", () => { + const dir = resolveDir("cli/cache-location"); + const defaultCacheFile = path.join(dir, ".prettiercache"); + const newCacheFile = path.join(dir, ".newprettiercache"); + const newCacheDir = path.join(dir, "cache-dir"); + + afterEach(async () => { + await rm(newCacheFile); + await rmdir(newCacheDir); + }); + + it("doesn't create `.prettiercache` file when `--cache-location` is used.", async () => { + await runPrettier(dir, [ + "--cache", + "--cache-location=.newprettiercache", + ".", + ]); + await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty( + "code", + "ENOENT" + ); + }); + + describe("specify file name", () => { + it("creates cache file at the specified file", async () => { + await runPrettier(dir, [ + "--cache", + "--cache-location=.newprettiercache", + ".", + ]); + const cacheFileData = await fs.readFile(newCacheFile, "utf8"); + await expect(isJson(cacheFileData)).toBe(true); + }); + + it("skips formatting when cache is available", async () => { + await runPrettier(dir, [ + "--cache", + "--cache-location=.newprettiercache", + "--write", + ".", + ]); + const { stdout } = await runPrettier(dir, [ + "--cache", + "--cache-location=.newprettiercache", + "--write", + ".", + ]); + expect(stripAnsi(stdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms \(cached\)$/), + expect.stringMatching(/^b\.js .+ms \(cached\)$/), + ]) + ); + }); + }); + + describe("specify director name", () => { + it("creates cache file into the specified directory", async () => { + await runPrettier(dir, ["--cache", "--cache-location=cache-dir/", "."]); + const fileNames = await fs.readdir(newCacheDir); + expect(fileNames.length).toBe(1); + const cacheFileName = fileNames[0]; + const filePath = path.join(newCacheDir, cacheFileName); + const cacheFileData = await fs.readFile(filePath, "utf8"); + await expect(isJson(cacheFileData)).toBe(true); + }); + + it("skips formatting when cache is available", async () => { + await runPrettier(dir, [ + "--cache", + "--cache-location=cache-dir/", + "--write", + ".", + ]); + const { stdout } = await runPrettier(dir, [ + "--cache", + "--cache-location=cache-dir/", + "--write", + ".", + ]); + expect(stripAnsi(stdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms \(cached\)$/), + expect.stringMatching(/^b\.js .+ms \(cached\)$/), + ]) + ); + }); + }); +}); diff --git a/tests/integration/cli/cache-location/a.js b/tests/integration/cli/cache-location/a.js new file mode 100644 index 000000000000..b824ea0f9278 --- /dev/null +++ b/tests/integration/cli/cache-location/a.js @@ -0,0 +1,3 @@ +function a() { + console.log("this is a.js") +} diff --git a/tests/integration/cli/cache-location/b.js b/tests/integration/cli/cache-location/b.js new file mode 100644 index 000000000000..7c8812b4e1e8 --- /dev/null +++ b/tests/integration/cli/cache-location/b.js @@ -0,0 +1,3 @@ +function b() { + console.log("this is b.js") +} From 78f664099d69c8face863dcad295f4964f20bf0c Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 04:32:40 +0900 Subject: [PATCH 07/53] Avoid spellcheck error --- cspell.json | 1 + tests/integration/__tests__/cache-location.js | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cspell.json b/cspell.json index 6e12e2740416..1bf5dc89d162 100644 --- a/cspell.json +++ b/cspell.json @@ -258,6 +258,7 @@ "precommit", "prefetch", "preorder", + "prettiercache", "prettierformatsource", "prettiergetsupportinfo", "prettierignore", diff --git a/tests/integration/__tests__/cache-location.js b/tests/integration/__tests__/cache-location.js index b6d69f88f3a9..eac2a78873d9 100644 --- a/tests/integration/__tests__/cache-location.js +++ b/tests/integration/__tests__/cache-location.js @@ -39,7 +39,7 @@ async function rmdir(dirPath) { describe("--cache-location option", () => { const dir = resolveDir("cli/cache-location"); const defaultCacheFile = path.join(dir, ".prettiercache"); - const newCacheFile = path.join(dir, ".newprettiercache"); + const newCacheFile = path.join(dir, ".new_prettiercache"); const newCacheDir = path.join(dir, "cache-dir"); afterEach(async () => { @@ -50,7 +50,7 @@ describe("--cache-location option", () => { it("doesn't create `.prettiercache` file when `--cache-location` is used.", async () => { await runPrettier(dir, [ "--cache", - "--cache-location=.newprettiercache", + "--cache-location=.new_prettiercache", ".", ]); await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty( @@ -63,7 +63,7 @@ describe("--cache-location option", () => { it("creates cache file at the specified file", async () => { await runPrettier(dir, [ "--cache", - "--cache-location=.newprettiercache", + "--cache-location=.new_prettiercache", ".", ]); const cacheFileData = await fs.readFile(newCacheFile, "utf8"); @@ -73,13 +73,13 @@ describe("--cache-location option", () => { it("skips formatting when cache is available", async () => { await runPrettier(dir, [ "--cache", - "--cache-location=.newprettiercache", + "--cache-location=.new_prettiercache", "--write", ".", ]); const { stdout } = await runPrettier(dir, [ "--cache", - "--cache-location=.newprettiercache", + "--cache-location=.new_prettiercache", "--write", ".", ]); From 54cbbbc78346136b8ca092658d5d389633326e63 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 04:44:03 +0900 Subject: [PATCH 08/53] Update snapshots for new options --- .../__snapshots__/early-exit.js.snap | 8 ++++++ .../__snapshots__/help-options.js.snap | 26 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/tests/integration/__tests__/__snapshots__/early-exit.js.snap b/tests/integration/__tests__/__snapshots__/early-exit.js.snap index 0daa6dcd4f9c..7aa8cca024e3 100644 --- a/tests/integration/__tests__/__snapshots__/early-exit.js.snap +++ b/tests/integration/__tests__/__snapshots__/early-exit.js.snap @@ -139,6 +139,10 @@ Editor options: Other options: + --cache Only format changed files + Defaults to false. + --cache-location Path to the cache file or directory + Defaults to .prettiercache. --no-color Do not colorize error messages. --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched. @@ -309,6 +313,10 @@ Editor options: Other options: + --cache Only format changed files + Defaults to false. + --cache-location Path to the cache file or directory + Defaults to .prettiercache. --no-color Do not colorize error messages. --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched. diff --git a/tests/integration/__tests__/__snapshots__/help-options.js.snap b/tests/integration/__tests__/__snapshots__/help-options.js.snap index a90130ce8a21..f4b17fbc97d4 100644 --- a/tests/integration/__tests__/__snapshots__/help-options.js.snap +++ b/tests/integration/__tests__/__snapshots__/help-options.js.snap @@ -44,6 +44,32 @@ Default: true exports[`show detailed usage with --help bracket-spacing (write) 1`] = `[]`; +exports[`show detailed usage with --help cache (stderr) 1`] = `""`; + +exports[`show detailed usage with --help cache (stdout) 1`] = ` +"--cache + + Only format changed files + +Default: false +" +`; + +exports[`show detailed usage with --help cache (write) 1`] = `[]`; + +exports[`show detailed usage with --help cache-location (stderr) 1`] = `""`; + +exports[`show detailed usage with --help cache-location (stdout) 1`] = ` +"--cache-location + + Path to the cache file or directory + +Default: .prettiercache +" +`; + +exports[`show detailed usage with --help cache-location (write) 1`] = `[]`; + exports[`show detailed usage with --help check (stderr) 1`] = `""`; exports[`show detailed usage with --help check (stdout) 1`] = ` From 2c7b9a368b464d21014ea74cf9ef05abc328c44b Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 05:08:01 +0900 Subject: [PATCH 09/53] Add `groupBy` for website --- website/playground/sidebar/SidebarOptions.js | 2 +- website/playground/util.js | 24 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/website/playground/sidebar/SidebarOptions.js b/website/playground/sidebar/SidebarOptions.js index d47347c7148a..51d3704f1eac 100644 --- a/website/playground/sidebar/SidebarOptions.js +++ b/website/playground/sidebar/SidebarOptions.js @@ -1,5 +1,5 @@ import * as React from "react"; -import { groupBy } from "../../../src/cli/utils.js"; +import { groupBy } from "../util.js"; import { SidebarCategory } from "./components.js"; import Option from "./options.js"; diff --git a/website/playground/util.js b/website/playground/util.js index 2752aebb68cc..587a54910cb8 100644 --- a/website/playground/util.js +++ b/website/playground/util.js @@ -94,3 +94,27 @@ export function getAstAutoFold(parser) { return astAutoFold.glimmer; } } + +/** + * Copied from https://github.com/prettier/prettier/blob/6fe21780115cf5f74f83876d64b03a727fbab220/src/cli/utils.js#L6-L27 + * @template Obj + * @template Key + * @param {Array} array + * @param {(value: Obj) => Key} iteratee + * @returns {{[p in Key]: T}} + */ +export function groupBy(array, iteratee) { + const result = Object.create(null); + + for (const value of array) { + const key = iteratee(value); + + if (Array.isArray(result[key])) { + result[key].push(value); + } else { + result[key] = [value]; + } + } + + return result; +} From 3e0bc435f0b7a3ae8c28ba780e709d3164c34e65 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 05:28:37 +0900 Subject: [PATCH 10/53] Use `fs.promises` instead of `fs/promises` --- tests/integration/__tests__/cache-location.js | 2 +- tests/integration/__tests__/cache.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/__tests__/cache-location.js b/tests/integration/__tests__/cache-location.js index eac2a78873d9..714fd6b81bf1 100644 --- a/tests/integration/__tests__/cache-location.js +++ b/tests/integration/__tests__/cache-location.js @@ -1,7 +1,7 @@ "use strict"; const path = require("path"); -const fs = require("fs/promises"); +const { promises: fs } = require("fs"); const { default: stripAnsi } = require("../../../vendors/strip-ansi.js"); diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index 8711fbc59d59..3263cdcf3d66 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -1,7 +1,7 @@ "use strict"; const path = require("path"); -const fs = require("fs/promises"); +const { promises: fs } = require("fs"); const { default: stripAnsi } = require("../../../vendors/strip-ansi.js"); const runPrettier = require("../run-prettier.js"); From 336262c16bef1965ef0f585a7c2fb2faa0847acc Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 16:47:16 +0900 Subject: [PATCH 11/53] Use `rimraf` instead of `fs.rm` --- tests/integration/__tests__/cache-location.js | 23 ++++--------------- tests/integration/__tests__/cache.js | 3 ++- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/tests/integration/__tests__/cache-location.js b/tests/integration/__tests__/cache-location.js index 714fd6b81bf1..5d91538dad4b 100644 --- a/tests/integration/__tests__/cache-location.js +++ b/tests/integration/__tests__/cache-location.js @@ -2,6 +2,7 @@ const path = require("path"); const { promises: fs } = require("fs"); +const rimraf = require("rimraf"); const { default: stripAnsi } = require("../../../vendors/strip-ansi.js"); @@ -20,31 +21,15 @@ function isJson(str) { } } -async function rm(file) { - try { - await fs.rm(file); - } catch { - // noop - } -} - -async function rmdir(dirPath) { - try { - await await fs.rm(dirPath, { recursive: true, force: true }); - } catch { - // noop - } -} - describe("--cache-location option", () => { const dir = resolveDir("cli/cache-location"); const defaultCacheFile = path.join(dir, ".prettiercache"); const newCacheFile = path.join(dir, ".new_prettiercache"); const newCacheDir = path.join(dir, "cache-dir"); - afterEach(async () => { - await rm(newCacheFile); - await rmdir(newCacheDir); + afterEach(() => { + rimraf.sync(newCacheFile); + rimraf.sync(newCacheDir); }); it("doesn't create `.prettiercache` file when `--cache-location` is used.", async () => { diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index 3263cdcf3d66..93ff57a5f140 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -2,6 +2,7 @@ const path = require("path"); const { promises: fs } = require("fs"); +const rimraf = require("rimraf"); const { default: stripAnsi } = require("../../../vendors/strip-ansi.js"); const runPrettier = require("../run-prettier.js"); @@ -23,7 +24,7 @@ describe("--cache option", () => { }); afterEach(async () => { - await fs.rm(defaultCacheFile); + rimraf.sync(defaultCacheFile); await fs.writeFile(path.join(dir, "a.js"), contentA); await fs.writeFile(path.join(dir, "b.js"), contentB); }); From e87a01e7a6beb54ab9939e01ecc3640c872f1db0 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 16:52:20 +0900 Subject: [PATCH 12/53] Fix properties order of `options` --- src/cli/constant.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cli/constant.js b/src/cli/constant.js index d4952669e1cb..3db0b01dff04 100644 --- a/src/cli/constant.js +++ b/src/cli/constant.js @@ -72,14 +72,14 @@ const categoryOrder = [ /* eslint sort-keys: "error" */ const options = { cache: { - type: "boolean", default: false, description: "Only format changed files", + type: "boolean", }, "cache-location": { - type: "path", - description: "Path to the cache file or directory", default: ".prettiercache", + description: "Path to the cache file or directory", + type: "path", }, check: { alias: "c", From 5fcd8bd87da25d8443a5e500e6a0c615f95349de Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 17:36:04 +0900 Subject: [PATCH 13/53] Install `find-cache-dir` and types --- package.json | 2 ++ yarn.lock | 23 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 91d2faa671b3..5369ca5a35b5 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "fast-glob": "3.2.11", "fast-json-stable-stringify": "2.1.0", "file-entry-cache": "6.0.1", + "find-cache-dir": "3.3.2", "find-parent-dir": "0.3.1", "flow-parser": "0.180.0", "get-stdin": "8.0.0", @@ -98,6 +99,7 @@ "@glimmer/reference": "0.84.2", "@types/estree": "0.0.51", "@types/file-entry-cache": "5.0.2", + "@types/find-cache-dir": "3.2.1", "@types/jest": "27.4.1", "@typescript-eslint/eslint-plugin": "5.20.0", "babel-jest": "27.5.1", diff --git a/yarn.lock b/yarn.lock index a7e2d3e7c434..feb3220e5fd4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1645,6 +1645,11 @@ dependencies: "@types/node" "*" +"@types/find-cache-dir@3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz#7b959a4b9643a1e6a1a5fe49032693cc36773501" + integrity sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw== + "@types/graceful-fs@^4.1.2": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -2401,6 +2406,11 @@ commenting@1.1.0: resolved "https://registry.yarnpkg.com/commenting/-/commenting-1.1.0.tgz#fae14345c6437b8554f30bc6aa6c1e1633033590" integrity sha512-YeNK4tavZwtH7jEgK1ZINXzLKm6DZdEMfsaaieOsCAN0S8vsY7UeuO3Q7d/M018EFgE+IeUAuBOKkFccBZsUZA== +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -3388,6 +3398,15 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +find-cache-dir@3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + find-parent-dir@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/find-parent-dir/-/find-parent-dir-0.3.1.tgz#c5c385b96858c3351f95d446cab866cbf9f11125" @@ -4856,7 +4875,7 @@ magic-string@^0.25.3: dependencies: sourcemap-codec "^1.4.4" -make-dir@^3.0.0: +make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -5406,7 +5425,7 @@ pirates@^4.0.4: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== -pkg-dir@^4.2.0: +pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== From e29a71488788637b796e27e62075b8137de85999 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 17:37:46 +0900 Subject: [PATCH 14/53] Change default cache file location to `node_modules/.cache/prettier` --- src/cli/constant.js | 4 +-- src/cli/find-cache-file.js | 52 ++++++++++++++++++++++++++++++++++++++ src/cli/format.js | 36 ++------------------------ 3 files changed, 56 insertions(+), 36 deletions(-) create mode 100644 src/cli/find-cache-file.js diff --git a/src/cli/constant.js b/src/cli/constant.js index 3db0b01dff04..1cf47d79287a 100644 --- a/src/cli/constant.js +++ b/src/cli/constant.js @@ -77,8 +77,8 @@ const options = { type: "boolean", }, "cache-location": { - default: ".prettiercache", - description: "Path to the cache file or directory", + description: + "Path to the cache file or directory\nDefaults to node_modules/.cache/prettier/.prettiercache", type: "path", }, check: { diff --git a/src/cli/find-cache-file.js b/src/cli/find-cache-file.js new file mode 100644 index 000000000000..ffca542a4f3a --- /dev/null +++ b/src/cli/find-cache-file.js @@ -0,0 +1,52 @@ +"use strict"; + +const { promises: fs } = require("fs"); +const path = require("path"); +const findCacheDir = require("find-cache-dir"); +const { createHash } = require("./utils.js"); + +/** + * Find default cache file (`./node_modules/.cache/prettier/.prettiercache`) using https://github.com/avajs/find-cache-dir + */ +function findDefaultCacheFile() { + const cacheDirThunk = findCacheDir({ name: "prettier", thunk: true }); + const cacheFilePath = cacheDirThunk(".prettiercache"); + return cacheFilePath; +} + +/** + * Inspired by `getCacheFile` function from ESLint + * https://github.com/eslint/eslint/blob/c2d0a830754b6099a3325e6d3348c3ba983a677a/lib/cli-engine/cli-engine.js#L424-L485 + * + * @param {string | undefined} cacheLocation + * @returns {Promise} + */ +async function findCacheFile(cacheLocation) { + if (!cacheLocation) { + return findDefaultCacheFile(); + } + const cwd = process.cwd(); + const normalizedCacheLocation = path.normalize(cacheLocation); + const looksLikeADirectory = normalizedCacheLocation.slice(-1) === path.sep; + const resolvedCacheLocation = path.resolve(cwd, normalizedCacheLocation); + const getCacheFileForDirectory = () => + path.join(resolvedCacheLocation, `.cache_${createHash(cwd)}`); + let fileStats; + try { + fileStats = await fs.lstat(resolvedCacheLocation); + } catch { + fileStats = null; + } + if (fileStats) { + if (fileStats.isDirectory() || looksLikeADirectory) { + return getCacheFileForDirectory(); + } + return resolvedCacheLocation; + } + if (looksLikeADirectory) { + return getCacheFileForDirectory(); + } + return resolvedCacheLocation; +} + +module.exports = findCacheFile; diff --git a/src/cli/format.js b/src/cli/format.js index cd8deaf60094..142d3777cc54 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -15,8 +15,8 @@ const { createIgnorer, errors } = require("./prettier-internal.js"); const { expandPatterns, fixWindowsSlashes } = require("./expand-patterns.js"); const getOptionsForFile = require("./options/get-options-for-file.js"); const isTTY = require("./is-tty.js"); +const findCacheFile = require("./find-cache-file.js"); const FormatResultsCache = require("./format-results-cache.js"); -const { createHash } = require("./utils.js"); function diff(a, b) { return require("diff").createTwoFilesPatch("", "", a, b, "", "", { @@ -285,38 +285,6 @@ async function formatStdin(context) { } } -/** - * Inspired by `getCacheFile` function from ESLint - * https://github.com/eslint/eslint/blob/c2d0a830754b6099a3325e6d3348c3ba983a677a/lib/cli-engine/cli-engine.js#L424-L485 - * - * @param {string} cacheLocation - * @returns {string} - */ -async function getCacheFile(cacheLocation) { - const cwd = process.cwd(); - const normalizedCacheLocation = path.normalize(cacheLocation); - const looksLikeADirectory = normalizedCacheLocation.slice(-1) === path.sep; - const resolvedCacheLocation = path.resolve(cwd, normalizedCacheLocation); - const getCacheFileForDirectory = () => - path.join(resolvedCacheLocation, `.cache_${createHash(cwd)}`); - let fileStats; - try { - fileStats = await fs.lstat(resolvedCacheLocation); - } catch { - fileStats = null; - } - if (fileStats) { - if (fileStats.isDirectory() || looksLikeADirectory) { - return getCacheFileForDirectory(); - } - return resolvedCacheLocation; - } - if (looksLikeADirectory) { - return getCacheFileForDirectory(); - } - return resolvedCacheLocation; -} - async function formatFiles(context) { // The ignorer will be used to filter file paths after the glob is checked, // before any files are actually written @@ -330,7 +298,7 @@ async function formatFiles(context) { } const formatResultsCache = context.argv.cache - ? new FormatResultsCache(await getCacheFile(context.argv.cacheLocation)) + ? new FormatResultsCache(await findCacheFile(context.argv.cacheLocation)) : undefined; for await (const pathOrError of expandPatterns(context)) { From 10952409b1247dfcea40925c39e84eb3b2e5f030 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 17:38:14 +0900 Subject: [PATCH 15/53] Fix tests following to change default cache file location --- tests/integration/__tests__/cache-location.js | 7 +++++-- tests/integration/__tests__/cache.js | 9 ++++++--- tests/integration/cli/cache/package.json | 3 +++ 3 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 tests/integration/cli/cache/package.json diff --git a/tests/integration/__tests__/cache-location.js b/tests/integration/__tests__/cache-location.js index 5d91538dad4b..f2a8ecee2512 100644 --- a/tests/integration/__tests__/cache-location.js +++ b/tests/integration/__tests__/cache-location.js @@ -23,7 +23,10 @@ function isJson(str) { describe("--cache-location option", () => { const dir = resolveDir("cli/cache-location"); - const defaultCacheFile = path.join(dir, ".prettiercache"); + const defaultCacheFile = path.join( + dir, + "node_modules/.cache/prettier/.prettiercache" + ); const newCacheFile = path.join(dir, ".new_prettiercache"); const newCacheDir = path.join(dir, "cache-dir"); @@ -32,7 +35,7 @@ describe("--cache-location option", () => { rimraf.sync(newCacheDir); }); - it("doesn't create `.prettiercache` file when `--cache-location` is used.", async () => { + it("doesn't create default cache file when `--cache-location` is used.", async () => { await runPrettier(dir, [ "--cache", "--cache-location=.new_prettiercache", diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index 93ff57a5f140..88ce5141f68d 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -13,7 +13,10 @@ function resolveDir(dir) { describe("--cache option", () => { const dir = resolveDir("cli/cache"); - const defaultCacheFile = path.join(dir, ".prettiercache"); + const defaultCacheFile = path.join( + dir, + "node_modules/.cache/prettier/.prettiercache" + ); let contentA; let contentB; @@ -24,12 +27,12 @@ describe("--cache option", () => { }); afterEach(async () => { - rimraf.sync(defaultCacheFile); + rimraf.sync(path.join(dir, "node_modules")); await fs.writeFile(path.join(dir, "a.js"), contentA); await fs.writeFile(path.join(dir, "b.js"), contentB); }); - it("creates default cache file named `.prettiercache`", async () => { + it("creates default cache file named `node_modules/.cache/prettier/.prettiercache`", async () => { await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty( "code", "ENOENT" diff --git a/tests/integration/cli/cache/package.json b/tests/integration/cli/cache/package.json new file mode 100644 index 000000000000..91dc022ee930 --- /dev/null +++ b/tests/integration/cli/cache/package.json @@ -0,0 +1,3 @@ +{ + "description": "this file is needed for testing to create default cache file" +} From d248cddbc05d93a73139b4ee8a56117024845042 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 17:44:02 +0900 Subject: [PATCH 16/53] Update snapshots for changing default cache file location --- tests/integration/__tests__/__snapshots__/early-exit.js.snap | 4 ++-- .../integration/__tests__/__snapshots__/help-options.js.snap | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/integration/__tests__/__snapshots__/early-exit.js.snap b/tests/integration/__tests__/__snapshots__/early-exit.js.snap index 7aa8cca024e3..607c04de2c68 100644 --- a/tests/integration/__tests__/__snapshots__/early-exit.js.snap +++ b/tests/integration/__tests__/__snapshots__/early-exit.js.snap @@ -142,7 +142,7 @@ Other options: --cache Only format changed files Defaults to false. --cache-location Path to the cache file or directory - Defaults to .prettiercache. + Defaults to node_modules/.cache/prettier/.prettiercache --no-color Do not colorize error messages. --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched. @@ -316,7 +316,7 @@ Other options: --cache Only format changed files Defaults to false. --cache-location Path to the cache file or directory - Defaults to .prettiercache. + Defaults to node_modules/.cache/prettier/.prettiercache --no-color Do not colorize error messages. --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched. diff --git a/tests/integration/__tests__/__snapshots__/help-options.js.snap b/tests/integration/__tests__/__snapshots__/help-options.js.snap index f4b17fbc97d4..b0a825d1e85c 100644 --- a/tests/integration/__tests__/__snapshots__/help-options.js.snap +++ b/tests/integration/__tests__/__snapshots__/help-options.js.snap @@ -63,8 +63,7 @@ exports[`show detailed usage with --help cache-location (stdout) 1`] = ` "--cache-location Path to the cache file or directory - -Default: .prettiercache + Defaults to node_modules/.cache/prettier/.prettiercache " `; From 85c85f662706621cc92a91af831d160880c25f13 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 17:57:00 +0900 Subject: [PATCH 17/53] Set `os.tmpdir()` as a fallback for default cache dir --- src/cli/find-cache-file.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cli/find-cache-file.js b/src/cli/find-cache-file.js index ffca542a4f3a..90573c0169f4 100644 --- a/src/cli/find-cache-file.js +++ b/src/cli/find-cache-file.js @@ -1,6 +1,7 @@ "use strict"; const { promises: fs } = require("fs"); +const os = require("os"); const path = require("path"); const findCacheDir = require("find-cache-dir"); const { createHash } = require("./utils.js"); @@ -9,8 +10,9 @@ const { createHash } = require("./utils.js"); * Find default cache file (`./node_modules/.cache/prettier/.prettiercache`) using https://github.com/avajs/find-cache-dir */ function findDefaultCacheFile() { - const cacheDirThunk = findCacheDir({ name: "prettier", thunk: true }); - const cacheFilePath = cacheDirThunk(".prettiercache"); + const cacheDir = + findCacheDir({ name: "prettier", create: true }) || os.tmpdir(); + const cacheFilePath = path.join(cacheDir, ".prettiercache"); return cacheFilePath; } From 5cec2f926dfb4d93c6097cf636cbfc88e6ec6db1 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 18:21:44 +0900 Subject: [PATCH 18/53] Add docs for `--cache` and `--cache-location` --- docs/cli.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/cli.md b/docs/cli.md index f72895ebd8a9..848871f43c69 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -204,3 +204,23 @@ Prevent errors when pattern is unmatched. ## `--no-plugin-search` Disable plugin autoloading. + +## `--cache` + +When you want to format only changed files, you can run Prettier with `--cache` flag. Enabling this flag can dramatically improve running time. + +```bash +prettier --write --cache src +``` + +## `--cache-location` + +Path to the cache file or directory location used by `--cache` flag. If you don't explicit `--cache-location`, Prettier saves cache file at `./node_modules/.cache/prettier/.prettiercache`. + +```bash +prettier --write --cache --cache-location=my_cache_file src +``` + +```bash +prettier --write --cache --cache-location=my_cache_dir/ src +``` From 8536899170bc40183b1ee8ab0a83f854050417ae Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 7 May 2022 18:46:26 +0900 Subject: [PATCH 19/53] Use `sdbm` instead of `node:crypto` --- cspell.json | 1 + package.json | 1 + scripts/vendors/vendor-meta.json | 18 ++++++++++++++++ scripts/vendors/vendors.mjs | 1 + src/cli/utils.js | 5 +++-- vendors/sdbm.d.ts | 3 +++ vendors/sdbm.js | 35 ++++++++++++++++++++++++++++++++ yarn.lock | 5 +++++ 8 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 vendors/sdbm.d.ts create mode 100644 vendors/sdbm.js diff --git a/cspell.json b/cspell.json index 1bf5dc89d162..a049d060d3d7 100644 --- a/cspell.json +++ b/cspell.json @@ -290,6 +290,7 @@ "sandhose", "Sapegin", "sbdchd", + "sdbm", "scandir", "Serializers", "setlocal", diff --git a/package.json b/package.json index 5369ca5a35b5..f3149c4c8a76 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,7 @@ "remark-math": "3.0.1", "remark-parse": "8.0.3", "resolve": "1.22.0", + "sdbm": "2.0.0", "semver": "7.3.7", "string-width": "5.0.1", "strip-ansi": "7.0.1", diff --git a/scripts/vendors/vendor-meta.json b/scripts/vendors/vendor-meta.json index 0a5c00b08b0b..e4505dcf7016 100644 --- a/scripts/vendors/vendor-meta.json +++ b/scripts/vendors/vendor-meta.json @@ -9,6 +9,7 @@ "html-void-elements": "html-void-elements.json", "leven": "leven.js", "mem": "mem.js", + "sdbm": "sdbm.js", "string-width": "string-width.js", "strip-ansi": "strip-ansi.js" }, @@ -503,6 +504,23 @@ }, "contributors": [] }, + { + "name": "sdbm", + "maintainers": [], + "version": "2.0.0", + "description": "SDBM non-cryptographic hash function", + "repository": "sindresorhus/sdbm", + "homepage": null, + "private": false, + "license": "MIT", + "licenseText": "MIT License\n\nCopyright (c) Sindre Sorhus (https://sindresorhus.com)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n", + "author": { + "name": "Sindre Sorhus", + "email": "sindresorhus@gmail.com", + "url": "https://sindresorhus.com" + }, + "contributors": [] + }, { "name": "ansi-regex", "maintainers": [], diff --git a/scripts/vendors/vendors.mjs b/scripts/vendors/vendors.mjs index c477dd756263..0473e0daa5e3 100644 --- a/scripts/vendors/vendors.mjs +++ b/scripts/vendors/vendors.mjs @@ -8,6 +8,7 @@ const vendors = [ "html-void-elements", "leven", "mem", + "sdbm", "string-width", "strip-ansi", "tempy", diff --git a/src/cli/utils.js b/src/cli/utils.js index 2a45a8ec2c5a..dc71b97a2b46 100644 --- a/src/cli/utils.js +++ b/src/cli/utils.js @@ -1,6 +1,7 @@ "use strict"; -const crypto = require("crypto"); +// eslint-disable-next-line no-restricted-modules +const { default: sdbm } = require("../../vendors/sdbm.js"); // eslint-disable-next-line no-console const printToScreen = console.log.bind(console); @@ -45,7 +46,7 @@ function pick(object, keys) { * @returns {string} */ function createHash(source) { - return crypto.createHash("md5").update(source).digest("hex"); + return sdbm(source).toString(); } module.exports = { printToScreen, groupBy, pick, createHash }; diff --git a/vendors/sdbm.d.ts b/vendors/sdbm.d.ts new file mode 100644 index 000000000000..812356628a7f --- /dev/null +++ b/vendors/sdbm.d.ts @@ -0,0 +1,3 @@ +// This file is generated automatically. +export {default} from "sdbm"; +export * from "sdbm"; diff --git a/vendors/sdbm.js b/vendors/sdbm.js new file mode 100644 index 000000000000..d867062988b5 --- /dev/null +++ b/vendors/sdbm.js @@ -0,0 +1,35 @@ +// @ts-nocheck +// This file is generated automatically +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// node_modules/sdbm/index.js +var sdbm_exports = {}; +__export(sdbm_exports, { + default: () => sdbm +}); +module.exports = __toCommonJS(sdbm_exports); +function sdbm(string) { + let hash = 0; + for (let i = 0; i < string.length; i++) { + hash = string.charCodeAt(i) + (hash << 6) + (hash << 16) - hash; + } + return hash >>> 0; +} +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = {}); diff --git a/yarn.lock b/yarn.lock index feb3220e5fd4..72e836b3e8d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5872,6 +5872,11 @@ scslre@^0.1.6: regexp-ast-analysis "^0.2.3" regexpp "^3.2.0" +sdbm@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sdbm/-/sdbm-2.0.0.tgz#23828c1195e341d0f5810c59dfa60d86278f8718" + integrity sha512-dspMGxvHiwSTgyrmm90jHQV2sDqK46ssbDK+bQAlJ5aRuPo3C7So108V6rCuCDbm1CrNWuPeMpmTNQKPl7vO+A== + semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" From ef3b7ab544f4aae2428681fa2328d8c7dc0328c6 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sun, 8 May 2022 16:55:24 +0900 Subject: [PATCH 20/53] Add changelog --- changelog_unreleased/cli/12800.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 changelog_unreleased/cli/12800.md diff --git a/changelog_unreleased/cli/12800.md b/changelog_unreleased/cli/12800.md new file mode 100644 index 000000000000..3a7629470e1c --- /dev/null +++ b/changelog_unreleased/cli/12800.md @@ -0,0 +1,23 @@ +#### Add new `--cache` and `--cache-location` (#12800 by @sosukesuzuki) + +Two new CLI options have been added for a caching system similar to [ESLint's one](https://eslint.org/docs/user-guide/command-line-interface#caching). Please see the [documentation](https://prettier.io/docs/en/cli.html#--cache) for details. + +##### `--cache` + +When you want to format only changed files, you can run Prettier with `--cache` flag. Enabling this flag can dramatically improve running time. + +```bash +prettier --write --cache src +``` + +##### `--cache-location` + +Path to the cache file or directory location used by `--cache` flag. If you don't explicit `--cache-location`, Prettier saves cache file at `./node_modules/.cache/prettier/.prettiercache`. + +```bash +prettier --write --cache --cache-location=my_cache_file src +``` + +```bash +prettier --write --cache --cache-location=my_cache_dir/ src +``` From a7c845faffa22218666b9433f0c1e6cb96f6ba94 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Mon, 9 May 2022 23:48:53 +0900 Subject: [PATCH 21/53] Fix `getHashOfOptions` --- src/cli/format-results-cache.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/cli/format-results-cache.js b/src/cli/format-results-cache.js index a68c04a66556..2b956720e2b8 100644 --- a/src/cli/format-results-cache.js +++ b/src/cli/format-results-cache.js @@ -9,7 +9,7 @@ const stringify = require("fast-json-stable-stringify"); const { version: prettierVersion } = require("../index.js"); const { createHash } = require("./utils.js"); -const configHashCache = new WeakMap(); +const optionsHashCache = new WeakMap(); const nodeVersion = process && process.version; /** @@ -17,10 +17,14 @@ const nodeVersion = process && process.version; * @returns {string} */ function getHashOfOptions(options) { - if (configHashCache.has(options)) { - return configHashCache.get(options); + if (optionsHashCache.has(options)) { + return optionsHashCache.get(options); } - return createHash(`${prettierVersion}_${nodeVersion}_${stringify(options)}`); + const hash = createHash( + `${prettierVersion}_${nodeVersion}_${stringify(options)}` + ); + optionsHashCache.set(options, hash); + return hash; } /** From 11a1c4f857fc78456a88963bb7567bda8b31243e Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Wed, 11 May 2022 13:00:39 +0900 Subject: [PATCH 22/53] Remove `cache-location` --- changelog_unreleased/cli/12800.md | 14 +-- docs/cli.md | 12 -- src/cli/constant.js | 5 - src/cli/find-cache-file.js | 36 +----- src/cli/format.js | 2 +- .../__snapshots__/early-exit.js.snap | 4 - .../__snapshots__/help-options.js.snap | 12 -- tests/integration/__tests__/cache-location.js | 115 ------------------ 8 files changed, 5 insertions(+), 195 deletions(-) delete mode 100644 tests/integration/__tests__/cache-location.js diff --git a/changelog_unreleased/cli/12800.md b/changelog_unreleased/cli/12800.md index 3a7629470e1c..13f4aaba6df9 100644 --- a/changelog_unreleased/cli/12800.md +++ b/changelog_unreleased/cli/12800.md @@ -1,4 +1,4 @@ -#### Add new `--cache` and `--cache-location` (#12800 by @sosukesuzuki) +#### Add new `--cache` (#12800 by @sosukesuzuki) Two new CLI options have been added for a caching system similar to [ESLint's one](https://eslint.org/docs/user-guide/command-line-interface#caching). Please see the [documentation](https://prettier.io/docs/en/cli.html#--cache) for details. @@ -9,15 +9,3 @@ When you want to format only changed files, you can run Prettier with `--cache` ```bash prettier --write --cache src ``` - -##### `--cache-location` - -Path to the cache file or directory location used by `--cache` flag. If you don't explicit `--cache-location`, Prettier saves cache file at `./node_modules/.cache/prettier/.prettiercache`. - -```bash -prettier --write --cache --cache-location=my_cache_file src -``` - -```bash -prettier --write --cache --cache-location=my_cache_dir/ src -``` diff --git a/docs/cli.md b/docs/cli.md index 848871f43c69..612b9dc7f920 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -212,15 +212,3 @@ When you want to format only changed files, you can run Prettier with `--cache` ```bash prettier --write --cache src ``` - -## `--cache-location` - -Path to the cache file or directory location used by `--cache` flag. If you don't explicit `--cache-location`, Prettier saves cache file at `./node_modules/.cache/prettier/.prettiercache`. - -```bash -prettier --write --cache --cache-location=my_cache_file src -``` - -```bash -prettier --write --cache --cache-location=my_cache_dir/ src -``` diff --git a/src/cli/constant.js b/src/cli/constant.js index 1cf47d79287a..5b0fb28a72ba 100644 --- a/src/cli/constant.js +++ b/src/cli/constant.js @@ -76,11 +76,6 @@ const options = { description: "Only format changed files", type: "boolean", }, - "cache-location": { - description: - "Path to the cache file or directory\nDefaults to node_modules/.cache/prettier/.prettiercache", - type: "path", - }, check: { alias: "c", category: coreOptions.CATEGORY_OUTPUT, diff --git a/src/cli/find-cache-file.js b/src/cli/find-cache-file.js index 90573c0169f4..13b32bf69a10 100644 --- a/src/cli/find-cache-file.js +++ b/src/cli/find-cache-file.js @@ -1,10 +1,8 @@ "use strict"; -const { promises: fs } = require("fs"); const os = require("os"); const path = require("path"); const findCacheDir = require("find-cache-dir"); -const { createHash } = require("./utils.js"); /** * Find default cache file (`./node_modules/.cache/prettier/.prettiercache`) using https://github.com/avajs/find-cache-dir @@ -17,38 +15,10 @@ function findDefaultCacheFile() { } /** - * Inspired by `getCacheFile` function from ESLint - * https://github.com/eslint/eslint/blob/c2d0a830754b6099a3325e6d3348c3ba983a677a/lib/cli-engine/cli-engine.js#L424-L485 - * - * @param {string | undefined} cacheLocation - * @returns {Promise} + * @returns {string} */ -async function findCacheFile(cacheLocation) { - if (!cacheLocation) { - return findDefaultCacheFile(); - } - const cwd = process.cwd(); - const normalizedCacheLocation = path.normalize(cacheLocation); - const looksLikeADirectory = normalizedCacheLocation.slice(-1) === path.sep; - const resolvedCacheLocation = path.resolve(cwd, normalizedCacheLocation); - const getCacheFileForDirectory = () => - path.join(resolvedCacheLocation, `.cache_${createHash(cwd)}`); - let fileStats; - try { - fileStats = await fs.lstat(resolvedCacheLocation); - } catch { - fileStats = null; - } - if (fileStats) { - if (fileStats.isDirectory() || looksLikeADirectory) { - return getCacheFileForDirectory(); - } - return resolvedCacheLocation; - } - if (looksLikeADirectory) { - return getCacheFileForDirectory(); - } - return resolvedCacheLocation; +function findCacheFile() { + return findDefaultCacheFile(); } module.exports = findCacheFile; diff --git a/src/cli/format.js b/src/cli/format.js index 142d3777cc54..ef81f6ed2881 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -298,7 +298,7 @@ async function formatFiles(context) { } const formatResultsCache = context.argv.cache - ? new FormatResultsCache(await findCacheFile(context.argv.cacheLocation)) + ? new FormatResultsCache(findCacheFile()) : undefined; for await (const pathOrError of expandPatterns(context)) { diff --git a/tests/integration/__tests__/__snapshots__/early-exit.js.snap b/tests/integration/__tests__/__snapshots__/early-exit.js.snap index 607c04de2c68..b680ff68c4e8 100644 --- a/tests/integration/__tests__/__snapshots__/early-exit.js.snap +++ b/tests/integration/__tests__/__snapshots__/early-exit.js.snap @@ -141,8 +141,6 @@ Other options: --cache Only format changed files Defaults to false. - --cache-location Path to the cache file or directory - Defaults to node_modules/.cache/prettier/.prettiercache --no-color Do not colorize error messages. --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched. @@ -315,8 +313,6 @@ Other options: --cache Only format changed files Defaults to false. - --cache-location Path to the cache file or directory - Defaults to node_modules/.cache/prettier/.prettiercache --no-color Do not colorize error messages. --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched. diff --git a/tests/integration/__tests__/__snapshots__/help-options.js.snap b/tests/integration/__tests__/__snapshots__/help-options.js.snap index b0a825d1e85c..9c46a4479eb7 100644 --- a/tests/integration/__tests__/__snapshots__/help-options.js.snap +++ b/tests/integration/__tests__/__snapshots__/help-options.js.snap @@ -57,18 +57,6 @@ Default: false exports[`show detailed usage with --help cache (write) 1`] = `[]`; -exports[`show detailed usage with --help cache-location (stderr) 1`] = `""`; - -exports[`show detailed usage with --help cache-location (stdout) 1`] = ` -"--cache-location - - Path to the cache file or directory - Defaults to node_modules/.cache/prettier/.prettiercache -" -`; - -exports[`show detailed usage with --help cache-location (write) 1`] = `[]`; - exports[`show detailed usage with --help check (stderr) 1`] = `""`; exports[`show detailed usage with --help check (stdout) 1`] = ` diff --git a/tests/integration/__tests__/cache-location.js b/tests/integration/__tests__/cache-location.js deleted file mode 100644 index f2a8ecee2512..000000000000 --- a/tests/integration/__tests__/cache-location.js +++ /dev/null @@ -1,115 +0,0 @@ -"use strict"; - -const path = require("path"); -const { promises: fs } = require("fs"); -const rimraf = require("rimraf"); - -const { default: stripAnsi } = require("../../../vendors/strip-ansi.js"); - -const runPrettier = require("../run-prettier.js"); - -function resolveDir(dir) { - return path.join(__dirname, "..", dir); -} - -function isJson(str) { - try { - JSON.parse(str); - return true; - } catch { - return false; - } -} - -describe("--cache-location option", () => { - const dir = resolveDir("cli/cache-location"); - const defaultCacheFile = path.join( - dir, - "node_modules/.cache/prettier/.prettiercache" - ); - const newCacheFile = path.join(dir, ".new_prettiercache"); - const newCacheDir = path.join(dir, "cache-dir"); - - afterEach(() => { - rimraf.sync(newCacheFile); - rimraf.sync(newCacheDir); - }); - - it("doesn't create default cache file when `--cache-location` is used.", async () => { - await runPrettier(dir, [ - "--cache", - "--cache-location=.new_prettiercache", - ".", - ]); - await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty( - "code", - "ENOENT" - ); - }); - - describe("specify file name", () => { - it("creates cache file at the specified file", async () => { - await runPrettier(dir, [ - "--cache", - "--cache-location=.new_prettiercache", - ".", - ]); - const cacheFileData = await fs.readFile(newCacheFile, "utf8"); - await expect(isJson(cacheFileData)).toBe(true); - }); - - it("skips formatting when cache is available", async () => { - await runPrettier(dir, [ - "--cache", - "--cache-location=.new_prettiercache", - "--write", - ".", - ]); - const { stdout } = await runPrettier(dir, [ - "--cache", - "--cache-location=.new_prettiercache", - "--write", - ".", - ]); - expect(stripAnsi(stdout).split("\n").filter(Boolean)).toEqual( - expect.arrayContaining([ - expect.stringMatching(/^a\.js .+ms \(cached\)$/), - expect.stringMatching(/^b\.js .+ms \(cached\)$/), - ]) - ); - }); - }); - - describe("specify director name", () => { - it("creates cache file into the specified directory", async () => { - await runPrettier(dir, ["--cache", "--cache-location=cache-dir/", "."]); - const fileNames = await fs.readdir(newCacheDir); - expect(fileNames.length).toBe(1); - const cacheFileName = fileNames[0]; - const filePath = path.join(newCacheDir, cacheFileName); - const cacheFileData = await fs.readFile(filePath, "utf8"); - await expect(isJson(cacheFileData)).toBe(true); - }); - - it("skips formatting when cache is available", async () => { - await runPrettier(dir, [ - "--cache", - "--cache-location=cache-dir/", - "--write", - ".", - ]); - const { stdout } = await runPrettier(dir, [ - "--cache", - "--cache-location=cache-dir/", - "--write", - ".", - ]); - expect(stripAnsi(stdout).split("\n").filter(Boolean)).toEqual( - expect.arrayContaining([ - expect.stringMatching(/^a\.js .+ms \(cached\)$/), - expect.stringMatching(/^b\.js .+ms \(cached\)$/), - ]) - ); - }); - }); -}); From 01159fb17c87a4a29ebcdc405accdf3151e8b131 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Wed, 11 May 2022 13:29:39 +0900 Subject: [PATCH 23/53] `prettiercache` -> `prettier-cache` --- cspell.json | 1 - src/cli/find-cache-file.js | 4 ++-- tests/integration/__tests__/cache.js | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cspell.json b/cspell.json index a049d060d3d7..37f04c11cdd1 100644 --- a/cspell.json +++ b/cspell.json @@ -258,7 +258,6 @@ "precommit", "prefetch", "preorder", - "prettiercache", "prettierformatsource", "prettiergetsupportinfo", "prettierignore", diff --git a/src/cli/find-cache-file.js b/src/cli/find-cache-file.js index 13b32bf69a10..5ef02279ab87 100644 --- a/src/cli/find-cache-file.js +++ b/src/cli/find-cache-file.js @@ -5,12 +5,12 @@ const path = require("path"); const findCacheDir = require("find-cache-dir"); /** - * Find default cache file (`./node_modules/.cache/prettier/.prettiercache`) using https://github.com/avajs/find-cache-dir + * Find default cache file (`./node_modules/.cache/prettier/.prettier-cache`) using https://github.com/avajs/find-cache-dir */ function findDefaultCacheFile() { const cacheDir = findCacheDir({ name: "prettier", create: true }) || os.tmpdir(); - const cacheFilePath = path.join(cacheDir, ".prettiercache"); + const cacheFilePath = path.join(cacheDir, ".prettier-cache"); return cacheFilePath; } diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index 88ce5141f68d..e2dd9a6be7f8 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -15,7 +15,7 @@ describe("--cache option", () => { const dir = resolveDir("cli/cache"); const defaultCacheFile = path.join( dir, - "node_modules/.cache/prettier/.prettiercache" + "node_modules/.cache/prettier/.prettier-cache" ); let contentA; @@ -32,7 +32,7 @@ describe("--cache option", () => { await fs.writeFile(path.join(dir, "b.js"), contentB); }); - it("creates default cache file named `node_modules/.cache/prettier/.prettiercache`", async () => { + it("creates default cache file named `node_modules/.cache/prettier/.prettier-cache`", async () => { await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty( "code", "ENOENT" From ed1047ba1c4da20b7cd79205ba3e4be5cb585098 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Wed, 11 May 2022 14:49:43 +0900 Subject: [PATCH 24/53] Remove cache file when run Prettier without `--cache` option --- src/cli/expand-patterns.js | 19 ++----------------- src/cli/format.js | 14 +++++++++++--- src/cli/utils.js | 20 +++++++++++++++++++- tests/integration/__tests__/cache.js | 7 +++++++ 4 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/cli/expand-patterns.js b/src/cli/expand-patterns.js index d62108f7ec95..c097b10d3870 100644 --- a/src/cli/expand-patterns.js +++ b/src/cli/expand-patterns.js @@ -1,9 +1,10 @@ "use strict"; const path = require("path"); -const { promises: fs } = require("fs"); const fastGlob = require("fast-glob"); +const { statSafe } = require("./utils.js"); + /** @typedef {import('./context').Context} Context */ /** @@ -173,22 +174,6 @@ function sortPaths(paths) { return paths.sort((a, b) => a.localeCompare(b)); } -/** - * Get stats of a given path. - * @param {string} filePath The path to target file. - * @returns {Promise} The stats. - */ -async function statSafe(filePath) { - try { - return await fs.stat(filePath); - } catch (error) { - /* istanbul ignore next */ - if (error.code !== "ENOENT") { - throw error; - } - } -} - /** * This function should be replaced with `fastGlob.escapePath` when these issues are fixed: * - https://github.com/mrmlnc/fast-glob/issues/261 diff --git a/src/cli/format.js b/src/cli/format.js index ef81f6ed2881..99209da6af7e 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -17,6 +17,7 @@ const getOptionsForFile = require("./options/get-options-for-file.js"); const isTTY = require("./is-tty.js"); const findCacheFile = require("./find-cache-file.js"); const FormatResultsCache = require("./format-results-cache.js"); +const { statSafe } = require("./utils.js"); function diff(a, b) { return require("diff").createTwoFilesPatch("", "", a, b, "", "", { @@ -297,9 +298,16 @@ async function formatFiles(context) { context.logger.log("Checking formatting..."); } - const formatResultsCache = context.argv.cache - ? new FormatResultsCache(findCacheFile()) - : undefined; + let formatResultsCache; + const cacheFilePath = findCacheFile(); + if (context.argv.cache) { + formatResultsCache = new FormatResultsCache(cacheFilePath); + } else { + const stat = await statSafe(cacheFilePath); + if (stat) { + await fs.unlink(cacheFilePath); + } + } for await (const pathOrError of expandPatterns(context)) { if (typeof pathOrError === "object") { diff --git a/src/cli/utils.js b/src/cli/utils.js index dc71b97a2b46..9eb93b3c06a3 100644 --- a/src/cli/utils.js +++ b/src/cli/utils.js @@ -1,5 +1,7 @@ "use strict"; +const { promises: fs } = require("fs"); + // eslint-disable-next-line no-restricted-modules const { default: sdbm } = require("../../vendors/sdbm.js"); @@ -49,4 +51,20 @@ function createHash(source) { return sdbm(source).toString(); } -module.exports = { printToScreen, groupBy, pick, createHash }; +/** + * Get stats of a given path. + * @param {string} filePath The path to target file. + * @returns {Promise} The stats. + */ +async function statSafe(filePath) { + try { + return await fs.stat(filePath); + } catch (error) { + /* istanbul ignore next */ + if (error.code !== "ENOENT") { + throw error; + } + } +} + +module.exports = { printToScreen, groupBy, pick, createHash, statSafe }; diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index e2dd9a6be7f8..cebcd00d0172 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -124,4 +124,11 @@ describe("--cache option", () => { ]) ); }); + + it("removes cache file when run Prettier without `--cache` option", async () => { + await runPrettier(dir, ["--cache", "--write", "."]); + await expect(fs.stat(defaultCacheFile)).resolves.not.toThrowError(); + await runPrettier(dir, ["--write", "."]); + await expect(fs.stat(defaultCacheFile)).rejects.toThrowError(); + }); }); From e7d739a3c088f1cca90bd4e9a555fc206e008a57 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Fri, 27 May 2022 17:34:13 +0900 Subject: [PATCH 25/53] Implement `--cache-strategy` --- src/cli/constant.js | 6 ++++++ src/cli/format-results-cache.js | 28 ++++++++++++++++++++++++++-- src/cli/format.js | 7 ++++++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/cli/constant.js b/src/cli/constant.js index 5b0fb28a72ba..6c7373f53083 100644 --- a/src/cli/constant.js +++ b/src/cli/constant.js @@ -76,6 +76,12 @@ const options = { description: "Only format changed files", type: "boolean", }, + "cache-strategy": { + default: "metadata", + describe: + "Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. ", + type: "string", + }, check: { alias: "c", category: coreOptions.CATEGORY_OUTPUT, diff --git a/src/cli/format-results-cache.js b/src/cli/format-results-cache.js index 2b956720e2b8..1ee1f962c36c 100644 --- a/src/cli/format-results-cache.js +++ b/src/cli/format-results-cache.js @@ -38,14 +38,38 @@ function getMetadataFromFileDescriptor(fileDescriptor) { return fileDescriptor.meta; } +const cacheStrategies = ["metadata", "content"]; + +/** + * @typedef {"metadata" | "content"} CacheStrategy + * + * @param {string} value + * @returns {value is CacheStrategy} + */ +function isValidCacheStrategy(value) { + return cacheStrategies.includes(value); +} + class FormatResultsCache { /** * @param {string} cacheFileLocation The path of cache file location. (default: `.prettiercache`) + * @param {string} cacheStrategy */ - constructor(cacheFileLocation) { + constructor(cacheFileLocation, cacheStrategy) { + if (!isValidCacheStrategy(cacheStrategy)) { + const errorMessage = `Cache strategy must beone of : ${cacheStrategies + .map((strategy) => `"${strategy}"`) + .join(", ")}`; + throw new Error(errorMessage); + } + + const useChecksum = cacheStrategy === "content"; + this.cacheFileLocation = cacheFileLocation; this.fileEntryCache = fileEntryCache.create( - /* cacheId */ cacheFileLocation + /* cacheId */ cacheFileLocation, + /* directory */ undefined, + useChecksum ); } diff --git a/src/cli/format.js b/src/cli/format.js index 99209da6af7e..19a06830b43b 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -301,7 +301,12 @@ async function formatFiles(context) { let formatResultsCache; const cacheFilePath = findCacheFile(); if (context.argv.cache) { - formatResultsCache = new FormatResultsCache(cacheFilePath); + try { + formatResultsCache = new FormatResultsCache(cacheFilePath); + } catch (error) { + context.logger.error(error.message); + process.exit(2); + } } else { const stat = await statSafe(cacheFilePath); if (stat) { From fd0484a353030c1f66293b56ff6ae5de3b8874e7 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Fri, 27 May 2022 17:39:58 +0900 Subject: [PATCH 26/53] Add tests for invalid cache strategy --- src/cli/format-results-cache.js | 2 +- tests/integration/__tests__/cache.js | 12 ++++++++++++ tests/integration/cli/cache-location/a.js | 3 --- tests/integration/cli/cache-location/b.js | 3 --- 4 files changed, 13 insertions(+), 7 deletions(-) delete mode 100644 tests/integration/cli/cache-location/a.js delete mode 100644 tests/integration/cli/cache-location/b.js diff --git a/src/cli/format-results-cache.js b/src/cli/format-results-cache.js index 1ee1f962c36c..3d0ec3f5d308 100644 --- a/src/cli/format-results-cache.js +++ b/src/cli/format-results-cache.js @@ -57,7 +57,7 @@ class FormatResultsCache { */ constructor(cacheFileLocation, cacheStrategy) { if (!isValidCacheStrategy(cacheStrategy)) { - const errorMessage = `Cache strategy must beone of : ${cacheStrategies + const errorMessage = `Cache strategy must be one of: ${cacheStrategies .map((strategy) => `"${strategy}"`) .join(", ")}`; throw new Error(errorMessage); diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index cebcd00d0172..564366a5b7c6 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -131,4 +131,16 @@ describe("--cache option", () => { await runPrettier(dir, ["--write", "."]); await expect(fs.stat(defaultCacheFile)).rejects.toThrowError(); }); + + it("throw error when cache-strategy is invalid", async () => { + const { stderr } = await runPrettier(dir, [ + "--cache", + "--cache-strategy", + "invalid", + ".", + ]); + expect(stripAnsi(stderr.trim())).toBe( + '[error] Cache strategy must be one of: "metadata", "content"' + ); + }); }); diff --git a/tests/integration/cli/cache-location/a.js b/tests/integration/cli/cache-location/a.js deleted file mode 100644 index b824ea0f9278..000000000000 --- a/tests/integration/cli/cache-location/a.js +++ /dev/null @@ -1,3 +0,0 @@ -function a() { - console.log("this is a.js") -} diff --git a/tests/integration/cli/cache-location/b.js b/tests/integration/cli/cache-location/b.js deleted file mode 100644 index 7c8812b4e1e8..000000000000 --- a/tests/integration/cli/cache-location/b.js +++ /dev/null @@ -1,3 +0,0 @@ -function b() { - console.log("this is b.js") -} From efbbe8e8c00b02fb28e001d47739b8e2689cf7a4 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Fri, 27 May 2022 17:45:20 +0900 Subject: [PATCH 27/53] Fix lint problems --- src/cli/format-results-cache.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/format-results-cache.js b/src/cli/format-results-cache.js index 3d0ec3f5d308..f81ae1eec2e4 100644 --- a/src/cli/format-results-cache.js +++ b/src/cli/format-results-cache.js @@ -52,7 +52,7 @@ function isValidCacheStrategy(value) { class FormatResultsCache { /** - * @param {string} cacheFileLocation The path of cache file location. (default: `.prettiercache`) + * @param {string} cacheFileLocation The path of cache file location. (default: `node_modules/.cache/prettier/prettier-cache`) * @param {string} cacheStrategy */ constructor(cacheFileLocation, cacheStrategy) { From c4eebf1d3bef94dd4745b0aa919504770dfb69f5 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Fri, 27 May 2022 18:33:14 +0900 Subject: [PATCH 28/53] Tweaks tests --- src/cli/format.js | 5 +- tests/integration/__tests__/cache.js | 202 ++++++++++++++------------- 2 files changed, 106 insertions(+), 101 deletions(-) diff --git a/src/cli/format.js b/src/cli/format.js index 19a06830b43b..23a1310d032e 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -302,7 +302,10 @@ async function formatFiles(context) { const cacheFilePath = findCacheFile(); if (context.argv.cache) { try { - formatResultsCache = new FormatResultsCache(cacheFilePath); + formatResultsCache = new FormatResultsCache( + cacheFilePath, + context.argv.cacheStrategy + ); } catch (error) { context.logger.error(error.message); process.exit(2); diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index 564366a5b7c6..35932595da49 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -32,106 +32,6 @@ describe("--cache option", () => { await fs.writeFile(path.join(dir, "b.js"), contentB); }); - it("creates default cache file named `node_modules/.cache/prettier/.prettier-cache`", async () => { - await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty( - "code", - "ENOENT" - ); - await runPrettier(dir, ["--cache", "."]); - await expect(fs.stat(defaultCacheFile)).resolves.not.toThrowError(); - }); - - it("does'nt format when cache is available", async () => { - const { stdout: firstStdout } = await runPrettier(dir, [ - "--cache", - "--write", - ".", - ]); - expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( - expect.arrayContaining([ - expect.stringMatching(/^a\.js .+ms$/), - expect.stringMatching(/^b\.js .+ms$/), - ]) - ); - - const { stdout: secondStdout } = await runPrettier(dir, [ - "--cache", - "--write", - ".", - ]); - expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( - expect.arrayContaining([ - expect.stringMatching(/^a\.js .+ms \(cached\)$/), - expect.stringMatching(/^b\.js .+ms \(cached\)$/), - ]) - ); - }); - - it("re-formats when a file has been updated.", async () => { - const { stdout: firstStdout } = await runPrettier(dir, [ - "--cache", - "--write", - ".", - ]); - expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( - expect.arrayContaining([ - expect.stringMatching(/^a\.js .+ms$/), - expect.stringMatching(/^b\.js .+ms$/), - ]) - ); - - // Update `a.js` - await fs.writeFile(path.join(dir, "a.js"), "const a = `a`;"); - - const { stdout: secondStdout } = await runPrettier(dir, [ - "--cache", - "--write", - ".", - ]); - expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( - // the cache of `b.js` is only available. - expect.arrayContaining([ - expect.stringMatching(/^a\.js .+ms$/), - expect.stringMatching(/^b\.js .+ms \(cached\)$/), - ]) - ); - }); - - it("re-formats when options has been updated.", async () => { - const { stdout: firstStdout } = await runPrettier(dir, [ - "--cache", - "--write", - ".", - ]); - expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( - expect.arrayContaining([ - expect.stringMatching(/^a\.js .+ms$/), - expect.stringMatching(/^b\.js .+ms$/), - ]) - ); - - const { stdout: secondStdout } = await runPrettier(dir, [ - "--cache", - "--write", - "--trailing-comma", - "all", - ".", - ]); - expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( - expect.arrayContaining([ - expect.stringMatching(/^a\.js .+ms$/), - expect.stringMatching(/^b\.js .+ms$/), - ]) - ); - }); - - it("removes cache file when run Prettier without `--cache` option", async () => { - await runPrettier(dir, ["--cache", "--write", "."]); - await expect(fs.stat(defaultCacheFile)).resolves.not.toThrowError(); - await runPrettier(dir, ["--write", "."]); - await expect(fs.stat(defaultCacheFile)).rejects.toThrowError(); - }); - it("throw error when cache-strategy is invalid", async () => { const { stderr } = await runPrettier(dir, [ "--cache", @@ -143,4 +43,106 @@ describe("--cache option", () => { '[error] Cache strategy must be one of: "metadata", "content"' ); }); + + describe("--cache-strategy metadata", () => { + it("creates default cache file named `node_modules/.cache/prettier/.prettier-cache`", async () => { + await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty( + "code", + "ENOENT" + ); + await runPrettier(dir, ["--cache", "."]); + await expect(fs.stat(defaultCacheFile)).resolves.not.toThrowError(); + }); + + it("does'nt format when cache is available", async () => { + const { stdout: firstStdout } = await runPrettier(dir, [ + "--cache", + "--write", + ".", + ]); + expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + + const { stdout: secondStdout } = await runPrettier(dir, [ + "--cache", + "--write", + ".", + ]); + expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms \(cached\)$/), + expect.stringMatching(/^b\.js .+ms \(cached\)$/), + ]) + ); + }); + + it("re-formats when a file has been updated.", async () => { + const { stdout: firstStdout } = await runPrettier(dir, [ + "--cache", + "--write", + ".", + ]); + expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + + // Update `a.js` + await fs.writeFile(path.join(dir, "a.js"), "const a = `a`;"); + + const { stdout: secondStdout } = await runPrettier(dir, [ + "--cache", + "--write", + ".", + ]); + expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( + // the cache of `b.js` is only available. + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms \(cached\)$/), + ]) + ); + }); + + it("re-formats when options has been updated.", async () => { + const { stdout: firstStdout } = await runPrettier(dir, [ + "--cache", + "--write", + ".", + ]); + expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + + const { stdout: secondStdout } = await runPrettier(dir, [ + "--cache", + "--write", + "--trailing-comma", + "all", + ".", + ]); + expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + }); + + it("removes cache file when run Prettier without `--cache` option", async () => { + await runPrettier(dir, ["--cache", "--write", "."]); + await expect(fs.stat(defaultCacheFile)).resolves.not.toThrowError(); + await runPrettier(dir, ["--write", "."]); + await expect(fs.stat(defaultCacheFile)).rejects.toThrowError(); + }); + }); }); From a5c265699424bfb8afcad2430753761c2eb1b0ac Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Fri, 27 May 2022 18:39:13 +0900 Subject: [PATCH 29/53] Add tests for timestamp --- tests/integration/__tests__/cache.js | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index 35932595da49..8b89888e64bf 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -110,6 +110,37 @@ describe("--cache option", () => { ); }); + it("re-formats when timestamp has been updated", async () => { + const { stdout: firstStdout } = await runPrettier(dir, [ + "--cache", + "--write", + ".", + ]); + expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + + // update timestamp + const time = new Date(); + await fs.utimes(path.join(dir, "a.js"), time, time); + + const { stdout: secondStdout } = await runPrettier(dir, [ + "--cache", + "--write", + ".", + ]); + expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( + // the cache of `b.js` is only available. + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms \(cached\)$/), + ]) + ); + }); + it("re-formats when options has been updated.", async () => { const { stdout: firstStdout } = await runPrettier(dir, [ "--cache", From 80f2d91100fcab2dacafca5ba48dadab79c8dbce Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Fri, 27 May 2022 18:42:32 +0900 Subject: [PATCH 30/53] Add tests for `--cache-strategy` --- tests/integration/__tests__/cache.js | 148 +++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index 8b89888e64bf..799c0489e049 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -176,4 +176,152 @@ describe("--cache option", () => { await expect(fs.stat(defaultCacheFile)).rejects.toThrowError(); }); }); + + describe("--cache-strategy content", () => { + it("creates default cache file named `node_modules/.cache/prettier/.prettier-cache`", async () => { + await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty( + "code", + "ENOENT" + ); + await runPrettier(dir, ["--cache", "--cache-strategy", "content", "."]); + await expect(fs.stat(defaultCacheFile)).resolves.not.toThrowError(); + }); + + it("does'nt format when cache is available", async () => { + const { stdout: firstStdout } = await runPrettier(dir, [ + "--cache", + "--cache-strategy", + "content", + "--write", + ".", + ]); + expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + + const { stdout: secondStdout } = await runPrettier(dir, [ + "--cache", + "--cache-strategy", + "content", + "--write", + ".", + ]); + expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms \(cached\)$/), + expect.stringMatching(/^b\.js .+ms \(cached\)$/), + ]) + ); + }); + + it("re-formats when a file has been updated.", async () => { + const { stdout: firstStdout } = await runPrettier(dir, [ + "--cache", + "--cache-strategy", + "content", + "--write", + ".", + ]); + expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + + // Update `a.js` + await fs.writeFile(path.join(dir, "a.js"), "const a = `a`;"); + + const { stdout: secondStdout } = await runPrettier(dir, [ + "--cache", + "--cache-strategy", + "content", + "--write", + ".", + ]); + expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( + // the cache of `b.js` is only available. + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms \(cached\)$/), + ]) + ); + }); + + it("does'nt re-format when timestamp has been updated", async () => { + const { stdout: firstStdout } = await runPrettier(dir, [ + "--cache", + "--cache-strategy", + "content", + "--write", + ".", + ]); + expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + + // update timestamp + const time = new Date(); + await fs.utimes(path.join(dir, "a.js"), time, time); + + const { stdout: secondStdout } = await runPrettier(dir, [ + "--cache", + "--cache-strategy", + "content", + "--write", + ".", + ]); + expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms \(cached\)$/), + expect.stringMatching(/^b\.js .+ms \(cached\)$/), + ]) + ); + }); + + it("re-formats when options has been updated.", async () => { + const { stdout: firstStdout } = await runPrettier(dir, [ + "--cache", + "--cache-strategy", + "content", + "--write", + ".", + ]); + expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + + const { stdout: secondStdout } = await runPrettier(dir, [ + "--cache", + "--write", + "--cache-strategy", + "content", + "--trailing-comma", + "all", + ".", + ]); + expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( + expect.arrayContaining([ + expect.stringMatching(/^a\.js .+ms$/), + expect.stringMatching(/^b\.js .+ms$/), + ]) + ); + }); + + it("removes cache file when run Prettier without `--cache` option", async () => { + await runPrettier(dir, ["--cache", "--write", "."]); + await expect(fs.stat(defaultCacheFile)).resolves.not.toThrowError(); + await runPrettier(dir, ["--write", "."]); + await expect(fs.stat(defaultCacheFile)).rejects.toThrowError(); + }); + }); }); From 2a8aa7cc621bbaa6b5f1c4fc4707088fd18ff7e9 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Fri, 27 May 2022 18:52:06 +0900 Subject: [PATCH 31/53] Add docs for cache-strategy --- changelog_unreleased/cli/12800.md | 10 +++++++++- docs/cli.md | 8 ++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/changelog_unreleased/cli/12800.md b/changelog_unreleased/cli/12800.md index 13f4aaba6df9..37e1587d7982 100644 --- a/changelog_unreleased/cli/12800.md +++ b/changelog_unreleased/cli/12800.md @@ -1,4 +1,4 @@ -#### Add new `--cache` (#12800 by @sosukesuzuki) +#### Add `--cache` CLI option (#12800 by @sosukesuzuki) Two new CLI options have been added for a caching system similar to [ESLint's one](https://eslint.org/docs/user-guide/command-line-interface#caching). Please see the [documentation](https://prettier.io/docs/en/cli.html#--cache) for details. @@ -9,3 +9,11 @@ When you want to format only changed files, you can run Prettier with `--cache` ```bash prettier --write --cache src ``` + +##### `--cache-strategy` + +Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. If no strategy is specified, metadata will be used. + +```bash +prettier --write --cache --cache-strategy content src +``` diff --git a/docs/cli.md b/docs/cli.md index 612b9dc7f920..f76838aee9d4 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -212,3 +212,11 @@ When you want to format only changed files, you can run Prettier with `--cache` ```bash prettier --write --cache src ``` + +## `--cache-strategy` + +Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. If no strategy is specified, metadata will be used. + +```bash +prettier --write --cache --cache-strategy content src +``` From 882213b9a25b77e302dcaab8709a92b20004faf1 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 28 May 2022 04:50:37 +0900 Subject: [PATCH 32/53] Address review --- src/cli/format.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/cli/format.js b/src/cli/format.js index 23a1310d032e..9cebec7cf8a9 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -384,7 +384,7 @@ async function formatFiles(context) { const start = Date.now(); - const existsCache = formatResultsCache?.existsAvailableFormatResultsCache( + const isCacheExists = formatResultsCache?.existsAvailableFormatResultsCache( filename, options ); @@ -393,7 +393,7 @@ async function formatFiles(context) { let output; try { - if (existsCache) { + if (isCacheExists) { result = { formatted: input }; } else { result = format(context, input, options); @@ -442,7 +442,7 @@ async function formatFiles(context) { } } else if (!context.argv.check && !context.argv.listDifferent) { const message = `${chalk.grey(filename)} ${Date.now() - start}ms`; - if (existsCache) { + if (isCacheExists) { context.logger.log(`${message} (cached)`); } else { context.logger.log(message); @@ -469,9 +469,7 @@ async function formatFiles(context) { } } - if (formatResultsCache) { - formatResultsCache.reconcile(); - } + formatResultsCache?.reconcile(); // Print check summary based on expected exit code if (context.argv.check) { From c6d72874ca12494ac8d0fb0a72209a6bb729cb70 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 28 May 2022 04:52:38 +0900 Subject: [PATCH 33/53] Fix `findCacheFile` --- src/cli/find-cache-file.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/cli/find-cache-file.js b/src/cli/find-cache-file.js index 5ef02279ab87..c8960ead4788 100644 --- a/src/cli/find-cache-file.js +++ b/src/cli/find-cache-file.js @@ -6,19 +6,14 @@ const findCacheDir = require("find-cache-dir"); /** * Find default cache file (`./node_modules/.cache/prettier/.prettier-cache`) using https://github.com/avajs/find-cache-dir + * + * @returns {string} */ -function findDefaultCacheFile() { +function findCacheFile() { const cacheDir = findCacheDir({ name: "prettier", create: true }) || os.tmpdir(); const cacheFilePath = path.join(cacheDir, ".prettier-cache"); return cacheFilePath; } -/** - * @returns {string} - */ -function findCacheFile() { - return findDefaultCacheFile(); -} - module.exports = findCacheFile; From 5f9f9f6ae5b90cd85522c41371d226c07fb90e7c Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 28 May 2022 04:59:32 +0900 Subject: [PATCH 34/53] Use Set.prototype.has --- src/cli/format-results-cache.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cli/format-results-cache.js b/src/cli/format-results-cache.js index f81ae1eec2e4..4c85c7a77dcb 100644 --- a/src/cli/format-results-cache.js +++ b/src/cli/format-results-cache.js @@ -39,6 +39,7 @@ function getMetadataFromFileDescriptor(fileDescriptor) { } const cacheStrategies = ["metadata", "content"]; +const cacheStrategiesSet = new Set(cacheStrategies); /** * @typedef {"metadata" | "content"} CacheStrategy @@ -47,7 +48,7 @@ const cacheStrategies = ["metadata", "content"]; * @returns {value is CacheStrategy} */ function isValidCacheStrategy(value) { - return cacheStrategies.includes(value); + return cacheStrategiesSet.has(value); } class FormatResultsCache { From cdda1e6d5f429550142921924dbb032ef7c14336 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 28 May 2022 05:19:16 +0900 Subject: [PATCH 35/53] Throw error with --stdin-filepath --- src/cli/constant.js | 2 +- src/cli/index.js | 4 +++- tests/integration/__tests__/cache.js | 9 +++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/cli/constant.js b/src/cli/constant.js index 6c7373f53083..13474b2fc60b 100644 --- a/src/cli/constant.js +++ b/src/cli/constant.js @@ -73,7 +73,7 @@ const categoryOrder = [ const options = { cache: { default: false, - description: "Only format changed files", + description: "Only format changed files. Cannot use with --stdin-filepath.", type: "boolean", }, "cache-strategy": { diff --git a/src/cli/index.js b/src/cli/index.js index 1c3ea9810ecf..60dea9d28096 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -96,7 +96,9 @@ async function main(context) { const hasFilePatterns = context.filePatterns.length > 0; const useStdin = - !hasFilePatterns && (!process.stdin.isTTY || context.argv.filePath); + !hasFilePatterns && + (!process.stdin.isTTY || context.argv.filePath) && + !context.argv.cache; if (context.argv.findConfigPath) { await logResolvedConfigPathOrDie(context); diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index 799c0489e049..8b1a467b57a1 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -44,6 +44,15 @@ describe("--cache option", () => { ); }); + it("throws error when use with --stdin-filepath", async () => { + const { stderr } = await runPrettier( + dir, + ["--cache", "--stdin-filepath", "foo.js"], + { input: "const a = a;" } + ); + expect(typeof stderr).toBe("string"); + }); + describe("--cache-strategy metadata", () => { it("creates default cache file named `node_modules/.cache/prettier/.prettier-cache`", async () => { await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty( From 76efb7b216fed78f7a6d5a73a52bb94e9e1dc2d6 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 28 May 2022 06:24:05 +0900 Subject: [PATCH 36/53] Update snapshots --- tests/integration/__tests__/__snapshots__/early-exit.js.snap | 4 ++-- .../integration/__tests__/__snapshots__/help-options.js.snap | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration/__tests__/__snapshots__/early-exit.js.snap b/tests/integration/__tests__/__snapshots__/early-exit.js.snap index b680ff68c4e8..5fc0a518af28 100644 --- a/tests/integration/__tests__/__snapshots__/early-exit.js.snap +++ b/tests/integration/__tests__/__snapshots__/early-exit.js.snap @@ -139,7 +139,7 @@ Editor options: Other options: - --cache Only format changed files + --cache Only format changed files. Cannot use with --stdin-filepath. Defaults to false. --no-color Do not colorize error messages. --no-error-on-unmatched-pattern @@ -311,7 +311,7 @@ Editor options: Other options: - --cache Only format changed files + --cache Only format changed files. Cannot use with --stdin-filepath. Defaults to false. --no-color Do not colorize error messages. --no-error-on-unmatched-pattern diff --git a/tests/integration/__tests__/__snapshots__/help-options.js.snap b/tests/integration/__tests__/__snapshots__/help-options.js.snap index 9c46a4479eb7..0a6c0074b077 100644 --- a/tests/integration/__tests__/__snapshots__/help-options.js.snap +++ b/tests/integration/__tests__/__snapshots__/help-options.js.snap @@ -49,7 +49,7 @@ exports[`show detailed usage with --help cache (stderr) 1`] = `""`; exports[`show detailed usage with --help cache (stdout) 1`] = ` "--cache - Only format changed files + Only format changed files. Cannot use with --stdin-filepath. Default: false " From 3fed1011bed204715e495a85e28a3dc4c316f272 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 28 May 2022 23:12:30 +0900 Subject: [PATCH 37/53] Fix error for cache and stdin --- src/cli/index.js | 8 +++++--- tests/integration/__tests__/cache.js | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/cli/index.js b/src/cli/index.js index 60dea9d28096..6cb0a64d5aaa 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -96,15 +96,17 @@ async function main(context) { const hasFilePatterns = context.filePatterns.length > 0; const useStdin = - !hasFilePatterns && - (!process.stdin.isTTY || context.argv.filePath) && - !context.argv.cache; + !hasFilePatterns && (!process.stdin.isTTY || context.argv.filePath); if (context.argv.findConfigPath) { await logResolvedConfigPathOrDie(context); } else if (context.argv.fileInfo) { await logFileInfoOrDie(context); } else if (useStdin) { + if (context.argv.cache) { + context.logger.error("`--cache` cannot be used with stdin."); + process.exit(2); + } await formatStdin(context); } else if (hasFilePatterns) { await formatFiles(context); diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index 8b1a467b57a1..803b1c050b0c 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -50,7 +50,9 @@ describe("--cache option", () => { ["--cache", "--stdin-filepath", "foo.js"], { input: "const a = a;" } ); - expect(typeof stderr).toBe("string"); + expect(stripAnsi(stderr.trim())).toBe( + "[error] `--cache` cannot be used with stdin." + ); }); describe("--cache-strategy metadata", () => { From fab9aa69e0468d39c9d38c035b44ec8d9c899fd9 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 28 May 2022 23:15:00 +0900 Subject: [PATCH 38/53] Use string constructor instead of toString --- src/cli/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli/utils.js b/src/cli/utils.js index 9eb93b3c06a3..a846c185cfde 100644 --- a/src/cli/utils.js +++ b/src/cli/utils.js @@ -48,7 +48,7 @@ function pick(object, keys) { * @returns {string} */ function createHash(source) { - return sdbm(source).toString(); + return String(sdbm(source)); } /** From ead522e19a33414b3e703025e01668c71f83d66c Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 28 May 2022 23:16:08 +0900 Subject: [PATCH 39/53] Update changelog --- changelog_unreleased/cli/12800.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/changelog_unreleased/cli/12800.md b/changelog_unreleased/cli/12800.md index 37e1587d7982..7b90057e0d30 100644 --- a/changelog_unreleased/cli/12800.md +++ b/changelog_unreleased/cli/12800.md @@ -1,6 +1,6 @@ -#### Add `--cache` CLI option (#12800 by @sosukesuzuki) +#### Add `--cache` and `--cache-strategy` CLI option (#12800 by @sosukesuzuki) -Two new CLI options have been added for a caching system similar to [ESLint's one](https://eslint.org/docs/user-guide/command-line-interface#caching). Please see the [documentation](https://prettier.io/docs/en/cli.html#--cache) for details. +Two new CLI options have been added for a caching system similar to [ESLint's one](https://eslint.org/docs/user-guide/command-line-interface#caching). ##### `--cache` From 307033e74b90ab855166a1e9d1b79ee628e42ba2 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Tue, 31 May 2022 03:26:16 +0900 Subject: [PATCH 40/53] Use flag validation --- src/cli/constant.js | 12 +++++++++++- src/cli/format-results-cache.js | 20 -------------------- src/cli/format.js | 13 ++++--------- tests/integration/__tests__/cache.js | 2 +- 4 files changed, 16 insertions(+), 31 deletions(-) diff --git a/src/cli/constant.js b/src/cli/constant.js index 13474b2fc60b..d13e92ce341d 100644 --- a/src/cli/constant.js +++ b/src/cli/constant.js @@ -77,10 +77,20 @@ const options = { type: "boolean", }, "cache-strategy": { + choices: [ + { + description: "Use the file metadata such as timestamps as cache keys", + value: "metadata", + }, + { + description: "Use the file content as cache keys", + value: "content", + }, + ], default: "metadata", describe: "Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. ", - type: "string", + type: "choice", }, check: { alias: "c", diff --git a/src/cli/format-results-cache.js b/src/cli/format-results-cache.js index 4c85c7a77dcb..3dfb79d16535 100644 --- a/src/cli/format-results-cache.js +++ b/src/cli/format-results-cache.js @@ -38,32 +38,12 @@ function getMetadataFromFileDescriptor(fileDescriptor) { return fileDescriptor.meta; } -const cacheStrategies = ["metadata", "content"]; -const cacheStrategiesSet = new Set(cacheStrategies); - -/** - * @typedef {"metadata" | "content"} CacheStrategy - * - * @param {string} value - * @returns {value is CacheStrategy} - */ -function isValidCacheStrategy(value) { - return cacheStrategiesSet.has(value); -} - class FormatResultsCache { /** * @param {string} cacheFileLocation The path of cache file location. (default: `node_modules/.cache/prettier/prettier-cache`) * @param {string} cacheStrategy */ constructor(cacheFileLocation, cacheStrategy) { - if (!isValidCacheStrategy(cacheStrategy)) { - const errorMessage = `Cache strategy must be one of: ${cacheStrategies - .map((strategy) => `"${strategy}"`) - .join(", ")}`; - throw new Error(errorMessage); - } - const useChecksum = cacheStrategy === "content"; this.cacheFileLocation = cacheFileLocation; diff --git a/src/cli/format.js b/src/cli/format.js index 9cebec7cf8a9..4f8eee1f55db 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -301,15 +301,10 @@ async function formatFiles(context) { let formatResultsCache; const cacheFilePath = findCacheFile(); if (context.argv.cache) { - try { - formatResultsCache = new FormatResultsCache( - cacheFilePath, - context.argv.cacheStrategy - ); - } catch (error) { - context.logger.error(error.message); - process.exit(2); - } + formatResultsCache = new FormatResultsCache( + cacheFilePath, + context.argv.cacheStrategy + ); } else { const stat = await statSafe(cacheFilePath); if (stat) { diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index 803b1c050b0c..c34b7169be47 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -40,7 +40,7 @@ describe("--cache option", () => { ".", ]); expect(stripAnsi(stderr.trim())).toBe( - '[error] Cache strategy must be one of: "metadata", "content"' + '[error] Invalid --cache-strategy value. Expected "content" or "metadata", but received "invalid".' ); }); From fffbd4ef0b8d561672d835694dbab3facffb8c6a Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Tue, 31 May 2022 03:33:29 +0900 Subject: [PATCH 41/53] Fix `cache-strategy` definition --- src/cli/constant.js | 3 +-- .../__tests__/__snapshots__/early-exit.js.snap | 6 ++++++ .../__snapshots__/help-options.js.snap | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/cli/constant.js b/src/cli/constant.js index d13e92ce341d..21ee34d35dbc 100644 --- a/src/cli/constant.js +++ b/src/cli/constant.js @@ -88,8 +88,7 @@ const options = { }, ], default: "metadata", - describe: - "Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. ", + description: "Strategy for the cache to use for detecting changed files.", type: "choice", }, check: { diff --git a/tests/integration/__tests__/__snapshots__/early-exit.js.snap b/tests/integration/__tests__/__snapshots__/early-exit.js.snap index 5fc0a518af28..1fa8e6fd7bb2 100644 --- a/tests/integration/__tests__/__snapshots__/early-exit.js.snap +++ b/tests/integration/__tests__/__snapshots__/early-exit.js.snap @@ -141,6 +141,9 @@ Other options: --cache Only format changed files. Cannot use with --stdin-filepath. Defaults to false. + --cache-strategy + Strategy for the cache to use for detecting changed files. + Defaults to metadata. --no-color Do not colorize error messages. --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched. @@ -313,6 +316,9 @@ Other options: --cache Only format changed files. Cannot use with --stdin-filepath. Defaults to false. + --cache-strategy + Strategy for the cache to use for detecting changed files. + Defaults to metadata. --no-color Do not colorize error messages. --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched. diff --git a/tests/integration/__tests__/__snapshots__/help-options.js.snap b/tests/integration/__tests__/__snapshots__/help-options.js.snap index 0a6c0074b077..490c8ec5e211 100644 --- a/tests/integration/__tests__/__snapshots__/help-options.js.snap +++ b/tests/integration/__tests__/__snapshots__/help-options.js.snap @@ -57,6 +57,24 @@ Default: false exports[`show detailed usage with --help cache (write) 1`] = `[]`; +exports[`show detailed usage with --help cache-strategy (stderr) 1`] = `""`; + +exports[`show detailed usage with --help cache-strategy (stdout) 1`] = ` +"--cache-strategy + + Strategy for the cache to use for detecting changed files. + +Valid options: + + metadata Use the file metadata such as timestamps as cache keys + content Use the file content as cache keys + +Default: metadata +" +`; + +exports[`show detailed usage with --help cache-strategy (write) 1`] = `[]`; + exports[`show detailed usage with --help check (stderr) 1`] = `""`; exports[`show detailed usage with --help check (stdout) 1`] = ` From 34a6712b974b195a534efbe5ba203a2ca7d4e5cf Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 4 Jun 2022 21:23:02 +0900 Subject: [PATCH 42/53] Update docs --- changelog_unreleased/cli/12800.md | 9 +++++++-- docs/cli.md | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/changelog_unreleased/cli/12800.md b/changelog_unreleased/cli/12800.md index 7b90057e0d30..69f75dec7f25 100644 --- a/changelog_unreleased/cli/12800.md +++ b/changelog_unreleased/cli/12800.md @@ -4,7 +4,12 @@ Two new CLI options have been added for a caching system similar to [ESLint's on ##### `--cache` -When you want to format only changed files, you can run Prettier with `--cache` flag. Enabling this flag can dramatically improve running time. +If this option is enabled, the following values are used as cache keys and the file is formatted only if one of them is changed. + +- Options +- Node.js version +- (if `--cache-strategy` is `metadata`) file metadata, such as timestamps +- (if `--cache-strategy` is `content`) content of the file ```bash prettier --write --cache src @@ -12,7 +17,7 @@ prettier --write --cache src ##### `--cache-strategy` -Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. If no strategy is specified, metadata will be used. +Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. If no strategy is specified, `metadata` will be used. ```bash prettier --write --cache --cache-strategy content src diff --git a/docs/cli.md b/docs/cli.md index f76838aee9d4..f64e6da6af6a 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -207,7 +207,12 @@ Disable plugin autoloading. ## `--cache` -When you want to format only changed files, you can run Prettier with `--cache` flag. Enabling this flag can dramatically improve running time. +If this option is enabled, the following values are used as cache keys and the file is formatted only if one of them is changed. + +- Options +- Node.js version +- (if `--cache-strategy` is `metadata`) file metadata, such as timestamps +- (if `--cache-strategy` is `content`) content of the file ```bash prettier --write --cache src @@ -215,7 +220,7 @@ prettier --write --cache src ## `--cache-strategy` -Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. If no strategy is specified, metadata will be used. +Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. If no strategy is specified, `metadata` will be used. ```bash prettier --write --cache --cache-strategy content src From c383f96ab35380aab53614b4394e36ca1e777676 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 4 Jun 2022 21:30:00 +0900 Subject: [PATCH 43/53] Mark highlihgt --- changelog_unreleased/cli/12800.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog_unreleased/cli/12800.md b/changelog_unreleased/cli/12800.md index 69f75dec7f25..1b45fb4ab780 100644 --- a/changelog_unreleased/cli/12800.md +++ b/changelog_unreleased/cli/12800.md @@ -1,4 +1,4 @@ -#### Add `--cache` and `--cache-strategy` CLI option (#12800 by @sosukesuzuki) +#### [HIGILIGHT]Add `--cache` and `--cache-strategy` CLI option (#12800 by @sosukesuzuki) Two new CLI options have been added for a caching system similar to [ESLint's one](https://eslint.org/docs/user-guide/command-line-interface#caching). From 46ec439c8676d9e8e8ba65a7c3d07d83e8a78a32 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 4 Jun 2022 23:00:43 +0900 Subject: [PATCH 44/53] Throw error for `--cache-strategy` without `--cache` --- src/cli/constant.js | 1 - src/cli/format.js | 8 +++++++- tests/integration/__tests__/cache.js | 13 +++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/cli/constant.js b/src/cli/constant.js index 21ee34d35dbc..c7cc8958dcd4 100644 --- a/src/cli/constant.js +++ b/src/cli/constant.js @@ -87,7 +87,6 @@ const options = { value: "content", }, ], - default: "metadata", description: "Strategy for the cache to use for detecting changed files.", type: "choice", }, diff --git a/src/cli/format.js b/src/cli/format.js index 4f8eee1f55db..4020e6423907 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -303,9 +303,15 @@ async function formatFiles(context) { if (context.argv.cache) { formatResultsCache = new FormatResultsCache( cacheFilePath, - context.argv.cacheStrategy + context.argv.cacheStrategy || "metadata" ); } else { + if (context.argv.cacheStrategy) { + context.logger.error( + "`--cache-strategy` is cannot be used without `--cache`." + ); + process.exit(2); + } const stat = await statSafe(cacheFilePath); if (stat) { await fs.unlink(cacheFilePath); diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index c34b7169be47..9f437b4a5fc0 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -55,6 +55,19 @@ describe("--cache option", () => { ); }); + it("throws error when use `--cache-strategy` without `--cache`.", async () => { + const { stderr } = await runPrettier( + dir, + ["foo.js", "--cache-strategy", "content"], + { + input: "const a = a;", + } + ); + expect(stripAnsi(stderr.trim())).toBe( + "[error] `--cache-strategy` is cannot be used without `--cache`." + ); + }); + describe("--cache-strategy metadata", () => { it("creates default cache file named `node_modules/.cache/prettier/.prettier-cache`", async () => { await expect(fs.stat(defaultCacheFile)).rejects.toHaveProperty( From a47884f4229ca74131b3c610fc2a4d3c59fa3cba Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 4 Jun 2022 23:14:45 +0900 Subject: [PATCH 45/53] Update docs --- docs/cli.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/cli.md b/docs/cli.md index f64e6da6af6a..9eac57d150ab 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -209,6 +209,7 @@ Disable plugin autoloading. If this option is enabled, the following values are used as cache keys and the file is formatted only if one of them is changed. +- Prettier version - Options - Node.js version - (if `--cache-strategy` is `metadata`) file metadata, such as timestamps @@ -218,6 +219,16 @@ If this option is enabled, the following values are used as cache keys and the f prettier --write --cache src ``` +Running Prettier without `--cache` will delete the cache. + +Also, since the cache file is stored in `./node_modules/.cache/prettier/.prettier-cache`, so you can use `rm -f . /node_modules/.cache/prettier/.prettier-cache` to remove them manually. + +:::caution + +Plugins version and implementation are not used as cache keys. We recommend that you delete the cache when updating plugins. + +::: + ## `--cache-strategy` Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. If no strategy is specified, `metadata` will be used. From aec547c54282cda817185548bf2fc1c4a88b9b08 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 4 Jun 2022 23:21:56 +0900 Subject: [PATCH 46/53] Fix typo --- changelog_unreleased/cli/12800.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog_unreleased/cli/12800.md b/changelog_unreleased/cli/12800.md index 1b45fb4ab780..0b3dfa7083aa 100644 --- a/changelog_unreleased/cli/12800.md +++ b/changelog_unreleased/cli/12800.md @@ -1,4 +1,4 @@ -#### [HIGILIGHT]Add `--cache` and `--cache-strategy` CLI option (#12800 by @sosukesuzuki) +#### [HIGHLIGHT]Add `--cache` and `--cache-strategy` CLI option (#12800 by @sosukesuzuki) Two new CLI options have been added for a caching system similar to [ESLint's one](https://eslint.org/docs/user-guide/command-line-interface#caching). From a66993b1c0a774d1fda68403d6a9cf5cea01e154 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 4 Jun 2022 23:29:22 +0900 Subject: [PATCH 47/53] Updates snapshots --- tests/integration/__tests__/__snapshots__/early-exit.js.snap | 2 -- tests/integration/__tests__/__snapshots__/help-options.js.snap | 2 -- 2 files changed, 4 deletions(-) diff --git a/tests/integration/__tests__/__snapshots__/early-exit.js.snap b/tests/integration/__tests__/__snapshots__/early-exit.js.snap index 1fa8e6fd7bb2..63f00b025ee6 100644 --- a/tests/integration/__tests__/__snapshots__/early-exit.js.snap +++ b/tests/integration/__tests__/__snapshots__/early-exit.js.snap @@ -143,7 +143,6 @@ Other options: Defaults to false. --cache-strategy Strategy for the cache to use for detecting changed files. - Defaults to metadata. --no-color Do not colorize error messages. --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched. @@ -318,7 +317,6 @@ Other options: Defaults to false. --cache-strategy Strategy for the cache to use for detecting changed files. - Defaults to metadata. --no-color Do not colorize error messages. --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched. diff --git a/tests/integration/__tests__/__snapshots__/help-options.js.snap b/tests/integration/__tests__/__snapshots__/help-options.js.snap index 490c8ec5e211..009f516d3495 100644 --- a/tests/integration/__tests__/__snapshots__/help-options.js.snap +++ b/tests/integration/__tests__/__snapshots__/help-options.js.snap @@ -68,8 +68,6 @@ Valid options: metadata Use the file metadata such as timestamps as cache keys content Use the file content as cache keys - -Default: metadata " `; From 5e7bd09ca4bf08f77b6362fe84585773ad2162e2 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sun, 5 Jun 2022 02:43:56 +0900 Subject: [PATCH 48/53] Update docs/cli.md Co-authored-by: Simon Lydell --- docs/cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli.md b/docs/cli.md index 9eac57d150ab..7336a47dd27a 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -221,7 +221,7 @@ prettier --write --cache src Running Prettier without `--cache` will delete the cache. -Also, since the cache file is stored in `./node_modules/.cache/prettier/.prettier-cache`, so you can use `rm -f . /node_modules/.cache/prettier/.prettier-cache` to remove them manually. +Also, since the cache file is stored in `./node_modules/.cache/prettier/.prettier-cache`, so you can use `rm ./node_modules/.cache/prettier/.prettier-cache` to remove it manually. :::caution From f93fa5561598a783547f035b698693902edf6abf Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sun, 5 Jun 2022 21:15:14 +0900 Subject: [PATCH 49/53] Fix error message --- src/cli/format.js | 2 +- tests/integration/__tests__/cache.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cli/format.js b/src/cli/format.js index 4020e6423907..f842ef583fe0 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -308,7 +308,7 @@ async function formatFiles(context) { } else { if (context.argv.cacheStrategy) { context.logger.error( - "`--cache-strategy` is cannot be used without `--cache`." + "`--cache-strategy` cannot be used without `--cache`." ); process.exit(2); } diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index 9f437b4a5fc0..6da77fcc4fb3 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -64,7 +64,7 @@ describe("--cache option", () => { } ); expect(stripAnsi(stderr.trim())).toBe( - "[error] `--cache-strategy` is cannot be used without `--cache`." + "[error] `--cache-strategy` cannot be used without `--cache`." ); }); From 6a9c07bbea6c178f4a0ee8a502a7ce4558977b9a Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sun, 5 Jun 2022 21:15:33 +0900 Subject: [PATCH 50/53] Remove `:::` syntax --- docs/cli.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index 7336a47dd27a..094801ccaf1a 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -223,11 +223,7 @@ Running Prettier without `--cache` will delete the cache. Also, since the cache file is stored in `./node_modules/.cache/prettier/.prettier-cache`, so you can use `rm ./node_modules/.cache/prettier/.prettier-cache` to remove it manually. -:::caution - -Plugins version and implementation are not used as cache keys. We recommend that you delete the cache when updating plugins. - -::: +> Plugins version and implementation are not used as cache keys. We recommend that you delete the cache when updating plugins. ## `--cache-strategy` From b2fb4bbd191a290775c1ee733bfaaace20dea9bb Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 11 Jun 2022 22:24:37 +0900 Subject: [PATCH 51/53] Defaults `content` --- changelog_unreleased/cli/12800.md | 7 ++++--- docs/cli.md | 4 ++-- src/cli/format.js | 2 +- tests/integration/__tests__/cache.js | 26 ++++++++++++++++++++++++-- 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/changelog_unreleased/cli/12800.md b/changelog_unreleased/cli/12800.md index 0b3dfa7083aa..a154ef1177ef 100644 --- a/changelog_unreleased/cli/12800.md +++ b/changelog_unreleased/cli/12800.md @@ -6,10 +6,11 @@ Two new CLI options have been added for a caching system similar to [ESLint's on If this option is enabled, the following values are used as cache keys and the file is formatted only if one of them is changed. +- Prettier version - Options - Node.js version -- (if `--cache-strategy` is `metadata`) file metadata, such as timestamps - (if `--cache-strategy` is `content`) content of the file +- (if `--cache-strategy` is `metadata`) file metadata, such as timestamps ```bash prettier --write --cache src @@ -17,8 +18,8 @@ prettier --write --cache src ##### `--cache-strategy` -Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. If no strategy is specified, `metadata` will be used. +Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. If no strategy is specified, `content` will be used. ```bash -prettier --write --cache --cache-strategy content src +prettier --write --cache --cache-strategy metadata src ``` diff --git a/docs/cli.md b/docs/cli.md index 094801ccaf1a..be5d2dba3c18 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -227,8 +227,8 @@ Also, since the cache file is stored in `./node_modules/.cache/prettier/.prettie ## `--cache-strategy` -Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. If no strategy is specified, `metadata` will be used. +Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. If no strategy is specified, `content` will be used. ```bash -prettier --write --cache --cache-strategy content src +prettier --write --cache --cache-strategy metadata src ``` diff --git a/src/cli/format.js b/src/cli/format.js index f842ef583fe0..8f5e4867f545 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -303,7 +303,7 @@ async function formatFiles(context) { if (context.argv.cache) { formatResultsCache = new FormatResultsCache( cacheFilePath, - context.argv.cacheStrategy || "metadata" + context.argv.cacheStrategy || "content" ); } else { if (context.argv.cacheStrategy) { diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js index 6da77fcc4fb3..863cd29017d9 100644 --- a/tests/integration/__tests__/cache.js +++ b/tests/integration/__tests__/cache.js @@ -74,7 +74,7 @@ describe("--cache option", () => { "code", "ENOENT" ); - await runPrettier(dir, ["--cache", "."]); + await runPrettier(dir, ["--cache", "--cache-strategy", "metadata", "."]); await expect(fs.stat(defaultCacheFile)).resolves.not.toThrowError(); }); @@ -82,6 +82,8 @@ describe("--cache option", () => { const { stdout: firstStdout } = await runPrettier(dir, [ "--cache", "--write", + "--cache-strategy", + "metadata", ".", ]); expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( @@ -94,6 +96,8 @@ describe("--cache option", () => { const { stdout: secondStdout } = await runPrettier(dir, [ "--cache", "--write", + "--cache-strategy", + "metadata", ".", ]); expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( @@ -108,6 +112,8 @@ describe("--cache option", () => { const { stdout: firstStdout } = await runPrettier(dir, [ "--cache", "--write", + "--cache-strategy", + "metadata", ".", ]); expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( @@ -123,6 +129,8 @@ describe("--cache option", () => { const { stdout: secondStdout } = await runPrettier(dir, [ "--cache", "--write", + "--cache-strategy", + "metadata", ".", ]); expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( @@ -138,6 +146,8 @@ describe("--cache option", () => { const { stdout: firstStdout } = await runPrettier(dir, [ "--cache", "--write", + "--cache-strategy", + "metadata", ".", ]); expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( @@ -154,6 +164,8 @@ describe("--cache option", () => { const { stdout: secondStdout } = await runPrettier(dir, [ "--cache", "--write", + "--cache-strategy", + "metadata", ".", ]); expect(stripAnsi(secondStdout).split("\n").filter(Boolean)).toEqual( @@ -169,6 +181,8 @@ describe("--cache option", () => { const { stdout: firstStdout } = await runPrettier(dir, [ "--cache", "--write", + "--cache-strategy", + "metadata", ".", ]); expect(stripAnsi(firstStdout).split("\n").filter(Boolean)).toEqual( @@ -180,6 +194,8 @@ describe("--cache option", () => { const { stdout: secondStdout } = await runPrettier(dir, [ "--cache", + "--cache-strategy", + "metadata", "--write", "--trailing-comma", "all", @@ -194,7 +210,13 @@ describe("--cache option", () => { }); it("removes cache file when run Prettier without `--cache` option", async () => { - await runPrettier(dir, ["--cache", "--write", "."]); + await runPrettier(dir, [ + "--cache", + "--write", + "--cache-strategy", + "metadata", + ".", + ]); await expect(fs.stat(defaultCacheFile)).resolves.not.toThrowError(); await runPrettier(dir, ["--write", "."]); await expect(fs.stat(defaultCacheFile)).rejects.toThrowError(); From 6646b2f7b6ac5200e674495931e1b802242ee6c5 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 11 Jun 2022 22:37:18 +0900 Subject: [PATCH 52/53] Update docs --- docs/cli.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/cli.md b/docs/cli.md index be5d2dba3c18..cada39ef61ae 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -227,7 +227,11 @@ Also, since the cache file is stored in `./node_modules/.cache/prettier/.prettie ## `--cache-strategy` -Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. If no strategy is specified, `content` will be used. +Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. + +In general, `metadata` is faster. However, `content` is useful for updating the timestamp without changing the file content. This can happen, for example, during git operations such as `git clone`, because it does not track file modification times. + +If no strategy is specified, `content` will be used. ```bash prettier --write --cache --cache-strategy metadata src From 197b568577c0646fa928e206db232d9fd788348f Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 11 Jun 2022 22:46:55 +0900 Subject: [PATCH 53/53] Fix by Prettier --- docs/cli.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli.md b/docs/cli.md index cada39ef61ae..af3afef5f9c1 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -227,7 +227,7 @@ Also, since the cache file is stored in `./node_modules/.cache/prettier/.prettie ## `--cache-strategy` -Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. +Strategy for the cache to use for detecting changed files. Can be either `metadata` or `content`. In general, `metadata` is faster. However, `content` is useful for updating the timestamp without changing the file content. This can happen, for example, during git operations such as `git clone`, because it does not track file modification times.