┌─ TS ───────────────────────────────────────────────────────────────────────┐
│ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; │
│ import { join } from "node:path"; │
│ import { tmpdir } from "node:os"; │
│ import { afterAll, beforeAll, describe, expect, it } from "vitest"; │
│ import { buildBacklinks, buildWikiMap, extractWikiTargets, resolveWikilink } from │
│ "./wikilinks"; │
│ import { wikiNodes } from "./remark-wikilinks"; │
│ │
│ // fixture content dir - the graph tests must not depend on what the │
│ // real collections currently hold │
│ let dir: string; │
│ const write = (rel: string, fm: string, body = "") => { │
│ const p = join(dir, rel); │
│ mkdirSync(join(p, ".."), { recursive: true }); │
│ writeFileSync(p, `---\n${fm}\n---\n\n${body}`); │
│ }; │
│ │
│ beforeAll(() => { │
│ dir = mkdtempSync(join(tmpdir(), "wiki-")); │
│ write("kb/nix/flakes.md", 'title: "FLAKES FIELD NOTES"', "cites [[windows/tss]] │
│ and [[Docs Page]]."); │
│ write("kb/nix/draft.md", 'title: "DRAFT"\ndraft: true', "cites [[flakes field no │
│ tes]]."); │
│ write("kb/windows/tss.md", 'title: "TSS TOOLSET"\nsource: "MS"', "literal `[[fla │
│ kes field notes]]` only."); │
│ writeFileSync(join(dir, "kb/windows/dir.yml"), "register: reference\n"); │
│ write("kb/site/page.md", 'title: "Docs Page"', "self [[Docs Page]], live [[FLAKE │
│ S FIELD NOTES]], dead [[nope]]."); │
│ writeFileSync(join(dir, "kb/site/dir.yml"), "register: docs\n"); │
│ }); │
│ afterAll(() => rmSync(dir, { recursive: true, force: true })); │
│ │
│ describe("buildWikiMap", () => { │
│ it("indexes every collection by id and title", () => { │
│ const map = buildWikiMap(dir); │
│ expect(resolveWikilink("nix/flakes", map)).toBe("/kb/nix/flakes/"); │
│ expect(resolveWikilink("Flakes Field Notes", map)).toBe("/kb/nix/flakes/"); │
│ expect(resolveWikilink("windows/tss", map)).toBe("/kb/windows/tss/"); │
│ expect(resolveWikilink("Docs Page", map)).toBe("/kb/site/page/"); │
│ }); │
│ │
│ it("is case-insensitive and trims", () => { │
│ const map = buildWikiMap(dir); │
│ expect(resolveWikilink(" TSS TOOLSET ", map)).toBe("/kb/windows/tss/"); │
│ }); │
│ │
│ it("excludes drafts", () => { │
│ expect(resolveWikilink("nix/draft", buildWikiMap(dir))).toBeNull(); │
│ }); │
│ │
│ it("returns null for unknown targets", () => { │
│ expect(resolveWikilink("A NOTE THAT DOES NOT EXIST", buildWikiMap(dir))).toBeN │
│ ull(); │
│ }); │
│ }); │
│ │
│ describe("wikiNodes", () => { │
│ const map = new Map([["docs page", "/kb/site/page/"]]); │
│ │
│ it("leaves plain text untouched", () => { │
│ expect(wikiNodes("no links here", map)).toBeNull(); │
│ }); │
│ │
│ it("splits text around a resolved link", () => { │
│ const nodes = wikiNodes("see [[Docs Page]] for glyphs", map)!; │
│ expect(nodes.map((n) => n.type)).toEqual(["text", "link", "text"]); │
│ expect(nodes[1].url).toBe("/kb/site/page/"); │
│ }); │
│ │
│ it("uses the alias as the label", () => { │
│ const nodes = wikiNodes("[[Docs Page|the page]]", map)!; │
│ expect((nodes[0].children as { value: string }[])[0].value).toBe("the page"); │
│ }); │
│ │
│ it("renders unresolved targets as dead-link spans", () => { │
│ const nodes = wikiNodes("[[missing]]", map)!; │
│ expect(nodes[0].type).toBe("html"); │
│ expect(nodes[0].value).toContain("wikilink-dead"); │
│ }); │
│ }); │
│ │
│ describe("extractWikiTargets", () => { │
│ it("collects targets, dropping aliases", () => { │
│ expect(extractWikiTargets("see [[a/b]] and [[X|the x]]")).toEqual(["a/b", "X"] │
│ ); │
│ }); │
│ │
│ it("ignores wikilinks inside inline code and fences", () => { │
│ const body = "live [[a]], literal `[[b]]`\n```\n[[c]]\n```\ndone"; │
│ expect(extractWikiTargets(body)).toEqual(["a"]); │
│ }); │
│ }); │
│ │
│ describe("buildBacklinks", () => { │
│ it("inverts the graph across the kb", () => { │
│ const bl = buildBacklinks(dir); │
│ const urls = (t: string) => (bl.get(t) ?? []).map((r) => r.url); │
│ expect(urls("/kb/windows/tss/")).toEqual(["/kb/nix/flakes/"]); │
│ expect(urls("/kb/nix/flakes/")).toEqual(["/kb/site/page/"]); │
│ }); │
│ │
│ it("labels sources with their register", () => { │
│ const src = buildBacklinks(dir).get("/kb/windows/tss/")![0]; │
│ expect(src.label).toBe("NOTE - FLAKES FIELD NOTES"); │
│ }); │
│ │
│ it("drops self-references, drafts, and code-span citations", () => { │
│ const bl = buildBacklinks(dir); │
│ for (const [target, list] of bl) { │
│ expect(list.map((r) => r.url)).not.toContain(target); │
│ expect(list.map((r) => r.url)).not.toContain("/kb/nix/draft/"); │
│ } │
│ // the reference body only cites in a code span - it must not be a source │
│ const flakes = bl.get("/kb/nix/flakes/") ?? []; │
│ expect(flakes.map((r) => r.url)).not.toContain("/kb/windows/tss/"); │
│ }); │
│ }); │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ TS ─────────────────────────────────┐
│ import { mkdtempSync, mkdirSync, rmSync, wri │
│ teFileSync } from "node:fs"; │
│ import { join } from "node:path"; │
│ import { tmpdir } from "node:os"; │
│ import { afterAll, beforeAll, describe, expe │
│ ct, it } from "vitest"; │
│ import { buildBacklinks, buildWikiMap, extra │
│ ctWikiTargets, resolveWikilink } from "./wik │
│ ilinks"; │
│ import { wikiNodes } from "./remark-wikilink │
│ s"; │
│ │
│ // fixture content dir - the graph tests mus │
│ t not depend on what the │
│ // real collections currently hold │
│ let dir: string; │
│ const write = (rel: string, fm: string, body │
│ = "") => { │
│ const p = join(dir, rel); │
│ mkdirSync(join(p, ".."), { recursive: true │
│ }); │
│ writeFileSync(p, `---\n${fm}\n---\n\n${bod │
│ y}`); │
│ }; │
│ │
│ beforeAll(() => { │
│ dir = mkdtempSync(join(tmpdir(), "wiki-")) │
│ ; │
│ write("kb/nix/flakes.md", 'title: "FLAKES │
│ FIELD NOTES"', "cites [[windows/tss]] and [[ │
│ Docs Page]]."); │
│ write("kb/nix/draft.md", 'title: "DRAFT"\n │
│ draft: true', "cites [[flakes field notes]]. │
│ "); │
│ write("kb/windows/tss.md", 'title: "TSS TO │
│ OLSET"\nsource: "MS"', "literal `[[flakes fi │
│ eld notes]]` only."); │
│ writeFileSync(join(dir, "kb/windows/dir.ym │
│ l"), "register: reference\n"); │
│ write("kb/site/page.md", 'title: "Docs Pag │
│ e"', "self [[Docs Page]], live [[FLAKES FIEL │
│ D NOTES]], dead [[nope]]."); │
│ writeFileSync(join(dir, "kb/site/dir.yml") │
│ , "register: docs\n"); │
│ }); │
│ afterAll(() => rmSync(dir, { recursive: true │
│ , force: true })); │
│ │
│ describe("buildWikiMap", () => { │
│ it("indexes every collection by id and tit │
│ le", () => { │
│ const map = buildWikiMap(dir); │
│ expect(resolveWikilink("nix/flakes", map │
│ )).toBe("/kb/nix/flakes/"); │
│ expect(resolveWikilink("Flakes Field Not │
│ es", map)).toBe("/kb/nix/flakes/"); │
│ expect(resolveWikilink("windows/tss", ma │
│ p)).toBe("/kb/windows/tss/"); │
│ expect(resolveWikilink("Docs Page", map) │
│ ).toBe("/kb/site/page/"); │
│ }); │
│ │
│ it("is case-insensitive and trims", () => │
│ { │
│ const map = buildWikiMap(dir); │
│ expect(resolveWikilink(" TSS TOOLSET " │
│ , map)).toBe("/kb/windows/tss/"); │
│ }); │
│ │
│ it("excludes drafts", () => { │
│ expect(resolveWikilink("nix/draft", buil │
│ dWikiMap(dir))).toBeNull(); │
│ }); │
│ │
│ it("returns null for unknown targets", () │
│ => { │
│ expect(resolveWikilink("A NOTE THAT DOES │
│ NOT EXIST", buildWikiMap(dir))).toBeNull(); │
│ }); │
│ }); │
│ │
│ describe("wikiNodes", () => { │
│ const map = new Map([["docs page", "/kb/si │
│ te/page/"]]); │
│ │
│ it("leaves plain text untouched", () => { │
│ expect(wikiNodes("no links here", map)). │
│ toBeNull(); │
│ }); │
│ │
│ it("splits text around a resolved link", ( │
│ ) => { │
│ const nodes = wikiNodes("see [[Docs Page │
│ ]] for glyphs", map)!; │
│ expect(nodes.map((n) => n.type)).toEqual │
│ (["text", "link", "text"]); │
│ expect(nodes[1].url).toBe("/kb/site/page │
│ /"); │
│ }); │
│ │
│ it("uses the alias as the label", () => { │
│ const nodes = wikiNodes("[[Docs Page|the │
│ page]]", map)!; │
│ expect((nodes[0].children as { value: st │
│ ring }[])[0].value).toBe("the page"); │
│ }); │
│ │
│ it("renders unresolved targets as dead-lin │
│ k spans", () => { │
│ const nodes = wikiNodes("[[missing]]", m │
│ ap)!; │
│ expect(nodes[0].type).toBe("html"); │
│ expect(nodes[0].value).toContain("wikili │
│ nk-dead"); │
│ }); │
│ }); │
│ │
│ describe("extractWikiTargets", () => { │
│ it("collects targets, dropping aliases", ( │
│ ) => { │
│ expect(extractWikiTargets("see [[a/b]] a │
│ nd [[X|the x]]")).toEqual(["a/b", "X"]); │
│ }); │
│ │
│ it("ignores wikilinks inside inline code a │
│ nd fences", () => { │
│ const body = "live [[a]], literal `[[b]] │
│ `\n```\n[[c]]\n```\ndone"; │
│ expect(extractWikiTargets(body)).toEqual │
│ (["a"]); │
│ }); │
│ }); │
│ │
│ describe("buildBacklinks", () => { │
│ it("inverts the graph across the kb", () = │
│ > { │
│ const bl = buildBacklinks(dir); │
│ const urls = (t: string) => (bl.get(t) ? │
│ ? []).map((r) => r.url); │
│ expect(urls("/kb/windows/tss/")).toEqual │
│ (["/kb/nix/flakes/"]); │
│ expect(urls("/kb/nix/flakes/")).toEqual( │
│ ["/kb/site/page/"]); │
│ }); │
│ │
│ it("labels sources with their register", ( │
│ ) => { │
│ const src = buildBacklinks(dir).get("/kb │
│ /windows/tss/")![0]; │
│ expect(src.label).toBe("NOTE - FLAKES FI │
│ ELD NOTES"); │
│ }); │
│ │
│ it("drops self-references, drafts, and cod │
│ e-span citations", () => { │
│ const bl = buildBacklinks(dir); │
│ for (const [target, list] of bl) { │
│ expect(list.map((r) => r.url)).not.toC │
│ ontain(target); │
│ expect(list.map((r) => r.url)).not.toC │
│ ontain("/kb/nix/draft/"); │
│ } │
│ // the reference body only cites in a co │
│ de span - it must not be a source │
│ const flakes = bl.get("/kb/nix/flakes/") │
│ ?? []; │
│ expect(flakes.map((r) => r.url)).not.toC │
│ ontain("/kb/windows/tss/"); │
│ }); │
│ }); │
└──────────────────────────────────────────────┘