diff --git a/quartz/plugins/emitters/404.tsx b/quartz/plugins/emitters/404.tsx deleted file mode 100644 index 04a006dd1..000000000 --- a/quartz/plugins/emitters/404.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import { QuartzEmitterPlugin } from "../types" -import { QuartzComponentProps } from "../../components/types" -import BodyConstructor from "../../components/Body" -import { pageResources, renderPage } from "../../components/renderPage" -import { FullPageLayout } from "../../cfg" -import { FullSlug } from "../../util/path" -import { sharedPageComponents } from "../../../quartz.layout" -import { NotFound } from "../../components" -import { defaultProcessedContent } from "../vfile" -import { write } from "./helpers" -import { i18n } from "../../i18n" - -export const NotFoundPage: QuartzEmitterPlugin = () => { - const opts: FullPageLayout = { - ...sharedPageComponents, - pageBody: NotFound(), - beforeBody: [], - left: [], - right: [], - } - - const { head: Head, pageBody, footer: Footer } = opts - const Body = BodyConstructor() - - return { - name: "404Page", - getQuartzComponents() { - return [Head, Body, pageBody, Footer] - }, - async *emit(ctx, _content, resources) { - const cfg = ctx.cfg.configuration - const slug = "404" as FullSlug - - const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`) - const path = url.pathname as FullSlug - const notFound = i18n(cfg.locale).pages.error.title - const [tree, vfile] = defaultProcessedContent({ - slug, - text: notFound, - description: notFound, - frontmatter: { title: notFound, tags: [] }, - }) - const externalResources = pageResources(path, resources) - const componentData: QuartzComponentProps = { - ctx, - fileData: vfile.data, - externalResources, - cfg, - children: [], - tree, - allFiles: [], - } - - yield write({ - ctx, - content: renderPage(cfg, slug, componentData, opts, externalResources), - slug, - ext: ".html", - }) - }, - async *partialEmit() {}, - } -} diff --git a/quartz/plugins/emitters/contentPage.tsx b/quartz/plugins/emitters/contentPage.tsx deleted file mode 100644 index c3410ecc3..000000000 --- a/quartz/plugins/emitters/contentPage.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import path from "path" -import { QuartzEmitterPlugin } from "../types" -import { QuartzComponentProps } from "../../components/types" -import HeaderConstructor from "../../components/Header" -import BodyConstructor from "../../components/Body" -import { pageResources, renderPage } from "../../components/renderPage" -import { FullPageLayout } from "../../cfg" -import { pathToRoot } from "../../util/path" -import { defaultContentPageLayout, sharedPageComponents } from "../../../quartz.layout" -import { Content } from "../../components" -import { styleText } from "util" -import { write } from "./helpers" -import { BuildCtx } from "../../util/ctx" -import { Node } from "unist" -import { StaticResources } from "../../util/resources" -import { QuartzPluginData } from "../vfile" - -async function processContent( - ctx: BuildCtx, - tree: Node, - fileData: QuartzPluginData, - allFiles: QuartzPluginData[], - opts: FullPageLayout, - resources: StaticResources, -) { - const slug = fileData.slug! - const cfg = ctx.cfg.configuration - const externalResources = pageResources(pathToRoot(slug), resources) - const componentData: QuartzComponentProps = { - ctx, - fileData, - externalResources, - cfg, - children: [], - tree, - allFiles, - } - - const content = renderPage(cfg, slug, componentData, opts, externalResources) - return write({ - ctx, - content, - slug, - ext: ".html", - }) -} - -export const ContentPage: QuartzEmitterPlugin> = (userOpts) => { - const opts: FullPageLayout = { - ...sharedPageComponents, - ...defaultContentPageLayout, - pageBody: Content(), - ...userOpts, - } - - const { head: Head, header, beforeBody, pageBody, afterBody, left, right, footer: Footer } = opts - const Header = HeaderConstructor() - const Body = BodyConstructor() - - return { - name: "ContentPage", - getQuartzComponents() { - return [ - Head, - Header, - Body, - ...header, - ...beforeBody, - pageBody, - ...afterBody, - ...left, - ...right, - Footer, - ] - }, - async *emit(ctx, content, resources) { - const allFiles = content.map((c) => c[1].data) - let containsIndex = false - - for (const [tree, file] of content) { - const slug = file.data.slug! - if (slug === "index") { - containsIndex = true - } - - // only process home page, non-tag pages, and non-index pages - if (slug.endsWith("/index") || slug.startsWith("tags/")) continue - yield processContent(ctx, tree, file.data, allFiles, opts, resources) - } - - if (!containsIndex) { - console.log( - styleText( - "yellow", - `\nWarning: you seem to be missing an \`index.md\` home page file at the root of your \`${ctx.argv.directory}\` folder (\`${path.join(ctx.argv.directory, "index.md")} does not exist\`). This may cause errors when deploying.`, - ), - ) - } - }, - async *partialEmit(ctx, content, resources, changeEvents) { - const allFiles = content.map((c) => c[1].data) - - // find all slugs that changed or were added - const changedSlugs = new Set() - for (const changeEvent of changeEvents) { - if (!changeEvent.file) continue - if (changeEvent.type === "add" || changeEvent.type === "change") { - changedSlugs.add(changeEvent.file.data.slug!) - } - } - - for (const [tree, file] of content) { - const slug = file.data.slug! - if (!changedSlugs.has(slug)) continue - if (slug.endsWith("/index") || slug.startsWith("tags/")) continue - - yield processContent(ctx, tree, file.data, allFiles, opts, resources) - } - }, - } -} diff --git a/quartz/plugins/emitters/folderPage.tsx b/quartz/plugins/emitters/folderPage.tsx deleted file mode 100644 index f9b181dff..000000000 --- a/quartz/plugins/emitters/folderPage.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { QuartzEmitterPlugin } from "../types" -import { QuartzComponentProps } from "../../components/types" -import HeaderConstructor from "../../components/Header" -import BodyConstructor from "../../components/Body" -import { pageResources, renderPage } from "../../components/renderPage" -import { ProcessedContent, QuartzPluginData, defaultProcessedContent } from "../vfile" -import { FullPageLayout } from "../../cfg" -import path from "path" -import { - FullSlug, - SimpleSlug, - stripSlashes, - joinSegments, - pathToRoot, - simplifySlug, -} from "../../util/path" -import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout" -import { FolderContent } from "../../components" -import { write } from "./helpers" -import { i18n, TRANSLATIONS } from "../../i18n" -import { BuildCtx } from "../../util/ctx" -import { StaticResources } from "../../util/resources" -interface FolderPageOptions extends FullPageLayout { - sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number -} - -async function* processFolderInfo( - ctx: BuildCtx, - folderInfo: Record, - allFiles: QuartzPluginData[], - opts: FullPageLayout, - resources: StaticResources, -) { - for (const [folder, folderContent] of Object.entries(folderInfo) as [ - SimpleSlug, - ProcessedContent, - ][]) { - const slug = joinSegments(folder, "index") as FullSlug - const [tree, file] = folderContent - const cfg = ctx.cfg.configuration - const externalResources = pageResources(pathToRoot(slug), resources) - const componentData: QuartzComponentProps = { - ctx, - fileData: file.data, - externalResources, - cfg, - children: [], - tree, - allFiles, - } - - const content = renderPage(cfg, slug, componentData, opts, externalResources) - yield write({ - ctx, - content, - slug, - ext: ".html", - }) - } -} - -function computeFolderInfo( - folders: Set, - content: ProcessedContent[], - locale: keyof typeof TRANSLATIONS, -): Record { - // Create default folder descriptions - const folderInfo: Record = Object.fromEntries( - [...folders].map((folder) => [ - folder, - defaultProcessedContent({ - slug: joinSegments(folder, "index") as FullSlug, - frontmatter: { - title: `${i18n(locale).pages.folderContent.folder}: ${folder}`, - tags: [], - }, - }), - ]), - ) - - // Update with actual content if available - for (const [tree, file] of content) { - const slug = stripSlashes(simplifySlug(file.data.slug!)) as SimpleSlug - if (folders.has(slug)) { - folderInfo[slug] = [tree, file] - } - } - - return folderInfo -} - -function _getFolders(slug: FullSlug): SimpleSlug[] { - var folderName = path.dirname(slug ?? "") as SimpleSlug - const parentFolderNames = [folderName] - - while (folderName !== ".") { - folderName = path.dirname(folderName ?? "") as SimpleSlug - parentFolderNames.push(folderName) - } - return parentFolderNames -} - -export const FolderPage: QuartzEmitterPlugin> = (userOpts) => { - const opts: FullPageLayout = { - ...sharedPageComponents, - ...defaultListPageLayout, - pageBody: FolderContent({ sort: userOpts?.sort }), - ...userOpts, - } - - const { head: Head, header, beforeBody, pageBody, afterBody, left, right, footer: Footer } = opts - const Header = HeaderConstructor() - const Body = BodyConstructor() - - return { - name: "FolderPage", - getQuartzComponents() { - return [ - Head, - Header, - Body, - ...header, - ...beforeBody, - pageBody, - ...afterBody, - ...left, - ...right, - Footer, - ] - }, - async *emit(ctx, content, resources) { - const allFiles = content.map((c) => c[1].data) - const cfg = ctx.cfg.configuration - - const folders: Set = new Set( - allFiles.flatMap((data) => { - return data.slug - ? _getFolders(data.slug).filter( - (folderName) => folderName !== "." && folderName !== "tags", - ) - : [] - }), - ) - - const folderInfo = computeFolderInfo(folders, content, cfg.locale) - yield* processFolderInfo(ctx, folderInfo, allFiles, opts, resources) - }, - async *partialEmit(ctx, content, resources, changeEvents) { - const allFiles = content.map((c) => c[1].data) - const cfg = ctx.cfg.configuration - - // Find all folders that need to be updated based on changed files - const affectedFolders: Set = new Set() - for (const changeEvent of changeEvents) { - if (!changeEvent.file) continue - const slug = changeEvent.file.data.slug! - const folders = _getFolders(slug).filter( - (folderName) => folderName !== "." && folderName !== "tags", - ) - folders.forEach((folder) => affectedFolders.add(folder)) - } - - // If there are affected folders, rebuild their pages - if (affectedFolders.size > 0) { - const folderInfo = computeFolderInfo(affectedFolders, content, cfg.locale) - yield* processFolderInfo(ctx, folderInfo, allFiles, opts, resources) - } - }, - } -} diff --git a/quartz/plugins/emitters/index.ts b/quartz/plugins/emitters/index.ts index d2de2ed1e..08fee89ed 100644 --- a/quartz/plugins/emitters/index.ts +++ b/quartz/plugins/emitters/index.ts @@ -1,12 +1,8 @@ -export { ContentPage } from "./contentPage" -export { TagPage } from "./tagPage" -export { FolderPage } from "./folderPage" export { ContentIndex as ContentIndex } from "./contentIndex" export { AliasRedirects } from "./aliases" export { Assets } from "./assets" export { Static } from "./static" export { Favicon } from "./favicon" export { ComponentResources } from "./componentResources" -export { NotFoundPage } from "./404" export { CNAME } from "./cname" export { CustomOgImages } from "./ogImage" diff --git a/quartz/plugins/emitters/tagPage.tsx b/quartz/plugins/emitters/tagPage.tsx deleted file mode 100644 index 5f238932d..000000000 --- a/quartz/plugins/emitters/tagPage.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { QuartzEmitterPlugin } from "../types" -import { QuartzComponentProps } from "../../components/types" -import HeaderConstructor from "../../components/Header" -import BodyConstructor from "../../components/Body" -import { pageResources, renderPage } from "../../components/renderPage" -import { ProcessedContent, QuartzPluginData, defaultProcessedContent } from "../vfile" -import { FullPageLayout } from "../../cfg" -import { FullSlug, getAllSegmentPrefixes, joinSegments, pathToRoot } from "../../util/path" -import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout" -import { TagContent } from "../../components" -import { write } from "./helpers" -import { i18n, TRANSLATIONS } from "../../i18n" -import { BuildCtx } from "../../util/ctx" -import { StaticResources } from "../../util/resources" - -interface TagPageOptions extends FullPageLayout { - sort?: (f1: QuartzPluginData, f2: QuartzPluginData) => number -} - -function computeTagInfo( - allFiles: QuartzPluginData[], - content: ProcessedContent[], - locale: keyof typeof TRANSLATIONS, -): [Set, Record] { - const tags: Set = new Set( - allFiles.flatMap((data) => data.frontmatter?.tags ?? []).flatMap(getAllSegmentPrefixes), - ) - - // add base tag - tags.add("index") - - const tagDescriptions: Record = Object.fromEntries( - [...tags].map((tag) => { - const title = - tag === "index" - ? i18n(locale).pages.tagContent.tagIndex - : `${i18n(locale).pages.tagContent.tag}: ${tag}` - return [ - tag, - defaultProcessedContent({ - slug: joinSegments("tags", tag) as FullSlug, - frontmatter: { title, tags: [] }, - }), - ] - }), - ) - - // Update with actual content if available - for (const [tree, file] of content) { - const slug = file.data.slug! - if (slug.startsWith("tags/")) { - const tag = slug.slice("tags/".length) - if (tags.has(tag)) { - tagDescriptions[tag] = [tree, file] - if (file.data.frontmatter?.title === tag) { - file.data.frontmatter.title = `${i18n(locale).pages.tagContent.tag}: ${tag}` - } - } - } - } - - return [tags, tagDescriptions] -} - -async function processTagPage( - ctx: BuildCtx, - tag: string, - tagContent: ProcessedContent, - allFiles: QuartzPluginData[], - opts: FullPageLayout, - resources: StaticResources, -) { - const slug = joinSegments("tags", tag) as FullSlug - const [tree, file] = tagContent - const cfg = ctx.cfg.configuration - const externalResources = pageResources(pathToRoot(slug), resources) - const componentData: QuartzComponentProps = { - ctx, - fileData: file.data, - externalResources, - cfg, - children: [], - tree, - allFiles, - } - - const content = renderPage(cfg, slug, componentData, opts, externalResources) - return write({ - ctx, - content, - slug: file.data.slug!, - ext: ".html", - }) -} - -export const TagPage: QuartzEmitterPlugin> = (userOpts) => { - const opts: FullPageLayout = { - ...sharedPageComponents, - ...defaultListPageLayout, - pageBody: TagContent({ sort: userOpts?.sort }), - ...userOpts, - } - - const { head: Head, header, beforeBody, pageBody, afterBody, left, right, footer: Footer } = opts - const Header = HeaderConstructor() - const Body = BodyConstructor() - - return { - name: "TagPage", - getQuartzComponents() { - return [ - Head, - Header, - Body, - ...header, - ...beforeBody, - pageBody, - ...afterBody, - ...left, - ...right, - Footer, - ] - }, - async *emit(ctx, content, resources) { - const allFiles = content.map((c) => c[1].data) - const cfg = ctx.cfg.configuration - const [tags, tagDescriptions] = computeTagInfo(allFiles, content, cfg.locale) - - for (const tag of tags) { - yield processTagPage(ctx, tag, tagDescriptions[tag], allFiles, opts, resources) - } - }, - async *partialEmit(ctx, content, resources, changeEvents) { - const allFiles = content.map((c) => c[1].data) - const cfg = ctx.cfg.configuration - - // Find all tags that need to be updated based on changed files - const affectedTags: Set = new Set() - for (const changeEvent of changeEvents) { - if (!changeEvent.file) continue - const slug = changeEvent.file.data.slug! - - // If it's a tag page itself that changed - if (slug.startsWith("tags/")) { - const tag = slug.slice("tags/".length) - affectedTags.add(tag) - } - - // If a file with tags changed, we need to update those tag pages - const fileTags = changeEvent.file.data.frontmatter?.tags ?? [] - fileTags.flatMap(getAllSegmentPrefixes).forEach((tag) => affectedTags.add(tag)) - - // Always update the index tag page if any file changes - affectedTags.add("index") - } - - // If there are affected tags, rebuild their pages - if (affectedTags.size > 0) { - // We still need to compute all tags because tag pages show all tags - const [_tags, tagDescriptions] = computeTagInfo(allFiles, content, cfg.locale) - - for (const tag of affectedTags) { - if (tagDescriptions[tag]) { - yield processTagPage(ctx, tag, tagDescriptions[tag], allFiles, opts, resources) - } - } - } - }, - } -}