ARTICLE DETAIL

资讯详情

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

Prettier Markdown 多行 Wiki 链接格式化深度解析:测试用例、输出快照与源码实现

Prettier Markdown 多行 Wiki 链接格式化深度解析:测试用例、输出快照与源码实现 Prettier Markdown 多行 Wiki 链接格式化深度解析测试用例、输出快照与源码实现【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettierPrettier 的 Markdown 解析器内置了对 Wiki 风格链接即 Obsidian 等笔记工具中常见的[[目标页面]]语法的识别与格式化支持。本文以仓库测试夹具 multi-line.md 为核心完整梳理 Prettier 面对跨行、嵌套、转义、未闭合以及混合行内标记的多行[[...]]结构时到底改了什么、留了什么并从解析层、预处理层、打印层三层源码揭示背后的实现原理。读完本文你将准确掌握 Prettier 对多行 Wiki 链接的全部格式化规则能够在自己的 Markdown 工程中预判格式化结果并规避格式化后链接被意外改写的坑。一、定位这份测试夹具在测什么在 Prettier 仓库中tests/format/markdown/wiki-link/目录专门用于验证 Markdown 解析器对 Wiki 链接语法的格式化行为目录下共 12 个测试夹具与对应的快照simple.md单行基础 Wiki 链接[[A simple wiki link on a single line]]multi-line.md本篇主角集中考察跨行场景共 19 组用例加 1 个引用块用例nested-link.mdWiki 链接内部再嵌套普通链接balias/别名语法[[目标|别名]]相关对应 issue-19525 的回归场景其余夹具分别覆盖额外空格、行尾折行、超长行、多余方括号等边界情况。驱动这些夹具的是 format.test.js它用runFormatTest以4 种参数组合各跑一遍runFormatTest(import.meta, [markdown], { proseWrap: always }); runFormatTest(import.meta, [markdown], { proseWrap: always, singleQuote: true, }); runFormatTest(import.meta, [markdown], { proseWrap: never }); runFormatTest(import.meta, [markdown], { proseWrap: preserve });即proseWrap: always默认、always singleQuote、never、preserve四种模式覆盖了 Prettier 对 Markdown 段落折行的全部策略。所有输入输出对都固化在快照snapshots/format.test.js.snap 中。二、完整用例清单输入与输出逐条对照以下为 multi-line.md 的 19 组用例另含 1 个引用块右侧是proseWrap: always模式下的输出该夹具的 4 种运行组合输出完全一致原因见第四节。为便于阅读\已按 Markdown 字面量还原#输入输出proseWrap: always行为归类1[[a/b]][[a/b]]保持多行2[[This is **not** a/_wiki link_]]同上原样保留保持多行3[[test1/[[wiki link]]/[[wiki link]] test2/]][[test1/[[wiki link]]/[[wiki link]] test2 ]]保持多行仅收尾处换行被折叠为空格4\[[test3 text/]]原样保留转义开头\[[按普通文本保持5[[test4 text/\]]原样保留转义结尾\]]保持6[[test5/abc ***def*** ghi/text]][[test5/abc _**def**_ ghi/text]]保持多行行内标记被规范化7[[test6 ***/text***text***text/*** text][[test6 *** text***text***text *** text]未闭合[[折叠为单行8\\[[test7/text]]原样保留双反斜杠转义开头保持9\\\[[test8/text]]原样保留三反斜杠转义开头保持10\\\\[[test9/text]]原样保留四反斜杠转义开头保持11\\\[[[test10/text]]原样保留混合转义保持12[[test11/text\]]原样保留转义结尾保持13[[test12/text\\\]]原样保留多反斜杠 转义结尾保持14[[test13/text\\\\]]原样保留多反斜杠保持15[[test14/text\\\]]]原样保留转义 多余右括号保持16test15]] text [[text/text]]原样保留两行行首已有闭合 行尾未闭合配对保持17[test16/text]][test16 text]]只有]]无[[折叠为单行18[[test17/text][[test17 text]只有[[无]]折叠为单行19[[test18 !--/comment/--]]原样保留内含 HTML 注释保持多行20[[test19 spantext/span/text]]原样保留内含行内 HTML保持多行引用块 [[quote test/ text]]原样保留块引用内的多行链接保持三、逐类行为解析改了什么、留了什么3.1 真正跨行的[[...]]一律保持多行用例 1、2、5、19、20 与引用块用例的输入输出完全一致[[a\nb]]不会被打平成[[a b]][[This is **not** a\n_wiki link_]]的换行也原样保留即使段落内部发生了其他规范化。这一点对写作时链接跨行的用户非常友好——Prettier 不会为了统一折行而破坏已有的跨行 Wiki 链接。特别值得注意的是用例 5 中abc ***def*** ghi被改写为abc _**def**_ ghi。这是 Markdown 行内标记的标准规范化***def***是加粗 斜体的合并写法Prettier 统一渲染为_**def**_斜体套加粗。这个细节非常关键它证明跨行的[[...]]内部内容仍按普通 Markdown 行内语法解析见第五节原理分析。3.2 收尾行的换行会被折叠为空格用例 3[[test1 ... ]]是唯一一个发生局部修改的真链接四行结构[[test1、[[wiki link]]、[[wiki link]] test2、]]中前三行原样保留只有紧贴闭合括号前的换行被替换成了空格输出为[[wiki link]] test2 ]]。可以推断当一段以]]收尾的跨行文本中内嵌了真正的单行Wiki 链接时收尾前多余的换行会被折叠为单个空格其余换行不受影响。3.3 转义反斜杠的组合全部按字面保留用例 4、5、8–15 覆盖了\[[、\]]、\\[[、\\\]、\\\\]]等层层反斜杠的组合。从快照看所有这些转义结构都按原样输出——Prettier 不会增减反斜杠也不会帮你修复转义。这说明 Markdown 解析器将转义后的[[/]]视为普通文本而 Prettier 对这类含[[或]]子串的文本段落采取了原样保真策略原理见 5.2 节。3.4 未闭合的[[/]]会被折叠成单行这是与直觉最不同的部分也是理解保护机制的关键对照组用例 7[[test6 ***/text***text***text/*** text]—— 整段文本里只有[[没有]]输出被折叠为一行[[test6 *** text***text***text *** text]用例 17[test16/text]]—— 只有单个[和]]折叠为[test16 text]]用例 18[[test17/text]—— 有[[但只有单个]折叠为[[test17 text]。对比 3.1 节可以看到规律只有当段落同时出现[[片段与]]片段存在被意外拼成合法链接的可能时换行才会被保护仅出现一半、无论如何折叠都不会形成[[...]]闭合对时段落就按普通文本参与常规折行。3.5 嵌套与行内 HTML/注释用例 1 内部嵌套了[[wiki link]]这样的真链接3.2 节已述用例 19/20 则在跨行链接内部放入 HTML 注释!-- ... --与span元素输出均完整保留多行结构。这组用例验证了任何让跨行片段在折叠后可能形成[[]]闭合对的组合都会触发换行保护。四、proseWrap 三模式对比一个反直觉的结论对比快照中 4 组运行的输出可以发现一个值得写进结论的事实multi-line.md 的全部输出在always、never、preserve三种模式下完全一致singleQuote对 Markdown 输出也无影响。例如折叠发生的用例 7/17/18 在preserve模式下同样被折叠为单行而保护的用例在always模式下也保持多行。原因在于这些多行[[...]]结构在预处理阶段就已经被标记为风险段落并绕过了常规的句子切分与折行逻辑见下节因此proseWrap的全局折行策略对它们根本不生效。这与 end-of-line.md、exceeds-line-length-in-prose.md 等真链接参与常规折行的夹具形成鲜明对比——那里proseWrap: always会把超长行内的[[...]]整体折到下一行而preserve则完全不动。五、源码级原理三层机制如何协同5.1 解析层micromark 的 Wiki 链接扩展Markdown 解析入口 parse-markdown.js 通过micromark生态组装扩展extensions: [ gfmSyntax({ singleTilde: false }), mathSyntax(), wikiLinkSyntax({ // We dont need support alias, use a fake string to bypass aliasDivider: { charCodeAt: () Number.NaN }, }), ... ], mdastExtensions: [gfmFromMarkdown(), mathFromMarkdown(), wikiLinkFromMarkdown(), ...]其中 Wiki 链接语法来自braindb/micromark-extension-wiki-linktokenizer与braindb/mdast-util-wiki-linkAST 转换生成的节点类型为wikiLink。源码注释明确说明了别名alias语法[[目标|别名]]不被支持Prettier 用charCodeAt: () Number.NaN的假 divider 绕过别名解析分支。因此[[a|b]]这类写法不会得到别名语义写作时不应依赖它。5.2 预处理层风险段落的换行保护真正决定多行[[...]]保不保留的是 preprocess.js 中的splitTextIntoSentencesif (node.type wikiLink) { markAncestors(parentStack); // word wrapping can accidentally merge nodes like [[foo\n[[wiki link]] return; } if (node.type ! text) return; if (node.raw.includes([[)) { for (const ancestor of parentStack) { if (ancestor.type paragraph) { canOpenAccidentalWikiLink.add(ancestor); } } } if (node.raw.includes(]])) { markAncestors(parentStack); }逻辑分两条线碰到真正的wikiLink节点或文本里含有]]时markAncestors会把其所在段落标记为风险位置代码注释原话折行可能意外合并节点如[[foo\n[[wiki link]]文本里含有[[时把段落记入canOpenAccidentalWikiLink集合。后续句子切分与折行逻辑会避开这些被标记的段落防止把跨行片段拼成[[...]]闭合对或把已存在的跨行链接打散。这正好解释了第三节的全部现象用例 1/2/5/19/20 同时含[[与]]→ 保护 → 保持多行用例 7/17/18 只有半边括号 → 无论如何折叠都不会凭空产生闭合对 → 不保护 → 正常参与折行被折叠。同时这也解释了为什么该夹具的输出与proseWrap无关——保护发生在折行策略介入之前。5.3 打印层真正的wikiLink节点如何输出对于被解析器确认的单行wikiLink节点打印器 mdast.js 的处理如下case wikiLink: { let contents; if (options.proseWrap preserve) { contents node.value; } else { contents node.value.replaceAll(/[\t\n]/g, ); } return [[[, contents, ]]]; }即proseWrap: preserve时原样保留节点内容其余模式下把节点值里的制表符/换行折叠为单个空格再包回[[与]]。这解释了 exceeds-line-length-in-prose-broken.md 等夹具中跨行但被识别为单个 wikiLink 节点的内容在always/never下被折叠的行为。综合三层实现可以得出一个完整结论Prettier 对跨行 Wiki 链接采用双轨策略——能被解析器确认为wikiLink节点的单行闭合的走打印器的规范化路径无法被确认的跨行[[...]]文本则靠预处理器的风险段落保护原样保留换行只对其中被解析出的行内标记做标准规范化如***def***→_**def**_。六、如何在本仓库验证这些行为仓库使用 Jest 组织格式测试见 jest.config.js安装依赖后可以针对该测试文件运行yarn jest tests/format/markdown/wiki-link/format.test.js该测试会对 multi-line.md 依次套用proseWrap: always、always singleQuote、never、preserve四组参数并与快照snapshots/format.test.js.snap 逐字节比对若输出与预期不符Jest 会输出完整 diff。无需安装依赖时也可以直接阅读快照文件——其中记录了每个用例的完整输入/输出对照是理解格式化行为的零成本途径。七、写作多行 Wiki 链接的工程建议结合本夹具与源码给出四条可直接落地的注意事项跨行链接是安全的但别指望它被打平只要段落同时含[[与]]Prettier 就会保留换行结构因此依赖折行的格式统一如统一为单行不会作用于跨行 Wiki 链接若想让链接紧凑应在源码里就写成单行。转义不要过度依赖自动修复\[[、\]]及多层反斜杠的组合会被原样保留Prettier 不会帮你修正转义层级写作时需自行保证转义正确。避免未闭合方括号留在正文里[[或]]单独出现时该段会被当作普通文本参与常规折行可能被折叠成意外形态同时注意嵌套写法[[foo\n[[wiki link]]正是源码注释中点名的意外合并风险场景。别使用别名语法[[目标|别名]]的别名解析被 Prettier 显式绕过见 parse-markdown.js 注释相关行为记录在 alias/ 的 issue-19525 回归用例中写作时应避免依赖该特性。【免费下载链接】prettierPrettier is an opinionated code formatter.项目地址: https://gitcode.com/gh_mirrors/pr/prettier创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表