跳至主内容区

Prettier 1.14:YAML 支持

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

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

本次发布新增了对 YAML 的支持,为所有语言添加了编译指示(即 /** @prettier */)支持,并优化了大文件的处理性能。同时还新增了多项语法特性支持,并进行了格式微调,让您的代码更加美观。✨

重要更新

YAML

支持 YAML(#4563#4742#4773#4854@ikatyang 实现)

Prettier 现在可以格式化 YAML 文件了!🎉

该实现高度符合 YAML 规范,并基于出色的 yaml 包实现,感谢 @eemeli。主要特性包括:

自动换行

与 Markdown 类似,如果不会影响文件语义,我们会将散文内容在 80 字符处强制换行。

输入示例:

>
Voilà! In view, a humble vaudevillian veteran cast vicariously as both victim and villain by the vicissitudes of Fate. This visage, no mere veneer of vanity, is a vestige of the vox populi, now vacant, vanished. However, this valourous visitation of a bygone vexation stands vivified and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition! The only verdict is vengeance; a vendetta held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous. Verily, this vichyssoise of verbiage veers most verbose, so let me simply add that it’s my very good honour to meet you and you may call me V.

输出:(使用 --prose-wrap always

>
Voilà! In view, a humble vaudevillian veteran cast vicariously as both victim
and villain by the vicissitudes of Fate. This visage, no mere veneer of
vanity, is a vestige of the vox populi, now vacant, vanished. However, this
valourous visitation of a bygone vexation stands vivified and has vowed to
vanquish these venal and virulent vermin vanguarding vice and vouchsafing the
violently vicious and voracious violation of volition! The only verdict is
vengeance; a vendetta held as a votive, not in vain, for the value and
veracity of such shall one day vindicate the vigilant and the virtuous.
Verily, this vichyssoise of verbiage veers most verbose, so let me simply add
that it’s my very good honour to meet you and you may call me V.

注意:proseWrap 选项默认设为 preserve,因此您需要显式指定 alwaysnever 来启用此功能。

忽略文件片段

如果因某些原因不希望格式化 YAML 文件的某部分,您始终可以在该片段前添加忽略注释:

# prettier-ignore
key : value
hello: world

Front Matter

格式化 YAML front matter(#4773@ikatyang 实现)

Prettier 现在可以格式化 CSS 和 Markdown 文件中以 --- 分隔的 YAML front matter:

输入 & 输出(Prettier 1.13):

---
key : value
---

# heading

content

输出(Prettier 1.14):

---
key: value
---

# heading

content

编译指示

全语言支持 requirePragma/insertPragma#4688#4699#4713@ikatyang 实现)

Prettier 1.7.0 和 1.8.0 引入了两个新选项: --require-pragma--insert-pragma。 但此前这些选项仅支持少数语言。 现在这些选项已支持所有语言,包括 YAML!

  • YAML

    # @prettier

    key: value
  • CSS/Less/SCSS

    /** @prettier */

    .class {
    display: none;
    }
  • GraphQL

    # @prettier

    query Browse($offset: Int) {
    browse(offset: $offset)
    }
  • Vue

    <!-- @prettier -->

    <template>
    <div>Template</div>
    </template>

JavaScript

禁止行内装饰器(独立参数装饰器除外)(#4830@suchipi 实现)

此前装饰器总是内联显示(因 MobX 采用此惯例),但社区反馈表明 MobX 是唯一遵循此惯例的主流库。 现在 Prettier 会将装饰器置于独立行,除非是参数装饰器。

// Input
@observer
class OrderLine {
@observable
price: number = 0;
@observable amount: number = 1;

foo(@required name) {
console.log(name);
}
}

// Output (Prettier 1.13)
@observer
class OrderLine {
@observable price: number = 0;
@observable amount: number = 1;

foo(@required name) {
console.log(name);
}
}

// Output (Prettier 1.14)
@observer
class OrderLine {
@observable
price: number = 0;
@observable
amount: number = 1;

foo(@required name) {
console.log(name);
}
}

单独处理 JSX 与 fbt 的空白符(#4717@karl 实现)

此前,JSX 中的空白字符始终会被保留,因为 Facebook 拥有自定义的翻译管道(fbt),该管道虽使用 JSX 语法却以不同于常规 JSX 的方式处理空白字符——这意味着我们若修改 JSX 空白处理逻辑,就会破坏 Facebook 的代码库。在 Prettier 1.14 中,我们实现了对 Facebook fbt 标签的检测机制,并对其采用与其他 JSX 标签不同的空白处理策略,显著提升了不同形式但逻辑等效代码的格式一致性。

// Input and Output from Prettier 1.13
first = (
<div>
Text<br />
More text<br />
And more<br />
</div>
);

second = (
<div>
Text<br />More text<br />And more<br />
</div>
);

third = (
<div>
Text
<br />
More text
<br />
And more
<br />
</div>
);

// Output from Prettier 1.14
first = (
<div>
Text
<br />
More text
<br />
And more
<br />
</div>
);

second = (
<div>
Text
<br />
More text
<br />
And more
<br />
</div>
);

third = (
<div>
Text
<br />
More text
<br />
And more
<br />
</div>
);

当标签或表达式间的分隔文本仅为单个字符时,我们会尽可能将整个组合内容打印在同一行。

// Input and Output from Prettier 1.13
x = (
<div>
{hour}:{minute}:{second}
</div>
);

x = (
<div>
{hour}
:
{minute}
:
{second}
</div>
);

x = (
<div>
{hour}:
{minute}:
{second}
</div>
);

// Output from Prettier 1.14
x = (
<div>
{hour}:{minute}:{second}
</div>
);

x = (
<div>
{hour}:{minute}:{second}
</div>
);

x = (
<div>
{hour}:{minute}:{second}
</div>
);

此项改进还优化了以下边界案例的输出效果:

// Input
this_really_should_split_across_lines =
<div>
before{stuff}after{stuff}after{stuff}after{stuff}after{stuff}after{stuff}after{stuff}after
</div>

// Output (Prettier 1.13)
this_really_should_split_across_lines = (
<div>
before{stuff}after{stuff}after{stuff}after{stuff}after{stuff}after{stuff}after{
stuff
}after
</div>
);


// Output (Prettier 1.14)
this_really_should_split_across_lines = (
<div>
before
{stuff}
after
{stuff}
after
{stuff}
after
{stuff}
after
{stuff}
after
{stuff}
after
{stuff}
after
</div>
);

强制折行 JSX 表达式中的箭头函数 JSX 内容 (#4601 by @duailibe)

此前版本中,JSX 表达式内的箭头函数 JSX 内容遵循通用的"适配或折行"规则,但在遍历数组时会降低可读性。Prettier 1.14 强制此类箭头函数内容折行显示,有效提升了代码可读性。

// Input
const UsersList = ({ users }) => (
<section>
<h2>Users list</h2>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</section>
)

// Output (Prettier 1.13)
const UsersList = ({ users }) => (
<section>
<h2>Users list</h2>
<ul>{users.map(user => <li key={user.id}>{user.name}</li>)}</ul>
</section>
);

// Output (Prettier 1.14)
const UsersList = ({ users }) => (
<section>
<h2>Users list</h2>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</section>
);

TypeScript

支持 TypeScript 3.0 新特性 (#4625, #4757 by @ikatyang)

即将发布的 TypeScript 3.0 包含多项语法新特性:unknown 类型元组在剩余参数和展开表达式中的应用。Prettier 1.14 已支持这些特性,待 TypeScript 3.0 发布后即可直接格式化相关代码。

// UnknownType
const json: unknown = JSON.parse(jsonText);

// RestType
type Foo = [string, number];
type Bar = [boolean, ...Foo];

// OptionalType
type Baz = [string, number?];

JSON

禁止键值对分离换行 (#4852 by @ikatyang)

长值换行本是 Prettier 的核心特性,但我们发现该规则无助于提升含长字符串值的 JSON 文件可读性。Prettier 1.14 将不再移动对象内的字符串值——无论其是否超出打印宽度限制。

// Input
{
"description": "a long long long long long long long long long long long long long long long long long paragraph"
}

// Output (Prettier 1.13)
{
"description":
"a long long long long long long long long long long long long long long long long long paragraph"
}

// Output (Prettier 1.14)
{
"description": "a long long long long long long long long long long long long long long long long long paragraph"
}

Markdown

仅对齐已手动对齐的列表 (#4893 by @ikatyang)

此前我们始终强制列表对齐,旨在优化缩进体验并规避缩进代码块解析错误,但这导致有序列表前出现非常规的双空格。Prettier 1.14 仅会在以下情况对齐列表:

  • 列表项已手动对齐

  • 首个列表项前至少存在两个空格

  • 列表内含缩进代码块

输入示例:

1.  123
1. 123

---

1. 123
1. 123

---

11. 123
1. 123

---

11. 123
1. 123

---

1. 123
1. 123

输出结果:(Prettier 1.13)

1.  123
1. 123

---

1. 123
1. 123

---

11. 123
1. 123

---

11. 123
1. 123

---

1. 123
1. 123

输出结果:(Prettier 1.14)

1.  123
1. 123

---

1. 123
1. 123

---

11. 123
1. 123

---

11. 123
1. 123

---

1. 123
1. 123

性能优化

大幅提升大文件处理性能 (#4790, #4848 by @sompylasar)

Prettier 的核心特性是在代码超出打印宽度时自动折行,这需要精确计算每个字符的视觉宽度。最简便的方法是使用 JavaScript 的 String#length 属性,但某些非 ASCII 字符(如 CJK 字符)的宽度大于拉丁字符。此前我们采用临时方案:将这些双宽度字符替换为两个空格后再计算长度。该方案虽有效,却因需对每个标记执行替换而影响性能。Prettier 1.14 新增了字符串纯 ASCII 检测机制,避免执行不必要的替换操作。该优化使大文件处理速度提升 20%

其他变更

API/CLI

支持配置文件中 pluginspluginSearchDirs 的相对路径 (#4667 by @ikatyang)

在 Prettier 1.13 中,我们引入了新的 pluginSearchDirs 选项用于指定插件搜索路径。该选项在命令行中使用时表现良好(始终相对于当前目录),但在配置文件中使用相对路径时却无法正常工作。Prettier 1.14 现已支持配置文件中 pluginSearchDirsplugins 的相对路径。

prettier 安装在不以 prettier 命名的目录时不再报错 (#4706 by @asottile)

Prettier 1.13 通过从 prettier 所在位置向上查找目录树寻找最近的 node_modules 来支持全局安装的插件,但我们假设总会存在名为 prettier 的目录,导致当安装在不同名称的目录时 Prettier 崩溃。在 Prettier 1.14 中我们改进了搜索逻辑,现在您可以重命名 prettier 目录(例如将分叉版本发布到 npm)。

在浏览器环境中支持 filepath 选项 (#4721 by @ikatyang)

Prettier 1.13 在浏览器环境中提供了一流支持,但当时只能通过 parser 选项选择解析器。Prettier 1.14 在浏览器 API 中新增了 filepath 选项支持,使 Prettier 能够像 Node.js API 那样自动推断应使用的解析器。这对于 Web 编辑器应用尤其有用!

向插件暴露 isPreviousLineEmpty 方法 (#4748 by @warrenseine)

此前我们向插件暴露了 isNextLineEmpty 方法,但未提供 isPreviousLineEmpty。Prettier 1.14 将其公开,因为在某些场景(如 C# 中的指令)中该方法非常实用。

JavaScript

支持 BigInt 字面量 (#4697 by @ikatyang)

默认的 babylon 解析器现已支持 BigInt 字面量。

const bigInt = 1n;

支持 throw 表达式 (#4695 by @VojtechStep)

默认的 babylon 解析器现已支持 throw 表达式

const assert = x => x || throw new Error('...');

当第二个参数也是调用表达式时始终展开第一个参数 (#4657 by @duailibe)

此前我们特别处理了当函数调用仅有两个参数且第一个参数是函数时不换行的场景。这种方式效果良好,但当第二个参数也是函数时,格式效果欠佳。

// Input
somePromise.then((args) => {
this.props.receiveFavoritesActions(id, [].concat(...args));
}, ({ isCanceled }) => !isCanceled && logger.warn(`Error getting actions for the product: ${id}`));

// Output (Prettier 1.13)
somePromise.then(args => {
this.props.receiveFavoritesActions(id, [].concat(...args));
}, ({ isCanceled }) => !isCanceled && logger.warn(`Error getting actions for the product: ${id}`));

// Output (Prettier 1.14)
somePromise.then(
args => {
this.props.receiveFavoritesActions(id, [].concat(...args));
},
({ isCanceled }) =>
!isCanceled && logger.warn(`Error getting actions for the product: ${id}`)
);

为 bind 语法中的 await 添加括号 (#4778 by @ikatyang)

此前 Prettier 错误移除了实验性 bind 语法中 await 所需的括号,导致语义改变。Prettier 1.14 现已正确保留括号。

// Input
const doBothThings = async () => {
const request = doAsyncThing();
return (await request)::doSyncThing();
};

// Output (Prettier 1.13)
const doBothThings = async () => {
const request = doAsyncThing();
return await request::doSyncThing(); // means `await (request::doSyncThing)`
};

// Output (Prettier 1.14)
const doBothThings = async () => {
const request = doAsyncThing();
return (await request)::doSyncThing();
};

允许顶级 superawait (#4823 by @ikatyang)

superawait 通常不允许出现在类和异步函数之外,但我们的范围格式化功能在选择函数内容时表现不佳。Prettier 1.14 允许它们在任意位置出现。

super();

await doSomething();

在函数组合启发式中屏蔽 thissuper (#4836 by @princed)

在 Prettier 1.13 中,我们改进了函数式组合函数(如 pipecompose 等)的格式化方式,将其参数放在单独的行上,但这导致了类中同名函数的误判问题。Prettier 1.14 在函数式组合启发式规则中将 thissuper 加入黑名单,避免了这种情况。

// Input
class X {
x() {
this.compose(a(), b);
super.compose(a(), b);
}
}

// Output (Prettier 1.13)
class X {
x() {
this.compose(
a(),
b
);
super.compose(
a(),
b
);
}
}

// Output (Prettier 1.14)
class X {
x() {
this.compose(a(), b);
super.compose(a(), b);
}
}

TypeScript

支持 import.meta (#4762 by @ikatyang)

在 Prettier 1.13 中,我们使用的 TypeScript 解析器版本不支持解析 import.meta 语法。在 Prettier 1.14 中更新 TypeScript 解析器后,现在可以正确解析和格式化这种语法。

console.log(import.meta.url);

类中使用字符串字面量键名的可选属性 (#4762 by @ikatyang)

在之前的版本中,使用字符串字面量作为键名的可选属性中的 ? 会被错误移除。Prettier 1.14 修复了这个错误,现在会保留该符号。

// Input
export class ClassExample {
"a-prop"?: boolean;
}

// Output (Prettier 1.13)
export class ClassExample {
"a-prop": boolean;
}

// Output (Prettier 1.14)
export class ClassExample {
"a-prop"?: boolean;
}

检测类中的多个父类并报错 (#4762 by @ikatyang)

类只允许有一个父类,但由于 TypeScript AST 中 extends 子句的内部结构与 implements 子句相同,导致允许多个父类存在。之前版本在打印时会静默丢弃多余的父类,可能造成格式化后的混淆。Prettier 1.14 现在会将其标记为语法错误。

// Input
class Foo extends BarComponent, BazService, QuuxProvider {}

// Output (Prettier 1.13)
class Foo extends BarComponent {}

// Output (Prettier 1.14)
/*
SyntaxError: Classes can only extend a single class.
*/

支持 JSX 扩展子元素 (#4885 by @ikatyang)

此前版本会错误移除 JSX 表达式中子元素扩展的 ... 符号。Prettier 1.14 已修复此问题。

// Input
const x = <div>{...[0]}</div>

// Output (Prettier 1.13)
const x = <div>{[0]}</div>;

// Output (Prettier 1.14)
const x = <div>{...[0]}</div>;

Flow

支持 .js.flow 扩展名 (#4777 by @ikatyang)

.js.flow 是 Flow 声明文件的扩展名,但此前未被识别。Prettier 1.14 现在会自动识别这类文件,无需在配置文件中额外添加覆盖规则。

CSS/Less/SCSS

正确处理 front-matter 与注释间的换行 (#4701 by @evilebottnawi)

Prettier 1.13 会将 front matter 与 CSS 注释间的多个换行替换为空格。Prettier 1.14 改为在它们之间保留换行。

/* Input */
---
key: value
---

/* comment */
.class {
display: none;
}

/* Output (Prettier 1.13) */
---
key: value
---
/* comment */
.class {
display: none;
}

/* Output (Prettier 1.14) */
---
key: value
---

/* comment */
.class {
display: none;
}

支持以右花括号结尾的 @ 规则 (#4733 by @davidgomes)

此前版本无法正确解析以 } 结尾的 @ 规则。Prettier 1.14 现在能正确识别并格式化这类规则。

/* Input */
@mixin placeholder {
&::placeholder {@content}
}

/* Output (Prettier 1.13) */
/*
SyntaxError: CssSyntaxError Unclosed block
*/

/* Output (Prettier 1.14) */
@mixin placeholder {
&::placeholder {
@content;
}
}

Markdown

保留电子邮件自动链接 (#4740 by @ikatyang)

此前版本会将自动链接格式化为其 URL 形式,这本身没有问题。但对于电子邮件链接存在特殊情况:它们会解析为 mailto: 链接。Prettier 1.14 保留电子邮件自动链接的原始格式。

<!-- Input -->
<hello@example.com>

<!-- Output (Prettier 1.13) -->
<mailto:hello@example.com>

<!-- Output (Prettier 1.14) -->
<hello@example.com>

取消 markdown 代码块语言名后必须空格的限制 (#4783 by @kachkaev)

在 Prettier 1.12 中,我们添加了对带属性的围栏代码块语言的支持,要求语言名与属性间用空格分隔。但这导致 Atom 中的代码高亮显示不一致:代码块会被高亮但不会被格式化。Prettier 1.14 更新了检测逻辑,现在行为应该保持一致。

<!-- Input -->
```js{something=something}
const x = 1;
```

<!-- Output (Prettier 1.13) -->
```js{something=something}
const x = 1;
```

<!-- Output (Prettier 1.14) -->
```js{something=something}
const x = 1;
```

使用语言别名查找 markdown 代码块的解析器 (#4734 by @ikatyang)

此前我们通过语言 nameextensions 来确定代码块格式化使用的解析器,但发现无法同时格式化 JSON with Comments 代码块并保持语法高亮正常工作,因为它们与 JSON 共享相同的 .json 扩展名,而注释语法高亮仅在 JSON5 中可用。我们在 Prettier 1.14 中增加了通过语言 aliases 查找解析器的支持,现在你可以使用 jsonc 来格式化 JSON with Comments 代码块并触发语法高亮。

<!-- Input -->
```jsonc
// comment
{"a":1}
```

<!-- Output (Prettier 1.13) -->
```jsonc
// comment
{"a":1}
```

<!-- Output (Prettier 1.14) -->
```jsonc
// comment
{ "a": 1 }
```

当编码字符的码点大于 U+FFFF 时保留其实体(#4832,由 @ikatyang 提交)

我们使用的 Markdown 解析器 (remark) 会将每个编码字符解析为单个 AST 节点,因此可以通过检测 AST 节点值长度是否为 1 来恢复编码字符。但存在一个例外:当字符的码点大于 0xFFFF 时,由于 JavaScript 使用 UTF-16(2 字节)编码字符串,单个字符长度可能为 2。Prettier 1.14 能正确识别这些字符,避免它们被转换为字面字符。

<!-- Input -->
&#x1F609;

<!-- Output (Prettier 1.13) -->
😉

<!-- Output (Prettier 1.14) -->
&#x1F609;

Vue

支持 Vue 文件的范围格式化(#4868,由 @ikatyang 提交)

此前 Vue 文件不支持范围格式化,Prettier 1.14 新增了此功能。

GraphQL

在非块字符串值中保留换行符(#4808,由 @ikatyang 提交)

非块字符串值仅允许单行显示,但 Prettier 1.13 错误地将转义的 \n 转换为实际换行。Prettier 1.14 现在能正确输出 \n

# Input
{
foo(input: {multiline: "ab\ncd"}) { id }
}

# Output (Prettier 1.13)
{
foo(input: { multiline: "ab
cd" }) {
id
}
}

# Output (Prettier 1.14)
{
foo(input: { multiline: "ab\ncd" }) {
id
}
}