feat: move broken wikilinks to CrawlLinks

This commit is contained in:
David Tobolik 2025-12-22 12:20:53 +01:00
parent 9c042dd717
commit 6ef6d837da
No known key found for this signature in database
2 changed files with 34 additions and 16 deletions

View File

@ -17,6 +17,10 @@ import { Root } from "hast"
interface Options { interface Options {
/** How to resolve Markdown paths */ /** How to resolve Markdown paths */
markdownLinkResolution: TransformOptions["strategy"] 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 */ /** Strips folders from a link so that it looks nice */
prettyLinks: boolean prettyLinks: boolean
openLinksInNewTab: boolean openLinksInNewTab: boolean
@ -26,6 +30,8 @@ interface Options {
const defaultOptions: Options = { const defaultOptions: Options = {
markdownLinkResolution: "absolute", markdownLinkResolution: "absolute",
checkBrokenWikilinks: true,
onBrokenWikilink: "disable",
prettyLinks: true, prettyLinks: true,
openLinksInNewTab: false, openLinksInNewTab: false,
lazyLoad: false, lazyLoad: false,
@ -103,6 +109,7 @@ export const CrawlLinks: QuartzTransformerPlugin<Partial<Options>> = (userOpts)
isAbsoluteUrl(dest, { httpOnly: false }) || dest.startsWith("#") isAbsoluteUrl(dest, { httpOnly: false }) || dest.startsWith("#")
) )
if (isInternal) { if (isInternal) {
const preResolve = dest
dest = node.properties.href = transformLink( dest = node.properties.href = transformLink(
file.data.slug!, file.data.slug!,
dest, dest,
@ -121,7 +128,32 @@ export const CrawlLinks: QuartzTransformerPlugin<Partial<Options>> = (userOpts)
// need to decodeURIComponent here as WHATWG URL percent-encodes everything // need to decodeURIComponent here as WHATWG URL percent-encodes everything
const full = decodeURIComponent(stripSlashes(destCanonical, true)) as FullSlug const full = decodeURIComponent(stripSlashes(destCanonical, true)) as FullSlug
const simple = simplifySlug(full) 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 node.properties["data-slug"] = full
} }

View File

@ -41,7 +41,6 @@ export interface Options {
enableYouTubeEmbed: boolean enableYouTubeEmbed: boolean
enableVideoEmbed: boolean enableVideoEmbed: boolean
enableCheckbox: boolean enableCheckbox: boolean
disableBrokenWikilinks: boolean
} }
const defaultOptions: Options = { const defaultOptions: Options = {
@ -57,7 +56,6 @@ const defaultOptions: Options = {
enableYouTubeEmbed: true, enableYouTubeEmbed: true,
enableVideoEmbed: true, enableVideoEmbed: true,
enableCheckbox: false, enableCheckbox: false,
disableBrokenWikilinks: false,
} }
const calloutMapping = { const calloutMapping = {
@ -208,7 +206,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>>
return src return src
}, },
markdownPlugins(ctx) { markdownPlugins() {
const plugins: PluggableList = [] const plugins: PluggableList = []
// regex replacements // regex replacements
@ -277,18 +275,6 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>>
// otherwise, fall through to regular link // 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: `<a class=\"internal broken\">${alias ?? fp}</a>`,
}
}
}
// internal link // internal link
const url = fp + anchor const url = fp + anchor