From aa1d536e29a55e4647452e87c2fa61c2b8c7d92d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 8 Jun 2022 00:24:11 +0800 Subject: [PATCH 01/29] Build(deps): Bump tmpl from 1.0.4 to 1.0.5 in /scripts/release (#12984) Bumps [tmpl](https://github.com/daaku/nodejs-tmpl) from 1.0.4 to 1.0.5. - [Release notes](https://github.com/daaku/nodejs-tmpl/releases) - [Commits](https://github.com/daaku/nodejs-tmpl/commits/v1.0.5) --- updated-dependencies: - dependency-name: tmpl dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- scripts/release/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/release/yarn.lock b/scripts/release/yarn.lock index c74d948b0..a1fc05b1c 100644 --- a/scripts/release/yarn.lock +++ b/scripts/release/yarn.lock @@ -2452,9 +2452,9 @@ throat@^6.0.1: integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w== tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz" - integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-fast-properties@^2.0.0: version "2.0.0" From 7f9a08762d1a6d4389bbe9f0e87eb47beddce50d Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 11 Jun 2022 00:06:22 +0900 Subject: [PATCH 02/29] Update dependency flow-parser to v0.180.0 (#12990) * Update `flow-parser` * Update snapshots --- package.json | 2 +- tests/format/js/regex/__snapshots__/jsfmt.spec.js.snap | 7 ------- tests/format/js/regex/jsfmt.spec.js | 2 +- .../misc/errors/flow/__snapshots__/jsfmt.spec.js.snap | 2 +- .../misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap | 2 +- .../consistent/__snapshots__/jsfmt.spec.js.snap | 2 +- yarn.lock | 8 ++++---- 7 files changed, 9 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index ec3ff7f23..c35b94b51 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "fast-glob": "3.2.11", "fast-json-stable-stringify": "2.1.0", "find-parent-dir": "0.3.1", - "flow-parser": "0.176.3", + "flow-parser": "0.180.0", "get-stdin": "8.0.0", "graphql": "15.6.1", "html-element-attributes": "3.1.0", diff --git a/tests/format/js/regex/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/regex/__snapshots__/jsfmt.spec.js.snap index aa295e37c..18d9ecdde 100644 --- a/tests/format/js/regex/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/js/regex/__snapshots__/jsfmt.spec.js.snap @@ -1,12 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`d-flag.js [flow] format 1`] = ` -"Invalid flags supplied to RegExp constructor 'd' (1:5) -> 1 | /./d; - | ^ - 2 |" -`; - exports[`d-flag.js [meriyah] format 1`] = ` "Unexpected regular expression flag (1:3) > 1 | /./d; diff --git a/tests/format/js/regex/jsfmt.spec.js b/tests/format/js/regex/jsfmt.spec.js index 1f132f341..857c89598 100644 --- a/tests/format/js/regex/jsfmt.spec.js +++ b/tests/format/js/regex/jsfmt.spec.js @@ -1,6 +1,6 @@ run_spec(__dirname, ["babel", "flow", "typescript"], { errors: { - flow: ["d-flag.js", "v-flag.js"], + flow: ["v-flag.js"], acorn: ["v-flag.js"], espree: ["v-flag.js"], meriyah: ["d-flag.js", "v-flag.js"], diff --git a/tests/format/misc/errors/flow/__snapshots__/jsfmt.spec.js.snap b/tests/format/misc/errors/flow/__snapshots__/jsfmt.spec.js.snap index 763008391..d968f3b33 100644 --- a/tests/format/misc/errors/flow/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/misc/errors/flow/__snapshots__/jsfmt.spec.js.snap @@ -9,7 +9,7 @@ exports[`async-arrow-invalid.js [flow] format 1`] = ` `; exports[`default-escaped.js [flow] format 1`] = ` -"Unexpected identifier, expected the token \`{\` (1:8) +"Unexpected token \`async\`, expected a declaration, statement or export specifiers (1:8) > 1 | export asyn\\u{63} from "async"; | ^^^^^^^^^^ 2 |" diff --git a/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap b/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap index 62bb784b9..b2c981a4b 100644 --- a/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap @@ -127,7 +127,7 @@ exports[`snippet: #1 [espree] format 1`] = ` `; exports[`snippet: #1 [flow] format 1`] = ` -"Unexpected token \`switch\` (1:7) +"Unexpected token \`switch\`, expected an identifier (1:7) > 1 | class switch() {} | ^^^^^^" `; diff --git a/tests/format/typescript/typeparams/consistent/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/typeparams/consistent/__snapshots__/jsfmt.spec.js.snap index f3ebb5358..d8a5c91d4 100644 --- a/tests/format/typescript/typeparams/consistent/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/typescript/typeparams/consistent/__snapshots__/jsfmt.spec.js.snap @@ -92,7 +92,7 @@ exports[`template-literal-types.ts [babel-flow] format 1`] = ` `; exports[`template-literal-types.ts [flow] format 1`] = ` -"Unexpected token \`\`\` (1:84) +"Unexpected token \`\`\`, expected a type (1:84) > 1 | const foo1: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo<\`Hello, \${keyof World}\`> = a; | ^ 2 |" diff --git a/yarn.lock b/yarn.lock index 1612dfd19..5c71102e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3427,10 +3427,10 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== -flow-parser@0.176.3: - version "0.176.3" - resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.176.3.tgz#a12d9c79ff1e857b70c9574c3e88be06d2f96181" - integrity sha512-KDzHEoEtc/kbW7NzujhfFkcTCdNi6VK91UpcdT3tc3yEAQdh4JXAEY/0TVJKipjRuVm8E2FxX/3B5Xpm3EFWXA== +flow-parser@0.180.0: + version "0.180.0" + resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.180.0.tgz#05d49a88715ceca0457607499a018e2bf5908d72" + integrity sha512-kkzsuGAhckWgn/G+JfCyEa6BYslGrjlH4CJL0LZhdn9of9ukvi7SzVQSFsrEhuhh/zQUghfUEoaeZy1wjQXpUg== form-data@^3.0.0: version "3.0.1" From 97bab8f984d86eb91fda654f5351c455152872a2 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 11 Jun 2022 20:08:46 +0900 Subject: [PATCH 03/29] Update `angular-estree-parser` (#12993) * Update `angular-estree-parser` * Add tests * Add changelog --- changelog_unreleased/angular/12993.md | 14 ++++++++++++++ package.json | 2 +- .../shorthand/__snapshots__/jsfmt.spec.js.snap | 15 +++++++++++++++ tests/format/angular/shorthand/basic.html | 1 + tests/format/angular/shorthand/jsfmt.spec.js | 1 + yarn.lock | 8 ++++---- 6 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 changelog_unreleased/angular/12993.md create mode 100644 tests/format/angular/shorthand/__snapshots__/jsfmt.spec.js.snap create mode 100644 tests/format/angular/shorthand/basic.html create mode 100644 tests/format/angular/shorthand/jsfmt.spec.js diff --git a/changelog_unreleased/angular/12993.md b/changelog_unreleased/angular/12993.md new file mode 100644 index 000000000..eac0ad6f8 --- /dev/null +++ b/changelog_unreleased/angular/12993.md @@ -0,0 +1,14 @@ +#### Fix shorthand properties formatting (#12993 by @sosukesuzuki, @fisker) + +```html + + + + + + + + +``` diff --git a/package.json b/package.json index c35b94b51..22e3f492a 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@typescript-eslint/typescript-estree": "5.27.0", "acorn": "8.7.0", "acorn-jsx": "5.3.2", - "angular-estree-parser": "2.5.0", + "angular-estree-parser": "2.5.1", "angular-html-parser": "1.8.0", "camelcase": "6.3.0", "chalk": "5.0.1", diff --git a/tests/format/angular/shorthand/__snapshots__/jsfmt.spec.js.snap b/tests/format/angular/shorthand/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 000000000..b3036f4c2 --- /dev/null +++ b/tests/format/angular/shorthand/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`basic.html format 1`] = ` +====================================options===================================== +parsers: ["angular"] +printWidth: 80 + | printWidth +=====================================input====================================== + + +=====================================output===================================== + + +================================================================================ +`; diff --git a/tests/format/angular/shorthand/basic.html b/tests/format/angular/shorthand/basic.html new file mode 100644 index 000000000..7d7223d9b --- /dev/null +++ b/tests/format/angular/shorthand/basic.html @@ -0,0 +1 @@ + diff --git a/tests/format/angular/shorthand/jsfmt.spec.js b/tests/format/angular/shorthand/jsfmt.spec.js new file mode 100644 index 000000000..b802ad867 --- /dev/null +++ b/tests/format/angular/shorthand/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["angular"]); diff --git a/yarn.lock b/yarn.lock index 5c71102e3..7e9a6ff89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1878,10 +1878,10 @@ ajv@^6.10.0, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -angular-estree-parser@2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/angular-estree-parser/-/angular-estree-parser-2.5.0.tgz#b2d05b54cc765c8181245c823fb9b4ef75dc5483" - integrity sha512-0OLRBYPkdlJHzRKL1PQVWLYnSXvAE7nnOVHhfVlqbdyAMg3rbk8U3Z4vk+5GG4x8ooT88T/89jtsOoGPzjKUbA== +angular-estree-parser@2.5.1: + version "2.5.1" + resolved "https://registry.yarnpkg.com/angular-estree-parser/-/angular-estree-parser-2.5.1.tgz#a08791f64f1a9453ecb99be5379f7f282e46c6ca" + integrity sha512-QP+1HEp9sUV3/ADU02IRc+Vn9vvWZS2rRkxiXCpSpZZx3BqcYTm2Eg/gWwLG3H9XASXnf9i1KyNOIYyRy5Ja+w== dependencies: lines-and-columns "^1.1.6" tslib "^2.0.3" From fdccc0f48e5bd3a17ae1e3500c1b5c1763c6a368 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sat, 11 Jun 2022 23:32:32 +0900 Subject: [PATCH 04/29] Add `--cache` CLI option (#12800) * Install `file-entry-cache` and types * Implement `FormatResultsCache` * Add `--cache` and `--cache-location` option * Implement `--cache` and `--cache-location` * Add tests for `--cache` * Add tests for `--cache-location` * Avoid spellcheck error * Update snapshots for new options * Add `groupBy` for website * Use `fs.promises` instead of `fs/promises` * Use `rimraf` instead of `fs.rm` * Fix properties order of `options` * Install `find-cache-dir` and types * Change default cache file location to `node_modules/.cache/prettier` * Fix tests following to change default cache file location * Update snapshots for changing default cache file location * Set `os.tmpdir()` as a fallback for default cache dir * Add docs for `--cache` and `--cache-location` * Use `sdbm` instead of `node:crypto` * Add changelog * Fix `getHashOfOptions` * Remove `cache-location` * `prettiercache` -> `prettier-cache` * Remove cache file when run Prettier without `--cache` option * Implement `--cache-strategy` * Add tests for invalid cache strategy * Fix lint problems * Tweaks tests * Add tests for timestamp * Add tests for `--cache-strategy` * Add docs for cache-strategy * Address review * Fix `findCacheFile` * Use Set.prototype.has * Throw error with --stdin-filepath * Update snapshots * Fix error for cache and stdin * Use string constructor instead of toString * Update changelog * Use flag validation * Fix `cache-strategy` definition * Update docs * Mark highlihgt * Throw error for `--cache-strategy` without `--cache` * Update docs * Fix typo * Updates snapshots * Update docs/cli.md Co-authored-by: Simon Lydell * Fix error message * Remove `:::` syntax * Defaults `content` * Update docs * Fix by Prettier Co-authored-by: Simon Lydell --- changelog_unreleased/cli/12800.md | 25 ++ cspell.json | 1 + docs/cli.md | 32 ++ package.json | 5 + scripts/vendors/vendor-meta.json | 18 + scripts/vendors/vendors.mjs | 1 + src/cli/constant.js | 19 + src/cli/expand-patterns.js | 19 +- src/cli/find-cache-file.js | 19 + src/cli/format-results-cache.js | 96 +++++ src/cli/format.js | 45 ++- src/cli/index.js | 4 + src/cli/utils.js | 31 +- .../__snapshots__/early-exit.js.snap | 8 + .../__snapshots__/help-options.js.snap | 29 ++ tests/integration/__tests__/cache.js | 373 ++++++++++++++++++ tests/integration/cli/cache/a.js | 3 + tests/integration/cli/cache/b.js | 3 + tests/integration/cli/cache/package.json | 3 + vendors/sdbm.d.ts | 3 + vendors/sdbm.js | 35 ++ website/playground/sidebar/SidebarOptions.js | 2 +- website/playground/util.js | 24 ++ yarn.lock | 37 +- 24 files changed, 811 insertions(+), 24 deletions(-) create mode 100644 changelog_unreleased/cli/12800.md create mode 100644 src/cli/find-cache-file.js create mode 100644 src/cli/format-results-cache.js 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 create mode 100644 tests/integration/cli/cache/package.json create mode 100644 vendors/sdbm.d.ts create mode 100644 vendors/sdbm.js diff --git a/changelog_unreleased/cli/12800.md b/changelog_unreleased/cli/12800.md new file mode 100644 index 000000000..a154ef117 --- /dev/null +++ b/changelog_unreleased/cli/12800.md @@ -0,0 +1,25 @@ +#### [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). + +##### `--cache` + +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 `content`) content of the file +- (if `--cache-strategy` is `metadata`) file metadata, such as timestamps + +```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, `content` will be used. + +```bash +prettier --write --cache --cache-strategy metadata src +``` diff --git a/cspell.json b/cspell.json index 6e12e2740..37f04c11c 100644 --- a/cspell.json +++ b/cspell.json @@ -289,6 +289,7 @@ "sandhose", "Sapegin", "sbdchd", + "sdbm", "scandir", "Serializers", "setlocal", diff --git a/docs/cli.md b/docs/cli.md index f72895ebd..af3afef5f 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -204,3 +204,35 @@ Prevent errors when pattern is unmatched. ## `--no-plugin-search` Disable plugin autoloading. + +## `--cache` + +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 + +```bash +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 ./node_modules/.cache/prettier/.prettier-cache` to remove it manually. + +> 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`. + +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 +``` diff --git a/package.json b/package.json index 22e3f492a..f3149c4c8 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,8 @@ "esutils": "2.0.3", "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", @@ -79,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", @@ -96,6 +99,8 @@ "@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/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/scripts/vendors/vendor-meta.json b/scripts/vendors/vendor-meta.json index 0a5c00b08..e4505dcf7 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 c477dd756..0473e0daa 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/constant.js b/src/cli/constant.js index 5755a06e0..c7cc8958d 100644 --- a/src/cli/constant.js +++ b/src/cli/constant.js @@ -71,6 +71,25 @@ const categoryOrder = [ */ /* eslint sort-keys: "error" */ const options = { + cache: { + default: false, + description: "Only format changed files. Cannot use with --stdin-filepath.", + 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", + }, + ], + description: "Strategy for the cache to use for detecting changed files.", + type: "choice", + }, check: { alias: "c", category: coreOptions.CATEGORY_OUTPUT, diff --git a/src/cli/expand-patterns.js b/src/cli/expand-patterns.js index d62108f7e..c097b10d3 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/find-cache-file.js b/src/cli/find-cache-file.js new file mode 100644 index 000000000..c8960ead4 --- /dev/null +++ b/src/cli/find-cache-file.js @@ -0,0 +1,19 @@ +"use strict"; + +const os = require("os"); +const path = require("path"); +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 findCacheFile() { + const cacheDir = + findCacheDir({ name: "prettier", create: true }) || os.tmpdir(); + const cacheFilePath = path.join(cacheDir, ".prettier-cache"); + return cacheFilePath; +} + +module.exports = findCacheFile; diff --git a/src/cli/format-results-cache.js b/src/cli/format-results-cache.js new file mode 100644 index 000000000..3dfb79d16 --- /dev/null +++ b/src/cli/format-results-cache.js @@ -0,0 +1,96 @@ +"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 optionsHashCache = new WeakMap(); +const nodeVersion = process && process.version; + +/** + * @param {*} options + * @returns {string} + */ +function getHashOfOptions(options) { + if (optionsHashCache.has(options)) { + return optionsHashCache.get(options); + } + const hash = createHash( + `${prettierVersion}_${nodeVersion}_${stringify(options)}` + ); + optionsHashCache.set(options, hash); + return hash; +} + +/** + * @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: `node_modules/.cache/prettier/prettier-cache`) + * @param {string} cacheStrategy + */ + constructor(cacheFileLocation, cacheStrategy) { + const useChecksum = cacheStrategy === "content"; + + this.cacheFileLocation = cacheFileLocation; + this.fileEntryCache = fileEntryCache.create( + /* cacheId */ cacheFileLocation, + /* directory */ undefined, + useChecksum + ); + } + + /** + * @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/format.js b/src/cli/format.js index 0f89481eb..8f5e4867f 100644 --- a/src/cli/format.js +++ b/src/cli/format.js @@ -15,6 +15,9 @@ 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 { statSafe } = require("./utils.js"); function diff(a, b) { return require("diff").createTwoFilesPatch("", "", a, b, "", "", { @@ -295,6 +298,26 @@ async function formatFiles(context) { context.logger.log("Checking formatting..."); } + let formatResultsCache; + const cacheFilePath = findCacheFile(); + if (context.argv.cache) { + formatResultsCache = new FormatResultsCache( + cacheFilePath, + context.argv.cacheStrategy || "content" + ); + } else { + if (context.argv.cacheStrategy) { + context.logger.error( + "`--cache-strategy` cannot be used without `--cache`." + ); + process.exit(2); + } + const stat = await statSafe(cacheFilePath); + if (stat) { + await fs.unlink(cacheFilePath); + } + } + for await (const pathOrError of expandPatterns(context)) { if (typeof pathOrError === "object") { context.logger.error(pathOrError.error); @@ -362,17 +385,28 @@ async function formatFiles(context) { const start = Date.now(); + const isCacheExists = formatResultsCache?.existsAvailableFormatResultsCache( + filename, + options + ); + let result; let output; try { - result = format(context, input, options); + if (isCacheExists) { + 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 +442,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 (isCacheExists) { + context.logger.log(`${message} (cached)`); + } else { + context.logger.log(message); + } } } else if (context.argv.debugCheck) { /* istanbul ignore else */ @@ -431,6 +470,8 @@ async function formatFiles(context) { } } + formatResultsCache?.reconcile(); + // Print check summary based on expected exit code if (context.argv.check) { if (numberOfUnformattedFilesFound === 0) { diff --git a/src/cli/index.js b/src/cli/index.js index 1c3ea9810..6cb0a64d5 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -103,6 +103,10 @@ async function main(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/src/cli/utils.js b/src/cli/utils.js index 3fc2637dd..a846c185c 100644 --- a/src/cli/utils.js +++ b/src/cli/utils.js @@ -1,5 +1,10 @@ "use strict"; +const { promises: fs } = require("fs"); + +// 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); @@ -38,4 +43,28 @@ function pick(object, keys) { return Object.fromEntries(entries); } -module.exports = { printToScreen, groupBy, pick }; +/** + * @param {string} source + * @returns {string} + */ +function createHash(source) { + return String(sdbm(source)); +} + +/** + * 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__/__snapshots__/early-exit.js.snap b/tests/integration/__tests__/__snapshots__/early-exit.js.snap index 0daa6dcd4..63f00b025 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. Cannot use with --stdin-filepath. + Defaults to false. + --cache-strategy + Strategy for the cache to use for detecting changed files. --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. Cannot use with --stdin-filepath. + Defaults to false. + --cache-strategy + Strategy for the cache to use for detecting changed files. --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 a90130ce8..009f516d3 100644 --- a/tests/integration/__tests__/__snapshots__/help-options.js.snap +++ b/tests/integration/__tests__/__snapshots__/help-options.js.snap @@ -44,6 +44,35 @@ 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. Cannot use with --stdin-filepath. + +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 +" +`; + +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`] = ` diff --git a/tests/integration/__tests__/cache.js b/tests/integration/__tests__/cache.js new file mode 100644 index 000000000..863cd2901 --- /dev/null +++ b/tests/integration/__tests__/cache.js @@ -0,0 +1,373 @@ +"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); +} + +describe("--cache option", () => { + const dir = resolveDir("cli/cache"); + const defaultCacheFile = path.join( + dir, + "node_modules/.cache/prettier/.prettier-cache" + ); + + 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 () => { + 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("throw error when cache-strategy is invalid", async () => { + const { stderr } = await runPrettier(dir, [ + "--cache", + "--cache-strategy", + "invalid", + ".", + ]); + expect(stripAnsi(stderr.trim())).toBe( + '[error] Invalid --cache-strategy value. Expected "content" or "metadata", but received "invalid".' + ); + }); + + it("throws error when use with --stdin-filepath", async () => { + const { stderr } = await runPrettier( + dir, + ["--cache", "--stdin-filepath", "foo.js"], + { input: "const a = a;" } + ); + expect(stripAnsi(stderr.trim())).toBe( + "[error] `--cache` cannot be used with stdin." + ); + }); + + 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` 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( + "code", + "ENOENT" + ); + await runPrettier(dir, ["--cache", "--cache-strategy", "metadata", "."]); + 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", + "--cache-strategy", + "metadata", + ".", + ]); + 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", + "metadata", + ".", + ]); + 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", + "--cache-strategy", + "metadata", + ".", + ]); + 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", + "--cache-strategy", + "metadata", + ".", + ]); + 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 timestamp has been updated", async () => { + const { stdout: firstStdout } = await runPrettier(dir, [ + "--cache", + "--write", + "--cache-strategy", + "metadata", + ".", + ]); + 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", + "--cache-strategy", + "metadata", + ".", + ]); + 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", + "--cache-strategy", + "metadata", + ".", + ]); + 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", + "metadata", + "--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", + "--cache-strategy", + "metadata", + ".", + ]); + await expect(fs.stat(defaultCacheFile)).resolves.not.toThrowError(); + await runPrettier(dir, ["--write", "."]); + 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(); + }); + }); +}); diff --git a/tests/integration/cli/cache/a.js b/tests/integration/cli/cache/a.js new file mode 100644 index 000000000..b824ea0f9 --- /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 000000000..4bc80d22e --- /dev/null +++ b/tests/integration/cli/cache/b.js @@ -0,0 +1,3 @@ +function b() { + console.log("this is b.js"); +} diff --git a/tests/integration/cli/cache/package.json b/tests/integration/cli/cache/package.json new file mode 100644 index 000000000..91dc022ee --- /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" +} diff --git a/vendors/sdbm.d.ts b/vendors/sdbm.d.ts new file mode 100644 index 000000000..812356628 --- /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 000000000..d86706298 --- /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/website/playground/sidebar/SidebarOptions.js b/website/playground/sidebar/SidebarOptions.js index d47347c71..51d3704f1 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 2752aebb6..587a54910 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; +} diff --git a/yarn.lock b/yarn.lock index 7e9a6ff89..72e836b3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1638,6 +1638,18 @@ 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/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" @@ -2394,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" @@ -3367,7 +3384,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== @@ -3381,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" @@ -4849,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== @@ -5399,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== @@ -5846,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 e5868ad368d593549408c90f2bd16bc1d76674b0 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 11 Jun 2022 23:36:35 +0900 Subject: [PATCH 05/29] Update changelog --- changelog_unreleased/cli/12800.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog_unreleased/cli/12800.md b/changelog_unreleased/cli/12800.md index a154ef117..7a8900582 100644 --- a/changelog_unreleased/cli/12800.md +++ b/changelog_unreleased/cli/12800.md @@ -2,6 +2,8 @@ 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 doc](https://prettier.io/docs/en/cli.html#--cache) for more details. + ##### `--cache` 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. From 2316e2fecd171335fc00f3ec97b7e46cc8964153 Mon Sep 17 00:00:00 2001 From: Sosuke Suzuki Date: Sun, 12 Jun 2022 01:55:11 +0900 Subject: [PATCH 06/29] Update `linguist-language` (#12994) * Install new `linguist-language` * Remove `.mts` and `.cts` * Update tests --- package.json | 2 +- src/language-js/index.js | 14 ++++------ .../__snapshots__/support-info.js.snap | 26 +++++++++++++++++-- yarn.lock | 8 +++--- 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index f3149c4c8..62e5c5545 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "json5": "2.2.1", "leven": "4.0.0", "lines-and-columns": "2.0.3", - "linguist-languages": "7.15.0", + "linguist-languages": "7.21.0", "mem": "9.0.2", "meriyah": "4.2.1", "micromatch": "4.0.5", diff --git a/src/language-js/index.js b/src/language-js/index.js index c20b8788c..bcf54ac64 100644 --- a/src/language-js/index.js +++ b/src/language-js/index.js @@ -68,15 +68,11 @@ const languages = [ codemirrorMimeType: "text/jsx", color: undefined, })), - createLanguage( - require("linguist-languages/data/TypeScript.json"), - (data) => ({ - since: "1.4.0", - parsers: ["typescript", "babel-ts"], - vscodeLanguageIds: ["typescript"], - extensions: [...data.extensions, ".mts", ".cts"], - }) - ), + createLanguage(require("linguist-languages/data/TypeScript.json"), () => ({ + since: "1.4.0", + parsers: ["typescript", "babel-ts"], + vscodeLanguageIds: ["typescript"], + })), createLanguage(require("linguist-languages/data/TSX.json"), () => ({ since: "1.4.0", parsers: ["typescript", "babel-ts"], diff --git a/tests/integration/__tests__/__snapshots__/support-info.js.snap b/tests/integration/__tests__/__snapshots__/support-info.js.snap index f016508d3..8e6f7d9e0 100644 --- a/tests/integration/__tests__/__snapshots__/support-info.js.snap +++ b/tests/integration/__tests__/__snapshots__/support-info.js.snap @@ -300,10 +300,13 @@ exports[`CLI --support-info (stdout) 1`] = ` ".frag", ".gs", ".jake", + ".javascript", ".jsb", ".jscad", ".jsfl", + ".jslib", ".jsm", + ".jspre", ".jss", ".mjs", ".njs", @@ -400,8 +403,8 @@ exports[`CLI --support-info (stdout) 1`] = ` "aliases": ["ts"], "codemirrorMimeType": "application/typescript", "codemirrorMode": "javascript", - "color": "#2b7489", - "extensions": [".ts", ".mts", ".cts"], + "color": "#3178c6", + "extensions": [".ts", ".cts", ".mts"], "interpreters": ["deno", "ts-node"], "linguistLanguageId": 378, "name": "TypeScript", @@ -415,6 +418,7 @@ exports[`CLI --support-info (stdout) 1`] = ` "aceMode": "javascript", "codemirrorMimeType": "text/jsx", "codemirrorMode": "jsx", + "color": "#3178c6", "extensions": [".tsx"], "group": "TypeScript", "linguistLanguageId": 94901924, @@ -427,8 +431,10 @@ exports[`CLI --support-info (stdout) 1`] = ` }, { "aceMode": "json", + "aliases": ["geojson", "jsonl", "topojson"], "codemirrorMimeType": "application/json", "codemirrorMode": "javascript", + "color": "#292929", "extensions": [".importmap"], "filenames": ["package.json", "package-lock.json", "composer.json"], "linguistLanguageId": 174, @@ -441,10 +447,14 @@ exports[`CLI --support-info (stdout) 1`] = ` }, { "aceMode": "json", + "aliases": ["geojson", "jsonl", "topojson"], "codemirrorMimeType": "application/json", "codemirrorMode": "javascript", + "color": "#292929", "extensions": [ ".json", + ".4DForm", + ".4DProject", ".avsc", ".geojson", ".gltf", @@ -462,8 +472,11 @@ exports[`CLI --support-info (stdout) 1`] = ` ], "filenames": [ ".arcconfig", + ".auto-changelog", + ".c8rc", ".htmlhintrc", ".imgbotconfig", + ".nycrc", ".tern-config", ".tern-project", ".watchmanconfig", @@ -484,8 +497,10 @@ exports[`CLI --support-info (stdout) 1`] = ` "aliases": ["jsonc"], "codemirrorMimeType": "text/javascript", "codemirrorMode": "javascript", + "color": "#292929", "extensions": [ ".jsonc", + ".code-snippets", ".sublime-build", ".sublime-commands", ".sublime-completions", @@ -502,6 +517,7 @@ exports[`CLI --support-info (stdout) 1`] = ` ], "filenames": [ ".babelrc", + ".devcontainer.json", ".eslintrc.json", ".jscsrc", ".jshintrc", @@ -528,6 +544,7 @@ exports[`CLI --support-info (stdout) 1`] = ` "aceMode": "javascript", "codemirrorMimeType": "application/json", "codemirrorMode": "javascript", + "color": "#267CB9", "extensions": [".json5"], "linguistLanguageId": 175, "name": "JSON5", @@ -553,6 +570,7 @@ exports[`CLI --support-info (stdout) 1`] = ` }, { "aceMode": "text", + "color": "#dc3a0c", "extensions": [".pcss", ".postcss"], "group": "CSS", "linguistLanguageId": 262764437, @@ -565,6 +583,7 @@ exports[`CLI --support-info (stdout) 1`] = ` }, { "aceMode": "less", + "aliases": ["less-css"], "codemirrorMimeType": "text/css", "codemirrorMode": "css", "color": "#1d365d", @@ -624,6 +643,7 @@ exports[`CLI --support-info (stdout) 1`] = ` "color": "#083fa1", "extensions": [ ".md", + ".livemd", ".markdown", ".mdown", ".mdwn", @@ -685,6 +705,7 @@ exports[`CLI --support-info (stdout) 1`] = ` "color": "#e34c26", "extensions": [ ".html", + ".hta", ".htm", ".html.hl", ".inc", @@ -750,6 +771,7 @@ exports[`CLI --support-info (stdout) 1`] = ` ".clang-format", ".clang-tidy", ".gemrc", + "CITATION.cff", "glide.lock", ".prettierrc", ".stylelintrc" diff --git a/yarn.lock b/yarn.lock index 72e836b3e..90f02e84b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4782,10 +4782,10 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== -linguist-languages@7.15.0: - version "7.15.0" - resolved "https://registry.yarnpkg.com/linguist-languages/-/linguist-languages-7.15.0.tgz#a93bed6b93015d8133622cb05da6296890862bfa" - integrity sha512-qkSSNDjDDycZ2Wcw+GziNBB3nNo3ddYUInM/PL8Amgwbd9RQ/BKGj2/1d6mdxKgBFnUqZuaDbkIwkE4KUwwmtQ== +linguist-languages@7.21.0: + version "7.21.0" + resolved "https://registry.yarnpkg.com/linguist-languages/-/linguist-languages-7.21.0.tgz#da0184f622367cb092f1f8ba435937a85534f675" + integrity sha512-KrWJJbFOvlDhjlt5OhUipVlXg+plUfRurICAyij1ZVxQcqPt/zeReb9KiUVdGUwwhS/2KS9h3TbyfYLA5MDlxQ== load-json-file@^4.0.0: version "4.0.0" From 1bec824556673302d67973a8374ee13819f74ab3 Mon Sep 17 00:00:00 2001 From: HosokawaR <45098934+HosokawaR@users.noreply.github.com> Date: Sun, 12 Jun 2022 12:19:09 +0900 Subject: [PATCH 07/29] Revert "print as instead of colon for babel-ts parser" (#12995) This reverts commit f49114141a21f4f3cb1bb569e241ce1dbcdece44. ; Conflicts: ; src/language-js/print/misc.js --- src/language-js/print/misc.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/language-js/print/misc.js b/src/language-js/print/misc.js index 81514bee0..3d5a7d0f8 100644 --- a/src/language-js/print/misc.js +++ b/src/language-js/print/misc.js @@ -55,11 +55,6 @@ function printTypeAnnotation(path, options, print) { const parentNode = path.getParentNode(); - // Workaround for https://github.com/babel/babel/issues/14498 - if (parentNode.type === "ArrayPattern" && options.parser === "babel-ts") { - return [" as ", print("typeAnnotation")]; - } - const isFunctionDeclarationIdentifier = parentNode.type === "DeclareFunction" && parentNode.id === node; From d2bcaea121d244a00100e5c31e2e8ca9fe5f1ced Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 14 Jun 2022 12:33:44 +0800 Subject: [PATCH 08/29] Throw errors for invalid `TSEmptyBodyFunctionExpression` node (#12982) --- changelog_unreleased/typescript/12982.md | 15 ++ src/language-js/parse/postprocess/index.js | 15 ++ .../parse/postprocess/throw-syntax-error.js | 12 ++ .../parse/postprocess/typescript.js | 11 +- .../invalid/__snapshots__/jsfmt.spec.js.snap | 144 ++++++++++++++++++ .../format/misc/errors/invalid/jsfmt.spec.js | 8 +- 6 files changed, 195 insertions(+), 10 deletions(-) create mode 100644 changelog_unreleased/typescript/12982.md create mode 100644 src/language-js/parse/postprocess/throw-syntax-error.js diff --git a/changelog_unreleased/typescript/12982.md b/changelog_unreleased/typescript/12982.md new file mode 100644 index 000000000..5be97d595 --- /dev/null +++ b/changelog_unreleased/typescript/12982.md @@ -0,0 +1,15 @@ +#### Stop parsing invalid code (#12982 by @fisker) + + +```tsx +// Input +const object = ({ methodName() }); + +// Prettier stable +const object = { methodName(); }; + +// Prettier main +SyntaxError: Unexpected token. (1:29) +> 1 | const object = ({ methodName() }); + | ^^ +``` diff --git a/src/language-js/parse/postprocess/index.js b/src/language-js/parse/postprocess/index.js index 3b3260bc6..62959af5b 100644 --- a/src/language-js/parse/postprocess/index.js +++ b/src/language-js/parse/postprocess/index.js @@ -6,6 +6,7 @@ const isTypeCastComment = require("../../utils/is-type-cast-comment.js"); const getLast = require("../../../utils/get-last.js"); const visitNode = require("./visit-node.js"); const { throwErrorForInvalidNodes } = require("./typescript.js"); +const throwSyntaxError = require("./throw-syntax-error.js"); function postprocess(ast, options) { if ( @@ -102,6 +103,20 @@ function postprocess(ast, options) { }; } break; + case "ObjectExpression": + // #12963 + if (options.parser === "typescript") { + const invalidProperty = node.properties.find( + (property) => + property.type === "Property" && + property.value.type === "TSEmptyBodyFunctionExpression" + ); + if (invalidProperty) { + throwSyntaxError(invalidProperty.value, "Unexpected token."); + } + } + break; + case "SequenceExpression": { // Babel (unlike other parsers) includes spaces and comments in the range. Let's unify this. const lastExpression = getLast(node.expressions); diff --git a/src/language-js/parse/postprocess/throw-syntax-error.js b/src/language-js/parse/postprocess/throw-syntax-error.js new file mode 100644 index 000000000..0505aba37 --- /dev/null +++ b/src/language-js/parse/postprocess/throw-syntax-error.js @@ -0,0 +1,12 @@ +"use strict"; +const createError = require("../../../common/parser-create-error.js"); + +function throwSyntaxError(node, message) { + const { start, end } = node.loc; + throw createError(message, { + start: { line: start.line, column: start.column + 1 }, + end: { line: end.line, column: end.column + 1 }, + }); +} + +module.exports = throwSyntaxError; diff --git a/src/language-js/parse/postprocess/typescript.js b/src/language-js/parse/postprocess/typescript.js index 6817084ab..aea24d9dd 100644 --- a/src/language-js/parse/postprocess/typescript.js +++ b/src/language-js/parse/postprocess/typescript.js @@ -1,15 +1,7 @@ "use strict"; -const createError = require("../../../common/parser-create-error.js"); const visitNode = require("./visit-node.js"); - -function throwSyntaxError(node, message) { - const { start, end } = node.loc; - throw createError(message, { - start: { line: start.line, column: start.column + 1 }, - end: { line: end.line, column: end.column + 1 }, - }); -} +const throwSyntaxError = require("./throw-syntax-error.js"); // Invalid decorators are removed since `@typescript-eslint/typescript-estree` v4 // https://github.com/typescript-eslint/typescript-eslint/pull/2375 @@ -73,6 +65,7 @@ function throwErrorForInvalidNodes(ast, options) { if (esTreeNode !== node) { return; } + throwErrorForInvalidDecorator(tsNode, esTreeNode, tsNodeToESTreeNodeMap); throwErrorForInvalidAbstractProperty(tsNode, esTreeNode); }); diff --git a/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap b/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap index b2c981a4b..851e810b2 100644 --- a/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap @@ -156,92 +156,236 @@ exports[`snippet: #1 [typescript] format 1`] = ` | ^" `; +exports[`snippet: #2 [acorn] format 1`] = ` +"Unexpected token (1:13) +> 1 | ({ method() }) + | ^" +`; + exports[`snippet: #2 [babel] format 1`] = ` +"Unexpected token, expected "{" (1:13) +> 1 | ({ method() }) + | ^" +`; + +exports[`snippet: #2 [babel] format 2`] = ` "Invalid parenthesized assignment pattern. (1:1) > 1 | (a = 1) = 2 | ^" `; exports[`snippet: #2 [babel-flow] format 1`] = ` +"Unexpected token, expected "{" (1:13) +> 1 | ({ method() }) + | ^" +`; + +exports[`snippet: #2 [babel-flow] format 2`] = ` "Invalid parenthesized assignment pattern. (1:1) > 1 | (a = 1) = 2 | ^" `; exports[`snippet: #2 [babel-ts] format 1`] = ` +"Unexpected token, expected "{" (1:13) +> 1 | ({ method() }) + | ^" +`; + +exports[`snippet: #2 [babel-ts] format 2`] = ` "Invalid parenthesized assignment pattern. (1:1) > 1 | (a = 1) = 2 | ^" `; +exports[`snippet: #2 [espree] format 1`] = ` +"Unexpected token } (1:13) +> 1 | ({ method() }) + | ^" +`; + exports[`snippet: #2 [flow] format 1`] = ` +"Unexpected token \`}\`, expected the token \`{\` (1:13) +> 1 | ({ method() }) + | ^" +`; + +exports[`snippet: #2 [flow] format 2`] = ` "Invalid left-hand side in assignment (1:2) > 1 | (a = 1) = 2 | ^^^^^" `; exports[`snippet: #2 [meriyah] format 1`] = ` +"Expected '{' (1:13) +> 1 | ({ method() }) + | ^" +`; + +exports[`snippet: #2 [meriyah] format 2`] = ` "Invalid left-hand side in assignment (1:9) > 1 | (a = 1) = 2 | ^" `; +exports[`snippet: #2 [typescript] format 1`] = ` +"Unexpected token. (1:10) +> 1 | ({ method() }) + | ^^" +`; + +exports[`snippet: #3 [acorn] format 1`] = ` +"Unexpected token (1:15) +> 1 | ({ method({}) }) + | ^" +`; + exports[`snippet: #3 [babel] format 1`] = ` +"Unexpected token, expected "{" (1:15) +> 1 | ({ method({}) }) + | ^" +`; + +exports[`snippet: #3 [babel] format 2`] = ` "Invalid parenthesized assignment pattern. (1:1) > 1 | (a += b) = 1 | ^" `; exports[`snippet: #3 [babel-flow] format 1`] = ` +"Unexpected token, expected "{" (1:15) +> 1 | ({ method({}) }) + | ^" +`; + +exports[`snippet: #3 [babel-flow] format 2`] = ` "Invalid parenthesized assignment pattern. (1:1) > 1 | (a += b) = 1 | ^" `; exports[`snippet: #3 [babel-ts] format 1`] = ` +"Unexpected token, expected "{" (1:15) +> 1 | ({ method({}) }) + | ^" +`; + +exports[`snippet: #3 [babel-ts] format 2`] = ` "Invalid parenthesized assignment pattern. (1:1) > 1 | (a += b) = 1 | ^" `; +exports[`snippet: #3 [espree] format 1`] = ` +"Unexpected token } (1:15) +> 1 | ({ method({}) }) + | ^" +`; + exports[`snippet: #3 [flow] format 1`] = ` +"Unexpected token \`}\`, expected the token \`{\` (1:15) +> 1 | ({ method({}) }) + | ^" +`; + +exports[`snippet: #3 [flow] format 2`] = ` "Invalid left-hand side in assignment (1:2) > 1 | (a += b) = 1 | ^^^^^^" `; exports[`snippet: #3 [meriyah] format 1`] = ` +"Expected '{' (1:15) +> 1 | ({ method({}) }) + | ^" +`; + +exports[`snippet: #3 [meriyah] format 2`] = ` "Invalid left-hand side in assignment (1:10) > 1 | (a += b) = 1 | ^" `; +exports[`snippet: #3 [typescript] format 1`] = ` +"Unexpected token. (1:10) +> 1 | ({ method({}) }) + | ^^^^" +`; + +exports[`snippet: #4 [acorn] format 1`] = ` +"Unexpected token (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + exports[`snippet: #4 [babel] format 1`] = ` +"Unexpected token, expected "{" (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + +exports[`snippet: #4 [babel] format 2`] = ` "Invalid left-hand side in parenthesized expression. (1:2) > 1 | (a = b) += 1 | ^" `; exports[`snippet: #4 [babel-flow] format 1`] = ` +"Unexpected token, expected "{" (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + +exports[`snippet: #4 [babel-flow] format 2`] = ` "Invalid left-hand side in parenthesized expression. (1:2) > 1 | (a = b) += 1 | ^" `; exports[`snippet: #4 [babel-ts] format 1`] = ` +"Unexpected token, expected "{" (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + +exports[`snippet: #4 [babel-ts] format 2`] = ` "Invalid left-hand side in parenthesized expression. (1:2) > 1 | (a = b) += 1 | ^" `; +exports[`snippet: #4 [espree] format 1`] = ` +"Unexpected token } (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + exports[`snippet: #4 [flow] format 1`] = ` +"Unexpected token \`}\`, expected the token \`{\` (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + +exports[`snippet: #4 [flow] format 2`] = ` "Invalid left-hand side in assignment (1:2) > 1 | (a = b) += 1 | ^^^^^" `; exports[`snippet: #4 [meriyah] format 1`] = ` +"Expected '{' (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + +exports[`snippet: #4 [meriyah] format 2`] = ` "Invalid left-hand side in assignment (1:10) > 1 | (a = b) += 1 | ^" `; + +exports[`snippet: #4 [typescript] format 1`] = ` +"Unexpected token. (1:10) +> 1 | ({ method(parameter,) }) + | ^^^^^^^^^^^^" +`; diff --git a/tests/format/misc/errors/invalid/jsfmt.spec.js b/tests/format/misc/errors/invalid/jsfmt.spec.js index bdc6df385..fa83db011 100644 --- a/tests/format/misc/errors/invalid/jsfmt.spec.js +++ b/tests/format/misc/errors/invalid/jsfmt.spec.js @@ -1,7 +1,13 @@ run_spec( { dirname: __dirname, - snippets: ["for each (a in b) {}", "class switch() {}"], + snippets: [ + "for each (a in b) {}", + "class switch() {}", + "({ method() })", + "({ method({}) })", + "({ method(parameter,) })", + ], }, [ "babel", From 00ee6fb6f8b7a44600daccb4da74323551566e28 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 14 Jun 2022 13:04:05 +0800 Subject: [PATCH 09/29] Skip depenedencies install --- scripts/release/release.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release/release.js b/scripts/release/release.js index 9f78aeda8..eef364088 100644 --- a/scripts/release/release.js +++ b/scripts/release/release.js @@ -39,7 +39,7 @@ async function run() { [ "./steps/validate-new-version.js", "./steps/check-git-status.js", - "./steps/install-dependencies.js", + // "./steps/install-dependencies.js",s params.manual && "./steps/run-tests.js", "./steps/update-version.js", params.manual && "./steps/generate-bundles.js", From 13dcc163300a45ef8a4129d7ae80c9302e81727e Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 14 Jun 2022 13:06:41 +0800 Subject: [PATCH 10/29] Release 2.7.0 --- .github/ISSUE_TEMPLATE/formatting.md | 2 +- .github/ISSUE_TEMPLATE/integration.md | 2 +- CHANGELOG.md | 6 +++ docs/browser.md | 26 ++++++------- package.json | 2 +- .../versioned_docs/version-stable/browser.md | 26 ++++++------- website/versioned_docs/version-stable/cli.md | 32 ++++++++++++++++ .../version-stable/configuration.md | 38 ++++++++++++++++--- .../versioned_docs/version-stable/plugins.md | 1 + 9 files changed, 101 insertions(+), 34 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/formatting.md b/.github/ISSUE_TEMPLATE/formatting.md index d1b763348..ccae59a79 100644 --- a/.github/ISSUE_TEMPLATE/formatting.md +++ b/.github/ISSUE_TEMPLATE/formatting.md @@ -26,7 +26,7 @@ Don't fill the form below manually! Let a program create a report for you: --> -**Prettier 2.6.2** +**Prettier 2.7.0** [Playground link](https://prettier.io/playground/#.....) ```sh diff --git a/.github/ISSUE_TEMPLATE/integration.md b/.github/ISSUE_TEMPLATE/integration.md index 9034f876b..1672e225d 100644 --- a/.github/ISSUE_TEMPLATE/integration.md +++ b/.github/ISSUE_TEMPLATE/integration.md @@ -20,7 +20,7 @@ BEFORE SUBMITTING AN ISSUE: **Environments:** -- Prettier Version: 2.6.2 +- Prettier Version: 2.7.0 - Running Prettier via: - Runtime: - Operating System: diff --git a/CHANGELOG.md b/CHANGELOG.md index af8d402c5..5c937786c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 2.7.0 + +[diff](https://github.com/prettier/prettier/compare/2.6.2...2.7.0) + +🔗 [Release Notes](https://prettier.io/blog/2022/06/14/2.7.0.html) + # 2.6.2 [diff](https://github.com/prettier/prettier/compare/2.6.1...2.6.2) diff --git a/docs/browser.md b/docs/browser.md index 5e0852d23..7a43c6cb7 100644 --- a/docs/browser.md +++ b/docs/browser.md @@ -32,8 +32,8 @@ See below for examples. ### Global ```html - - + + - + + + + + + + + + +``` + +### Vue + +#### Allow formatting for Vue template expression written in TypeScript ([#12584](https://github.com/prettier/prettier/pull/12584) by [@sosukesuzuki](https://github.com/sosukesuzuki)) + + +```vue + + + + + + + + + + + + + + +``` + +#### Infer Stylus parser for Vue SFC style block ([#12707](https://github.com/prettier/prettier/pull/12707) by [@lsdsjy](https://github.com/lsdsjy)) + +`