diff --git a/src/components/scripts/currentSlug.ts b/src/components/scripts/currentSlug.ts new file mode 100644 index 0000000..7e0d8ba --- /dev/null +++ b/src/components/scripts/currentSlug.ts @@ -0,0 +1,31 @@ +import { getBasePath, getFullSlugFromUrl, simplifySlug } from "@quartz-community/utils"; + +export interface GraphSlugInput { + canonicalSlug?: string; + urlSlug: string; + basePath: string; +} + +export function getCurrentGraphSlug(): string { + const canonicalSlug = document.body?.dataset?.slug; + return resolveGraphSlug({ + canonicalSlug, + urlSlug: canonicalSlug ? "" : getFullSlugFromUrl(), + basePath: canonicalSlug ? "" : getBasePath(), + }); +} + +export function resolveGraphSlug({ canonicalSlug, urlSlug, basePath }: GraphSlugInput): string { + let slug = canonicalSlug || urlSlug; + + if (!canonicalSlug) { + const base = basePath.replace(/^\//, "").replace(/\/$/, ""); + if (base && (slug === base || slug.startsWith(`${base}/`))) { + slug = slug.slice(base.length); + if (slug.startsWith("/")) slug = slug.slice(1); + } + } + + const simplified = simplifySlug(slug); + return simplified === "" ? "index" : simplified; +} diff --git a/src/components/scripts/graph.inline.ts b/src/components/scripts/graph.inline.ts index 3b2773e..f221bc0 100644 --- a/src/components/scripts/graph.inline.ts +++ b/src/components/scripts/graph.inline.ts @@ -1,21 +1,10 @@ // @ts-nocheck -import { - removeAllChildren, - getBasePath, - getFullSlugFromUrl, - simplifySlug, - resolveBasePath, -} from "@quartz-community/utils"; +import { removeAllChildren, simplifySlug, resolveBasePath } from "@quartz-community/utils"; +import { getCurrentGraphSlug } from "./currentSlug"; (function () { function getSlugFromUrl() { - var slug = getFullSlugFromUrl(); - var base = getBasePath(); - if (base && slug.startsWith(base.replace(/^\//, ""))) { - slug = slug.slice(base.replace(/^\//, "").length); - if (slug.startsWith("/")) slug = slug.slice(1); - } - return slug; + return getCurrentGraphSlug(); } function loadScript(src) { diff --git a/test/currentSlug.test.ts b/test/currentSlug.test.ts new file mode 100644 index 0000000..feb8347 --- /dev/null +++ b/test/currentSlug.test.ts @@ -0,0 +1,87 @@ +import { describe, expect, it } from "vitest"; +import { getCurrentGraphSlug, resolveGraphSlug } from "../src/components/scripts/currentSlug"; + +describe("resolveGraphSlug", () => { + it("canonicalizes a Quartz folder-page body slug to the graph index key", () => { + expect( + resolveGraphSlug({ + canonicalSlug: "projects/example/index", + urlSlug: "projects/example", + basePath: "", + }), + ).toBe("projects/example/"); + }); + + it("preserves a trailing-slash folder URL when body metadata is unavailable", () => { + expect( + resolveGraphSlug({ + canonicalSlug: undefined, + urlSlug: "projects/example/", + basePath: "", + }), + ).toBe("projects/example/"); + }); + + it("preserves an ordinary page slug from body metadata", () => { + expect( + resolveGraphSlug({ + canonicalSlug: "hermes/overview", + urlSlug: "hermes/overview", + basePath: "", + }), + ).toBe("hermes/overview"); + }); + + it("preserves an ordinary page slug through the URL fallback", () => { + expect( + resolveGraphSlug({ + canonicalSlug: undefined, + urlSlug: "hermes/overview", + basePath: "", + }), + ).toBe("hermes/overview"); + }); + + it("strips the configured base path from the URL fallback", () => { + expect( + resolveGraphSlug({ + canonicalSlug: undefined, + urlSlug: "archive/projects/example/", + basePath: "/archive", + }), + ).toBe("projects/example/"); + }); + + it("does not strip a similar URL path segment", () => { + expect( + resolveGraphSlug({ + canonicalSlug: undefined, + urlSlug: "archives/projects/example/", + basePath: "/archive", + }), + ).toBe("archives/projects/example/"); + }); + + it("uses Quartz's browser URL and base-path utilities for the fallback", () => { + const previousWindow = Object.getOwnPropertyDescriptor(globalThis, "window"); + const previousDocument = Object.getOwnPropertyDescriptor(globalThis, "document"); + + Object.defineProperty(globalThis, "window", { + configurable: true, + value: { location: { pathname: "/archive/hermes/overview/" } }, + }); + Object.defineProperty(globalThis, "document", { + configurable: true, + value: { body: { dataset: { basepath: "/archive" } } }, + }); + + try { + expect(getCurrentGraphSlug()).toBe("hermes/overview"); + } finally { + if (previousWindow) Object.defineProperty(globalThis, "window", previousWindow); + else Reflect.deleteProperty(globalThis, "window"); + if (previousDocument) Object.defineProperty(globalThis, "document", previousDocument); + else Reflect.deleteProperty(globalThis, "document"); + } + }); +});