mirror of
https://github.com/jackyzha0/quartz.git
synced 2026-02-03 22:15:42 -06:00
* Initial plan * refactor: remove BuildCtx mutation from FrontMatter plugin - Remove temporary cast to mutable allSlugs array - Move alias collection to build orchestration layer - Update ctx.allSlugs immutably after parsing - Apply same pattern to incremental rebuild - Verified alias functionality works correctly Co-authored-by: saberzero1 <8161064+saberzero1@users.noreply.github.com> * fix: ensure alias collection happens before filtering in rebuild flow Move alias collection before filterContent() in rebuild flow to match initial build flow. This ensures consistent behavior where aliases from all markdown files (including those that will be filtered out) are included in ctx.allSlugs in both build scenarios. Co-authored-by: saberzero1 <8161064+saberzero1@users.noreply.github.com> * refactor: simplify collectAliases using functional array methods Replace imperative for-loop with declarative filter/flatMap chain for better readability and conciseness. Functionally equivalent but more idiomatic TypeScript. Co-authored-by: saberzero1 <8161064+saberzero1@users.noreply.github.com> * docs: update roadmap with completion status for decoupling phases Mark phases 1-5 as completed with detailed status notes: - Phase 1 (Foundation): vfile-schema, plugin-context, test-helpers - Phase 2 (Utility Abstraction): ctx.utils migration complete - Phase 3 (Component Decoupling): component registry created - Phase 4 (Immutability): BuildCtx readonly, alias collection refactored - Phase 5 (Full Migration): all plugins migrated to new pattern Add implementation status summary showing all objectives achieved. Co-authored-by: saberzero1 <8161064+saberzero1@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: saberzero1 <8161064+saberzero1@users.noreply.github.com>
163 lines
4.9 KiB
TypeScript
163 lines
4.9 KiB
TypeScript
import matter from "gray-matter"
|
|
import remarkFrontmatter from "remark-frontmatter"
|
|
import { QuartzTransformerPlugin } from "../types"
|
|
import yaml from "js-yaml"
|
|
import toml from "toml"
|
|
import { FilePath, FullSlug } from "../../util/path"
|
|
import { QuartzPluginData } from "../vfile"
|
|
import { i18n } from "../../i18n"
|
|
|
|
export interface Options {
|
|
delimiters: string | [string, string]
|
|
language: "yaml" | "toml"
|
|
}
|
|
|
|
const defaultOptions: Options = {
|
|
delimiters: "---",
|
|
language: "yaml",
|
|
}
|
|
|
|
function coalesceAliases(data: { [key: string]: any }, aliases: string[]) {
|
|
for (const alias of aliases) {
|
|
if (data[alias] !== undefined && data[alias] !== null) return data[alias]
|
|
}
|
|
}
|
|
|
|
function coerceToArray(input: string | string[]): string[] | undefined {
|
|
if (input === undefined || input === null) return undefined
|
|
|
|
// coerce to array
|
|
if (!Array.isArray(input)) {
|
|
input = input
|
|
.toString()
|
|
.split(",")
|
|
.map((tag: string) => tag.trim())
|
|
}
|
|
|
|
// remove all non-strings
|
|
return input
|
|
.filter((tag: unknown) => typeof tag === "string" || typeof tag === "number")
|
|
.map((tag: string | number) => tag.toString())
|
|
}
|
|
|
|
/**
|
|
* @plugin FrontMatter
|
|
* @category Transformer
|
|
*
|
|
* @reads None (processes raw frontmatter)
|
|
* @writes vfile.data.frontmatter
|
|
* @writes vfile.data.aliases
|
|
*
|
|
* @dependencies None
|
|
*/
|
|
export const FrontMatter: QuartzTransformerPlugin<Partial<Options>> = (userOpts) => {
|
|
const opts = { ...defaultOptions, ...userOpts }
|
|
return {
|
|
name: "FrontMatter",
|
|
markdownPlugins(ctx) {
|
|
const { cfg, utils } = ctx
|
|
|
|
// Helper function to get alias slugs using ctx.utils
|
|
const getAliasSlugs = (aliases: string[]): FullSlug[] => {
|
|
const res: FullSlug[] = []
|
|
for (const alias of aliases) {
|
|
const isMd = utils!.path.getFileExtension(alias) === "md"
|
|
const mockFp = isMd ? alias : alias + ".md"
|
|
const slug = utils!.path.slugify(mockFp as FilePath)
|
|
res.push(slug)
|
|
}
|
|
return res
|
|
}
|
|
|
|
return [
|
|
[remarkFrontmatter, ["yaml", "toml"]],
|
|
() => {
|
|
return (_, file) => {
|
|
const fileData = Buffer.from(file.value as Uint8Array)
|
|
const { data } = matter(fileData, {
|
|
...opts,
|
|
engines: {
|
|
yaml: (s) => yaml.load(s, { schema: yaml.JSON_SCHEMA }) as object,
|
|
toml: (s) => toml.parse(s) as object,
|
|
},
|
|
})
|
|
|
|
if (data.title != null && data.title.toString() !== "") {
|
|
data.title = data.title.toString()
|
|
} else {
|
|
data.title = file.stem ?? i18n(cfg.configuration.locale).propertyDefaults.title
|
|
}
|
|
|
|
const tags = coerceToArray(coalesceAliases(data, ["tags", "tag"]))
|
|
if (tags) data.tags = [...new Set(tags.map((tag: string) => utils!.path.slugTag(tag)))]
|
|
|
|
const aliases = coerceToArray(coalesceAliases(data, ["aliases", "alias"]))
|
|
if (aliases) {
|
|
data.aliases = aliases // frontmatter
|
|
file.data.aliases = getAliasSlugs(aliases)
|
|
}
|
|
|
|
if (data.permalink != null && data.permalink.toString() !== "") {
|
|
data.permalink = data.permalink.toString() as FullSlug
|
|
const aliases = file.data.aliases ?? []
|
|
aliases.push(data.permalink)
|
|
file.data.aliases = aliases
|
|
}
|
|
|
|
const cssclasses = coerceToArray(coalesceAliases(data, ["cssclasses", "cssclass"]))
|
|
if (cssclasses) data.cssclasses = cssclasses
|
|
|
|
const socialImage = coalesceAliases(data, ["socialImage", "image", "cover"])
|
|
|
|
const created = coalesceAliases(data, ["created", "date"])
|
|
if (created) {
|
|
data.created = created
|
|
}
|
|
|
|
const modified = coalesceAliases(data, [
|
|
"modified",
|
|
"lastmod",
|
|
"updated",
|
|
"last-modified",
|
|
])
|
|
if (modified) data.modified = modified
|
|
data.modified ||= created // if modified is not set, use created
|
|
|
|
const published = coalesceAliases(data, ["published", "publishDate", "date"])
|
|
if (published) data.published = published
|
|
|
|
if (socialImage) data.socialImage = socialImage
|
|
|
|
// fill in frontmatter
|
|
file.data.frontmatter = data as QuartzPluginData["frontmatter"]
|
|
}
|
|
},
|
|
]
|
|
},
|
|
}
|
|
}
|
|
|
|
declare module "vfile" {
|
|
interface DataMap {
|
|
aliases: FullSlug[]
|
|
frontmatter: { [key: string]: unknown } & {
|
|
title: string
|
|
} & Partial<{
|
|
tags: string[]
|
|
aliases: string[]
|
|
modified: string
|
|
created: string
|
|
published: string
|
|
description: string
|
|
socialDescription: string
|
|
publish: boolean | string
|
|
draft: boolean | string
|
|
lang: string
|
|
enableToc: string
|
|
cssclasses: string[]
|
|
socialImage: string
|
|
comments: boolean | string
|
|
}>
|
|
}
|
|
}
|