跳至主内容区

Prettier 1.15:支持 HTML、Vue、Angular 和 MDX

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

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

本次更新新增了对 HTML、Vue、Angular 和 MDX 的支持。 同时修复了装饰器位置问题, 新增了 JSX 单引号选项, 支持通过 shebang 推断解析器, 增加了多项新语法特性支持,并优化了部分格式化细节。

重要更新

HTML/Vue/Angular

支持 HTML、Vue 和 Angular (#5259 by @ikatyang, #4753 by @evilebottnawi, #2083 by @azz)

Prettier 现在可以格式化 HTML、Vue 和 Angular 文件了!🎉

我们使用从 Angular 中提取的 angular-html-parser 来解析这些 HTML 和 HTML 模板文件,得益于 Angular 团队的工作,该解析器对 HTML 规范的兼容性极高。

以下是几个关键特性:

空格敏感格式化

在日常 HTML 开发中,您可能注意到以下两种情况会产生不同输出:

htmloutput
with spaces1<b> 2 </b>31 2 3
without spaces1<b>2</b>3123

这是因为行内元素中的空格具有语义重要性。

因此我们无法安全地将

<a href="https://prettier.io/">Prettier is an opinionated code formatter.</a>

格式化为

<a href="https://prettier.io/">
Prettier is an opinionated code formatter.
</a>

否则可能改变浏览器中的实际渲染效果。

为避免破坏代码或保持原样不做处理,我们引入了_空格敏感格式化_机制:

  • 根据元素的默认 CSS display 值判断其内部空格是否重要

  • 通过智能换行避免增减重要空格

例如:

<!-- <span> is an inline element, <div> is a block element -->

<!-- input -->
<span class="dolorum atque aspernatur">Est molestiae sunt facilis qui rem.</span>
<div class="voluptatem architecto at">Architecto rerum architecto incidunt sint.</div>

<!-- output -->
<span class="dolorum atque aspernatur"
>Est molestiae sunt facilis qui rem.</span
>
<div class="voluptatem architecto at">
Architecto rerum architecto incidunt sint.
</div>

由于 CSS display 属性可能被修改,我们还支持通过魔法注释(如 <!-- display: block -->)告知 Prettier 如何格式化元素:

<!-- input -->
<!-- display: block -->
<span class="dolorum atque aspernatur">Est molestiae sunt facilis qui rem.</span>

<!-- output -->
<!-- display: block -->
<span class="dolorum atque aspernatur">
Est molestiae sunt facilis qui rem.
</span>

我们还提供了全局空格敏感度选项,以满足不同安全需求或偏好:

--html-whitespace-sensitivity(默认值 css

  • css - 遵循 CSS display 属性的默认值

  • strict - 所有空格均视为重要

  • ignore - 所有空格均视为不重要

自动解析器推断

Prettier 通过文件名推断使用的解析器。以下是 HTML、Vue 和 Angular 的默认匹配模式:

  • *.html:使用 --parser html

  • *.component.html:使用 --parser angular

  • *.vue:使用 --parser vue

请确保您的文件名匹配正确的解析器(尤其 Angular 用户),若不匹配, 需通过 overrides 字段手动指定解析器。

注意:在 --parser html 模式下不会触发框架特定格式化。

JavaScript 中的 HTML 模板字面量

本次更新还支持 html 模板标签(或包含 HTML 的"标签"注释):

  • html`code`

  • /* HTML */ `code`

// input
const foo = html`<div class="voluptatem architecto at">Architecto rerum ${interpolation} architecto incidunt sint.</div>`;

// output
const foo = html`
<div class="voluptatem architecto at">
Architecto rerum ${interpolation} architecto incidunt sint.
</div>
`;
Vue 格式化支持

现已支持以下 Vue 特定语法结构:

  • 插值表达式

    • {{ something }}
  • 属性

    • v-something
    • :something
    • @something
    • v-for
    • slot-scope
Angular 格式化

支持以下 Angular 特有的语法结构:

  • 插值表达式

    • {{ something }}
  • 属性

    • (something)
    • [something]
    • [(something)]
    • *something
  • 内联模板

    • @Component({ template: `<div>Hello World</div>` })

MDX

支持 MDX (#4975 by @ikatyang)

MDX 是一种 Markdown 扩展语法,允许使用 JSX 构建文档。现在您可以使用 Prettier 格式化 MDX 文件,我们会同时处理其中的 Markdown 和 JSX 部分!

<!-- Input -->

import {
colors } from
'./theme'
import Palette from './components/palette'

# Colors

<Palette colors={
colors}
/>

<!-- Output -->

import { colors } from "./theme";
import Palette from "./components/palette";

# Colors

<Palette colors={colors} />

JavaScript

扁平化嵌套三元表达式的 else 分支 (#5039 by @suchipi, #5272 by @duailibe, #5333 by @ikatyang)

此前嵌套的三元表达式总会产生缩进,导致深层嵌套的三元表达式缩进层级不断增加。为解决此问题,我们现在以类似 if..else if..else 代码块的格式化方式,对嵌套三元表达式的 else 分支进行扁平化处理。

// Input
const example1 =
someValue === 'a' ? 'hello world, branch a'
: someValue === 'b' ? 'hello world, branch a && b'
: someValue === 'c' ? 'hello world, branch a && b && c'
: someValue === 'd' ? 'hello world, branch a && b && c && d'
: null;

const example2 =
someValue === 'a'
? someValue === 'b'
? someValue === 'c'
? 'hello world, branch a && b && c'
: 'hello world, branch a && b && !c'
: 'hello world, branch a && !b'
: null;

// Output (Prettier 1.14)
const example1 =
someValue === "a"
? "hello world, branch a"
: someValue === "b"
? "hello world, branch a && b"
: someValue === "c"
? "hello world, branch a && b && c"
: someValue === "d"
? "hello world, branch a && b && c && d"
: null;

const example2 =
someValue === "a"
? someValue === "b"
? someValue === "c"
? "hello world, branch a && b && c"
: "hello world, branch a && b && !c"
: "hello world, branch a && !b"
: null;

// Output (Prettier 1.15)
const example1 =
someValue === "a"
? "hello world, branch a"
: someValue === "b"
? "hello world, branch a && b"
: someValue === "c"
? "hello world, branch a && b && c"
: someValue === "d"
? "hello world, branch a && b && c && d"
: null;

const example2 =
someValue === "a"
? someValue === "b"
? someValue === "c"
? "hello world, branch a && b && c"
: "hello world, branch a && b && !c"
: "hello world, branch a && !b"
: null;

保持装饰器原有的行内位置 (#5188 by @duailibe)

在 Prettier 1.14 之前,装饰器总是与其修饰对象位于同一行。

部分用户反馈此格式不够理想,因此在 Prettier 1.14 中我们改为始终将装饰器置于独立行。

但随后又有其他用户反馈此格式并非适用于所有场景。

我们希望在可能情况下保持格式一致性,因此尝试制定启发式规则来决定装饰器位置。

经过 #4924 的长期讨论,我们最终认为没有可靠方法统一判定装饰器位置。因此在 Prettier 1.15 中,我们决定保留用户原始书写风格:若装饰器与其修饰对象间存在换行,则保持换行;若原本位于同行,则保持同行。

// Input
class Hello {
@decorator inline = 'value';

@decorator
ownLine = 'value';

@decorator({
hello: 'world'
}) multiLine = 'value';
}

// Output (Prettier 1.14)
class Hello {
@decorator
inline = "value";

@decorator
ownLine = "value";

@decorator({
hello: "world"
})
multiLine = "value";
}

// Output (Prettier 1.15)
class Hello {
@decorator inline = "value";

@decorator
ownLine = "value";

@decorator({
hello: "world"
})
multiLine = "value";
}

保持装饰器的原始顺序 (#5207 by @duailibe)

装饰器尚未成为 ECMA 官方标准,且[导出类装饰器的放置位置]尚无定论。为帮助提案作者收集反馈,Babel 7 同时支持了在导出类前后放置装饰器。Prettier 1.15 新增对此的支持并保留您的原始顺序(待规范标准化后,我们将改为统一格式)。

// decorator before export
@decorator export class Bar {}

// decorator after export
export @decorator class Foo {}

改进对象换行启发式规则 (#5205 by @j-f1)

此前当对象超出打印宽度时,Prettier 会自动将其换行显示。若输入代码中对象内部存在换行,Prettier 也会保持多行格式。这导致您必须手动合并对象才能恢复单行显示。为消除此类手动操作,我们修改了行为:现在仅检查 { 与首个属性键之间是否存在换行:

// Input
const data = { foo: 'bar',
baz: 'quux'
}
/* You’d get this format by deleting the newline after the `{` */

// Output (Prettier 1.14)
const data = {
foo: 'bar',
baz: 'quux'
}

// Output (Prettier 1.15)
const data = { foo: 'bar', baz: 'quux' }

JSX

新增 JSX 单引号选项 (#4798 by @smirea)

在社区[强烈呼吁]下,Prettier 1.15 新增了在 JSX 中使用单引号的选项:--jsx-single-quote(或在配置/API 中使用 jsxSingleQuote)。

// with --jsx-single-quote
<div class='hello'>world</div>

// without --jsx-single-quote
<div class="hello">world</div>

正确拆分 JSX 文本 (#5006 by @yuliaHope)

Prettier 1.14 版本曾意外引入了一些糟糕的 JSX 换行问题,现已修复。

// Input
<div>
Sales tax estimated using a rate of {salesTax * 100}%.
</div>;
<BasicText light>(avg. {value}/5)</BasicText>;
<Link to={orgURL(this.props.org.name)}>
Go to {this.props.org.name}'s profile
</Link>;

// Output (Prettier 1.14)
<div>
Sales tax estimated using a rate of {salesTax * 100}
%.
</div>;
<BasicText light>
(avg. {value}
/5)
</BasicText>;
<Link to={orgURL(this.props.org.name)}>
Go to {this.props.org.name}
's profile
</Link>;

// Output (Prettier 1.15)
<div>Sales tax estimated using a rate of {salesTax * 100}%.</div>;
<BasicText light>(avg. {value}/5)</BasicText>;
<Link to={orgURL(this.props.org.name)}>
Go to {this.props.org.name}'s profile
</Link>;

Flow

支持 inexact 语法 (#5304 by @jbrown215, #5356 by @existentialism)

Flow 团队计划未来默认将所有对象类型视为精确类型,因此引入了新语法来标记非精确对象类型。Prettier 1.15 现已支持该语法。

type T = {
a: number,
...
}

保留 Flow 类型转换注释的换行 (#5280, #5290 by @swac)

此前,包围 Flow 类型转换注释的括号有时会被移除,导致 Flow 注释语法失效。此问题已在 Prettier 1.15 中修复。

// Input
(obj /*: Class */).property

// Output (Prettier 1.14)
obj /*: Class */.property;

// Output (Prettier 1.15)
(obj /*: Class */).property;

Markdown

保留数学公式语法 (#5050, #5220 by @ikatyang)

此前部分 remark-math 语法结构会被误判为普通文本而损坏。Prettier 1.15 现已保留这些结构,确保数学公式语法安全使用。

$inline-math$

$$
block-math
$$

其他变更

API/CLI

通过 Shebang 推断无扩展名文件的解析器 (#5149 by @haggholm)

此前我们依赖文件扩展名推断解析器,但 CLI 脚本常无扩展名,需手动指定解析器。Prettier 1.15 中,当格式化无扩展名文件时,若首行存在 shebang,将据此推断解析器。

# Input
$ cat bin/example
#!/usr/bin/env node
  require ( "../src/cli" ) . run ( )

$ prettier bin/example

# Output (Prettier 1.14)
[error] No parser could be inferred for file: bin/example

# Output (Prettier 1.15)
#!/usr/bin/env node
require("../src/cli").run();

新增 trim 命令修剪当前行空白 (#4772 by @warrenseine)

此前插件 API 缺少直接删除当前行缩进的方法。虽然可通过变通方案实现,但不够可靠。现新增可靠的 trim 命令专用于修剪空白。

彩色验证提示信息 (#5020, #5057 by @ikatyang)

此前选项验证错误信息无颜色标识,难以快速定位问题选项及有效值。Prettier 1.15 的彩色提示让问题一目了然。

# Input
$ prettier filename.js --trailing-comma wow

# Output (Prettier 1.14)
[error] Invalid ``--trailing-comma`` value. Expected "all", "es5" or "none", but received `"wow"`.

# Output (Prettier 1.15)
[error] Invalid --trailing-comma value. Expected "all", "es5" or "none", but received "wow".

允许打印机预处理 AST (#5041 by @ikatyang)

有时我们需要转换 AST 以方便打印。过去这项工作在解析器中完成,但这样会暴露内部实现给外部用户——那些可能尝试构建自定义解析器的用户,这并不理想。在 Prettier 1.15 中,你现在可以使用 printer.preprocess 来预处理 AST,而无需暴露 API 的任何内部细节。

interface Printer {
preprocess(ast: AST, options: object): AST;
}

更友好的不支持配置格式错误提示 (#4969 by @ikatyang)

以前加载不受支持的配置文件会抛出看似 Prettier 内部错误的提示信息,我们在 Prettier 1.15 中改进了这个提示。

# Input
$ prettier filename.js --config .prettierrc.wow

# Output (Prettier 1.14)
[error] Invalid configuration file: Cannot read property 'sync' of undefined

# Output (Prettier 1.15)
[error] Invalid configuration file: No sync loader specified for extension ".wow"

新增强制统一换行符的选项 (#5327 by @kachkaev)

过去 Prettier 总是保留原始换行符,这在大多数情况下没有问题。但当开发者从不同操作系统协作时,中央 Git 仓库容易出现混合换行符,导致大量差异。Prettier 1.15 新增 --end-of-line <auto|lf|crlf|cr> 选项帮助解决换行符问题。

JavaScript

将单星号注释视为 JSDoc (#5206 by @j-f1, #5330 by @lydell)

当注释缩进变化时,Prettier 现在会正确缩进首行仅含单个 */* 而非 /**)的 JSDoc 风格注释:

// Input
if (true) {
/*
* Oh no
*/
}

// Output (Prettier 1.14)
if (true) {
/*
* Oh no
*/
}

// Output (Prettier 1.15)
if (true) {
/*
* Oh no
*/
}

修正混合乘方/取模运算的括号问题 (#5243 by @bakkot)

修复混合使用幂运算和取模时的括号问题 (#5243 by @bakkot)

// Input
const val = (n % 10) ** 2

// Output (Prettier 1.14)
const val = n % 10 ** 2;

// Output (Prettier 1.15)
const val = (n % 10) ** 2;

try..finally 中正确打印注释(#5252,由 @aquibm

在之前的版本中,try-finally 语句中的某些注释会以错误顺序打印。 Prettier 现在已能正确打印它们。

// Input
// comment 1
try {
// comment 2
}
// comment 3
finally // comment 4
{
// comment 5
}

// Output (Prettier 1.14)
// comment 1
try {
// comment 2
} finally { // comment 4
// comment 3
// comment 5
}

// Output (Prettier 1.15)
// comment 1
try {
// comment 2
} finally {
// comment 3
// comment 4
// comment 5
}

先前版本中 try-finally 语句的某些注释会被打印在错误位置,现在 Prettier 已正确打印。

catch 子句中的注释打印到正确位置 (#5202 by @duailibe)

// Input
try {} catch (
// comment
e
) {}

// Output (Prettier 1.14)
try {
} catch (// comment
e) {}

// Output (Prettier 1.15)
try {
} catch (
// comment
e
) {}

现在 catch 子句中的注释会像其他子句一样打印在独立行。

函数体为条件表达式的箭头函数参数自动内联 (#5209 by @duailibe)

// Input
x.then(() => a ?
veryVerVeryveryVerVeryveryVerVeryveryVerVeryLong:
veryVerVeryveryVerVeryveryVerVeryveryVerVeryLong
);

// Output (Prettier 1.14)
x.then(
() =>
a
? veryVerVeryveryVerVeryveryVerVeryveryVerVeryLong
: veryVerVeryveryVerVeryveryVerVeryveryVerVeryLong
);

// Output (Prettier 1.15)
x.then(() =>
a
? veryVerVeryveryVerVeryveryVerVeryveryVerVeryLong
: veryVerVeryveryVerVeryveryVerVeryveryVerVeryLong
);

当函数调用参数是函数体为条件表达式的箭头函数时,无需额外缩进层级。Prettier 1.15 现在会将其内联显示。

修复注释导致的变量声明意外缩进问题 (#5190 by @duailibe)

// Input
const // Comment
a = 1;

// Output (Prettier 1.14)
const // Comment
a = 1;

// Output (Prettier 1.15)
const // Comment
a = 1;

先前版本中变量声明的注释会导致声明符无缩进打印,该问题已在 Prettier 1.15 修复。

保留可选链表达式 (?.) 中的三元运算符括号 (#5179 by @existentialism)

// Input
(a ? b : c)?.d;

// Output (Prettier 1.14)
a ? b : c?.d;

// Output (Prettier 1.15)
(a ? b : c)?.d;

在 GraphQL 标签中同时转义 ${ 和反引号 (#5137 by @lydell)

过去,在嵌入式 GraphQL 中类似插值的字符串会被错误地取消转义,导致 JavaScript 将其视为插值。在 Prettier 1.15 版本中这些问题已得到正确转义处理。

// Input
const schema = gql`
type Project {
"Pattern: \`\${project}\`"
pattern: String
}
`;

// Output (Prettier 1.14)
const schema = gql`
type Project {
"Pattern: \`${project}\`"
pattern: String
}
`;

// Output (Prettier 1.15)
const schema = gql`
type Project {
"Pattern: \`\${project}\`"
pattern: String
}
`;

过去嵌入式 GraphQL 中类似插值的字符串会被错误取消转义,导致 JavaScript 误认为插值语法。Prettier 1.15 已正确转义这些字符。

ES5 不兼容的键名保留引号 (#5157 by @koba04)

// Input
var obj = {
"𐊧": 'ok',
𐊧: 'ok'
};

// Output (Prettier 1.14)
var obj = {
𐊧: "ok",
𐊧: "ok"
};

// Output (Prettier 1.15)
var obj = {
"𐊧": "ok",
𐊧: "ok"
};

当函数第二个参数是三元表达式时不再紧凑排布参数 (#5151 by @onurtemizkan)

此前存在特殊情况:当函数调用的第一个参数是函数且第二个参数不是复杂表达式时,Prettier 会采用紧凑排布参数。 但我们将三元表达式也视为非复杂表达式,实际上三元表达式可能很复杂。 因此在 Prettier 1.15 中我们调整了这一行为。

// Input
func(
() => { thing(); },
something(longArgumentName, anotherLongArgumentName) ? someOtherThing() : somethingElse(true, 0)
);

// Output (Prettier 1.14)
func(() => {
thing();
}, something(longArgumentName, anotherLongArgumentName) ? someOtherThing() : somethingElse(true, 0));

// Output (Prettier 1.15)
func(
() => {
thing();
},
something(longArgumentName, anotherLongArgumentName)
? someOtherThing()
: somethingElse(true, 0)
);

支持测试函数中以数字形式传递超时参数 (#5085 by @j-f1)

当测试函数将超时时间(数字)作为第三个参数传递时, Prettier 现在能保持其特殊的测试函数格式化规则:

// Input
it('Handles at least 10k untracked files without failing', async () => {
hello()
}, 25000)

// Output (Prettier 1.14)
it(
"Handles at least 10k untracked files without failing",
async () => {
hello();
},
25000
);

// Output (Prettier 1.15)
it('Handles at least 10k untracked files without failing', async () => {
hello()
}, 25000)

将 beforeEach 类调用格式化为普通函数调用 (#5011 by @ericsakmar)

此前 beforeEach 的参数会被错误地紧凑排布,该问题已在 Prettier 1.15 修复。

// Input
beforeEach(done =>
startService()
.pipe(tap(service => (instance = service)))
.subscribe(() => done()),
);

// Output (Prettier 1.14)
beforeEach(done =>
startService()
.pipe(tap(service => (instance = service)))
.subscribe(() => done()));

// Output (Prettier 1.15)
beforeEach(done =>
startService()
.pipe(tap(service => (instance = service)))
.subscribe(() => done())
);

将管道操作符的前导注释单独成行打印 (#5015 by @flxwu)

在 Prettier 1.14 中,管道操作符前的注释会导致右侧参数无法正确缩进, 该问题已在 Prettier 1.15 修复。

// Input
function pipeline() {
0
// Comment
|> x
}

// Output (Prettier 1.14)
function pipeline() {
0
|> // Comment
x;
}

// Output (Prettier 1.15)
function pipeline() {
0 |>
// Comment
x;
}

保留 new 表达式中的悬空注释 (#5017 by @flxwu)

现在传递给 new 调用的注释(而非表达式)会被保留在括号内, 不再被提取到括号外部。

// Input
new Thing(/* comment */)

// Output (Prettier 1.14)
new Thing /* comment */();

// Output (Prettier 1.15)
new Thing(/* comment */);

移除绑定表达式冗余的 ASI 保护符 (#4970 by @TitanSnow)

在启用 --no-semi 选项时, Prettier 1.15 移除了绑定表达式中不必要的分号。

// Input
a::b.c

// Output (Prettier 1.14)
;a::b.c

// Output (Prettier 1.15)
a::b.c

不删除绑定表达式中必需的括号 (#4964 by @TitanSnow)

绑定表达式中必需的括号现已在 Prettier 1.15 中保留。

// Input
a::(b.c());

// Output (Prettier 1.14)
a::b.c();

// Output (Prettier 1.15)
a::(b.c());

修复函数调用中三元表达式的缩进问题 (#4368 by @malcolmsgroves)

函数调用中三元表达式内的逻辑表达式现在能正确缩进。

// Input
fn(
bifornCringerMoshedPerplexSawder,
askTrovenaBeenaDependsRowans,
glimseGlyphsHazardNoopsTieTie === averredBathersBoxroomBuggyNurl &&
anodyneCondosMalateOverateRetinol // <--
? annularCooeedSplicesWalksWayWay
: kochabCooieGameOnOboleUnweave
);

// Output (Prettier 1.14)
fn(
bifornCringerMoshedPerplexSawder,
askTrovenaBeenaDependsRowans,
glimseGlyphsHazardNoopsTieTie === averredBathersBoxroomBuggyNurl &&
anodyneCondosMalateOverateRetinol // <--
? annularCooeedSplicesWalksWayWay
: kochabCooieGameOnOboleUnweave
);

// Output (Prettier 1.15)
fn(
bifornCringerMoshedPerplexSawder,
askTrovenaBeenaDependsRowans,
glimseGlyphsHazardNoopsTieTie === averredBathersBoxroomBuggyNurl &&
anodyneCondosMalateOverateRetinol // <--
? annularCooeedSplicesWalksWayWay
: kochabCooieGameOnOboleUnweave
);

不移动 import 语句中的注释 (#5016 by @ericsakmar)

import 语句中的注释不再被移动到 import 语句的输出位置之外。

// Input
import x, {
// comment
y
} from 'z';


// Output (Prettier 1.14)
import x, { y } from "z";
// comment

// Output (Prettier 1.15)
import x, {
// comment
y
} from "z";

修复 while 循环注释的不稳定输出 (#5251 by @jaideng123)

while 循环中的注释现在能被正确格式化, 无需多次格式化即可获得稳定输出。

// Input
while(
true
// Comment
) {}

// First Output (Prettier 1.14)
while (true) // Comment
{}

// Second Output (Prettier 1.14)
while (
true // Comment
) {}

// First & Second Output (Prettier 1.15)
while (
true
// Comment
) {}

稳定打印函数声明与其函数体之间的注释 (#5250 by @jaideng123)

函数声明与其函数体之间的注释现在无需多次格式化即可获得最终结果。

// Input
function foo() // this is a function
{
return 42
}

// First Output (Prettier 1.14)
function foo() { // this is a function
return 42;
}

// Second Output (Prettier 1.14)
function foo() {
// this is a function
return 42;
}

// First & Second Output (Prettier 1.15)
function foo() {
// this is a function
return 42;
}

JSX

不打断 JSX 中的逻辑表达式链 (#5092 by @duailibe)

现在可以避免 JSX 中逻辑表达式链产生不必要的缩进。

// Input
const TestComponent = () => {
return (
<>
{cats && memes && (
<Foo bar><Trololol /></Foo>
)}
</>
);
}

// Output (Prettier 1.14)
const TestComponent = () => {
return (
<>
{cats &&
memes && (
<Foo bar>
<Trololol />
</Foo>
)}
</>
);
};

// Output (Prettier 1.15)
const TestComponent = () => {
return (
<>
{cats && memes && (
<Foo bar>
<Trololol />
</Foo>
)}
</>
);
};

不将非断空格转换为普通空格 (#5165 by @vjeux, #5334 by @ikatyang)

此前非断空格被当作普通空格处理, 导致被替换为普通空格。 该问题已在 Prettier 1.15 修复。

· 代表非断空格)

// Input
function OhMyWhitespace() {
return (
<Dialog>
<p>
Supprimer l’objectif «·{goal.name}·»·?
</p>
</Dialog>
)
}

// Output (Prettier 1.14)
function OhMyWhitespace() {
return (
<Dialog>
<p>
Supprimer l’objectif «
{goal.name}
·»·?
</p>
</Dialog>
);
}

// Output (Prettier 1.15)
function OhMyWhitespace() {
return (
<Dialog>
<p>Supprimer l’objectif «·{goal.name}·»·?</p>
</Dialog>
);
}

不拆分简单的 JSX 开始标签 (#5078 by @duailibe)

简单的 JSX 开始标签不再被拆分成多行。

// Input
function HelloWorld() {
const listItemText = (
<ListItemText
primary={
<PrimaryText>{`Two Factor Authentication is ${enabledText}`}</PrimaryText>
}
/>
);
}

// Output (Prettier 1.14)
function HelloWorld() {
const listItemText = (
<ListItemText
primary={
<PrimaryText
>{`Two Factor Authentication is ${enabledText}`}</PrimaryText>
}
/>
);
}

// Output (Prettier 1.15)
function HelloWorld() {
const listItemText = (
<ListItemText
primary={
<PrimaryText>{`Two Factor Authentication is ${enabledText}`}</PrimaryText>
}
/>
);
}

修复 JSX 表达式中箭头函数的回归问题 (#5063 by @ikatyang)

// Input
<div className="search-filter-chips">
{scopes.filter(scope => scope.value !== "").map((scope, i) => (
<FilterChip
query={this.props.query}
onFilterChosen={this.onSearchScopeClicked}
/>
))}
</div>;

// Output (Prettier 1.14)
<div className="search-filter-chips">
{scopes.filter(scope => scope.value !== "").map((scope, i) => (
<FilterChip
query={this.props.query}
onFilterChosen={this.onSearchScopeClicked}
/>
))}
</div>;

// Output (Prettier 1.15)
<div className="search-filter-chips">
{scopes
.filter(scope => scope.value !== "")
.map((scope, i) => (
<FilterChip
query={this.props.query}
onFilterChosen={this.onSearchScopeClicked}
/>
))}
</div>;

Flow

支持单标识符的内联泛型(由 @duailibe#5066 中贡献)

单标识符的泛型现在会始终内联显示。

// Input
const longVariableName: Array<number> = this.foo.bar.baz.collider.body.vertices.reduce();

// Output (Prettier 1.14)
const longVariableName: Array<
number
> = this.foo.bar.baz.collider.body.vertices.reduce();

// Output (Prettier 1.15)
const longVariableName: Array<number> = this.foo.bar.baz.collider.body.vertices.reduce();

避免在 extends 中强制内联类声明(由 @aquibm#5244 中贡献)

extends 中的类声明现在能正确换行显示。

// Input
declare interface ExtendsMany extends Interface1, Interface2, Interface3, Interface4, Interface5, Interface6, Interface7 {
x: string;
}

// Output (Prettier 1.14)
declare interface ExtendsMany
extends Interface1, Interface2, Interface3, Interface4, Interface5, Interface6, Interface7 {
x: string;
}

// Output (Prettier 1.15)
declare interface ExtendsMany
extends Interface1,
Interface2,
Interface3,
Interface4,
Interface5,
Interface6,
Interface7 {
x: string;
}

移除 export default 中异步函数的多余括号(由 @jbrown215#5303 中贡献)

现在会移除 export default async function 周围不必要的括号。 此问题仅在使用 flow 解析器时出现。

// Input
export default async function foo() {};

// Output (Prettier 1.14)
export default (async function foo() {});

// Output (Prettier 1.15)
export default async function foo() {}

TypeScript

为非空断言添加 ASI 保护机制(由 @duailibe#5262 中贡献)

--no-semi 模式下,非空断言所需的分号将不再被移除。

// Input
const el = ReactDOM.findDOMNode(ref)
;(el as HTMLElement)!.style.cursor = 'pointer'

// Output (Prettier 1.14)
const el = ReactDOM.findDOMNode(ref)
(el as HTMLElement)!.style.cursor = "pointer"

// Output (Prettier 1.15)
const el = ReactDOM.findDOMNode(ref)
;(el as HTMLElement)!.style.cursor = "pointer"

修复方法签名中使用 prettier-ignore 时多余分号问题(由 @onurtemizkan#5160 中贡献)

Prettier 1.15 移除了方法签名末尾的多余分号。

// Input
interface SharedWorkerGlobalScope extends WorkerGlobalScope {
// prettier-ignore
addEventListener(): void;
addEventListener(type: string): void;
}

// Output (Prettier 1.14)
interface SharedWorkerGlobalScope extends WorkerGlobalScope {
// prettier-ignore
addEventListener(): void;;
addEventListener(type: string): void;
}

// Output (Prettier 1.15)
interface SharedWorkerGlobalScope extends WorkerGlobalScope {
// prettier-ignore
addEventListener(): void;
addEventListener(type: string): void;
}

修复带默认值的解构语法中无效括号问题(由 @ikatyang#5096 中贡献)

此前 Prettier 会为带默认值的解构语法生成无效括号,该问题已在 1.15 版本修复。

// Input
({ prop: toAssign = "default" } = { prop: "propval" });

// Output (Prettier 1.14)
({ prop: (toAssign = "default") } = { prop: "propval" });

// Output (Prettier 1.15)
({ prop: toAssign = "default" } = { prop: "propval" });

--no-semi 模式下移除带修饰符的类属性分号(由 @ikatyang#5083 中贡献)

Prettier 1.14 会在类属性的下一个属性含修饰符时添加多余分号(实际无需分号)。 1.15 版本移除了这些不必要的分号。

// Input
class Reader {
private [kBuffer]: Buffer
private [kOffset]: number
}

// Output (1.14)
class Reader {
private [kBuffer]: Buffer;
private [kOffset]: number
}

// Output (1.15)
class Reader {
private [kBuffer]: Buffer
private [kOffset]: number
}

修复 ClassExpression 的 extends 中复杂节点括号丢失问题(由 @ikatyang#5074 中贡献)

此前 ClassExpression 的 extends 中复杂节点的括号会被错误移除,导致代码无效。 该问题已在 1.15 版本修复。

// Input
let Subclass2 = class extends (Superclass as AssertedSuperclass) {};

// Output (Prettier 1.14)
let Subclass2 = class extends Superclass as AssertedSuperclass {};

// Output (Prettier 1.15)
let Subclass2 = class extends (Superclass as AssertedSuperclass) {};

修复 TSOptionalType 必要括号丢失问题(由 @ikatyang#5056 中贡献)

此前元组中可选类型的必要括号会被错误移除,导致代码无效。 该问题已在 1.15 版本修复。

// Input
type T = [("a" | "b")?];

// Output (Prettier 1.14)
type T = ["a" | "b"?];

// Output (Prettier 1.15)
type T = [("a" | "b")?];

CSS

修复同时存在 front matter 和 /* prettier-ignore */ 时的输出错误(由 @ikatyang#5103 中贡献)

Prettier 1.14 因处理 front matter 的方式导致 AST 位置信息偏移, 进而使 /* prettier-ignore */ 产生无效输出。 此问题已在 1.15 版本修复。

/* Input */
---
hello: world
---

/* prettier-ignore */
.foo {}

/* Output (Prettier 1.14) */
---
hello: world
---

/* prettier-ignore */
pretti

/* Output (Prettier 1.15) */
---
hello: world
---

/* prettier-ignore */
.foo {}

保留 CSS-in-JS 模板中的换行符(由 @onurtemizkan#5240 中贡献)

CSS-in-JS 模板中的插值可能包含任意内容,因此 1.15 版本会保留其中的换行符。

// Input
const foo = styled.div`
${secondary}
flex: 0 0 auto;
`;

// Output (Prettier 1.14)
const foo = styled.div`
${secondary} flex: 0 0 auto;
`;

// Output (Prettier 1.15)
const foo = styled.div`
${secondary}
flex: 0 0 auto;
`;

Markdown

允许 front matter 分隔符包含尾部空格(由 @ikatyang#5107 中贡献)

此前仅能识别不带尾部空格的分隔符,导致主题分隔符(如 ---)被错误识别为 front matter 结束符。 1.15 版本已修复此问题(允许分隔符包含尾部空格),这与 GitHub 处理 front matter 的方式一致。

· 表示空格)

<!-- Input -->
---
Title: Title
---···

__strong__ **strong**

---

<!-- Output (Prettier 1.14) -->
---
Title: Title
---···
__strong__ **strong**
---

<!-- Output (Prettier 1.15) -->
---
Title: Title
---···

**strong** **strong**

---

不再在拉丁文字与韩文之间添加空格(由 @ikatyang#5040 中贡献)

此前,我们总是在拉丁字母与 CJK 字符之间插入空格以提升可读性, 但根据收到的反馈,韩文使用常规空格, 插入空格反而会导致问题。 因此在 Prettier 1.15 中我们针对韩文(Hangul)禁用了此行为。中文和日文的处理方式保持不变。

<!-- Input -->
예문Latin예문Latin 예문Latin예문 Latin예문Latin 예문 Latin 예문

<!-- Output (Prettier 1.14) -->
예문 Latin 예문 Latin 예문 Latin 예문 Latin 예문 Latin 예문 Latin 예문

<!-- Output (Prettier 1.15) -->
예문Latin예문Latin 예문Latin예문 Latin예문Latin 예문 Latin 예문

保留围栏代码块中的首尾换行符 (#5038 by @ikatyang)

在 Prettier 1.14 中,围栏代码块的首尾换行符会被移除, 这导致其他插件(如 php)无法正确格式化代码块。 Prettier 1.15 已改为保留首尾换行符。

<!-- Input -->
```

hello

```

<!-- Output (Prettier 1.14) -->
```
hello
```

<!-- Output (Prettier 1.15) -->
```

hello

```

单行段落脚注定义采用内联格式 (#5025 by @ikatyang)

此前,脚注定义中超出打印宽度的内容会被强制换行, 但将单行段落拆分成多行对可读性提升有限。 因此在 Prettier 1.15 中我们统一采用内联格式处理单行段落脚注。

<!-- Input -->
[^1]: In eos repellat fugit dolor veritatis doloremque nobis. Provident ut est illo.

<!-- Output (Prettier 1.14) -->
[^1]:

In eos repellat fugit dolor veritatis doloremque nobis. Provident ut est illo.

<!-- Output (Prettier 1.15) -->
[^1]: In eos repellat fugit dolor veritatis doloremque nobis. Provident ut est illo.

正确打印纯空白尾随换行符前的列表 (#5024 by @ikatyang)

在 Prettier 1.14 中,位于纯空白尾随换行符前的列表会被错误识别为松散列表。 这导致用户需要二次格式化才能稳定代码,但稳定后的输出结果却是错误的。 此问题已在 Prettier 1.15 中修复。

// Input
if (condition) {
md`
- 123
- 456
`;
}

// First Output (Prettier 1.14)
if (condition) {
md`
- 123
- 456
`;
}

// Second Output (Prettier 1.14)
if (condition) {
md`
- 123

- 456
`;
}

// First & Second Output (Prettier 1.15)
if (true) {
md`
- 123
- 456
`;
}

YAML

正确转义引号 (#5236 by @ikatyang)

此前双引号字符串中的转义引号会导致输出无效。 Prettier 1.15 已修复此问题。

# Input
"'a\"b"

# Output (Prettier 1.14)
''a"b'

# Output (Prettier 1.15)
'''a"b'

保留文档及文档头部的尾随注释 (#5027 by @ikatyang)

在 Prettier 1.14 中,文档及文档头部的尾随注释可能被意外移动。 Prettier 1.15 将确保这些注释始终保留在原位。

# Input
--- # hello
... # world

# Output (Prettier 1.14)
# hello
# world

# Output (Prettier 1.15)
--- # hello
... # world

不再对长值流式映射项报错 (#5027 by @ikatyang)

此前过长的流式映射值会被误识别为键, 由于超过 [1024 字符] 的隐式键不被允许, 这将引发语法错误。Prettier 1.15 已修复此问题。

(long 表示超过 1024 字符的长文本)

# Input
{x: long}

# Output (Prettier 1.14)
SyntaxError: The "undefine...ndefined" key is too long

# Output (Prettier 1.15)
{
x: long
}

空映射值优先采用隐式键 (#4972 by @ikatyang)

此前带空值的映射项总是以[显式键]打印, 这种方式并不常见且容易造成困惑。 Prettier 1.15 将在此类场景中统一采用隐式键。

# Input
a:
b:

# Output (Prettier 1.14)
a:
? b

# Output (Prettier 1.15)
a:
b:

致谢! ❤️

感谢所有为此版本做出贡献的开发者, 以及提交问题反馈的各位用户。 Prettier 是社区驱动的项目, 正是因为有你们的支持才能持续发展。谢谢!

特别感谢 @j-f1, @suchipi@existentialism 审阅本博文!