From fb6ece04787a84c00f4b476a8a78e6b0730cd9a5 Mon Sep 17 00:00:00 2001 From: saberzero1 Date: Wed, 10 Jun 2026 23:00:41 +0200 Subject: [PATCH] feat: allow self-hosted fonts --- dist/emitter.d.ts | 6 ++ dist/emitter.js | 147 ++++++++++++++++++++++++++++++++++++++ dist/emitter.js.map | 1 + dist/index.d.ts | 5 +- dist/index.js | 91 ++++++++++++++++++++++- dist/index.js.map | 2 +- dist/types.d.ts | 14 +++- package.json | 8 ++- src/emitter.ts | 76 ++++++++++++++++++++ src/index.ts | 5 +- src/transformer.ts | 8 ++- src/types.ts | 14 +++- src/util/process-fonts.ts | 27 +++++++ test/transformer.test.ts | 75 +++++++++++++++++++ tsup.config.ts | 1 + 15 files changed, 467 insertions(+), 13 deletions(-) create mode 100644 dist/emitter.d.ts create mode 100644 dist/emitter.js create mode 100644 dist/emitter.js.map create mode 100644 src/emitter.ts create mode 100644 src/util/process-fonts.ts diff --git a/dist/emitter.d.ts b/dist/emitter.d.ts new file mode 100644 index 0000000..3d70e1e --- /dev/null +++ b/dist/emitter.d.ts @@ -0,0 +1,6 @@ +import { QuartzEmitterPlugin } from '@quartz-community/types'; +import { QuartzFontsOptions } from './types.js'; + +declare const QuartzFontsEmitter: QuartzEmitterPlugin>; + +export { QuartzFontsEmitter }; diff --git a/dist/emitter.js b/dist/emitter.js new file mode 100644 index 0000000..7ff91f8 --- /dev/null +++ b/dist/emitter.js @@ -0,0 +1,147 @@ +import { createRequire } from 'module'; +import fs from 'fs'; +import path from 'path'; + +createRequire(import.meta.url); + +// src/defaults.ts +var DEFAULT_WEIGHTS = { + title: [400, 700], + header: [400, 700], + body: [400, 600], + code: [400, 600] +}; +var DEFAULT_ITALIC = { + title: false, + header: false, + body: true, + code: false +}; + +// src/util/google-fonts.ts +function normalizeFontSpec(spec) { + return typeof spec === "string" ? { name: spec } : spec; +} +function mergeInto(map, role, spec) { + const { name, weights, includeItalic } = normalizeFontSpec(spec); + const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role]; + const italic = includeItalic ?? DEFAULT_ITALIC[role]; + const existing = map.get(name); + if (existing) { + for (const w of resolvedWeights) existing.weights.add(w); + if (italic) existing.italic = true; + } else { + map.set(name, { name, weights: new Set(resolvedWeights), italic }); + } +} +function formatMergedEntry(entry) { + const sortedWeights = [...entry.weights].sort((a, b) => a - b); + const features = []; + if (entry.italic) { + features.push("ital"); + } + if (sortedWeights.length > 1) { + const weightSpec = entry.italic ? sortedWeights.flatMap((w) => [`0,${w}`, `1,${w}`]).sort().join(";") : sortedWeights.join(";"); + features.push(`wght@${weightSpec}`); + } + if (features.length > 0) { + return `${entry.name}:${features.join(",")}`; + } + return entry.name; +} +function googleFontHref(fonts) { + const merged = /* @__PURE__ */ new Map(); + mergeInto(merged, "header", fonts.header); + mergeInto(merged, "body", fonts.body); + mergeInto(merged, "code", fonts.code); + if (fonts.title) { + mergeInto(merged, "title", fonts.title); + } + const families = [...merged.values()].map(formatMergedEntry); + const params = families.map((f) => `family=${encodeURIComponent(f)}`).join("&"); + return `https://fonts.googleapis.com/css2?${params}&display=swap`; +} + +// src/util/process-fonts.ts +var fontMimeMap = { + truetype: "ttf", + woff: "woff", + woff2: "woff2", + opentype: "otf" +}; +var fontUrlPattern = /url\((https:\/\/fonts.gstatic.com\/.+(?:\/|(?:kit=))(.+?)[.&].+?)\)\sformat\('(\w+?)'\);/g; +function processGoogleFonts(stylesheet, baseUrl) { + const fontFiles = []; + const processedStylesheet = stylesheet.replace( + fontUrlPattern, + (match, url, filename, format) => { + const extension = fontMimeMap[format] ?? format; + const rewrittenUrl = `https://${baseUrl}/static/fonts/${filename}.${extension}`; + fontFiles.push({ url, filename, extension }); + return match.replace(url, rewrittenUrl); + } + ); + return { processedStylesheet, fontFiles }; +} + +// src/emitter.ts +var QUARTZ_DEFAULT_HEADER = "Schibsted Grotesk"; +var QUARTZ_DEFAULT_BODY = "Source Sans Pro"; +var QUARTZ_DEFAULT_CODE = "IBM Plex Mono"; +var defaultOptions = { + useThemeFonts: true, + fontOrigin: "googleFonts" +}; +var QuartzFontsEmitter = (userOptions) => { + const options = { ...defaultOptions, ...userOptions }; + return { + name: "QuartzFontsEmitter", + async *emit(ctx, _content, _resources) { + if (options.fontOrigin !== "selfHosted") { + return; + } + const baseUrl = ctx.cfg.configuration.baseUrl; + if (!baseUrl) { + throw new Error("[QuartzFontsEmitter] baseUrl is required for selfHosted fonts."); + } + const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER; + const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY; + const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE; + const href = googleFontHref({ + title: options.title, + header: headerSpec, + body: bodySpec, + code: codeSpec + }); + const cssResponse = await fetch(href); + if (!cssResponse.ok) { + throw new Error( + `[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}` + ); + } + const cssText = await cssResponse.text(); + const { processedStylesheet, fontFiles } = processGoogleFonts(cssText, baseUrl); + const fontsDir = path.join(ctx.argv.output, "static", "fonts"); + await fs.promises.mkdir(fontsDir, { recursive: true }); + for (const fontFile of fontFiles) { + const fontResponse = await fetch(fontFile.url); + if (!fontResponse.ok) { + throw new Error( + `[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})` + ); + } + const buf = await fontResponse.arrayBuffer(); + const filePath = path.join(fontsDir, `${fontFile.filename}.${fontFile.extension}`); + await fs.promises.writeFile(filePath, Buffer.from(buf)); + yield filePath; + } + const cssPath = path.join(fontsDir, "quartz-fonts.css"); + await fs.promises.writeFile(cssPath, processedStylesheet); + yield cssPath; + } + }; +}; + +export { QuartzFontsEmitter }; +//# sourceMappingURL=emitter.js.map +//# sourceMappingURL=emitter.js.map \ No newline at end of file diff --git a/dist/emitter.js.map b/dist/emitter.js.map new file mode 100644 index 0000000..308d6ec --- /dev/null +++ b/dist/emitter.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["../src/defaults.ts","../src/util/google-fonts.ts","../src/util/process-fonts.ts","../src/emitter.ts"],"names":[],"mappings":"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","file":"emitter.js","sourcesContent":["export const OBSIDIAN_SANS_STACK =\n 'ui-sans-serif, -apple-system, BlinkMacSystemFont, system-ui, \"Segoe UI\", Roboto, \"Inter\", \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", sans-serif';\n\nexport const OBSIDIAN_MONO_STACK =\n 'ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Roboto Mono\", \"DejaVu Sans Mono\", \"Liberation Mono\", Menlo, Monaco, \"Consolas\", \"Source Code Pro\", monospace';\n\nexport type FontRole = \"title\" | \"header\" | \"body\" | \"code\";\n\nexport const DEFAULT_WEIGHTS: Record = {\n title: [400, 700],\n header: [400, 700],\n body: [400, 600],\n code: [400, 600],\n};\n\nexport const DEFAULT_ITALIC: Record = {\n title: false,\n header: false,\n body: true,\n code: false,\n};\n","import type { FontSpecification } from \"../types\";\nimport type { FontRole } from \"../defaults\";\nimport { DEFAULT_WEIGHTS, DEFAULT_ITALIC } from \"../defaults\";\n\nfunction normalizeFontSpec(spec: FontSpecification): {\n name: string;\n weights?: number[];\n includeItalic?: boolean;\n} {\n return typeof spec === \"string\" ? { name: spec } : spec;\n}\n\nexport function getFontName(spec: FontSpecification): string {\n return typeof spec === \"string\" ? spec : spec.name;\n}\n\nexport function formatFontSpecification(role: FontRole, spec: FontSpecification): string {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const features: string[] = [];\n if (italic) {\n features.push(\"ital\");\n }\n\n if (resolvedWeights.length > 1) {\n const weightSpec = italic\n ? resolvedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : resolvedWeights.join(\";\");\n\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${name}:${features.join(\",\")}`;\n }\n\n return name;\n}\n\ninterface MergedFontEntry {\n name: string;\n weights: Set;\n italic: boolean;\n}\n\nfunction mergeInto(\n map: Map,\n role: FontRole,\n spec: FontSpecification,\n): void {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const existing = map.get(name);\n if (existing) {\n for (const w of resolvedWeights) existing.weights.add(w);\n if (italic) existing.italic = true;\n } else {\n map.set(name, { name, weights: new Set(resolvedWeights), italic });\n }\n}\n\nfunction formatMergedEntry(entry: MergedFontEntry): string {\n const sortedWeights = [...entry.weights].sort((a, b) => a - b);\n const features: string[] = [];\n\n if (entry.italic) {\n features.push(\"ital\");\n }\n\n if (sortedWeights.length > 1) {\n const weightSpec = entry.italic\n ? sortedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : sortedWeights.join(\";\");\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${entry.name}:${features.join(\",\")}`;\n }\n return entry.name;\n}\n\nexport function googleFontHref(fonts: {\n title?: FontSpecification;\n header: FontSpecification;\n body: FontSpecification;\n code: FontSpecification;\n}): string {\n const merged = new Map();\n\n mergeInto(merged, \"header\", fonts.header);\n mergeInto(merged, \"body\", fonts.body);\n mergeInto(merged, \"code\", fonts.code);\n\n if (fonts.title) {\n mergeInto(merged, \"title\", fonts.title);\n }\n\n const families = [...merged.values()].map(formatMergedEntry);\n const params = families.map((f) => `family=${encodeURIComponent(f)}`).join(\"&\");\n return `https://fonts.googleapis.com/css2?${params}&display=swap`;\n}\n","import type { ProcessedFontResult, GoogleFontFile } from \"../types\";\n\nconst fontMimeMap: Record = {\n truetype: \"ttf\",\n woff: \"woff\",\n woff2: \"woff2\",\n opentype: \"otf\",\n};\n\nconst fontUrlPattern =\n /url\\((https:\\/\\/fonts.gstatic.com\\/.+(?:\\/|(?:kit=))(.+?)[.&].+?)\\)\\sformat\\('(\\w+?)'\\);/g;\n\nexport function processGoogleFonts(stylesheet: string, baseUrl: string): ProcessedFontResult {\n const fontFiles: GoogleFontFile[] = [];\n\n const processedStylesheet = stylesheet.replace(\n fontUrlPattern,\n (match, url: string, filename: string, format: string) => {\n const extension = fontMimeMap[format] ?? format;\n const rewrittenUrl = `https://${baseUrl}/static/fonts/${filename}.${extension}`;\n fontFiles.push({ url, filename, extension });\n return match.replace(url, rewrittenUrl);\n },\n );\n\n return { processedStylesheet, fontFiles };\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport type { QuartzEmitterPlugin, BuildCtx, FilePath } from \"@quartz-community/types\";\nimport type { QuartzFontsOptions } from \"./types\";\nimport { googleFontHref } from \"./util/google-fonts\";\nimport { processGoogleFonts } from \"./util/process-fonts\";\n\nconst QUARTZ_DEFAULT_HEADER = \"Schibsted Grotesk\";\nconst QUARTZ_DEFAULT_BODY = \"Source Sans Pro\";\nconst QUARTZ_DEFAULT_CODE = \"IBM Plex Mono\";\n\nconst defaultOptions: QuartzFontsOptions = {\n useThemeFonts: true,\n fontOrigin: \"googleFonts\",\n};\n\nexport const QuartzFontsEmitter: QuartzEmitterPlugin> = (\n userOptions?: Partial,\n) => {\n const options: QuartzFontsOptions = { ...defaultOptions, ...userOptions };\n\n return {\n name: \"QuartzFontsEmitter\",\n async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator {\n if (options.fontOrigin !== \"selfHosted\") {\n return;\n }\n\n const baseUrl = ctx.cfg.configuration.baseUrl;\n if (!baseUrl) {\n throw new Error(\"[QuartzFontsEmitter] baseUrl is required for selfHosted fonts.\");\n }\n\n const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;\n const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;\n const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;\n\n const href = googleFontHref({\n title: options.title,\n header: headerSpec,\n body: bodySpec,\n code: codeSpec,\n });\n\n const cssResponse = await fetch(href);\n if (!cssResponse.ok) {\n throw new Error(\n `[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`,\n );\n }\n const cssText = await cssResponse.text();\n\n const { processedStylesheet, fontFiles } = processGoogleFonts(cssText, baseUrl);\n\n const fontsDir = path.join(ctx.argv.output, \"static\", \"fonts\");\n await fs.promises.mkdir(fontsDir, { recursive: true });\n\n for (const fontFile of fontFiles) {\n const fontResponse = await fetch(fontFile.url);\n if (!fontResponse.ok) {\n throw new Error(\n `[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`,\n );\n }\n const buf = await fontResponse.arrayBuffer();\n const filePath = path.join(fontsDir, `${fontFile.filename}.${fontFile.extension}`);\n await fs.promises.writeFile(filePath, Buffer.from(buf));\n yield filePath as FilePath;\n }\n\n const cssPath = path.join(fontsDir, \"quartz-fonts.css\");\n await fs.promises.writeFile(cssPath, processedStylesheet);\n yield cssPath as FilePath;\n },\n };\n};\n"]} \ No newline at end of file diff --git a/dist/index.d.ts b/dist/index.d.ts index e0353cd..6d4a5d7 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1,7 +1,8 @@ import { QuartzTransformerPlugin } from '@quartz-community/types'; -export { QuartzTransformerPlugin } from '@quartz-community/types'; +export { QuartzEmitterPlugin, QuartzTransformerPlugin } from '@quartz-community/types'; import { QuartzFontsOptions } from './types.js'; -export { FontFileEntry, FontSpecification, QuartzFontRegistry } from './types.js'; +export { FontFileEntry, FontSpecification, GoogleFontFile, ProcessedFontResult, QuartzFontRegistry } from './types.js'; +export { QuartzFontsEmitter } from './emitter.js'; declare const QuartzFonts: QuartzTransformerPlugin>; diff --git a/dist/index.js b/dist/index.js index 8d3ee71..ab89f51 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,4 +1,6 @@ import { createRequire } from 'module'; +import fs from 'fs'; +import path from 'path'; const require$1 = createRequire(import.meta.url); var __require = /* @__PURE__ */ ((x2) => typeof require$1 !== "undefined" ? require$1 : typeof Proxy !== "undefined" ? new Proxy(x2, { @@ -316,12 +318,97 @@ var QuartzFonts = (userOptions) => { { content: buildLayeredCSS(fonts), inline: true }, { content: buildUnlayeredCSS(fonts), inline: true } ]; - const additionalHead = options.fontOrigin === "googleFonts" ? buildGoogleFontsHead(options) : []; + let additionalHead = []; + if (options.fontOrigin === "googleFonts") { + additionalHead = buildGoogleFontsHead(options); + } else if (options.fontOrigin === "selfHosted") { + additionalHead = [_("link", { rel: "stylesheet", href: "/static/fonts/quartz-fonts.css" })]; + } return { css, js: [], additionalHead }; } }; }; -export { QuartzFonts }; +// src/util/process-fonts.ts +var fontMimeMap = { + truetype: "ttf", + woff: "woff", + woff2: "woff2", + opentype: "otf" +}; +var fontUrlPattern = /url\((https:\/\/fonts.gstatic.com\/.+(?:\/|(?:kit=))(.+?)[.&].+?)\)\sformat\('(\w+?)'\);/g; +function processGoogleFonts(stylesheet, baseUrl) { + const fontFiles = []; + const processedStylesheet = stylesheet.replace( + fontUrlPattern, + (match, url, filename, format) => { + const extension = fontMimeMap[format] ?? format; + const rewrittenUrl = `https://${baseUrl}/static/fonts/${filename}.${extension}`; + fontFiles.push({ url, filename, extension }); + return match.replace(url, rewrittenUrl); + } + ); + return { processedStylesheet, fontFiles }; +} + +// src/emitter.ts +var QUARTZ_DEFAULT_HEADER2 = "Schibsted Grotesk"; +var QUARTZ_DEFAULT_BODY2 = "Source Sans Pro"; +var QUARTZ_DEFAULT_CODE2 = "IBM Plex Mono"; +var defaultOptions2 = { + useThemeFonts: true, + fontOrigin: "googleFonts" +}; +var QuartzFontsEmitter = (userOptions) => { + const options = { ...defaultOptions2, ...userOptions }; + return { + name: "QuartzFontsEmitter", + async *emit(ctx, _content, _resources) { + if (options.fontOrigin !== "selfHosted") { + return; + } + const baseUrl = ctx.cfg.configuration.baseUrl; + if (!baseUrl) { + throw new Error("[QuartzFontsEmitter] baseUrl is required for selfHosted fonts."); + } + const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER2; + const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY2; + const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE2; + const href = googleFontHref({ + title: options.title, + header: headerSpec, + body: bodySpec, + code: codeSpec + }); + const cssResponse = await fetch(href); + if (!cssResponse.ok) { + throw new Error( + `[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}` + ); + } + const cssText = await cssResponse.text(); + const { processedStylesheet, fontFiles } = processGoogleFonts(cssText, baseUrl); + const fontsDir = path.join(ctx.argv.output, "static", "fonts"); + await fs.promises.mkdir(fontsDir, { recursive: true }); + for (const fontFile of fontFiles) { + const fontResponse = await fetch(fontFile.url); + if (!fontResponse.ok) { + throw new Error( + `[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})` + ); + } + const buf = await fontResponse.arrayBuffer(); + const filePath = path.join(fontsDir, `${fontFile.filename}.${fontFile.extension}`); + await fs.promises.writeFile(filePath, Buffer.from(buf)); + yield filePath; + } + const cssPath = path.join(fontsDir, "quartz-fonts.css"); + await fs.promises.writeFile(cssPath, processedStylesheet); + yield cssPath; + } + }; +}; + +export { QuartzFonts, QuartzFontsEmitter }; //# sourceMappingURL=index.js.map //# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/dist/index.js.map b/dist/index.js.map index 0156b89..f81b6c2 100644 --- a/dist/index.js.map +++ b/dist/index.js.map @@ -1 +1 @@ -{"version":3,"sources":["../node_modules/preact/src/constants.js","../node_modules/preact/src/util.js","../node_modules/preact/src/options.js","../node_modules/preact/src/create-element.js","../node_modules/preact/src/diff/catch-error.js","../node_modules/preact/src/component.js","../node_modules/preact/src/create-context.js","../src/defaults.ts","../src/util/registry.ts","../src/util/google-fonts.ts","../src/util/validate.ts","../src/transformer.ts"],"names":["slice","options","vnodeId","EMPTY_ARR","createElement","type","props","children","key","ref","i","normalizedProps","arguments","length","call","defaultProps","createVNode","original","vnode","__k","__","__b","__e","__c","constructor","__v","__i","__u","error","oldVNode","errorInfo","component","ctor","handled","getDerivedStateFromError","setState","__d","componentDidCatch","__E","e","Promise","prototype","then","bind","resolve","setTimeout","w","a","f"],"mappings":"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","file":"index.js","sourcesContent":["/** Normal hydration that attaches to a DOM tree but does not diff it. */\nexport const MODE_HYDRATE = 1 << 5;\n/** Signifies this VNode suspended on the previous render */\nexport const MODE_SUSPENDED = 1 << 7;\n/** Indicates that this node needs to be inserted while patching children */\nexport const INSERT_VNODE = 1 << 2;\n/** Indicates a VNode has been matched with another VNode in the diff */\nexport const MATCHED = 1 << 1;\n\n/** Reset all mode flags */\nexport const RESET_MODE = ~(MODE_HYDRATE | MODE_SUSPENDED);\n\nexport const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\nexport const XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\nexport const MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n\nexport const NULL = null;\nexport const UNDEFINED = undefined;\nexport const EMPTY_OBJ = /** @type {any} */ ({});\nexport const EMPTY_ARR = [];\nexport const IS_NON_DIMENSIONAL =\n\t/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;\n","import { EMPTY_ARR } from './constants';\n\nexport const isArray = Array.isArray;\n\n/**\n * Assign properties from `props` to `obj`\n * @template O, P The obj and props types\n * @param {O} obj The object to copy properties to\n * @param {P} props The object to copy properties from\n * @returns {O & P}\n */\nexport function assign(obj, props) {\n\t// @ts-expect-error We change the type of `obj` to be `O & P`\n\tfor (let i in props) obj[i] = props[i];\n\treturn /** @type {O & P} */ (obj);\n}\n\n/**\n * Remove a child node from its parent if attached. This is a workaround for\n * IE11 which doesn't support `Element.prototype.remove()`. Using this function\n * is smaller than including a dedicated polyfill.\n * @param {import('./index').ContainerNode} node The node to remove\n */\nexport function removeNode(node) {\n\tif (node && node.parentNode) node.parentNode.removeChild(node);\n}\n\nexport const slice = EMPTY_ARR.slice;\n","import { _catchError } from './diff/catch-error';\n\n/**\n * The `option` object can potentially contain callback functions\n * that are called during various stages of our renderer. This is the\n * foundation on which all our addons like `preact/debug`, `preact/compat`,\n * and `preact/hooks` are based on. See the `Options` type in `internal.d.ts`\n * for a full list of available option hooks (most editors/IDEs allow you to\n * ctrl+click or cmd+click on mac the type definition below).\n * @type {import('./internal').Options}\n */\nconst options = {\n\t_catchError\n};\n\nexport default options;\n","import { slice } from './util';\nimport options from './options';\nimport { NULL, UNDEFINED } from './constants';\n\nlet vnodeId = 0;\n\n/**\n * Create an virtual node (used for JSX)\n * @param {import('./internal').VNode[\"type\"]} type The node name or Component constructor for this\n * virtual node\n * @param {object | null | undefined} [props] The properties of the virtual node\n * @param {Array} [children] The children of the\n * virtual node\n * @returns {import('./internal').VNode}\n */\nexport function createElement(type, props, children) {\n\tlet normalizedProps = {},\n\t\tkey,\n\t\tref,\n\t\ti;\n\tfor (i in props) {\n\t\tif (i == 'key') key = props[i];\n\t\telse if (i == 'ref') ref = props[i];\n\t\telse normalizedProps[i] = props[i];\n\t}\n\n\tif (arguments.length > 2) {\n\t\tnormalizedProps.children =\n\t\t\targuments.length > 3 ? slice.call(arguments, 2) : children;\n\t}\n\n\t// If a Component VNode, check for and apply defaultProps\n\t// Note: type may be undefined in development, must never error here.\n\tif (typeof type == 'function' && type.defaultProps != NULL) {\n\t\tfor (i in type.defaultProps) {\n\t\t\tif (normalizedProps[i] === UNDEFINED) {\n\t\t\t\tnormalizedProps[i] = type.defaultProps[i];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn createVNode(type, normalizedProps, key, ref, NULL);\n}\n\n/**\n * Create a VNode (used internally by Preact)\n * @param {import('./internal').VNode[\"type\"]} type The node name or Component\n * Constructor for this virtual node\n * @param {object | string | number | null} props The properties of this virtual node.\n * If this virtual node represents a text node, this is the text of the node (string or number).\n * @param {string | number | null} key The key for this virtual node, used when\n * diffing it against its children\n * @param {import('./internal').VNode[\"ref\"]} ref The ref property that will\n * receive a reference to its created child\n * @returns {import('./internal').VNode}\n */\nexport function createVNode(type, props, key, ref, original) {\n\t// V8 seems to be better at detecting type shapes if the object is allocated from the same call site\n\t// Do not inline into createElement and coerceToVNode!\n\t/** @type {import('./internal').VNode} */\n\tconst vnode = {\n\t\ttype,\n\t\tprops,\n\t\tkey,\n\t\tref,\n\t\t_children: NULL,\n\t\t_parent: NULL,\n\t\t_depth: 0,\n\t\t_dom: NULL,\n\t\t_component: NULL,\n\t\tconstructor: UNDEFINED,\n\t\t_original: original == NULL ? ++vnodeId : original,\n\t\t_index: -1,\n\t\t_flags: 0\n\t};\n\n\t// Only invoke the vnode hook if this was *not* a direct copy:\n\tif (original == NULL && options.vnode != NULL) options.vnode(vnode);\n\n\treturn vnode;\n}\n\nexport function createRef() {\n\treturn { current: NULL };\n}\n\nexport function Fragment(props) {\n\treturn props.children;\n}\n\n/**\n * Check if a the argument is a valid Preact VNode.\n * @param {*} vnode\n * @returns {vnode is VNode}\n */\nexport const isValidElement = vnode =>\n\tvnode != NULL && vnode.constructor === UNDEFINED;\n","import { NULL } from '../constants';\n\n/**\n * Find the closest error boundary to a thrown error and call it\n * @param {object} error The thrown value\n * @param {import('../internal').VNode} vnode The vnode that threw the error that was caught (except\n * for unmounting when this parameter is the highest parent that was being\n * unmounted)\n * @param {import('../internal').VNode} [oldVNode]\n * @param {import('../internal').ErrorInfo} [errorInfo]\n */\nexport function _catchError(error, vnode, oldVNode, errorInfo) {\n\t/** @type {import('../internal').Component} */\n\tlet component,\n\t\t/** @type {import('../internal').ComponentType} */\n\t\tctor,\n\t\t/** @type {boolean} */\n\t\thandled;\n\n\tfor (; (vnode = vnode._parent); ) {\n\t\tif ((component = vnode._component) && !component._processingException) {\n\t\t\ttry {\n\t\t\t\tctor = component.constructor;\n\n\t\t\t\tif (ctor && ctor.getDerivedStateFromError != NULL) {\n\t\t\t\t\tcomponent.setState(ctor.getDerivedStateFromError(error));\n\t\t\t\t\thandled = component._dirty;\n\t\t\t\t}\n\n\t\t\t\tif (component.componentDidCatch != NULL) {\n\t\t\t\t\tcomponent.componentDidCatch(error, errorInfo || {});\n\t\t\t\t\thandled = component._dirty;\n\t\t\t\t}\n\n\t\t\t\t// This is an error boundary. Mark it as having bailed out, and whether it was mid-hydration.\n\t\t\t\tif (handled) {\n\t\t\t\t\treturn (component._pendingError = component);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\terror = e;\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow error;\n}\n","import { assign } from './util';\nimport { diff, commitRoot } from './diff/index';\nimport options from './options';\nimport { Fragment } from './create-element';\nimport { MODE_HYDRATE, NULL } from './constants';\n\n/**\n * Base Component class. Provides `setState()` and `forceUpdate()`, which\n * trigger rendering\n * @param {object} props The initial component props\n * @param {object} context The initial context from parent components'\n * getChildContext\n */\nexport function BaseComponent(props, context) {\n\tthis.props = props;\n\tthis.context = context;\n}\n\n/**\n * Update component state and schedule a re-render.\n * @this {import('./internal').Component}\n * @param {object | ((s: object, p: object) => object)} update A hash of state\n * properties to update with new values or a function that given the current\n * state and props returns a new partial state\n * @param {() => void} [callback] A function to be called once component state is\n * updated\n */\nBaseComponent.prototype.setState = function (update, callback) {\n\t// only clone state when copying to nextState the first time.\n\tlet s;\n\tif (this._nextState != NULL && this._nextState != this.state) {\n\t\ts = this._nextState;\n\t} else {\n\t\ts = this._nextState = assign({}, this.state);\n\t}\n\n\tif (typeof update == 'function') {\n\t\t// Some libraries like `immer` mark the current state as readonly,\n\t\t// preventing us from mutating it, so we need to clone it. See #2716\n\t\tupdate = update(assign({}, s), this.props);\n\t}\n\n\tif (update) {\n\t\tassign(s, update);\n\t}\n\n\t// Skip update if updater function returned null\n\tif (update == NULL) return;\n\n\tif (this._vnode) {\n\t\tif (callback) {\n\t\t\tthis._stateCallbacks.push(callback);\n\t\t}\n\t\tenqueueRender(this);\n\t}\n};\n\n/**\n * Immediately perform a synchronous re-render of the component\n * @this {import('./internal').Component}\n * @param {() => void} [callback] A function to be called after component is\n * re-rendered\n */\nBaseComponent.prototype.forceUpdate = function (callback) {\n\tif (this._vnode) {\n\t\t// Set render mode so that we can differentiate where the render request\n\t\t// is coming from. We need this because forceUpdate should never call\n\t\t// shouldComponentUpdate\n\t\tthis._force = true;\n\t\tif (callback) this._renderCallbacks.push(callback);\n\t\tenqueueRender(this);\n\t}\n};\n\n/**\n * Accepts `props` and `state`, and returns a new Virtual DOM tree to build.\n * Virtual DOM is generally constructed via [JSX](https://jasonformat.com/wtf-is-jsx).\n * @param {object} props Props (eg: JSX attributes) received from parent\n * element/component\n * @param {object} state The component's current state\n * @param {object} context Context object, as returned by the nearest\n * ancestor's `getChildContext()`\n * @returns {ComponentChildren | void}\n */\nBaseComponent.prototype.render = Fragment;\n\n/**\n * @param {import('./internal').VNode} vnode\n * @param {number | null} [childIndex]\n */\nexport function getDomSibling(vnode, childIndex) {\n\tif (childIndex == NULL) {\n\t\t// Use childIndex==null as a signal to resume the search from the vnode's sibling\n\t\treturn vnode._parent\n\t\t\t? getDomSibling(vnode._parent, vnode._index + 1)\n\t\t\t: NULL;\n\t}\n\n\tlet sibling;\n\tfor (; childIndex < vnode._children.length; childIndex++) {\n\t\tsibling = vnode._children[childIndex];\n\n\t\tif (sibling != NULL && sibling._dom != NULL) {\n\t\t\t// Since updateParentDomPointers keeps _dom pointer correct,\n\t\t\t// we can rely on _dom to tell us if this subtree contains a\n\t\t\t// rendered DOM node, and what the first rendered DOM node is\n\t\t\treturn sibling._dom;\n\t\t}\n\t}\n\n\t// If we get here, we have not found a DOM node in this vnode's children.\n\t// We must resume from this vnode's sibling (in it's parent _children array)\n\t// Only climb up and search the parent if we aren't searching through a DOM\n\t// VNode (meaning we reached the DOM parent of the original vnode that began\n\t// the search)\n\treturn typeof vnode.type == 'function' ? getDomSibling(vnode) : NULL;\n}\n\n/**\n * Trigger in-place re-rendering of a component.\n * @param {import('./internal').Component} component The component to rerender\n */\nfunction renderComponent(component) {\n\tlet oldVNode = component._vnode,\n\t\toldDom = oldVNode._dom,\n\t\tcommitQueue = [],\n\t\trefQueue = [];\n\n\tif (component._parentDom) {\n\t\tconst newVNode = assign({}, oldVNode);\n\t\tnewVNode._original = oldVNode._original + 1;\n\t\tif (options.vnode) options.vnode(newVNode);\n\n\t\tdiff(\n\t\t\tcomponent._parentDom,\n\t\t\tnewVNode,\n\t\t\toldVNode,\n\t\t\tcomponent._globalContext,\n\t\t\tcomponent._parentDom.namespaceURI,\n\t\t\toldVNode._flags & MODE_HYDRATE ? [oldDom] : NULL,\n\t\t\tcommitQueue,\n\t\t\toldDom == NULL ? getDomSibling(oldVNode) : oldDom,\n\t\t\t!!(oldVNode._flags & MODE_HYDRATE),\n\t\t\trefQueue\n\t\t);\n\n\t\tnewVNode._original = oldVNode._original;\n\t\tnewVNode._parent._children[newVNode._index] = newVNode;\n\t\tcommitRoot(commitQueue, newVNode, refQueue);\n\t\toldVNode._dom = oldVNode._parent = null;\n\n\t\tif (newVNode._dom != oldDom) {\n\t\t\tupdateParentDomPointers(newVNode);\n\t\t}\n\t}\n}\n\n/**\n * @param {import('./internal').VNode} vnode\n */\nfunction updateParentDomPointers(vnode) {\n\tif ((vnode = vnode._parent) != NULL && vnode._component != NULL) {\n\t\tvnode._dom = vnode._component.base = NULL;\n\t\tfor (let i = 0; i < vnode._children.length; i++) {\n\t\t\tlet child = vnode._children[i];\n\t\t\tif (child != NULL && child._dom != NULL) {\n\t\t\t\tvnode._dom = vnode._component.base = child._dom;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn updateParentDomPointers(vnode);\n\t}\n}\n\n/**\n * The render queue\n * @type {Array}\n */\nlet rerenderQueue = [];\n\n/*\n * The value of `Component.debounce` must asynchronously invoke the passed in callback. It is\n * important that contributors to Preact can consistently reason about what calls to `setState`, etc.\n * do, and when their effects will be applied. See the links below for some further reading on designing\n * asynchronous APIs.\n * * [Designing APIs for Asynchrony](https://blog.izs.me/2013/08/designing-apis-for-asynchrony)\n * * [Callbacks synchronous and asynchronous](https://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/)\n */\n\nlet prevDebounce;\n\nconst defer =\n\ttypeof Promise == 'function'\n\t\t? Promise.prototype.then.bind(Promise.resolve())\n\t\t: setTimeout;\n\n/**\n * Enqueue a rerender of a component\n * @param {import('./internal').Component} c The component to rerender\n */\nexport function enqueueRender(c) {\n\tif (\n\t\t(!c._dirty &&\n\t\t\t(c._dirty = true) &&\n\t\t\trerenderQueue.push(c) &&\n\t\t\t!process._rerenderCount++) ||\n\t\tprevDebounce != options.debounceRendering\n\t) {\n\t\tprevDebounce = options.debounceRendering;\n\t\t(prevDebounce || defer)(process);\n\t}\n}\n\n/**\n * @param {import('./internal').Component} a\n * @param {import('./internal').Component} b\n */\nconst depthSort = (a, b) => a._vnode._depth - b._vnode._depth;\n\n/** Flush the render queue by rerendering all queued components */\nfunction process() {\n\tlet c,\n\t\tl = 1;\n\n\t// Don't update `renderCount` yet. Keep its value non-zero to prevent unnecessary\n\t// process() calls from getting scheduled while `queue` is still being consumed.\n\twhile (rerenderQueue.length) {\n\t\t// Keep the rerender queue sorted by (depth, insertion order). The queue\n\t\t// will initially be sorted on the first iteration only if it has more than 1 item.\n\t\t//\n\t\t// New items can be added to the queue e.g. when rerendering a provider, so we want to\n\t\t// keep the order from top to bottom with those new items so we can handle them in a\n\t\t// single pass\n\t\tif (rerenderQueue.length > l) {\n\t\t\trerenderQueue.sort(depthSort);\n\t\t}\n\n\t\tc = rerenderQueue.shift();\n\t\tl = rerenderQueue.length;\n\n\t\tif (c._dirty) {\n\t\t\trenderComponent(c);\n\t\t}\n\t}\n\tprocess._rerenderCount = 0;\n}\n\nprocess._rerenderCount = 0;\n","import { enqueueRender } from './component';\nimport { NULL } from './constants';\n\nexport let i = 0;\n\nexport function createContext(defaultValue) {\n\tfunction Context(props) {\n\t\tif (!this.getChildContext) {\n\t\t\t/** @type {Set | null} */\n\t\t\tlet subs = new Set();\n\t\t\tlet ctx = {};\n\t\t\tctx[Context._id] = this;\n\n\t\t\tthis.getChildContext = () => ctx;\n\n\t\t\tthis.componentWillUnmount = () => {\n\t\t\t\tsubs = NULL;\n\t\t\t};\n\n\t\t\tthis.shouldComponentUpdate = function (_props) {\n\t\t\t\t// @ts-expect-error even\n\t\t\t\tif (this.props.value != _props.value) {\n\t\t\t\t\tsubs.forEach(c => {\n\t\t\t\t\t\tc._force = true;\n\t\t\t\t\t\tenqueueRender(c);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tthis.sub = c => {\n\t\t\t\tsubs.add(c);\n\t\t\t\tlet old = c.componentWillUnmount;\n\t\t\t\tc.componentWillUnmount = () => {\n\t\t\t\t\tif (subs) {\n\t\t\t\t\t\tsubs.delete(c);\n\t\t\t\t\t}\n\t\t\t\t\tif (old) old.call(c);\n\t\t\t\t};\n\t\t\t};\n\t\t}\n\n\t\treturn props.children;\n\t}\n\n\tContext._id = '__cC' + i++;\n\tContext._defaultValue = defaultValue;\n\n\t/** @type {import('./internal').FunctionComponent} */\n\tContext.Consumer = (props, contextValue) => {\n\t\treturn props.children(contextValue);\n\t};\n\n\t// we could also get rid of _contextRef entirely\n\tContext.Provider =\n\t\tContext._contextRef =\n\t\tContext.Consumer.contextType =\n\t\t\tContext;\n\n\treturn Context;\n}\n","export const OBSIDIAN_SANS_STACK =\n 'ui-sans-serif, -apple-system, BlinkMacSystemFont, system-ui, \"Segoe UI\", Roboto, \"Inter\", \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", sans-serif';\n\nexport const OBSIDIAN_MONO_STACK =\n 'ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Roboto Mono\", \"DejaVu Sans Mono\", \"Liberation Mono\", Menlo, Monaco, \"Consolas\", \"Source Code Pro\", monospace';\n\nexport type FontRole = \"title\" | \"header\" | \"body\" | \"code\";\n\nexport const DEFAULT_WEIGHTS: Record = {\n title: [400, 700],\n header: [400, 700],\n body: [400, 600],\n code: [400, 600],\n};\n\nexport const DEFAULT_ITALIC: Record = {\n title: false,\n header: false,\n body: true,\n code: false,\n};\n","import type { QuartzFontRegistry } from \"../types\";\n\nconst REGISTRY_KEY = \"__quartzFonts\";\n\nexport function readFontRegistry(): QuartzFontRegistry | undefined {\n const registry = (globalThis as Record)[REGISTRY_KEY];\n if (registry && typeof registry === \"object\" && \"themeName\" in registry && \"fonts\" in registry) {\n return registry as QuartzFontRegistry;\n }\n return undefined;\n}\n\nexport function isQuartzThemeEnabled(): boolean {\n return REGISTRY_KEY in (globalThis as Record);\n}\n","import type { FontSpecification } from \"../types\";\nimport type { FontRole } from \"../defaults\";\nimport { DEFAULT_WEIGHTS, DEFAULT_ITALIC } from \"../defaults\";\n\nfunction normalizeFontSpec(spec: FontSpecification): {\n name: string;\n weights?: number[];\n includeItalic?: boolean;\n} {\n return typeof spec === \"string\" ? { name: spec } : spec;\n}\n\nexport function getFontName(spec: FontSpecification): string {\n return typeof spec === \"string\" ? spec : spec.name;\n}\n\nexport function formatFontSpecification(role: FontRole, spec: FontSpecification): string {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const features: string[] = [];\n if (italic) {\n features.push(\"ital\");\n }\n\n if (resolvedWeights.length > 1) {\n const weightSpec = italic\n ? resolvedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : resolvedWeights.join(\";\");\n\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${name}:${features.join(\",\")}`;\n }\n\n return name;\n}\n\ninterface MergedFontEntry {\n name: string;\n weights: Set;\n italic: boolean;\n}\n\nfunction mergeInto(\n map: Map,\n role: FontRole,\n spec: FontSpecification,\n): void {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const existing = map.get(name);\n if (existing) {\n for (const w of resolvedWeights) existing.weights.add(w);\n if (italic) existing.italic = true;\n } else {\n map.set(name, { name, weights: new Set(resolvedWeights), italic });\n }\n}\n\nfunction formatMergedEntry(entry: MergedFontEntry): string {\n const sortedWeights = [...entry.weights].sort((a, b) => a - b);\n const features: string[] = [];\n\n if (entry.italic) {\n features.push(\"ital\");\n }\n\n if (sortedWeights.length > 1) {\n const weightSpec = entry.italic\n ? sortedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : sortedWeights.join(\";\");\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${entry.name}:${features.join(\",\")}`;\n }\n return entry.name;\n}\n\nexport function googleFontHref(fonts: {\n title?: FontSpecification;\n header: FontSpecification;\n body: FontSpecification;\n code: FontSpecification;\n}): string {\n const merged = new Map();\n\n mergeInto(merged, \"header\", fonts.header);\n mergeInto(merged, \"body\", fonts.body);\n mergeInto(merged, \"code\", fonts.code);\n\n if (fonts.title) {\n mergeInto(merged, \"title\", fonts.title);\n }\n\n const families = [...merged.values()].map(formatMergedEntry);\n const params = families.map((f) => `family=${encodeURIComponent(f)}`).join(\"&\");\n return `https://fonts.googleapis.com/css2?${params}&display=swap`;\n}\n","import type { FontSpecification } from \"../types\";\nimport type { FontRole } from \"../defaults\";\nimport { DEFAULT_WEIGHTS, DEFAULT_ITALIC } from \"../defaults\";\n\ninterface GoogleFontEntry {\n family: string;\n id: string;\n weights: number[];\n styles: string[];\n subsets: string[];\n}\n\ntype FontMetadata = Record;\n\nlet metadataCache: FontMetadata | null | false = null;\n\nfunction loadMetadata(): FontMetadata | undefined {\n if (metadataCache === false) return undefined;\n if (metadataCache) return metadataCache;\n\n try {\n // `require` is injected by tsup's banner via createRequire(import.meta.url).\n // Using it directly avoids a duplicate createRequire import in the bundle.\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { APIv1 } = require(\"google-font-metadata\") as { APIv1: FontMetadata };\n metadataCache = APIv1;\n return metadataCache;\n } catch {\n metadataCache = false;\n return undefined;\n }\n}\n\nfunction fontToId(family: string): string {\n return family.toLowerCase().replace(/\\s+/g, \"-\");\n}\n\nexport interface ValidationWarning {\n font: string;\n role: string;\n message: string;\n}\n\nexport function validateFontSpec(\n role: string,\n spec: FontSpecification,\n fontRole: FontRole,\n): ValidationWarning[] {\n const metadata = loadMetadata();\n if (!metadata) return [];\n\n const warnings: ValidationWarning[] = [];\n const name = typeof spec === \"string\" ? spec : spec.name;\n const id = fontToId(name);\n const entry = metadata[id];\n\n if (!entry) {\n warnings.push({\n font: name,\n role,\n message: `\"${name}\" is not a recognized Google Font. It will be requested but may fail to load.`,\n });\n return warnings;\n }\n\n const weights =\n typeof spec === \"object\" && spec.weights ? spec.weights : DEFAULT_WEIGHTS[fontRole];\n const italic =\n typeof spec === \"object\" && spec.includeItalic !== undefined\n ? spec.includeItalic\n : DEFAULT_ITALIC[fontRole];\n\n for (const weight of weights) {\n if (!entry.weights.includes(weight)) {\n warnings.push({\n font: name,\n role,\n message: `\"${name}\" does not support weight ${weight}. Available weights: ${entry.weights.join(\", \")}.`,\n });\n }\n }\n\n if (italic && !entry.styles.includes(\"italic\")) {\n warnings.push({\n font: name,\n role,\n message: `\"${name}\" does not support italic style. Available styles: ${entry.styles.join(\", \")}.`,\n });\n }\n\n return warnings;\n}\n","import { h } from \"preact\";\nimport type { QuartzTransformerPlugin, CSSResource } from \"@quartz-community/types\";\nimport type { FontSpecification, QuartzFontsOptions } from \"./types\";\nimport { OBSIDIAN_SANS_STACK, OBSIDIAN_MONO_STACK } from \"./defaults\";\nimport { readFontRegistry, isQuartzThemeEnabled } from \"./util/registry\";\nimport { googleFontHref } from \"./util/google-fonts\";\nimport { validateFontSpec } from \"./util/validate\";\n\nconst QUARTZ_DEFAULT_HEADER = \"Schibsted Grotesk\";\nconst QUARTZ_DEFAULT_BODY = \"Source Sans Pro\";\nconst QUARTZ_DEFAULT_CODE = \"IBM Plex Mono\";\n\nconst defaultOptions: QuartzFontsOptions = {\n useThemeFonts: true,\n fontOrigin: \"googleFonts\",\n};\n\nfunction toFontFamily(spec: FontSpecification): string {\n if (typeof spec === \"string\") return spec;\n return spec.name;\n}\n\ninterface ResolvedFonts {\n title: string;\n body: string;\n header: string;\n code: string;\n interface: string;\n h1: string;\n h2: string;\n h3: string;\n h4: string;\n h5: string;\n h6: string;\n}\n\nfunction resolveFonts(options: QuartzFontsOptions): ResolvedFonts {\n const registry = options.useThemeFonts !== false ? readFontRegistry() : undefined;\n\n if (options.useThemeFonts !== false && !registry) {\n if (isQuartzThemeEnabled()) {\n console.warn(\n \"[QuartzFonts] QuartzTheme is enabled but its font registry is empty. \" +\n \"Ensure QuartzTheme runs before QuartzFonts (lower defaultOrder number). \" +\n \"Falling back to Obsidian defaults.\",\n );\n }\n }\n\n const themeFonts = registry?.fonts ?? {};\n\n const resolve = (\n spec: FontSpecification | undefined,\n ...fallbacks: (string | undefined)[]\n ): string => {\n if (spec !== undefined) return toFontFamily(spec);\n for (const fb of fallbacks) {\n if (fb !== undefined) return fb;\n }\n return OBSIDIAN_SANS_STACK;\n };\n\n const body = resolve(\n options.body,\n themeFonts[\"--font-text\"],\n QUARTZ_DEFAULT_BODY,\n OBSIDIAN_SANS_STACK,\n );\n const header = resolve(\n options.header,\n themeFonts[\"--font-text\"],\n QUARTZ_DEFAULT_HEADER,\n OBSIDIAN_SANS_STACK,\n );\n const code = resolve(\n options.code,\n themeFonts[\"--font-monospace\"],\n QUARTZ_DEFAULT_CODE,\n OBSIDIAN_MONO_STACK,\n );\n const interfaceFont = resolve(\n options.interface,\n themeFonts[\"--font-interface\"],\n OBSIDIAN_SANS_STACK,\n );\n const title = resolve(options.title, undefined, header);\n\n const resolveHeading = (level: 1 | 2 | 3 | 4 | 5 | 6): string => {\n const levelKey = `h${level}` as keyof QuartzFontsOptions;\n const themeVarKey = `--h${level}-font`;\n const userSpec = options[levelKey] as FontSpecification | undefined;\n\n if (userSpec !== undefined) return toFontFamily(userSpec);\n if (themeFonts[themeVarKey]) return themeFonts[themeVarKey];\n if (options.header !== undefined) return toFontFamily(options.header);\n if (themeFonts[\"--font-text\"]) return themeFonts[\"--font-text\"];\n return header;\n };\n\n return {\n title,\n body,\n header,\n code,\n interface: interfaceFont,\n h1: resolveHeading(1),\n h2: resolveHeading(2),\n h3: resolveHeading(3),\n h4: resolveHeading(4),\n h5: resolveHeading(5),\n h6: resolveHeading(6),\n };\n}\n\nfunction runValidation(options: QuartzFontsOptions): void {\n const specs: Array<{\n role: string;\n spec: FontSpecification;\n fontRole: \"title\" | \"header\" | \"body\" | \"code\";\n }> = [];\n\n if (options.title) specs.push({ role: \"title\", spec: options.title, fontRole: \"title\" });\n if (options.header) specs.push({ role: \"header\", spec: options.header, fontRole: \"header\" });\n if (options.body) specs.push({ role: \"body\", spec: options.body, fontRole: \"body\" });\n if (options.code) specs.push({ role: \"code\", spec: options.code, fontRole: \"code\" });\n\n const headingLevels = [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"] as const;\n for (const level of headingLevels) {\n const spec = options[level];\n if (spec) specs.push({ role: level, spec, fontRole: \"header\" });\n }\n\n for (const { role, spec, fontRole } of specs) {\n const warnings = validateFontSpec(role, spec, fontRole);\n for (const w of warnings) {\n console.warn(`[QuartzFonts] ${w.role}: ${w.message}`);\n }\n }\n}\n\nfunction buildLayeredCSS(fonts: ResolvedFonts): string {\n return [\n \"@layer quartz-fonts {\",\n \" :root {\",\n ` --titleFont: ${fonts.title};`,\n ` --bodyFont: ${fonts.body};`,\n ` --headerFont: ${fonts.header};`,\n ` --codeFont: ${fonts.code};`,\n ` --font-text: ${fonts.body};`,\n ` --font-interface: ${fonts.interface};`,\n ` --font-monospace: ${fonts.code};`,\n ` --h1-font: ${fonts.h1};`,\n ` --h2-font: ${fonts.h2};`,\n ` --h3-font: ${fonts.h3};`,\n ` --h4-font: ${fonts.h4};`,\n ` --h5-font: ${fonts.h5};`,\n ` --h6-font: ${fonts.h6};`,\n \" }\",\n \"}\",\n ].join(\"\\n\");\n}\n\nfunction buildUnlayeredCSS(fonts: ResolvedFonts): string {\n return [\n `h1 { font-family: ${fonts.h1}; }`,\n `h2 { font-family: ${fonts.h2}; }`,\n `h3 { font-family: ${fonts.h3}; }`,\n `h4 { font-family: ${fonts.h4}; }`,\n `h5 { font-family: ${fonts.h5}; }`,\n `h6 { font-family: ${fonts.h6}; }`,\n ].join(\"\\n\");\n}\n\nfunction buildGoogleFontsHead(options: QuartzFontsOptions): unknown[] {\n const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;\n const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;\n const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;\n\n const href = googleFontHref({\n title: options.title,\n header: headerSpec,\n body: bodySpec,\n code: codeSpec,\n });\n\n return [\n h(\"link\", { rel: \"preconnect\", href: \"https://fonts.googleapis.com\" }),\n h(\"link\", { rel: \"preconnect\", href: \"https://fonts.gstatic.com\", crossOrigin: \"anonymous\" }),\n h(\"link\", { rel: \"stylesheet\", href }),\n ];\n}\n\nexport const QuartzFonts: QuartzTransformerPlugin> = (\n userOptions?: Partial,\n) => {\n const options: QuartzFontsOptions = { ...defaultOptions, ...userOptions };\n\n if (options.fontOrigin === \"googleFonts\") {\n runValidation(options);\n }\n\n return {\n name: \"QuartzFonts\",\n textTransform(_ctx, src) {\n return src;\n },\n externalResources(_ctx) {\n const fonts = resolveFonts(options);\n\n const css: CSSResource[] = [\n { content: buildLayeredCSS(fonts), inline: true },\n { content: buildUnlayeredCSS(fonts), inline: true },\n ];\n\n const additionalHead: unknown[] =\n options.fontOrigin === \"googleFonts\" ? buildGoogleFontsHead(options) : [];\n\n return { css, js: [], additionalHead };\n },\n };\n};\n"]} \ No newline at end of file +{"version":3,"sources":["../node_modules/preact/src/constants.js","../node_modules/preact/src/util.js","../node_modules/preact/src/options.js","../node_modules/preact/src/create-element.js","../node_modules/preact/src/diff/catch-error.js","../node_modules/preact/src/component.js","../node_modules/preact/src/create-context.js","../src/defaults.ts","../src/util/registry.ts","../src/util/google-fonts.ts","../src/util/validate.ts","../src/transformer.ts","../src/util/process-fonts.ts","../src/emitter.ts"],"names":["slice","options","vnodeId","EMPTY_ARR","createElement","type","props","children","key","ref","i","normalizedProps","arguments","length","call","defaultProps","createVNode","original","vnode","__k","__","__b","__e","__c","constructor","__v","__i","__u","error","oldVNode","errorInfo","component","ctor","handled","getDerivedStateFromError","setState","__d","componentDidCatch","__E","e","Promise","prototype","then","bind","resolve","setTimeout","w","a","f","QUARTZ_DEFAULT_HEADER","QUARTZ_DEFAULT_BODY","QUARTZ_DEFAULT_CODE","defaultOptions"],"mappings":"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","file":"index.js","sourcesContent":["/** Normal hydration that attaches to a DOM tree but does not diff it. */\nexport const MODE_HYDRATE = 1 << 5;\n/** Signifies this VNode suspended on the previous render */\nexport const MODE_SUSPENDED = 1 << 7;\n/** Indicates that this node needs to be inserted while patching children */\nexport const INSERT_VNODE = 1 << 2;\n/** Indicates a VNode has been matched with another VNode in the diff */\nexport const MATCHED = 1 << 1;\n\n/** Reset all mode flags */\nexport const RESET_MODE = ~(MODE_HYDRATE | MODE_SUSPENDED);\n\nexport const SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\nexport const XHTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';\nexport const MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';\n\nexport const NULL = null;\nexport const UNDEFINED = undefined;\nexport const EMPTY_OBJ = /** @type {any} */ ({});\nexport const EMPTY_ARR = [];\nexport const IS_NON_DIMENSIONAL =\n\t/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;\n","import { EMPTY_ARR } from './constants';\n\nexport const isArray = Array.isArray;\n\n/**\n * Assign properties from `props` to `obj`\n * @template O, P The obj and props types\n * @param {O} obj The object to copy properties to\n * @param {P} props The object to copy properties from\n * @returns {O & P}\n */\nexport function assign(obj, props) {\n\t// @ts-expect-error We change the type of `obj` to be `O & P`\n\tfor (let i in props) obj[i] = props[i];\n\treturn /** @type {O & P} */ (obj);\n}\n\n/**\n * Remove a child node from its parent if attached. This is a workaround for\n * IE11 which doesn't support `Element.prototype.remove()`. Using this function\n * is smaller than including a dedicated polyfill.\n * @param {import('./index').ContainerNode} node The node to remove\n */\nexport function removeNode(node) {\n\tif (node && node.parentNode) node.parentNode.removeChild(node);\n}\n\nexport const slice = EMPTY_ARR.slice;\n","import { _catchError } from './diff/catch-error';\n\n/**\n * The `option` object can potentially contain callback functions\n * that are called during various stages of our renderer. This is the\n * foundation on which all our addons like `preact/debug`, `preact/compat`,\n * and `preact/hooks` are based on. See the `Options` type in `internal.d.ts`\n * for a full list of available option hooks (most editors/IDEs allow you to\n * ctrl+click or cmd+click on mac the type definition below).\n * @type {import('./internal').Options}\n */\nconst options = {\n\t_catchError\n};\n\nexport default options;\n","import { slice } from './util';\nimport options from './options';\nimport { NULL, UNDEFINED } from './constants';\n\nlet vnodeId = 0;\n\n/**\n * Create an virtual node (used for JSX)\n * @param {import('./internal').VNode[\"type\"]} type The node name or Component constructor for this\n * virtual node\n * @param {object | null | undefined} [props] The properties of the virtual node\n * @param {Array} [children] The children of the\n * virtual node\n * @returns {import('./internal').VNode}\n */\nexport function createElement(type, props, children) {\n\tlet normalizedProps = {},\n\t\tkey,\n\t\tref,\n\t\ti;\n\tfor (i in props) {\n\t\tif (i == 'key') key = props[i];\n\t\telse if (i == 'ref') ref = props[i];\n\t\telse normalizedProps[i] = props[i];\n\t}\n\n\tif (arguments.length > 2) {\n\t\tnormalizedProps.children =\n\t\t\targuments.length > 3 ? slice.call(arguments, 2) : children;\n\t}\n\n\t// If a Component VNode, check for and apply defaultProps\n\t// Note: type may be undefined in development, must never error here.\n\tif (typeof type == 'function' && type.defaultProps != NULL) {\n\t\tfor (i in type.defaultProps) {\n\t\t\tif (normalizedProps[i] === UNDEFINED) {\n\t\t\t\tnormalizedProps[i] = type.defaultProps[i];\n\t\t\t}\n\t\t}\n\t}\n\n\treturn createVNode(type, normalizedProps, key, ref, NULL);\n}\n\n/**\n * Create a VNode (used internally by Preact)\n * @param {import('./internal').VNode[\"type\"]} type The node name or Component\n * Constructor for this virtual node\n * @param {object | string | number | null} props The properties of this virtual node.\n * If this virtual node represents a text node, this is the text of the node (string or number).\n * @param {string | number | null} key The key for this virtual node, used when\n * diffing it against its children\n * @param {import('./internal').VNode[\"ref\"]} ref The ref property that will\n * receive a reference to its created child\n * @returns {import('./internal').VNode}\n */\nexport function createVNode(type, props, key, ref, original) {\n\t// V8 seems to be better at detecting type shapes if the object is allocated from the same call site\n\t// Do not inline into createElement and coerceToVNode!\n\t/** @type {import('./internal').VNode} */\n\tconst vnode = {\n\t\ttype,\n\t\tprops,\n\t\tkey,\n\t\tref,\n\t\t_children: NULL,\n\t\t_parent: NULL,\n\t\t_depth: 0,\n\t\t_dom: NULL,\n\t\t_component: NULL,\n\t\tconstructor: UNDEFINED,\n\t\t_original: original == NULL ? ++vnodeId : original,\n\t\t_index: -1,\n\t\t_flags: 0\n\t};\n\n\t// Only invoke the vnode hook if this was *not* a direct copy:\n\tif (original == NULL && options.vnode != NULL) options.vnode(vnode);\n\n\treturn vnode;\n}\n\nexport function createRef() {\n\treturn { current: NULL };\n}\n\nexport function Fragment(props) {\n\treturn props.children;\n}\n\n/**\n * Check if a the argument is a valid Preact VNode.\n * @param {*} vnode\n * @returns {vnode is VNode}\n */\nexport const isValidElement = vnode =>\n\tvnode != NULL && vnode.constructor === UNDEFINED;\n","import { NULL } from '../constants';\n\n/**\n * Find the closest error boundary to a thrown error and call it\n * @param {object} error The thrown value\n * @param {import('../internal').VNode} vnode The vnode that threw the error that was caught (except\n * for unmounting when this parameter is the highest parent that was being\n * unmounted)\n * @param {import('../internal').VNode} [oldVNode]\n * @param {import('../internal').ErrorInfo} [errorInfo]\n */\nexport function _catchError(error, vnode, oldVNode, errorInfo) {\n\t/** @type {import('../internal').Component} */\n\tlet component,\n\t\t/** @type {import('../internal').ComponentType} */\n\t\tctor,\n\t\t/** @type {boolean} */\n\t\thandled;\n\n\tfor (; (vnode = vnode._parent); ) {\n\t\tif ((component = vnode._component) && !component._processingException) {\n\t\t\ttry {\n\t\t\t\tctor = component.constructor;\n\n\t\t\t\tif (ctor && ctor.getDerivedStateFromError != NULL) {\n\t\t\t\t\tcomponent.setState(ctor.getDerivedStateFromError(error));\n\t\t\t\t\thandled = component._dirty;\n\t\t\t\t}\n\n\t\t\t\tif (component.componentDidCatch != NULL) {\n\t\t\t\t\tcomponent.componentDidCatch(error, errorInfo || {});\n\t\t\t\t\thandled = component._dirty;\n\t\t\t\t}\n\n\t\t\t\t// This is an error boundary. Mark it as having bailed out, and whether it was mid-hydration.\n\t\t\t\tif (handled) {\n\t\t\t\t\treturn (component._pendingError = component);\n\t\t\t\t}\n\t\t\t} catch (e) {\n\t\t\t\terror = e;\n\t\t\t}\n\t\t}\n\t}\n\n\tthrow error;\n}\n","import { assign } from './util';\nimport { diff, commitRoot } from './diff/index';\nimport options from './options';\nimport { Fragment } from './create-element';\nimport { MODE_HYDRATE, NULL } from './constants';\n\n/**\n * Base Component class. Provides `setState()` and `forceUpdate()`, which\n * trigger rendering\n * @param {object} props The initial component props\n * @param {object} context The initial context from parent components'\n * getChildContext\n */\nexport function BaseComponent(props, context) {\n\tthis.props = props;\n\tthis.context = context;\n}\n\n/**\n * Update component state and schedule a re-render.\n * @this {import('./internal').Component}\n * @param {object | ((s: object, p: object) => object)} update A hash of state\n * properties to update with new values or a function that given the current\n * state and props returns a new partial state\n * @param {() => void} [callback] A function to be called once component state is\n * updated\n */\nBaseComponent.prototype.setState = function (update, callback) {\n\t// only clone state when copying to nextState the first time.\n\tlet s;\n\tif (this._nextState != NULL && this._nextState != this.state) {\n\t\ts = this._nextState;\n\t} else {\n\t\ts = this._nextState = assign({}, this.state);\n\t}\n\n\tif (typeof update == 'function') {\n\t\t// Some libraries like `immer` mark the current state as readonly,\n\t\t// preventing us from mutating it, so we need to clone it. See #2716\n\t\tupdate = update(assign({}, s), this.props);\n\t}\n\n\tif (update) {\n\t\tassign(s, update);\n\t}\n\n\t// Skip update if updater function returned null\n\tif (update == NULL) return;\n\n\tif (this._vnode) {\n\t\tif (callback) {\n\t\t\tthis._stateCallbacks.push(callback);\n\t\t}\n\t\tenqueueRender(this);\n\t}\n};\n\n/**\n * Immediately perform a synchronous re-render of the component\n * @this {import('./internal').Component}\n * @param {() => void} [callback] A function to be called after component is\n * re-rendered\n */\nBaseComponent.prototype.forceUpdate = function (callback) {\n\tif (this._vnode) {\n\t\t// Set render mode so that we can differentiate where the render request\n\t\t// is coming from. We need this because forceUpdate should never call\n\t\t// shouldComponentUpdate\n\t\tthis._force = true;\n\t\tif (callback) this._renderCallbacks.push(callback);\n\t\tenqueueRender(this);\n\t}\n};\n\n/**\n * Accepts `props` and `state`, and returns a new Virtual DOM tree to build.\n * Virtual DOM is generally constructed via [JSX](https://jasonformat.com/wtf-is-jsx).\n * @param {object} props Props (eg: JSX attributes) received from parent\n * element/component\n * @param {object} state The component's current state\n * @param {object} context Context object, as returned by the nearest\n * ancestor's `getChildContext()`\n * @returns {ComponentChildren | void}\n */\nBaseComponent.prototype.render = Fragment;\n\n/**\n * @param {import('./internal').VNode} vnode\n * @param {number | null} [childIndex]\n */\nexport function getDomSibling(vnode, childIndex) {\n\tif (childIndex == NULL) {\n\t\t// Use childIndex==null as a signal to resume the search from the vnode's sibling\n\t\treturn vnode._parent\n\t\t\t? getDomSibling(vnode._parent, vnode._index + 1)\n\t\t\t: NULL;\n\t}\n\n\tlet sibling;\n\tfor (; childIndex < vnode._children.length; childIndex++) {\n\t\tsibling = vnode._children[childIndex];\n\n\t\tif (sibling != NULL && sibling._dom != NULL) {\n\t\t\t// Since updateParentDomPointers keeps _dom pointer correct,\n\t\t\t// we can rely on _dom to tell us if this subtree contains a\n\t\t\t// rendered DOM node, and what the first rendered DOM node is\n\t\t\treturn sibling._dom;\n\t\t}\n\t}\n\n\t// If we get here, we have not found a DOM node in this vnode's children.\n\t// We must resume from this vnode's sibling (in it's parent _children array)\n\t// Only climb up and search the parent if we aren't searching through a DOM\n\t// VNode (meaning we reached the DOM parent of the original vnode that began\n\t// the search)\n\treturn typeof vnode.type == 'function' ? getDomSibling(vnode) : NULL;\n}\n\n/**\n * Trigger in-place re-rendering of a component.\n * @param {import('./internal').Component} component The component to rerender\n */\nfunction renderComponent(component) {\n\tlet oldVNode = component._vnode,\n\t\toldDom = oldVNode._dom,\n\t\tcommitQueue = [],\n\t\trefQueue = [];\n\n\tif (component._parentDom) {\n\t\tconst newVNode = assign({}, oldVNode);\n\t\tnewVNode._original = oldVNode._original + 1;\n\t\tif (options.vnode) options.vnode(newVNode);\n\n\t\tdiff(\n\t\t\tcomponent._parentDom,\n\t\t\tnewVNode,\n\t\t\toldVNode,\n\t\t\tcomponent._globalContext,\n\t\t\tcomponent._parentDom.namespaceURI,\n\t\t\toldVNode._flags & MODE_HYDRATE ? [oldDom] : NULL,\n\t\t\tcommitQueue,\n\t\t\toldDom == NULL ? getDomSibling(oldVNode) : oldDom,\n\t\t\t!!(oldVNode._flags & MODE_HYDRATE),\n\t\t\trefQueue\n\t\t);\n\n\t\tnewVNode._original = oldVNode._original;\n\t\tnewVNode._parent._children[newVNode._index] = newVNode;\n\t\tcommitRoot(commitQueue, newVNode, refQueue);\n\t\toldVNode._dom = oldVNode._parent = null;\n\n\t\tif (newVNode._dom != oldDom) {\n\t\t\tupdateParentDomPointers(newVNode);\n\t\t}\n\t}\n}\n\n/**\n * @param {import('./internal').VNode} vnode\n */\nfunction updateParentDomPointers(vnode) {\n\tif ((vnode = vnode._parent) != NULL && vnode._component != NULL) {\n\t\tvnode._dom = vnode._component.base = NULL;\n\t\tfor (let i = 0; i < vnode._children.length; i++) {\n\t\t\tlet child = vnode._children[i];\n\t\t\tif (child != NULL && child._dom != NULL) {\n\t\t\t\tvnode._dom = vnode._component.base = child._dom;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn updateParentDomPointers(vnode);\n\t}\n}\n\n/**\n * The render queue\n * @type {Array}\n */\nlet rerenderQueue = [];\n\n/*\n * The value of `Component.debounce` must asynchronously invoke the passed in callback. It is\n * important that contributors to Preact can consistently reason about what calls to `setState`, etc.\n * do, and when their effects will be applied. See the links below for some further reading on designing\n * asynchronous APIs.\n * * [Designing APIs for Asynchrony](https://blog.izs.me/2013/08/designing-apis-for-asynchrony)\n * * [Callbacks synchronous and asynchronous](https://blog.ometer.com/2011/07/24/callbacks-synchronous-and-asynchronous/)\n */\n\nlet prevDebounce;\n\nconst defer =\n\ttypeof Promise == 'function'\n\t\t? Promise.prototype.then.bind(Promise.resolve())\n\t\t: setTimeout;\n\n/**\n * Enqueue a rerender of a component\n * @param {import('./internal').Component} c The component to rerender\n */\nexport function enqueueRender(c) {\n\tif (\n\t\t(!c._dirty &&\n\t\t\t(c._dirty = true) &&\n\t\t\trerenderQueue.push(c) &&\n\t\t\t!process._rerenderCount++) ||\n\t\tprevDebounce != options.debounceRendering\n\t) {\n\t\tprevDebounce = options.debounceRendering;\n\t\t(prevDebounce || defer)(process);\n\t}\n}\n\n/**\n * @param {import('./internal').Component} a\n * @param {import('./internal').Component} b\n */\nconst depthSort = (a, b) => a._vnode._depth - b._vnode._depth;\n\n/** Flush the render queue by rerendering all queued components */\nfunction process() {\n\tlet c,\n\t\tl = 1;\n\n\t// Don't update `renderCount` yet. Keep its value non-zero to prevent unnecessary\n\t// process() calls from getting scheduled while `queue` is still being consumed.\n\twhile (rerenderQueue.length) {\n\t\t// Keep the rerender queue sorted by (depth, insertion order). The queue\n\t\t// will initially be sorted on the first iteration only if it has more than 1 item.\n\t\t//\n\t\t// New items can be added to the queue e.g. when rerendering a provider, so we want to\n\t\t// keep the order from top to bottom with those new items so we can handle them in a\n\t\t// single pass\n\t\tif (rerenderQueue.length > l) {\n\t\t\trerenderQueue.sort(depthSort);\n\t\t}\n\n\t\tc = rerenderQueue.shift();\n\t\tl = rerenderQueue.length;\n\n\t\tif (c._dirty) {\n\t\t\trenderComponent(c);\n\t\t}\n\t}\n\tprocess._rerenderCount = 0;\n}\n\nprocess._rerenderCount = 0;\n","import { enqueueRender } from './component';\nimport { NULL } from './constants';\n\nexport let i = 0;\n\nexport function createContext(defaultValue) {\n\tfunction Context(props) {\n\t\tif (!this.getChildContext) {\n\t\t\t/** @type {Set | null} */\n\t\t\tlet subs = new Set();\n\t\t\tlet ctx = {};\n\t\t\tctx[Context._id] = this;\n\n\t\t\tthis.getChildContext = () => ctx;\n\n\t\t\tthis.componentWillUnmount = () => {\n\t\t\t\tsubs = NULL;\n\t\t\t};\n\n\t\t\tthis.shouldComponentUpdate = function (_props) {\n\t\t\t\t// @ts-expect-error even\n\t\t\t\tif (this.props.value != _props.value) {\n\t\t\t\t\tsubs.forEach(c => {\n\t\t\t\t\t\tc._force = true;\n\t\t\t\t\t\tenqueueRender(c);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tthis.sub = c => {\n\t\t\t\tsubs.add(c);\n\t\t\t\tlet old = c.componentWillUnmount;\n\t\t\t\tc.componentWillUnmount = () => {\n\t\t\t\t\tif (subs) {\n\t\t\t\t\t\tsubs.delete(c);\n\t\t\t\t\t}\n\t\t\t\t\tif (old) old.call(c);\n\t\t\t\t};\n\t\t\t};\n\t\t}\n\n\t\treturn props.children;\n\t}\n\n\tContext._id = '__cC' + i++;\n\tContext._defaultValue = defaultValue;\n\n\t/** @type {import('./internal').FunctionComponent} */\n\tContext.Consumer = (props, contextValue) => {\n\t\treturn props.children(contextValue);\n\t};\n\n\t// we could also get rid of _contextRef entirely\n\tContext.Provider =\n\t\tContext._contextRef =\n\t\tContext.Consumer.contextType =\n\t\t\tContext;\n\n\treturn Context;\n}\n","export const OBSIDIAN_SANS_STACK =\n 'ui-sans-serif, -apple-system, BlinkMacSystemFont, system-ui, \"Segoe UI\", Roboto, \"Inter\", \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", sans-serif';\n\nexport const OBSIDIAN_MONO_STACK =\n 'ui-monospace, SFMono-Regular, \"Cascadia Mono\", \"Roboto Mono\", \"DejaVu Sans Mono\", \"Liberation Mono\", Menlo, Monaco, \"Consolas\", \"Source Code Pro\", monospace';\n\nexport type FontRole = \"title\" | \"header\" | \"body\" | \"code\";\n\nexport const DEFAULT_WEIGHTS: Record = {\n title: [400, 700],\n header: [400, 700],\n body: [400, 600],\n code: [400, 600],\n};\n\nexport const DEFAULT_ITALIC: Record = {\n title: false,\n header: false,\n body: true,\n code: false,\n};\n","import type { QuartzFontRegistry } from \"../types\";\n\nconst REGISTRY_KEY = \"__quartzFonts\";\n\nexport function readFontRegistry(): QuartzFontRegistry | undefined {\n const registry = (globalThis as Record)[REGISTRY_KEY];\n if (registry && typeof registry === \"object\" && \"themeName\" in registry && \"fonts\" in registry) {\n return registry as QuartzFontRegistry;\n }\n return undefined;\n}\n\nexport function isQuartzThemeEnabled(): boolean {\n return REGISTRY_KEY in (globalThis as Record);\n}\n","import type { FontSpecification } from \"../types\";\nimport type { FontRole } from \"../defaults\";\nimport { DEFAULT_WEIGHTS, DEFAULT_ITALIC } from \"../defaults\";\n\nfunction normalizeFontSpec(spec: FontSpecification): {\n name: string;\n weights?: number[];\n includeItalic?: boolean;\n} {\n return typeof spec === \"string\" ? { name: spec } : spec;\n}\n\nexport function getFontName(spec: FontSpecification): string {\n return typeof spec === \"string\" ? spec : spec.name;\n}\n\nexport function formatFontSpecification(role: FontRole, spec: FontSpecification): string {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const features: string[] = [];\n if (italic) {\n features.push(\"ital\");\n }\n\n if (resolvedWeights.length > 1) {\n const weightSpec = italic\n ? resolvedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : resolvedWeights.join(\";\");\n\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${name}:${features.join(\",\")}`;\n }\n\n return name;\n}\n\ninterface MergedFontEntry {\n name: string;\n weights: Set;\n italic: boolean;\n}\n\nfunction mergeInto(\n map: Map,\n role: FontRole,\n spec: FontSpecification,\n): void {\n const { name, weights, includeItalic } = normalizeFontSpec(spec);\n const resolvedWeights = weights ?? DEFAULT_WEIGHTS[role];\n const italic = includeItalic ?? DEFAULT_ITALIC[role];\n\n const existing = map.get(name);\n if (existing) {\n for (const w of resolvedWeights) existing.weights.add(w);\n if (italic) existing.italic = true;\n } else {\n map.set(name, { name, weights: new Set(resolvedWeights), italic });\n }\n}\n\nfunction formatMergedEntry(entry: MergedFontEntry): string {\n const sortedWeights = [...entry.weights].sort((a, b) => a - b);\n const features: string[] = [];\n\n if (entry.italic) {\n features.push(\"ital\");\n }\n\n if (sortedWeights.length > 1) {\n const weightSpec = entry.italic\n ? sortedWeights\n .flatMap((w) => [`0,${w}`, `1,${w}`])\n .sort()\n .join(\";\")\n : sortedWeights.join(\";\");\n features.push(`wght@${weightSpec}`);\n }\n\n if (features.length > 0) {\n return `${entry.name}:${features.join(\",\")}`;\n }\n return entry.name;\n}\n\nexport function googleFontHref(fonts: {\n title?: FontSpecification;\n header: FontSpecification;\n body: FontSpecification;\n code: FontSpecification;\n}): string {\n const merged = new Map();\n\n mergeInto(merged, \"header\", fonts.header);\n mergeInto(merged, \"body\", fonts.body);\n mergeInto(merged, \"code\", fonts.code);\n\n if (fonts.title) {\n mergeInto(merged, \"title\", fonts.title);\n }\n\n const families = [...merged.values()].map(formatMergedEntry);\n const params = families.map((f) => `family=${encodeURIComponent(f)}`).join(\"&\");\n return `https://fonts.googleapis.com/css2?${params}&display=swap`;\n}\n","import type { FontSpecification } from \"../types\";\nimport type { FontRole } from \"../defaults\";\nimport { DEFAULT_WEIGHTS, DEFAULT_ITALIC } from \"../defaults\";\n\ninterface GoogleFontEntry {\n family: string;\n id: string;\n weights: number[];\n styles: string[];\n subsets: string[];\n}\n\ntype FontMetadata = Record;\n\nlet metadataCache: FontMetadata | null | false = null;\n\nfunction loadMetadata(): FontMetadata | undefined {\n if (metadataCache === false) return undefined;\n if (metadataCache) return metadataCache;\n\n try {\n // `require` is injected by tsup's banner via createRequire(import.meta.url).\n // Using it directly avoids a duplicate createRequire import in the bundle.\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const { APIv1 } = require(\"google-font-metadata\") as { APIv1: FontMetadata };\n metadataCache = APIv1;\n return metadataCache;\n } catch {\n metadataCache = false;\n return undefined;\n }\n}\n\nfunction fontToId(family: string): string {\n return family.toLowerCase().replace(/\\s+/g, \"-\");\n}\n\nexport interface ValidationWarning {\n font: string;\n role: string;\n message: string;\n}\n\nexport function validateFontSpec(\n role: string,\n spec: FontSpecification,\n fontRole: FontRole,\n): ValidationWarning[] {\n const metadata = loadMetadata();\n if (!metadata) return [];\n\n const warnings: ValidationWarning[] = [];\n const name = typeof spec === \"string\" ? spec : spec.name;\n const id = fontToId(name);\n const entry = metadata[id];\n\n if (!entry) {\n warnings.push({\n font: name,\n role,\n message: `\"${name}\" is not a recognized Google Font. It will be requested but may fail to load.`,\n });\n return warnings;\n }\n\n const weights =\n typeof spec === \"object\" && spec.weights ? spec.weights : DEFAULT_WEIGHTS[fontRole];\n const italic =\n typeof spec === \"object\" && spec.includeItalic !== undefined\n ? spec.includeItalic\n : DEFAULT_ITALIC[fontRole];\n\n for (const weight of weights) {\n if (!entry.weights.includes(weight)) {\n warnings.push({\n font: name,\n role,\n message: `\"${name}\" does not support weight ${weight}. Available weights: ${entry.weights.join(\", \")}.`,\n });\n }\n }\n\n if (italic && !entry.styles.includes(\"italic\")) {\n warnings.push({\n font: name,\n role,\n message: `\"${name}\" does not support italic style. Available styles: ${entry.styles.join(\", \")}.`,\n });\n }\n\n return warnings;\n}\n","import { h } from \"preact\";\nimport type { QuartzTransformerPlugin, CSSResource } from \"@quartz-community/types\";\nimport type { FontSpecification, QuartzFontsOptions } from \"./types\";\nimport { OBSIDIAN_SANS_STACK, OBSIDIAN_MONO_STACK } from \"./defaults\";\nimport { readFontRegistry, isQuartzThemeEnabled } from \"./util/registry\";\nimport { googleFontHref } from \"./util/google-fonts\";\nimport { validateFontSpec } from \"./util/validate\";\n\nconst QUARTZ_DEFAULT_HEADER = \"Schibsted Grotesk\";\nconst QUARTZ_DEFAULT_BODY = \"Source Sans Pro\";\nconst QUARTZ_DEFAULT_CODE = \"IBM Plex Mono\";\n\nconst defaultOptions: QuartzFontsOptions = {\n useThemeFonts: true,\n fontOrigin: \"googleFonts\",\n};\n\nfunction toFontFamily(spec: FontSpecification): string {\n if (typeof spec === \"string\") return spec;\n return spec.name;\n}\n\ninterface ResolvedFonts {\n title: string;\n body: string;\n header: string;\n code: string;\n interface: string;\n h1: string;\n h2: string;\n h3: string;\n h4: string;\n h5: string;\n h6: string;\n}\n\nfunction resolveFonts(options: QuartzFontsOptions): ResolvedFonts {\n const registry = options.useThemeFonts !== false ? readFontRegistry() : undefined;\n\n if (options.useThemeFonts !== false && !registry) {\n if (isQuartzThemeEnabled()) {\n console.warn(\n \"[QuartzFonts] QuartzTheme is enabled but its font registry is empty. \" +\n \"Ensure QuartzTheme runs before QuartzFonts (lower defaultOrder number). \" +\n \"Falling back to Obsidian defaults.\",\n );\n }\n }\n\n const themeFonts = registry?.fonts ?? {};\n\n const resolve = (\n spec: FontSpecification | undefined,\n ...fallbacks: (string | undefined)[]\n ): string => {\n if (spec !== undefined) return toFontFamily(spec);\n for (const fb of fallbacks) {\n if (fb !== undefined) return fb;\n }\n return OBSIDIAN_SANS_STACK;\n };\n\n const body = resolve(\n options.body,\n themeFonts[\"--font-text\"],\n QUARTZ_DEFAULT_BODY,\n OBSIDIAN_SANS_STACK,\n );\n const header = resolve(\n options.header,\n themeFonts[\"--font-text\"],\n QUARTZ_DEFAULT_HEADER,\n OBSIDIAN_SANS_STACK,\n );\n const code = resolve(\n options.code,\n themeFonts[\"--font-monospace\"],\n QUARTZ_DEFAULT_CODE,\n OBSIDIAN_MONO_STACK,\n );\n const interfaceFont = resolve(\n options.interface,\n themeFonts[\"--font-interface\"],\n OBSIDIAN_SANS_STACK,\n );\n const title = resolve(options.title, undefined, header);\n\n const resolveHeading = (level: 1 | 2 | 3 | 4 | 5 | 6): string => {\n const levelKey = `h${level}` as keyof QuartzFontsOptions;\n const themeVarKey = `--h${level}-font`;\n const userSpec = options[levelKey] as FontSpecification | undefined;\n\n if (userSpec !== undefined) return toFontFamily(userSpec);\n if (themeFonts[themeVarKey]) return themeFonts[themeVarKey];\n if (options.header !== undefined) return toFontFamily(options.header);\n if (themeFonts[\"--font-text\"]) return themeFonts[\"--font-text\"];\n return header;\n };\n\n return {\n title,\n body,\n header,\n code,\n interface: interfaceFont,\n h1: resolveHeading(1),\n h2: resolveHeading(2),\n h3: resolveHeading(3),\n h4: resolveHeading(4),\n h5: resolveHeading(5),\n h6: resolveHeading(6),\n };\n}\n\nfunction runValidation(options: QuartzFontsOptions): void {\n const specs: Array<{\n role: string;\n spec: FontSpecification;\n fontRole: \"title\" | \"header\" | \"body\" | \"code\";\n }> = [];\n\n if (options.title) specs.push({ role: \"title\", spec: options.title, fontRole: \"title\" });\n if (options.header) specs.push({ role: \"header\", spec: options.header, fontRole: \"header\" });\n if (options.body) specs.push({ role: \"body\", spec: options.body, fontRole: \"body\" });\n if (options.code) specs.push({ role: \"code\", spec: options.code, fontRole: \"code\" });\n\n const headingLevels = [\"h1\", \"h2\", \"h3\", \"h4\", \"h5\", \"h6\"] as const;\n for (const level of headingLevels) {\n const spec = options[level];\n if (spec) specs.push({ role: level, spec, fontRole: \"header\" });\n }\n\n for (const { role, spec, fontRole } of specs) {\n const warnings = validateFontSpec(role, spec, fontRole);\n for (const w of warnings) {\n console.warn(`[QuartzFonts] ${w.role}: ${w.message}`);\n }\n }\n}\n\nfunction buildLayeredCSS(fonts: ResolvedFonts): string {\n return [\n \"@layer quartz-fonts {\",\n \" :root {\",\n ` --titleFont: ${fonts.title};`,\n ` --bodyFont: ${fonts.body};`,\n ` --headerFont: ${fonts.header};`,\n ` --codeFont: ${fonts.code};`,\n ` --font-text: ${fonts.body};`,\n ` --font-interface: ${fonts.interface};`,\n ` --font-monospace: ${fonts.code};`,\n ` --h1-font: ${fonts.h1};`,\n ` --h2-font: ${fonts.h2};`,\n ` --h3-font: ${fonts.h3};`,\n ` --h4-font: ${fonts.h4};`,\n ` --h5-font: ${fonts.h5};`,\n ` --h6-font: ${fonts.h6};`,\n \" }\",\n \"}\",\n ].join(\"\\n\");\n}\n\nfunction buildUnlayeredCSS(fonts: ResolvedFonts): string {\n return [\n `h1 { font-family: ${fonts.h1}; }`,\n `h2 { font-family: ${fonts.h2}; }`,\n `h3 { font-family: ${fonts.h3}; }`,\n `h4 { font-family: ${fonts.h4}; }`,\n `h5 { font-family: ${fonts.h5}; }`,\n `h6 { font-family: ${fonts.h6}; }`,\n ].join(\"\\n\");\n}\n\nfunction buildGoogleFontsHead(options: QuartzFontsOptions): unknown[] {\n const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;\n const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;\n const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;\n\n const href = googleFontHref({\n title: options.title,\n header: headerSpec,\n body: bodySpec,\n code: codeSpec,\n });\n\n return [\n h(\"link\", { rel: \"preconnect\", href: \"https://fonts.googleapis.com\" }),\n h(\"link\", { rel: \"preconnect\", href: \"https://fonts.gstatic.com\", crossOrigin: \"anonymous\" }),\n h(\"link\", { rel: \"stylesheet\", href }),\n ];\n}\n\nexport const QuartzFonts: QuartzTransformerPlugin> = (\n userOptions?: Partial,\n) => {\n const options: QuartzFontsOptions = { ...defaultOptions, ...userOptions };\n\n if (options.fontOrigin === \"googleFonts\") {\n runValidation(options);\n }\n\n return {\n name: \"QuartzFonts\",\n textTransform(_ctx, src) {\n return src;\n },\n externalResources(_ctx) {\n const fonts = resolveFonts(options);\n\n const css: CSSResource[] = [\n { content: buildLayeredCSS(fonts), inline: true },\n { content: buildUnlayeredCSS(fonts), inline: true },\n ];\n\n let additionalHead: unknown[] = [];\n if (options.fontOrigin === \"googleFonts\") {\n additionalHead = buildGoogleFontsHead(options);\n } else if (options.fontOrigin === \"selfHosted\") {\n additionalHead = [h(\"link\", { rel: \"stylesheet\", href: \"/static/fonts/quartz-fonts.css\" })];\n }\n\n return { css, js: [], additionalHead };\n },\n };\n};\n","import type { ProcessedFontResult, GoogleFontFile } from \"../types\";\n\nconst fontMimeMap: Record = {\n truetype: \"ttf\",\n woff: \"woff\",\n woff2: \"woff2\",\n opentype: \"otf\",\n};\n\nconst fontUrlPattern =\n /url\\((https:\\/\\/fonts.gstatic.com\\/.+(?:\\/|(?:kit=))(.+?)[.&].+?)\\)\\sformat\\('(\\w+?)'\\);/g;\n\nexport function processGoogleFonts(stylesheet: string, baseUrl: string): ProcessedFontResult {\n const fontFiles: GoogleFontFile[] = [];\n\n const processedStylesheet = stylesheet.replace(\n fontUrlPattern,\n (match, url: string, filename: string, format: string) => {\n const extension = fontMimeMap[format] ?? format;\n const rewrittenUrl = `https://${baseUrl}/static/fonts/${filename}.${extension}`;\n fontFiles.push({ url, filename, extension });\n return match.replace(url, rewrittenUrl);\n },\n );\n\n return { processedStylesheet, fontFiles };\n}\n","import fs from \"fs\";\nimport path from \"path\";\nimport type { QuartzEmitterPlugin, BuildCtx, FilePath } from \"@quartz-community/types\";\nimport type { QuartzFontsOptions } from \"./types\";\nimport { googleFontHref } from \"./util/google-fonts\";\nimport { processGoogleFonts } from \"./util/process-fonts\";\n\nconst QUARTZ_DEFAULT_HEADER = \"Schibsted Grotesk\";\nconst QUARTZ_DEFAULT_BODY = \"Source Sans Pro\";\nconst QUARTZ_DEFAULT_CODE = \"IBM Plex Mono\";\n\nconst defaultOptions: QuartzFontsOptions = {\n useThemeFonts: true,\n fontOrigin: \"googleFonts\",\n};\n\nexport const QuartzFontsEmitter: QuartzEmitterPlugin> = (\n userOptions?: Partial,\n) => {\n const options: QuartzFontsOptions = { ...defaultOptions, ...userOptions };\n\n return {\n name: \"QuartzFontsEmitter\",\n async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator {\n if (options.fontOrigin !== \"selfHosted\") {\n return;\n }\n\n const baseUrl = ctx.cfg.configuration.baseUrl;\n if (!baseUrl) {\n throw new Error(\"[QuartzFontsEmitter] baseUrl is required for selfHosted fonts.\");\n }\n\n const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER;\n const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY;\n const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE;\n\n const href = googleFontHref({\n title: options.title,\n header: headerSpec,\n body: bodySpec,\n code: codeSpec,\n });\n\n const cssResponse = await fetch(href);\n if (!cssResponse.ok) {\n throw new Error(\n `[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`,\n );\n }\n const cssText = await cssResponse.text();\n\n const { processedStylesheet, fontFiles } = processGoogleFonts(cssText, baseUrl);\n\n const fontsDir = path.join(ctx.argv.output, \"static\", \"fonts\");\n await fs.promises.mkdir(fontsDir, { recursive: true });\n\n for (const fontFile of fontFiles) {\n const fontResponse = await fetch(fontFile.url);\n if (!fontResponse.ok) {\n throw new Error(\n `[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`,\n );\n }\n const buf = await fontResponse.arrayBuffer();\n const filePath = path.join(fontsDir, `${fontFile.filename}.${fontFile.extension}`);\n await fs.promises.writeFile(filePath, Buffer.from(buf));\n yield filePath as FilePath;\n }\n\n const cssPath = path.join(fontsDir, \"quartz-fonts.css\");\n await fs.promises.writeFile(cssPath, processedStylesheet);\n yield cssPath as FilePath;\n },\n };\n};\n"]} \ No newline at end of file diff --git a/dist/types.d.ts b/dist/types.d.ts index ab7bd95..3afe33e 100644 --- a/dist/types.d.ts +++ b/dist/types.d.ts @@ -26,9 +26,19 @@ interface QuartzFontsOptions { * Where to load fonts from. * - `"googleFonts"`: generate Google Fonts `` tags. * - `"local"`: assume fonts are available locally (no loading). + * - `"selfHosted"`: download Google Fonts and serve from the build output. * Defaults to `"local"` (no automatic font loading). */ - fontOrigin?: "googleFonts" | "local"; + fontOrigin?: "googleFonts" | "local" | "selfHosted"; +} +interface ProcessedFontResult { + processedStylesheet: string; + fontFiles: GoogleFontFile[]; +} +interface GoogleFontFile { + url: string; + filename: string; + extension: string; } interface QuartzFontRegistry { themeName: string; @@ -45,4 +55,4 @@ interface FontFileEntry { unicodeRange?: string | null; } -export type { FontFileEntry, FontSpecification, QuartzFontRegistry, QuartzFontsOptions }; +export type { FontFileEntry, FontSpecification, GoogleFontFile, ProcessedFontResult, QuartzFontRegistry, QuartzFontsOptions }; diff --git a/package.json b/package.json index b7f96a5..c7907f1 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,10 @@ "quartz": { "name": "quartz-fonts", "displayName": "Quartz Fonts", - "category": "transformer", + "category": [ + "transformer", + "emitter" + ], "version": "1.0.0", "quartzVersion": ">=5.0.0", "dependencies": [], @@ -403,7 +406,8 @@ "type": "string", "enum": [ "googleFonts", - "local" + "local", + "selfHosted" ] } } diff --git a/src/emitter.ts b/src/emitter.ts new file mode 100644 index 0000000..91e173d --- /dev/null +++ b/src/emitter.ts @@ -0,0 +1,76 @@ +import fs from "fs"; +import path from "path"; +import type { QuartzEmitterPlugin, BuildCtx, FilePath } from "@quartz-community/types"; +import type { QuartzFontsOptions } from "./types"; +import { googleFontHref } from "./util/google-fonts"; +import { processGoogleFonts } from "./util/process-fonts"; + +const QUARTZ_DEFAULT_HEADER = "Schibsted Grotesk"; +const QUARTZ_DEFAULT_BODY = "Source Sans Pro"; +const QUARTZ_DEFAULT_CODE = "IBM Plex Mono"; + +const defaultOptions: QuartzFontsOptions = { + useThemeFonts: true, + fontOrigin: "googleFonts", +}; + +export const QuartzFontsEmitter: QuartzEmitterPlugin> = ( + userOptions?: Partial, +) => { + const options: QuartzFontsOptions = { ...defaultOptions, ...userOptions }; + + return { + name: "QuartzFontsEmitter", + async *emit(ctx: BuildCtx, _content: unknown[], _resources: unknown): AsyncGenerator { + if (options.fontOrigin !== "selfHosted") { + return; + } + + const baseUrl = ctx.cfg.configuration.baseUrl; + if (!baseUrl) { + throw new Error("[QuartzFontsEmitter] baseUrl is required for selfHosted fonts."); + } + + const headerSpec = options.header ?? QUARTZ_DEFAULT_HEADER; + const bodySpec = options.body ?? QUARTZ_DEFAULT_BODY; + const codeSpec = options.code ?? QUARTZ_DEFAULT_CODE; + + const href = googleFontHref({ + title: options.title, + header: headerSpec, + body: bodySpec, + code: codeSpec, + }); + + const cssResponse = await fetch(href); + if (!cssResponse.ok) { + throw new Error( + `[QuartzFontsEmitter] Failed to fetch Google Fonts CSS: ${cssResponse.status} ${cssResponse.statusText}`, + ); + } + const cssText = await cssResponse.text(); + + const { processedStylesheet, fontFiles } = processGoogleFonts(cssText, baseUrl); + + const fontsDir = path.join(ctx.argv.output, "static", "fonts"); + await fs.promises.mkdir(fontsDir, { recursive: true }); + + for (const fontFile of fontFiles) { + const fontResponse = await fetch(fontFile.url); + if (!fontResponse.ok) { + throw new Error( + `[QuartzFontsEmitter] Failed to fetch font file: ${fontFile.url} (${fontResponse.status} ${fontResponse.statusText})`, + ); + } + const buf = await fontResponse.arrayBuffer(); + const filePath = path.join(fontsDir, `${fontFile.filename}.${fontFile.extension}`); + await fs.promises.writeFile(filePath, Buffer.from(buf)); + yield filePath as FilePath; + } + + const cssPath = path.join(fontsDir, "quartz-fonts.css"); + await fs.promises.writeFile(cssPath, processedStylesheet); + yield cssPath as FilePath; + }, + }; +}; diff --git a/src/index.ts b/src/index.ts index 9861646..e30b11a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,13 @@ export { QuartzFonts } from "./transformer"; +export { QuartzFontsEmitter } from "./emitter"; export type { FontSpecification, QuartzFontsOptions, QuartzFontRegistry, FontFileEntry, + ProcessedFontResult, + GoogleFontFile, } from "./types"; -export type { QuartzTransformerPlugin } from "@quartz-community/types"; +export type { QuartzTransformerPlugin, QuartzEmitterPlugin } from "@quartz-community/types"; diff --git a/src/transformer.ts b/src/transformer.ts index 5d4d222..3d3127e 100644 --- a/src/transformer.ts +++ b/src/transformer.ts @@ -212,8 +212,12 @@ export const QuartzFonts: QuartzTransformerPlugin> = { content: buildUnlayeredCSS(fonts), inline: true }, ]; - const additionalHead: unknown[] = - options.fontOrigin === "googleFonts" ? buildGoogleFontsHead(options) : []; + let additionalHead: unknown[] = []; + if (options.fontOrigin === "googleFonts") { + additionalHead = buildGoogleFontsHead(options); + } else if (options.fontOrigin === "selfHosted") { + additionalHead = [h("link", { rel: "stylesheet", href: "/static/fonts/quartz-fonts.css" })]; + } return { css, js: [], additionalHead }; }, diff --git a/src/types.ts b/src/types.ts index 5ec963e..9fa74ca 100644 --- a/src/types.ts +++ b/src/types.ts @@ -36,9 +36,21 @@ export interface QuartzFontsOptions { * Where to load fonts from. * - `"googleFonts"`: generate Google Fonts `` tags. * - `"local"`: assume fonts are available locally (no loading). + * - `"selfHosted"`: download Google Fonts and serve from the build output. * Defaults to `"local"` (no automatic font loading). */ - fontOrigin?: "googleFonts" | "local"; + fontOrigin?: "googleFonts" | "local" | "selfHosted"; +} + +export interface ProcessedFontResult { + processedStylesheet: string; + fontFiles: GoogleFontFile[]; +} + +export interface GoogleFontFile { + url: string; + filename: string; + extension: string; } export interface QuartzFontRegistry { diff --git a/src/util/process-fonts.ts b/src/util/process-fonts.ts new file mode 100644 index 0000000..f50778e --- /dev/null +++ b/src/util/process-fonts.ts @@ -0,0 +1,27 @@ +import type { ProcessedFontResult, GoogleFontFile } from "../types"; + +const fontMimeMap: Record = { + truetype: "ttf", + woff: "woff", + woff2: "woff2", + opentype: "otf", +}; + +const fontUrlPattern = + /url\((https:\/\/fonts.gstatic.com\/.+(?:\/|(?:kit=))(.+?)[.&].+?)\)\sformat\('(\w+?)'\);/g; + +export function processGoogleFonts(stylesheet: string, baseUrl: string): ProcessedFontResult { + const fontFiles: GoogleFontFile[] = []; + + const processedStylesheet = stylesheet.replace( + fontUrlPattern, + (match, url: string, filename: string, format: string) => { + const extension = fontMimeMap[format] ?? format; + const rewrittenUrl = `https://${baseUrl}/static/fonts/${filename}.${extension}`; + fontFiles.push({ url, filename, extension }); + return match.replace(url, rewrittenUrl); + }, + ); + + return { processedStylesheet, fontFiles }; +} diff --git a/test/transformer.test.ts b/test/transformer.test.ts index bee671c..64637cc 100644 --- a/test/transformer.test.ts +++ b/test/transformer.test.ts @@ -3,6 +3,7 @@ import type { BuildCtx } from "@quartz-community/types"; import { QuartzFonts } from "../src/transformer"; import { formatFontSpecification, googleFontHref, getFontName } from "../src/util/google-fonts"; +import { processGoogleFonts } from "../src/util/process-fonts"; import { validateFontSpec } from "../src/util/validate"; const REGISTRY_KEY = "__quartzFonts"; @@ -200,6 +201,39 @@ describe("QuartzFonts", () => { }); }); + describe("selfHosted fontOrigin", () => { + it("injects link to self-hosted CSS file instead of Google Fonts", () => { + const head = getAdditionalHead( + QuartzFonts({ + fontOrigin: "selfHosted", + body: "Inter", + header: "Playfair Display", + code: "JetBrains Mono", + }), + ); + + expect(head).toHaveLength(1); + const link = head[0] as { props: Record }; + expect(link.props.rel).toBe("stylesheet"); + expect(link.props.href).toBe("/static/fonts/quartz-fonts.css"); + }); + + it("still emits layered CSS with font variables", () => { + const css = getCSSContent( + QuartzFonts({ + fontOrigin: "selfHosted", + body: "Inter", + header: "Playfair Display", + code: "JetBrains Mono", + }), + ); + + expect(css).toContain("--bodyFont: Inter"); + expect(css).toContain("--headerFont: Playfair Display"); + expect(css).toContain("--codeFont: JetBrains Mono"); + }); + }); + describe("with QuartzTheme registry", () => { beforeEach(() => { setRegistry({ @@ -380,3 +414,44 @@ describe("validateFontSpec", () => { expect(Array.isArray(warnings)).toBe(true); }); }); + +describe("processGoogleFonts", () => { + it("rewrites font URLs to self-hosted paths", () => { + const css = ` +@font-face { + font-family: 'Inter'; + src: url(https://fonts.gstatic.com/s/inter/v18/abc123def456.woff2) format('woff2'); +}`; + const { processedStylesheet, fontFiles } = processGoogleFonts(css, "example.com"); + + expect(processedStylesheet).toContain("https://example.com/static/fonts/abc123def456.woff2"); + expect(processedStylesheet).not.toContain("fonts.gstatic.com"); + expect(fontFiles).toHaveLength(1); + const [first] = fontFiles; + expect(first).toBeDefined(); + if (!first) { + throw new Error("Expected at least one font file"); + } + expect(first.filename).toBe("abc123def456"); + expect(first.extension).toBe("woff2"); + }); + + it("handles multiple font faces", () => { + const css = ` +@font-face { + font-family: 'Inter'; + src: url(https://fonts.gstatic.com/s/inter/v18/abc123.woff2) format('woff2'); +} +@font-face { + font-family: 'Inter'; + src: url(https://fonts.gstatic.com/s/inter/v18/def456.woff) format('woff'); +}`; + const { fontFiles } = processGoogleFonts(css, "example.com"); + expect(fontFiles).toHaveLength(2); + }); + + it("returns empty fontFiles for CSS without font URLs", () => { + const { fontFiles } = processGoogleFonts("body { color: red; }", "example.com"); + expect(fontFiles).toHaveLength(0); + }); +}); diff --git a/tsup.config.ts b/tsup.config.ts index e52a013..9dec135 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -91,6 +91,7 @@ export default defineConfig({ entry: { index: "src/index.ts", types: "src/types.ts", + emitter: "src/emitter.ts", }, format: ["esm"], dts: true,