- 开发工具
- 代码质量
- Lint
- 静态分析
【免费下载链接】markdownlint
A Node.js style checker and lint tool for Markdown/CommonMark files.
MD018(no-missing-space-atx)是 markdownlint 的 headings / atx / spaces 类别规则之一,用于检测 ATX 风格标题中#井号与标题文本之间缺少空格的问题。这类写法虽然在部分渲染器中可能被解析,但不符合 CommonMark 规范约定,容易导致标题渲染异常。阅读本文后,你将掌握 MD018 的触发条件、与 MD019/MD020/MD021 三个相邻规则的边界划分、源码级检测逻辑,以及如何在项目中使用 markdownlint 定位并自动修复此类问题。
规则总览
规则MD018的核心信息如下:
| 属性 | 值 |
|---|---|
| 规则名 | MD018 |
| 别名(Aliases) | no-missing-space-atx |
| 标签(Tags) | atx、headings、spaces |
| 可自动修复 | 部分违规可通过工具修复(Fixable: Some violations can be fixed by tooling) |
该信息直接来自仓库中的规则文档 doc/md018.md 第 1~7 行,并与其源码实现 lib/md018.mjs 中的规则元数据一一对应:
// lib/md018.mjs export default { "names": [ "MD018", "no-missing-space-atx" ], "description": "No space after hash on atx style heading", "tags": [ "headings", "atx", "spaces" ], "parser": "micromark", "function": function MD018(params, onError) { // ... } };所有规则通过 lib/rules.mjs 统一注册,其中第 16 行导入md018,并在第 73 行加入规则列表。规则名的names数组同时支持数字编号MD018与语义别名no-missing-space-atx,二者可在配置中任选其一引用(详见 doc/CustomRules.md 第 89~95 行关于规则结构字段的约定)。
触发条件:井号后缺少空格
当 ATX 风格标题的#井号与标题文本之间没有空格时,规则 MD018 就会被触发。文档 doc/md018.md 给出的违规示例为:
#Heading 1 ##Heading 2对应的正确写法是用单个空格将标题文本与井号分隔:
# Heading 1 ## Heading 2注意这里的判断对象是「井号与标题文本之间的空格」,与其余三个 ATX 空格规则的边界划分如下:
| 规则 | 名称 | 检查内容 | 位置 |
|---|---|---|---|
| MD018 | no-missing-space-atx | 缺少空格 | 开放式 ATX 标题的左侧(井号后) |
| MD019 | no-multiple-space-atx | 多个空格 | 开放式 ATX 标题的左侧 |
| MD020 | no-missing-space-closed-atx | 缺少空格 | 闭合式 ATX 标题的两侧 |
| MD021 | no-multiple-space-closed-atx | 多个空格 | 闭合式 ATX 标题的两侧 |
- 开放式 ATX 标题:如
# Heading 1,只有左侧井号序列,无结尾井号; - 闭合式 ATX 标题:如
# Heading 1 #,两侧都有井号序列,此时 MD020 / MD021 分别接管「缺空格」与「多空格」的检查。
相邻规则(MD019、MD020、MD021)的完整定义可参见 doc/md019.md、doc/md020.md、doc/md021.md。
同时触发多条规则的情形
一个不规范的标题可能同时命中多条规则,例如闭合式标题左侧缺空格、右侧多空格时,MD020 与 MD021 会分别报告。测试用例 test/atx-heading-spacing-trailing-spaces.md 与 test/MD011-MD021.md(第 14~24 行)集中展示了这种组合场景:
##Heading 1 {MD018} ## Heading 2 {MD019} ##Heading 3 {MD020} ## ## Heading 5 {MD021} ##测试文件中的{MD018}、{MD019}等花括号标注是 markdownlint 测试体系中的预期结果标记,表示该行应当报告对应编号的规则违规。例如 test/atx_heading_spacing.md 第 1 行的#Heading 1 {MD018}即期望在第 1 行报告一条 MD018。同一文件第 3、5 行的## Heading 2 {MD019}则期望报告 MD019(多个空格),可见 MD018 与 MD019 对「零空格」与「多空格」的判定互斥且互补。
源码级检测逻辑
逐行正则匹配
MD018 的实现位于 lib/md018.mjs,其核心检测是一个逐行循环 + 正则匹配,关键判断条件为:
/^#+[^# \t]/.test(line) && !/#\s*$/.test(line) && !line.startsWith("#️⃣")三段条件合在一起的含义是:
/^#+[^# \t]/:行首出现一个或多个#,且紧跟其后的字符不是#、空格或制表符——即井号后直接跟了标题文本,判定为缺空格;!/#\s*$/.test(line):行尾不是以#结尾,排除闭合式 ATX 标题(闭合式标题交给 MD020 处理);!line.startsWith("#️⃣"):排除以「井号键帽」emoji(#️⃣)开头的标题——这是 MD018 对 emoji 场景的特殊处理。
第 2、3 个条件结合测试用例可以看出:##️️ Keycap Number Sign {MD018}(见 test/emoji-headings.md 第 11 行)这类「#+ 变体选择符 + 井号键帽 emoji」的写法仍会被判定为 MD018,而规范的空格分隔写法则不会。
跳过代码块与 HTML 块
与很多行级规则一样,MD018 不会对代码块内部的内容做检查。源码通过filterByTypesCached获取codeFenced(围栏代码块)、codeIndented(缩进代码块)、htmlFlow(HTML 块)三类 token 的行号区间,并把这些行号收集进ignoreBlockLineNumbers集合(lib/md018.mjs 第 16~18 行):
for (const ignoreBlock of filterByTypesCached([ "codeFenced", "codeIndented", "htmlFlow" ])) { addRangeToSet(ignoreBlockLineNumbers, ignoreBlock.startLine, ignoreBlock.endLine); }因此下面这段代码块中的#Heading不会被误报:
```text #Heading inside code fence ```这一「跳过块级内容」的机制基于 Micromark 解析产生的 token,filterByTypesCached在 lib/cache.mjs 第 60~67 行实现,内部通过缓存避免对同一组 token 反复过滤,提升规则执行效率。
错误上报与可修复信息
判定违规后,MD018 通过addErrorContext上报错误,并附带fixInfo修复信息(lib/md018.mjs 第 28~39 行):
addErrorContext( onError, lineIndex + 1, line.trim(), undefined, undefined, [ 1, hashCount + 1 ], { "editColumn": hashCount + 1, "insertText": " " } );其中:
- 第 4 个参数是行号(1 起始);
[ 1, hashCount + 1 ]是错误范围:从第 1 列(第一个井号)到hashCount + 1列,覆盖整个井号序列,方便编辑器高亮定位;fixInfo指示修复方式为:在editColumn = hashCount + 1(井号序列之后)位置插入一个空格(insertText: " ")。
addErrorContext本身是 markdownlint 提供的通用错误上报辅助函数,定义于 helpers/helpers.cjs 第 300~317 行:它会对上下文做换行符归一化与省略号截断处理,再调用addError完成上报。正因为修复信息只是「插入一个空格」,所以文档中标注的「部分违规可被工具修复」是准确的——MD018 的所有违规都可以通过一次性插入空格完成修复,但自动修复需要由支持该能力的工具(如 markdownlint CLI 的--fix或集成了 fix 能力的编辑器插件)来执行。
实测与边界场景
仓库测试集中提供了多组针对 MD018 的验证用例,可用于理解规则的精确行为:
- 开放式标题缺空格:
#Heading 1 {MD018}(test/atx_heading_spacing.md 第 1 行); - 短标题缺空格:
#A报告{MD018:5}(test/short-headings.md 第 5~7 行),说明单字符标题同样适用; - 闭合式标题的区分:
#D#报告的是{MD020:15}而非 MD018(test/short-headings.md 第 15~17 行),印证了「行尾以#结尾的闭合式标题交给 MD020」的分工; - emoji 边界:
##️⃣ Keycap Number Sign {MD018}(test/emoji-headings.md 第 11 行)验证了 emoji 场景下的判定; - 非断行空格:
## Non-breaking space {MD018}(test/headings-with-invalid-spaces.md 第 7 行)说明 NBSP(U+00A0)不会被正则中的[ \t]视为空格,因此#后紧跟 NBSP 时依然触发 MD018。
测试文件中{MD018:行号}的标注格式表示「该行报告的违规编号与行号」,这些预期结果会与 markdownlint 实际输出进行比对(参见测试入口 test/markdownlint-test.mjs),从而保证规则行为长期稳定。
在项目中使用 markdownlint 检查与修复
基本用法
markdownlint 是面向 Node.js 的 Markdown/CommonMark 风格检查工具,可在仓库 package.json 中查看其依赖与脚本配置。安装并运行后,规则默认全部启用,因此 MD018 会在没有额外配置的情况下直接生效。
# 安装 markdownlint(CLI 与库二选一或同时安装) npm install markdownlint markdownlint-cli2 # 检查单个文件 npx markdownlint-cli2 "docs/**/*.md" # 自动修复 npx markdownlint-cli2 --fix "docs/**/*.md"通过配置禁用或启用 MD018
若某份文档(或整个项目)有意使用无空格的标题写法,可显式关闭 MD018。markdownlint 支持以 JSON 配置指定规则编号或别名:
{ "MD018": false }等效写法:
{ "no-missing-space-atx": false }行内注释控制
markdownlint 支持在 Markdown 文件内通过 HTML 注释临时启用/禁用规则。例如在文件头部禁用 MD018:
<!-- markdownlint-disable MD018 --> #Heading 1 #Heading 2 <!-- markdownlint-enable MD018 -->仓库测试文件大量使用了这一机制,例如 test/atx_heading_spacing.md 第 7~9 行用<!-- markdownlint-configure-file { ... } -->内联配置关闭first-line-heading,以便聚焦验证 MD018/MD019;test/headings-with-invalid-spaces.md 第 39~41 行则通过内联配置关闭heading-style与no-hard-tabs,从而隔离出空格相关规则的行为。这说明在实际项目中,合理使用行内配置可以在不影响其他规则的前提下单独调试某条空格规则。
与其他 ATX 空格规则协同排查
当一处标题同时出现「缺空格」与「多空格」问题时,建议按以下顺序排查:
- 先看标题是否是闭合式(行尾有无
#)——闭合式由 MD020/MD021 检查,开放式由 MD018/MD019 检查; - 再确认井号与文本之间是零个空格(MD018/MD020)还是多个空格(MD019/MD021);
- 若标题以
#️⃣emoji 开头,注意 MD018 对合法写法有豁免,但变体选择符场景仍需检查。
所有四条规则共享headings、spaces标签,可以从 tag 维度在配置中统一管理,例如:
{ "MD018": true, "MD019": true, "MD020": true, "MD021": true }总结
MD018(no-missing-space-atx)通过行级正则与 Micromark token 过滤机制,精确检测 ATX 标题井号后缺失空格的写法,并携带「插入一个空格」的修复信息支持自动化修复。其检测逻辑刻意排除了代码块、HTML 块、行尾闭合井号以及合法的#️⃣emoji 前缀,与 MD019/MD020/MD021 共同构成了完整的 ATX 标题空格检查体系。对追求渲染一致性与文档规范性的团队而言,启用 MD018 并以--fix自动修复,是最低成本的标题规范化手段。
- 开发工具
- 代码质量
- Lint
- 静态分析
【免费下载链接】markdownlint
A Node.js style checker and lint tool for Markdown/CommonMark files.
相关推荐
pnpm audit 漏洞摘要“净额”统计:被 auditConfig 忽略的漏洞不再计入结果
pnpm audit 漏洞摘要“净额”统计:被 auditConfig 忽略的漏洞不再计入结果 导读 pnpm audit 的摘要输出此前会把被 auditCo
开发工具代码质量Lint静态分析markdownlint MD019(no-multiple-space-atx)规则详解:ATX 标题井号后多余空格检测与修复
markdownlint MD019(no multiple space atx)规则详解:ATX 标题井号后多余空格检测与修复 MD019 是 markdow
开发工具代码质量Lint静态分析markdownlint 规则 MD021(no-multiple-space-closed-atx):闭合 ATX 标题井号内多余空格的检测与自动修复
markdownlint 规则 MD021(no multiple space closed atx):闭合 ATX 标题井号内多余空格的检测与自动修复 本篇文
开发工具代码质量Lint静态分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考