
Prettier 1.11 版本详解CSS 格式化修复、TypeScript 2.7/2.8 新语法支持与 API 增强【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettierPrettier 1.11.0 是一次以稳定性与正确性为核心的版本发布它大幅修复了 CSS/SCSS/Less 的格式化缺陷at-rule、注释、!default、插值、嵌套选择器等补齐了 TypeScript 2.7/2.8 引入的 definite assignment!:、unique symbol与条件类型等新语法解析能力同时在 JavaScript、Markdown、插件 API 与配置行为上做了多项一致性改进。通过本文你将完整掌握 1.11.0 涉及的每项变更的前后行为差异、底层实现位置以及升级后可以立即使用的配置与命令行用法。版本概览一次以正确格式化为主题的发布1.11.0 的主要基调是修复格式化本身破坏了代码的问题。与新增功能相比这一版本更多地把精力放在了保证 Prettier 输出的结果不会改变代码语义上——无论是 SCSS 中warn/error字符串内容被篡改、Less 中分号被误删导致编译错误还是 Markdown 链接 URL 中的 HTML 实体被破坏这些都属于格式化工具反而引入 bug的典型场景。本次发布的另一个标志性事件是Facebook 代码库的全部 JavaScript 文件已完成 Prettier 化。据发布说明Facebook 并未发起集中的强制迁移而是提供了迁移工具链和文档让各团队自行按目录逐步转换——在 8 个月内 75% 的代码被自然转换最终补齐剩余部分并强制全量使用。这说明 Prettier 在面对大规模真实代码包括各种边缘用例时具备足够的健壮性。从仓库结构看CSS 相关的解析与打印逻辑集中在 src/language-css解析入口 parser-postcss.js打印器 printer-postcss.jsTypeScript 语法则走 language-js/parse/typescript.js 与 language-js/print 目录下的打印器Markdown 相关实现位于 language-markdown。本文后续将逐项结合这些源码说明变更的来龙去脉。CSS/SCSS/Less本轮修复的重头戏1.11.0 的 CSS 系列修复主要来自对 PostCSS 抽象语法树各节点类型的精细化处理。以下是本轮变更的完整清单。格式化 at-rules此前if、include等 at-rule 未被 Prettier 格式化而是原样输出1.11.0 开始它们会被真正排版。在 printer-postcss.js 的css-atrule分支中at-rule 的参数、选择器、值与被包含的节点都会被递归打印并通过group/indent/softline组合实现折行与缩进// Before: .selector { include absolute($top: $long-variable-name, $right: $long-variable-name, $left: auto, $bottom: auto); } // After: .selector { include absolute( $top: $long-variable-name, $right: $long-variable-name, $left: auto, $bottom: auto ); }支持流行的 PostCSS 插件语法一些流行 PostCSS 插件如 postcss-mixins依赖特定的写法而旧版 Prettier 会在格式化时破坏它们。1.11.0 对此类插件约定的写法保持尊重// Before $theme: blue; a { mixin $(theme) -colors; } // After $theme: blue; a { mixin $(theme)-colors; }支持 SCSS 中的 YAML front-matter 块Jekyll 依赖 YAML front-matter 来识别 SCSS 文件而旧版 Prettier 会将其移除导致部署后的 Jekyll 站点失效。1.11.0 会保留 front-matter。在仓库中front-matter 的解析与判定由 src/main/front-matter 模块完成如 is-front-matter.js、parse.jsCSS 解析器在 parser-postcss.js 中将其识别为front-matter节点类型打印器则直接透传见 printer-postcss.js 的case front-matter从而保证样式不被改写--- title: Title description: Description --- a { color: red; }列表套列表的排版优化SCSS 中形如$space-scale: (0, 0) (0.25, 0-25) ...的列表嵌套此前处理不佳1.11.0 改善了其折行行为// Before: $space-scale: (0, 0) (0.25, 0-25) (0.5, 0-5) (0.75, 0-75) (1, 1) ( 1.25, 1-25 ) (1.5, 1-5) (1.75, 1-75) (2, 2) (2.25, 2-25) (2.5, 2-5) ( 2.75, 2-75 ) (3, 3) (3.25, 3-25) (3.5, 3-5) (3.75, 3-75) (4, 4); // After: $space-scale: (0, 0) (0.25, 0-25) (0.5, 0-5) (0.75, 0-75) (1, 1) (1.25, 1-25) (1.5, 1-5) (1.75, 1-75) (2, 2) (2.25, 2-25) (2.5, 2-5) (2.75, 2-75) (3, 3) (3.25, 3-25) (3.5, 3-5) (3.75, 3-75) (4, 4);保留 font-face 声明中的注释旧版会在以下场景中删除src列表中间的注释font-face { font-family: Prettier; src: local(Prettier), /* Local */ url(http://prettier.com/font.woff) /* Network*/ }1.10.2 输出注释丢失font-face { font-family: Prettier; src: local(Prettier), url(http://prettier.com/font.woff); /* Network*/ }1.11.0 输出注释保留font-face { font-family: Prettier; src: local(Prettier), /* Local */ url(http://prettier.com/font.woff); /* Network*/ }行首为括号时的缩进修正此前若某行待缩进内容的第一个字符是括号会被错误地缩进 3 个空格而非 2 个// Before: $longVariable: ( (mobile $mobile) (tablet $tablet) (desktop $desktop) (wide $wide) ); // After: $longVariable: ( (mobile $mobile) (tablet $tablet) (desktop $desktop) (wide $wide) );SCSS map 内不再添加多余空格// Before: $map: map-merge($map, ($key: $value)); // After: $map: map-merge($map, ($key: $value));选择器列表中的 SCSS 尾随注释不再破坏格式旧版会把选择器列表行尾的注释错误地挪动位置破坏整个代码结构// Before formatting: .field { [data-field-id1], // Name [data-field-id2], // Email [data-field-id3], // Postal Code { background: green; } } // After formatting (Prettier 1.10.2): .field { [data-field-id1], // Name [data-field-id2], // Email [data-field-id3] { background: green; } } // After formatting (Prettier 1.11.0): .field { [data-field-id1], // Name [data-field-id2], // Email [data-field-id3], // Postal Code { background: green; } }importurl 后不再错误追加逗号/* Before */ import url(responsive/gt768.css), screen and (min-width: 768px); /* After */ import url(responsive/gt768.css) screen and (min-width: 768px);正确处理!default指令旧版会把!default排版得远离其所属的值现在它会紧贴值之后。在 parser-postcss.js 中解析器通过DEFAULT_SCSS_DIRECTIVE正则/(\s*)(!default).*$/将!default从值中剥离并标记为node.scssDefault打印阶段再统一拼接从而保证格式正确$theme-checkbox-colors: ( default: $theme-color-border, checked: $theme-color-checked, disabled: $theme-color-disabled, disabled-font: $theme-color-font-secondary ) !default;1.11.0 输出$theme-checkbox-colors: ( default: $theme-color-border, checked: $theme-color-checked, disabled: $theme-color-disabled, disabled-font: $theme-color-font-secondary ) !default;不再格式化warn与error指令内容SCSS 允许在构建期用warn/error输出文本。旧版把这两个指令误当作媒体查询处理导致字符串内容被改写如句点前被插入空格// Unformatted code: if ($error) { error An error occurred: (#{$error}).; } // Formatted code (Prettier 1.10.2): if ($error) { error error (#{$error}) .; } // Formatted code (Prettier 1.11.0): if ($error) { error error (#{$error}).; }保留 Less 规则集声明后的分号Less 的分离规则集detached ruleset声明后的分号一旦被删除就会引发编译错误1.11.0 会保留它。对应实现可见 printer-postcss.js 中isDetachedRulesetDeclarationNode(node) ? ; : 的分支逻辑// Before: detached-ruleset: { background: red; } // After: detached-ruleset: { background: red; };空 CSS 文件不再追加末尾换行此前 Prettier 会给 CSS 文件添加文件末尾换行而 JS 文件不会这在引入 Prettier 时会带来不必要的源码控制变更。1.11.0 让 CSS 与 JS 行为保持一致空文件不再追加换行。忽略带 SCSS 插值的media参数旧版会把包含 SCSS 插值的媒体查询破坏掉// Unformatted code: $sm-only: (min-width: 768px) and (max-width: 991px); $lg-and-up: (min-width: 1200px); media screen and #{$sm-only, $lg-and-up} { color: #000; } // Formatted code (Prettier 1.10.2): media screen and, { color: #000; } // Formatted code (Prettier 1.11.0): $sm-only: (min-width: 768px) and (max-width: 991px); $lg-and-up: (min-width: 1200px); media screen and #{$sm-only, $lg-and-up} { color: #000; }选择器运算符与左花括号之间不再打印两个空格SCSS 允许选择器以运算符结尾旧版会在与{之间多打一个空格// Unformatted code: .this .ends .with .an .operator { // content } // Formatted code (Prettier 1.10.2): .this .ends .with .an .operator { // content } // Formatted code (Prettier 1.11.0): .this .ends .with .an .operator { // content }不再更改 SCSS 函数名大小写旧版会在某些场景把 SCSS 函数名错误地小写化getBp→getbp1.11.0 修复了这一点// Unformatted code: include breakpoint (getBp(md)) { :nth-child(2n 3) { clear: both; } } // Formatted code (Prettier 1.10.2): include breakpoint (getbp(md)) { :nth-child(2n 3) { clear: both; } } // Formatted code (Prettier 1.11.0): include breakpoint(getBp(md)) { :nth-child(2n 3) { clear: both; } }保留 Vue 深度选择器两侧空格Vue SFC 中style scoped内常用深度选择器旧版会把两侧空格剥掉导致语义变化!-- Unformatted code: -- style scoped .box .child-component { font-size: 24px; } /style !-- Formatted code (Prettier 1.10.2): -- style scoped .box.child-component { font-size: 24px; } /style !-- Formatted code (Prettier 1.11.0): -- style scoped .box .child-component { font-size: 24px; } /style修复nestat-rule旧版会在传入nest的选择器的周围添加空格从而改变规则实际编译出的选择器// Unformatted code: .title { nest :global(h1) { background: red; } } // Formatted code (Prettier 1.10.2): .title { nest :global(h1) { background: red; } } // Formatted code (Prettier 1.11.0): .title { nest :global(h1) { background: red; } }progid:DXImageTransform.Microsoft.gradient原样输出针对 IE 专属的filter值progid:开头旧版有大量格式化逻辑但效果仍不佳。由于这类值非标准且使用率低1.11.0 决定原样打印、不做格式化。对应的实现策略可以在 parser-postcss.js 看到以progid:开头的值被直接识别为value-unknown节点从而绕过格式化管线。不再小写化嵌套选择器// Unformatted code: .foo { -1Bar { } } // Formatted code: (Prettier 1.10.2): .foo { -1bar { } } // Formatted code: (Prettier 1.11.0): .foo { -1Bar { } }TypeScript跟进 2.7/2.8 新语法TypeScript 2.7 引入的 definite assignment 与unique symbol以及 2.8 的条件类型均在 1.11.0 中获得完整支持。这些能力依赖的解析与打印代码位于 language-js/parse/typescript.js 与 language-js/print 目录对应测试散落在 tests/format/typescript。支持 definite assignment!:TS 2.7 允许在类属性上使用!:表示即使没有初始化器运行时也必然有值而非undefined。旧版直接解析失败class MyComponent { ngModel!: ng.INgModelController; }1.10.2 报错SyntaxError: expected. (2:10) 1 | class MyComponent { 2 | ngModel!: ng.INgModelController; | ^ 3 | }1.11.0 起该语法被完整支持。支持unique symbol类型同样来自 TS 2.7 的unique symbol此前无法解析interface SymbolConstructor { readonly iterator: unique symbol; }1.10.2 报错SyntaxError: ; expected. (2:29) 1 | interface SymbolConstructor { 2 | readonly iterator: unique symbol; | ^ 3 | } 4 |1.11.0 起支持。支持条件类型TS 2.8 的条件类型外形类似类型位置上的三元表达式1.11.0 可以正确解析与排版export type DeepReadonlyT T extends any[] ? DeepReadonlyArrayT[number] : T extends object ? DeepReadonlyObjectT : T;1.10.2 报错SyntaxError: ; expected. (2:7) 1 | export type DeepReadonlyT 2 | T extends any[] ? DeepReadonlyArrayT[number] : | ^ 3 | T extends object ? DeepReadonlyObjectT : 4 | T;不再移除(void 0)!的括号旧版会把(void 0)!的括号移除从而改变程序的 AST 结构1.11.0 修复了这一语义级错误。TSNonNullExpression 参与链式格式化旧版在调用链中出现 TS 非空断言!时不会应用链式折行样式导致代码风格割裂// Unformatted code: this.tokenLoads.get(locator).get(start).push({}); this.tokenLoads.get(locator)!.get(start).push({}); // Formatted code (Prettier 1.10.2): this.tokenLoads .get(locator) .get(start) .push({}); this.tokenLoads.get(locator)!.get(start).push({}); // Formatted code (Prettier 1.11.0): this.tokenLoads .get(locator) .get(start) .push({}); this.tokenLoads .get(locator)! .get(start) .push({});JavaScript格式化一致性与边界修复使用--use-tabs时三元的缩进计算错误Tab 在不同环境下宽度不同旧版用 tab 缩进三元表达式时会出现错误缩进。1.11.0 改为正确计算缩进量// Before: const abc props.something ? xyz : { prop1: a, prop2: b, prop3: false, }; // After: const abc props.something ? xyz : { prop1: a, prop2: b, prop3: false };return参数带注释时不再打印怪异的括号// Unformatted code: func1(function() { return func2 //comment .bar(); }); // Formatted code (Prettier 1.10.2): func1(function() { return (func2 //comment .bar() ); }); // Formatted code (Prettier 1.11.0): func1(function() { return ( func2 //comment .bar() ); });尊重 markdown-in-js 的 0 缩进以md为标签的模板字符串会触发 markdown 子格式化相关实现见 language-js/embed/markdown.js。旧版会强行插入缩进// Unformatted code md This is some markdown // Formatted code (Prettier 1.10.2) md This is some markdown ;这对react-markings等支持缩进的模板标签函数是可接受的但会破坏一些不支持缩进的自有 markdown 格式化器。1.11.0 尊重你在模板中设定的缩进层级——支持缩进的库可以继续缩进不支持的库则可以不缩进// Formatted code (Prettier 1.11.0) md This is some markdown ;AMDdefine调用按 CJSrequire风格格式化AMD 匿名模块常写成define([...], function(...){...})。旧版会把数组和回调函数强行折行产生大量顶层缩进1.11.0 让二者贴紧显著减少缩进层级define([ jquery, common/global.context, common/log.event, some_project/square, some_project/rectangle, some_project/triangle, some_project/circle, some_project/star ], function($, Context, EventLogger, Square, Rectangle, Triangle, Circle, Star) { console.log(some code); });1.10.2 输出大量顶层缩进define( [ jquery, common/global.context, common/log.event, some_project/square, some_project/rectangle, some_project/triangle, some_project/circle, some_project/star ], function($, Context, EventLogger, Square, Rectangle, Triangle, Circle, Star) { console.log(some code); } );1.11.0 输出数组与函数表达式贴紧缩进层级减少define([ jquery, common/global.context, common/log.event, some_project/square, some_project/rectangle, some_project/triangle, some_project/circle, some_project/star ], function( $, Context, EventLogger, Square, Rectangle, Triangle, Circle, Star ) { console.log(some code); });QUnit 的skip与test采用相同格式Prettier 此前对describe/it/test做了特殊格式化但未覆盖 QUnit 的skip导致在test与skip间切换时代码风格不一致// Unformatted code test(this is a really long description of some test I want to go here, function (assert) { assert(something); }); skip(this is a really long description of some test I want to go here, function (assert) { assert(something); }); // Formatted code (Prettier 1.10.2): test(this is a really long description of some test I want to go here, function(assert) { assert(something); }); skip( this is a really long description of some test I want to go here, function(assert) { assert(something); } );1.11.0 将skip与describe/it/test同等对待// Formatted code (Prettier 1.11.0): test(this is a really long description of some test I want to go here, function(assert) { assert(something); }); skip(this is a really long description of some test I want to go here, function(assert) { assert(something); });JSX 字符串字面量属性含换行时按多行处理JSX 规范允许字符串字面量属性包含换行符但旧版不会因此把元素视为多行1.11.0 会正确折行// Unformatted code: path dM4.765 16.829l3.069-2.946 5.813 5.748 11.33-11.232 3.006 3.18-14.36 14.080z /; // Formatted code (Prettier 1.10.2): path dM4.765 16.829l3.069-2.946 5.813 5.748 11.33-11.232 3.006 3.18-14.36 14.080z /; // Formatted code (Prettier 1.11.0): path dM4.765 16.829l3.069-2.946 5.813 5.748 11.33-11.232 3.006 3.18-14.36 14.080z /;Flow 解析器支持 JSX children 的 spread在 JSX children 表达式容器中使用 spread 操作符时旧版 flow 解析器直接报错JSX {...edges} /JSXSyntaxError: Unexpected token ... (2:4) 1 | JSX 2 | {...edges} | ^^^ 3 | /JSX1.11.0 起 flow 解析器支持该语法。修复 flow maybe object 返回类型打印报错旧版遇到 flow 的?{...}对象返回类型会抛异常function getScaledData({x}): ?{foo: number} {}1.11.0 已修复。--insert-pragma不再删除重复 pragma旧版对重复 pragma 的注释块使用--insert-pragma时会误删多余项// Unformatted code: /** * x 1 * x 2 */ // Formatted code (Prettier 1.10.2): /** * format * x 2 */ // Formatted code (Prettier 1.11.0): /** * format * x 1 * x 2 */flow 类型断言三元表达式用括号包裹为提升可读性类型断言的三元表达式现在会被括号包裹// Unformatted code: const foo ((1?2:3): number); // Formatted code (Prettier 1.10.2): const foo (1 ? 2 : 3: number); // Formatted code (Prettier 1.11.0): const foo ((1 ? 2 : 3): number);修复 DeclareExportDeclaration 缺失分号flow 库定义文件.flow中declare export default语句的分号此前会被误删1.11.0 已修复。Markdown缩进、实体与代码块细节列表项后的标题不再被并入列表!-- Unformatted code -- * Something ### Some heading !-- Formatted code (Prettier 1.10.2) -- * Something ### Some heading !-- Formatted code (Prettier 1.11.0) -- * Something ### Some heading链接 URL 中的 HTML 实体不再被解析旧版会把 URL 中的错误地当作 HTML 实体处理被破坏为¤cy1.11.0 原样保留!-- Unformatted code -- [Test](http://localhost:8080/test?languageDEcurrencyEUR) !-- Formatted code (Prettier 1.10.2) -- [Test](http://localhost:8080/test?languageDE¤cyEUR) !-- Formatted code (Prettier 1.11.0) -- [Test](http://localhost:8080/test?languageDEcurrencyEUR)代码块内模板字符串的缩进修正!-- Unformatted code: -- - 1 - 2 - 3 js something asd asd asd !-- Formatted code (Prettier 1.10.2): -- * 1 * 2 * 3 js something asd asd asd ; 1.11.0 中代码块内 JS 模板字符串的缩进被正确计算!-- Formatted code (Prettier 1.11.0): -- * 1 * 2 * 3 js something asd asd asd ; footnoteDefinition 中多行内容的缩进修正包含代码块的脚注此前缩进错误!-- Unformatted code: -- Some text.[^fn] [^fn]: Here is a footnote which includes code. rs fn main() { println!(this is some Rust!); } !-- Formatted code (Prettier 1.10.2): -- Some text.[^fn] [^fn]: Here is a footnote which includes code. rs fn main() { println!(this is some Rust!); }1.11.0 输出代码块不再被错误缩进 md Some text.[^fn] [^fn]: Here is a footnote which includes code. rs fn main() { println!(this is some Rust!); }### 列表项缩进遵循 tabWidth 选项 CommonMark 对列表项内代码块的缩进要求与原版 Markdown 语法存在分歧导致某些缩进代码块被渲染成普通段落。1.11.0 使用 tabWidth 选项决定列表项的缩进量 md !-- before -- 1. Change to your home directory: cd !-- after -- 1. Change to your home directory: cd这一改动也改善了编辑器中列表的缩进表现此图为发布说明中的演示动画API 与插件支持插件自定义选项插件可定义自己的选项作为社区语言插件 beta API 的完善1.11.0 允许插件声明自己的选项。这些选项可以出现在--help输出中prettier --plugin prettier/plugin-python --help也可以作为命令行参数使用prettier --plugin my-cool-prettier-plugin --my-custom-option还可以写在.prettierrc配置文件中{ plugins: [foo-plugin], fooOption: bar }从仓库当前实现看每个语言插件都通过自己的 options.js 导出选项并由插件加载机制统一合并见 src/main/plugins 下各插件对options字段的组装这一机制正是 1.11.0 引入插件选项能力后的延续。配置文件不再允许 kebab-case 选项旧版会误允许.prettierrc中出现 kebab-case 变体{ print-width: 100 }这并非预期行为1.11.0 已回退——此类选项必须写成printWidthcamelCase。配置传入--config时仍尊重 EditorConfig这是.editorconfig处理行为的一个小改动此前一旦通过--config指定了配置文件.editorconfig就会被忽略1.11.0 起两者可以同时生效。相关逻辑位于 src/config/editorconfig如 editorconfig-to-prettier.js将 EditorConfig 规则转换为 Prettier 选项并与显式配置合并。总结与升级建议Prettier 1.11.0 的价值集中体现在三方面CSS 生态修复at-rule 格式化、!default、warn/error、插值、嵌套选择器、front-matter 等十余项修复让 SCSS/Less/CSS 的格式化结果不再破坏语义对使用 Jekyll、postcss-mixins、Vue SFC 的项目尤为重要TypeScript 新语法跟进!:、unique symbol、条件类型让 1.11.0 可以直接格式化 TS 2.7/2.8 时代的代码同时修复了非空断言语义被破坏的问题一致性与 APIskip与test对齐、AMDdefine贴紧、--insert-pragma去重修复、插件自定义选项、--config与 EditorConfig 共存等改进降低了引入 Prettier 时的迁移成本。升级后建议重点回归以下场景包含 SCSS 插值与注释的样式文件、使用非空断言的 TypeScript 链式调用、含代码块的 Markdown 脚注与列表以及启用了--use-tabs或--insert-pragma的项目。相关格式化测试可在仓库的 tests/format/css、tests/format/typescript、tests/format/js 与 tests/format/markdown 中查看作为验证格式化行为的参考样例。【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考