Skip to content

Commit

Permalink
fix(aliases): Fix wikilink resolution for aliases
Browse files Browse the repository at this point in the history
With markdownLinkResolution: "shortest", aka "+/- how Obsidian does it"
and given pages A and nested/B which has an alias Z, if you try to link
from A using [[Z]] it wouldn't work and get 404.

This is caused by alias slugs (nested/Z in this case, emitted by
AliasRedirects) not being present in the `allSlugs` list which is used
by the link transformer.

The fix is to compute the alias slugs in the frontmatter transformer
and add them to `allSlugs` there.
Also we store them in file data to avoid recomputing them when emitting
alias redirect pages.

Fixes #904

Note: given how currently the markdown/html transformers are ordered
this doesn't really work.

Given pages A and nested/B which has an alias Z, here's the order which
currently happens:

md-transformers(A) => html-transformers(A) =>
md-transformers(B) => html-transformers(B)

Since the nested/Z slug will get added when md-transformers(B) are run,
but the slugs are used by html-transformers(A) when resolving it's
links - the link [[Z]] in A will still 404

A fix for this is to split the parser into two stages - first apply the
md-transformers to all files, and only then apply html-transformers to
all files.

I did just that in a different commit, which is needed for this one to
work correctly.
  • Loading branch information
necauqua committed Jan 3, 2025
1 parent 2e6a675 commit 5122560
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 34 deletions.
32 changes: 3 additions & 29 deletions quartz/plugins/emitters/aliases.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { FilePath, FullSlug, joinSegments, resolveRelative, simplifySlug } from "../../util/path"
import { FilePath, joinSegments, resolveRelative, simplifySlug } from "../../util/path"
import { QuartzEmitterPlugin } from "../types"
import path from "path"
import { write } from "./helpers"
import DepGraph from "../../depgraph"

Expand All @@ -14,20 +13,7 @@ export const AliasRedirects: QuartzEmitterPlugin = () => ({

const { argv } = ctx
for (const [_tree, file] of content) {
const dir = path.posix.relative(argv.directory, path.dirname(file.data.filePath!))
const aliases = file.data.frontmatter?.aliases ?? []
const slugs = aliases.map((alias) => path.posix.join(dir, alias) as FullSlug)
const permalink = file.data.frontmatter?.permalink
if (typeof permalink === "string") {
slugs.push(permalink as FullSlug)
}

for (let slug of slugs) {
// fix any slugs that have trailing slash
if (slug.endsWith("/")) {
slug = joinSegments(slug, "index") as FullSlug
}

for (const slug of file.data.aliases ?? []) {
graph.addEdge(file.data.filePath!, joinSegments(argv.output, slug + ".html") as FilePath)
}
}
Expand All @@ -40,20 +26,8 @@ export const AliasRedirects: QuartzEmitterPlugin = () => ({

for (const [_tree, file] of content) {
const ogSlug = simplifySlug(file.data.slug!)
const dir = path.posix.relative(argv.directory, path.dirname(file.data.filePath!))
const aliases = file.data.frontmatter?.aliases ?? []
const slugs: FullSlug[] = aliases.map((alias) => path.posix.join(dir, alias) as FullSlug)
const permalink = file.data.frontmatter?.permalink
if (typeof permalink === "string") {
slugs.push(permalink as FullSlug)
}

for (let slug of slugs) {
// fix any slugs that have trailing slash
if (slug.endsWith("/")) {
slug = joinSegments(slug, "index") as FullSlug
}

for (const slug of file.data.aliases ?? []) {
const redirUrl = resolveRelative(slug, file.data.slug!)
const fp = await write({
ctx,
Expand Down
31 changes: 27 additions & 4 deletions quartz/plugins/transformers/frontmatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import remarkFrontmatter from "remark-frontmatter"
import { QuartzTransformerPlugin } from "../types"
import yaml from "js-yaml"
import toml from "toml"
import { slugTag } from "../../util/path"
import { FullSlug, joinSegments, sluggify, slugTag } from "../../util/path"
import { QuartzPluginData } from "../vfile"
import { i18n } from "../../i18n"
import { BuildCtx } from "../../util/ctx"
import { VFile } from "vfile"
import path from "path"

export interface Options {
delimiters: string | [string, string]
Expand Down Expand Up @@ -40,11 +43,26 @@ function coerceToArray(input: string | string[]): string[] | undefined {
.map((tag: string | number) => tag.toString())
}

function getAliasSlugs(aliases: string[], { argv }: BuildCtx, file: VFile): FullSlug[] {
const dir = path.posix.relative(argv.directory, path.dirname(file.data.filePath!))
const slugs: FullSlug[] = aliases.map(
(alias) => path.posix.join(dir, sluggify(alias)) as FullSlug,
)
const permalink = file.data.frontmatter?.permalink
if (typeof permalink === "string") {
slugs.push(permalink as FullSlug)
}
// fix any slugs that have trailing slash
return slugs.map((slug) =>
slug.endsWith("/") ? (joinSegments(slug, "index") as FullSlug) : slug,
)
}

export const FrontMatter: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
const opts = { ...defaultOptions, ...userOpts }
return {
name: "FrontMatter",
markdownPlugins({ cfg }) {
markdownPlugins(ctx) {
return [
[remarkFrontmatter, ["yaml", "toml"]],
() => {
Expand All @@ -60,14 +78,18 @@ export const FrontMatter: QuartzTransformerPlugin<Partial<Options>> = (userOpts)
if (data.title != null && data.title.toString() !== "") {
data.title = data.title.toString()
} else {
data.title = file.stem ?? i18n(cfg.configuration.locale).propertyDefaults.title
data.title = file.stem ?? i18n(ctx.cfg.configuration.locale).propertyDefaults.title
}

const tags = coerceToArray(coalesceAliases(data, ["tags", "tag"]))
if (tags) data.tags = [...new Set(tags.map((tag: string) => slugTag(tag)))]

const aliases = coerceToArray(coalesceAliases(data, ["aliases", "alias"]))
if (aliases) data.aliases = aliases
if (aliases) {
data.aliases = aliases
const slugs = getAliasSlugs(aliases, ctx, file)
ctx.allSlugs.push(...(file.data.aliases = slugs))
}
const cssclasses = coerceToArray(coalesceAliases(data, ["cssclasses", "cssclass"]))
if (cssclasses) data.cssclasses = cssclasses

Expand Down Expand Up @@ -98,6 +120,7 @@ export const FrontMatter: QuartzTransformerPlugin<Partial<Options>> = (userOpts)

declare module "vfile" {
interface DataMap {
aliases: FullSlug[]
frontmatter: { [key: string]: unknown } & {
title: string
} & Partial<{
Expand Down
2 changes: 1 addition & 1 deletion quartz/util/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function getFullSlug(window: Window): FullSlug {
return res
}

function sluggify(s: string): string {
export function sluggify(s: string): string {
return s
.split("/")
.map((segment) =>
Expand Down

0 comments on commit 5122560

Please sign in to comment.