obsidian的双链构造-vite插件

#代码实现

在之前的个人博客设计中,怎么处理 md 的 frontmatter 的格式,现在想到一个好的办法,使用 vite 插件,通过设置 rootDir 目录,模仿 obsidian 的 metadataCache 来构造双链和按需导入相对路径的图片,原来的插件可以去掉,要明确责任, obsidian-cache 只负责全局视野

需要维护一个双向图

核心数据结构 (Containers)

快速扫描阶段 建议在 JS/TS 中使用以下四个容器来实现 $O(1)$ 级别的快速检索:

  • metadataCache (Map<string, FileMetadata>):
    • Key: 文件的相对全路径 (如 vault/work/project-a.md)。这是全库唯一的标识符。
    • Value: 包含 Frontmatter、标题列表、最后修改时间、Slug 等。 用自己的 util 工具解析。特别是 outlinks,从 [[]] 中提取出来
    • 后续分类,标签,的过滤和总数从这里遍历
    • 先只解析 md 文件,其他文件格式只有 mtime 和 size,hash 也空字符串,
  • filenameToPath (Map<string, string[]>):
    • Key: 文件名(含后缀,如 project-a.md , 123.jpg )。
    • Value: 一个路径数组。支持重名文件消歧义。
    • 后续查询的时候,如果查询的文件名没有后缀,自动添加 .md
  • aliasesToPath (Map<string, string[]>):
    • Key: 别名文字(如 项目X)。
    • Value: 路径数组。一个别名可能指向多个文件(虽然不推荐,但系统需兼容)。
  • slugToPath (Map<string, string>):
    • Key: 最终生成的 URL Slug。
    • Value: 对应的文件路径。用于 SvelteKit 路由根据 URL 找文件。

快速扫描结束,需要管理双链,实现一个邻接表

"A.md": { "B.md": [ { "position": { "line": 5 }, "text": "别名1" } ], "C.md": [ { "position": { "line": 10 }, "text": "别名2" }, { "position": { "line": 45 }, "text": "C" } ] }

image-obsidian的双链构造-vite插件.png

删除 src/lib/obsidian-cache 我重新建一个 vite 插件,目的是在指定目录下,获取目录内的 md 文件的双链关系

插件初始化 ,启动阶段

首先加载之前的缓存 json,也可以为空

扫描所有文件的,包括图片和其他附件,构建缓存条目,方便双链去定位和链接 ,这个可以异步进行,先是把容器目录的相对路径作为 key,系统文件的修改时间 mtime 和 size,计算 hash(md 需要,其他条目的 hash为空字符串),这一步先不解析 md里面的内容

对比之前的缓存 json,对比相同 key 的 mtime 和 size ,如果都相同, 就用之前的解析结果,如果不同,再对比 hash,hash 相同也行。Hash 不同,则需要进行 md 文件解析

解析文件除了 frontmatter 需要用 util 解析,还需要关注 [[]] 的链接

/** Raw link from Phase 1 — link is unresolved basename, type not yet set */
Export interface RawLink {
link: string; // raw basename (e.g. 'My Note' or 'image.Png')
Original: string; // raw markdown text
DisplayText?: string;
Embed: boolean; // true = ! Prefix
}

放在 metadata 中,字段名是 outlinks 数据结构为 RawLink[]

扫描完成,下一步 进行双链构建 用一个单独的类 这个阶段,所有的 md 文件都有 metadata 信息了,包括 slug,oulinks,

提取所有 slug 名,构建一个 slugToPath 的 map,方便构建的时候获取 slug 的映射和总数,这个和双链无关,但是其他地方需要用到

为了构建双链,我需要 filenameToPath 和 aliasesToPath 两个 map。对外暴露出查找方法,让 rawLink 中的 link (raw basename ) 能找到文件 唯一的 key。这个查找方法需要判断查找参数是否包含 .md,不过不包含,需要补全 .md,还要去 aliases 中查询,如果有完整的后缀名,就不去 aliases 中查询

这三个 map 在遍历 metadata 的时候一起生成

这时候,再进行第二次遍历,使用 metadata 的 outlinks 里的 link 去查找是否存在文件, 用 filenameToPath 或者 aliasesToPath,找到了在 metadata 中加入一个新的字段 resolvedLinks,link(raw basename)和 对应 pathKey 加入进去

CC

obsidian的双链构造-vite插件

本文作者
Huxzhi
发布于
更新于
许可协议
CC BY-NC-SA 4.0

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!