The Trans Directory
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix(aliases): wikilink resolution for aliases (#1681)

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.

authored by

Anton Bulakh and committed by
GitHub
29c533a2 1a026424

+30 -32
+4 -29
quartz/plugins/emitters/aliases.ts
··· 1 - import { FilePath, FullSlug, joinSegments, resolveRelative, simplifySlug } from "../../util/path" 1 + import { FilePath, joinSegments, resolveRelative, simplifySlug } from "../../util/path" 2 2 import { QuartzEmitterPlugin } from "../types" 3 - import path from "path" 4 3 import { write } from "./helpers" 5 4 import DepGraph from "../../depgraph" 5 + import { getAliasSlugs } from "../transformers/frontmatter" 6 6 7 7 export const AliasRedirects: QuartzEmitterPlugin = () => ({ 8 8 name: "AliasRedirects", ··· 14 14 15 15 const { argv } = ctx 16 16 for (const [_tree, file] of content) { 17 - const dir = path.posix.relative(argv.directory, path.dirname(file.data.filePath!)) 18 - const aliases = file.data.frontmatter?.aliases ?? [] 19 - const slugs = aliases.map((alias) => path.posix.join(dir, alias) as FullSlug) 20 - const permalink = file.data.frontmatter?.permalink 21 - if (typeof permalink === "string") { 22 - slugs.push(permalink as FullSlug) 23 - } 24 - 25 - for (let slug of slugs) { 26 - // fix any slugs that have trailing slash 27 - if (slug.endsWith("/")) { 28 - slug = joinSegments(slug, "index") as FullSlug 29 - } 30 - 17 + for (const slug of getAliasSlugs(file.data.frontmatter?.aliases ?? [], argv, file)) { 31 18 graph.addEdge(file.data.filePath!, joinSegments(argv.output, slug + ".html") as FilePath) 32 19 } 33 20 } ··· 40 27 41 28 for (const [_tree, file] of content) { 42 29 const ogSlug = simplifySlug(file.data.slug!) 43 - const dir = path.posix.relative(argv.directory, path.dirname(file.data.filePath!)) 44 - const aliases = file.data.frontmatter?.aliases ?? [] 45 - const slugs: FullSlug[] = aliases.map((alias) => path.posix.join(dir, alias) as FullSlug) 46 - const permalink = file.data.frontmatter?.permalink 47 - if (typeof permalink === "string") { 48 - slugs.push(permalink as FullSlug) 49 - } 50 30 51 - for (let slug of slugs) { 52 - // fix any slugs that have trailing slash 53 - if (slug.endsWith("/")) { 54 - slug = joinSegments(slug, "index") as FullSlug 55 - } 56 - 31 + for (const slug of file.data.aliases ?? []) { 57 32 const redirUrl = resolveRelative(slug, file.data.slug!) 58 33 const fp = await write({ 59 34 ctx,
+26 -3
quartz/plugins/transformers/frontmatter.ts
··· 3 3 import { QuartzTransformerPlugin } from "../types" 4 4 import yaml from "js-yaml" 5 5 import toml from "toml" 6 - import { slugTag } from "../../util/path" 6 + import { FilePath, FullSlug, joinSegments, slugifyFilePath, slugTag } from "../../util/path" 7 7 import { QuartzPluginData } from "../vfile" 8 8 import { i18n } from "../../i18n" 9 + import { Argv } from "../../util/ctx" 10 + import { VFile } from "vfile" 11 + import path from "path" 9 12 10 13 export interface Options { 11 14 delimiters: string | [string, string] ··· 40 43 .map((tag: string | number) => tag.toString()) 41 44 } 42 45 46 + export function getAliasSlugs(aliases: string[], argv: Argv, file: VFile): FullSlug[] { 47 + const dir = path.posix.relative(argv.directory, path.dirname(file.data.filePath!)) 48 + const slugs: FullSlug[] = aliases.map( 49 + (alias) => path.posix.join(dir, slugifyFilePath(alias as FilePath)) as FullSlug, 50 + ) 51 + const permalink = file.data.frontmatter?.permalink 52 + if (typeof permalink === "string") { 53 + slugs.push(permalink as FullSlug) 54 + } 55 + // fix any slugs that have trailing slash 56 + return slugs.map((slug) => 57 + slug.endsWith("/") ? (joinSegments(slug, "index") as FullSlug) : slug, 58 + ) 59 + } 60 + 43 61 export const FrontMatter: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => { 44 62 const opts = { ...defaultOptions, ...userOpts } 45 63 return { 46 64 name: "FrontMatter", 47 - markdownPlugins({ cfg }) { 65 + markdownPlugins({ cfg, allSlugs, argv }) { 48 66 return [ 49 67 [remarkFrontmatter, ["yaml", "toml"]], 50 68 () => { ··· 67 85 if (tags) data.tags = [...new Set(tags.map((tag: string) => slugTag(tag)))] 68 86 69 87 const aliases = coerceToArray(coalesceAliases(data, ["aliases", "alias"])) 70 - if (aliases) data.aliases = aliases 88 + if (aliases) { 89 + data.aliases = aliases // frontmatter 90 + const slugs = (file.data.aliases = getAliasSlugs(aliases, argv, file)) 91 + allSlugs.push(...slugs) 92 + } 71 93 const cssclasses = coerceToArray(coalesceAliases(data, ["cssclasses", "cssclass"])) 72 94 if (cssclasses) data.cssclasses = cssclasses 73 95 ··· 98 120 99 121 declare module "vfile" { 100 122 interface DataMap { 123 + aliases: FullSlug[] 101 124 frontmatter: { [key: string]: unknown } & { 102 125 title: string 103 126 } & Partial<{