跳至主内容区

Prettier 3.2:支持 JSONC 和 Angular 的 ICU 表达式

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

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

本次发布包含多项新特性,例如新增 JSONC 解析器、支持 Angular 的 ICU 表达式以及大量错误修复。

我们仍在收集 Prettier 3.1 中发布的 --experimental-ternaries 选项的反馈。请阅读博客文章《三元表达式的奇妙案例》并通过提供的 Google 表单链接提交反馈。

此外,我们推荐阅读 Fabio Spampinato 撰写的《Prettier CLI 性能深度解析》。这个更快的 CLI 将作为 4.0 版本发布。

如果您认可 Prettier 的价值并希望支持我们的工作,请考虑通过 我们的 OpenCollective 直接赞助,或赞助我们依赖的项目如 typescript-eslintremarkBabel。感谢您持续的支持!

重要更新

JSON

新增 jsonc 解析器 (#15831 by @fisker)

此前,我们将 .jsonc 文件的解析器识别为 json,但若需保留尾部逗号,必须使用变通配置 {parser: "json5", quoteProps: "preserve", singleQuote: false}

新增的 jsonc 解析器:

  • 始终为对象键添加引号

  • 使用双引号包裹字符串

  • 当然会遵循 trailingComma 选项配置

Angular

支持格式化 Angular ICU 表达式 (#15777 by @sosukesuzuki)

支持两种 Angular ICU 表达式:pluralselect

<span i18n>
Updated:
{minutes, plural,
=0 {just now}
=1 {one minute ago}
other {{{minutes}} minutes ago}
}
</span>

<span i18n>
The author is {gender, select, male {male} female {female} other {other}}
</span>

其他变更

JavaScript

避免在模板插值中添加换行 (#15209 by @bakkot)

在如下模板字符串中:

`this is a long message which contains an interpolation: ${format(data)} <- like this`;

避免在格式化表达式时添加换行,除非原表达式已包含换行或因嵌套函数等原因不可避免。此前当模板插值"复杂度较高"时可能引入换行:

`this is a long message which contains an interpolation: ${format(
data,
)} <- like this`;

现在将保留原始格式。

${...} 内已存在换行,则按正常规则格式化。

修复含空行的方法链非幂等格式化问题 (#15522 by @seiyab)

// Input
Foo.a()

.b();

// Prettier 3.1 (first format)
Foo.a()
.b();

// Prettier 3.1 (second format)
Foo.a().b();


// Prettier 3.2
Foo.a()

.b();

修复函数调用中三元表达式的格式化问题 (#15677 by @fisker)

// Input
stopDirectory = await (useCache
? memoizedFindProjectRoot
: findProjectRootWithoutCache)(path.dirname(path.resolve(filePath)));

// Prettier 3.1
stopDirectory = await (useCache
? memoizedFindProjectRoot
: findProjectRootWithoutCache)(path.dirname(path.resolve(filePath)));

// Prettier 3.2
stopDirectory = await (
useCache ? memoizedFindProjectRoot : findProjectRootWithoutCache
)(path.dirname(path.resolve(filePath)));

修复可选链操作符的不一致问题 (#15806 by @fisker)

该问题仅在使用 typescriptmeriyah 或其他除 babel 外的 ESTree 解析器时出现。

// Input
function someFunctionName() {
return isEqual(a.map(([t, _]) => t?.id), b.map(([t, _]) => t?.id));
return isEqual(a?.map(([t, _]) => t?.id), b?.map(([t, _]) => t?.id));
}
theValue = Object.entries(someLongObjectName).filter(
([listingId]) => someListToCompareToHere.includes(listingId),
);
theValue = Object.entries(someLongObjectName).filter(
([listingId]) => someListToCompareToHere?.includes(listingId),
);

// Prettier 3.1
function someFunctionName() {
return isEqual(
a.map(([t, _]) => t?.id),
b.map(([t, _]) => t?.id),
);
return isEqual(a?.map(([t, _]) => t?.id), b?.map(([t, _]) => t?.id));
}
theValue = Object.entries(someLongObjectName).filter(([listingId]) =>
someListToCompareToHere.includes(listingId),
);
theValue = Object.entries(someLongObjectName).filter(
([listingId]) => someListToCompareToHere?.includes(listingId),
);

// Prettier 3.2
function someFunctionName() {
return isEqual(
a.map(([t, _]) => t?.id),
b.map(([t, _]) => t?.id),
);
return isEqual(
a?.map(([t, _]) => t?.id),
b?.map(([t, _]) => t?.id),
);
}
theValue = Object.entries(someLongObjectName).filter(([listingId]) =>
someListToCompareToHere.includes(listingId),
);
theValue = Object.entries(someLongObjectName).filter(([listingId]) =>
someListToCompareToHere?.includes(listingId),
);

修复 if 语句中的注释问题 (#15826 by @fisker)

// Input
if (foo) for (i = 2; i > 0; i--) console.log(i); // comment 1
else bar();

for (;;){
if (foo) continue; // comment 2
else bar();
}

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

// Prettier 3.2
if (foo)
for (i = 2; i > 0; i--) console.log(i); // comment 1
else bar();

for (;;) {
if (foo)
continue; // comment 2
else bar();
}

TypeScript

改进条件类型别名的布局 (#15811 by @seiyab)

// Input
type FallbackFlags<F extends Flags | undefined> =
Equals<NonNullableFlag<F>["flags"], {}> extends true
? Dict<any>
: NonNullableFlag<F>["flags"];

// Prettier 3.1
type FallbackFlags<F extends Flags | undefined> = Equals<
NonNullableFlag<F>["flags"],
{}
> extends true
? Dict<any>
: NonNullableFlag<F>["flags"];

// Prettier 3.2
type FallbackFlags<F extends Flags | undefined> =
Equals<NonNullableFlag<F>["flags"], {}> extends true
? Dict<any>
: NonNullableFlag<F>["flags"];

HTML

修复被 Prettier 忽略的未闭合元素的格式化问题 (#15748 by @fisker)

<!-- Input -->
<!-- prettier-ignore -->
<h1>
Hello <span>world!

<!-- Prettier 3.1 -->
<!-- prettier-ignore -->
<h1>

<!-- Prettier 3.2 -->
<!-- prettier-ignore -->
<h1>
Hello <span>world!

Angular

修复被 prettier-ignore 忽略的 Angular 控制流块 (#15827 by @fisker)

<!-- Input -->
<!-- prettier-ignore -->
@if (condition) {
Foo
} @else {
Other
}

<!-- Prettier 3.1 -->
<!-- prettier-ignore -->
@if (condition) {
Foo
}
} @else {
Other
}

<!-- Prettier 3.2 -->
<!-- prettier-ignore -->
@if (condition) {
Foo
}
@else {
Other
}

避免在 for 块的第三个表达式中为 track 添加冒号 (#15887 by @sosukesuzuki)

<!-- Input -->
@for (item of items; let i = $index; track block) {}

<!-- Prettier 3.1 -->
@for (item of items; let i = $index; track: block) {}

<!-- Prettier 3.2 -->
@for (item of items; let i = $index; track block) {}

Ember / Handlebars

保留路径字面量片段 (#15605 by @maxpowa)

修复了包含字面量片段的 Handlebars 输入文件被重新格式化后解包字面量片段,导致输出出现语法错误的问题。

<!-- Input -->
{{input.[funky<api!response]}}
{{input.[this one has spaces]}}
{{input.[anotherone].[0]}}

<!-- Prettier 3.1 -->
{{input.funky<api!response}}
{{input.this one has spaces}}
{{input.anotherone.0}}

<!-- Prettier 3.2 -->
{{input.[funky<api!response]}}
{{input.[this one has spaces]}}
{{input.anotherone.[0]}}

GraphQL

改进 GraphQL 联合类型的格式化 (#15870 by @ArchitGajjar)

# Input
union SearchResult = Conference| Festival | Concert | Venue | Conference| Festival | Concert | Venue

# Prettier 3.1
union SearchResult =
Conference
| Festival
| Concert
| Venue
| Conference
| Festival
| Concert
| Venue

# Prettier 3.2
union SearchResult =
| Conference
| Festival
| Concert
| Venue
| Conference
| Festival
| Concert
| Venue

API

支持在配置文件中使用绝对路径作为插件 (#15666 by @fisker)

// prettier.config.cjs
module.exports = {
plugins: [
// posix style
"/path/to/plugin.js",
// Windows style
"D:\\\\path\\to\\plugin.js",
// Use `require.resolve`
require.resolve("my-awesome-prettier-plugin"),
],
};

修复 getFileInfogetSupportInfo 的类型定义 (#15854 by @auvred)

const plugin: Plugin = {};

prettier.getFileInfo("./file.ext", {
plugins: [plugin],
});

prettier.getSupportInfo({ plugins: [plugin], showDeprecated: true });

其他改进

修复文档中关于 cursorOffset 与 rangeStart/rangeEnd 不兼容的错误声明 (#15750 by @ExplodingCabbage)

实际上,由于 @ds300 的工作,cursorOffset 选项与 rangeStart/rangeEnd 兼容已有五年多。然而,Prettier 的文档(包括 CLI 的 --help 文本)一直错误地声称它们不兼容。现在文档已经修复。