Prettier Markdown 格式化行为深度解析:以 kitchen-sink 测试用例 test-case.md 为样本
Prettier Markdown 格式化行为深度解析以 kitchen-sink 测试用例 test-case.md 为样本【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettierPrettier 作为有主见的代码格式化器其 Markdown 支持同样遵循解析 AST 后按规则重排的核心哲学。本文以 tests/format/markdown/markdown/test-case.md 这份覆盖 Markdown 几乎所有语法元素的测试样本为主线逐项拆解 Prettier 对段落、标题、列表、引用块、代码块、链接、强调等结构的实际格式化规则并结合快照输出与 src/language-markdown 下的打印源码验证其底层实现。读完本文你将能准确预判 Prettier 对任意 Markdown 片段的格式化结果并理解仓库内 Markdown 回归测试的运作方式。一、测试用例的定位一份 Markdown 语法大全样本test-case.md源自著名的 github-markdown-kitchen-sink 示例文档一份刻意收集了 Markdown 各种写法的语法大全被 Prettier 仓库收录为 Markdown 打印器的回归测试输入。文件首行保留了对原始文档的链接说明其后依次排布了如下语法要素普通段落与缩进段落4 空格缩进的代码块Setext 标题Header 1与 ATX 标题#######含带闭合井号的写法# Header 1 #引用块普通文本、嵌套标题与列表、含缩进代码块的引用无序列表-、、*三种标记与有序列表1.主题分隔线* * *、***、*****、- - -、长-线行内链接、带 title 的链接、快捷引用式链接与引用定义强调*single*、_single_、**double**、__double__行内代码、缩进代码块、语言标注为 markdown 的围栏代码块图片语法与 HTML 注释。它与同目录下的 real-world-case.md一份模拟真实项目 README 的长文档样本互为补充前者覆盖语法宽度后者覆盖真实文档深度共同构成 markdown 目录下的两条核心测试基线。二、测试如何运行runFormatTest 与两种选项组合驱动该用例的测试文件 format.test.js 内容极简只有两行runFormatTest调用runFormatTest(import.meta, [markdown], { proseWrap: always }); runFormatTest(import.meta, [markdown], { proseWrap: always, singleQuote: true, });它揭示了两个关键信息测试固定开启proseWrap: always。这是影响 Markdown 输出的最重要的选项它决定段落prose文本在超过printWidth默认 80时是否强制换行重排。Prettier 的proseWrap选项共有always、preserve、never三档仓库对该用例选择最激进的always从而可以观察到完整的重排行为。singleQuote在 Markdown 中并非直接作用于正文而是作用于链接/图片的 title 属性引号详见下文第六节测试专门跑了两遍以覆盖双引号与单引号两种偏好下的输出差异。runFormatTest是仓库统一的快照测试框架测试执行时先格式化输入文件再将输入与输出按固定格式写入snapshots/format.test.js.snap后续运行逐字比对任何打印行为的变动都会导致快照 diff 从而暴露回归。在仓库根目录执行yarn jest tests/format/markdown/markdown即可单独运行本组测试修改了 Markdown 打印逻辑后这正是验证行为是否意外改变的第一道防线。三、Markdown 打印器的选项面为何只有两个选项从源码看Markdown 语言模块刻意保持极简的选项面。 src/language-markdown/options.js 全文如下import commonOptions from ../common/common-options.evaluate.js; const options { proseWrap: commonOptions.proseWrap, singleQuote: commonOptions.singleQuote, }; export default options;也就是说Markdown 打印器只从公共选项中暴露proseWrap与singleQuote两项其余选项缩进宽度、分号等对 Markdown 均不适用——这解释了为什么同一份test-case.md在两种选项组合下输出差异仅限于链接 title 的引号风格而段落换行、列表标记等行为完全一致。四、标题ATX 与 Setext 两种风格的取舍test-case.md同时包含了 ATX 标题# Header 1与 Setext 标题Header 1 下划线快照输出揭示了 Prettier 对二者的差异化处理。ATX 标题#级别保留但发生两处规范化——其一# Header 1 #这类带闭合井号的写法闭合井号会被移除统一为无闭合标记的 ATX 风格其二相邻标题之间会自动补足一个空行快照中# Header 1与## Header 2之间由输入的无空行变为输出的一空行分隔。Setext 标题Header 1的下划线标注被原样保留不会被改写为#风格。其实现见 src/language-markdown/print/heading.jsprintSetextHeading会回溯原始文本截取标题行下方的/-下划线并原样打印而 ATX 分支则是#.repeat(path.node.depth) 后拼接子节点内容heading.js。一个值得注意的例外是所有缩进 4 空格即缩进代码块中的标题无论 ATX 还是 Setext、无论是否带闭合井号都被原样保留——因为缩进代码块在解析阶段就被视为代码不参与标题语义。五、段落、引用块与 proseWrap 重排proseWrap: always最直观的效果体现在长文本段落上。test-case.md中块引用内那句超长的 Lorem ipsum 文本输入为一行输出则被按 80 列拆成三行且每一行都保留前缀 Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.这正是prose 换行机制的体现。底层实现位于 src/language-markdown/print/paragraph.js段落打印先将子节点逐个打印再通过flattenFill把嵌套的fill文档展平后重新组装交给文档打印器的fill指令做能放得下就放、放不下就换行的度量式排版——这与代码打印器处理长函数调用链的思路同源都是 Wadler prettier 打印算法的典型应用。对比之下缩进 4 空格的代码块包括 Lorem ipsum...这类被整体缩进的内容不做任何重排长行原样保留。可见是否换行完全取决于内容在解析后属于 prose 还是 code。块引用还体现了另一个细节当块引用内同时包含标题与列表时 ## This is a header.之后会被补上一行独立的空行将标题与 1. This is the first list item.隔开保证块内结构清晰可读。六、列表无序标记交替、有序编号保留test-case.md连续给出了-、、*三种无序列表标记快照输出显示它们被统一为两种标记交替出现- Red - Green - Blue * Red * Green * Blue - Red - Green - Blue即第一组用-、第二组用*、第三组又用-。这一规律直接对应 src/language-markdown/print/list.js 中的前缀生成逻辑nthSiblingIndex % 2 0 ? - : * ——同级兄弟列表按索引奇偶交替使用-与*既保证了标记一致又在视觉上形成层次差异。有序列表则不同1. Buy flour and salt / 1. Mix together with water / 1. Bake三个条目在输入输出中均保持1.编号原样。这正是 Markdown 打印器git-diff-friendly的处理策略list.js 中的hasGitDiffFriendlyOrderedList逻辑不把1.展开为1. 2. 3.避免新增/删除条目时整段列表产生 diff 噪音若源码中的起始编号非 1则保留实际数值同样可见于 list.js 对node.start的处理并在超过 CommonMark 上限999_999_999时截断。七、主题分隔线五种写法统一为---用例给出了* * *、***、*****、- - -与一行超长的-共五种分隔线写法快照输出显示它们全部归一为---。这是 Markdown 打印器对 thematicBreak 节点的标准化处理语义等价的分隔线写法不保留原始花样统一输出最短的标准形式。同样缩进代码块内的分隔线写法* * *、***等因属于代码块而被原样保留。八、链接、引用定义与图片title 引号随 singleQuote 变化test-case.md覆盖了行内链接、带 title 链接、快捷引用式链接与引用定义四种形式。在proseWrap: alwayssingleQuote: true的组合下行内链接[an example](http://example.com Example)的 title 由双引号变为单引号Example引用定义[id]: http://example.com Optional Title同样变为Optional Title图片![Alt Text](http://placehold.it/200x50 Image Title)的 title 同步变为Image Title无 title 的链接[This link](http://example.com)不受影响。这解释了 format.test.js 为何要跑两组配置对比两组快照format.test.js.snap可见singleQuote只影响 title 引号这一处其余输出完全一致。同样地所有缩进代码块中的链接写法含双引号 title均原样保留。九、强调与行内代码*归一为___归一为**强调是 Markdown 中写法最自由的语法之一Prettier 在此同样执行语义等价则统一风格的策略输入输出*single asterisks*_single asterisks__single underscores__single underscores_**double asterisks****double asterisks**__double underscores__**double underscores**即单层强调统一使用_双层强调统一使用**__被改写为**。行内代码code则不做任何改动原样保留因为行内代码属于代码语义而非 prose。十、代码块缩进代码块保留、围栏代码块递归格式化test-case.md中代码块分为两类行为截然不同缩进代码块行首 4 空格整体原样保留。包括Paragraph:\n\n Code这种块内再缩进的嵌套结构以及缩进块内的标题、列表、链接、分隔线等一切内容都不被格式化。围栏代码块包裹这里是本用例最有意思的地方。快照显示语言标注为markdown的围栏块内部内容同样被格式化了——输入块内的 Red列表在输出中变为* Red* Red变为- Red与正文列表的交替规则完全一致。其原因是 src/language-markdown/embed.js 中的嵌入逻辑围栏代码块只要语言可被推断出对应 parsermarkdown恰好能被inferParser识别为 Markdown 自身就会通过textToDoc递归调用对应解析器重新格式化再以printCodeFences包回围栏外壳只有当语言缺失或无法推断如markdown之外的未知语言时才原样保留。而块内有序列表1. Buy flour and salt保持1.不变再次印证了有序编号的 git-diff-friendly 策略在递归格式化中同样生效。十一、快照回归测试的实践价值将 test-case.md 与snapshots/format.test.js.snap 中的输入、输出对照阅读可以在一个文件内完整推演出 Prettier 的 Markdown 格式化决策树内容属于 prose段落、块引用文本→ 按printWidth换行重排内容属于代码缩进块、行内代码、不可推断语言的围栏块→ 原样保留语法语义等价但写法多样分隔线、强调标记、列表标记、标题闭合井号、链接 title 引号→ 统一为规范形式有序列表编号 → 保持 diff 友好不重排编号。这套输入 快照的测试组织方式是 Prettier 全仓库通用的模式每个语法主题如 heading、list、link、blockquote、fenced-code-block 等目录都拥有独立的输入用例与对应快照任何打印规则的调整都必须同时通过全部用例。如果你正在调试某个 Markdown 边界行为最快的路径就是新增或修改一个tests/format/markdown/下的.md输入文件运行yarn jest观察快照 diff。十二、延伸阅读src/language-markdown/index.js 与 src/language-markdown/parsers.jsMarkdown 语言注册与解析器接入src/language-markdown/print/index.js各节点类型到打印函数的分发入口src/language-markdown/embed.js围栏代码块、MDX 导入/导出/JSX 的嵌入格式化src/language-markdown/print/code.js围栏代码块外壳的打印docs/options.mdproseWrap、singleQuote等选项的完整说明tests/format/markdown/markdown/real-world-case.md同一测试目录下的真实文档长样本可与test-case.md对照观察真实场景中的格式化效果。【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Windows 11“显示桌面”按钮消失?恢复方法与快捷键替代方案全攻略

