Prettier 1.10:Prettier 诞生一周年 🎂
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
Prettier 周年快乐!令人惊叹的是,Prettier 发布仅一年就获得了如此广泛的采用和众多贡献者。在这个特别版本中,我们将对这个项目进行小型回顾。
本次发布本身也令人振奋——Prettier 现已部分支持 .vue 文件,其内部架构经过重构后提供了规范的插件 API,能够支持多种编程语言!
回顾
自 2013 年 gofmt 问世以来,我(@vjeux)就对自动化代码格式化产生了浓厚兴趣。我逐渐发现自动格式化工具能解决诸多日常问题:关于代码风格的争论、手动格式化代码的繁琐、编写代码修改工具的困难...
去年十二月,竟有两位开发者同时在独立开发 JavaScript 美化工具,这无疑是个信号!Pieter Vanderwerff 基于 Flow 基础设施用 Reason 语言构建了一个,而 James Long 则用 JavaScript 实现。我扮演了啦啦队角色:为两个项目搭建相同的测试套件,在现有代码库上运行他们的美化器,并整理出格式化效果不佳的问题清单。
遗憾的是假期结束后,两位开发者都不得不回归本职工作 :( 我决定接手并说服经理让我全职投入。James 已将其项目(Prettier)开源,而我更熟悉 JavaScript,因此决定专注完善这个项目!
以下是本项目运行良好的关键实践:
精心撰写发布说明
发布说明有个有趣特性:即使内容平平也常被广泛传播。大家似乎都想知道某软件的 1.5.8 版本是否发布,这恰是推广项目的良机。
在 Prettier 的版本发布中,我花费大量篇幅解释变更背后的设计理念,并讨论那些本该单独写成博文的内容(比如本文!)。更何况,人总会犯懒找各种借口不写博文,但版本更新却迫在眉睫——这恰好形成良性推动力。
建立清晰的决策机制
代码风格规则最具争议性,同时需要大量决策。我尝试设计了一套能推动进展的决策流程。
人们常诉诸情感化论据来证明自己的风格偏好最优。我们需要纯粹理性的标准——即使结果不符合个人偏好,也无法质疑方法论本身。
我找到的最佳方案是统计 Facebook 代码库中各变体的使用频次。通过呈现相对数据(风格 A 的使用量是风格 B 的 5 倍)易于说服他人。一个由数千人多年维护的大型代码库形成的惯例,或许不是最优解,但通常不会引发巨大争议。
并非所有问题都适用此法:有些方案难分高下,或难以设计优雅的格式化算法。此时需要建立升级机制,由单人(我)最终决策。这样无需达成艰难共识也能持续推进。
将开源社区作为试验田
一个完整的代码格式化工具属于那种需要近乎完美才能引入像 Facebook 这样大型代码库的项目。第一印象至关重要,要确保在所有边缘情况下都能正确处理(不会生成无效 JavaScript 或改变代码行为)非常困难。
我最初的计划是独自闭关开发六个月,直到所有功能都完美实现后再推广使用。没想到开源社区的质量标准不同——人们很早就开始使用了。在个人项目中使用 Prettier 的风险远低于在 Facebook 数百万行代码中部署。
事实证明,在整个开发过程中持续获取反馈对项目大有裨益,直到最终达到 Facebook 的启用标准。
开源即贡献者
项目前六个月虽然我全职投入,但最终只贡献了半数提交记录。这本身就令人惊叹!不仅项目推进效率翻倍,社区成员还完成了我无暇顾及的工作:支持所有主流编辑器集成、创建模糊测试工具、添加 TypeScript 支持、实现多语言混合解析框架等等。
虽然 Facebook 内部并未使用全部功能,但其中许多最终派上用场。CSS-in-JS 支持让模板字面量中的 GraphQL 片段格式化变得轻松。庞大的用户群也帮助我们发现了各种冷门 bug(这些隐患最终也会影响我们),众多贡献者及时修复了它们。
最棒的是,六个月前我结束全职工作后,项目在 @azz 带领下持续发展。感谢所有以不同方式提供帮助的伙伴,能与各位共同书写历史令人振奋!
开源为项目创造了奇迹:不仅为 Facebook 带来巨大价值(几乎全部 JavaScript 文件都已格式化),更通过广泛采用惠及整个行业。
工具链:Jest 快照测试与 Playground
这些贡献得以实现,得益于便捷的错误报告、代码提交与审查流程。项目中影响最深远的两个工具是 jest 快照测试和 playground。
快照测试对格式化工具堪称神器。添加新测试易如反掌:只需在测试目录创建目标代码文件并运行 jest,搞定!任何改动后,你都能直观看到大量示例的格式化差异,进而判断优化效果。对审查者同样友好——所有变更内容都清晰呈现为前后对比。我发现自己更关注快照结果而非具体实现逻辑。
Playground 能快速复现问题或体验 Prettier,无需安装开发环境或切换分支。实践证明,这是获取高质量可操作错误报告的宝贵渠道。
重要更新
支持 Vue 单文件组件 (#3563) by @vjeux
Vue SFC 支持需求强烈 (#2097)。我们推出了部分支持方案:HTML 内容保持原样输出,而 <script> 和 <style> 标签现在会通过 Prettier 格式化。
只需在 *.vue 文件上运行 prettier 即可体验!
Prettier 插件 API (#3536) by @azz
随着 Prettier 对 JavaScript 的支持日趋稳定,近期我们收到了许多希望扩展新语言的贡献请求,特别是添加 Python 和 PHP 支持的 PR。我们既要保持核心 prettier 包的可移植性和可维护性,又要让用户能在更多语言中使用 Prettier。为此我们推出了[插件 API]!Prettier 插件可以为格式化工具提供解析器或打印器,它们享有原生支持的地位,甚至能实现嵌入式语言支持(例如在 Markdown 中格式化特定语言代码)。
使用插件非常简单:通过 npm/yarn 安装后,像往常一样运行 Prettier 即可,无需额外配置!
目前有三个官方插件正在开发中:
这三个插件都处于积极开发阶段,尚未达到生产环境使用标准,但请持续关注它们的进展!
若您有兴趣参与插件开发,可查看对应仓库的 issue 列表,或测试您的代码并提交问题报告!同样地,如果您想为 Prettier 开发新语言支持但需要启动帮助,请在 prettier 仓库创建 issue,我们将全力协助。
Prettier 内置语言现已重构为基于插件 API 实现,这能确保核心 API 保持通用性。
更多细节请查阅文档。
请注意:由于这是重要的新功能,我们在文档中将其标记为 beta 版。虽然预计不会出现重大问题,但这意味着下个版本可能存在破坏性变更。
其他变更
TypeScript
支持数字分隔符 (#3580),由 @azz 贡献
数字分隔符是阶段 3 的 ECMAScript 提案。TypeScript 2.7 已支持此特性,现在 Prettier 也会保留这些分隔符。
// Before
SyntaxError: ',' expected. (1:10)
> 1 | var a = 1_000_000_000;
| ^
2 | var b = 0b1101_0101_1001;
3 | var c = 0xAE_FE_2F;
// After
var a = 1_000_000_000;
var b = 0b1101_0101_1001;
var c = 0xAE_FE_2F;
Flow
将 Flow 类型注释注解打印为注释 (#3449),由 @duailibe 贡献
Flow 的类型注释注解无需编译即可实现类型检查。在此版本前,使用 flow 解析器的 Prettier 不会将类型注解打印为注释,导致该功能不可用。现在 Prettier 能正确识别 /*: ... */ 类注释并保持其注释形态。对 /*:: ... */ 注释的支持正在开发中。
// Input
let foo: string = "a";
// Before
let foo: string = "a";
// After
let foo: string = "a";
在箭头函数参数后打印注释 (#3444),由 @duailibe 贡献
使用 babylon 解析器时,Flow 注释存在注释位置错乱等问题。本次发布修复了其中一项,让我们离全面支持该功能更近一步。
// Before
const run = (cmd /*: string */ /*: Promise<void> */) => {};
// After
const run = (cmd /*: string */) /*: Promise<void> */ => {};
当 arrowParens 设为 "always" 时在 FunctionTypeAnnotation 中添加括号 (#3616),由 @duailibe 贡献
在上个版本新增的 arrowParens 选项曾被忽略应用于 flow 函数类型注解。现在当设置 arrowParens 为 always 时,单参数周围将自动添加括号。
// --arrow-parens always
// Before
type SomeType = {
something: number => number
};
// After
type SomeType = {
something: (number) => number
};
JSX
在 JSX 中内联 do 表达式 (#3607) by @vjeux
"Do expressions" 是 ECMAScript 第一阶段提案,在 JSX 中特别有用。为避免不必要的缩进层级,我们已将其内联到表达式容器中。
// Before
{
do {
// ...
}
}
// After
{do {
// ...
}}
修复带注释的箭头属性后意外添加软换行的问题 (#3641) 由 @duailibe
修复了 JSX 属性中意外出现额外换行符的问题。
// --print-width 13 (for demonstration)
// Before
<span
attr={
// comment
() =>
true
}
/>;
// After
<span
attr={
// comment
() =>
true
}
/>;
避免为作为属性的 JSX 元素添加 () 包裹 (#3640) by @duailibe
JSX 规范 允许将元素作为属性传递给另一个元素(隐藏特性)。此前 Prettier 错误添加了括号导致语法错误,此版本已修复。注意:部分解析器支持此特性,但现有 JSX 转换器均不支持,首个正式支持此特性的将是当前处于测试阶段的 Babel 7。
// Before
<Foo
content=(
<div>
<div />
</div>
)
/>
// After
<Foo
content=<div>
<div />
</div>
/>
SCSS
按原样打印选择器内的注释 (#3649) by @vjeux
由于使用的 CSS 选择器解析器不支持注释,导致选择器注释可能被移至错误行。此问题已修复。
/* Before */
// Foo
.foo,
// Bar .bar {
display: block;
}
/* After */
// Foo
.foo,
// Bar
.bar {
display: block;
}
GraphQL
更新 GraphQL 解析器 (#3605) by @vjeux
GraphQL 规范持续演进,Prettier 现已支持三项新特性:
支持使用字符串描述标注类型
"""
Type description
"""
type Foo {
"some description"
someProperty: String!
"""
some really
long description
"""
someOtherProperty: [String!]!
}
内容为空时可省略 {}
extend enum Site @onEnum
支持扩展所有类型
extend input InputType {
other: Float = 1.23e4
}
Markdown
在多解析器中用硬换行替换换行符 (#3611) by @ikatyang
修复了在 Markdown 块引用中嵌入 JavaScript 时,块引用的 > 字符被意外移除的问题。
<!-- before -->
> ````md
> <!-- prettier-ignore -->
> ```js
ugly ( code ) ;
```
> ````
<!-- after -->
> ````md
> <!-- prettier-ignore -->
> ```js
> ugly ( code ) ;
> ```
> ````
使 Markdown 链接标题引号遵循 singleQuote 配置 (#3481) by @j-f1
此前所有链接标题均使用 " 引号,现在将遵循 singleQuote 选项。若标题同时包含 ' 和 ",则使用 () 样式引号。
<!-- --single-quote -->
<!-- Before -->
[ref]: https://prettier.io "Prettier"
[other-ref]: https://example.com "Shakespeare's \"Romeo and Juliet\" is a famous play"
<!-- after -->
[ref]: https://prettier.io 'Prettier'
[other-ref]: https://example.com (Shakespeare's "Romeo and Juliet" is a famous play)
正确打印无替代文本的 Markdown 图片引用 (#3643) by @duailibe
修复了无替代文本的图片引用导致 Prettier 崩溃的边界情况。
<!-- before -->
TypeError: doc is null
<!-- after -->
![][logo]
[logo]: https://github.com/prettier/prettier-logo/blob/master/images/prettier-logo-light.png?raw=true
列表缩进遵循 tabWidth 配置 (#3694) by @ikatyang
此前强制使用 2 空格缩进列表,现改为遵循 tadWidth 配置。
<!-- --tab-width 4 -->
<!-- before -->
* one
* two
<!-- after -->
* one
* two
API
在 getSupportInfo() 中添加 options 字段 (#3433) by @ikatyang
prettier.getSupportInfo().options 现在将返回 Prettier 支持的所有配置项数组。
仅在版本控制目录内查找 .editorconfig (#3559) by @josephfrazier
此前因为 Prettier 支持 .editorconfig 文件且会一直查找到 ~/.editorconfig,导致相同仓库在不同开发者机器上格式化结果不一致(因全局编辑器配置影响)。现改为仅查找至项目根目录,避免意外副作用。
CLI
统一通过日志记录器处理所有 CLI 输出 (#3515) by @azz
--loglevel 选项在少数情况下未被遵循,现已修复,该选项将作用于所有 CLI 日志输出。
# Before
$ prettier --loglevel silent --write test.js
test.js 91ms
# After
$ prettier --loglevel silent --write test.js
忽略文件时保持原样输出 (#3618) by @duailibe
我们修复了 CLI 处理忽略文件时的行为。此前当文件被 .prettierignore 忽略时,CLI 不会输出任何内容,这影响了编辑器集成。新行为如下:
-
使用
--write时,不读取也不写入被忽略的文件。 -
未使用
--write时,读取被忽略的文件并原样输出。
编辑器集成
在 getSupportInfo() 中添加 PostCSS 扩展名 (#3454) by @ardevelop
部分编辑器集成使用 Prettier 的 getSupportInfo 函数动态支持所有语言。本次更新添加了对 *.pcss 文件的支持。
在 getSupportInfo() 中添加"带注释的 JSON"支持 (#3496) by @thorn0
同理,"带注释的 JSON" 文件现在也能被正确识别。
