Merge branch 'main' into next

# Conflicts:
#	docs/browser.md
#	package.json
#	scripts/release/yarn.lock
#	scripts/vendors/vendor-meta.json
#	scripts/vendors/vendors.mjs
#	src/cli/expand-patterns.js
#	src/cli/format.js
#	src/cli/utils.js
#	src/language-js/index.js
#	src/language-js/parse/postprocess/index.js
#	src/language-js/parse/postprocess/typescript.js
#	tests/format/misc/errors/invalid/jsfmt.spec.js
#	tests/integration/__tests__/__snapshots__/support-info.js.snap
#	website/playground/sidebar/SidebarOptions.js
#	yarn.lock
This commit is contained in:
fisker committed 2022-06-19 11:12:22 +08:00
commit 0e0f879c1a
68 files changed
+2789 -1286

No files matched your search

+4
View File
@@ -5,6 +5,10 @@
# See https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-fileltfilegt
# Prettier bump after release
# 2.7.1
1f9baef429df2516ff8eb835fd8c020b5e854a5f
# 2.7.0
59ec4f2ef4aef37c10a265aa7fc568c596fb4427
# 2.6.2
e2d9e9139d34043c8e6c45cda2777de013340e2e
# 2.6.1
+1 -1
View File
@@ -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.1**
[Playground link](https://prettier.io/playground/#.....)
```sh
+1 -1
View File
@@ -20,7 +20,7 @@ BEFORE SUBMITTING AN ISSUE:
**Environments:**
- Prettier Version: 2.6.2
- Prettier Version: 2.7.1
- Running Prettier via: <!-- CLI, Node.js API, Browser API, etc. -->
- Runtime: <!-- Node.js v14, Chrome v83, etc. -->
- Operating System: <!-- Windows, Linux, macOS, etc. -->
+45
View File
@@ -1,3 +1,48 @@
# 2.7.1
[diff](https://github.com/prettier/prettier/compare/2.7.0...2.7.1)
#### Keep useful empty lines in description ([#13013](https://github.com/prettier/prettier/pull/13013) by [@chimurai](https://github.com/chimurai))
<!-- prettier-ignore -->
```graphql
# Input
"""
First line
Second Line
"""
type Person {
name: String
}
# Prettier 2.7.0
"""
First line
Second Line
"""
type Person {
name: String
}
# Prettier 2.7.1
"""
First line
Second Line
"""
type Person {
name: String
}
```
# 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)
-21
View File
@@ -1,21 +0,0 @@
#### Print the number of files need change (#12561 by @Harry-Hopkinson)
```console
# Prettier stable
$ prettier src --check
Checking formatting...
[warn] src/fileA.js
[warn] Code style issues found in the above file(s). Forgot to run Prettier?
# Prettier main
$ prettier src --check
Checking formatting...
[warn] src/fileA.js
[warn] src/fileB.js
[warn] Code style issues found in 2 files. Forgot to run Prettier?
$ prettier src --check
Checking formatting...
[warn] src/fileA.js
[warn] Code style issues found in the above file. Forgot to run Prettier?
```
-3
View File
@@ -1,3 +0,0 @@
#### Infer parser for `.importmap` files (#12603 by @fisker)
Format [`.importmap`](https://github.com/WICG/import-maps#import-map-processing) files as a JSON files.
-6
View File
@@ -1,6 +0,0 @@
#### Simplify performance test (#12682, #12698 by @fisker)
When `--debug-benchmark` or `--debug-repeat` is passed:
1. The CLI skips print code to the screen or write file
1. Set log level to `debug` automatically
-17
View File
@@ -1,17 +0,0 @@
#### Add support for printing SchemaExtension nodes (#12519 by @trevor-scheer)
<!-- prettier-ignore -->
```graphql
# Input
extend schema { subscription: Subscription }
extend schema @directive
# Prettier stable
N/A - throws error
# Prettier main
extend schema {
subscription: Subscription
}
extend schema @directive
```
-59
View File
@@ -1,59 +0,0 @@
#### Fix single line and empty description formatting (#12608 by @chimurai, @fisker)
<!-- prettier-ignore -->
```graphql
# Input
""" Customer """
type Person {
name: String
}
""""""
type Person {
name: String
}
# Prettier 2.6.2
"""
Customer
"""
type Person {
name: String
}
"""
"""
type Person {
name: String
}
# Prettier 2.6.2 (second format)
"""
Customer
"""
type Person {
name: String
}
"""
"""
type Person {
name: String
}
# Prettier main
"""
Customer
"""
type Person {
name: String
}
"""
"""
type Person {
name: String
}
```
-32
View File
@@ -1,32 +0,0 @@
#### Support Speculation Rules API formatting in HTML (#12882 by @sosukesuzuki)
Please read https://web.dev/speculative-prerendering/ for about Speculation Rules API.
<!-- prettier-ignore -->
```html
<!-- Input -->
<script type="speculationrules">
{
"prerender": [
{"source": "list", "urls": ["https://a.test/foo"]}
]
}
</script>
<!-- Prettier stable -->
<script type="speculationrules">
{
"prerender": [
{"source": "list", "urls": ["https://a.test/foo"]}
]
}
</script>
<!-- Prettier main -->
<script type="speculationrules">
{
"prerender": [{ "source": "list", "urls": ["https://a.test/foo"] }]
}
</script>
```
-33
View File
@@ -1,33 +0,0 @@
#### Preserve blank line between export specifiers (#12746 by @sosukesuzuki)
<!-- prettier-ignore -->
```jsx
// Input
export {
// a
foo1,
// b
bar1,
baz1,
} from "mod";
// Prettier stable
export {
// a
foo1,
// b
bar1,
baz1,
} from "mod";
// Prettier main
export {
// a
foo1,
// b
bar1,
baz1,
} from "mod";
```
-77
View File
@@ -1,77 +0,0 @@
#### Make more callee patterns recognized as "test call callee". (#12779 by @HosokawaR)
Supports test calls like [Playwright `test.describe`](https://playwright.dev/docs/api/class-test#test-describe).
<!-- prettier-ignore -->
```jsx
// Input
test.step("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe.only("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe.parallel("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe.parallel.only("does something really long and complicated so I have to write a very long name for the testThis is a very", () => {});
test.describe.serial("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe.serial.only("does something really long and complicated so I have to write a very long name for the test", () => {});
// Prettier stable
test.step(
"does something really long and complicated so I have to write a very long name for the test",
() => {}
);
test.describe(
"does something really long and complicated so I have to write a very long name for the test",
() => {}
);
test.describe.only(
"does something really long and complicated so I have to write a very long name for the test",
() => {}
);
test.describe.parallel(
"does something really long and complicated so I have to write a very long name for the test",
() => {}
);
test.describe.parallel.only(
"does something really long and complicated so I have to write a very long name for the testThis is a very",
() => {}
);
test.describe.serial(
"does something really long and complicated so I have to write a very long name for the test",
() => {}
);
test.describe.serial.only(
"does something really long and complicated so I have to write a very long name for the test",
() => {}
);
// Prettier main
test.step("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe
.only("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe
.parallel("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe.parallel
.only("does something really long and complicated so I have to write a very long name for the testThis is a very", () => {});
test.describe
.serial("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe.serial
.only("does something really long and complicated so I have to write a very long name for the test", () => {});
```
-49
View File
@@ -1,49 +0,0 @@
#### Fix comment formats in (#12860 by @HosokawaR)
This change fixes comments format in `exports` to algin with the comment format in `import`
Although this change does not affect the comments format in `import`, follows change log contains examples of comments in `import` for reference.
<!-- prettier-ignore -->
```jsx
// Input
export {
foo,
bar as // comment
baz,
}
import {
foo,
bar as // comment
baz,
} from 'foo'
// Prettier stable
export {
foo,
bar as baz, // comment
};
import {
foo,
// comment
bar as baz,
} from "foo";
// Prettier main
export {
foo,
// comment
bar as baz,
};
import {
foo,
// comment
bar as baz,
} from "foo";
```
-13
View File
@@ -1,13 +0,0 @@
#### Print `as` instead of `:` for `babel-ts` parser (#12706 by @HosokawaR)
<!-- prettier-ignore -->
```tsx
// Input
[x as any] = x;
// Prettier stable
[x: any] = x;
// Prettier main
[x as any] = x;
```
-37
View File
@@ -1,37 +0,0 @@
#### [HIGHLIGHT]Support TypeScript 4.7 (#12896, #12897, #12898, #12900, #12921, #12924, #12959 by @sosukesuzuki)
Support [TypeScript 4.7](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7) new features!
##### [Instantiation Expressions](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#instantiation-expressions)
```ts
interface Box<T> {
value: T;
}
function makeBox<T>(value: T) {
return { value };
}
const makeHammerBox = makeBox<Hammer>;
const makeWrenchBox = makeBox<Wrench>;
```
##### [Optional Variance](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#optional-variance-annotations-for-type-parameters)
```ts
interface Animal {
animalStuff: any;
}
interface Dog extends Animal {
dogStuff: any;
}
type Getter<out T> = () => T;
type Setter<in T> = (value: T) => void;
```
##### [`extends` constraints for `infer`](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#extends-constraints-on-infer-type-variables)
```ts
type FirstString<T> = T extends [infer extends string, ...unknown[]]
? S
: never;
```
-19
View File
@@ -1,19 +0,0 @@
#### fix formatting for typescript Enum with computed members (#12930 by @HosokawaR)
<!-- prettier-ignore -->
```tsx
// Input
enum A {
[i++],
}
// Prettier stable
enum A {
i++,
}
// Prettier main
enum A {
[i++],
}
```
-31
View File
@@ -1,31 +0,0 @@
#### Allow formatting for Vue template expression written in TypeScript (#12584 by @sosukesuzuki)
<!-- prettier-ignore -->
```vue
<!-- input -->
<script setup lang="ts">
let x: string | number = 1
</script>
<template>
{{ (x as number).toFixed(2) }}
</template>
<!-- Prettier stable -->
<script setup lang="ts">
let x: string | number = 1
</script>
<template>
{{ (x as number).toFixed(2) }}
</template>
<!-- Prettier main -->
<script setup lang="ts">
let x: string | number = 1;
</script>
<template>
{{ (x as number).toFixed(2) }}
</template>
```
-3
View File
@@ -1,3 +0,0 @@
#### Infer Stylus parser for Vue SFC style block (#12707 by @lsdsjy)
`<style lang="stylus">` blocks in Vue SFCs can be processed by the appropriate plugin if any.
-19
View File
@@ -1,19 +0,0 @@
#### Avoid printing attribute per line in Vue SFC blocks (#12895 by @sosukesuzuki)
<!-- prettier-ignore -->
```vue
<!-- Input (singleAttributePerLine: true) -->
<script lang="ts" setup>
</script>
<!-- Prettier stable -->
<script
lang="ts"
setup
>
</script>
<!-- Prettier main -->
<script lang="ts" setup>
</script>
```
+1
View File
@@ -292,6 +292,7 @@
"sandhose",
"Sapegin",
"sbdchd",
"sdbm",
"scandir",
"Serializers",
"setlocal",
+14 -14
View File
@@ -109,20 +109,20 @@ The support information looks like this:
```typescript
{
languages: Array<{
name: string,
since?: string,
parsers: string[],
group?: string,
tmScope?: string,
aceMode?: string,
codemirrorMode?: string,
codemirrorMimeType?: string,
aliases?: string[],
extensions?: string[],
filenames?: string[],
linguistLanguageId?: number,
vscodeLanguageIds?: string[],
}>
name: string;
since?: string;
parsers: string[];
group?: string;
tmScope?: string;
aceMode?: string;
codemirrorMode?: string;
codemirrorMimeType?: string;
aliases?: string[];
extensions?: string[];
filenames?: string[];
linguistLanguageId?: number;
vscodeLanguageIds?: string[];
}>;
}
```
+13 -13
View File
@@ -32,8 +32,8 @@ See below for examples.
### Global
```html
<script src="https://unpkg.com/prettier@2.6.2/standalone.js"></script>
<script src="https://unpkg.com/prettier@2.6.2/parser-graphql.js"></script>
<script src="https://unpkg.com/prettier@2.7.1/standalone.js"></script>
<script src="https://unpkg.com/prettier@2.7.1/parser-graphql.js"></script>
<script>
prettier.format("type Query { hello: String }", {
parser: "graphql",
@@ -48,8 +48,8 @@ Note that the [`unpkg` field](https://unpkg.com/#examples) in Prettier’s `pack
```html
<script type="module">
import * as prettier from "https://unpkg.com/prettier@2.6.2/esm/standalone.mjs";
import parserGraphql from "https://unpkg.com/prettier@2.6.2/esm/parser-graphql.mjs";
import * as prettier from "https://unpkg.com/prettier@2.7.1/esm/standalone.mjs";
import parserGraphql from "https://unpkg.com/prettier@2.7.1/esm/parser-graphql.mjs";
prettier.format("type Query { hello: String }", {
parser: "graphql",
@@ -62,8 +62,8 @@ Note that the [`unpkg` field](https://unpkg.com/#examples) in Prettier’s `pack
```js
define([
"https://unpkg.com/prettier@2.6.2/standalone.js",
"https://unpkg.com/prettier@2.6.2/parser-graphql.js",
"https://unpkg.com/prettier@2.7.1/standalone.js",
"https://unpkg.com/prettier@2.7.1/parser-graphql.js",
], (prettier, ...plugins) => {
prettier.format("type Query { hello: String }", {
parser: "graphql",
@@ -88,8 +88,8 @@ This syntax doesn’t necessarily work in the browser, but it can be used when b
### Worker
```js
importScripts("https://unpkg.com/prettier@2.6.2/standalone.js");
importScripts("https://unpkg.com/prettier@2.6.2/parser-graphql.js");
importScripts("https://unpkg.com/prettier@2.7.1/standalone.js");
importScripts("https://unpkg.com/prettier@2.7.1/parser-graphql.js");
prettier.format("type Query { hello: String }", {
parser: "graphql",
plugins: prettierPlugins,
@@ -102,8 +102,8 @@ If you want to format [embedded code](options.md#embedded-language-formatting),
```html
<script type="module">
import * as prettier from "https://unpkg.com/prettier@2.6.2/esm/standalone.mjs";
import parserBabel from "https://unpkg.com/prettier@2.6.2/esm/parser-babel.mjs";
import * as prettier from "https://unpkg.com/prettier@2.7.1/esm/standalone.mjs";
import parserBabel from "https://unpkg.com/prettier@2.7.1/esm/parser-babel.mjs";
console.log(
prettier.format("const html=/* HTML */ `<DIV> </DIV>`", {
@@ -119,9 +119,9 @@ The HTML code embedded in JavaScript stays unformatted because the `html` parser
```html
<script type="module">
import * as prettier from "https://unpkg.com/prettier@2.6.2/esm/standalone.mjs";
import parserBabel from "https://unpkg.com/prettier@2.6.2/esm/parser-babel.mjs";
import parserHtml from "https://unpkg.com/prettier@2.6.2/esm/parser-html.mjs";
import * as prettier from "https://unpkg.com/prettier@2.7.1/esm/standalone.mjs";
import parserBabel from "https://unpkg.com/prettier@2.7.1/esm/parser-babel.mjs";
import parserHtml from "https://unpkg.com/prettier@2.7.1/esm/parser-html.mjs";
console.log(
prettier.format("const html=/* HTML */ `<DIV> </DIV>`", {
+32
View File
@@ -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
```
+11 -5
View File
@@ -1,6 +1,6 @@
{
"name": "prettier",
"version": "2.7.0-dev",
"version": "2.8.0-dev",
"description": "Prettier is an opinionated code formatter",
"bin": "./bin/prettier.cjs",
"repository": "prettier/prettier",
@@ -30,7 +30,7 @@
"@typescript-eslint/typescript-estree": "5.27.0",
"acorn": "8.7.1",
"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",
@@ -47,8 +47,10 @@
"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.178.1",
"flow-parser": "0.180.0",
"get-stdin": "8.0.0",
"graphql": "16.5.0",
"html-element-attributes": "3.1.0",
@@ -60,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",
@@ -80,6 +82,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",
@@ -95,8 +98,11 @@
"@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": "28.1.1",
"@typescript-eslint/eslint-plugin": "5.26.0",
"babel-jest": "27.5.1",
"benchmark": "2.1.4",
"browserslist-to-esbuild": "1.1.1",
"c8": "7.11.3",
@@ -126,7 +132,7 @@
"node-fetch": "3.2.4",
"npm-run-all": "4.1.5",
"path-browserify": "1.0.1",
"prettier": "2.6.2",
"prettier": "2.7.1",
"pretty-bytes": "6.0.0",
"rimraf": "3.0.2",
"rollup-plugin-license": "2.8.1",
+9 -4
View File
@@ -14,10 +14,15 @@ async function run() {
const { runGit, readJson } = await import("./utils.js");
const params = minimist(process.argv.slice(2), {
string: ["version"],
boolean: ["dry", "manual"],
string: ["version", "repo"],
boolean: ["dry", "manual", "skip-dependencies-install"],
alias: { v: "version" },
default: { manual: false },
default: {
manual: false,
dry: false,
"skip-dependencies-install": false,
repo: "git@github.com:prettier/prettier.git",
},
});
const { stdout: previousVersion } = await runGit([
@@ -39,7 +44,7 @@ async function run() {
[
"./steps/validate-new-version.js",
"./steps/check-git-status.js",
"./steps/install-dependencies.js",
!params["skip-dependencies-install"] && "./steps/install-dependencies.js",
params.manual && "./steps/run-tests.js",
"./steps/update-version.js",
params.manual && "./steps/generate-bundles.js",
+3 -3
View File
@@ -7,7 +7,7 @@ async function format() {
await runYarn(["lint:prettier", "--write"]);
}
async function commit(version) {
async function commit({ version, repo }) {
await runGit(["commit", "-am", `Bump Prettier dependency to ${version}`]);
// Add rev to `.git-blame-ignore-revs` file
@@ -19,7 +19,7 @@ async function commit(version) {
fs.writeFileSync(file, text);
await runGit(["commit", "-am", `Git blame ignore ${version}`]);
await runGit(["push"]);
await runGit(["push", "--repo", repo]);
}
async function bump({
@@ -50,5 +50,5 @@ export default async function bumpPrettier(params) {
await logPromise("Updating files", format());
await logPromise("Bump default branch version", bump(params));
await logPromise("Committing changed files", commit(version));
await logPromise("Committing changed files", commit(params));
}
+3 -3
View File
@@ -1,10 +1,10 @@
import { runGit, logPromise } from "../utils.js";
async function pushGit({ version }) {
async function pushGit({ version, repo }) {
await runGit(["commit", "-am", `Release ${version}`]);
await runGit(["tag", "-a", version, "-m", `Release ${version}`]);
await runGit(["push"]);
await runGit(["push", "--tags"]);
await runGit(["push", "--repo", repo]);
await runGit(["push", "--tags", "--repo", repo]);
}
export default function pushToGit(params) {
@@ -49,6 +49,17 @@ export default async function waitForBotRelease({ dry, version }) {
await waitForEnter();
}
console.log(
outdentString(chalk/* indent */ `
1. Go to {green.underline https://www.npmjs.com/package/prettier/access}
2. Make sure "{yellow Publishing access}" section is set to "{yellow Require two-factor authentication or automation tokens}".
Press ENTER to continue.
`)
);
await waitForEnter();
console.log(
outdentString(chalk/* indent */ `
1. Go to {green.underline https://github.com/prettier/release-workflow/actions/workflows/release.yml}
+19
View File
@@ -69,6 +69,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,
+1 -17
View File
@@ -1,6 +1,6 @@
import path from "node:path";
import { promises as fs } from "node:fs";
import fastGlob from "fast-glob";
import {statSafe} from "./utils.js"
/** @typedef {import('./context').Context} Context */
@@ -171,22 +171,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<import('fs').Stats | undefined>} 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
+19
View File
@@ -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;
+96
View File
@@ -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;
+46 -1
View File
@@ -7,6 +7,9 @@ import { createIgnorer, errors } from "./prettier-internal.js";
import { expandPatterns, fixWindowsSlashes } from "./expand-patterns.js";
import getOptionsForFile from "./options/get-options-for-file.js";
import isTTY from "./is-tty.js";
import findCacheFile from "./find-cache-file.js";
import FormatResultsCache from "./format-results-cache.js";
import { statSafe } from "./utils.js";
const { getStdin } = thirdParty;
@@ -301,6 +304,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);
@@ -368,17 +391,32 @@ async function formatFiles(context) {
const start = Date.now();
const isCacheExists = formatResultsCache?.existsAvailableFormatResultsCache(
filename,
options
);
let result;
let output;
try {
<<<<<<< HEAD
result = await format(context, input, options);
=======
if (isCacheExists) {
result = { formatted: input };
} else {
result = format(context, input, options);
}
>>>>>>> main
output = result.formatted;
} catch (error) {
handleError(context, filename, error, printedFilename);
continue;
}
formatResultsCache?.setFormatResultsCache(filename, options);
const isDifferent = output !== input;
if (printedFilename) {
@@ -414,7 +452,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 */
@@ -437,6 +480,8 @@ async function formatFiles(context) {
}
}
formatResultsCache?.reconcile();
// Print check summary based on expected exit code
if (context.argv.check) {
if (numberOfUnformattedFilesFound === 0) {
+4
View File
@@ -93,6 +93,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);
+30
View File
@@ -2,6 +2,11 @@ import { __internal as sharedWithCli } from "../index.js";
const { arrayify, getLast, isNonEmptyArray, partition } = sharedWithCli.utils;
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);
@@ -40,6 +45,30 @@ function pick(object, keys) {
return Object.fromEntries(entries);
}
/**
* @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<import('fs').Stats | undefined>} The stats.
*/
async function statSafe(filePath) {
try {
return await fs.stat(filePath);
} catch (error) {
/* istanbul ignore next */
if (error.code !== "ENOENT") {
throw error;
}
}
}
export {
arrayify,
getLast,
@@ -48,4 +77,5 @@ export {
printToScreen,
groupBy,
pick,
createHash, statSafe
};
+5 -4
View File
@@ -134,10 +134,11 @@ function genericPrint(path, options, print) {
lines[0] = lines[0].trim();
}
return join(
hardline,
['"""', ...(lines.length > 0 ? lines : []), '"""'].filter(Boolean)
);
if (lines.every((line) => line === "")) {
lines.length = 0;
}
return join(hardline, ['"""', ...lines, '"""']);
}
return [
'"',
-1
View File
@@ -64,7 +64,6 @@ const languages = [
since: "1.4.0",
parsers: ["typescript", "babel-ts"],
vscodeLanguageIds: ["typescript"],
extensions: [...data.extensions, ".mts", ".cts"],
})),
createLanguage(linguistLanguages.TSX, () => ({
since: "1.4.0",
@@ -4,6 +4,7 @@ import isTypeCastComment from "../../utils/is-type-cast-comment.js";
import getLast from "../../../utils/get-last.js";
import visitNode from "./visit-node.js";
import { throwErrorForInvalidNodes } from "./typescript.js";
import throwSyntaxError from "./throw-syntax-error.js";
function postprocess(ast, options) {
if (
@@ -100,6 +101,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);
@@ -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;
@@ -1,13 +1,5 @@
import createError from "../../../common/parser-create-error.js";
import visitNode from "./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 },
});
}
import throwSyntaxError from "./throw-syntax-error.js";
// Invalid decorators are removed since `@typescript-eslint/typescript-estree` v4
// https://github.com/typescript-eslint/typescript-eslint/pull/2375
@@ -71,6 +63,7 @@ function throwErrorForInvalidNodes(ast, options) {
if (esTreeNode !== node) {
return;
}
throwErrorForInvalidDecorator(tsNode, esTreeNode, tsNodeToESTreeNodeMap);
throwErrorForInvalidAbstractProperty(tsNode, esTreeNode);
});
-5
View File
@@ -51,11 +51,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;
@@ -0,0 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`basic.html format 1`] = `
====================================options=====================================
parsers: ["angular"]
printWidth: 80
| printWidth
=====================================input======================================
<ng-container *ngTemplateOutlet="someTmpl; context: {app}"></ng-container>
=====================================output=====================================
<ng-container *ngTemplateOutlet="someTmpl; context: { app }"></ng-container>
================================================================================
`;
@@ -0,0 +1 @@
<ng-container *ngTemplateOutlet="someTmpl; context: {app}"></ng-container>
@@ -0,0 +1 @@
run_spec(__dirname, ["angular"]);
@@ -32,6 +32,55 @@ type Person {name: String}
"""
type Person {name: String}
"""
Empty lines before
"""
type Person {name: String}
"""
Empty lines after
"""
type Person {name: String}
"""
Empty lines around
"""
type Person {name: String}
"""
First line
Second Line
"""
type Person {name: String}
"""
First line
Second Line
"""
type Person {name: String}
"""
First line
Second Line
"""
type Person {name: String}
=====================================output=====================================
"""
Customer
@@ -74,6 +123,54 @@ type Person {
name: String
}
"""
Empty lines before
"""
type Person {
name: String
}
"""
Empty lines after
"""
type Person {
name: String
}
"""
Empty lines around
"""
type Person {
name: String
}
"""
First line
Second Line
"""
type Person {
name: String
}
"""
First line
Second Line
"""
type Person {
name: String
}
"""
First line
Second Line
"""
type Person {
name: String
}
================================================================================
`;
@@ -23,3 +23,52 @@ type Person {name: String}
2
"""
type Person {name: String}
"""
Empty lines before
"""
type Person {name: String}
"""
Empty lines after
"""
type Person {name: String}
"""
Empty lines around
"""
type Person {name: String}
"""
First line
Second Line
"""
type Person {name: String}
"""
First line
Second Line
"""
type Person {name: String}
"""
First line
Second Line
"""
type Person {name: String}
@@ -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;
+1 -1
View File
@@ -1,6 +1,6 @@
run_spec(import.meta, ["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"],
@@ -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 |"
@@ -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() {}
| ^^^^^^"
`;
@@ -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,) })
| ^^^^^^^^^^^^"
`;
@@ -1,7 +1,13 @@
run_spec(
{
importMeta: import.meta,
snippets: ["for each (a in b) {}", "class switch() {}"],
snippets: [
"for each (a in b) {}",
"class switch() {}",
"({ method() })",
"({ method({}) })",
"({ method(parameter,) })",
],
},
[
"babel",
@@ -139,6 +139,10 @@ Editor options:
Other options:
--cache Only format changed files. Cannot use with --stdin-filepath.
Defaults to false.
--cache-strategy <metadata|content>
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 <metadata|content>
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.
@@ -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 <metadata|content>
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`] = `
+373
View File
@@ -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();
});
});
});
+3
View File
@@ -0,0 +1,3 @@
function a() {
console.log("this is a.js")
}
+3
View File
@@ -0,0 +1,3 @@
function b() {
console.log("this is b.js");
}
+3
View File
@@ -0,0 +1,3 @@
{
"description": "this file is needed for testing to create default cache file"
}
+3
View File
@@ -0,0 +1,3 @@
// This file is generated automatically.
export {default} from "sdbm";
export * from "sdbm";
+35
View File
@@ -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 = {});
+531
View File
@@ -0,0 +1,531 @@
---
author: "Sosuke Suzuki (@sosukesuzuki)"
authorURL: "https://github.com/sosukesuzuki"
title: "Prettier 2.7: new --cache CLI option and TypeScript 4.7 syntax!"
---
This release includes a new `--cache` CLI option. Enabling this option will use some attributes as cache keys and format files only if they have changed. This could dramatically improve CLI performance.
We've also added support formatting for TypeScript 4.7 syntax!
If you enjoy Prettier and would like to support our work, consider sponsoring us directly via [our OpenCollective](https://opencollective.com/prettier) or by sponsoring the projects we depend on, including [typescript-eslint](https://opencollective.com/typescript-eslint), [remark](https://opencollective.com/unified), and [Babel](https://opencollective.com/babel).
<!--truncate-->
## Highlights
### TypeScript
#### Support TypeScript 4.7 ([#12896](https://github.com/prettier/prettier/pull/12896), [#12897](https://github.com/prettier/prettier/pull/12897), [#12898](https://github.com/prettier/prettier/pull/12898), [#12900](https://github.com/prettier/prettier/pull/12900), [#12921](https://github.com/prettier/prettier/pull/12921), [#12924](https://github.com/prettier/prettier/pull/12924), [#12959](https://github.com/prettier/prettier/pull/12959) by [@sosukesuzuki](https://github.com/sosukesuzuki))
Support [TypeScript 4.7](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7) new features!
##### [Instantiation Expressions](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#instantiation-expressions)
<!-- prettier-ignore -->
```ts
interface Box<T> {
value: T;
}
function makeBox<T>(value: T) {
return { value };
}
const makeHammerBox = makeBox<Hammer>;
const makeWrenchBox = makeBox<Wrench>;
```
##### [Optional Variance Annotations](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#optional-variance-annotations-for-type-parameters)
<!-- prettier-ignore -->
```ts
interface Animal {
animalStuff: any;
}
interface Dog extends Animal {
dogStuff: any;
}
type Getter<out T> = () => T;
type Setter<in T> = (value: T) => void;
```
##### [`extends` constraints for `infer`](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#extends-constraints-on-infer-type-variables)
<!-- prettier-ignore -->
```ts
type FirstString<T> = T extends [infer S extends string, ...unknown[]]
? S
: never;
```
### CLI
#### Add `--cache` and `--cache-strategy` CLI options ([#12800](https://github.com/prettier/prettier/pull/12800) by [@sosukesuzuki](https://github.com/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 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.
- 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
```
## Other Changes
### JavaScript
#### Preserve blank line between export specifiers ([#12746](https://github.com/prettier/prettier/pull/12746) by [@sosukesuzuki](https://github.com/sosukesuzuki))
<!-- prettier-ignore -->
```jsx
// Input
export {
// a
foo1,
// b
bar1,
baz1,
} from "mod";
// Prettier 2.6
export {
// a
foo1,
// b
bar1,
baz1,
} from "mod";
// Prettier 2.7
export {
// a
foo1,
// b
bar1,
baz1,
} from "mod";
```
#### Make more callee patterns recognized as "test call callee". ([#12779](https://github.com/prettier/prettier/pull/12779) by [@HosokawaR](https://github.com/HosokawaR))
Supports test calls like [Playwright `test.describe`](https://playwright.dev/docs/api/class-test#test-describe).
<!-- prettier-ignore -->
```jsx
// Input
test.step("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe.only("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe.parallel("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe.parallel.only("does something really long and complicated so I have to write a very long name for the testThis is a very", () => {});
test.describe.serial("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe.serial.only("does something really long and complicated so I have to write a very long name for the test", () => {});
// Prettier 2.6
test.step(
"does something really long and complicated so I have to write a very long name for the test",
() => {}
);
test.describe(
"does something really long and complicated so I have to write a very long name for the test",
() => {}
);
test.describe.only(
"does something really long and complicated so I have to write a very long name for the test",
() => {}
);
test.describe.parallel(
"does something really long and complicated so I have to write a very long name for the test",
() => {}
);
test.describe.parallel.only(
"does something really long and complicated so I have to write a very long name for the testThis is a very",
() => {}
);
test.describe.serial(
"does something really long and complicated so I have to write a very long name for the test",
() => {}
);
test.describe.serial.only(
"does something really long and complicated so I have to write a very long name for the test",
() => {}
);
// Prettier 2.7
test.step("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe
.only("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe
.parallel("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe.parallel
.only("does something really long and complicated so I have to write a very long name for the testThis is a very", () => {});
test.describe
.serial("does something really long and complicated so I have to write a very long name for the test", () => {});
test.describe.serial
.only("does something really long and complicated so I have to write a very long name for the test", () => {});
```
#### Fix comment formats in ([#12860](https://github.com/prettier/prettier/pull/12860) by [@HosokawaR](https://github.com/HosokawaR))
This change fixes the comment format in `exports` to align with the comment format in `import`.
Although this change does not affect the comments format in `import`, follows change log contains examples of comments in `import` for reference.
<!-- prettier-ignore -->
```jsx
// Input
export {
foo,
bar as // comment
baz,
}
import {
foo,
bar as // comment
baz,
} from 'foo'
// Prettier 2.6
export {
foo,
bar as baz, // comment
};
import {
foo,
// comment
bar as baz,
} from "foo";
// Prettier 2.7
export {
foo,
// comment
bar as baz,
};
import {
foo,
// comment
bar as baz,
} from "foo";
```
### TypeScript
#### Print `as` instead of `:` for `babel-ts` parser ([#12706](https://github.com/prettier/prettier/pull/12706) by [@HosokawaR](https://github.com/HosokawaR))
<!-- prettier-ignore -->
```tsx
// Input
[x as any] = x;
// Prettier 2.6
[x: any] = x;
// Prettier 2.7
[x as any] = x;
```
#### Fix formatting for typescript Enum with computed members ([#12930](https://github.com/prettier/prettier/pull/12930) by [@HosokawaR](https://github.com/HosokawaR))
<!-- prettier-ignore -->
```tsx
// Input
enum A {
[i++],
}
// Prettier 2.6
enum A {
i++,
}
// Prettier 2.7
enum A {
[i++],
}
```
#### Stop parsing invalid code ([#12982](https://github.com/prettier/prettier/pull/12982) by [@fisker](https://github.com/fisker))
<!-- prettier-ignore -->
```tsx
// Input
const object = ({ methodName() });
// Prettier 2.6
const object = { methodName(); };
// Prettier 2.7
SyntaxError: Unexpected token. (1:29)
> 1 | const object = ({ methodName() });
| ^^
```
### HTML
#### Support Speculation Rules API formatting in HTML ([#12882](https://github.com/prettier/prettier/pull/12882) by [@sosukesuzuki](https://github.com/sosukesuzuki))
Please read https://web.dev/speculative-prerendering/ for more information about the Speculation Rules API.
<!-- prettier-ignore -->
```html
<!-- Input -->
<script type="speculationrules">
{
"prerender": [
{"source": "list", "urls": ["https://a.test/foo"]}
]
}
</script>
<!-- Prettier 2.6 -->
<script type="speculationrules">
{
"prerender": [
{"source": "list", "urls": ["https://a.test/foo"]}
]
}
</script>
<!-- Prettier 2.7 -->
<script type="speculationrules">
{
"prerender": [{ "source": "list", "urls": ["https://a.test/foo"] }]
}
</script>
```
### Vue
#### Allow formatting for Vue template expression written in TypeScript ([#12584](https://github.com/prettier/prettier/pull/12584) by [@sosukesuzuki](https://github.com/sosukesuzuki))
<!-- prettier-ignore -->
```vue
<!-- input -->
<script setup lang="ts">
let x: string | number = 1
</script>
<template>
{{ (x as number).toFixed(2) }}
</template>
<!-- Prettier 2.6 -->
<script setup lang="ts">
let x: string | number = 1
</script>
<template>
{{ (x as number).toFixed(2) }}
</template>
<!-- Prettier 2.7 -->
<script setup lang="ts">
let x: string | number = 1;
</script>
<template>
{{ (x as number).toFixed(2) }}
</template>
```
#### Infer Stylus parser for Vue SFC style block ([#12707](https://github.com/prettier/prettier/pull/12707) by [@lsdsjy](https://github.com/lsdsjy))
`<style lang="stylus">` blocks in Vue SFCs can be processed by the appropriate plugin if any.
#### Avoid printing attribute per line in Vue SFC blocks ([#12895](https://github.com/prettier/prettier/pull/12895) by [@sosukesuzuki](https://github.com/sosukesuzuki))
<!-- prettier-ignore -->
```vue
<!-- Input (singleAttributePerLine: true) -->
<script lang="ts" setup>
</script>
<!-- Prettier 2.6 -->
<script
lang="ts"
setup
>
</script>
<!-- Prettier 2.7 -->
<script lang="ts" setup>
</script>
```
### Angular
#### Fix shorthand properties formatting ([#12993](https://github.com/prettier/prettier/pull/12993) by [@sosukesuzuki](https://github.com/sosukesuzuki), [@fisker](https://github.com/fisker))
```html
<!-- Input -->
<ng-container *ngTemplateOutlet="someTmpl; context: { app }"></ng-container>
<!-- Prettier 2.6 -->
<ng-container
*ngTemplateOutlet="someTmpl; context: { app: this.app }"
></ng-container>
<!-- Prettier 2.7 -->
<ng-container *ngTemplateOutlet="someTmpl; context: { app }"></ng-container>
```
### GraphQL
#### Add support for printing SchemaExtension nodes ([#12519](https://github.com/prettier/prettier/pull/12519) by [@trevor-scheer](https://github.com/trevor-scheer))
<!-- prettier-ignore -->
```graphql
# Input
extend schema { subscription: Subscription }
extend schema @directive
# Prettier 2.6
N/A - throws error
# Prettier 2.7
extend schema {
subscription: Subscription
}
extend schema @directive
```
#### Fix single line and empty description formatting ([#12608](https://github.com/prettier/prettier/pull/12608) by [@chimurai](https://github.com/chimurai), [@fisker](https://github.com/fisker))
<!-- prettier-ignore -->
```graphql
# Input
""" Customer """
type Person {
name: String
}
""""""
type Person {
name: String
}
# Prettier 2.6.2
"""
Customer
"""
type Person {
name: String
}
"""
"""
type Person {
name: String
}
# Prettier 2.6.2 (second format)
"""
Customer
"""
type Person {
name: String
}
"""
"""
type Person {
name: String
}
# Prettier 2.7
"""
Customer
"""
type Person {
name: String
}
"""
"""
type Person {
name: String
}
```
### CLI
#### Print the number of files need change ([#12561](https://github.com/prettier/prettier/pull/12561) by [@Harry-Hopkinson](https://github.com/Harry-Hopkinson))
```console
# Prettier 2.6
$ prettier src --check
Checking formatting...
[warn] src/fileA.js
[warn] Code style issues found in the above file(s). Forgot to run Prettier?
# Prettier 2.7
$ prettier src --check
Checking formatting...
[warn] src/fileA.js
[warn] src/fileB.js
[warn] Code style issues found in 2 files. Forgot to run Prettier?
$ prettier src --check
Checking formatting...
[warn] src/fileA.js
[warn] Code style issues found in the above file. Forgot to run Prettier?
```
#### Infer parser for `.importmap` files ([#12603](https://github.com/prettier/prettier/pull/12603) by [@fisker](https://github.com/fisker))
Format [`.importmap`](https://github.com/WICG/import-maps#import-map-processing) files as JSON files.
#### Simplify performance test ([#12682](https://github.com/prettier/prettier/pull/12682), [#12698](https://github.com/prettier/prettier/pull/12698) by [@fisker](https://github.com/fisker))
When `--debug-benchmark` or `--debug-repeat` is passed:
1. The CLI skips print code to the screen or write file
1. Set log level to `debug` automatically
+2 -2
View File
@@ -320,7 +320,7 @@ const UsersSection = ({ language }) => {
<div style={{ marginLeft: ".7em", width: "260px" }}>
<p>
More than{" "}
<strong data-placeholder="dependent-github">4 million</strong>{" "}
<strong data-placeholder="dependent-github">4.3 million</strong>{" "}
dependent repositories on GitHub
</p>
<Button href="https://github.com/prettier/prettier/network/dependents">
@@ -336,7 +336,7 @@ const UsersSection = ({ language }) => {
<div style={{ marginLeft: ".7em", width: "260px" }}>
<p>
More than{" "}
<strong data-placeholder="dependent-npm">10.6k</strong>{" "}
<strong data-placeholder="dependent-npm">11.3k</strong>{" "}
dependent packages on npm
</p>
<Button href="https://www.npmjs.com/browse/depended/prettier">
+24
View File
@@ -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<Obj>} 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;
}
+14 -14
View File
@@ -116,20 +116,20 @@ The support information looks like this:
```typescript
{
languages: Array<{
name: string,
since?: string,
parsers: string[],
group?: string,
tmScope?: string,
aceMode?: string,
codemirrorMode?: string,
codemirrorMimeType?: string,
aliases?: string[],
extensions?: string[],
filenames?: string[],
linguistLanguageId?: number,
vscodeLanguageIds?: string[],
}>
name: string;
since?: string;
parsers: string[];
group?: string;
tmScope?: string;
aceMode?: string;
codemirrorMode?: string;
codemirrorMimeType?: string;
aliases?: string[];
extensions?: string[];
filenames?: string[];
linguistLanguageId?: number;
vscodeLanguageIds?: string[];
}>;
}
```
@@ -33,8 +33,8 @@ See below for examples.
### Global
```html
<script src="https://unpkg.com/prettier@2.6.2/standalone.js"></script>
<script src="https://unpkg.com/prettier@2.6.2/parser-graphql.js"></script>
<script src="https://unpkg.com/prettier@2.7.1/standalone.js"></script>
<script src="https://unpkg.com/prettier@2.7.1/parser-graphql.js"></script>
<script>
prettier.format("type Query { hello: String }", {
parser: "graphql",
@@ -49,8 +49,8 @@ Note that the [`unpkg` field](https://unpkg.com/#examples) in Prettier’s `pack
```html
<script type="module">
import prettier from "https://unpkg.com/prettier@2.6.2/esm/standalone.mjs";
import parserGraphql from "https://unpkg.com/prettier@2.6.2/esm/parser-graphql.mjs";
import prettier from "https://unpkg.com/prettier@2.7.1/esm/standalone.mjs";
import parserGraphql from "https://unpkg.com/prettier@2.7.1/esm/parser-graphql.mjs";
prettier.format("type Query { hello: String }", {
parser: "graphql",
@@ -63,8 +63,8 @@ Note that the [`unpkg` field](https://unpkg.com/#examples) in Prettier’s `pack
```js
define([
"https://unpkg.com/prettier@2.6.2/standalone.js",
"https://unpkg.com/prettier@2.6.2/parser-graphql.js",
"https://unpkg.com/prettier@2.7.1/standalone.js",
"https://unpkg.com/prettier@2.7.1/parser-graphql.js",
], (prettier, ...plugins) => {
prettier.format("type Query { hello: String }", {
parser: "graphql",
@@ -89,8 +89,8 @@ This syntax doesn’t necessarily work in the browser, but it can be used when b
### Worker
```js
importScripts("https://unpkg.com/prettier@2.6.2/standalone.js");
importScripts("https://unpkg.com/prettier@2.6.2/parser-graphql.js");
importScripts("https://unpkg.com/prettier@2.7.1/standalone.js");
importScripts("https://unpkg.com/prettier@2.7.1/parser-graphql.js");
prettier.format("type Query { hello: String }", {
parser: "graphql",
plugins: prettierPlugins,
@@ -103,8 +103,8 @@ If you want to format [embedded code](options.md#embedded-language-formatting),
```html
<script type="module">
import prettier from "https://unpkg.com/prettier@2.6.2/esm/standalone.mjs";
import parserBabel from "https://unpkg.com/prettier@2.6.2/esm/parser-babel.mjs";
import prettier from "https://unpkg.com/prettier@2.7.1/esm/standalone.mjs";
import parserBabel from "https://unpkg.com/prettier@2.7.1/esm/parser-babel.mjs";
console.log(
prettier.format("const html=/* HTML */ `<DIV> </DIV>`", {
@@ -120,9 +120,9 @@ The HTML code embedded in JavaScript stays unformatted because the `html` parser
```html
<script type="module">
import prettier from "https://unpkg.com/prettier@2.6.2/esm/standalone.mjs";
import parserBabel from "https://unpkg.com/prettier@2.6.2/esm/parser-babel.mjs";
import parserHtml from "https://unpkg.com/prettier@2.6.2/esm/parser-html.mjs";
import prettier from "https://unpkg.com/prettier@2.7.1/esm/standalone.mjs";
import parserBabel from "https://unpkg.com/prettier@2.7.1/esm/parser-babel.mjs";
import parserHtml from "https://unpkg.com/prettier@2.7.1/esm/parser-html.mjs";
console.log(
prettier.format("const html=/* HTML */ `<DIV> </DIV>`", {
@@ -205,3 +205,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
```
@@ -177,9 +177,37 @@ If you’d like a JSON schema to validate your configuration, one is available h
## EditorConfig
If `options.editorconfig` is `true` and an [`.editorconfig` file](https://editorconfig.org/) is in your project, Prettier will parse it and convert its properties to the corresponding Prettier configuration. This configuration will be overridden by `.prettierrc`, etc. Currently, the following EditorConfig properties are supported:
If `options.editorconfig` is `true` and an [`.editorconfig` file](https://editorconfig.org/) is in your project, Prettier will parse it and convert its properties to the corresponding Prettier configuration. This configuration will be overridden by `.prettierrc`, etc.
- `end_of_line`
- `indent_style`
- `indent_size`/`tab_width`
- `max_line_length`
Here’s an annotated description of how different properties map to Prettier’s behavior:
```ini
# Stop the editor from looking for .editorconfig files in the parent directories
# root = true
[*]
# Non-configurable Prettier behaviors
charset = utf-8
insert_final_newline = true
# Caveat: Prettier won’t trim trailing whitespace inside template strings, but your editor might.
# trim_trailing_whitespace = true
# Configurable Prettier behaviors
# (change these if your Prettier config differs)
end_of_line = lf
indent_style = space
indent_size = 2
max_line_length = 80
```
Here’s a copy+paste-ready `.editorconfig` file if you use the default options:
```ini
[*]
charset = utf-8
insert_final_newline = true
end_of_line = lf
indent_style = space
indent_size = 2
max_line_length = 80
```
@@ -61,6 +61,7 @@ To turn off plugin autoloading, use `--no-plugin-search` when using Prettier CLI
- [`prettier-plugin-apex`](https://github.com/dangmai/prettier-plugin-apex) by [**@dangmai**](https://github.com/dangmai)
- [`prettier-plugin-elm`](https://github.com/gicentre/prettier-plugin-elm) by [**@giCentre**](https://github.com/gicentre)
- [`prettier-plugin-erb`](https://github.com/adamzapasnik/prettier-plugin-erb) by [**@adamzapasnik**](https://github.com/adamzapasnik)
- [`prettier-plugin-glsl`](https://github.com/NaridaL/glsl-language-toolkit/tree/main/packages/prettier-plugin-glsl) by [**@NaridaL**](https://github.com/NaridaL)
- [`prettier-plugin-go-template`](https://github.com/NiklasPor/prettier-plugin-go-template) by [**@NiklasPor**](https://github.com/NiklasPor)
- [`prettier-plugin-java`](https://github.com/jhipster/prettier-java) by [**@JHipster**](https://github.com/jhipster)
- [`prettier-plugin-jsonata`](https://github.com/Stedi/prettier-plugin-jsonata) by [**@Stedi**](https://github.com/Stedi)
+968 -741
View File
File diff suppressed because it is too large. Load diff