This commit is contained in:
Emile Bangma 2025-12-09 02:23:58 +07:00 committed by GitHub
commit 08fa7e8ef3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 59 additions and 18 deletions

View File

@ -2,6 +2,10 @@
title: AliasRedirects
tags:
- plugin/emitter
permalink: "permalink"
alias:
- "alias"
- "aliases"
---
This plugin emits HTML redirect pages for aliases and permalinks defined in the frontmatter of content files.
@ -11,14 +15,15 @@ For example, A `foo.md` has the following frontmatter
```md title="foo.md"
---
title: "Foo"
permalink: "bar"
alias:
- "bar"
- "baz"
---
```
The target `host.me/bar` will be redirected to `host.me/foo`
The target `host.me/bar` and `host.me/baz` will be redirected to `host.me/foo`
Note that these are permanent redirect.
Note that these are permanent redirects.
The emitter supports the following aliases:

View File

@ -1,4 +1,4 @@
import { FullSlug, isRelativeURL, resolveRelative, simplifySlug } from "../../util/path"
import { FullSlug, FilePath, joinSegments, isRelativeURL, resolveRelative, simplifySlug } from "../../util/path"
import { QuartzEmitterPlugin } from "../types"
import { write } from "./helpers"
import { BuildCtx } from "../../util/ctx"
@ -38,17 +38,45 @@ async function* processFile(ctx: BuildCtx, file: VFile) {
export const AliasRedirects: QuartzEmitterPlugin = () => ({
name: "AliasRedirects",
async *emit(ctx, content) {
for (const [_tree, file] of content) {
yield* processFile(ctx, file)
}
},
async *partialEmit(ctx, _content, _resources, changeEvents) {
for (const changeEvent of changeEvents) {
if (!changeEvent.file) continue
if (changeEvent.type === "add" || changeEvent.type === "change") {
// add new ones if this file still exists
yield* processFile(ctx, changeEvent.file)
}
},
async emit(ctx, content, _resources): Promise<FilePath[]> {
const { argv } = ctx
const fps: FilePath[] = []
for (const [_tree, file] of content) {
const ogSlug = simplifySlug(file.data.slug!)
const slugs = [file.data.frontmatter?.permalink || [], ...(file.data.aliases || [])]
for (const slug of slugs ?? []) {
if (typeof slug !== "string") continue
const redirUrl = file.data.slug!
const fp = await write({
ctx,
content: `
<!DOCTYPE html>
<html lang="en-us">
<head>
<title>${ogSlug}</title>
<link rel="canonical" href="${redirUrl}">
<meta name="robots" content="noindex">
<meta charset="utf-8">
<meta http-equiv="refresh" content="0; url=${redirUrl}">
</head>
</html>
`,
slug: slug as FullSlug,
ext: ".html",
})
fps.push(fp)
}
}
},

View File

@ -3,9 +3,12 @@ import remarkFrontmatter from "remark-frontmatter"
import { QuartzTransformerPlugin } from "../types"
import yaml from "js-yaml"
import toml from "toml"
import { FilePath, FullSlug, getFileExtension, slugifyFilePath, slugTag } from "../../util/path"
import { FilePath, FullSlug, getFileExtension, joinSegments, slugifyFilePath, slugTag } from "../../util/path"
import { QuartzPluginData } from "../vfile"
import { i18n } from "../../i18n"
import { Argv } from "../../util/ctx"
import { VFile } from "vfile"
export interface Options {
delimiters: string | [string, string]
@ -19,7 +22,10 @@ const defaultOptions: Options = {
function coalesceAliases(data: { [key: string]: any }, aliases: string[]) {
for (const alias of aliases) {
if (data[alias] !== undefined && data[alias] !== null) return data[alias]
if (data[alias] !== undefined && data[alias] !== null) {
if (typeof data[alias] === "string") return [data[alias]]
return data[alias]
}
}
}
@ -40,13 +46,14 @@ function coerceToArray(input: string | string[]): string[] | undefined {
.map((tag: string | number) => tag.toString())
}
function getAliasSlugs(aliases: string[]): FullSlug[] {
const res: FullSlug[] = []
for (const alias of aliases) {
const isMd = getFileExtension(alias) === "md"
const mockFp = isMd ? alias : alias + ".md"
const slug = slugifyFilePath(mockFp as FilePath)
res.push(slug)
export function getAliasSlugs(aliases: string[], _argv: Argv, file: VFile): FullSlug[] {
const slugs: FullSlug[] = aliases.map(
(alias) => (alias.startsWith("/") ? alias : `/${alias}`) as FullSlug,
)
const permalink = file.data.frontmatter?.permalink
if (typeof permalink === "string") {
const absolutePermalink = permalink.startsWith("/") ? permalink : `/${permalink}`
slugs.push(absolutePermalink as FullSlug)
}
return res
@ -145,6 +152,7 @@ declare module "vfile" {
published: string
description: string
socialDescription: string
permalink: string
publish: boolean | string
draft: boolean | string
lang: string