기존에 slug 기능을 제공하기는 하지만, 기존에 제공하는 slug는 baseurl 기반이 아니다. 그래서 링크의 가장 마지막 부분(BaseURL/Blog/General/slug의 slug 부분)만 설정할 수 있다. 기존 블로그에서 옮겨오다보니, 전체 URL를 리다이렉트 할 필요가 있었다.

Plugin 생성

quartz/plugins/emitters/customredirect.ts 파일을 생성한다. 해당 파일의 내용은 아래와 같다.

import { FilePath, FullSlug, joinSegments, resolveRelative, simplifySlug } from "../../util/path"
import { QuartzEmitterPlugin } from "../types"
import path from "path"
import { write } from "./helpers"
import DepGraph from "../../depgraph"

// FullSlug 타입으로 변환하는 헬퍼 함수 추가
function toFullSlug(slug: string): FullSlug {
  return slug as FullSlug
}

export const CustomRedirects: QuartzEmitterPlugin = () => ({
  name: "CustomRedirects",
  getQuartzComponents() {
    return []
  },
  async getDependencyGraph(ctx, content, _resources) {
    const graph = new DepGraph<FilePath>()

    const { argv } = ctx
    for (const [_tree, file] of content) {
      const dir = path.posix.relative(argv.directory, path.dirname(file.data.filePath!))
      const redirectTo = file.data.frontmatter?.redirect_from

      if (typeof redirectTo === "string") {
        const slug = toFullSlug(redirectTo)
        graph.addEdge(file.data.filePath!, joinSegments(argv.output, slug + ".html") as FilePath)
      }
    }

    return graph
  },
  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 redirectTo = file.data.frontmatter?.redirect_from

      if (typeof redirectTo === "string") {
        const slug = toFullSlug(redirectTo)
        const redirUrl = resolveRelative(slug, 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,
          ext: ".html",
        })

        fps.push(fp)
      }
    }
    return fps
  },
})

index.ts에 추가

해당 플러그인을 quartz/plugins/emitters/index.ts에 추가한다. export { CustomRedirects } from "./customredirect" 부분이 추가된 부분이다.

export { ContentPage } from "./contentPage"
export { TagPage } from "./tagPage"
export { FolderPage } from "./folderPage"
export { ContentIndex } from "./contentIndex"
export { AliasRedirects } from "./aliases"
export { CustomRedirects } from "./customredirect"
export { Assets } from "./assets"
export { Static } from "./static"
export { ComponentResources } from "./componentResources"
export { NotFoundPage } from "./404"
export { CNAME } from "./cname"

프론트메타 추가

적용을 원하는 문서에 redirect_from: "posts/4"이렇게 추가하면, https://gurumii.com/posts/4로 접속했을 때, 해당 문서로 리다이렉트 된다.

오류

다만, redirect_from에 들어가는 주소가 복잡하면(한글과 기타 기호가 섞일 경우), 제대로 전달을 못 한다. (/posts/ 부분이 추가되는 등) 현재 상태로는 해결할 생각은 없다.