news 2026/10/19 21:20:44

markdownlint 链接与标题空格规则实战:基于 MD011–MD021 测试文件的逐条解析与自动修复

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
markdownlint 链接与标题空格规则实战:基于 MD011–MD021 测试文件的逐条解析与自动修复
  • 开发工具
  • 代码质量
  • Lint
  • 静态分析

【免费下载链接】markdownlint

A Node.js style checker and lint tool for Markdown/CommonMark files.

项目地址:https://gitcode.com/gh_mirrors/ma/markdownlint
点击查看免费下载

导读

本文以 markdownlint 仓库中的综合场景测试文件 test/MD011-MD021.md 为骨架,逐条剖析其中覆盖的 8 条核心规则——反向链接(MD011)、连续空行(MD012)、行长(MD013)、命令提示符(MD014)以及 ATX 标题空格系规则(MD018–MD021)。你将掌握每条规则的触发条件、参数配置、{MD###}行内标记的断言机制,以及 markdownlint 通过markdownlint-configure-file内联配置和applyFixes自动修复的完整工作流。

测试文件结构:用内联标记声明预期结果

test/MD011-MD021.md 是一个"自描述"的 Markdown 文件——它不仅是 lint 的输入样例,其内容本身也通过行内注释标明了每条规则期望报告的行号:

# Top level heading <!-- markdownlint-disable MD003 --> A (reversed)[link] example. {MD011} ... {MD012:7} {MD013:8} {MD013:10} <!-- markdownlint-configure-file { "line-length": { "heading_line_length": 40 } } -->

其中的关键机制来自测试驱动 test/markdownlint-test-scenarios.mjs:测试框架用正则/\{(MD\d+)(?::([-+]?)(\d+))?\}/g扫描文件内容,将{MD011}解析为"第 5 行应报告 MD011",将{MD013:8}解析为"第 8 行应报告 MD013",支持{MD013:+2}这类相对行号写法(+/-前缀表示偏移)。随后框架对 lint 返回的ruleNames[0]与实际报告行号做深度比对,任何"多报"或"漏报"都会导致测试失败,这就是test/markdownlint-test-scenarios.mjs.snapshot中快照的来源。

值得注意的是,该文件第 3 行用<!-- markdownlint-disable MD003 -->关闭了标题风格一致性检查,避免标题用例干扰本文关注的规则;文件末尾的markdownlint-configure-file注释则在整文件范围内把line-length规则的heading_line_length调整为 40(详见下文 MD013 小节)。

MD011:反向链接语法(no-reversed-links)

触发条件与修复

MD011 在文本疑似链接、但方括号与圆括号顺序颠倒时触发,例如(reversed)[link]。规则文档 doc/md011.md 给出的正确写法是reversed,并特别说明 Markdown Extra 风格的脚注For (example)[^1]不触发该规则。

测试文件中第 5 行A (reversed)[link] example. {MD011}与第 26 行*Another* (reversed)[link] example. {MD011}两处命中,快照显示二者均被报告为errorDetail: "(reversed)[link]"、errorRange: [3,16]与[11,16],并携带自动修复信息insertText: "reversed"。

源码级实现原理

在 lib/md011.mjs 中,核心正则:

const reversedLinkRe = /(^|[^\\])\(([^()]+)\)\[([^\]^][^\]]*)\](?!\()/g;

实现细节包括:

  • 通过filterByTypesCached(["codeFenced", "codeIndented", "mathFlow"])跳过围栏代码块、缩进代码块和数学公式块,避免误报代码内容;
  • 通过filterByTypesCached(["codeText", "mathText"])排除行内代码与行内数学,并用hasOverlap做区间重叠判断;
  • 检测linkText与linkDestination是否以反斜杠结尾,避免命中转义场景;
  • 命中后给出完整fixInfo(editColumn、deleteCount、insertText),因此 MD011 位于 lib/constants.mjs 的fixableRuleNames列表中,可被applyFixes自动重排为reversed。

MD012:连续空行(no-multiple-blanks)

触发条件与参数

MD012 在文档中出现连续多个空行时触发。测试文件第 7 行附近存在两处连续空行,快照报告errorDetail: "Expected: 1; Actual: 2",且fixInfo.deleteCount: -1(表示在上一行末尾删掉一个换行)。

规则文档 doc/md012.md 给出参数:

参数类型默认值含义
maximuminteger1允许的最大连续空行数

代码块内部的连续空行不触发本规则。

源码级实现原理

lib/md012.mjs 的实现非常精简:先用filterByTypesCached(["codeFenced", "codeIndented"])收集代码块行号集合,然后逐行扫描,count = (inCode || line.trim().length > 0) ? 0 : count + 1——非空行或代码块内重置计数,连续空行累计,一旦count > maximum即报错并附带删除一个换行的修复信息。maximum通过Number(params.config.maximum || 1)读取,可直接在配置中调整。

MD013:行长(line-length)

参数表与三种检查模式

MD013 是本文档涉及参数最多的规则,完整参数见 doc/md013.md:

参数类型默认值含义
line_lengthinteger80普通行最大字符数
heading_line_lengthinteger80标题行最大字符数
code_block_line_lengthinteger80代码块行最大字符数
headingsbooleantrue是否检查标题
code_blocksbooleantrue是否检查代码块
tablesbooleantrue是否检查表格
strictbooleanfalse严格模式:任何超长行都报错
sternbooleanfalse严格但允许整行无空格

默认行为存在"无空格豁免":只要超出限制的部分没有空格(如长 URL),就不报错。strict关闭该豁免;stern则只对"可换行但未换行"的行报错。此外,链接/图片的引用定义行、以及仅包含单个链接/图片(可带强调)的独立行,即使在strict模式下也永远豁免,因为这类行无法在不破坏 URL 的前提下拆分。

测试文件中的应用

测试文件第 8 行是 99 个字符的长行(Expected: 80; Actual: 99),第 10 行是超长的 H2 标题。文件末尾的内联配置把标题行上限收窄:

<!-- markdownlint-configure-file { "line-length": { "heading_line_length": 40 } } -->

因此快照中第 10 行报告Expected: 40; Actual: 62——同一规则对普通行与标题行使用了不同阈值,这正是heading_line_length独立生效的实证。

源码级实现原理

lib/md013.mjs 通过filterByTypesCached收集atxHeading/setextHeading、codeFenced/codeIndented、table、autolink/image/link/literalAutolink等 token 的行号区间,逐行按inCode → codeLineLength、isHeading → headingLineLength、否则lineLength选取阈值;并用line.replace(/\S*$/u, "#")实现"无空格豁免"(strict/stern模式下不替换)。definitionLineIndices(来自getReferenceLinkImageData())实现引用定义豁免,linkOnlyLineNumbers实现"纯链接独立行"豁免。MD013 本身不可自动修复(fixInfo: null),需要手动换行。

MD014:命令前的美元符号(commands-show-output)

触发条件

MD014 在代码块展示 shell 命令、且所有命令都以$开头、同时没有任何输出行时触发。规则文档 doc/md014.md 强调三个边界:

  • 若命令后附带了输出($ ls后跟foo bar),不触发;
  • 若只有部分命令有输出,不触发;
  • 只有"全部命令带$且全部无输出"才触发。

测试文件第 12 行缩进代码块$ command with no output {MD014}命中,快照给出errorRange: [5,2](即$两字符)与fixInfo: { editColumn: 5, deleteCount: 2 }——自动修复会直接删掉美元符号与空格。

源码级实现原理

lib/md014.mjs 遍历codeFenced/codeIndented代码块,取出codeFlowValue子节点,用/^(\s*)(\$\s+)/匹配行首$。只有dollarMatches.length === codeFlowValues.length(全部行带$)时才报告所有命中行。这意味着只要代码块内出现一行未以$开头的文本(典型如命令输出),规则即整体放行。

MD018–MD021:ATX 标题空格四规则

这四条规则共同管理 ATX 风格标题(#开头)中"井号与标题文本之间"的空格数量,按"开式(不闭合)"与"闭式(行尾也加井号)"分为四组:

规则别名检查对象判定
MD018no-missing-space-atx开式标题井号后缺少空格
MD019no-multiple-space-atx开式标题井号后多于一个空格
MD020no-missing-space-closed-atx闭式标题井号两侧缺少空格
MD021no-multiple-space-closed-atx闭式标题井号两侧多于一个空格

测试文件中的四组用例

测试文件 14–24 行密集放置了六条用例,恰好覆盖四种违规:

行号原文命中的规则快照报告
14##No space A {MD018}MD018errorRange: [1,3],修复为在列 3 插入空格
16## Multiple spaces B {MD019}MD019errorRange: [4,1],删除第 4 列一个空格
18##No space C {MD020} ##MD020左侧缺空格,整行重写为## No space C {MD020} ##
20## No space D {MD020}##MD020右侧缺空格,同样整行重写
22## Multiple spaces E {MD021} ##MD021左侧多空格,删除第 4 列一个空格
24## Multiple spaces F {MD021} ##MD021右侧多空格,删除第 30 列一个空格

从快照可见:MD018/019 的修复是"局部插入/删除",而 MD020 因可能同时涉及左右两侧,采用editColumn: 1, deleteCount: 行长度, insertText: 规范化整行的重写式修复——这也解释了 lib/md020.mjs 中同时判断leftSpaceLength与rightSpaceLength/rightEscape的原因。

源码级实现原理

MD018 在 lib/md018.mjs 中用正则/^#+[^# \t]/定位"井号后紧跟非井号非空格"的行,同时排除以#结尾的闭式标题、排除#️⃣emoji 序列,并跳过codeFenced/codeIndented/htmlFlow区间,修复方式是editColumn: hashCount + 1, insertText: " "。

MD019 与 MD021 共用同一辅助函数validateHeadingSpaces,实现在 lib/md019-md021.mjs:基于 micromark 解析出的atxHeadingtoken,通过getHeadingStyle(heading)区分"atx"与"atx_closed"两种风格;delta = 1正向扫描开式/闭式标题的起始空白,delta = -1仅对闭式标题扫描结尾空白。只有当whitespace.text.length > 1(多于一个空格)时才报告,修复为删除多余空格。这从实现层面印证:MD019 只检查开头,MD021 则开头、结尾都检查(闭式标题两侧皆可能违规)。

综合验证:快照与自动修复

test/markdownlint-test-scenarios.mjs.snapshot 中MD011-MD021.md条目完整记录了 13 个错误对象(2×MD011、1×MD012、2×MD013、1×MD014、1×MD018、1×MD019、2×MD020、2×MD021),并给出了applyFixes后的整文件内容:

# Top level heading <!-- markdownlint-disable MD003 --> A reversed example. {MD011} 123456789 ... ## 123456789 ... command with no output {MD014} ## No space A {MD018} ## Multiple spaces B {MD019} ## No space C {MD020} ## ## No space D {MD020} ## ## Multiple spaces E {MD021} ## ## Multiple spaces F {MD021} ##

修复后再次 lint,markdownlint-test-scenarios.mjs会断言"可修复的错误均已清零"(fixableRuleNames中列出的规则必须全部修复),完成"报错 → 修复 → 复核"的闭环。你可以用同样的方式在本地验证:运行node test/markdownlint-test-scenarios.mjs(或直接node test/harness.mjs test/MD011-MD021.md查看 token 结构与 lint 结果),即可复现本文所有结论。

实战小结

通过这一份测试文件,可以提炼出四条可直接迁移到日常 Markdown 写作的准则:

  1. 链接语法:始终使用文本,避免括号顺序颠倒(MD011),它可被自动修复;
  2. 空行与行长:段落间最多保留一个空行(MD012),普通行控制在 80 字符内、标题行可通过heading_line_length单独收紧(MD013),超长行需手动拆分;
  3. 代码块命令:要么省略$,要么展示命令输出,二者缺一不可(MD014),全无输出的$前缀命令会被自动删除提示符;
  4. 标题空格:ATX 标题井号后必须有且仅有一个空格;使用闭式标题时两侧同样要求恰好一个空格(MD018–MD021),除 MD020 采用整行重写修复外,其余均为局部空格增删。

若需按项目自定义这些阈值,除了常规的options.config配置外,还可以像本测试文件一样使用<!-- markdownlint-configure-file { ... } -->内联注释按文件覆盖配置(语法见 README.md),实现"单文件级"的精细化规则调优。

  • 开发工具
  • 代码质量
  • Lint
  • 静态分析

【免费下载链接】markdownlint

A Node.js style checker and lint tool for Markdown/CommonMark files.

项目地址:https://gitcode.com/gh_mirrors/ma/markdownlint
点击查看免费下载

相关推荐

上一篇:昇腾GE的CMake示例指南
下一篇:Newsyc:终极开源Hacker News iOS客户端完整指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/18 11:15:52

浙大开源nanoMuse轻量化视觉模型:本地部署与推理优化实战指南

1. 从标题拆解 nanoMuse 的核心定位1.1 这个项目到底在解决什么问题先把标题拆开看。“Muse”在近期的技术社区里被反复提及&#xff0c;通常指向一类具备较强生成与编辑能力的视觉内容模型&#xff0c;而“浙大开源版”和“nanoMuse”这两个词组合在一起&#xff0c;传递出的信…

作者头像 李华
网站建设 2026/10/19 5:29:42

设计院协同设计软件有哪些?信息中心主任的选型清单

导语&#xff1a;市面上自称『协同设计软件』的产品很多&#xff0c;但真正适合设计院的并不多。本文从勘察设计行业角度&#xff0c;帮你梳理设计院协同设计软件的主要类型&#xff0c;并给出信息中心主任可直接套用的选型清单。 一、先分清&#xff1a;协同设计软件大致分几类…

作者头像 李华
网站建设 2026/10/18 21:58:17

按摩浴缸服务商哪个靠谱?亲测复盘选型经验

去年帮一个精品民宿项目做卫浴采购&#xff0c;前后接触了六七家按摩浴缸服务商&#xff0c;踩过坑也攒了些经验。今天把选型过程复盘一下&#xff0c;重点聊聊怎么判断一家服务商是否靠谱。先说踩过的坑最开始图省事&#xff0c;找了一家号称"厂家直销"的贸易商。样…

作者头像 李华
网站建设 2026/10/18 14:29:55

AI音乐与虚拟艺人如何重塑2026音乐产业

1. 榜单现象解析&#xff1a;2026年3月11日Hot 100背后的音乐产业变革2026年3月11日这一期的Billboard Hot 100榜单呈现出几个显著特征&#xff1a;AI生成音乐首次占据榜单12%席位&#xff0c;虚拟艺人合作单曲包揽前三甲&#xff0c;而传统唱片公司发行的作品在Top 10中仅存3席…

作者头像 李华
网站建设 2026/10/18 12:29:12

物理AI的触觉短板:力反馈与触觉传感的工程实践

1. 从一次失败的抓取实验说起去年秋天&#xff0c;我在调试一套桌面级机械臂抓取系统时&#xff0c;遇到了一个让我重新审视整个技术路线的问题。机械臂的视觉模块能准确识别出桌面上那个纸杯的位置&#xff0c;路径规划也顺利地把夹爪送到了杯壁两侧&#xff0c;但就在闭合夹爪…

作者头像 李华