Prettier 1.6:配置文件与JSX改进
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
本次更新为Prettier添加了配置文件支持,并对JSX排版进行了重要增强。
特别感谢 @azz 在版本发布期间的贡献——由于我休假和团队变动导致投入时间减少,他承担了仓库维护工作并实现了本版本的诸多变更。
重要更新
配置
通过cosmiconfig实现工作区配置 (#2434) by @azz
自Prettier首个版本发布以来,用户一直呼吁支持.prettierrc文件。我们始终致力于精简配置选项,避免让开发者在新项目初始化时又增加一个必须的.dotfile。
但现实需求是:我们需要统一的配置机制来协调所有集成环境。此前缺乏标准方案导致各工具采用互不兼容的处理方式,现在Prettier将自行解决这个问题。
// .prettierrc
{
"trailingComma": "es5",
"singleQuote": true
}
配置文件支持详情请参阅 README文档。
支持.prettierignore文件 (#2412) by @evilebottnawi
除了指定配置规则外,现在可通过.prettierignore文件声明需要排除格式化的文件。
## .prettierignore
dist/
package.json
JSX
优化JSX格式化效果 (#2398) by @suchipi
此前阻碍用户采用Prettier的主要痛点在于JSX的排版方式。我们全面梳理了相关issue并做出以下调整:
- 当JSX换行时,返回JSX的箭头函数表达式将自动添加圆括号
// Before
const Component = props =>
<div>
Hello {props.name}!
</div>;
// After
const Component = props => (
<div>
Hello {props.name}!
</div>
);
- JSX内部(或包含JSX)的三元表达式改用圆括号进行差异化排版
// Before
<div>
{props.isVisible
? <BaseForm
url="/auth/google"
method="GET"
/>
: <Placeholder />}
</div>;
// After
<div>
{props.isVisible ? (
<BaseForm
url="/auth/google"
method="GET"
/>
) : (
<Placeholder />
)}
</div>
- 逻辑表达式(|| 或 &&)中的JSX在换行时始终包裹圆括号
// Before
<div>
{props.isVisible &&
<BaseForm
url="/auth/google"
method="GET"
/>}
</div>;
// After
<div>
{props.isVisible && (
<BaseForm
url="/auth/google"
method="GET"
/>
)}
</div>
这些改进更符合社区主流JSX书写习惯,有望让Prettier在更多场景落地应用 ;)
JSX单表达式内联处理 (#2442) by @karl
初期JSX处理会保留源码中的大量换行,虽然减少了对代码库的改动,但导致相同语义的代码存在多种排版形式,削弱了格式化工具的一致性价值。
后续每个版本我们都在强化统一排版规则,最新决策是:当JSX对象仅包含单个子元素时,现在会始终采用内联排版。
// Before
return (
<div>
{this.props.test}
</div>
);
return <div>{this.props.test}</div>;
// After
return <div>{this.props.test}</div>;
return <div>{this.props.test}</div>;
确保JSX前导空格后强制换行 (#2348) by @karl
前导空格现在会独占一行。此前出现在标签前的空格会导致异常缩进效果,与其它内容产生视觉偏差。
// Before
<span className="d1">
{' '}<a
href="https://github.schibsted.io/finn/troika"
className="link"
/>
</span>
// After
<span className="d1">
{' '}
<a
href="https://github.schibsted.io/finn/troika"
className="link"
/>
</span>
其他变更
JSON
使用 babylon.parseExpression 解析 JSON (#2476) by @josephfrazier
此前我们使用严格的 JSON 解析器,遇到注释或尾随逗号时会报错。这在实际使用中很不方便,因为许多 JSON 文件在 JavaScript 或 json5 环境下解析时并不如此严格。现在我们放宽了限制,改用 JavaScript 解析器来处理 JSON。这意味着如果原始文件包含注释,它们将被保留。
请注意这是纯增量改进,如果你的原始文件符合 JSON 规范,格式化后仍将保持有效 JSON。
// Before
Syntax error
// After
{ /* some comment */ "a": 1 }
JavaScript
三个及以上链式调用时换行打印 (#2673) by @azz
这是长期存在的链式调用打印问题。Prettier 曾尝试将除最终回调外的所有调用压缩在一行,但这降低了可读性。我们最终采用的解决方案是:当方法链中包含三个及以上函数调用时,始终进行多行换行处理。
// Before
Promise.resolve(0).catch(function(err) {}).then(function(res) {
//
});
// After
Promise.resolve(0)
.catch(function(err) {})
.then(function(res) {
//
});
增加更多监督性括号 (#2423) by @azz
括号处理是个热点话题,因为它们不属于 AST 节点,Prettier 会忽略原始括号并重新生成。我们收集了用户反馈的各种情况,发现当比较运算链式出现且 % 与 * 或 / 混用时,某些边缘情况会令人困惑。
需要说明的是,我们仍会移除基础算术运算符(+-*/)组合中的冗余括号。
// Before
x !== y === z;
x * y % z;
// After
(x !== y) === z;
(x * y) % z;
在 JSX 中支持 prettier-ignore (#2487) by @azz
现在可以在 JSX 表达式中添加注释来忽略下一个元素的格式化,这在需要跳过特定 JSX 块时非常实用。
// Before
<Component>
{/*prettier-ignore*/}
<span ugly format="" />
</Component>
// Before
<Component>
{/*prettier-ignore*/}
<span ugly format='' />
</Component>
不再吞没 prettier-ignore 注释 (#2664)
为支持某些边缘情况,在内部实现中我们采用通用方式避免打印注释,改为在调用处打印。但实际发现使用 prettier-ignore 时,这些注释根本没有被打印!此问题现已修复。
// Before
push(
<td> :)
</td>,
);
// After
push(
// prettier-ignore
<td> :)
</td>,
);
修复 do-while 条件语句的缩进 (#2359) by @jsnajdr
有人报告 do-while 语句在 while 条件多行时格式错误,这花了 6 个月时间,证实了我的直觉:这种结构在实际开发中确实不常用。
// Before
do {} while (
someVeryLongFunc(
someVeryLongArgA,
someVeryLongArgB,
someVeryLongArgC
)
);
// After
do {} while (
someVeryLongFunc(
someVeryLongArgA,
someVeryLongArgB,
someVeryLongArgC
)
);
处理序列表达式换行 (#2388) by @bakkot
JavaScript 的序列表达式是另一个使用率较低的特性。此前当它们跨多行时,我们的打印效果不佳,现在已修正 :)
// Before
(a = b ? c : "lllllllllllllllllllllll"), (a = b
? c
: "lllllllllllllllllllllll"), (a = b ? c : "lllllllllllllllllllllll"), (a = b
? c
: "lllllllllllllllllllllll"), (a = b ? c : "lllllllllllllllllllllll");
// After
(a = b ? c : 'lllllllllllllllllllllll'),
(a = b ? c : 'lllllllllllllllllllllll'),
(a = b ? c : 'lllllllllllllllllllllll'),
(a = b ? c : 'lllllllllllllllllllllll'),
(a = b ? c : 'lllllllllllllllllllllll')
清除注释中的尾部空白 (#2494) by @azz
Prettier 的立场是清除所有尾部空白。虽然我们曾不处理用户生成的注释内容,但这不代表注释中应该保留空白 :)
// Before
// There is some space here ->______________
// After
// There is some space here ->
修复类装饰器中交错的注释 (#2660, #2661)
之前我们对类声明内部的注释处理过于简单粗暴——总是把所有注释移到顶部。现在变得更精准了:我们会保留装饰器之间和extends周围的交错注释。
// Before
// A
// B
// C
@Foo()
@Bar()
class Bar {}
// After
// A
@Foo()
// B
@Bar()
// C
class Bar {}
改进 bind 表达式格式 (#2493) by @azz
bind 表达式正在 TC39 讨论中,我们决定让 Prettier 支持其格式化。之前处理方式很粗糙——只是简单链式拼接。现在采用了与 . 运算符方法链相同的处理逻辑,同时修复了某些边缘情况下输出无效代码的问题。
// Before
observable::filter(data => data.someTest)::throttle(() =>
interval(10)::take(1)::takeUntil(observable::filter(data => someOtherTest))
)::map(someFunction);
// After
observable
::filter(data => data.someTest)
::throttle(() =>
interval(10)::take(1)::takeUntil(observable::filter(data => someOtherTest))
)
::map(someFunction);
添加可选 catch 绑定支持 (#2570) by @existentialism
TC39 正在讨论允许省略catch(e)中的参数。我们确保 Prettier 能支持这种用法。
// Before
Syntax error
// After
try {} catch {}
添加可选链语法支持 (#2572) by @azz
这是 TC39 讨论的另一个新提案——可选链语法。当前是阶段 1 提案,语法可能随时变更。
obj?.prop // optional static property access
obj?.[expr] // optional dynamic property access
func?.(...args) // optional function or method call
正确处理 Closure Compiler 类型转换语法 (#2484) by @yangsu
注释处理本就棘手,特别是当它们的位置决定语义时。我们针对 Closure Compiler 的类型转换注释做了特殊处理,确保其语义保持不变。
// Before
let assignment /** @type {string} */ = getValue();
// After
let assignment = /** @type {string} */ (getValue());
在成员链中内联首个计算属性访问 (#2670) by @azz
计算属性访问换行显示显得怪异,因此我们添加特殊规则将其内联。
// Before
data
[key]('foo')
.then(() => console.log('bar'))
.catch(() => console.log('baz'));
// After
data[key]('foo')
.then(() => console.log('bar'))
.catch(() => console.log('baz'));
Flow
支持 opaque 类型和 export star (#2543, #2542) by @existentialism
Flow 团队推出了两种令人振奋的新语法功能。Prettier 现已支持它们。我个人等待opaque 类型已经太太太太久了!
// Before
Syntax error
// After
opaque type ID = string;
export type * from "module";
移除类型对象和接口中键名的不必要引号 (#2643) by @jackyho112
Prettier 早期版本就对 JavaScript 对象做了引号清理,但当时忘了应用到 Flow 和 TypeScript 类型上。
// Before
type A = {
"string": "A";
}
// After
type A = {
string: "A";
}
修复一元函数类型中的 TypeParameter 打印问题 (#2406) by @danwang
哎呀,在这个特定场景下我们漏掉了泛型参数。
// Before
type myFunction = A => B;
// After
type myFunction = <T>(A) => B;
在 ArrayTypeAnnotation 内部保留 FunctionTypeAnnotation 的括号 (#2561) by @azz
括号问题……终有一天我们会全部解决掉 :)
// Before
const actionArray: () => void[] = [];
// After
const actionArray: (() => void)[] = [];
TypeScript
支持 TypeScript 2.5 RC (#2672) by @azz
TypeScript 2.5 RC 近日发布,现在你也可以在 TypeScript 中使用即将推出的"optional catch binding"语法了 🎉
全局声明中不再添加 namespace 关键字 (#2329) by @azz
// Before
namespace global {
export namespace JSX { }
}
// After
global {
export namespace JSX {}
}
修复 <this.Component /> 的格式化问题 (#2472) by @backus
得益于 JavaScript 无类型和宽松的特性,我们曾能够将 undefined 与字符串拼接,从而产生了一些有趣的代码。现在这个问题已经修复了 :)
// Before
<undefined.Author />
// After
<this.Author />
允许类型断言紧贴包裹 (#2439) by @azz
我们希望确保为 JavaScript 和 Flow 添加的所有特殊情况也适用于 TypeScript 结构。在这种情况下,如果对象被 as 操作符包裹,它们也应该紧贴。
// Before
const state = JSON.stringify(
{
next: window.location.href,
nonce,
} as State
);
// After
const state = JSON.stringify({
next: window.location.href,
nonce,
} as State);
移除二元表达式中类型断言的多余括号 (#2419) by @azz
大多数情况下我们为了正确性而添加括号,但在这个例子中,我们添加了多余的括号,因此可以直接移除它们,让代码更简洁 :)
// Before
(<x>a) || {};
// After
<x>a || {};
在赋值语句中为左侧的类型断言添加括号 (#2525) by @azz
又一个缺少括号的情况。值得庆幸的是,如今这种情况非常少见了,而且只出现在极其罕见的边缘案例中。
// Before
foo.bar as Baz = [bar];
// After
(foo.bar as Baz) = [bar];
为 TSInterfaceDeclaration 打印 declare 关键字 (#2574) by @existentialism
declare 关键字对 interface 没有任何作用,因此我们从不主动添加。但如果你在声明文件中看到除了接口之外的所有内容都有 declare 前缀,会感觉很奇怪。所以现在如果原始代码中有 declare,我们会保留它。
// Before
interface Dictionary<T> {
[index: string]: T
}
// After
declare interface Dictionary<T> {
[index: string]: T
}
CSS
统一 CSS 中的引号使用 (#2624) by @lydell
为了发布第一个 CSS 版本,我们保留了字符串引号的原始状态。现在我们将遵循 prettier 的 singleQuote 选项。难点在于确保为所有特殊情况输出正确的代码,包括转义字符、Unicode 字符、emoji 以及像 charset 这样只支持双引号的特殊规则...
// Before
div {
content: "abc";
}
// After
div {
content: 'abc';
}
统一 CSS 中的数字格式 (#2627) by @lydell
这是另一个可以重用 JavaScript 格式化逻辑来改进 CSS 输出的地方。
// Before
foo {
border: 1px solid rgba(0., 0.0, .0, .3);
}
// After
foo {
border: 1px solid rgba(0, 0, 0, 0.3);
}
在选择器中为未加引号的 CSS 属性值添加引号 (#2644) by @lydell
我始终记不清属性值引号的使用规则,所以我们现在统一为其添加引号。
// Before
a[id=test] {}
// After
a[id="test"] {}
添加对 css 关键字的支持 (#2337) by @zanza00
// Before
const header = css`.top-bar {background: black;margin: 0;position: fixed;}`
// After
const header = css`
.top-bar {
background: black;
margin: 0;
position: fixed;
}
`;
支持 styled-components 中引用现有组件 (#2552, #2619) by @azz
styled-components 提供了多种将模板字面量标记为 CSS 的变体。虽然将这些方式硬编码到 Prettier 中并非理想方案,但既然已经开始了,索性就完整实现。
styled(ExistingComponent)`
css: property;
`;
styled.button.attr({})`
border: rebeccapurple;
`;
裁剪后代组合器中的空白字符 (#2411) by @azz
我们使用的 CSS 解析器无法提供 100% 的语义树:它们常在解析失败时直接返回原始输入。因此我们需要在保持正确性的前提下清理这些内容。此前我们原样输出选择器之间的空格,但现在明确知道用单个空格替换才是正确做法。
// Before
.hello
.how-you-doin {
height: 42;
}
// After
.hello .how-you-doin {
height: 42;
}
解析前移除 BOM 标记 (#2373) by @azz
处理 BOM 曾是我前世的噩梦。所幸在 2017 年这已不再是严重问题,多数工具都能识别它。感谢 @azz 修复了 CSS 解析的相关边界情况。
// Before
[BOM]/* Block comment *
html {
content: "#{1}";
}
// After
[BOM]/* Block comment */
html {
content: "#{1}";
}
GraphQL
支持 GraphQL 的局部格式化 (#2319) by @josephfrazier
此前在 GraphQL 文件中使用局部格式化功能会抛出异常,现已修复为仅格式化选定内容。
增加 .gql 扩展名识别为 GraphQL (#2357) by @rrdelaney
在 Facebook,我们使用 .graphql 扩展名,但 .gql 也很常见,在确定使用哪个解析器的启发式方法中支持它的成本很低。
CLI
支持忽略模式的多匹配规则 (#2356) by @evilebottnawi
此前已支持多个匹配规则叠加,本次更新允许添加忽略文件的通配符模式,对忽略深层嵌套文件夹特别实用。
prettier --write '{**/*,*}.{js,jsx,json}' '!vendor/**'
使 --list-different 支持 --stdin 模式 (#2393) by @josephfrazier
这是检测代码是否需要格式化的便捷方式。相关功能早已存在,本次更新只是完善了它们的协同工作逻辑。
$ echo 'call ( ) ;' | prettier --list-different
(stdin)
$ echo $?
1
