Prettier 格式化 Markdown 多行 HTML 属性:原样保留策略与解析、打印实现解析

发布时间:2026/9/21 20:09:26

Prettier 格式化 Markdown 多行 HTML 属性:原样保留策略与解析、打印实现解析 Prettier 格式化 Markdown 多行 HTML 属性原样保留策略与解析、打印实现解析【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier本文以 Prettier 仓库中的 Markdown 格式化测试夹具 multiline-attribute.md 为核心剖析 Prettier 对 Markdown 中跨行 HTML 标签属性multiline HTML attribute的处理策略。读者读完将理解为什么 Prettier 对这类内容「不格式化」而是逐字保留、块级与行内上下文分别遵循怎样的判定规则以及该行为在解析层micromark 扩展与打印层literalline各自的实现依据。一、测试夹具multiline-attribute.md 在测什么在 Prettier 仓库中tests/format/markdown/html/目录集中存放 Markdown 内嵌 HTML 的格式化回归测试。其中 multiline-attribute.md 专门构造了「属性值跨行」的 HTML 标签覆盖四种典型上下文unknown titleline 1 line 2 /unknown div titleline 1 line 2 /div - In list - In list unknown titleline 1 line 2 /unknown div titleline 1 line 2 /div in blockquote unknown titleline 1 line 2 /unknown div titleline 1 line 2 /div text unknown titleline 1 line 2 text /unknown text该夹具刻意混用了两种标签unknownPrettier 未知的标签名与divHTML 已知块级标签名并让它们出现在四个位置文档根级独占段落紧贴行首书写嵌套列表- In list下两层缩进6 个空格的列表项内部块引用blockquote每行以前缀标记且属性内第二行带 2 个空格缩进行内上下文夹在普通段落文本text ... text之间。这种设计意在验证无论 HTML 处于何种 Markdown 容器中跨行属性内容都能被完整解析并且最终输出与输入完全一致。二、三种 proseWrap 下的快照输入恒等于输出与仓库其他格式测试一样该夹具由 format.test.js 驱动runFormatTest(import.meta, [markdown], { proseWrap: always }); runFormatTest(import.meta, [markdown], { proseWrap: never }); runFormatTest(import.meta, [markdown], { proseWrap: preserve });即同一份输入会分别在proseWrap: always / never / preserve三种取值下运行。查看快照文件 format.test.js.snap 中对应multiline-attribute.md的三段记录可以发现一个关键事实三种模式下输出都与输入逐字符一致。例如proseWrap: preserve的输出unknown titleline 1 line 2 /unknown div titleline 1 line 2 /div - In list - In list unknown titleline 1 line 2 /unknown div titleline 1 line 2 /div in blockquote unknown titleline 1 line 2 /unknown div titleline 1 line 2 /div text unknown titleline 1 line 2 text /unknown textproseWrap本用于控制 Markdown 段落的折行行为always强制折行、never不折行、preserve保留原文换行但在这里它对多行 HTML 属性毫无影响——原因正是下文将展开的HTML 节点在 Prettier 的 Markdown 打印器中属于「literal字面量」内容其内部的换行与缩进被整体保留不参与任何折行计算。细看输出还能发现几个被刻意保留的「不整齐」细节块引用场景中unknown的结束标签前是 /unknown即前缀后紧跟空格再加引号与div的写法一致但保留原样嵌套列表中外层列表项缩进为 4 空格、内层为 6 空格属性第二行缩进 6 空格、结束标签行缩进 4 空格——这些参差缩进全部原样输出行内场景中text与text /unknown text的空格也保持不变。这正是该测试要锁定的回归行为不要试图「修正」用户在多行 HTML 属性里书写的空白与换行。三、逐场景行为拆解3.1 根级块级 HTML作为独立段落处理unknown .../unknown与div .../div独占一行、两侧有空行会被 micromark 解析为块级html节点。Prettier 输出时在节点前后维持空行分隔见快照输出第一段节点本身的内容原样保留。3.2 嵌套列表缩进原样不参与列表对齐列表内的 HTML 标签保持其相对列表项的缩进层级4 空格列表项缩进 2 空格标签缩进标签内部第二行缩进 6 空格、结束标签行缩进 4 空格全部不做归一化。对比同一目录下的 multiline.md有序列表内嵌table与 multiline-with-trailing-space.md末尾带尾随空格的多行表格可见这是一致的策略多行 HTML 的缩进层级由用户书写决定Prettier 不参与排版。3.3 块引用前缀语义完整保留块引用中每行物理行以开头而属性值内的第二行line 2前有两个空格最终输出为 line 2。Prettier 需要保证两点属性内部的换行不被当作 Markdown 段落的软换行去折叠同时前缀与内部缩进逐字保留。3.4 行内上下文内部换行不被折叠text unknown titleline 1\n line 2\ntext /unknown text中HTML 处于段落内部但标签属性里的两行换行依然完整输出没有被折成一行。这说明「行内 HTML 节点」与「段落文本折行」是两条互不干扰的打印路径。四、原理一解析层如何保留属性内空白Markdown 的 HTML 解析通常把标签视为整块文本。若解析器在遇到属性值内的换行时直接截断或规整空白后续打印层就无从保留。Prettier 的解决方案在 micromark-extension-html-text.js 中// copied from https://github.com/micromark/micromark/blob/... // and modified to preserve whitespace inside quoted attribute values该文件头部注释明确指出该扩展复制自 micromark 官方的html-text实现并做了修改以保留引号包裹的属性值内部的空白。文件内部的状态机完整实现了标签解析的各个阶段——tagOpenAttributeName属性名、tagOpenAttributeValueBefore属性值前的空白、tagOpenAttributeValueUnquoted无引号属性值、以及双引号/单引号属性值状态——正是这些状态机分支负责在引号内持续消费包括换行在内的字符从而让unknown titleline 1\n line 2\n这样的内容被完整解析进 AST属性值内的换行与缩进不会丢失。从源码结构看这一步是整个「多行属性原样保留」能力的前置条件解析器先保证不丢字符打印器才能保证不改字符。五、原理二打印层用 literalline 锁死换行解析完成后HTML 节点进入打印阶段。在 Markdown 打印器的节点分发处 mdast.jscase html: { const { parent, isLast } path; const value parent.type root isLast ? node.value.trimEnd() : node.value; const isHtmlComment /^!--.*--$/s.test(value); return replaceEndOfLine( value, isHtmlComment ? hardline : markAsRoot(literalline), ); }这段代码揭示了保留策略的机制literalline字面换行对非注释 HTML节点内每个换行都替换为literalline文档节点。literalline与hardline一样强制换行但区别在于换行后不做缩进规范化——这正是输出中嵌套列表那组参差缩进4/6/4 空格能原样保住的原因。同时markAsRoot(...)确保该字面量行不会被外层上下文如列表、块引用的align注入额外缩进。注释特例HTML 注释!-- ... --使用hardline因为注释参与正常的缩进对齐语义。根级尾随修剪仅当节点是文档根root的最后一个子节点时才会trimEnd()去掉结尾空白避免文件末尾残留多余换行其余位置一律不动。空白处理还调用了replaceEndOfLine将 CRLF 等行尾统一为当前换行符配置但换行位置本身保持原样。这条路径在 replaceEndOfLine 所在模块与文档构建器的配合下最终形成「逐字节级」的保真输出。六、原理三行内 HTML 的判定规则为什么第 3.4 节的行内场景不会被proseWrap折叠关键在于行内 HTML 节点的判定。在 children.jsfunction shouldPrePrintHardline({ node, parent }) { const isInlineNode INLINE_NODE_TYPES.has(node.type) !(node.type liquidNode !INLINE_NODE_WRAPPER_TYPES.has(parent.type)); const isInlineHTML node.type html INLINE_NODE_WRAPPER_TYPES.has(parent.type); return !isInlineNode !isInlineHTML; }当html节点的父节点属于 INLINE_NODE_WRAPPER_TYPES包含paragraph、heading、tableCell等行内容器时它被认定为行内 HTML前后不插入硬换行与其他行内节点文本、强调、行内代码等一样参与段落文档构建但节点内部的多行属性内容依旧通过第五节的literalline路径保留换行。于是出现了看似矛盾、实则自洽的结果HTML 节点与周围文本的关系按行内处理而 HTML 自身的内部结构按字面量处理。块引用与列表场景中 HTML 的父节点不是行内容器因此shouldPrePrintHardline返回 true节点前后会插入硬换行/空行保证块级语义正确。七、运行验证与同目录配套用例该行为可随时在仓库内复现验证该测试由 Jest 执行入口为 format.test.js其中runFormatTest是 tests/config/format-test 提供的统一测试框架自动读取同目录.md夹具、分别断言输入与快照输出更新断言则快照写入 format.test.js.snap约 1063 行涵盖该目录全部 10 个夹具在三种proseWrap下的结果若需调整实现改动点锁定在 mdast.js 的case html分支与 micromark-extension-html-text.js 的属性值状态机。同目录的配套夹具从不同侧面夯实同类行为值得对照阅读夹具验证点multiline.md有序列表内嵌多行table表格整体原样multiline-with-trailing-space.md多行表格带尾随空格时的保留inline-html.md行内 HTML 与文本的间距、div块级断行inline-vs-block.mddiv块与span行内与后续文本的间距差异beginning-tag-after-a-list-item.md列表项之后紧跟 HTML 标签的空行插入八、对使用者的实用结论多行 HTML 属性不会被「修复」如果你手写的div titleline 1\n line 2\n缩进不齐Prettier 会原样保留这既是优点对含换行的属性值如aria-label、SVG 描述、模板字符串安全也是约束想让它整齐需自己排版。proseWrap对 HTML 节点无效无论配置always、never还是preserveHTML 块内部的换行都不参与折行算法不受该选项影响。换行符仍会统一节点内的换行虽然位置不变但会经replaceEndOfLine统一为配置的行尾风格因此跨平台提交时行尾依然稳定。调试建议当发现 Markdown 中 HTML 被意外改动时优先检查标签是否被识别为行内 HTML父容器类型以及是否命中根级末尾节点的trimEnd()特殊分支——这两处是仅有的例外路径。总而言之Prettier 对 Markdown 多行 HTML 属性的处理可以概括为一句原则解析阶段不丢字符保留引号属性内的空白打印阶段用literalline不改换行从而对多行 HTML 实现输入即输出的字面量保真。该原则由 multiline-attribute.md 这一测试夹具在根级、列表、块引用、行内四种上下文中锁定是理解 Prettier Markdown 内嵌 HTML 行为的最佳入口。【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/21 20:04:26

