跳至主内容区

Prettier 3.6:实验性快速 CLI 及全新 OXC 与 Hermes 插件!

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

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

本次发布包含多个重要功能更新,我们非常高兴能与您分享。

首先,我们推出了一项实验性高性能 CLI 功能(通过 --experimental-cli 标志启用)。此前该 CLI 仅在 prettier@next 中可用,现在您只需使用一个标志即可启用。我们鼓励您尝试并提供反馈!如果您对其内部实现感兴趣,请阅读 Fabio 撰写的 Prettier CLI 性能深度解析

此外,我们发布了两个新的官方插件:@prettier/plugin-oxc@prettier/plugin-hermes。这些插件独立于 Prettier 核心包提供。

我们要衷心感谢所有促成此次精彩发布的贡献者:@fabiospampinato@43081j@pralkarz 以及新 CLI 的贡献者;OXC 贡献者 @boshen@overlookmotel 及其他成员;以及 Meta 的 FlowHermes 团队。感谢所有人的卓越贡献!

期待这些新功能能提升您的开发体验。祝您代码格式化愉快!

重要更新

CLI

支持实验性 CLI (#17151, #17396 by @fisker)

您可能已听说或使用过我们性能大幅提升的新版 CLI。从 Prettier 3.6 开始,您无需安装不稳定的 v4 版本即可使用。

# Run CLI with `--experimental-cli`
prettier . --check --experimental-cli

# Or use environment variable `PRETTIER_EXPERIMENTAL_CLI=1`
PRETTIER_EXPERIMENTAL_CLI=1 prettier . --check

JavaScript

新增官方插件 @prettier/plugin-oxc (#17472, #17483 by @fisker)

@prettier/plugin-oxc 基于 OXC(使用 Rust 编写的高性能 JavaScript/TypeScript 解析器)。

该插件包含两个新解析器:oxc(JavaScript 语法)和 oxc-ts(TypeScript 语法),使用方法如下:

  1. 安装插件

    yarn add --dev prettier @prettier/plugin-oxc
  2. .prettierrc 文件中添加配置

    plugins:
    - "@prettier/plugin-oxc"

由于包体积限制,此插件未包含在 prettier 主包中,需要单独安装。

更多信息请参见插件主页

特别感谢 OXC 团队(@boshen@overlookmotel 及其他贡献者)。

Flow

新增官方插件 @prettier/plugin-hermes (#17520 by @fisker)

@prettier/plugin-hermes 基于 Hermes JS 引擎

此插件包含一个新的解析器 hermes(用于 Flow 语法),使用方式如下:

  1. 安装插件

    yarn add --dev prettier @prettier/plugin-hermes
  2. .prettierrc 文件中添加以下内容:

    plugins:
    - "@prettier/plugin-hermes"

由于包体积限制,此插件未包含在 prettier 主包中,需要单独安装。

我们计划在 v4 版本中将其作为 Flow 语法的默认解析器,同时将移除 babel-flow 解析器,请尝试使用。

更多信息,请查看插件主页

衷心感谢 Hermes 团队。

其他变更

JavaScript

ReturnStatementExpressionStatement 中为 SequenceExpression 添加括号 (#17085 by @TYKevin)

// Input
function a() {
return ( a, b)
}

(a(), b());

// Prettier 3.5
function a() {
return a, b;
}

a(), b();

// Prettier 3.6
function a() {
return (a, b);
}

(a(), b());

在类属性键中为 AssignmentExpression 添加括号 (#17145 by @fisker)

此前我们仅在对象键中为 AssignmentExpression 添加括号,而未在类属性键中添加。感谢 Biome 让我们注意到这一不一致性。

// Input
a = {
[(x = "key")]: 1,
}

class A {
[(x = "property")] = 1;
[(x = "method")]() {}
}

// Prettier 3.5
a = {
[(x = "key")]: 1,
};

class A {
[x = "property"] = 1;
[(x = "method")]() {}
}

// Prettier 3.6
a = {
[(x = "key")]: 1,
};

class A {
[(x = "property")] = 1;
[(x = "method")]() {}
}

在可选成员表达式中为数字添加括号 (#17190 by @fisker)

在 Prettier 3.6 之前,当成员表达式的对象为数字时,格式化存在不一致性。

使用 babel 解析器(以及其他基于 Babel 的解析器)时,我们会将数字打印为无括号形式;而使用 typescript 解析器(以及其他 ESTree 解析器)时,则会添加括号。

从技术上讲,括号并非必需,但如果我们将其打印为 1?.toString(),而用户后来发现无需使用 ?.,则无法直接删除问号,因为 1.toString() 会引发 SyntaxError。因此,我们决定始终为其添加括号。

// Input
(1)?.toString();
(1.5)?.toString();

// Prettier 3.5 (--parser=babel)
1?.toString();
1.5?.toString();

// Prettier 3.5 (--parser=typescript)
(1)?.toString();
(1.5)?.toString();

// Prettier 3.6
(1)?.toString();
(1.5)?.toString();

移除对实验性 Records & Tuples 的支持 (#17363 by @fisker)

ES 提案 JavaScript Records & Tuples Proposal 已被撤回。

保留 CSS 单词与嵌入表达式之间的空格 (#17398 by @sosukesuzuki)

// Input
const Heading = styled.h1`
font-size: var(--font-size-h${level});
`;

// Prettier 3.5
const Heading = styled.h1`
font-size: var(--font-size-h ${level});
`;

// Prettier 3.6
const Heading = styled.h1`
font-size: var(--font-size-h${level});
`;

修复赋值格式不一致问题 (#17469 by @fisker)

// Input
didScheduleRenderPhaseUpdateDuringThisPassFoo = didScheduleRenderPhaseUpdate = true

// Prettier 3.5 (--parser=babel)
didScheduleRenderPhaseUpdateDuringThisPassFoo =
didScheduleRenderPhaseUpdate = true;

// Prettier 3.5 (--parser=typescript)
didScheduleRenderPhaseUpdateDuringThisPassFoo = didScheduleRenderPhaseUpdate =
true;

// Prettier 3.6
didScheduleRenderPhaseUpdateDuringThisPassFoo =
didScheduleRenderPhaseUpdate = true;

修复成员链格式不一致问题 (#17470 by @fisker)

// Input
s.get(u)?.trigger({ triggerKind: y.SignatureHelpTriggerKind.InvokeFooBarBaz123 });

// Prettier 3.5 (--parser=babel)
s.get(u)?.trigger({
triggerKind: y.SignatureHelpTriggerKind.InvokeFooBarBaz123,
});

// Prettier 3.5 (--parser=typescript)
s
.get(u)
?.trigger({ triggerKind: y.SignatureHelpTriggerKind.InvokeFooBarBaz123 });

// Prettier 3.6
s.get(u)?.trigger({
triggerKind: y.SignatureHelpTriggerKind.InvokeFooBarBaz123,
});

修复可选链作为计算键的问题 (#17486 by @fisker)

// Input
const a = { [y?.z]() {} };
class A { [y?.z]() {} };

// Prettier 3.5
const a = { [y?.z]?() {} };
class A {
[y?.z]?() {}
}

// Prettier 3.6
const a = { [y?.z]() {} };
class A {
[y?.z]() {}
}

acornmeriyah 解析器支持类型转换注释 (#17491, #17566 by @ArnaudBarre, #17600 by #fisker)

此前该功能仅由 Babel 解析器支持。

// Input
/** @type {MyType} */ (x).foo;

// Prettier 3.5 (--parser=acorn|meriyah)
/** @type {MyType} */ x.foo;

// Prettier 3.6
/** @type {MyType} */ (x).foo;

修复带标签模板字符串中的注释格式不稳定问题 (#17510 by @fisker)

// Input
foo
// Comment
`x`

// Prettier 3.5 (First format)
foo// Comment
`x`;

// Prettier 3.5 (Second format)
foo // Comment
`x`;

// Prettier 3.6
foo // Comment
`x`;

提升可选方法调用中 JSX 格式的一致性 (#17616 by @seiyab)

// Input
<SuspendyTree>
<div style={{ height: 200, overflow: "scroll" }}>
{Array(20)
.fill()
?.map((_, i) => (
<h2 key={i}>{i + 1}</h2>
))}
</div>
</SuspendyTree>;

// Prettier 3.5 (ESTree based parsers like espree and typescript)
<SuspendyTree>
<div style={{ height: 200, overflow: "scroll" }}>
{Array(20)
.fill()
?.map((_, i) => <h2 key={i}>{i + 1}</h2>)}
</div>
</SuspendyTree>;

// Prettier 3.5 (babel and babel-ts parser)
<SuspendyTree>
<div style={{ height: 200, overflow: "scroll" }}>
{Array(20)
.fill()
?.map((_, i) => (
<h2 key={i}>{i + 1}</h2>
))}
</div>
</SuspendyTree>;

// Prettier 3.6 (parsers of both types)
<SuspendyTree>
<div style={{ height: 200, overflow: "scroll" }}>
{Array(20)
.fill()
?.map((_, i) => (
<h2 key={i}>{i + 1}</h2>
))}
</div>
</SuspendyTree>;

TypeScript

TSImportType 中支持导入类型属性 (#16881 by @fisker)

// Input
type A = import("foo", {with: {type: "json"}})

// Prettier 3.5
type A = import("foo")

// Prettier 3.6
type A = import("foo", { with: { type: "json" } });

修复逻辑表达式和交集类型中的注释问题 (#17193 by @fisker)

// Input
export type ErrorLike =
SerializedProps<Error> &
// cause is a new addition to Error that is not yet available in all runtimes. We have added
// it to try and pinpoint additional reasoning for failures such as Node's fetch.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
{ cause?: unknown };

// Prettier 3.5
export type ErrorLike =
SerializedProps<Error> & // cause is a new addition to Error that is not yet available in all runtimes. We have added
// it to try and pinpoint additional reasoning for failures such as Node's fetch.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
{ cause?: unknown };

// Prettier 3.5 (second format)
export type ErrorLike =
SerializedProps<Error> & // it to try and pinpoint additional reasoning for failures such as Node's fetch. // cause is a new addition to Error that is not yet available in all runtimes. We have added
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
{ cause?: unknown };

// Prettier 3.6
export type ErrorLike = SerializedProps<Error> &
// cause is a new addition to Error that is not yet available in all runtimes. We have added
// it to try and pinpoint additional reasoning for failures such as Node's fetch.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/cause
{ cause?: unknown };

改进映射类型中的换行检测 (#17498 by @fisker)

// Input
type A = { readonly
[A in B]: T}

// Prettier 3.5
type A = {
readonly [A in B]: T;
};

// Prettier 3.6
type A = { readonly [A in B]: T };

避免在 prettier-ignore 标记的索引签名后打印多余分号 (#17538 by @sosukesuzuki)

// Input
type foo = {
// prettier-ignore
[key: string]: bar;
};

// Prettier 3.5
type foo = {
// prettier-ignore
[key: string]: bar;;
};

// Prettier 3.6
type foo = {
// prettier-ignore
[key: string]: bar;
};

Flow

修复 ConditionalTypeAnnotation 中缺失的括号 (#17196 by @fisker)

// Input
type T<U> = 'a' | ('b' extends U ? 'c' : empty);
type T<U> = 'a' & ('b' extends U ? 'c' : empty);

// Prettier 3.5
type T<U> = "a" | "b" extends U ? "c" : empty;
type T<U> = "a" & "b" extends U ? "c" : empty;

// Prettier 3.6
type T<U> = "a" | ("b" extends U ? "c" : empty);
type T<U> = "a" & ("b" extends U ? "c" : empty);

JSON

允许格式化仅含注释的 JSONC 文件 (#17269 by @fisker)

// Input
// Comment

// Prettier 3.5
SyntaxError: Unexpected token (1:11)
> 1 | // Comment
| ^

// Prettier 3.6
// Comment

禁止使用带括号的表达式 (#17598 by @fisker)

// Input
[1, (2)]

// Prettier 3.5
[1, 2]

// Prettier 3.6
SyntaxError: 'ParenthesizedExpression' is not allowed in JSON. (1:5)
> 1 | [1, (2)]
| ^^^

CSS

支持 Tailwind 的 @utility 指令 (#17362 by @sosukesuzuki)

此项变更支持 Tailwind CSS V4 的 @utility 指令。

/* Input */
@utility tab-* {
tab-size: --value(--tab-size-*);
}

/* Prettier 3.5 */
@utility tab-* {
tab-size: --value(--tab-size- *);
}

/* Prettier 3.6 */
@utility tab-* {
tab-size: --value(--tab-size-*);
}

移除 :has 伪类调用的额外缩进 (#17541 by @sosukesuzuki)

/* Input */
li:has(
path[d="M544,272H480V150.627L523.314,107.314A16,16,0,0,0,500.686,84.687L457.373,128H415a127.00381,127.00381,0,1,0-254,0H118.627L75.314,84.687A16,16,0,1,0,52.686,107.314L96,150.627V272H32a16,16,0,0,0,0,32H96v24a174.98856,174.98856,0,0,0,30.484,98.889L68.687,484.686a15.99972,15.99972,0,1,0,22.627,22.627l55.616-55.616A175.45165,175.45165,0,0,0,272,504h32a175.45165,175.45165,0,0,0,125.07-52.303l55.616,55.616a15.99972,15.99972,0,0,0,22.627-22.627l-57.797-57.797A174.98856,174.98856,0,0,0,480,328V304h64a16,16,0,0,0,0-32ZM288,32.01263A95.99568,95.99568,0,0,1,383,128H193A95.99568,95.99568,0,0,1,288,32.01263ZM448,328c0,79.401-64.598,144-144,144V236a12.00052,12.00052,0,0,0-12-12h-8a12.00052,12.00052,0,0,0-12,12V472c-79.402,0-144-64.599-144-144V160H448Z"]
) {
display: none;
}

/* Prettier 3.5 */
li:has(
path[d="M544,272H480V150.627L523.314,107.314A16,16,0,0,0,500.686,84.687L457.373,128H415a127.00381,127.00381,0,1,0-254,0H118.627L75.314,84.687A16,16,0,1,0,52.686,107.314L96,150.627V272H32a16,16,0,0,0,0,32H96v24a174.98856,174.98856,0,0,0,30.484,98.889L68.687,484.686a15.99972,15.99972,0,1,0,22.627,22.627l55.616-55.616A175.45165,175.45165,0,0,0,272,504h32a175.45165,175.45165,0,0,0,125.07-52.303l55.616,55.616a15.99972,15.99972,0,0,0,22.627-22.627l-57.797-57.797A174.98856,174.98856,0,0,0,480,328V304h64a16,16,0,0,0,0-32ZM288,32.01263A95.99568,95.99568,0,0,1,383,128H193A95.99568,95.99568,0,0,1,288,32.01263ZM448,328c0,79.401-64.598,144-144,144V236a12.00052,12.00052,0,0,0-12-12h-8a12.00052,12.00052,0,0,0-12,12V472c-79.402,0-144-64.599-144-144V160H448Z"]
) {
display: none;
}

/* Prettier 3.6 */
li:has(
path[d="M544,272H480V150.627L523.314,107.314A16,16,0,0,0,500.686,84.687L457.373,128H415a127.00381,127.00381,0,1,0-254,0H118.627L75.314,84.687A16,16,0,1,0,52.686,107.314L96,150.627V272H32a16,16,0,0,0,0,32H96v24a174.98856,174.98856,0,0,0,30.484,98.889L68.687,484.686a15.99972,15.99972,0,1,0,22.627,22.627l55.616-55.616A175.45165,175.45165,0,0,0,272,504h32a175.45165,175.45165,0,0,0,125.07-52.303l55.616,55.616a15.99972,15.99972,0,0,0,22.627-22.627l-57.797-57.797A174.98856,174.98856,0,0,0,480,328V304h64a16,16,0,0,0,0-32ZM288,32.01263A95.99568,95.99568,0,0,1,383,128H193A95.99568,95.99568,0,0,1,288,32.01263ZM448,328c0,79.401-64.598,144-144,144V236a12.00052,12.00052,0,0,0-12-12h-8a12.00052,12.00052,0,0,0-12,12V472c-79.402,0-144-64.599-144-144V160H448Z"]
) {
display: none;
}

Less

修复函数参数被错误转换为小写的问题 (#17502 by @fisker)

// Input
.what {
.make-modifier(1A, "1a.png");
.make-modifier(AA, "1a.png");
}

// Prettier 3.5
.what {
.make-modifier(1a, "1a.png");
.make-modifier(AA, "1a.png");
}

// Prettier 3.6
.what {
.make-modifier(1A, "1a.png");
.make-modifier(AA, "1a.png");
}

HTML

修复当标签名是对象原型属性时的格式化问题 (#17501 by @fisker)

<!-- Input -->
<constructor>
text
</constructor>

<!-- Prettier 3.5 -->
TypeError: Vn(...).startsWith is not a function

<!-- Prettier 3.6 -->
<constructor> text </constructor>

Angular

支持 Angular 19.2 中引入的 TemplateLiteral (#17238 by @fisker)

Angular 19.2 增加了TemplateLiteral 的支持

<!-- Input -->
<div>{{ `Hello, ${
getName('world')}` }}</div>

<!-- Prettier 3.5 -->
<div>
{{ `Hello, ${
getName('world')}` }}
</div>

<!-- Prettier 3.6 -->
<div>{{ `Hello, ${getName("world")}` }}</div>

移除 Angular @for 控制流中 track 后面的额外冒号 (#17280 by @claudio-herger)

// Input
@for (item of items; let i = $index; let count = $count; track block) {}

// Prettier 3.5
@for (item of items; let i = $index; let count = $count; track: block) {}

// Prettier 3.6
@for (item of items; let i = $index; let count = $count; track block) {}

支持 Angular 20 (#17534 by @fisker)

// Input
{{
( (a in (b)))
}}
{{
( (tag ` a ${ b } \u0063 `))
}}
{{
( (` a ${ b } \u0063 `))
}}
{{ void(1 + 2) }}

// Prettier 3.5
The new syntax is not correctly recognized.

// Prettier 3.6
{{ a in b }}
{{ tag` a ${b} \u0063 ` }}
{{ ` a ${b} \u0063 ` }}
{{ void (1 + 2) }}

MJML

支持在 <mj-style> 标签内格式化 CSS (#17338 by @iryusa)

<!-- Input -->
<mj-style>
.hello {
color: blue;
border: 1px solid blue;
font-size:12px;
} p { font-size: 14px; }
</mj-style>

<!-- Prettier 3.5 -->
<mj-style>
.hello { color: blue; border: 1px solid blue; font-size:12px; } p { font-size:
14px; }
</mj-style>

<!-- Prettier 3.6 -->
<mj-style>
.hello {
color: blue;
border: 1px solid blue;
font-size: 12px;
}
p {
font-size: 14px;
}
</mj-style>

正确解析 <mj-style><mj-raw> 标签 (#17400 by @fisker)

<!-- Input -->
<mj-style>
a::before {
content: "</p>";
}
</mj-style>

<!-- Prettier 3.5 -->
SyntaxError: Unexpected closing tag "p".

<!-- Prettier 3.6 -->
Correctly parsed as CSS.

Markdown

修复块引用中相邻 Markdown 语法的问题 (#16596 by @fiji-flo)

<!-- Input -->
> `x`
> `y`

> _x_
> _y_

> [foo](http://foo)
> [bar](http://bar)

> `this` behaves
> `correctly`

<!-- Prettier 3.5 -->
> `x` > `y`

> _x_ > _y_

> [foo](http://foo) > [bar](http://bar)

> `this` behaves `correctly`

<!-- Prettier 3.6 -->
> `x` `y`

> _x_ _y_

> [foo](http://foo) [bar](http://bar)

> `this` behaves `correctly`

修复 Markdown 在列表中意外插入换行的问题 (#16637 by @byplayer)

<!-- Input -->
- Level 1
- Level 1-1

- Level 2

<!-- Prettier 3.5 -->
- Level 1

- Level 1-1

- Level 2


<!-- Prettier 3.6 -->
- Level 1
- Level 1-1

- Level 2

修复粗体强调格式问题 (#17143 by @fiji-flo)

大多数 Markdown 实现不支持 1**_2_**3,因此改用 1***2***3

<!-- Input -->
1***2***3
1**_2_**3

<!-- Prettier 3.5 -->
1**_2_**3
1**_2_**3

<!-- Prettier 3.6 -->
1***2***3
1***2***3

YAML

避免在空的映射或序列前添加换行 (#16074 by @BapRx)

# Input
---
myDict: {}
# comment
myList: []
# comment

# Prettier 3.5
---
myDict:
{}
# comment
myList:
[]
# comment

# Prettier 3.6
---
myDict: {}
# comment
myList: []
# comment

API

plugins 选项中接受 URL (#17166 by @fisker)

plugins 选项现在在所有公共 API 中接受带有 file: 协议的 URL 或以 file: 开头的 URL 字符串。

// `URL`
await prettier.check("foo", {
parser: "my-cool-parser",
plugins: [new URL("./path/to/plugin.js", import.meta.url)],
});
await prettier.format("foo", {
parser: "my-cool-parser",
plugins: [new URL("./path/to/plugin.js", import.meta.url)],
});
await prettier.formatWithCursor("foo", {
parser: "my-cool-parser",
cursorOffset: 2,
plugins: [new URL("./path/to/plugin.js", import.meta.url)],
});
await prettier.getFileInfo("/path/to/file", {
plugins: [new URL("./path/to/plugin.js", import.meta.url)],
});
await prettier.getSupportInfo({
plugins: [new URL("./path/to/plugin.js", import.meta.url)],
});

// URL string
await prettier.check("foo", {
parser: "my-cool-parser",
plugins: ["file:///path/to/plugin.js"],
});
await prettier.format("foo", {
parser: "my-cool-parser",
plugins: ["file:///path/to/plugin.js"],
});
await prettier.formatWithCursor("foo", {
parser: "my-cool-parser",
cursorOffset: 2,
plugins: ["file:///path/to/plugin.js"],
});
await prettier.getFileInfo("/path/to/file", {
plugins: ["file:///path/to/plugin.js"],
});
await prettier.getSupportInfo({
plugins: ["file:///path/to/plugin.js"],
});

接受在 resolveConfig 中使用 URL 作为自定义配置文件 (#17167 by @fisker)

prettier.resolveConfig() 现在接受使用 URL(需为 file: 协议)或以 file: 开头的 URL 字符串作为自定义配置文件路径。

// `URL`
await prettier.resolveConfig("path/to/file", {
config: new URL("/path/to/prettier-config-file", import.meta.url),
});

// URL string
await prettier.resolveConfig("path/to/file", {
config: "file:///path/to/prettier-config-file",
});

停止将 *.frag 文件识别为 JavaScript 文件 (#17178 by @fisker)

此前 *.frag 文件被识别为 JavaScript 文件,但 .frag 扩展名也用于 GLSL(OpenGL 着色语言),导致 Prettier 尝试将其格式化为 JavaScript 时出错。

自 Prettier 3.6 起,除 *.start.frag*.end.fragstart.fragend.frag 之外的 *.frag 文件不再被视为 JavaScript 文件。

如果您的 JavaScript 文件使用了不匹配上述模式的 .frag 扩展名,可通过 overrides 配置覆盖此行为。

export default {
overrides: {
files: "**/*.frag",
options: {
parser: "babel",
},
},
};

languages API 添加 isSupported 函数支持 (#17331 by @JounQin, #17490 by @fisker)

此前自定义插件的 languages API 仅支持通过文件基本名或扩展名推断解析器。

Prettier 3.6 新增了 isSupported: (options: { filepath: string }) => boolean 函数,允许插件根据完整路径(例如特定目录中的文件)检查文件是否受支持。

备注

Prettier 无法保证 filepath 在磁盘上实际存在。
通过 API(如 prettier.format())调用时,也无法保证该路径有效。

若未提供 isSupported 函数,其行为将与之前版本保持一致。

export const languages = [
{
name: "foo",
parsers: ["foo"],
isSupported: ({ filepath }) => filepath.includes(".foo"),
},
];

新增 mjml 解析器 (#17339 by @fisker)

此前版本已通过 html 解析器支持 MJML,为区分 MJML 特定语法,现新增专用 mjml 解析器。

通过 --check-ignore-pragma 忽略文件 (#17344 by @wnayes)

单个文件现在可通过在文件顶部添加 @noformat@noprettier 的 "pragma" 注释来跳过格式化。

启用此功能需使用新选项 --check-ignore-pragma(或通过配置文件/API 的 checkIgnorePragma 选项)。

语言插件可实现该功能支持。大多数内置解析器(包括 JavaScript(TypeScript)、CSS、HTML、JSON、Markdown(MDX)、YAML 和 GraphQL)均已更新支持此功能。

/**
* @noformat
*/

export default matrix(
1, 0, 0,
0, 1, 0,
0, 0, 1
);

修复 prettier.getFileInfo() 中的插件加载问题 (#17548 by @fisker)

在先前版本中,prettier.getFileInfo() 仅从 .prettierrc 读取 parser 配置,但未加载插件来通过插件的 languages 推断解析器。Prettier 3.6 已修复此问题。

// prettier-plugin-foo
export const languages = [
{
parsers: ["foo"],
extensions: [".foo"],
},
];
# .prettierrc
plugins:
- prettier-plugin-foo
prettier --file-info file.foo

# Prettier 3.5
{ "ignored": false, "inferredParser": null }

# Prettier 3.6
{ "ignored": false, "inferredParser": "foo" }

允许插件在推断解析器时覆盖内置解析器 (#17549 by @fisker)

先前在推断文件解析器时,内置插件会被优先检查,导致第三方插件无法覆盖 .js 等文件的解析器。

// prettier-plugin-foo
export const languages = [
{
parsers: ["foo"],
extensions: [".js"],
},
];
// prettier.config.js
import * as prettierPluginFoo from "prettier-plugin-foo";

export default {
plugins: [prettierPluginFoo],
};
prettier --file-info file.js

# Prettier 3.5
{ "ignored": false, "inferredParser": "babel" }

# Prettier 3.6
{ "ignored": false, "inferredParser": "foo" }

CLI

禁止同时使用 --config--no-config (#12221 by @Balastrong)

$ prettier --config=.prettierrc --no-config .
[error] Cannot use --no-config and --config together.

--cache-strategy=content 时忽略文件修改时间 (#17438 by @fisker)

在先前版本中,使用 --cache-strategy=content 时,即使文件内容未改变,只要修改时间变化就会重新格式化。Prettier 3.6 已修复此问题。

修复无法格式化文件的提示信息 (#17505 by @fisker)

touch unknown
prettier --check unknown

# Prettier 3.5
Checking formatting...
unknown
[error] No parser could be inferred for file "</path/to/unknown>".
All matched files use Prettier code style!

# Prettier 3.6
Checking formatting...
unknown
[error] No parser could be inferred for file "</path/to/unknown>".
Error occurred when checking code style in the above file.

修复解析器无法推断时的 exitCode (#17505 by @fisker)

touch unknown
prettier --check unknown > /dev/null;echo $?

# Prettier 3.5
[error] No parser could be inferred for file "</path/to/unknown>".
0

# Prettier 3.6
[error] No parser could be inferred for file "</path/to/unknown>".
2

其他改进

修复 cursorOffset 的嵌入式格式化问题 (#17254 by @fisker)

<!-- Input (--cursor-offset=1) -->
# Angular note

```typescript
import {Component} from '@angular/core';

@Component({
selector: 'app-root',
standalone: true,
imports: [],
template: `
<h1>

{{ title }}</h1>
`,
styleUrls: ['./app.component.css'],
})
export class AppComponent {
title = 'default';
}
```

<!-- Prettier 3.5 -->
Error: There are too many 'cursor' in doc.

<!-- Prettier 3.6 -->
# Angular note

```typescript
import { Component } from "@angular/core";

@Component({
selector: "app-root",
standalone: true,
imports: [],
template: `
<h1>
{{ title }}
</h1>
`,
styleUrls: ["./app.component.css"],
})
export class AppComponent {
title = "default";
}
```