Biome 的 useFencedCodeLanguage 规则:用 languageOnly 选项约束 Markdown 围栏代码块的信息字符串
开发工具Lint格式化静态分析代码质量前端【免费下载链接】biomeA toolchain for web projects, aimed to provide functionalities to maintain them. Biome offers formatter and linter, usable via CLI and LSP.项目地址https://gitcode.com/gh_mirrors/bi/biome点击查看免费下载本文聚焦 Biome 仓库中biome_markdown_analyze的 lint 规则useFencedCodeLanguageMarkdown 语言重点讲解其languageOnly选项的行为与实现它要求围栏代码块fenced code block的 info string 只能包含语言标签本身不允许携带title...之类的元数据。文章以该规则的真实测试用例languageOnly.md为主线结合源码实现与快照输出说明如何在biome.json中配置该规则、理解其诊断行为以及从源码层面掌握其判定逻辑。读完你将能精确配置 Markdown 代码块语言标签约束并读懂配套的 spec 测试文件。一、规则背景为什么需要为代码块强制声明语言标签Markdown 中的围栏代码块以 或 ~~~ 包裹的代码块可以在开围栏后紧跟一段信息字符串info string渲染器如 GitHub、VitePress、Docusaurus通常取其中的第一个词作为语言标签用于语法高亮。如果信息字符串为空代码块将无法获得任何高亮而如果信息字符串中混入了额外元数据如js titlea.js不同渲染器的解析结果可能不一致。useFencedCodeLanguage规则正是为此设计。在 use_fenced_code_language.rs 中规则声明如下pub UseFencedCodeLanguage { version: 2.5.14, name: useFencedCodeLanguage, language: md, sources: [RuleSource::MarkdownLint(md040, fenced-code-language).same()], recommended: true, }几个关键事实规则位于nursery组说明它尚未完全稳定行为可能在后续版本变化它的来源对应 markdownlint 的MD040 / fenced-code-language规则规则查询的语法节点是MdFencedCodeBlock见 use_fenced_code_language.rs规则暴露两个可配置选项allowedLanguages与languageOnly二者由 use_fenced_code_language.rsbiome_rule_options 中的UseFencedCodeLanguageOptions结构体承载。二、languageOnly 选项本测试文档的核心主题2.1 选项语义根据规则文档use_fenced_code_language.rs当languageOnly为true时要求 info string 恰好只包含语言标签不允许携带元数据或其他非空白内容。默认值为false。对应 JSON 配置{ options: { languageOnly: true } }在 biome_rule_options/src/use_fenced_code_language.rs 中该选项被定义为Optionbool/// Requires the info string to contain only a language tag, without metadata or other /// non-whitespace content. #[serde(skip_serializing_if Option::_::is_none)] pub language_only: Optionbool,结构体整体使用#[serde(rename_all camelCase, deny_unknown_fields, default)]因此配置文件中写languageOnlycamelCase且未知字段会被拒绝。2.2 测试文档 languageOnly.md 的完整内容本任务关联的文档 languageOnly.md 是biome_markdown_analyze的 spec 测试输入全文如下!-- metadata should generate a diagnostic; surrounding whitespace is allowed -- js console.log(1) js titlea.js console.log(1) js console.log(1) js console.log(1) 文件头部的注释直接点明了该用例要验证的两件事元数据应产生诊断js titlea.js这类语言标签 元数据的组合在languageOnly: true下应当报错周围空白是允许的 js语言标签前存在空格不会产生诊断。这 4 个代码块构成了一组对照实验代码块info string期望行为jsjs通过js titlea.jsjs titlea.js产生诊断jsjs通过 jsjs含前导空格通过空白被允许对应的规则配置位于同目录的 languageOnly.options.json{ $schema: ../../../../../../packages/biomejs/biome/configuration_schema.json, linter: { rules: { nursery: { useFencedCodeLanguage: { level: error, options: { languageOnly: true } } } } } }即将规则级别设为error并开启languageOnly: true。2.3 快照输出期望诊断的精确形态该用例的期望输出记录在 languageOnly.md.snap 中。整个测试只产生1 条诊断定位在第 7 行第 6 列js titlea.js中js之后的元数据部分languageOnly.md:7:6 lint/nursery/useFencedCodeLanguage i This info string contains content beyond the language tag. 7 │ js titlea.js │ ^^^^^^^^^^^^^ ... i The languageOnly option requires the info string to contain only a language tag. i Remove metadata after the language tag.诊断文本由源码 use_fenced_code_language.rs 中的ExtraInfo分支生成包含三层信息主消息This info string contains content beyond the language tag.info string 包含语言标签以外的内容说明The languageOnly option requires the info string to contain only a language tag.修复提示Remove metadata after the language tag.删除语言标签后的元数据。快照同时验证了两个边界行为 js前导空格未命中诊断而同一文件中其他仅含js的块也未命中诊断。三、从源码理解判定逻辑三个问题状态3.1 判定流程useFencedCodeLanguage规则的核心逻辑在run方法中use_fenced_code_language.rs。它定义了三种违规形态FencedCodeLanguageIssuepub enum FencedCodeLanguageIssue { /// The info string has no language tag. Missing, /// The language tag isnt part of the allowedLanguages option. NotAllowed(TextRange), /// The info string has content beyond the language tag while languageOnly is enabled. ExtraInfo(TextRange), }Missinginfo string 缺失或全为空白——对应无语言标签的代码块NotAllowed语言标签不在allowedLanguages白名单内ExtraInfolanguageOnly开启时info string 中语言标签之后还有多余内容——这正是本测试文档命中的状态。run的关键步骤通过block.code_list()取 info string。源码注释特别说明code-info-string 的 lexing 会把非边界处的 info string 作为一个整体字面量保留因此code_list中只有一个元素需要再把它拆分为语言标签 元数据两部分对 info string 做trim()若为空则报Missing用split_once(char::is_whitespace)取出第一个空白分隔的 token 作为语言标签计算语言标签与多余内容的精确文本范围TextRange供诊断定位使用。3.2 languageOnly 的判定细节对应ExtraInfo的判断逻辑use_fenced_code_language.rsif options.language_only Some(true) trimmed.len() language.len() { let extra_start language_end; let extra_end leading_ws trimmed.len(); if let Some(range) to_range(extra_start, extra_end) { issues.push(FencedCodeLanguageIssue::ExtraInfo(range)); } }判定条件拆解只有当language_only显式为Some(true)时才启用trimmed.len() language.len()成立意味着语言标签之后还有非空白内容诊断范围从language_end开始、延伸到extra_end因此js titlea.js中被标注的是titlea.js这段快照中的^^^^^^^^^^^^^即此范围。这也解释了为什么周围空白是允许的 js的 info string 是js先trim()得到js再按空白拆分后语言标签就是jstrimmed.len() language.len()不满足条件自然不产生诊断。3.3 Missing 场景的诊断差异当信息字符串为空时如\n...\nrun会返回Missing状态诊断消息为use_fenced_code_language.rs主消息This fenced code block is missing a language tag.提示渲染器使用语言标签为受支持的语言选择语法高亮修复建议如果配置了allowedLanguages则提示添加allowedLanguages中配置的某个语言否则提示在开围栏后添加语言标签如js或在不希望/不支持高亮时使用text。四、在 biome.json 中实战配置4.1 仅开启 languageOnly{ linter: { rules: { nursery: { useFencedCodeLanguage: { level: error, options: { languageOnly: true } } } } } }适用场景文档中代码块统一不带元数据例如站点的代码块标题由其他机制注入强制 info string 保持纯净。4.2 与 allowedLanguages 组合使用languageOnly与allowedLanguages可以同时启用参考同目录的 combined.options.json{ linter: { rules: { nursery: { useFencedCodeLanguage: { level: error, options: { allowedLanguages: [js, ts], languageOnly: true } } } } } }对应的测试输入 combined.md 只有一行注释和一个代码块python titleexample.py print(1) python既不在白名单内info string 又带有title元数据因此同时命中NotAllowed与ExtraInfo两类诊断——这正是同时产生两种问题的组合用例。4.3 仅使用 allowedLanguages若不需要languageOnly约束可参考 allowedLanguages.options.json{ linter: { rules: { nursery: { useFencedCodeLanguage: { level: error, options: { allowedLanguages: [js, ts] } } } } } }其测试输入 allowedLanguages.md 验证了多个细节无语言标签的块 →Missingjs、python→js通过、python报NotAllowedJS大写→ 报NotAllowed因为语言标签按大小写敏感匹配规则文档明确 Language tags are matched case-sensitivelyjs titleexample.js→ 在未开启languageOnly时js合法因此不报错js前导空格→ 正常通过。注意allowedLanguages为空默认[]时接受任意语言标签此时规则只检查是否缺失语言标签。五、配套测试覆盖规则的完整行为矩阵useFencedCodeLanguage在仓库中拥有成体系的测试资产均位于 tests/specs/nursery/useFencedCodeLanguage 目录valid.md零诊断基线。覆盖js、~~~python波浪线围栏、js titlea.js未开languageOnly时合法、text等通过场景特别验证了缩进代码块不是围栏代码块不会被该规则检查invalid.md纯Missing场景合集。包括普通围栏、波浪线围栏、列表项内的围栏、引用块内的围栏以及未闭合的围栏——说明规则对嵌套在列表/引用中的代码块同样生效languageOnly.md本文核心验证languageOnly: true下的ExtraInfo判定与空白容忍allowedLanguages.md验证白名单、大小写敏感、空列表语义combined.md验证两选项同时开启时诊断叠加。此外tests/suppression/nursery/useFencedCodeLanguage 目录还包含规则的抑制suppression测试覆盖了code.md、nested.md、nested_quote.md、suppressed_multiline_quote.md等场景用于验证// biome-ignore等抑制注释在 Markdown 代码块上的行为。这些测试由 spec_tests.rs 驱动每个.md输入搭配可选的.options.json生成.snap快照形成输入 → 配置 → 期望诊断的完整闭环。六、使用须知与限制nursery 稳定性规则属于 nursery 组尚未稳定未来可能调整行为或默认配置。快照输出中也会附带提示This rule belongs to the nursery group, which means it is not yet stable and may change in the future.见 languageOnly.md.snap大小写敏感allowedLanguages按大小写严格匹配js不匹配JS空列表语义allowedLanguages为空表示不限制语言标签只检查是否缺失作用范围规则只作用于围栏代码块缩进代码块不会被检查配置校验选项结构体启用了deny_unknown_fields配置中任何拼写错误的选项名都会导致配置解析失败配置模式可参考 packages/biomejs/biome/configuration_schema.json。综上languageOnly是useFencedCodeLanguage规则中用于收紧 info string 纯净度的选项配合allowedLanguages白名单可以让团队 Markdown 文档中的代码块语言声明保持高度一致。本文给出的配置片段、判定逻辑与测试用例均可在当前仓库对应路径中直接验证。赞分享开发工具Lint格式化静态分析代码质量前端【免费下载链接】biomeA toolchain for web projects, aimed to provide functionalities to maintain them. Biome offers formatter and linter, usable via CLI and LSP.项目地址https://gitcode.com/gh_mirrors/bi/biome点击查看免费下载相关推荐Biome Markdown 规则 useFencedCodeLanguage 详解强制围栏代码块声明语言标签Biome Markdown 规则 useFencedCodeLanguage 详解强制围栏代码块声明语言标签 导读 useFencedCodeLanguag开发工具Lint格式化静态分析代码质量前端Biome useFencedCodeLanguage 规则实战强制 Markdown 围栏代码块声明语言标签Biome useFencedCodeLanguage 规则实战强制 Markdown 围栏代码块声明语言标签 useFencedCodeLanguage 是开发工具Lint格式化静态分析代码质量前端Biome useFencedCodeLanguage 规则实战指南强制 Markdown 围栏代码块声明语言标签Biome useFencedCodeLanguage 规则实战指南强制 Markdown 围栏代码块声明语言标签 导读 本文围绕 Biome 中用于 Mar开发工具Lint格式化静态分析代码质量前端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考