- 开发工具
- 代码质量
- Lint
- 静态分析
【免费下载链接】markdownlint
A Node.js style checker and lint tool for Markdown/CommonMark files.
markdownlint 是一款面向 Markdown/CommonMark 文件的 Node.js 风格检查与 lint 工具,其内置的MD035/hr-style(Horizontal rule style)规则专门负责校验文档中水平分隔线(thematic break,又称 HR)的书写风格是否一致。本文以测试用例 test/hr-in-blockquote-star.md 为核心骨架,结合规则源码 lib/md035.mjs、官方规则文档 doc/md035.md 与 JSON Schema 配置声明,完整讲解 MD035 的检测原理、consistent与显式style两种工作模式,以及水平分隔线出现在引用块(blockquote)和嵌套引用块中时的校验行为。读完本文,你将能够准确预判 MD035 在各类嵌套场景下的报错行号,并掌握通过配置文件与行内注释配置hr-style的完整实战方法。
一、MD035 规则速览:检测什么、何时触发
MD035 的规则名与别名定义在 lib/md035.mjs 中:
export default { "names": [ "MD035", "hr-style" ], "description": "Horizontal rule style", "tags": [ "hr" ], "parser": "micromark", "function": function MD035(params, onError) { /* ... */ } };- Tags:
hr - Aliases:
hr-style(在配置文件中推荐使用别名,如"hr-style": { "style": "***" }) - Parser:
micromark,即规则基于 micromark 解析器产出的 token 流工作,而不是基于 markdown-it
该规则在文档中使用了不一致风格的水平分隔线时触发。官方文档 doc/md035.md 给出了触发示例:
--- - - - *** * * * ****五种写法风格各不相同,默认配置下除第一行外全部报错。修复方式则是全文统一为同一种写法:
--- --- ---二、识别规则:什么样的行会被当作水平分隔线
MD035 只对真正被解析器识别为 thematic break 的行生效。根据 doc/md035.md 的说明:
一条行必须包含3 个或以上相同的
-、_或*字符(字符之间允许可选空格),才能被识别为水平分隔线。
也就是说:
---、- - -、-----属于破折号(dash)风格;___、_ _ _、_____属于下划线(underscore)风格;***、* * *、*****属于星号(star)风格。
字符数不足 3 个(如--)不会产生 thematicBreak token,MD035 自然也不会报错。同时要注意水平分隔线的常见约束:它通常必须独占一行(两侧允许空白),并且不能紧跟段落文本——这与 Markdown 的分隔线解析规则一致。
三、核心测试用例精读:星号风格与三层嵌套引用块
本文的关联文档 test/hr-in-blockquote-star.md 是 markdownlint 测试套件中专门验证「星号风格」水平分隔线在普通文档、单层引用块、双层嵌套引用块三种场景下 MD035 行为的用例,文件内的{MD035:行号}注释是测试驱动(harness)预期的报错位置:
# HR in Blockquote, Star *** ← 第 3 行:首个分隔线,确立 consistent 基准风格 ___ ← 第 5 行:{MD035:5} --- ← 第 7 行:{MD035:7} > Text > > --- ← 第 13 行:{MD035:13} > > *** > > ___ ← 第 17 行:{MD035:17} > > Text * * * ← 第 23 行:{MD035:23} > Text > > > Text > > > > --- ← 第 29 行:{MD035:29} > > > > *** > > > > ___ ← 第 33 行:{MD035:33} > > > > Text > > Text3.1 第一段:正文区的星号基准
文件开头的***(第 3 行)是全文档第一个被解析出的 thematicBreak。在默认的consistent模式下,MD035 会以它为基准风格***,因此后续正文中的___(第 5 行)与---(第 7 行)与基准不符,分别产生{MD035:5}与{MD035:7}两处报错。这与 lib/md035.mjs 的实现完全一致——风格一旦确定便不再改变:
let style = String(params.config.style || "consistent").trim(); const thematicBreaks = filterByTypesCached([ "thematicBreak" ]); for (const token of thematicBreaks) { const { startLine, text } = token; if (style === "consistent") { style = text; // 以第一个分隔线的 text 作为基准 } addErrorDetailIf(onError, startLine, style, text); }这里token.text是分隔线所在行的原始文本(如***、* * *),addErrorDetailIf在text !== style时报告MD035,并给出「期望风格 → 实际风格」的细节信息。
3.2 第二段:单层引用块内的分隔线
> Text > > --- > > *** > > ___ > > Text引用块内的分隔线同样会生成 thematicBreak token,且 MD035 会把它们与正文区的分隔线放在同一套一致性规则下比较:
- 第 13 行
> ---与基准***不符 →{MD035:13}; - 第 15 行
> ***与基准一致 → 不报错; - 第 17 行
> ___与基准不符 →{MD035:17}。
注意报错行号是源码中的物理行号(包含>前缀在内的整行),这也解释了为什么引用块中的分隔线行号比正文区大得多。
3.3 第三段:双层嵌套引用块与* * *
* * * ← 第 23 行 > Text > > > Text > > > > --- ← 第 29 行 > > > > *** > > > > ___ ← 第 33 行 > > > > Text > > Text这一段有两个关键信息点:
- 第 23 行的
* * *也是星号风格的变体,但它的token.text是* * *(带空格),与基准***文本不完全相同,因此在文本级比较下被判为不一致,产生{MD035:23}。这体现了 MD035 的严格性:风格按原始文本逐字符比较,***与* * *即使视觉上同属星号体系,也会被当作不同风格。 - 双层嵌套引用块(
> > ---、> > ***、> > ___)内的分隔线同样参与全局一致性比较:第 29 行> > ---与第 33 行> > ___报错,第 31 行> > ***与基准一致不报错。
最终整个文件的预期结果为{MD035:5} {MD035:7} {MD035:13} {MD035:17} {MD035:23} {MD035:29} {MD035:33},即除基准行(第 3 行)与两处***(第 15、31 行)外全部报错。
四、机制解读:引用块为何不影响 MD035 判断
从上面的测试用例可以提炼出 MD035 的两个重要行为特征:
特征一:解析与比较分离。规则通过 lib/cache.mjs 中的filterByTypesCached([ "thematicBreak" ])一次性取出 micromark token 流中所有类型为thematicBreak的 token。该辅助函数调用 helpers/micromark-helpers.cjs 的filterByTypes遍历扁平 token 列表,按类型过滤并缓存结果,同一规则在一次 lint 中不会重复计算。引用块前缀>是 blockQuote 容器语法,与分隔线本身无关——分隔线的核心 token 文本依然是---、***或___,因此 MD035 并不感知「这个分隔线在不在引用块里」。
特征二:全局单基准。consistent模式下,基准风格取整个文档(包括所有嵌套层级的引用块)中第一个thematicBreak 的文本,之后所有层级的分隔线都与该基准比较。这解释了测试用例中引用块内外共享同一套判断标准的原因。从源码结构还可以推断:MD035 的遍历顺序就是 micromark token 流中 thematicBreak 出现的先后顺序,与文档行序一致。
五、参数详解:consistent 与显式 style
MD035 只有一个参数style,官方文档 doc/md035.md 声明其类型为string、默认值为consistent:
| 取值 | 行为 |
|---|---|
consistent(默认) | 全文所有水平分隔线必须与第一个水平分隔线完全一致 |
任意字符串(如"***"、"- - -"、"_____") | 强制所有水平分隔线与该字符串逐字符一致 |
JSON Schema 中hr-style节点的声明还补充了规则级别的通用配置面:除了style外,还允许enabled(布尔,默认true)与severity(error/warning,默认error),并且整个规则节点本身支持布尔值快捷开关(false关闭、true启用)。
六、实战配置示例
6.1 配置文件方式(.markdownlint.json / .markdownlint.jsonc)
{ "MD035": { "style": "***" }, "hr-style": { "style": "* * *" } }MD035与别名hr-style均可作为配置键。注意:上例中MD035与hr-style同时出现时,markdownlint 对同名规则的配置合并/覆盖语义以实际解析结果为准,实战建议只使用其中一个键,避免歧义。若要全文统一使用破折号风格,可配置{ "hr-style": { "style": "---" } }。
6.2 行内配置注释方式
markdownlint 支持在 Markdown 文件内部通过 HTML 注释动态配置规则。测试用例 test/hr_style_stars.md 展示了在文件末尾用markdownlint-configure-file指令把风格固定为***:
<!-- markdownlint-configure-file { "hr-style": { "style": "***" } } -->该文件前 21 行的预期结果为{MD035:5} {MD035:7} {MD035:9} ... {MD035:19}——除第 3 行***外,* * *、*****、---、- - -、-----、___、_ _ _、_____全部报错,验证了显式style: "***"的强制语义。
再看一个自定义间隔风格的例子 test/hr-style-custom.md:
Text --- ← {MD035:5} Text - - - ← {MD035:10} Text - - - Text *** ← {MD035:19} <!-- markdownlint-configure-file { "hr-style": { "style": "- - -" } } -->将style显式设置为"- - -"后,只有第 15 行那个逐字符完全相等的- - -通过校验,---、- - -、***全部被判定为违规。这一用例再次印证:MD035 的风格比较是逐字符的字符串精确比较,空格数量差异(- - -与- - -)也会导致不匹配。
相关测试族还包括 test/hr_style_dashes.md(style: "---")、test/hr_style_long.md(style: "_____")、test/hr_style_inconsistent.md(默认 consistent 全量报错)以及 test/hr_style_stars.md,它们共同覆盖了三种字符、带空格变体与长分隔线等全部风格形态。
七、扩展场景:引用块与列表中的其他变体用例
除星号用例外,测试套件还有两个孪生用例:
- test/hr-in-blockquote-dash.md:以正文
---(第 3 行)为基准,单层引用块(第 13、17 行)与嵌套引用块(第 29、31、33 行)内的***、___报错; - test/hr-in-blockquote-under.md:以正文
___(第 3 行)为基准,正文第 5、7 行与引用块第 13、15、23、29、31 行报错。
两者与星号用例结构完全对称,预期行号虽不同,但判断逻辑一致,可用于交叉验证 MD035 的跨场景一致性。
当水平分隔线出现在列表项中时,行为同样基于 token 文本比较。参考 test/hr-in-list-star.md(列表项内的- ---、- ***、- ___依次产生{MD035:14} {MD035:18} {MD035:24} {MD035:26} {MD035:28})与 test/hr-in-list-dash.md 可知:无论分隔线位于引用块还是列表项内,MD035 的判定标准始终是「该行分隔线文本与全局基准文本是否逐字符相等」,容器语法本身不参与比较。这也是整个 MD035 规则最具实用价值的行为模型——写 Markdown 时只要记住「全文(含所有块级容器内)第一个分隔线的写法就是唯一合法写法」即可。
八、验证方法:如何在自己的环境中复现
在仓库根目录下,可以通过测试驱动脚本 test/harness.mjs 对任意 Markdown 文件执行 lint 并查看结构化结果:
node test/harness.mjs test/hr-in-blockquote-star.md该脚本会调用markdownlint/promise的lint({ "files": [ file ] }),并输出包含每条MD035报错行号与规则细节的完整结果对象,可用于自行核对本文给出的所有预期行号。注意运行前需要先按 README.md 安装依赖(npm install),因为解析链路依赖 micromark 及其扩展包。
另外,测试套件 test/markdownlint-test.mjs 会遍历所有test/*.md测试文件,校验其中{MDxxx:行号}注释与实际 lint 结果完全一致;同时通过validateJsonUsingConfigSchemaStrict测试(test/markdownlint-test.mjs)校验各测试文件中markdownlint-configure-file行内配置符合 schema/markdownlint-config-schema-strict.json 的严格模式。这意味着像{MD035:29}这样的注释本身就是可运行的回归断言。
九、小结:MD035 的判定模型
将本文内容收敛为一个可迁移的判定模型:
- 先找基准:默认
consistent模式下,基准 = 文档中第一个被 micromark 解析为thematicBreak的行的原始文本;引用块嵌套不影响「第一个」的语义。 - 逐字符比较:后续所有 thematicBreak(无论正文、引用块、嵌套引用块还是列表项)的原始文本都与基准逐字符比较,相等才通过。
- 显式覆盖:配置
style后,基准被固定为配置字符串,规则从「一致性」退化为「强制指定风格」,***与* * *、- - -与- - -这类空格差异都会被区分。 - 行号即物理行号:报错行号始终是包含
>前缀在内的源码物理行号,测试注释{MD035:n}是精确的回归断言。
掌握了这套模型,你不仅能读懂 test/hr-in-blockquote-star.md 这样结构化的测试用例,还能在真实项目中快速定位水平分隔线风格不一致问题,并通过配置文件或行内注释实现全仓库统一的 HR 书写规范。
- 开发工具
- 代码质量
- Lint
- 静态分析
【免费下载链接】markdownlint
A Node.js style checker and lint tool for Markdown/CommonMark files.
相关推荐
Go 模板处理全解析:从 text/template 语法到 Web 视图层实战(build-web-application-with-golang 7.4)
Go 模板处理全解析:从 text/template 语法到 Web 视图层实战(build web application with golang 7.4)
开发工具代码质量Lint静态分析智能窗口管理革命:FancyZones如何重塑Windows多任务生产力范式
智能窗口管理革命:FancyZones如何重塑Windows多任务生产力范式 在数字工作时代,窗口管理已成为现代生产力软件的核心痛点。传统Windows操作系统
开发工具代码质量Lint静态分析markdownlint 规则 MD035(hr-style):水平分割线样式一致性检查实战指南
markdownlint 规则 MD035(hr style):水平分割线样式一致性检查实战指南 本篇指南聚焦 markdownlint 项目中的 MD035
开发工具代码质量Lint静态分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考