欲练此功必先自宫:后端开发最佳实践与面试避坑指南

欲练此功必先自宫:后端开发最佳实践与面试避坑指南 面试被问原理答不上来,是不是觉得脑子里一片浆糊?别慌,这不是你笨,而是你一直只记结论,没摸透底层逻辑。很多新人学编程,就像练绝世武功,光背招式口诀,连内力运行路线都没搞清,遇到变招直接卡壳。…

2026/9/21 20:04:26

新手避坑:Python爬虫被拒的5个致命原因与修复方案

新手避坑:Python爬虫被拒的5个致命原因与修复方案 面试被问到爬虫原理,你只记得用 requests 库发请求,却被反问“为什么对方服务器直接返回 403 禁止访问?”瞬间大脑空白。这种窘境不是个例,很多初学者把爬虫当成简单的…

2026/9/21 20:59:29

第三方ROM刷不进去?多半是底包版本没配对

刷过安卓机的人大概率都经历过这样一种场面:TWRP里滑动确认刷入,进度条刚走两秒,红色报错直接拍在脸上——“Cant install this package on top of incompatible data”又或者是“Error 7”,再看下面小字,写着“This p…

2026/9/21 20:59:28

3步搞定电脑怎么换输入法,附保姆级教程与性能优化实录

3步搞定电脑怎么换输入法,附保姆级教程与性能优化实录 配置环境就卡半天,改个输入法设置能折腾两小时?别急,这篇保姆级教程不玩虚的,直接上硬货。很多开发者和工程从业者都遇到过:新装系统后,输入法切换延迟高、资源占用飙升,甚至导致 IDE…

2026/9/21 20:59:28

宅男宅女电视剧开发避坑速查手册:3步搞定环境配置

宅男宅女电视剧开发避坑速查手册:3步搞定环境配置 配置环境就卡半天,这是无数后端开发者入门时的噩梦。你明明照着文档一步步来,结果终端报错信息像天书一样,重启电脑、重装依赖、换版本,折腾一下午还是没跑通。别急,这份 速查手册…

2026/9/21 20:54:28

审计署是干什么的:3年老兵拆解高频面试题

审计署是干什么的:3年老兵拆解高频面试题 版本升级后 API 全变了,这种痛感在技术圈太常见,但在考公或国企面试中,面对“审计署是干什么的”这类高频面试题,很多考生却像面对一个未更新文档的旧接口,脑子一片空白。…

2026/9/21 3:28:31

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/21 3:33:19

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/21 0:02:23

OpenResearch:构建可复现的开放式研究工作流

第一次看到“OpenResearch”这个名字,我脑子里冒出的不是某个具体软件,而更像一种研究方式的宣言:开放、可复现、可验证。这三件事放在一起,其实比大多数人想象中难得多。过去几年我一直在折腾自己的研究工作流,从纯纸…

2026/9/20 4:54:47

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/21 18:32:12

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/21 10:29:02

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码