Windows 11“显示桌面”按钮消失?恢复方法与快捷键替代方案全攻略

Windows 11 把很多老用户用惯的东西都改得“妈不认”,任务栏算是最典型的一个。以前在 Windows 10 里,屏幕右下角那根细细的“显示桌面”条,鼠标一甩过去就能让所有窗口瞬间让路;到了 Windows 11,它变成了一条只有在鼠…

2026/9/19 12:18:31 阅读更多 →
Qt 5.15.19与Qt for MCUs 2.11 LTS:嵌入式GUI稳定性范式升级

Qt 5.15.19与Qt for MCUs 2.11 LTS:嵌入式GUI稳定性范式升级

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 12:18:31 阅读更多 →
CI-03T空调语音控制实战:红外码库、电平匹配与声学结构

CI-03T空调语音控制实战:红外码库、电平匹配与声学结构

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/19 12:18:31 阅读更多 →

最新新闻

Plant 3D槽式三通参数化:Python驱动的工程级建模实践

Plant 3D槽式三通参数化:Python驱动的工程级建模实践

1. 这不是“画个图就完事”的活儿:Plant 3D里一个槽式三通背后的真实成本在化工、石化、电力这些流程工业的设计现场,AutoCAD Plant 3D绝不是CAD软件的简单升级版,它是一套带“物理属性”的数字孪生前置引擎。你拖进去一个三通,系…

