- 开发工具
- 代码质量
- Lint
- 静态分析
【免费下载链接】markdownlint
A Node.js style checker and lint tool for Markdown/CommonMark files.
markdownlint 的
MD009(no-trailing-spaces)规则负责检查行尾多余的空白字符。本文以仓库测试用例 test/trailing-spaces-in-lists-default.md 为骨架,深入拆解该规则在有序/无序列表场景下的默认判定逻辑,并结合规则文档 doc/md009.md、规则实现 lib/md009.mjs 与快照测试 test/markdownlint-test-scenarios.mjs.snapshot,讲清默认行为、全部四个配置参数以及如何按需放行列表项内空白行。
测试用例概览:为什么专门有一份"列表尾随空格"用例
test/trailing-spaces-in-lists-default.md是 markdownlint 场景测试(由 test/markdownlint-test-scenarios.mjs 驱动)中的一份标准 Markdown 样例。它的特殊之处在于:内容全部由列表(有序列表1.与无序列表-)构成,用于专门验证 MD009 规则在列表结构下的默认判定结果。
该文件使用项目约定俗成的断言格式:形如{MD009:9}的行表示"第 9 行应当报告 MD009 违规"。测试框架(test/harness.mjs)会解析这些标记,将其与真实 lint 结果比对,并进一步校验applyFixes修复后不再残留任何可修复错误。也就是说,这份用例不仅验证"报错位置是否准确",还隐含验证了"修复是否彻底"。
从完整断言看,默认配置下该用例预期报告8 处MD009 违规:
- 第 9、16、18 行(第一段有序列表)
- 第 29、36、38 行(嵌套有序列表)
- 第 44、51 行(有序列表内嵌无序列表)
MD009 规则本体:四个参数与默认值
规则全貌见 doc/md009.md,标签为whitespace,别名no-trailing-spaces,属于可修复规则(Fixable,见 lib/constants.mjs 的fixableRuleNames列表)。它检查任何以"非预期空白"结尾的行,修复方式即删除行尾空白。
其参数及默认值如下表:
| 参数 | 类型 | 默认值 | 作用 |
|---|---|---|---|
br_spaces | integer | 2 | 允许行尾存在指定数量的空格,用于产生显式换行(hard break,即<br>) |
code_blocks | boolean | false | 是否检查缩进/围栏代码块内的行尾空格 |
list_item_empty_lines | boolean | false | 是否允许列表项内"空白行"上的缩进空格 |
strict | boolean | false | 是否报告"虽然空格数量合法、但并未形成换行"的多余空白 |
注意:
br_spaces必须设置为>= 2才会生效;设为1与设为0等价,即完全禁止任何行尾空格(详见 doc/md009.md)。
默认行为逐个拆解:列表场景下到底哪些行会被报
默认配置(br_spaces=2、code_blocks=false、list_item_empty_lines=false、strict=false)下,lib/md009.mjs的判定流程可以概括为:
- 逐行计算
trailingSpaces = 行长度 - trimEnd 后长度; - 若行尾空格数大于 0,且该行不在代码块行集合、不在列表项空白行放行集合中,并且满足"数量不等于
expected"(expected = br_spaces < 2 ? 0 : br_spaces,默认即2)或 strict 相关条件,则报告违规; - 报告内容为
Expected: 0 or 2; Actual: N,并附带可修复信息(editColumn指向首个空格位置、deleteCount为空格数量)。
对照test/trailing-spaces-in-lists-default.md,可以得到三条关键结论:
结论一:列表项的"续行缩进空格"不属于行尾空格,不会误报。例如第 3~5 行(1. text后的text)虽然行首有 3 个缩进空格,但这些空格不在行尾,MD009 只看"行末空白",因此完全放行。
结论二:列表项之间"空白行上的缩进空格"在默认配置下会被报。这是本用例的核心演示点。例如第 8 行text(3 个缩进空格)与第 9 行(纯空白、含缩进空格)——注意断言{MD009:9}报告的就是第 9 行,快照(test/markdownlint-test-scenarios.mjs.snapshot)中该行errorDetail为Expected: 0 or 2; Actual: 3,errorRange为[1, 3]。同理第 15、17 行({MD009:16}、{MD009:18})也是列表项间空白行的缩进空格被报。
结论三:缩进量越大,报告的空格数越多。嵌套列表场景中,第 26、34 行等空白行带有 6 个缩进空格,对应{MD009:29}、{MD009:36}、{MD009:38}三处报告,快照显示其Actual值为6;而第 41、47 行的空白行(5 个空格)对应{MD009:44}、{MD009:51},Actual值为5。
这里需要澄清一个常见误解:默认配置并不"放行"列表项空白行,只是这些空白行上的空格恰好因为"缩进对齐"语义,容易被误认为内容的一部分。MD009 默认的放行范围只有两处——代码块内(code_blocks=false)以及"数量恰好等于br_spaces(默认 2)"的行尾空格。
深层原因:为什么空白行缩进会被判违规
要理解上面结论二、三,需要看规则实现的底层数据结构。lib/md009.mjs 基于 micromark 解析器("parser": "micromark",项目使用 lib/micromark-parse.mjs 生成 token 树),MD009 使用了以下关键 token 类型:
codeFenced/codeIndented:围栏/缩进代码块(code_blocks关闭时用addRangeToSet把它们整体加入豁免集合,见 helpers/micromark-helpers.cjs 的addRangeToSet,该方法把[start, end]闭区间逐行加入集合);listOrdered/listUnordered:有序/无序列表容器,用于计算列表项空白行豁免集合;listItemIndent/listItemPrefix:列表项缩进与标记 token;paragraph/codeText:段落与行内代码(仅strict=true时用于豁免判定)。
默认情况下,listItemEmptyLines为false,listItemLineNumbers集合为空,因此没有任何列表内空白行被豁免——行尾有任何空格就会触发报告,除非空格数量恰好为 0 或 2(br_spaces的取值)。这正是trailing-spaces-in-lists-default.md中所有报告行Actual值都是 3、5、6 等"非 0 非 2"数值的原因;而如果某个空白行恰好只缩进 2 个空格,它反而会因为"等于br_spaces"而被放行。
参数实战:list_item_empty_lines如何放行列表空白行
规则文档 doc/md009.md 明确指出:使用空格缩进列表项内部的空白行通常没有必要,但部分解析器要求这样做。当需要兼容这类解析器时,将list_item_empty_lines设为true即可放行该场景,且即使strict同时为true也仍然放行。
仓库提供了两个配套用例:
- test/trailing-spaces-in-lists-allowed.md:文件末尾通过行内配置注释启用
list_item_empty_lines: true,此时原先trailing-spaces-in-lists-default.md中的 8 处报告全部消失(从快照看 3 处为 0/2/6 的Actual不再出现),说明列表项空白行被整体豁免; - test/trailing-spaces-in-lists-allowed-strict.md:同样启用
list_item_empty_lines: true,并额外叠加"strict": true,验证了"strict 下依然放行列表空白行"这一组合语义。
对应的配置写法(两种方式等价):
{ "MD009": { "list_item_empty_lines": true } }或在 Markdown 文件内用行内配置注释:
<!-- markdownlint-configure-file { "no-trailing-spaces": { "list_item_empty_lines": true } } -->文档 doc/md009.md 给出的典型场景示例:
- list item text [2 spaces] list item text从实现上看,lib/md009.mjs 在list_item_empty_lines为真时会遍历所有listOrdered/listUnorderedtoken,把整个列表区间的行号加入豁免集合,然后从后往前扫描listItemIndent子 token:凡是"后面没有实际内容(content)"的列表项缩进行都会被从集合中删除。换句话说,只放行"确实没有内容、纯粹是缩进占位的空白行",列表项末尾带真实内容后的缩进空白仍会被检查——这正是该参数实现"仅针对空行"的精确定义。
其余三个参数的联动与边界
虽然本用例聚焦默认配置,但要完整掌握 MD009 在列表之外的判定,还需要理解另外三个参数(其行为均有对应测试佐证):
br_spaces(默认 2):只有>= 2才作为"允许数量"生效。test/trailing_spaces_br.md精确演示了这一点——单空格行报错(Actual: 1)、双空格行放行、三/四空格行报错。列表场景同理:空白行缩进恰好 2 空格时被放行,3 空格及以上则报错。code_blocks(默认 false):默认豁免围栏代码块与缩进代码块内的行尾空格(某些编程语言语法需要)。对比 test/trailing-spaces-in-code-false.md(缩进代码块内 5 行全部放行)与 test/trailing-spaces-in-code-true.md(围栏与缩进代码块内的行全部被报)即可验证。strict(默认 false):默认情况下,即使行尾空格数量等于br_spaces,只要没真的形成 hard break(比如出现在段落末尾),也不会报。开启strict后这类"不必要的换行空格"也会被报告。参见 test/hard-line-breaks.md(启用"strict": true后段落内多处双空格被报)。
参数之间的关系可以概括为:code_blocks与list_item_empty_lines决定"哪些行完全豁免",br_spaces决定"每行允许的精确空格数",strict决定"数量正确但位置不当的空格是否被追加报告"。
验证方式:如何在本仓库复现
如果希望亲手验证上述结论,可在仓库根目录执行:
npm install npm test其中test脚本(见 package.json)会运行包括test/markdownlint-test-scenarios.mjs在内的全套测试,对test/*.md逐文件执行 lint 并与{MD009:行号}断言、快照比对。需要单独观察某一用例的原始输出时,可以使用 test/harness.mjs 直接解析 token 树并打印 lint 结果:
node test/harness.mjs test/trailing-spaces-in-lists-default.md该脚本会打印文件的 micromark token 结构,随后输出lint({ "files": [ ... ] })的完整结果对象,其中包含errorDetail(如Expected: 0 or 2; Actual: 3)、errorRange与fixInfo,可与本文前述的快照数据一一对应。
小结
- MD009 默认在列表项之间的空白行上报告行尾空格(数量 ≠ 0 且 ≠
br_spaces时),test/trailing-spaces-in-lists-default.md的 8 处断言({MD009:9/16/18/29/36/38/44/51})是这一行为的直接证据; - 缩进对齐产生的行首空格不是行尾空格,不会被误报;真正被报的是空白行上的缩进空格;
- 需要兼容要求列表项空白行缩进的解析器时,用
list_item_empty_lines: true放行;需要额外放行代码块时用code_blocks: true;需要收紧判定时用strict: true;br_spaces决定硬换行允许的空格数(>= 2才生效); - 所有行为均有 lib/md009.mjs 实现与 test/markdownlint-test-scenarios.mjs.snapshot 快照双重佐证,读者可沿 doc/md009.md、doc/Rules.md 继续深入。
- 开发工具
- 代码质量
- Lint
- 静态分析
【免费下载链接】markdownlint
A Node.js style checker and lint tool for Markdown/CommonMark files.
相关推荐
markdownlint MD009 规则实战:用 list_item_empty_lines 放行列表项内空行的尾随空格
markdownlint MD009 规则实战:用 list_item_empty_lines 放行列表项内空行的尾随空格 本文以 markdownlint 仓
开发工具代码质量Lint静态分析markdownlint MD009 代码块尾随空格豁免:code_blocks 参数与内联配置实战
markdownlint MD009 代码块尾随空格豁免:code_blocks 参数与内联配置实战 本文围绕 markdownlint 的 MD009(no
开发工具代码质量Lint静态分析markdownlint MD009 尾随空格规则默认行为解析:以 trailing-spaces-in-code-default 测试场景为例
markdownlint MD009 尾随空格规则默认行为解析:以 trailing spaces in code default 测试场景为例 markdow
开发工具代码质量Lint静态分析
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考