
Biome Markdown 格式化器如何保护围栏代码块中的 CSS 内容以 mdn-font-face-1 测试用例为线索的源码级解析【免费下载链接】biomeA toolchain for web projects, aimed to provide functionalities to maintain them. Biome offers formatter and linter, usable via CLI and LSP.项目地址: https://gitcode.com/gh_mirrors/bi/biome本篇文章以 Biome 仓库中的测试规格文件crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-font-face-1.md为切入点深入解析 Biome Markdown 格式化器formatter对围栏代码块fenced code block内部代码内容的处理策略包括围栏长度归一化、代码块缩进保护、CommonMark 兼容规则以及对应的测试基础设施与配置方法。读完本文你将理解为什么格式混乱的 CSS 代码块在 Markdown 中会被原样保留并掌握如何运行、验证与配置 Biome 的 Markdown 格式化能力。一、这个测试用例在测什么一段“故意凌乱”的 CSS 代码块在 Biome 仓库中crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-font-face-1.md是一个从 Prettier 官方测试套件中迁移过来的 Markdown 规格测试输入文件其完整内容如下css font-face { font-family: HeydingsControlsRegular; src: url(fonts/heydings_controls-webfont.eot); src: url(fonts/heydings_controls-webfont.eot?#iefix) format(embedded-opentype), url(fonts/heydings_controls-webfont.woff) format(woff), url(fonts/heydings_controls-webfont.ttf) format(truetype); font-weight: normal; font-style: normal; }这段输入刻意模拟了开发者从 MDNMozilla Developer Network教程中复制过来的真实代码CSS 规则内部缩进极其混乱——src 属性前有 8 个空格、第二条 url(...) 完全没有缩进、font-weight 与 font-style 的缩进也不一致。与之配套的期望输出文件 mdn-font-face-1.md.prettier-snap 表明**Prettier 与 Biome 对该文件格式化后的结果与输入完全一致代码块内部的 CSS 内容被逐字保留**。 这正是该测试用例要验证的核心行为**Markdown 格式化器不应重排围栏代码块内部的代码**。无论内部 CSS 缩进多么混乱只要它位于反引号围栏内就属于“字面量内容”verbatim content格式化器只负责处理 Markdown 语法层面的结构围栏本身、外层缩进、空行而把内部代码原样交给渲染器。 同目录下还有姊妹用例 mdn-font-face-2.md其中包含了 tech(color-COLVr1) 这类现代 CSS 字体技术语法同样验证了含 tech()/format() 参数的复杂 font-face 声明在围栏内不会被重排。 ## 二、围栏代码块格式化核心FormatMdFencedCodeBlock 的源码实现 围栏代码块的格式化逻辑位于 [fenced_code_block.rs](https://link.gitcode.com/i/9b3bcfc6417f8b5cd092e422ff98eec1)核心类型是 FormatMdFencedCodeBlock它实现了 FormatNodeRuleMdFencedCodeBlock。在 fmt_fields 方法中代码块被拆解为 l_fence开围栏、r_fence闭围栏、r_fence_indent闭围栏缩进、content内容、code_list语言标识、indent开围栏缩进等字段分别处理。 ### 2.1 围栏长度归一化CommonMark §4.5 规则 fenced_code_block.rs 中最关键的一段逻辑是围栏长度的计算第 26–33 行 rust // Compute the minimum fence length needed (CommonMark §4.5). // The fence must be strictly longer than any same-character sequence // in the content, otherwise the inner sequence would be parsed as a // closing fence. E.g. if the content contains (3 backticks), // the outer fence needs at least 4. let max_inner longest_fence_char_sequence(node, ); let fence_len (max_inner 1).max(3); let normalized_fence: String std::iter::repeat_n(, fence_len).collect();其原理遵循 CommonMark 规范 §4.5闭围栏是一行至少与开围栏等长或更长的连续反引号序列。如果代码内容中恰好存在与围栏等长的连续反引号解析器会把内容中的那一段误判为闭围栏。因此 Biome 会先通过辅助函数longest_fence_char_sequence扫描内容中最长的连续反引号序列max_inner再将围栏长度设为max_inner 1且至少 3 个反引号从而保证围栏永远“严格长于”内容中的任何反引号串。随后开围栏l_fence与闭围栏r_fence都会被替换为统一计算出的normalized_fence。也就是说即使源码中使用了 4 个、5 个甚至更多反引号只要内容中不需要更长的围栏输出都会被归一化为恰好够用的长度。这正是“Markdown 语法层格式化”与“代码内容不动”之间边界的一个典型体现。2.2 围栏缩进的移除与标准化除了围栏长度代码块还涉及缩进处理开围栏前的缩进indent字段用于将代码块嵌入列表等嵌套结构Biome 会遍历这些缩进 token通过format_removed将其从输出中移除再依赖外层如列表项的格式化器统一重新生成缩进第 44–50 行。闭围栏前的缩进r_fence_indent字段同样被format_removed移除并重新标准化第 96–102 行随后写出归一化后的闭围栏第 104–117 行。此外代码还统计了开围栏的缩进宽度opening_fence_indent第 39–42 行供后续内容处理时计算“应被剥离的公共缩进”使用。2.3 语言标识与内容的分流处理code_list如 css 中的css与围栏一并输出。而代码块内容content的处理分为两种情况若内容中不存在MdCodeContent即文档级代码块通常以单个字面量节点存储则走普通内容格式化分支若存在MdCodeContent节点则对每个代码内容节点调用FormatMdCodeContentOptions并传入opening_fence_indent见 fenced_code_block.rs。三、代码内容保护FormatMdCodeContent 如何逐行剥离缩进代码块内部的逐行处理由 code_content.rs 中的FormatMdCodeContent完成其行为在源码注释中有明确说明“Trivia is excluded on both sides”两侧排除 trivia即开围栏信息字符串后的空白不会作为多余内容行输出。关键算法在fmt_fields中第 36–82 行跳过行首换行符value_token以开围栏行末的换行符开头\r\n或\n先从line_start中跳过避免产生空内容行。剥离公共缩进对每一行从行首开始在不超过opening_fence_indent开围栏缩进宽度的范围内剥离连续空格第 43–50 行。这意味着如果代码块位于列表项中开围栏有 2 空格缩进内容行的前 2 个空格会被视为 Markdown 语法缩进而去除剩余部分才是真正的代码。逐行按原样输出剥离缩进后的每一行代码通过syntax_token_cow_slice(...).with_literal_line_breaks()以字面换行的方式写出第 27–34 行。with_literal_line_breaks保证换行符被忠实保留不会参与自动换行line wrapping或重排。对于 CRLF\r\n行尾代码还做了细致的兼容处理遇到\r时若后面紧跟\n则一起作为行尾输出否则单独生成一个不依赖父级的字面换行第 62–74 行。这就是mdn-font-face-1.md中那些“缩进错乱”的 CSS 行得以原样保留的根本原因格式化器只剥离与围栏对齐的 Markdown 语法缩进而 CSS 内部每个属性、每条url(...)之前的空格都是代码内容的组成部分会被逐字输出。整段font-face对格式化器而言是“不透明”的文本从而保证了 MDN 示例这种真实代码在文档中不会被破坏。四、测试基础设施该用例如何被自动执行与验证4.1 从 Prettier 测试套件迁移这些mdn-*.md文件来自 prepare_tests.js该脚本以 Prettier 仓库的tests/format目录为输入遍历其中所有测试文件将输入文件复制到 Biome 的tests/specs/prettier/对应目录从 Prettier 的快照中提取“输出”部分并用 Prettier 自身重新格式化后写入.prettier-snap文件第 122–124 行若 Prettier 重格式化前后不一致还会额外生成.prettier-snap-original文件用于比对。也就是说.md是输入、.prettier-snap是期望输出二者成对出现构成一份可对照的格式化测试规格。4.2 Prettier 兼容性快照测试prettier_tests.rs 通过宏批量生成测试tests_macros::gen_tests! {tests/specs/prettier/markdown/**/*.{md}, crate::test_snapshot, }每个.md输入都会触发test_snapshot它使用PrettierTestFile读取测试文件以MdFormatOptions::default()IndentStyle::Space、缩进宽度 2和 GFMGitHub Flavored Markdown方言构造MarkdownTestFormatLanguage最终交给PrettierSnapshot::new(...)执行格式化并与.prettier-snap期望输出对比。这保证了 Biome 的 Markdown 输出与 Prettier 保持兼容——这正是mdn-font-face-1.md这类用例存在的意义任何破坏代码块内容的行为都会导致快照不一致从而让测试失败。4.3 通用规格测试与运行方式除了 Prettier 兼容性测试Biome 还有一套面向自身语法的规格测试 spec_tests.rs它扫描tests/specs/markdown/**/*.md下的所有用例并以启用 Markdown formatter 的配置执行SpecSnapshot测试。两套测试体系共同守护 Markdown 格式化行为。在仓库根目录下运行以下命令即可执行 Markdown 格式化器的全部测试cargo test -p biome_markdown_formatter若要只跑某个特定用例例如本主题的mdn-font-face-1可用cargo test -p biome_markdown_formatter -- mdn_font_face五、如何在真实项目中启用与配置 Markdown 格式化需要特别说明的是在 Biome 当前配置源码 markdown.rs 中Markdown 格式化器默认处于禁用状态pub type MarkdownFormatterEnabled Boolfalse;注释明确指出“Keep it disabled by default while experimental”即实验功能默认关闭。因此若要在项目中使用需在biome.json中显式开启{ formatter: { indentStyle: space, indentWidth: 2, lineWidth: 80 }, markdown: { formatter: { enabled: true, indentStyle: space, indentWidth: 2, lineWidth: 80, proseWrap: preserve, lineEnding: lf, trailingNewline: true }, parser: { frontmatter: false, gfm: true } } }各配置项的含义与默认值来自 markdown.rs 的结构体定义配置项默认值作用markdown.formatter.enabledfalse实验性默认关闭是否启用 Markdown 格式化indentStyle跟随全局测试默认spaceMarkdown 文件的缩进风格indentWidth2缩进宽度lineWidth80单行最大宽度proseWrappreserve段落换行策略preserve保持原样、always按行宽重排、never合并为单行手动换行行尾两个空格或反斜杠始终保留见 context.rs 中ProseWrap枚举定义lineEndinglf行尾风格auto在 Windows 用 CRLF、其他平台用 LFtrailingNewlinetrue文件末尾是否保留换行符parser.frontmatterfalse是否解析文件开头的 frontmatterparser.gfmtrue是否启用 GitHub Flavored Markdown 扩展启用后可对单个文件执行格式化验证本文描述的行为biome format crates/biome_markdown_formatter/tests/specs/prettier/markdown/code/mdn-font-face-1.md也可以搭配--write参数直接写入格式化结果或使用biome check做整体检查。六、关键要点总结代码块是字面量无论代码块内部 CSS 的缩进多么混乱Biome 的 Markdown 格式化器都会将其逐字保留——这正是 code_content.rs 中with_literal_line_breaks逐行原样输出的结果。格式化边界清晰格式化器只处理 Markdown 语法层围栏长度归一化、围栏前后缩进标准化、空行并遵循 CommonMark §4.5 规则保证围栏严格长于内容中的反引号序列见 fenced_code_block.rs。兼容性有测试兜底mdn-font-face-1.md及其.prettier-snap期望输出由 prepare_tests.js 从 Prettier 套件迁移而来经 prettier_tests.rs 的快照机制持续验证。默认关闭、需显式开启Markdown 格式化器在 markdown.rs 中默认禁用属于实验性功能需在biome.json中设置markdown.formatter.enabled: true后才会生效。对于希望在文档中嵌入 CSS、JavaScript 等代码示例的开发者而言理解“代码块内容受保护、Markdown 结构被规范化”这一设计能够帮助你放心地把凌乱的示例代码放进 Markdown——Biome 会替你把围栏整理干净同时绝不擅自动你的代码。【免费下载链接】biomeA toolchain for web projects, aimed to provide functionalities to maintain them. Biome offers formatter and linter, usable via CLI and LSP.项目地址: https://gitcode.com/gh_mirrors/bi/biome创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考