markdownlint MD009 规则进阶:用 `code_blocks: true` 检查代码块内的行尾空白

发布时间:2026/10/16 8:49:43
markdownlint MD009 规则进阶:用 `code_blocks: true` 检查代码块内的行尾空白 开发工具代码质量Lint静态分析【免费下载链接】markdownlintA Node.js style checker and lint tool for Markdown/CommonMark files.项目地址https://gitcode.com/gh_mirrors/ma/markdownlint点击查看免费下载本文以 markdownlint 仓库中的场景测试文档 test/trailing-spaces-in-code-true.md 为切入点深入讲解 MD009no-trailing-spaces规则在开启code_blocks参数后如何把检查范围从普通正文扩展到缩进代码块与围栏代码块并结合规则源码 lib/md009.mjs、官方规则说明 doc/md009.md 与 JSON Schema 配置定义说明每个参数的默认值、取值语义和自动修复行为。读完本文你将能准确判断自己的 Markdown 文档中哪些行尾空白会被 MD009 报告、如何通过配置文件精确控制检查边界以及如何利用测试标记语法验证规则行为。一、测试文档是什么MD009 行为的三段式实验样本markdownlint 仓库把test/目录下的.md文件当作“场景测试样本”每个文件就是一个真实的 Markdown 文档其中嵌入了{MD009:-5}之类的预期错误标记并由测试驱动 test/markdownlint-test-scenarios.mjs 自动 lint 该文件、比对标记行号与实际报告行号最后对结果生成快照 test/markdownlint-test-scenarios.mjs.snapshot。trailing-spaces-in-code-true.md正是这一机制下的一个典型样本。它的文件名即测试意图“代码中的行尾空白Code True”即把no-trailing-spaces规则的code_blocks参数显式配置为true验证代码块内的行尾空白此时也会被报告。文档整体分为三个结构完全相同的段落分别对应 Markdown 的三种代码形态普通段落代码行第 1 段未经缩进、无围栏会被解析为普通文本缩进代码块第 2 段每行以 4 个空格开头构成codeIndented围栏代码块第 3 段以js围栏包裹构成codeFenced。三种形态内部都是同一份 5 行 JavaScript 代码const a 10;到const e 10;但行尾空白数量从 0 递增到 4 个空格形成一组梯度样本随后用一行预期标记注释声明“应当报告哪些行”。二、逐行解读测试样本标记语法与内联配置用sed ... | cat -A查看该文件可看到行尾空白的确切字节分布每行末尾的$表示行结束之前的多余空格即为“行尾空白”# Trailing Spaces in Code - True$ $ const a 10;$ const b 10; $ ← 行尾 1 个空格 const c 10; $ ← 行尾 2 个空格 const d 10; $ ← 行尾 3 个空格 const e 10; $ ← 行尾 4 个空格 $ {MD009:-5} {MD009:-3} {MD009:-2}$ $ const a 10;$ const b 10; $ const c 10; $ const d 10; $ const e 10; $ $ {MD009:-5} {MD009:-3} {MD009:-2}$ $ js$ const a 10;$ const b 10; $ const c 10; $ const d 10; $ const e 10; $ $ $ {MD009:-6} {MD009:-4} {MD009:-3}$文件末尾通过 HTML 注释形式的内联配置inline configuration开启检查!-- markdownlint-configure-file { code-block-style: false, no-trailing-spaces: { code_blocks: true } } --code-block-style: false是为了屏蔽 MD046 对代码块风格一致性consistent的干扰让本样本只聚焦 MD009no-trailing-spaces.code_blocks: true则是本次实验的“自变量”——把检查范围扩大到代码块。预期标记{MD009:-5}的含义测试驱动 test/markdownlint-test-scenarios.mjs 中的正则/\{(MD\d)(?::([-]?)(\d))?\}/g解析这些标记MD009表示期望触发 MD009 规则-5表示“该标记所在行的上数 5 行”为预期错误行-为负向偏移为正向偏移无符号则为绝对行号。因此第一组标记{MD009:-5} {MD009:-3} {MD009:-2}位于第 9 行分别指向上数第 5、3、2 行即第 4、6、7 行——对应行尾为 1、3、4 个空格的三行。注意恰好 2 个空格的const c 10;第 5 行没有被标记这正是 MD009 默认允许 2 个空格作为硬换行hard break的设计。缩进代码块段第 11~15 行同理标记指向第 12、14、15 行。围栏代码块段第 19~25 行因围栏占去一行标记偏移变为-6 -4 -3指向第 21、23、24 行。快照中的完整验证结果在快照 test/markdownlint-test-scenarios.mjs.snapshot 中trailing-spaces-in-code-true.md的用例报告了全部 9 个错误每段 3 个例如{ lineNumber: 4, ruleNames: [MD009, no-trailing-spaces], ruleDescription: Trailing spaces, errorDetail: Expected: 0 or 2; Actual: 1, errorRange: [14, 1], fixInfo: { editColumn: 14, deleteCount: 1 }, severity: error }errorDetail的 “Expected: 0 or 2” 呼应了规则参数br_spaces默认值为 2即允许 0 个或恰好 2 个行尾空格fixInfo则给出可自动修复的列位置与删除数量证明 MD009 属于“Fixable”可修复规则doc/md009.md 顶部亦有此说明。三、规则源码解析code_blocks到底改了什么要理解为什么true与默认行为不同需要看规则实现 lib/md009.mjs。核心逻辑如下let brSpaces params.config.br_spaces; brSpaces Number((brSpaces undefined) ? 2 : brSpaces); const codeBlocks params.config.code_blocks; const includeCode (codeBlocks undefined) ? false : !!codeBlocks; const listItemEmptyLines !!params.config.list_item_empty_lines; const strict !!params.config.strict; const codeBlockLineNumbers new Set(); if (!includeCode) { for (const codeBlock of filterByTypesCached([ codeFenced ])) { addRangeToSet(codeBlockLineNumbers, codeBlock.startLine 1, codeBlock.endLine - 1); } for (const codeBlock of filterByTypesCached([ codeIndented ])) { addRangeToSet(codeBlockLineNumbers, codeBlock.startLine, codeBlock.endLine); } }这段代码揭示了code_blocks的真实机制它不是“开启后新增一类检查”而是“关闭时预先豁免代码块”。默认情况下includeCode false规则通过 micromark 解析出的codeFenced围栏代码块与codeIndented缩进代码块两类 token把它们的行号收集进codeBlockLineNumbers集合随后逐行检查时跳过这些行——这正是 doc/md009.md 所说明的默认允许代码块内的行尾空白因为“某些编程语言要求行尾空白有意义”。当code_blocks: true时includeCode为真codeBlockLineNumbers保持为空集合豁免逻辑失效代码块行与普通文本行一视同仁地接受行尾空白检查。围栏代码块的豁免区间是startLine 1到endLine - 1围栏行本身不豁免围栏后的行尾空白照常检查而缩进代码块是整个startLine到endLine区间。addRangeToSet来自 helpers/micromark-helpers.cjs负责把闭区间内的行号批量加入集合。逐行判定与自动修复随后的主循环逐行计算行尾空白数量并依据参数组合决定是否报告const expected (brSpaces 2) ? 0 : brSpaces; for (let lineIndex 0; lineIndex params.lines.length; lineIndex) { const line params.lines[lineIndex]; const lineNumber lineIndex 1; const trailingSpaces line.length - line.trimEnd().length; if ( trailingSpaces !codeBlockLineNumbers.has(lineNumber) !listItemLineNumbers.has(lineNumber) ( (expected ! trailingSpaces) || (strict (!paragraphLineNumbers.has(lineNumber) || codeInlineLineNumbers.has(lineNumber))) ) ) { const column line.length - trailingSpaces 1; addError( onError, lineNumber, Expected: (expected 0 ? : 0 or ) expected ; Actual: trailingSpaces, undefined, [ column, trailingSpaces ], { editColumn: column, deleteCount: trailingSpaces } ); } }关键点expected (brSpaces 2) ? 0 : brSpacesbr_spaces只有设为2 或更大才生效设为 1 与 0 行为相同即完全禁止行尾空白doc/md009.md 明确强调“必须将br_spaces设为 2该参数才会生效”。判定条件为“有行尾空白”且“不在豁免集合中”且数量不等于expected或 strict 模式下的特殊场景。报告错误时附带fixInfo: { editColumn, deleteCount }供applyFixes自动删除多余空格——这正是快照中fixed字段内容变化的原因。其他三个参数对检查范围的影响br_spacesinteger默认2允许的行尾空格数用于显式硬换行。list_item_empty_linesboolean默认false设为true时允许列表项内的空行保留空格部分解析器有此要求即使在strict: true下也生效doc/md009.md。源码中通过listItemLineNumbers集合对listOrdered/listUnordered的listItemPrefix、listItemIndent、content子 token 进行行号收集与回收。strictboolean默认false默认情况下即使行尾空格数量恰好等于允许值只要不构成硬换行例如出现在段落末尾也不会报告strict: true会把这类“不必要的换行”也揪出来。源码中表现为把paragraph与codeText行内代码的行号纳入专门集合后参与条件判断。四、三份对照样本默认、false与true的行为差异仓库中还有两份同系列对照样本构成完整的行为矩阵样本文件配置报告数量报告范围test/trailing-spaces-in-code-default.md仅code-block-style: false未设置code_blocks3 个仅普通段落第 4、6、7 行test/trailing-spaces-in-code-false.mdcode_blocks: false显式关闭3 个仅普通段落第 4、6、7 行test/trailing-spaces-in-code-true.mdcode_blocks: true显式开启9 个普通段落 缩进代码块 围栏代码块从快照 test/markdownlint-test-scenarios.mjs.snapshot 可以确认default与false两份样本的errors数组都只有 3 项只覆盖普通段落只有true样本是完整的 9 项。这验证了规则源码中“codeBlocks undefined与显式false等价”的行为——undefined与false都会让includeCode为假。自动修复结果的对比三份样本快照中的fixed字段同样值得对比。以true样本为例修复后的内容为换行符已归一化# Trailing Spaces in Code - True const a 10; const b 10; const c 10; const d 10; const e 10; ...可见修复策略是删除数量不为 2 的行尾空格1、3、4 个被清除但保留恰好 2 个空格的行因为 2 个空格是合法的硬换行。default/false样本中代码块内的行尾空白因被豁免而原样保留在fixed输出里这从侧面印证了“只有code_blocks: true时修复才会触及代码块行”。快照之外测试驱动还会对修复结果再次执行 lint 并断言“不存在仍有fixInfo的错误”保证自动修复一次到位test/markdownlint-test-scenarios.mjs 中 “Fixable error(s) not fixed.” 断言。五、在实际项目中如何配置与使用配置文件写法在项目根目录创建.markdownlint.json或.markdownlint.jsonc、.markdownlint.yaml即可全局开启对代码块行尾空白的检查{ no-trailing-spaces: { code_blocks: true, br_spaces: 2, list_item_empty_lines: false, strict: false } }对应的 YAML 写法no-trailing-spaces: code_blocks: true br_spaces: 2 list_item_empty_lines: false strict: false如果只想对某个文件临时启用可以在该文件内使用与测试样本相同的内联配置注释!-- markdownlint-configure-file { no-trailing-spaces: { code_blocks: true } } --运行检查的命令行工具为 markdownlint-cli / markdownlint-cli2见 README.md示例npx markdownlint-cli2 **/*.md参数取值速查表依据 JSON Schema规则在配置 schema schema/markdownlint-config-schema.json 中定义如下参数参数类型默认值说明enabledbooleantrue是否启用该规则severitystringerror报告级别可选error/warningbr_spacesinteger最小值 02允许用于换行的行尾空格数必须 2 才生效code_blocksbooleanfalse是否检查代码块内的行尾空白list_item_empty_linesbooleanfalse是否允许列表项内空行保留空格strictbooleanfalse是否报告未构成换行的多余空格规则整体还支持直接写布尔值no-trailing-spaces: false或error/warning字符串来快速启停。一条实用的决策建议是否开启code_blocks: true取决于你的代码块内容形态如果代码块内主要是 JavaScript、Python、Go 等对行尾空白不敏感、编辑器通常不会留尾随空格的代码开启后可以帮你保持仓库“零行尾空白”的整洁状态也便于 CI 统一校验如果文档中混有对行尾空格敏感或需要保留尾随空白的内容例如部分 shell 脚本、格式文件、逐字复制的日志片段建议保持默认false或用strictlist_item_empty_lines组合做更细粒度的控制可参考 test/trailing-spaces-in-lists-allowed-strict.md 的用法。六、小结从测试样本到规则的完整闭环test/trailing-spaces-in-code-true.md虽然看起来只是一个 34 行的测试数据文件但它完整覆盖了 MD009 规则的关键行为面三种代码形态普通段落、缩进代码块、围栏代码块在同一配置下的统一表现行尾空格数量的梯度0/1/2/3/4验证了br_spaces 2的“0 或 2 个允许”语义标记语法{MD009:-N}精确描述了预期行号与规则实现、快照三方互相印证内联配置注释展示了无需全局配置文件即可局部调整规则参数的官方用法。当你理解了code_blocks: true在 lib/md009.mjs 中“取消豁免”的本质后就可以举一反三同样的 token 豁免思路也适用于 MD010制表符、MD040 等与代码块相关的规则。测试文档既是回归测试的资产也是理解规则语义最直接、最权威的“活文档”。赞分享开发工具代码质量Lint静态分析【免费下载链接】markdownlintA Node.js style checker and lint tool for Markdown/CommonMark files.项目地址https://gitcode.com/gh_mirrors/ma/markdownlint点击查看免费下载相关推荐markdownlint MD009 代码块尾随空格豁免code_blocks 参数与内联配置实战markdownlint MD009 代码块尾随空格豁免code_blocks 参数与内联配置实战 本文围绕 markdownlint 的 MD009no开发工具代码质量Lint静态分析markdownlint MD009 尾随空格规则代码块与严格模式的完整配置实战markdownlint MD009 尾随空格规则代码块与严格模式的完整配置实战 导读 本文以 markdownlint 仓库中的 code block tr开发工具代码质量Lint静态分析markdownlint 规则 MD009no-trailing-spaces深入解析尾随空白的检测、配置与自动修复markdownlint 规则 MD009no trailing spaces深入解析尾随空白的检测、配置与自动修复 本篇文章围绕 markdownlin开发工具代码质量Lint静态分析上一篇Decepticon 文件媒介式多 Agent 协作契约Workspace 目录布局、终端状态交接与编排器读取协议下一篇大模型下载太慢3 条镜像路线 1 份避坑清单3 分钟跑通创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询