ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Prettier check-ignore-pragma 与 `@noformat` 标记:文件级格式化豁免机制全解析

Prettier check-ignore-pragma 与 `@noformat` 标记:文件级格式化豁免机制全解析 Prettier check-ignore-pragma 与noformat标记文件级格式化豁免机制全解析【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettierPrettier 从 v3.6.0 起提供了checkIgnorePragma选项允许单个文件通过在文件头部书写noprettier/noformat注释来主动拒绝被格式化。本文以仓库测试目录tests/format/misc/check-ignore-pragma/中的 Markdown 测试用例为切入点讲解该选项的配置方式、启用与禁用两种状态下的行为差异并深入src/main/core.js、src/language-markdown/pragma.js等源码揭示 pragma 检测的完整调用链。读完本文你将掌握如何让某个文件永久豁免格式化以及为什么默认不开启此功能的底层原理并能自行在 Markdown、YAML、GraphQL、HTML 等语言中正确书写豁免标记。从一个两行测试文件说起关联文档 with-noformat-pragma.md 是 Prettier 测试套件中一个典型的测试输入文件全文只有两行有效内容!-- noformat -- I should be formatted !!它位于disabled/目录下配合同目录的 format.test.js 使用runFormatTest(import.meta, [markdown], { checkIgnorePragma: false });checkIgnorePragma: false表示关闭pragma 忽略检查——这正是该选项的默认值。此时即使文件头部带有!-- noformat --标记Prettier 依然会照常格式化文件内容。该目录的快照文件 format.test.js.snap 明确记录了这一点输入中的I should be formatted !!被输出为规范的I should be formatted !!而!-- noformat --注释本身则原样保留。简而言之这组测试验证的核心语义是pragma 只在checkIgnorePragma开启时才生效不开启时noformat注释只是普通注释不产生任何豁免效果。check-ignore-pragma 选项配置方式与默认值该选项在源码中的正式定义位于 src/main/core-options.evaluate.jscheckIgnorePragma: { category: CATEGORY_SPECIAL, type: boolean, default: false, description: Check whether the files first docblock comment contains noprettier or noformat to determine if it should be formatted., cliCategory: CATEGORY_OTHER, },官方文档 docs/options.md 对其说明如下默认值CLI 覆盖API 覆盖false--check-ignore-pragmacheckIgnorePragma: bool三种典型使用方式# CLI启用后带豁免标记的文件将不被格式化 prettier --check-ignore-pragma --write **/*.md # APINode.js 中通过选项对象传入 await prettier.format(source, { parser: markdown, checkIgnorePragma: true }); # 配置文件在 prettier.config.js 中全局开启 export default { checkIgnorePragma: true };需要特别强调的是文档明确指出Checking for these markers incurs a small upfront cost during formatting, so its not enabled by default.检测这些标记在格式化前会带来少量额外开销因此默认不启用。这是该选项默认false的根本原因——每个文件在真正进入格式化流程之前都要先扫描一遍头部注释属于额外的前置成本。启用与禁用快照测试揭示的行为对比仓库在tests/format/misc/check-ignore-pragma/下同时维护了两套互为对照的测试根目录下的 markdown/ 以checkIgnorePragma: true运行disabled/markdown/ 则以checkIgnorePragma: false运行两套测试的输入文件几乎完全一致仅正文内容不同启用套件用wont、禁用套件用should以便快照结果更易辨识。对比两边的快照可以直观看到行为差异输入checkIgnorePragma: true输出checkIgnorePragma: false输出!-- noformat --I wont format !!原样保留I wont format !!未被整理正常格式化为I should be formatted !!无 pragma 的I will format !!正常格式化为I will format !!正常格式化关键结论开启时文件首个有效位置存在noformat/noprettier标记 → 整个文件跳过格式化输出等于输入见 启用态快照 中with-noformat-pragma.md的用例。未开启时pragma 标记被忽略文件照常格式化见 禁用态快照。没有任何 pragma 的文件无论选项开与关行为完全一致都正常格式化对照without-pragma.md的用例。marker 注释本身始终保留无论是否触发豁免!-- noformat --这行注释在输出中都原样存在不会被删除或重排。底层原理从选项到豁免的完整调用链checkIgnorePragma并不是在文档打印print阶段起作用的而是在格式化的最前端——src/main/core.js的formatWithCursor函数中完成短路判断async function hasPragma(text, options) { const selectedParser await resolveParser(options); return !selectedParser.hasPragma || selectedParser.hasPragma(text); } async function hasIgnorePragma(text, options) { const selectedParser await resolveParser(options); return selectedParser.hasIgnorePragma?.(text); } async function formatWithCursor(originalText, originalOptions) { // ... if ( (options.rangeStart options.rangeEnd text ! ) || (options.requirePragma !(await hasPragma(text, options))) || (options.checkIgnorePragma (await hasIgnorePragma(text, options))) ) { return { formatted: originalText, // 直接返回原文一步格式化都不做 cursorOffset: originalOptions.cursorOffset, comments: [], }; } // ... }见 src/main/core.js这段代码揭示了三件事hasIgnorePragma通过resolveParser找到当前语言对应的解析器插件然后调用插件暴露的hasIgnorePragma方法如果解析器没有实现该方法则视为不存在豁免标记。检测命中后formatted直接等于originalText也就是说 Prettier连光标偏移处理、range 处理、BOM 处理之后的格式化流程都不会进入真正做到零改动跳过。该短路判断与requirePragma要求文件必须带format/prettier标记才格式化并列二者都是文件级准入/豁免策略属于同一套设计哲学。Markdown 专属实现pragma.js 与 front matter 处理不同语言对文件第一个注释的定义并不相同因此每个语言插件都各自实现了hasIgnorePragma。Markdown 的实现位于 src/language-markdown/pragma.jsconst hasIgnorePragma (text) parseFrontMatter(text) .content.trimStart() .match(MARKDOWN_HAS_IGNORE_PRAGMA_REGEXP)?.index 0;这里有两个值得注意的设计front matter 先行剥离parseFrontMatter实现见 src/main/front-matter/parse.js会先识别并剥离文件开头的 YAML / TOML front matter以---或开头支持...作为 YAML 结束分隔符再把剩余的正文用于 pragma 匹配。这意味着 pragma 既可以直接写在文件第一行也可以写在 front matter 块之后的第一行——仓库测试文件 front-matter-with-noformat-pragma.md 专门验证了这种场景。匹配必须命中索引 0?.index 0要求匹配到的 pragma 必须位于去空白后的正文最开头。也就是说noformat注释必须是文件或 front matter 之后的第一个有效内容位置稍有偏移便不生效。而 Markdown 的匹配正则定义在 src/utilities/pragma/pragma.evaluate.js它支持三种书写形式export const [MARKDOWN_HAS_PRAGMA_REGEXP, MARKDOWN_HAS_IGNORE_PRAGMA_REGEXP] [ FORMAT_PRAGMAS, // [format, prettier] FORMAT_IGNORE_PRAGMAS, // [noformat, noprettier] ].map((pragmas) { const pragma (?:${pragmas.join(|)}); return new RegExp( [ String.raw!--\s*${pragma}\s*--, String.raw\{\s*\/\*\s*${pragma}\s*\*\/\s*\}, !--.*\r?\n[\\s\\S]*(^|\n)[^\\S\n]*${pragma}[^\\S\n]*($|\n)[\\s\\S]*\n.*--, ].join(|), m, ); });对应的三种合法写法分别是!-- noformat --{/* noformat */}!-- 一些说明文字 noformat 更多说明文字 --注意第三种形式是多行注释只要noformat出现在注释内部任意一行的行首即可命中。因此你可以把豁免标记和一段说明例如此文件为自动生成请勿修改合写在一个多行注释里这也是官方文档示例JS docblock之外的、Markdown 语言专属的灵活用法。其他语言中的豁免标记写法noprettier/noformat并不是 Markdown 专有机制。在 src/utilities/pragma/pragma.evaluate.js 中YAML、GraphQL、HTML 各有对应的正则且同样共享noformat/noprettier这两个标记词YAMLYAML_HAS_IGNORE_PRAGMA_REGEXP文件头部以# noformat注释开头GraphQLGRAPHQL_HAS_IGNORE_PRAGMA_REGEXP# noformat注释HTMLHTML_HAS_IGNORE_PRAGMA_REGEXP!-- noformat --注释Markdown如上文所述的三种形式JS / CSS 等其余语言复用首个 docblock/注释的通用约定noprettier/noformat均可。仓库测试套件 check-ignore-pragma 为 css、graphql、html、js、json5、markdown、mdx、vue、yaml 等九种语言都准备了启用/禁用双向的用例且每种语言都覆盖了noformat与noprettier两种标记以及无标记对照是理解该功能跨语言行为的最佳参考。与 requirePragma、insertPragma 的关系checkIgnorePragma在 src/main/core-options.evaluate.js 中与requirePragma、insertPragma同属pragma 家族三者构成一套完整的渐进式采用方案选项作用典型场景insertPragma在文件头部插入format标记团队逐步迁移参与者格式化一批文件并打上已格式化烙印requirePragma只格式化带format/prettier标记的文件CI 与自动化工具只处理已迁移文件checkIgnorePragma跳过带noformat/noprettier标记的文件让特定文件如第三方生成物、含特殊格式的文档永久豁免从 src/main/core.js 的formatWithCursor可以看出requirePragma与checkIgnorePragma的判断发生在同一处短路逻辑中两者都是按文件头部标记决定是否进入格式化而insertPragma则在正常格式化前调用printer.insertPragma(text)注入format标记。官方文档 docs/options.md 对insertPragma与requirePragma的关系有专门说明二者不建议同时开启同时开启时requirePragma优先级更高。实践建议豁免标记应写在整个文件的最前面若存在 front matter则紧随其后因为实现要求匹配命中正文的索引 0Markdown 中可写在!-- --单行注释、{/* */}注释或包含说明文字的多行注释内。不要依赖默认开启checkIgnorePragma默认false即便文件中写了noformat也不会生效。要么在 CLI / API / 配置中显式开启要么改用!-- prettier-ignore --等行内忽略注释该功能与 pragma 机制相互独立。豁免是文件级的一旦命中 pragma整个文件原样返回无法做到只豁免文件中的某一段。如果只需要局部豁免应使用行级/块级 ignore 注释。适合自动生成文件或特殊排版文档当某个文件由脚本生成、或包含必须保持原始空白的表格/ASCII 图时在文件头标注noformat并配合 CI 中的--check-ignore-pragma可以避免格式化工具反复改写这些内容。延伸阅读仓库内相关文件选项定义与官方说明src/main/core-options.evaluate.js、docs/options.md短路判断与调用链src/main/core.jsMarkdown 实现与正则src/language-markdown/pragma.js、src/utilities/pragma/pragma.evaluate.jsfront matter 剥离逻辑src/main/front-matter/parse.js跨语言测试套件tests/format/misc/check-ignore-pragma/【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表