2026/9/20 14:58:22 阅读更多 →
高中数学知识点全总结:三年考点、易错点与复习方法梳理

高中数学知识点全总结:三年考点、易错点与复习方法梳理

简介:这是一份面向高中学生与备考考生的数学知识点总结合集,按高考常考模块梳理了函数与导数、平面向量与三角函数、数列、空间向量与立体几何、概率统计、解析几何及参数方程等核心内容,并附有不等式求解、压轴题应对技巧和常见易错点分析&a…

2026/9/20 14:58:22 阅读更多 →
CANN Runtime trace日志机制全解析:落盘路径、事件类型与维测信息定位指南

CANN Runtime trace日志机制全解析:落盘路径、事件类型与维测信息定位指南

CANNAscend人工智能任务调度 【免费下载链接】runtime 本项目提供CANN运行时组件和维测功能组件。 项目地址: https://gitcode.com/cann/runtime 点击查看 免费下载 导读 trace机制是CANN Runtime提供的一套"内存记录、异常落盘"的维测信息采集方案&…

2026/9/20 14:58:22 阅读更多 →
Biome 的 useTopLevelHeading 规则:强制 Markdown 文档以一级标题开头(nursery 组)

Biome 的 useTopLevelHeading 规则:强制 Markdown 文档以一级标题开头(nursery 组)

Biome 的 useTopLevelHeading 规则:强制 Markdown 文档以一级标题开头(nursery 组) 【免费下载链接】biome A toolchain for web projects, aimed to provide functionalities to maintain them. Biome offers formatter and linter, usable …

2026/9/20 14:58:22 阅读更多 →
GB/T 27930-2024与ISO 15118-20兼容性测试系统设计实践

GB/T 27930-2024与ISO 15118-20兼容性测试系统设计实践

简介:面向新能源汽车充电系统研发、测试与标准合规工程师,这份PDF文档聚焦新国标GB/T 27930.2-2024(2015)与ISO 15118-20充电协议的兼容性测试方案,重点解决大功率及双向充电场景下的互操作性验证难题。资源仅含1个PDF…

2026/9/20 14:58:22 阅读更多 →
Apache SkyWalking OAP 新指标扩展实战:Source 与 Scope 的完整开发指南

Apache SkyWalking OAP 新指标扩展实战:Source 与 Scope 的完整开发指南

可观测性后端微服务云原生 【免费下载链接】skywalking APM, Application Performance Monitoring System 项目地址: https://gitcode.com/gh_mirrors/sky/skywalking 点击查看 免费下载 本文基于 Apache SkyWalking OAP(Observability Analysis Platfo…

2026/9/20 14:57:21 阅读更多 →

日新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/20 0:00:46 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/20 0:00:46 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/20 0:00:46 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/19 23:01:36 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/19 17:50:38 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/19 23:35:34 阅读更多 →