From 6ef6d837da5bb0dc05cc811d93ac9caba8300269 Mon Sep 17 00:00:00 2001 From: David Tobolik Date: Mon, 22 Dec 2025 12:20:53 +0100 Subject: [PATCH] feat: move broken wikilinks to CrawlLinks --- quartz/plugins/transformers/links.ts | 34 +++++++++++++++++++++++++++- quartz/plugins/transformers/ofm.ts | 16 +------------ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/quartz/plugins/transformers/links.ts b/quartz/plugins/transformers/links.ts index f4451d927..46958fa4d 100644 --- a/quartz/plugins/transformers/links.ts +++ b/quartz/plugins/transformers/links.ts @@ -17,6 +17,10 @@ import { Root } from "hast" interface Options { /** How to resolve Markdown paths */ markdownLinkResolution: TransformOptions["strategy"] + /** Check for broken wikilinks */ + checkBrokenWikilinks: boolean + /** What to do with broken wikilinks */ + onBrokenWikilink: "disable" | "remove" | "error" /** Strips folders from a link so that it looks nice */ prettyLinks: boolean openLinksInNewTab: boolean @@ -26,6 +30,8 @@ interface Options { const defaultOptions: Options = { markdownLinkResolution: "absolute", + checkBrokenWikilinks: true, + onBrokenWikilink: "disable", prettyLinks: true, openLinksInNewTab: false, lazyLoad: false, @@ -103,6 +109,7 @@ export const CrawlLinks: QuartzTransformerPlugin> = (userOpts) isAbsoluteUrl(dest, { httpOnly: false }) || dest.startsWith("#") ) if (isInternal) { + const preResolve = dest dest = node.properties.href = transformLink( file.data.slug!, dest, @@ -121,7 +128,32 @@ export const CrawlLinks: QuartzTransformerPlugin> = (userOpts) // need to decodeURIComponent here as WHATWG URL percent-encodes everything const full = decodeURIComponent(stripSlashes(destCanonical, true)) as FullSlug const simple = simplifySlug(full) - outgoing.add(simple) + + const allSimpleSlugs = ctx.allSlugs.map((slug) => simplifySlug(slug)) + if ( + opts.checkBrokenWikilinks && + !allSimpleSlugs.includes(simple) && + !preResolve.startsWith(".") && + !preResolve.startsWith("/") + ) { + const action = opts.onBrokenWikilink + if (action === "error") { + throw new Error( + `Broken wikilink found in ${file.data.slug}: ${preResolve} resolves to ${simple}`, + ) + } else if (action === "remove") { + // Remove the link but keep the text + if (_parent && _index !== undefined) { + _parent.children.splice(_index, 1, ...node.children) + } + } else { + // Add "broken" class + classes.push("broken") + node.properties.className = classes + } + } else { + outgoing.add(simple) + } node.properties["data-slug"] = full } diff --git a/quartz/plugins/transformers/ofm.ts b/quartz/plugins/transformers/ofm.ts index 7a523aa59..f36bcb3cc 100644 --- a/quartz/plugins/transformers/ofm.ts +++ b/quartz/plugins/transformers/ofm.ts @@ -41,7 +41,6 @@ export interface Options { enableYouTubeEmbed: boolean enableVideoEmbed: boolean enableCheckbox: boolean - disableBrokenWikilinks: boolean } const defaultOptions: Options = { @@ -57,7 +56,6 @@ const defaultOptions: Options = { enableYouTubeEmbed: true, enableVideoEmbed: true, enableCheckbox: false, - disableBrokenWikilinks: false, } const calloutMapping = { @@ -208,7 +206,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin> return src }, - markdownPlugins(ctx) { + markdownPlugins() { const plugins: PluggableList = [] // regex replacements @@ -277,18 +275,6 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin> // otherwise, fall through to regular link } - // treat as broken link if slug not in ctx.allSlugs - if (opts.disableBrokenWikilinks) { - const slug = slugifyFilePath(fp as FilePath) - const exists = ctx.allSlugs && ctx.allSlugs.includes(slug) - if (!exists) { - return { - type: "html", - value: `${alias ?? fp}`, - } - } - } - // internal link const url = fp + anchor