From cfcd9c18acf8a6630aed6406f19a23037bfcddda Mon Sep 17 00:00:00 2001 From: saberzero1 Date: Fri, 22 May 2026 23:03:39 +0200 Subject: [PATCH] feat: split and hash inlined assets --- quartz/components/renderPage.tsx | 32 ++++++++++- quartz/plugins/emitters/componentResources.ts | 55 ++++++++++++++++++- quartz/util/ctx.ts | 2 + 3 files changed, 85 insertions(+), 4 deletions(-) diff --git a/quartz/components/renderPage.tsx b/quartz/components/renderPage.tsx index aad75a3..83beff5 100644 --- a/quartz/components/renderPage.tsx +++ b/quartz/components/renderPage.tsx @@ -1,7 +1,12 @@ import { render } from "preact-render-to-string" import { QuartzComponent, QuartzComponentProps } from "./types" import BodyConstructor from "./Body" -import { CSSResource, JSResourceToScriptElement, StaticResources } from "../util/resources" +import { + CSSResource, + JSResource, + JSResourceToScriptElement, + StaticResources, +} from "../util/resources" import { FullSlug, RelativeURL, joinSegments, normalizeHastElement } from "../util/path" import { clone } from "../util/clone" import { visit } from "unist-util-visit" @@ -46,6 +51,27 @@ export function pageResources( } } + const extracted = ctx?.extractedInlineResources + const resolvedCss: CSSResource[] = staticResources.css.map((resource) => { + if (!(resource.inline ?? false) || !extracted) return resource + const filename = extracted.get(resource.content) + if (!filename) return resource + return { content: joinSegments(baseDir, filename) } + }) + + const resolvedJs: JSResource[] = staticResources.js.map((resource) => { + if (resource.contentType !== "inline" || !extracted) return resource + const filename = extracted.get(resource.script) + if (!filename) return resource + return { + src: joinSegments(baseDir, filename), + loadTime: resource.loadTime, + contentType: "external" as const, + moduleType: resource.moduleType, + spaPreserve: resource.spaPreserve, + } + }) + const contentIndexPath = joinSegments(baseDir, "static/contentIndex.json") const contentIndexScript = `const fetchData = fetch("${contentIndexPath}").then(data => data.json())` @@ -55,7 +81,7 @@ export function pageResources( content: joinSegments(baseDir, cssFile), }, ...componentCssResources, - ...staticResources.css, + ...resolvedCss, ], js: [ { @@ -69,7 +95,7 @@ export function pageResources( spaPreserve: true, script: contentIndexScript, }, - ...staticResources.js, + ...resolvedJs, ], additionalHead: staticResources.additionalHead, } diff --git a/quartz/plugins/emitters/componentResources.ts b/quartz/plugins/emitters/componentResources.ts index 9a44496..4bd6a37 100644 --- a/quartz/plugins/emitters/componentResources.ts +++ b/quartz/plugins/emitters/componentResources.ts @@ -275,7 +275,7 @@ function addGlobalPageResources(ctx: BuildCtx, componentResources: ComponentReso export const ComponentResources: QuartzEmitterPlugin = () => { return { name: "ComponentResources", - async *emit(ctx, _content, _resources) { + async *emit(ctx, _content, resources) { const cfg = ctx.cfg.configuration // component specific scripts and styles const componentResources = getComponentResources(ctx) @@ -395,6 +395,59 @@ export const ComponentResources: QuartzEmitterPlugin = () => { ctx.componentCssMap = cssStringToFilename + // Extract inline CSS/JS from plugin externalResources() into external files. + // This prevents large inline payloads (e.g. theme CSS) from being duplicated + // into every HTML page's . + const extractedInlineResources = new Map() + for (const cssResource of resources.css) { + if (!(cssResource.inline ?? false)) continue + + let output: string + try { + output = transform({ + filename: "plugin-resource.css", + code: Buffer.from(cssResource.content), + minify: true, + targets: lightningTargets, + include: Features.MediaQueries, + }).code.toString() + } catch { + output = cssResource.content + } + + const hash = hashContent(output) + const slug = `static/resource-style-${hash}` + const filename = `${slug}.css` + extractedInlineResources.set(cssResource.content, filename) + + yield write({ + ctx, + slug: slug as FullSlug, + ext: ".css", + content: output, + }) + } + + for (const jsResource of resources.js) { + if (jsResource.contentType !== "inline") continue + + const minified = await joinScripts([jsResource.script]) + const hash = hashContent(minified) + const loadTimePrefix = jsResource.loadTime === "beforeDOMReady" ? "before" : "after" + const slug = `static/resource-${loadTimePrefix}-${hash}` + const filename = `${slug}.js` + extractedInlineResources.set(jsResource.script, filename) + + yield write({ + ctx, + slug: slug as FullSlug, + ext: ".js", + content: minified, + }) + } + + ctx.extractedInlineResources = extractedInlineResources + const cssHash = useHashing ? hashContent(cssContent) : null const prescriptHash = useHashing ? hashContent(prescript) : null const postscriptHash = useHashing ? hashContent(postscript) : null diff --git a/quartz/util/ctx.ts b/quartz/util/ctx.ts index 00fd15f..20f2b31 100644 --- a/quartz/util/ctx.ts +++ b/quartz/util/ctx.ts @@ -42,6 +42,8 @@ export interface BuildCtx { hashedResourceNames?: HashedResourceNames /** Maps CSS content strings to their emitted hashed filenames. Populated by ComponentResources. */ componentCssMap?: Map + /** Maps inline CSS/JS content to extracted external file paths. Populated by ComponentResources. */ + extractedInlineResources?: Map } export function trieFromAllFiles(allFiles: QuartzPluginData[]): FileTrieNode {