资讯详情

资讯详情

markdownlint MD023 规则实战:从源码与测试用例解析“标题必须顶格书写”

开发工具代码质量Lint静态分析【免费下载链接】markdownlintA Node.js style checker and lint tool for Markdown/CommonMark files.项目地址https://gitcode.com/gh_mirrors/ma/markdownlint点击查看免费下载导读MD023别名heading-start-left是 markdownlint 中用于强制ATX/Setext 标题必须从行首开始无前导空格缩进的规则属于headings、spaces标签组。本文以仓库测试文件 test/headings_with_spaces_at_the_beginning.md 为切入点逐行拆解该规则命中与豁免的边界场景普通段落、代码块注释、列表内标题、行内配置指令并结合规则源码 lib/md023.mjs 与相邻规则 lib/md022.mjs 的底层实现、测试快照 test/markdownlint-test-scenarios.mjs.snapshot 中的输出与自动修复结果讲清规则触发条件、修复原理及与 MD022 的协作关系。读完本文你将能准确预判“带缩进的标题”何时会被 MD023 拦截、如何利用fixInfo自动修复以及在列表等嵌套结构中如何规避误报。一、规则背景MD023 检查什么依据官方规则文档 doc/md023.md同内容收录于 doc/Rules.mdHeadings must start at the beginning of the line标题必须从行首开始。该规则在以下场景触发Some text # Indented heading修正方式是将标题顶格书写Some text # Heading例外场景块引用block quote会使整行看起来“有缩进”但这是合法的 # Heading in Block Quote规则信息一览项目值规则名MD023别名heading-start-left标签headings、spaces是否可自动修复是部分违规可由工具修复修复类型删除标题前的缩进字符editColumndeleteCount设计动机标题如果带有前导空格缩进Markdown 解析器不会把它当作标题而会渲染为普通文本Rationale 原文即指出此点这会造成文档结构与视觉预期的偏差。二、从规则源码看检测原理规则实现在 lib/md023.mjs其核心逻辑基于 micromark 解析器产生的 token 流const headings filterByTypesCached([ atxHeading, linePrefix, setextHeading ]);检测条件由三部分组合而成lib/md023.mjs#L14-L34前一个 token 是linePrefix行前缀即行首的空格/缩进后一个 token 不是linePrefix说明该行确实以标题开始前后两个 token 位于同一行startLine相同。命中后调用addErrorContext记录违规并携带editColumn起始列与deleteCount删除字符数作为fixInfo供自动修复使用。linePrefixtoken 的endColumn - startColumn即为缩进长度。关于 token 过滤与缓存filterByTypesCached定义于 lib/cache.mjs底层委托给 helpers/micromark-helpers.cjs 的filterByTypes并在规则执行期间按参数做缓存避免多条规则重复遍历 token 流。三、测试文件逐行解读哪些缩进会被判违规测试文件 test/headings_with_spaces_at_the_beginning.md 全文件仅 25 行却覆盖了 MD023 的 4 个命中点与 2 类豁免场景。3.1 ATX 标题带 1 个空格第 3 行# Heading 1 {MD023}第 1 列是空格linePrefix长度为 1随后同一行出现atxHeading→ 触发 MD023。快照中对应错误test/markdownlint-test-scenarios.mjs.snapshot{ lineNumber: 3, ruleNames: [ MD023, heading-start-left ], errorContext: # Heading 1 {MD023}, errorRange: [ 1, 1 ], fixInfo: { editColumn: 1, deleteCount: 1 } }3.2 Setext 标题两条下划线一行缩进一行不缩进第 5–6、8–9 行Setext 标题由“文本行 下划线行”构成MD023 对下划线行的缩进同样敏感Setext style fully indented {MD023} 文本行与下划线行都带 1 个空格缩进 → 触发。快照中第 5 行错误test/markdownlint-test-scenarios.mjs.snapshot#L14842-L14861的fixInfo为{ editColumn: 1, deleteCount: 1 }。对比第 8–9 行Setext style title only indented {MD023} 文本行缩进、下划线行顶格。快照显示第 8 行仍被判定违规test/markdownlint-test-scenarios.mjs.snapshot#L14862-L14881因为setextHeading的起始位置取决于文本行是否带前导缩进而下划线行顶格并不会“抵消”违规。反过来说如果文本行顶格而下划线行缩进同样会触发——两条线必须都顶格才算合规。3.3 列表项内嵌套标题第 19–20 行与 MD022 的联动* This is another case where MD023 shouldnt be triggered # Test {MD022} Valid heading for CommonMark (see section 5.2) # Test {MD022} {MD023} Also valid heading for CommonMark这是本测试文件中信息量最大的部分需要结合 CommonMark 规范 5.2 节来理解第 19 行列表项内缩进 2 个空格后的# Test ...。按 CommonMark标题位于列表项内容内部是合法嵌套因此MD023 不触发但因为其上方/下方缺少空行触发MD022标题周围应有空行。快照显示 MD022 给出两条错误Above/Belowtest/markdownlint-test-scenarios.mjs.snapshot#L14772-L14805fixInfo均为insertText: \n。第 20 行缩进 4 个空格# Test ...。此时标题不仅缺少空行MD022而且缩进已超过列表内容所需的缩进级别linePrefix与atxHeading出现在同一行且无其他 token 隔断因此MD023 同时触发。快照中第 20 行的 MD023 错误为test/markdownlint-test-scenarios.mjs.snapshot#L14882-L14901{ lineNumber: 20, errorContext: # Test {MD022} {MD023} Als..., errorRange: [ 3, 2 ], fixInfo: { editColumn: 3, deleteCount: 2 } }注意errorRange从第 3 列开始、长度为 2——修复只删除多出的 2 个空格保留列表项内容缩进所需的 2 个空格避免破坏列表嵌套结构。这正是 markdownlint 自动修复“最小干预”设计的一个体现。3.4 豁免场景代码块中的“井号”注释第 11–16 行* Test situations in which MD023 shouldnt be triggered. rb # This shouldnt trigger MD023 as it is a code comment. foo And here is some code代码栅栏内的 # ... 是代码注释不是 Markdown 标题。micromark 会将其解析为 codeFenced 内容而非 atxHeading因此 MD023 的 token 过滤天然将其排除——快照中该区域没有任何错误。这一场景说明**MD023 的判定完全依赖解析结果token 类型而不是简单的正则匹配**这也是本项目采用 micromark 解析器parser: micromark见 [lib/md023.mjs#L11](https://link.gitcode.com/i/8b8fd0e261275647a187c029d8a20313#L11)的根本原因。 ### 3.5 行内配置指令关闭相邻规则的干扰第 22–25 行 测试文件末尾通过 HTML 注释形式声明了行内配置 markdown !-- markdownlint-configure-file { heading-style: false, first-line-heading: false } --该指令关闭了heading-styleMD003ATX 与 Setext 标题风格一致性与first-line-headingMD041首行应为一级标题使得测试可以专注验证 MD023/MD022而不会被文件首行Some text触发 MD041、也不会因混合 ATX/Setext 风格触发 MD003。这种markdownlint-configure-file行内配置是仓库测试用例的常用手法也适用于实际项目按文件粒度定向开关规则。四、自动修复applyFixes 与 fixInfo 的完整链路修复能力由fixInfo提供最终通过applyFixes落地。测试框架 test/markdownlint-test-scenarios.mjs 展示了完整验证链路用lint()检测得到errors其中包含fixInfo调用applyFixes(content, errors)生成修复后文本对修复后文本再次lint()断言不再残留任何带fixInfo的错误同时校验凡出现fixInfo的规则名必须登记在 lib/constants.mjs 的fixableRuleNames中MD022、MD023 均在列。快照中fixed字段展示了本测试文件的修复结果test/markdownlint-test-scenarios.mjs.snapshot#L14903第 3、5、8 行的前导空格被删除deleteCount: 1第 20 行删除第 3 列起的 2 个多余空格editColumn: 3, deleteCount: 2MD022 在第 19、20 行标题上方插入空行insertText: \n。注意第 5 行的 Setext 下划线行修复后下划线仍保留 1 个前导空格因为该违规标记在第 5 行文本行上fixInfo只针对文本行下划线行并不单独计错——这也解释了快照中第 6 行没有单独错误条目的原因。修复后标题变为普通文本行MD023 对后续下划线行的缩进不再追究这是规则“基于 token 同源判定”的又一佐证。五、与相邻规则的边界划分MD023 常与以下规则在同一行同时出现实际使用中需要区分职责规则检查内容本测试文件中的体现MD022blanks-around-headings标题上/下方是否缺少空行第 19、20 行每个标题报 Above/Below 两条MD023heading-start-left标题是否带前导空格缩进第 3、5、8、20 行MD003heading-styleATX 与 Setext 风格是否混用被行内配置关闭以隔离干扰MD041first-line-heading文档首行是否为一/二级标题被行内配置关闭文件首行为Some textMD022 的实现位于 lib/md022.mjs其lines_above/lines_below参数支持按标题级别1–6分别配置空行数量整数或整数数组-1表示任意数量并通过getBlockQuotePrefixTexthelpers/micromark-helpers.cjs在插入空行时自动带上块引用前缀保证修复不破坏块引用结构。这与 MD023 的deleteCount修复思路同源修复必须尊重文档原有的结构上下文。六、实战建议与验证方式顶格是硬性要求无论 ATX#还是 Setext/---标题起始行与下划线行都应从第 1 列开始除非位于块引用内。列表内标题先补空行再谈缩进列表项内的标题常见问题其实是 MD022缺空行而非 MD023只有当缩进超过列表内容所需缩进量时MD023 才会介入此时自动修复只会删除多余的缩进。代码块注释不会误报因为 MD023 基于 micromark 的 token 类型判定栅栏代码、行内代码中的#内容天然豁免。本地复现可直接运行node test/harness.mjs test/headings_with_spaces_at_the_beginning.md查看lint的完整结果对象test/harness.mjs或用--profile观察 1000 次 lint 的耗时运行node --test test/markdownlint-test-scenarios.mjs可执行全部场景测试并校验快照与自动修复闭环。结语MD023 表面上是“去掉标题前的空格”这么简单但通过 test/headings_with_spaces_at_the_beginning.md 这个 25 行的小样本可以看到真正的工程复杂度在于区分“结构缩进”列表、块引用与“非法缩进”、在自动修复时只删除多余字符以及与 MD022、MD003、MD041 等规则的职责协同。理解 token 级别的判定方式你就能在任何 Markdown 文档中准确预测并快速修复标题缩进问题。赞分享开发工具代码质量Lint静态分析【免费下载链接】markdownlintA Node.js style checker and lint tool for Markdown/CommonMark files.项目地址https://gitcode.com/gh_mirrors/ma/markdownlint点击查看免费下载相关推荐markdownlint MD023 规则详解标题必须从行首开始heading-start-leftmarkdownlint MD023 规则详解标题必须从行首开始heading start left MD023 是 markdownlint 规则集中与开发工具代码质量Lint静态分析markdownlint MD025「单标题」规则深度解析从测试用例到源码实现markdownlint MD025「单标题」规则深度解析从测试用例到源码实现 本文以 markdownlint 仓库版本 0.41.0中的测试夹具 te开发工具代码质量Lint静态分析markdownlint 空标题无内容标题与 MD019 规则从测试用例到源码实现markdownlint 空标题无内容标题与 MD019 规则从测试用例到源码实现 导读 本文以 markdownlint 仓库中的场景测试文件 test开发工具代码质量Lint静态分析上一篇XGP-save-extractor 使用指南3 分钟提取 Xbox Game Pass 存档下一篇抖音无水印批量下载怎么做新手 5 分钟上手的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →