feat: support emitters defining external resources, emit link from contentindex directly

This commit is contained in:
Jacky Zhao 2025-03-05 18:16:17 -08:00
parent 5a39719898
commit 5b13ff2199
8 changed files with 34 additions and 8 deletions

View file

@ -99,8 +99,6 @@ export const Latex: QuartzTransformerPlugin<Options> = (opts?: Options) => {
}, },
], ],
} }
} else {
return {}
} }
}, },
} }

View file

@ -127,7 +127,7 @@ export default (() => {
} }
} }
const { css, js } = externalResources const { css, js, additionalHead } = externalResources
const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`) const url = new URL(`https://${cfg.baseUrl ?? "example.com"}`)
const path = url.pathname as FullSlug const path = url.pathname as FullSlug
@ -177,7 +177,7 @@ export default (() => {
<link rel="stylesheet" href={googleFontHref(cfg.theme)} /> <link rel="stylesheet" href={googleFontHref(cfg.theme)} />
</> </>
)} )}
<link rel="preconnect" href="https://cdnjs.cloudflare.com" crossOrigin={"anonymous"} /> <link rel="preconnect" href="https://cdnjs.cloudflare.com" crossOrigin="anonymous" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
{/* OG/Twitter meta tags */} {/* OG/Twitter meta tags */}
<meta name="og:site_name" content={cfg.pageTitle}></meta> <meta name="og:site_name" content={cfg.pageTitle}></meta>
@ -213,6 +213,13 @@ export default (() => {
{js {js
.filter((resource) => resource.loadTime === "beforeDOMReady") .filter((resource) => resource.loadTime === "beforeDOMReady")
.map((res) => JSResourceToScriptElement(res, true))} .map((res) => JSResourceToScriptElement(res, true))}
{additionalHead.map((resource) => {
if (typeof resource === "function") {
return resource(fileData)
} else {
return resource
}
})}
</head> </head>
) )
} }

View file

@ -54,6 +54,7 @@ export function pageResources(
}, },
...staticResources.js, ...staticResources.js,
], ],
additionalHead: staticResources.additionalHead,
} }
if (fileData.hasMermaidDiagram) { if (fileData.hasMermaidDiagram) {

View file

@ -182,6 +182,20 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
return emitted return emitted
}, },
externalResources: (ctx) => {
if (opts?.enableRSS) {
return {
additionalHead: [
<link
rel="alternate"
type="application/rss+xml"
title="RSS Feed"
href={`https://${ctx.cfg.configuration.baseUrl}/index.xml`}
/>,
],
}
}
},
getQuartzComponents: () => [], getQuartzComponents: () => [],
} }
} }

View file

@ -6,9 +6,10 @@ export function getStaticResourcesFromPlugins(ctx: BuildCtx) {
const staticResources: StaticResources = { const staticResources: StaticResources = {
css: [], css: [],
js: [], js: [],
additionalHead: [],
} }
for (const transformer of ctx.cfg.plugins.transformers) { for (const transformer of [...ctx.cfg.plugins.transformers, ...ctx.cfg.plugins.emitters]) {
const res = transformer.externalResources ? transformer.externalResources(ctx) : {} const res = transformer.externalResources ? transformer.externalResources(ctx) : {}
if (res?.js) { if (res?.js) {
staticResources.js.push(...res.js) staticResources.js.push(...res.js)
@ -16,6 +17,9 @@ export function getStaticResourcesFromPlugins(ctx: BuildCtx) {
if (res?.css) { if (res?.css) {
staticResources.css.push(...res.css) staticResources.css.push(...res.css)
} }
if (res?.additionalHead) {
staticResources.additionalHead.push(...res.additionalHead)
}
} }
// if serving locally, listen for rebuilds and reload the page // if serving locally, listen for rebuilds and reload the page

View file

@ -59,8 +59,6 @@ export const Latex: QuartzTransformerPlugin<Partial<Options>> = (opts) => {
}, },
], ],
} }
default:
return { css: [], js: [] }
} }
}, },
} }

View file

@ -13,6 +13,7 @@ export interface PluginTypes {
} }
type OptionType = object | undefined type OptionType = object | undefined
type ExternalResourcesFn = (ctx: BuildCtx) => Partial<StaticResources> | undefined
export type QuartzTransformerPlugin<Options extends OptionType = undefined> = ( export type QuartzTransformerPlugin<Options extends OptionType = undefined> = (
opts?: Options, opts?: Options,
) => QuartzTransformerPluginInstance ) => QuartzTransformerPluginInstance
@ -21,7 +22,7 @@ export type QuartzTransformerPluginInstance = {
textTransform?: (ctx: BuildCtx, src: string) => string textTransform?: (ctx: BuildCtx, src: string) => string
markdownPlugins?: (ctx: BuildCtx) => PluggableList markdownPlugins?: (ctx: BuildCtx) => PluggableList
htmlPlugins?: (ctx: BuildCtx) => PluggableList htmlPlugins?: (ctx: BuildCtx) => PluggableList
externalResources?: (ctx: BuildCtx) => Partial<StaticResources> externalResources?: ExternalResourcesFn
} }
export type QuartzFilterPlugin<Options extends OptionType = undefined> = ( export type QuartzFilterPlugin<Options extends OptionType = undefined> = (
@ -44,4 +45,5 @@ export type QuartzEmitterPluginInstance = {
content: ProcessedContent[], content: ProcessedContent[],
resources: StaticResources, resources: StaticResources,
): Promise<DepGraph<FilePath>> ): Promise<DepGraph<FilePath>>
externalResources?: ExternalResourcesFn
} }

View file

@ -1,5 +1,6 @@
import { randomUUID } from "crypto" import { randomUUID } from "crypto"
import { JSX } from "preact/jsx-runtime" import { JSX } from "preact/jsx-runtime"
import { QuartzPluginData } from "../plugins/vfile"
export type JSResource = { export type JSResource = {
loadTime: "beforeDOMReady" | "afterDOMReady" loadTime: "beforeDOMReady" | "afterDOMReady"
@ -62,4 +63,5 @@ export function CSSResourceToStyleElement(resource: CSSResource, preserve?: bool
export interface StaticResources { export interface StaticResources {
css: CSSResource[] css: CSSResource[]
js: JSResource[] js: JSResource[]
additionalHead: (JSX.Element | ((pageData: QuartzPluginData) => JSX.Element))[]
} }