十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Tolaria 类型文档创建策略:ADR-0096「根目录创建类型文档」及其源码实现剖析

Tolaria 类型文档创建策略:ADR-0096「根目录创建类型文档」及其源码实现剖析 Tolaria 类型文档创建策略ADR-0096「根目录创建类型文档」及其源码实现剖析【免费下载链接】tolariaDesktop app to manage markdown knowledge bases项目地址: https://gitcode.com/GitHub_Trending/to/tolariaTolaria 用 Markdown frontmattertype: Type而非文件位置来定义类型但早期的 UI 创建流程仍默认把新类型文档写进type/目录。ADR-0096 决定了新的创建策略UI 创建的类型文档一律落在 vault 根目录{vault}/{slug}.md已有类型文档保持原位有效。读完本文你会理解这一决策如何与 Tolaria 的元数据优先模型对齐、创建流程在源码中如何以三态计划保证碰撞安全以及旧 vault 的type/、types/目录为什么无需迁移即可继续工作。背景类型身份来自 frontmatter而不是文件夹Tolaria 的类型系统有一条根本原则类型定义type document就是 frontmatter 中带type: Type的普通 Markdown 笔记与它存放在哪个文件夹无关。官方概念文档 Types 明确写道Tolaria does not infer type from folder location. Moving a file into another folder does not change its type.Tolaria 不会根据文件夹位置推断类型移动文件不会改变其类型。一个标准的类型文档长这样节选自 site/concepts/types.md--- type: Type _icon: folder _color: blue _sidebar_label: Projects _order: 10 --- # Project其中type: Type是这是一份类型定义的标记_icon、_color、_sidebar_label、_order这些下划线前缀的系统属性控制侧边栏分组、图标、颜色、排序。类型文档还可以携带template字段或直接在# TypeName标题之后写模板结构为新建该类型笔记时提供初始内容。这条元数据优先原则并非孤立存在。配套的 ADR-0025type 字段规范化 确立了type:作为规范化字段仍兼容Is A等旧别名ADR-0006扁平 vault 结构 确立了大多数笔记平铺在 vault 根目录的整体布局ADR-0033子文件夹扫描 则让扫描器覆盖所有非隐藏子目录。ADR-0096 是在这套地基上对创建动作本身做的收口。决策前的矛盾type/目录的路径特殊性与 vault 模型的冲突ADR-0096 的 Context 一节记录了决策动因Tolaria 依据 frontmattertype: Type识别类型定义不依据文件系统位置但旧文档和 UI 创建流程仍把type/当作新类型文档的规范落点并对已经使用types/复数目录的 vault 做了兼容回退这种基于文件夹的创建策略与整体 vault 模型冲突笔记从所有非隐藏文件夹扫描而来类型身份来自元数据而且根目录的type.md/note.md定义早已被修复repair和引导bootstrap流程使用。仓库中仍能看到旧约定的痕迹。例如内置的 getting started 引导文本src-tauri/src/vault/getting_started.rs里还写着 Type definitions live intype/demo vault 也把类型文档放在type/目录下如 demo-vault-v2/type/note.md、demo-vault-v2/type/project.md。ADR-0096 正是针对这类路径被特殊化的历史包袱做出的修正。决策新建类型文档一律落在 vault 根目录ADR-0096 的核心决策可以概括为五条类型文档 任意 frontmatter 含type: Type的 Markdown 笔记UI 新创建的类型文档使用{vault}/{slug}.md即 vault 根目录下以类型名 slug 化命名已经存在于type/、types/或其他被扫描文件夹中的类型文档保持有效继续驱动模板、图标、颜色、可见性、排序和侧边栏分组创建动作不静默迁移或移动任何已有类型文档根目录文件名冲突按文件冲突处理——创建类型文档时绝不允许覆盖已有笔记。源码印证resolveNewType如何决定落点创建逻辑的前端实现在 src/hooks/useNoteCreation.ts。关键函数resolveNewTypesrc/hooks/useNoteCreation.ts#L377-L398展示了落点决策的全部细节export function resolveNewType({ typeName, vaultPath, defaultWorkspacePath, vaults [] }: NewTypeParams): { entry: VaultEntry content: string } { const normalizedTypeName normalizeTypeCreationName(typeName) const creationVaultPath resolveCreationVaultPath(vaultPath, defaultWorkspacePath, vaults) const slug slugify(normalizedTypeName) const entry { ...buildNewEntry({ path: joinVaultPath(creationVaultPath, ${slug}.md), // 注意没有 type/ 前缀 slug, title: normalizedTypeName, type: Type, status: null, }), workspace: workspaceForVaultPath(creationVaultPath, vaults, defaultWorkspacePath), } return { entry, content: ---\ntype: Type\n---\n\n# ${normalizedTypeName}\n, } }两个细节值得注意路径拼接直接是joinVaultPath(creationVaultPath,${slug}.md)与笔记创建resolveNewNote使用同一种根目录命名规则——这正是 ADR 所说的移除创建路径的特殊性removes special casing from creation。类型创建与笔记创建共享同一套路径约定不再分叉。生成的内容是最小骨架frontmatter 只有一行type: Type正文只有一个# 类型名标题。图标、颜色、模板等都留给用户后续编辑符合类型文档就是普通笔记的定位。类型名还会先经过normalizeTypeCreationName规范化src/hooks/useNoteCreation.ts#L370-L375内置别名表TYPE_CREATION_ALIASES把notes映射到Note再走canonicalizeTypeName做大小写规范化。这解释了后文测试中对内置 Note 类型创建会被视为已存在的场景。创建流程的三态计划existing / blocked / create真正保证创建安全的是planNewTypeCreationsrc/hooks/useNoteCreation.ts#L502-L531。它把一次创建请求归约为三种互斥状态export function planNewTypeCreation({ defaultWorkspacePath, entries, typeName, vaultPath, vaults, }: NewTypeParams { entries: VaultEntry[] }): TypeCreationPlan { const existingType findEquivalentTypeEntry(entries, typeName) if (existingType) return { status: existing, entry: existingType } const resolved resolveNewType({ typeName, vaultPath, defaultWorkspacePath, vaults }) const collision findPathCollision(entries, resolved.entry.path) if (collision) { return { status: blocked, message: buildCreationCollisionMessage({ noun: type, title: typeName, path: resolved.entry.path }), } } return { status: create, resolved } }状态判定条件用户可见行为existing按标题/slug 匹配到已有类型条目findEquivalentTypeEntryL468-L474对entry.isA Type且标题或 slug 相同的条目做等价匹配ToastType X already exists不写入任何文件blockedvault 中不存在同名类型但根目录已存在同 slug 的任意笔记findPathCollisionToastCannot create type X because x.md already exists写入中止create无等价类型、无路径冲突持久化{vault}/{slug}.md并上报type_created事件冲突消息由buildCreationCollisionMessage统一生成src/hooks/useNoteCreation.ts#L462-L466const filename notePathFilename(path) return Cannot create ${noun} ${title} because ${filename} already exists这正是 ADR 中root filename collisions are handled as file collisions根目录文件名冲突按文件冲突处理的直接落地。执行层createTypeFromNamesrc/hooks/useNoteCreation.ts#L698-L737在持久化前还会追加一次findTypeTargetCollision复查兜住计划之后才出现的文件系统状态变化而isAlreadyExistsErrorL533-L536把already exists / file exists / eexist之类的持久化异常统一翻译成同样的冲突话术确保绝不覆盖的语义在 UI 层一致呈现。此外还有一个静默变体createTypeSilentlyL739-L775供 wikilink 缺失类型补建等流程复用同一套计划逻辑冲突时抛出异常而不是覆盖。碰撞安全的端到端验证Playwright 冒烟测试上述语义不是孤立的单元测试假设而是被桌面端冒烟测试逐条验证的。tests/smoke/collision-create-flows.spec.ts 中第一个用例missing-type creation keeps the dialog open when a root filename already exists在临时 vault 根目录预置hotel.md一个type: Note的普通笔记再建一份hotel-guide.mdtype: Hotel通过缺失类型提示触发创建 Hotel 类型的对话框断言 Toast 显示Cannot create type Hotel because hotel.md already exists关键断言回读hotel.md确认内容仍是# Existing Hotel Note——原有笔记没有被触碰对话框也没有关闭。另一个用例则验证existing分支当根目录已存在note.md内置 Note 类型文档时通过命令面板创建 Note 类型预期 Toast 为Type Note already exists且文件内容保持原样。这两组断言与 ADR 决策中collision message instead of writing into a fallback folder用冲突消息取代写入回退文件夹完全对应。辅助的单测可继续在 src/hooks/useNoteCreation.helpers.test.ts 与 src/hooks/useNoteCreation.extra.test.ts 中查看。兼容性旧type/、types/目录为什么免迁移ADR-0096 的 Consequences 一节承诺已有 vault 中的type/或types/类型文档保持可读因为vault 扫描本来就包含非隐藏子目录。这一点可以从两条证据链确认扫描模型ADR-0033 确立的子文件夹扫描 文件夹树使任何非隐藏目录下的.md都会进入索引isA: Type的判定只依赖 frontmatter实际样例仓库自带演示库 demo-vault-v2/type/ 下存放着note.md、project.md、area.md等类型文档它们全部以type: Typefrontmatter 自声明位置在type/子目录而非根目录却同样是有效的类型定义。因此 ADR 对旧 vault 采取的是只改新增行为、不动存量数据的策略用户默认可以把类型文档当作普通根笔记检查与编辑如果根目录已有同 slug 笔记类型创建直接失败并给出冲突消息而不是偷偷写进某个回退文件夹ADR 还留了一个实现余量legacytype/目录可以从文件夹树中隐藏避免旧类型文档与侧边栏 Types 分组重复展示若未来用户需要从文件夹型类型文档到根目录型类型文档的引导式迁移需要重新评估。被否决的备选方案ADR 同时记录了两条未选路线及其代价对理解当前设计边界很有帮助规范化的type/目录好处是天然避免根目录文件名冲突但代价是把类型身份这件事人为地与路径绑定——既然身份已由 frontmatter 定义路径特殊化就是模型不一致动态沿用每个 vault 的现有文件夹约定对复数types/的 vault 改动最小但会造成不同 vault 行为不一致且让types/只处于部分支持状态。选中的根目录方案则一次性消除了创建路径的特殊分支并与根目录管理的默认类型脚手架如 bootstrap/repair 流程使用的根note.md/type.md对齐。小结ADR-0096 用一个看似微小的落点变更把创建类型文档纳入了 Tolaria 的元数据优先模型类型身份看 frontmatter创建落点看 vault 根目录兼容性看既有扫描能力安全性看三态计划与碰撞消息。对使用者而言实际效果是——通过 创建类型指南 或命令面板新建一个类型后你会在 vault 根目录得到一个形如---\ntype: Type\n---\n\n# 类型名\n的.md文件若根目录已有同名笔记你会看到明确的冲突提示而不是被静默覆盖。相关的源码入口planNewTypeCreation/resolveNewType均在 src/hooks/useNoteCreation.ts与端到端测试tests/smoke/collision-create-flows.spec.ts构成了这条策略可验证的完整证据链。【免费下载链接】tolariaDesktop app to manage markdown knowledge bases项目地址: https://gitcode.com/GitHub_Trending/to/tolaria创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表