跳至主内容区

Prettier 2.2:新增 JavaScript 解析器、支持 TS 4.1 并发布 ESM 独立包

· 1 分钟阅读
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

本次发布支持了新的 JavaScript 解析器 espreemeriyah,支持 TypeScript 4.1,为现代浏览器提供了 ESM 独立包,并包含大量错误修复和改进!

重要更新

JavaScript

新增 espreemeriyah 解析器 (#9000, #9514 by @fisker)

parser 选项新增两个可选值:

TypeScript

支持 TypeScript 4.1 (#9473, #9636 by @sosukesuzuki)

映射类型中的键重映射
// Input
type MappedTypeWithNewKeys<T> = {
[K in keyof T as NewKeyType]: T[K]
};

// Prettier 2.1
SyntaxError: Unexpected token, expected "]" (2:17)
1 | type MappedTypeWithNewKeys<T> = {
> 2 | [K in keyof T as NewKeyType]: T[K]
| ^
3 | };

// Prettier 2.2
type MappedTypeWithNewKeys<T> = {
[K in keyof T as NewKeyType]: T[K]
};
模板字面量类型
// Input
type HelloWorld = `Hello, ${keyof World}`

// Prettier 2.1
SyntaxError: Unexpected token, expected "}" (1:35)
> 1 | type HelloWorld = `Hello, ${keyof World}`
| ^

// Prettier 2.2
type HelloWorld = `Hello, ${keyof World}`;

API

ESM 独立包 (#8983 by @Monchi, @fisker)

Prettier 现在提供了 ES 模块格式的独立包,可直接在现代浏览器中使用:

import prettier from "https://unpkg.com/prettier/esm/standalone.mjs";
import parserGraphql from "https://unpkg.com/prettier/esm/parser-graphql.mjs";

prettier.format("query { }", {
parser: "graphql",
plugins: [parserGraphql],
});

其他变更

JavaScript

保留嵌入 CSS 中模板值的间距 (#9078 by @sosukesuzuki)

// Input
const style = css`
width: ${size}${sizeUnit};
`;

// Prettier 2.1
const style = css`
width: ${size} ${sizeUnit};
`;

// Prettier 2.2
const style = css`
width: ${size}${sizeUnit};
`;

修复带嵌入语法的模板字面量中的注释位置 (#9278 by @fisker)

// Input
html`${
foo
/* comment */
}`;
html`
${
foo
/* comment */
}
`;
graphql`${
foo
/* comment */
}`;
css`${
foo
/* comment */
}`;

// Prettier 2.1
html`${foo}`;
/* comment */
html`
${foo}
/* comment */
`;
graphql`
${foo}
/* comment */
`;
css`
${foo}
/* comment */
`;


// Prettier 2.2
html`${
foo
/* comment */
}`;
html`
${
foo
/* comment */
}
`;
graphql`${
foo
/* comment */
}`;
css`${
foo
/* comment */
}`;

优化超类名称较长的类赋值格式 (#9341 by @sosukesuzuki)

此改进优化了 Google Closure Library 命名空间的格式。

// Input
aaaaaaaa.bbbbbbbb.cccccccc.dddddddd.eeeeeeee.ffffffff.gggggggg2 = class extends (
aaaaaaaa.bbbbbbbb.cccccccc.dddddddd.eeeeeeee.ffffffff.gggggggg1
) {
method () {
console.log("foo");
}
};

// Prettier 2.1
aaaaaaaa.bbbbbbbb.cccccccc.dddddddd.eeeeeeee.ffffffff.gggggggg2 = class extends aaaaaaaa
.bbbbbbbb.cccccccc.dddddddd.eeeeeeee.ffffffff.gggggggg1 {
method() {
console.log("foo");
}
};

// Prettier 2.2
aaaaaaaa.bbbbbbbb.cccccccc.dddddddd.eeeeeeee.ffffffff.gggggggg2 = class extends (
aaaaaaaa.bbbbbbbb.cccccccc.dddddddd.eeeeeeee.ffffffff.gggggggg1
) {
method() {
console.log("foo");
}
};

修复 while 语句主体前导注释的位置 (#9345 by @sosukesuzuki)

// Input
while(1) // Comment
foo();

// Prettier 2.1
while (
1 // Comment
)
foo();

// Prettier 2.2
while (1)
// Comment
foo();

更新至 @babel/parser 7.12 (#9408, #9476, #9597 by @sosukesuzuki)

已将 JavaScript 解析器更新至 @babel/parser 7.12,修复了若干错误并支持部分新语法。

支持导入断言 (Import Assertions)

2.1 版本支持的"模块属性"提案已发生重大变更,并更名为"导入断言"。

import foo from "./foo.json" assert { type: "json" };
支持使用字符串名称的导入导出
let happy = "happy";
export { happy as "😃" };
支持类静态块
class C {
static #x = 42;
static y;
static {
try {
this.y = doSomethingWith(this.#x);
} catch {
this.y = "unknown";
}
}
}

保留 HTML 和 Markdown 中无效的模板字面量 (#9431 by @fisker)

// Input
foo = html`<div>\u{prettier}</div>`;
foo = html`\u{prettier}${foo}pr\u{0065}ttier`;
foo = markdown`# \u{prettier}\u{0065}`;

// Prettier 2.1
foo = html``;
foo = html`null${foo}prettier`;
foo = markdown`
# \u{prettier}\u{0065}
`;

// Prettier 2.2
foo = html`<div>\u{prettier}</div>`;
foo = html`\u{prettier}${foo}pr\u{0065}ttier`;
foo = markdown`# \u{prettier}\u{0065}`;

修复 import {a as a}export {a as a} 的格式 (#9435 by @fisker)

// Input
import { a as a } from "a";
export { b as b } from "b";

// Prettier 2.1
import { a } from "a";
export { b } from "b";

// Prettier 2.2
import { a as a } from "a";
export { b as b } from "b";

修复 yield JSX 表达式的格式化 (#9650 by @brainkim)

// Input
function* f() {
yield <div>generator</div>
}
// Prettier 2.1
function* f() {
yield (<div>generator</div>);
}
// Prettier 2.2
function* f() {
yield <div>generator</div>;
}

在尾参包裹时展平函数表达式参数 (#9662 by @thorn0)

// Prettier 2.1
function* mySagas() {
yield effects.takeEvery(rexpress.actionTypes.REQUEST_START, function* ({
id
}) {
console.log(id);
yield rexpress.actions(store).writeHead(id, 400);
yield rexpress.actions(store).end(id, "pong");
console.log("pong");
});
}

// Prettier 2.2
function* mySagas() {
yield effects.takeEvery(
rexpress.actionTypes.REQUEST_START,
function* ({ id }) {
console.log(id);
yield rexpress.actions(store).writeHead(id, 400);
yield rexpress.actions(store).end(id, "pong");
console.log("pong");
}
);
}

修复 require(/* comment */) 导致的崩溃 (#9670 by @fisker)

// Input
require(/* comment */)

// Prettier 2.1
Error: Comment "comment" was not printed. Please report this error!

// Prettier 2.2
require(/* comment */);

TypeScript

保留被忽略对象类型和接口中的最后一个分隔符 (#9318 by @sosukesuzuki)

// Input
let x: {
// prettier-ignore
y: z;
};

// Prettier 2.1
let x: {
// prettier-ignore
y: z;;
};

// Prettier 2.2
let x: {
// prettier-ignore
y: z;
};

在对象字面量属性中的赋值语句添加括号 (#9484 by @fisker)

// Input
foo = { bar: (a = b) };

// Prettier 2.1
foo = { bar: a = b };

// Prettier 2.2
foo = { bar: (a = b) };

修复 typescriptflow 之间类型格式的不一致 (#9521 by @fisker)

// Input
const name: SomeGeneric<
Pick<Config, "ONE_LONG_PROP" | "ANOTHER_LONG_PROP">
> = null;

// Prettier 2.1 (--parser=typescript)
const name: SomeGeneric<Pick<
Config,
"ONE_LONG_PROP" | "ANOTHER_LONG_PROP"
>> = null;

// Prettier 2.1 (--parser=flow)
const name: SomeGeneric<
Pick<Config, "ONE_LONG_PROP" | "ANOTHER_LONG_PROP">
> = null;

// Prettier 2.2 (typescript and flow parser)
const name: SomeGeneric<
Pick<Config, "ONE_LONG_PROP" | "ANOTHER_LONG_PROP">
> = null;

修复 prettier-ignore 标记的映射类型 (#9551 by @fisker)

// Input
type a= {
// prettier-ignore
[A in B]: C | D
}

// Prettier 2.1
type a = {
// prettier-ignore
A in B: C | D;
};

// Prettier 2.2
type a = {
// prettier-ignore
[A in B]: C | D
};

Flow

检测到 @flow 指令时自动将 babel 解析器切换至 babel-flow (#9071 by @fisker)

只要您的 Flow 文件包含该指令,使用 .js 扩展名就是安全的。Prettier 将正确解析和格式化这些文件,无需额外配置。此前解析器能识别该指令,但在格式化上存在细微问题(例如在 Flow 中取消数字键的引号是不安全的)。

// Input (with --parser babel)
// @flow
f<T>({ "2": 2 })

// Prettier 2.1
// @flow
f<T>({ 2: 2 });

// Prettier 2.2
// @flow
f<T>({ "2": 2 });

含有未知成员的枚举 (#9432 by @gkz)

此前不支持此特性。现在,以下代码会被格式化:

// Input
enum E {
A,
B,
...
}

// Prettier 2.1: parse error

// Prettier 2.2
enum E {
A,
B,
...
}

this 参数注解 (#9457 by @dsainati1, #9489 by @fisker)

// Input
function f(this: string, a: number) {
}

type T = (this: boolean, a: number) => boolean;

// Prettier 2.1
function f(this: string, a: number) {}

type T = (a: number) => boolean;

// Prettier 2.2
function f(this: string, a: number) {
}

type T = (this: boolean, a: number) => boolean;

支持 BigIntLiteralTypeAnnotationBigIntTypeAnnotation (#9523 by @fisker)

在 Flow 中增加了对 BigIntLiteralTypeAnnotationBigIntTypeAnnotation 的支持。

// Input
const foo: bigint = 1n;
const bar: baz<1n> = 1n;

// Prettier 2.1
Error: unknown type: "BigIntTypeAnnotation"
at ...

// Prettier 2.2
const foo: bigint = 1n;
const bar: baz<1n> = 1n;

在泛型类型注解中将更多简单类型视为不换行 (#9543 by @fisker)

// Input
const foo1: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo<symbol> = a
const foo2: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo<"STRING"> = a;
const foo3: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo<0> = a;

// Prettier 2.1
const foo1: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo<
symbol
> = a;
const foo2: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo<
"STRING"
> = a;
const foo3: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo<
0
> = a;

// Prettier 2.2 (typescript and flow parser)
const foo1: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo<symbol> = a
const foo2: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo<"STRING"> = a;
const foo3: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo<0> = a;

修复 prettier-ignore 的类型断言周围缺失的括号 (#9553 by @fisker)

// Input
transform(
// prettier-ignore
(pointTransformer: (Point => Point))
);

// Prettier 2.1
transform(
// prettier-ignore
pointTransformer: (Point => Point)
);

// Prettier 2.2
transform(
// prettier-ignore
(pointTransformer: (Point => Point))
);

改进注释类型检测 (#9563 by @fisker)

// Input
foo/*::<bar>*/(baz);
class Foo {
bar( data: Array<string>) {}
}

// Prettier 2.1
foo/*:: <bar> */(baz);
class Foo {
bar(data: Array/*:: <string> */) {}
}

// Prettier 2.2
foo/*:: <bar> */(baz);
class Foo {
bar(data: Array<string>) {}
}

Less

修复值列表中的注释 (#9356 by @thorn0)

// Input
@test-space-separated: #aaaaaa // Start with A
#bbbbbb // then some B
#cccccc; // and round it out with C

// Prettier 2.1
@test-space-separated: #aaaaaa a // Start with
#bbbbbb b // then some
#cccccc; // and round it out with C

// Prettier 2.2
@test-space-separated: #aaaaaa // Start with A
#bbbbbb // then some B
#cccccc; // and round it out with C

HTML

修复读取 Node.sourceSpan 时崩溃的问题 (#9368 by @fisker)

<!-- Input -->
<strong>a</strong>-<strong>b</strong>-

<!-- Prettier 2.1 -->
TypeError: Cannot read property 'line' of undefined
at forceNextEmptyLine ...

<!-- Prettier 2.2 -->
<strong>a</strong>-<strong>b</strong>-

Vue

修复 v-for 的格式不一致问题 (#9225 by @zweimach)

<!-- Input -->
<template>
<div
v-for="({ longLongProp=42, anotherLongLongProp='Hello, World!' }, index) of longLongLongLongLongLongLongLongList"
></div>
<div
v-for="({firstValue, secondValue, thirdValue, fourthValue, fifthValue, sixthValue}, objectKey, index) in objectWithAVeryVeryVeryVeryLongName"
></div>
</template>

<!-- Prettier 2.1 -->
<template>
<div
v-for="({ longLongProp = 42, anotherLongLongProp = 'Hello, World!' },
index) of longLongLongLongLongLongLongLongList"
></div>
<div
v-for="({
firstValue,
secondValue,
thirdValue,
fourthValue,
fifthValue,
sixthValue,
},
objectKey,
index) in objectWithAVeryVeryVeryVeryLongName"
></div>
</template>

<!-- Prettier 2.2 -->
<template>
<div
v-for="(
{ longLongProp = 42, anotherLongLongProp = 'Hello, World!' }, index
) of longLongLongLongLongLongLongLongList"
></div>
<div
v-for="(
{
firstValue,
secondValue,
thirdValue,
fourthValue,
fifthValue,
sixthValue,
},
objectKey,
index
) in objectWithAVeryVeryVeryVeryLongName"
></div>
</template>

修复插槽的格式不一致问题,支持 Vue 3 的 script[setup]style[vars] (#9609 by @fisker)

<!-- Input -->
<script setup="props, {emit }"></script>
<style vars="{color }"></style>
<template>
<div>
<div v-slot="{destructuring:{ a:{b}}}"/>
<div v-slot:name="{destructuring:{ a:{b}}}"/>
<div #default="{destructuring:{ a:{b}}}"/>
<slot slot-scope="{destructuring:{ a:{b}}}"/>
</div>
</template>

<!-- Prettier 2.1 -->
<script setup="props, {emit }"></script>
<style vars="{color }"></style>
<template>
<div>
<div v-slot="{ destructuring: { a: { b } } }" />
<div v-slot:name="{ destructuring: { a: { b } } }" />
<div #default="{ destructuring: { a: { b } } }" />
<slot
slot-scope="{
destructuring: {
a: { b },
},
}"
/>
</div>
</template>

<!-- Prettier 2.2 -->
<script setup="props, { emit }"></script>
<style vars="{ color }"></style>
<template>
<div>
<div
v-slot="{
destructuring: {
a: { b },
},
}"
/>
<div
v-slot:name="{
destructuring: {
a: { b },
},
}"
/>
<div
#default="{
destructuring: {
a: { b },
},
}"
/>
<slot
slot-scope="{
destructuring: {
a: { b },
},
}"
/>
</div>
</template>

Handlebars (alpha)

修复 <Textarea /> 后换行不稳定的问题 (#9403 by @fisker,由 simple-html-tokenizer 中的修复 @rwjblue 提供)

// Input
<Textarea />
{{#if true}}
Test
{{/if}}

// Prettier 2.1
<Textarea />

{{#if true}}
Test
{{/if}}

// Prettier 2.1 (second format)
<Textarea />
{{#if true}}
Test
{{/if}}

// Prettier 2.2
<Textarea />
{{#if true}}
Test
{{/if}}

Markdown

将 remark-math 更新至 3.0.1;仅当原本已转义时才转义美元符号 (#7938 by @fisker@thorn0)

<!-- Input -->
Paragraph with $14 million.

Paragraph with $14 million. But if more $dollars on the same line...

<!-- Prettier 2.1 -->
Paragraph with \$14 million.

Paragraph with $14 million. But if more $dollars on the same line...

<!-- Prettier 2.2 -->
Paragraph with $14 million.

Paragraph with $14 million. But if more $dollars on the same line...

修复缺少闭合围栏的代码块中缺失空行的问题 (#8786 by @fisker)

require("prettier").format("```a\n\n\n\n", { parser: "markdown" });

<!-- Prettier 2.1 -->
'```a\n\n```\n'

<!-- Prettier 2.2 -->
'```a\n\n\n\n```\n'

新增对 [[wiki-style]] 链接的支持 (#9275 by @iamrecursion)

  1. 支持 [[wiki-style]] 链接确保格式化时不会因跨行换行而破坏链接结构

  2. 链接内容([[]] 括号内部分)将被视为原始文本处理。这是因为依赖 [[]] 语法的各类工具对其内部允许的内容格式尚未达成共识

<!-- Input -->
If I have a prose that forces a wiki link to end up crossing the [[line width limit]] like this. It's wrapped into an invalid state.

<!-- Prettier 2.1 -->
If I have a prose that forces a wiki link to end up crossing the [[line width
limit]] like this. It's wrapped into an invalid state.

<!-- Prettier 2.2 -->
If I have a prose that forces a wiki link to end up crossing the
[[line width limit]] like this. It's wrapped into an invalid state.

使代码块语言检测机制与其他流行工具保持一致 (#9365 by @kachkaev)

Prettier 1.12 起,类似 ```js {something=something} 的代码块会被识别为 JavaScript 并进行格式化。早期因少有工具在语言标识后添加元数据,当时决定允许省略空格,因此 ```js{something=something} 也能被识别为 JavaScript

随着 Remark v8(Prettier 的依赖项)发布,在某些边缘场景中出现代码块识别与格式化的不一致现象。同时发现 Prettier 的格式化结果与 VSCode 语法高亮存在差异。为促进工具间一致性并遵循 Commonmark 规范,现强制要求在语言标识与元数据之间保留空格

<!-- Input -->
```js {something=something}
console.log ( "hello world" );
```

```js{something=something}
console.log ( "hello world" );
```

<!-- Prettier 2.1 -->
```js {something=something}
console.log("hello world");
```

```js{something=something}
console.log("hello world");
```

<!-- Prettier 2.2 -->
```js {something=something}
console.log("hello world");
```

```js{something=something}
console.log ( "hello world" );
```

修复空表格后多出空行的问题 (#9654 by @fisker)

<!-- Input -->
Test line 1

| Specify the selected option : | Option 1 |
| ----------------------------- | -------- |

Test line 6

<!-- Prettier 2.1 -->
Test line 1

| Specify the selected option : | Option 1 |
| ----------------------------- | -------- |


Test line 6

<!-- Prettier 2.2 -->
Test line 1

| Specify the selected option : | Option 1 |
| ----------------------------- | -------- |

Test line 6

MDX

修复 JSX 中多余空行的问题 (#9267 by @fisker)

<!-- Input -->
# title

<Jsx>

text

</Jsx>

<!-- Prettier 2.1 -->
# title

<Jsx>


text

</Jsx>


(Extra empty lines added after `<Jsx>` and `</Jsx>`)

<!-- Prettier 2.2 -->
# title

<Jsx>

text

</Jsx>

YAML

应用 trailingComma 配置项 (#9665 by @fisker)

--trailing-comma=none 时,流式映射(flowMapping)和流式序列(flowSequence)中不应添加尾部逗号

# Input
flow-mapping:
{
"object-does-not-fit-within-print-width": "------",
"TEST": "comma IS added here"
}
flow-sequence:
[
"object-does-not-fit-within-print-width", "------",
"TEST", "comma IS added here"
]

# Prettier 2.1
mapping:
{
"object-does-not-fit-within-print-width": "------",
"TEST": "comma IS added here",
}
flow-sequence:
[
"object-does-not-fit-within-print-width",
"------",
"TEST",
"comma IS added here",
]

# Prettier 2.2
flow-mapping:
{
"object-does-not-fit-within-print-width": "------",
"TEST": "comma IS added here"
}
flow-sequence:
[
"object-does-not-fit-within-print-width",
"------",
"TEST",
"comma IS added here"
]

修复 flowMappingflowSequence 内的注释处理 (#9669 by @fisker)

# Input
a:
[
a, b,
# comment
]

b:
# prettier-ignore
{
a: 1, b: 2,
# comment
}

# Prettier 2.1
a: [a, b]
# comment

b:
# prettier-ignore
{
a: 1, b: 2,
# comment
}
# comment


# Prettier 2.1 (second format)
a:
[a, b]
# comment

b:
# prettier-ignore
{
a: 1, b: 2,
# comment
}
# comment
# comment


# Prettier 2.2
a: [
a,
b,
# comment
]

b:
# prettier-ignore
{
a: 1, b: 2,
# comment
}

API

停止为 .jsonl 文件自动推断 json 解析器 (#9371 by @fisker)

// Prettier 2.1
$ prettier --check .
Checking formatting...
[error] bad.jsonl: SyntaxError: Unexpected token (2:1)
[error] 1 | '{"type": "t/f", "head": "England", "relation": "invaded", "tail": "United States"}'
[error] > 2 | '{"type": "t/f", "head": "England", "relation": "attacked", "tail": "Baltimore"}'
[error] | ^
[error] 3 |
All matched files use Prettier code style!

// Prettier 2.2
$ prettier --check .
Checking formatting...
All matched files use Prettier code style!

在文档末尾刷新行后缀内容 (#9703 by @dangmai)

当文档未包含尾部换行符时,Prettier 未刷新行后缀内容。此修复强制刷新所有行后缀内容(即使无尾部换行符)

CLI

pre-commit 支持迁移至 github.com/pre-commit/mirrors-prettier (#8937 by @FloChehab)

pre-commit 支持已迁移至 https://github.com/pre-commit/mirrors-prettier, 请更新您的 .pre-commit-config.yaml 配置文件。

- - repo: https://github.com/prettier/prettier
+ - repo: https://github.com/pre-commit/mirrors-prettier
- rev: "2.2.0"
+ rev: "v2.2.0"
hooks:
- id: prettier

修复:处理名称包含数字的目录和文件时出错 (#9298 by @fisker)

$ cat 1/index.js
hello('world')

// Prettier 2.1
$ prettier 1
[error] The "path" argument must be of type string. Received type number (1)

// Prettier 2.2
$ prettier 1
hello("world");