This commit is contained in:
@ -7,6 +7,8 @@ import { FilePath, 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 = {
|
||||
@ -25,18 +27,19 @@ export const NotFoundPage: QuartzEmitterPlugin = () => {
|
||||
getQuartzComponents() {
|
||||
return [Head, Body, pageBody, Footer]
|
||||
},
|
||||
async emit(ctx, _content, resources, emit): Promise<FilePath[]> {
|
||||
async emit(ctx, _content, resources): Promise<FilePath[]> {
|
||||
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 externalResources = pageResources(path, resources)
|
||||
const notFound = i18n(cfg.locale).pages.error.title
|
||||
const [tree, vfile] = defaultProcessedContent({
|
||||
slug,
|
||||
text: "Not Found",
|
||||
description: "Not Found",
|
||||
frontmatter: { title: "Not Found", tags: [] },
|
||||
text: notFound,
|
||||
description: notFound,
|
||||
frontmatter: { title: notFound, tags: [] },
|
||||
})
|
||||
const componentData: QuartzComponentProps = {
|
||||
fileData: vfile.data,
|
||||
@ -48,8 +51,9 @@ export const NotFoundPage: QuartzEmitterPlugin = () => {
|
||||
}
|
||||
|
||||
return [
|
||||
await emit({
|
||||
content: renderPage(slug, componentData, opts, externalResources),
|
||||
await write({
|
||||
ctx,
|
||||
content: renderPage(cfg, slug, componentData, opts, externalResources),
|
||||
slug,
|
||||
ext: ".html",
|
||||
}),
|
||||
|
@ -1,24 +1,21 @@
|
||||
import { FilePath, FullSlug, joinSegments, resolveRelative, simplifySlug } from "../../util/path"
|
||||
import { QuartzEmitterPlugin } from "../types"
|
||||
import path from "path"
|
||||
import { write } from "./helpers"
|
||||
|
||||
export const AliasRedirects: QuartzEmitterPlugin = () => ({
|
||||
name: "AliasRedirects",
|
||||
getQuartzComponents() {
|
||||
return []
|
||||
},
|
||||
async emit({ argv }, content, _resources, emit): Promise<FilePath[]> {
|
||||
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 dir = path.posix.relative(argv.directory, path.dirname(file.data.filePath!))
|
||||
|
||||
let aliases: FullSlug[] = file.data.frontmatter?.aliases ?? file.data.frontmatter?.alias ?? []
|
||||
if (typeof aliases === "string") {
|
||||
aliases = [aliases]
|
||||
}
|
||||
|
||||
const aliases = file.data.frontmatter?.aliases ?? []
|
||||
const slugs: FullSlug[] = aliases.map((alias) => path.posix.join(dir, alias) as FullSlug)
|
||||
const permalink = file.data.frontmatter?.permalink
|
||||
if (typeof permalink === "string") {
|
||||
@ -32,7 +29,8 @@ export const AliasRedirects: QuartzEmitterPlugin = () => ({
|
||||
}
|
||||
|
||||
const redirUrl = resolveRelative(slug, file.data.slug!)
|
||||
const fp = await emit({
|
||||
const fp = await write({
|
||||
ctx,
|
||||
content: `
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-us">
|
||||
|
@ -10,7 +10,7 @@ export const Assets: QuartzEmitterPlugin = () => {
|
||||
getQuartzComponents() {
|
||||
return []
|
||||
},
|
||||
async emit({ argv, cfg }, _content, _resources, _emit): Promise<FilePath[]> {
|
||||
async emit({ argv, cfg }, _content, _resources): Promise<FilePath[]> {
|
||||
// glob all non MD/MDX/HTML files in content folder and copy it over
|
||||
const assetsPath = argv.output
|
||||
const fps = await glob("**", argv.directory, ["**/*.md", ...cfg.configuration.ignorePatterns])
|
||||
|
@ -13,7 +13,7 @@ export const CNAME: QuartzEmitterPlugin = () => ({
|
||||
getQuartzComponents() {
|
||||
return []
|
||||
},
|
||||
async emit({ argv, cfg }, _content, _resources, _emit): Promise<FilePath[]> {
|
||||
async emit({ argv, cfg }, _content, _resources): Promise<FilePath[]> {
|
||||
if (!cfg.configuration.baseUrl) {
|
||||
console.warn(chalk.yellow("CNAME emitter requires `baseUrl` to be set in your configuration"))
|
||||
return []
|
||||
|
@ -13,6 +13,7 @@ import { QuartzComponent } from "../../components/types"
|
||||
import { googleFontHref, joinStyles } from "../../util/theme"
|
||||
import { Features, transform } from "lightningcss"
|
||||
import { transform as transpile } from "esbuild"
|
||||
import { write } from "./helpers"
|
||||
|
||||
type ComponentResources = {
|
||||
css: string[]
|
||||
@ -93,7 +94,6 @@ function addGlobalPageResources(
|
||||
function gtag() { dataLayer.push(arguments); }
|
||||
gtag("js", new Date());
|
||||
gtag("config", "${tagId}", { send_page_view: false });
|
||||
|
||||
document.addEventListener("nav", () => {
|
||||
gtag("event", "page_view", {
|
||||
page_title: document.title,
|
||||
@ -118,10 +118,10 @@ function addGlobalPageResources(
|
||||
} else if (cfg.analytics?.provider === "umami") {
|
||||
componentResources.afterDOMLoaded.push(`
|
||||
const umamiScript = document.createElement("script")
|
||||
umamiScript.src = "https://analytics.umami.is/script.js"
|
||||
umamiScript.src = cfg.analytics.host ?? "https://analytics.umami.is/script.js"
|
||||
umamiScript.setAttribute("data-website-id", "${cfg.analytics.websiteId}")
|
||||
umamiScript.async = true
|
||||
|
||||
|
||||
document.head.appendChild(umamiScript)
|
||||
`)
|
||||
}
|
||||
@ -130,9 +130,11 @@ function addGlobalPageResources(
|
||||
componentResources.afterDOMLoaded.push(spaRouterScript)
|
||||
} else {
|
||||
componentResources.afterDOMLoaded.push(`
|
||||
window.spaNavigate = (url, _) => window.location.assign(url)
|
||||
const event = new CustomEvent("nav", { detail: { url: document.body.dataset.slug } })
|
||||
document.dispatchEvent(event)`)
|
||||
window.spaNavigate = (url, _) => window.location.assign(url)
|
||||
window.addCleanup = () => {}
|
||||
const event = new CustomEvent("nav", { detail: { url: document.body.dataset.slug } })
|
||||
document.dispatchEvent(event)
|
||||
`)
|
||||
}
|
||||
|
||||
let wsUrl = `ws://localhost:${ctx.argv.wsPort}`
|
||||
@ -146,9 +148,9 @@ function addGlobalPageResources(
|
||||
loadTime: "afterDOMReady",
|
||||
contentType: "inline",
|
||||
script: `
|
||||
const socket = new WebSocket('${wsUrl}')
|
||||
socket.addEventListener('message', () => document.location.reload())
|
||||
`,
|
||||
const socket = new WebSocket('${wsUrl}')
|
||||
socket.addEventListener('message', () => document.location.reload())
|
||||
`,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -168,7 +170,7 @@ export const ComponentResources: QuartzEmitterPlugin<Options> = (opts?: Partial<
|
||||
getQuartzComponents() {
|
||||
return []
|
||||
},
|
||||
async emit(ctx, _content, resources, emit): Promise<FilePath[]> {
|
||||
async emit(ctx, _content, resources): Promise<FilePath[]> {
|
||||
// component specific scripts and styles
|
||||
const componentResources = getComponentResources(ctx)
|
||||
// important that this goes *after* component scripts
|
||||
@ -190,7 +192,8 @@ export const ComponentResources: QuartzEmitterPlugin<Options> = (opts?: Partial<
|
||||
])
|
||||
|
||||
const fps = await Promise.all([
|
||||
emit({
|
||||
write({
|
||||
ctx,
|
||||
slug: "index" as FullSlug,
|
||||
ext: ".css",
|
||||
content: transform({
|
||||
@ -207,12 +210,14 @@ export const ComponentResources: QuartzEmitterPlugin<Options> = (opts?: Partial<
|
||||
include: Features.MediaQueries,
|
||||
}).code.toString(),
|
||||
}),
|
||||
emit({
|
||||
write({
|
||||
ctx,
|
||||
slug: "prescript" as FullSlug,
|
||||
ext: ".js",
|
||||
content: prescript,
|
||||
}),
|
||||
emit({
|
||||
write({
|
||||
ctx,
|
||||
slug: "postscript" as FullSlug,
|
||||
ext: ".js",
|
||||
content: postscript,
|
||||
|
@ -5,7 +5,8 @@ import { escapeHTML } from "../../util/escape"
|
||||
import { FilePath, FullSlug, SimpleSlug, joinSegments, simplifySlug } from "../../util/path"
|
||||
import { QuartzEmitterPlugin } from "../types"
|
||||
import { toHtml } from "hast-util-to-html"
|
||||
import path from "path"
|
||||
import { write } from "./helpers"
|
||||
import { i18n } from "../../i18n"
|
||||
|
||||
export type ContentIndex = Map<FullSlug, ContentDetails>
|
||||
export type ContentDetails = {
|
||||
@ -38,7 +39,7 @@ function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndex): string {
|
||||
const base = cfg.baseUrl ?? ""
|
||||
const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<url>
|
||||
<loc>https://${joinSegments(base, encodeURI(slug))}</loc>
|
||||
<lastmod>${content.date?.toISOString()}</lastmod>
|
||||
${content.date && `<lastmod>${content.date.toISOString()}</lastmod>`}
|
||||
</url>`
|
||||
const urls = Array.from(idx)
|
||||
.map(([slug, content]) => createURLEntry(simplifySlug(slug), content))
|
||||
@ -48,12 +49,11 @@ function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndex): string {
|
||||
|
||||
function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndex, limit?: number): string {
|
||||
const base = cfg.baseUrl ?? ""
|
||||
const root = `https://${base}`
|
||||
|
||||
const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<item>
|
||||
<title>${escapeHTML(content.title)}</title>
|
||||
<link>${joinSegments(root, encodeURI(slug))}</link>
|
||||
<guid>${joinSegments(root, encodeURI(slug))}</guid>
|
||||
<link>https://${joinSegments(base, encodeURI(slug))}</link>
|
||||
<guid>https://${joinSegments(base, encodeURI(slug))}</guid>
|
||||
<description>${content.richContent ?? content.description}</description>
|
||||
<pubDate>${content.date?.toUTCString()}</pubDate>
|
||||
</item>`
|
||||
@ -78,8 +78,8 @@ function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndex, limit?: nu
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>${escapeHTML(cfg.pageTitle)}</title>
|
||||
<link>${root}</link>
|
||||
<description>${!!limit ? `Last ${limit} notes` : "Recent notes"} on ${escapeHTML(
|
||||
<link>https://${base}</link>
|
||||
<description>${!!limit ? i18n(cfg.locale).pages.rss.lastFewNotes({ count: limit }) : i18n(cfg.locale).pages.rss.recentNotes} on ${escapeHTML(
|
||||
cfg.pageTitle,
|
||||
)}</description>
|
||||
<generator>Quartz -- quartz.jzhao.xyz</generator>
|
||||
@ -92,7 +92,7 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
|
||||
opts = { ...defaultOptions, ...opts }
|
||||
return {
|
||||
name: "ContentIndex",
|
||||
async emit(ctx, content, _resources, emit) {
|
||||
async emit(ctx, content, _resources) {
|
||||
const cfg = ctx.cfg.configuration
|
||||
const emitted: FilePath[] = []
|
||||
const linkIndex: ContentIndex = new Map()
|
||||
@ -116,7 +116,8 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
|
||||
|
||||
if (opts?.enableSiteMap) {
|
||||
emitted.push(
|
||||
await emit({
|
||||
await write({
|
||||
ctx,
|
||||
content: generateSiteMap(cfg, linkIndex),
|
||||
slug: "sitemap" as FullSlug,
|
||||
ext: ".xml",
|
||||
@ -126,7 +127,8 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
|
||||
|
||||
if (opts?.enableRSS) {
|
||||
emitted.push(
|
||||
await emit({
|
||||
await write({
|
||||
ctx,
|
||||
content: generateRSSFeed(cfg, linkIndex, opts.rssLimit),
|
||||
slug: "index" as FullSlug,
|
||||
ext: ".xml",
|
||||
@ -134,7 +136,7 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
|
||||
)
|
||||
}
|
||||
|
||||
const fp = path.join("static", "contentIndex") as FullSlug
|
||||
const fp = joinSegments("static", "contentIndex") as FullSlug
|
||||
const simplifiedIndex = Object.fromEntries(
|
||||
Array.from(linkIndex).map(([slug, content]) => {
|
||||
// remove description and from content index as nothing downstream
|
||||
@ -147,7 +149,8 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
|
||||
)
|
||||
|
||||
emitted.push(
|
||||
await emit({
|
||||
await write({
|
||||
ctx,
|
||||
content: JSON.stringify(simplifiedIndex),
|
||||
slug: fp,
|
||||
ext: ".json",
|
||||
|
@ -8,6 +8,7 @@ import { FilePath, pathToRoot } from "../../util/path"
|
||||
import { defaultContentPageLayout, sharedPageComponents } from "../../../quartz.layout"
|
||||
import { Content } from "../../components"
|
||||
import chalk from "chalk"
|
||||
import { write } from "./helpers"
|
||||
|
||||
export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => {
|
||||
const opts: FullPageLayout = {
|
||||
@ -26,7 +27,7 @@ export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOp
|
||||
getQuartzComponents() {
|
||||
return [Head, Header, Body, ...header, ...beforeBody, pageBody, ...left, ...right, Footer]
|
||||
},
|
||||
async emit(ctx, content, resources, emit): Promise<FilePath[]> {
|
||||
async emit(ctx, content, resources): Promise<FilePath[]> {
|
||||
const cfg = ctx.cfg.configuration
|
||||
const fps: FilePath[] = []
|
||||
const allFiles = content.map((c) => c[1].data)
|
||||
@ -48,8 +49,9 @@ export const ContentPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOp
|
||||
allFiles,
|
||||
}
|
||||
|
||||
const content = renderPage(slug, componentData, opts, externalResources)
|
||||
const fp = await emit({
|
||||
const content = renderPage(cfg, slug, componentData, opts, externalResources)
|
||||
const fp = await write({
|
||||
ctx,
|
||||
content,
|
||||
slug,
|
||||
ext: ".html",
|
||||
|
@ -17,8 +17,10 @@ import {
|
||||
} from "../../util/path"
|
||||
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
|
||||
import { FolderContent } from "../../components"
|
||||
import { write } from "./helpers"
|
||||
import { i18n } from "../../i18n"
|
||||
|
||||
export const FolderPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
|
||||
export const FolderPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => {
|
||||
const opts: FullPageLayout = {
|
||||
...sharedPageComponents,
|
||||
...defaultListPageLayout,
|
||||
@ -35,7 +37,7 @@ export const FolderPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
|
||||
getQuartzComponents() {
|
||||
return [Head, Header, Body, ...header, ...beforeBody, pageBody, ...left, ...right, Footer]
|
||||
},
|
||||
async emit(ctx, content, resources, emit): Promise<FilePath[]> {
|
||||
async emit(ctx, content, resources): Promise<FilePath[]> {
|
||||
const fps: FilePath[] = []
|
||||
const allFiles = content.map((c) => c[1].data)
|
||||
const cfg = ctx.cfg.configuration
|
||||
@ -56,7 +58,10 @@ export const FolderPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
|
||||
folder,
|
||||
defaultProcessedContent({
|
||||
slug: joinSegments(folder, "index") as FullSlug,
|
||||
frontmatter: { title: `Folder: ${folder}`, tags: [] },
|
||||
frontmatter: {
|
||||
title: `${i18n(cfg.locale).pages.folderContent.folder}: ${folder}`,
|
||||
tags: [],
|
||||
},
|
||||
}),
|
||||
]),
|
||||
)
|
||||
@ -81,8 +86,9 @@ export const FolderPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
|
||||
allFiles,
|
||||
}
|
||||
|
||||
const content = renderPage(slug, componentData, opts, externalResources)
|
||||
const fp = await emit({
|
||||
const content = renderPage(cfg, slug, componentData, opts, externalResources)
|
||||
const fp = await write({
|
||||
ctx,
|
||||
content,
|
||||
slug,
|
||||
ext: ".html",
|
||||
|
19
quartz/plugins/emitters/helpers.ts
Normal file
19
quartz/plugins/emitters/helpers.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import path from "path"
|
||||
import fs from "fs"
|
||||
import { BuildCtx } from "../../util/ctx"
|
||||
import { FilePath, FullSlug, joinSegments } from "../../util/path"
|
||||
|
||||
type WriteOptions = {
|
||||
ctx: BuildCtx
|
||||
slug: FullSlug
|
||||
ext: `.${string}` | ""
|
||||
content: string
|
||||
}
|
||||
|
||||
export const write = async ({ ctx, slug, ext, content }: WriteOptions): Promise<FilePath> => {
|
||||
const pathToPage = joinSegments(ctx.argv.output, slug + ext) as FilePath
|
||||
const dir = path.dirname(pathToPage)
|
||||
await fs.promises.mkdir(dir, { recursive: true })
|
||||
await fs.promises.writeFile(pathToPage, content)
|
||||
return pathToPage
|
||||
}
|
@ -8,7 +8,7 @@ export const Static: QuartzEmitterPlugin = () => ({
|
||||
getQuartzComponents() {
|
||||
return []
|
||||
},
|
||||
async emit({ argv, cfg }, _content, _resources, _emit): Promise<FilePath[]> {
|
||||
async emit({ argv, cfg }, _content, _resources): Promise<FilePath[]> {
|
||||
const staticPath = joinSegments(QUARTZ, "static")
|
||||
const fps = await glob("**", staticPath, cfg.configuration.ignorePatterns)
|
||||
await fs.promises.cp(staticPath, joinSegments(argv.output, "static"), {
|
||||
|
@ -14,8 +14,10 @@ import {
|
||||
} from "../../util/path"
|
||||
import { defaultListPageLayout, sharedPageComponents } from "../../../quartz.layout"
|
||||
import { TagContent } from "../../components"
|
||||
import { write } from "./helpers"
|
||||
import { i18n } from "../../i18n"
|
||||
|
||||
export const TagPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
|
||||
export const TagPage: QuartzEmitterPlugin<Partial<FullPageLayout>> = (userOpts) => {
|
||||
const opts: FullPageLayout = {
|
||||
...sharedPageComponents,
|
||||
...defaultListPageLayout,
|
||||
@ -32,7 +34,7 @@ export const TagPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
|
||||
getQuartzComponents() {
|
||||
return [Head, Header, Body, ...header, ...beforeBody, pageBody, ...left, ...right, Footer]
|
||||
},
|
||||
async emit(ctx, content, resources, emit): Promise<FilePath[]> {
|
||||
async emit(ctx, content, resources): Promise<FilePath[]> {
|
||||
const fps: FilePath[] = []
|
||||
const allFiles = content.map((c) => c[1].data)
|
||||
const cfg = ctx.cfg.configuration
|
||||
@ -46,7 +48,10 @@ export const TagPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
|
||||
|
||||
const tagDescriptions: Record<string, ProcessedContent> = Object.fromEntries(
|
||||
[...tags].map((tag) => {
|
||||
const title = tag === "index" ? "Tag Index" : `Tag: #${tag}`
|
||||
const title =
|
||||
tag === "index"
|
||||
? i18n(cfg.locale).pages.tagContent.tagIndex
|
||||
: `${i18n(cfg.locale).pages.tagContent.tag}: #${tag}`
|
||||
return [
|
||||
tag,
|
||||
defaultProcessedContent({
|
||||
@ -80,8 +85,9 @@ export const TagPage: QuartzEmitterPlugin<FullPageLayout> = (userOpts) => {
|
||||
allFiles,
|
||||
}
|
||||
|
||||
const content = renderPage(slug, componentData, opts, externalResources)
|
||||
const fp = await emit({
|
||||
const content = renderPage(cfg, slug, componentData, opts, externalResources)
|
||||
const fp = await write({
|
||||
ctx,
|
||||
content,
|
||||
slug: file.data.slug!,
|
||||
ext: ".html",
|
||||
|
Reference in New Issue
Block a user