feat: split and hash inlined assets

This commit is contained in:
saberzero1
2026-05-22 23:03:39 +02:00
parent cfba672f51
commit cfcd9c18ac
3 changed files with 85 additions and 4 deletions

View File

@@ -1,7 +1,12 @@
import { render } from "preact-render-to-string" import { render } from "preact-render-to-string"
import { QuartzComponent, QuartzComponentProps } from "./types" import { QuartzComponent, QuartzComponentProps } from "./types"
import BodyConstructor from "./Body" 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 { FullSlug, RelativeURL, joinSegments, normalizeHastElement } from "../util/path"
import { clone } from "../util/clone" import { clone } from "../util/clone"
import { visit } from "unist-util-visit" 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 contentIndexPath = joinSegments(baseDir, "static/contentIndex.json")
const contentIndexScript = `const fetchData = fetch("${contentIndexPath}").then(data => data.json())` const contentIndexScript = `const fetchData = fetch("${contentIndexPath}").then(data => data.json())`
@@ -55,7 +81,7 @@ export function pageResources(
content: joinSegments(baseDir, cssFile), content: joinSegments(baseDir, cssFile),
}, },
...componentCssResources, ...componentCssResources,
...staticResources.css, ...resolvedCss,
], ],
js: [ js: [
{ {
@@ -69,7 +95,7 @@ export function pageResources(
spaPreserve: true, spaPreserve: true,
script: contentIndexScript, script: contentIndexScript,
}, },
...staticResources.js, ...resolvedJs,
], ],
additionalHead: staticResources.additionalHead, additionalHead: staticResources.additionalHead,
} }

View File

@@ -275,7 +275,7 @@ function addGlobalPageResources(ctx: BuildCtx, componentResources: ComponentReso
export const ComponentResources: QuartzEmitterPlugin = () => { export const ComponentResources: QuartzEmitterPlugin = () => {
return { return {
name: "ComponentResources", name: "ComponentResources",
async *emit(ctx, _content, _resources) { async *emit(ctx, _content, resources) {
const cfg = ctx.cfg.configuration const cfg = ctx.cfg.configuration
// component specific scripts and styles // component specific scripts and styles
const componentResources = getComponentResources(ctx) const componentResources = getComponentResources(ctx)
@@ -395,6 +395,59 @@ export const ComponentResources: QuartzEmitterPlugin = () => {
ctx.componentCssMap = cssStringToFilename 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 <head>.
const extractedInlineResources = new Map<string, string>()
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 cssHash = useHashing ? hashContent(cssContent) : null
const prescriptHash = useHashing ? hashContent(prescript) : null const prescriptHash = useHashing ? hashContent(prescript) : null
const postscriptHash = useHashing ? hashContent(postscript) : null const postscriptHash = useHashing ? hashContent(postscript) : null

View File

@@ -42,6 +42,8 @@ export interface BuildCtx {
hashedResourceNames?: HashedResourceNames hashedResourceNames?: HashedResourceNames
/** Maps CSS content strings to their emitted hashed filenames. Populated by ComponentResources. */ /** Maps CSS content strings to their emitted hashed filenames. Populated by ComponentResources. */
componentCssMap?: Map<string, string> componentCssMap?: Map<string, string>
/** Maps inline CSS/JS content to extracted external file paths. Populated by ComponentResources. */
extractedInlineResources?: Map<string, string>
} }
export function trieFromAllFiles(allFiles: QuartzPluginData[]): FileTrieNode<BuildTimeTrieData> { export function trieFromAllFiles(allFiles: QuartzPluginData[]): FileTrieNode<BuildTimeTrieData> {