From b4236e5142c31829cf809c0fbc8370ac22b6d1ba Mon Sep 17 00:00:00 2001 From: kabirgh <15871468+kabirgh@users.noreply.github.com> Date: Sun, 10 Mar 2024 00:42:23 +0000 Subject: [PATCH] feat(perf:fast-rebuilds): Stop mutating resources param in ComponentResources emitter (#977) * Stop mutating resources param in ComponentResources emitter * Add done rebuilding log for fast rebuilds * Move google font loading to Head component * Simplify code and fix comment --- quartz.config.ts | 3 +- quartz/build.ts | 2 + quartz/components/Head.tsx | 4 +- quartz/plugins/emitters/componentResources.ts | 143 ++++++------------ quartz/plugins/index.ts | 17 +++ quartz/util/theme.ts | 1 + 6 files changed, 72 insertions(+), 98 deletions(-) diff --git a/quartz.config.ts b/quartz.config.ts index 2cdadb7..4b98325 100644 --- a/quartz.config.ts +++ b/quartz.config.ts @@ -19,6 +19,7 @@ const config: QuartzConfig = { ignorePatterns: ["private", "templates", ".obsidian"], defaultDateType: "created", theme: { + fontOrigin: "googleFonts", cdnCaching: true, typography: { header: "Schibsted Grotesk", @@ -72,7 +73,7 @@ const config: QuartzConfig = { filters: [Plugin.RemoveDrafts()], emitters: [ Plugin.AliasRedirects(), - Plugin.ComponentResources({ fontOrigin: "googleFonts" }), + Plugin.ComponentResources(), Plugin.ContentPage(), Plugin.FolderPage(), Plugin.TagPage(), diff --git a/quartz/build.ts b/quartz/build.ts index d72b8dd..972a7e8 100644 --- a/quartz/build.ts +++ b/quartz/build.ts @@ -309,6 +309,8 @@ async function partialRebuildFromEntrypoint( } await rimraf([...destinationsToDelete]) + console.log(chalk.green(`Done rebuilding in ${perf.timeSince()}`)) + toRemove.clear() release() clientRefresh() diff --git a/quartz/components/Head.tsx b/quartz/components/Head.tsx index 3cb6bea..46ba5e0 100644 --- a/quartz/components/Head.tsx +++ b/quartz/components/Head.tsx @@ -1,6 +1,7 @@ import { i18n } from "../i18n" import { FullSlug, joinSegments, pathToRoot } from "../util/path" import { JSResourceToScriptElement } from "../util/resources" +import { googleFontHref } from "../util/theme" import { QuartzComponent, QuartzComponentConstructor, QuartzComponentProps } from "./types" export default (() => { @@ -21,10 +22,11 @@ export default (() => { {title} - {cfg.theme.cdnCaching && ( + {cfg.theme.cdnCaching && cfg.theme.fontOrigin === "googleFonts" && ( <> + )} diff --git a/quartz/plugins/emitters/componentResources.ts b/quartz/plugins/emitters/componentResources.ts index eae496e..0bccb60 100644 --- a/quartz/plugins/emitters/componentResources.ts +++ b/quartz/plugins/emitters/componentResources.ts @@ -8,7 +8,6 @@ import popoverScript from "../../components/scripts/popover.inline" import styles from "../../styles/custom.scss" import popoverStyle from "../../components/styles/popover.scss" import { BuildCtx } from "../../util/ctx" -import { StaticResources } from "../../util/resources" import { QuartzComponent } from "../../components/types" import { googleFontHref, joinStyles } from "../../util/theme" import { Features, transform } from "lightningcss" @@ -69,13 +68,8 @@ async function joinScripts(scripts: string[]): Promise { return res.code } -function addGlobalPageResources( - ctx: BuildCtx, - staticResources: StaticResources, - componentResources: ComponentResources, -) { +function addGlobalPageResources(ctx: BuildCtx, componentResources: ComponentResources) { const cfg = ctx.cfg.configuration - const reloadScript = ctx.argv.serve // popovers if (cfg.enablePopovers) { @@ -85,12 +79,12 @@ function addGlobalPageResources( if (cfg.analytics?.provider === "google") { const tagId = cfg.analytics.tagId - staticResources.js.push({ - src: `https://www.googletagmanager.com/gtag/js?id=${tagId}`, - contentType: "external", - loadTime: "afterDOMReady", - }) componentResources.afterDOMLoaded.push(` + const gtagScript = document.createElement("script") + gtagScript.src = "https://www.googletagmanager.com/gtag/js?id=${tagId}" + gtagScript.async = true + document.head.appendChild(gtagScript) + window.dataLayer = window.dataLayer || []; function gtag() { dataLayer.push(arguments); } gtag("js", new Date()); @@ -147,115 +141,72 @@ function addGlobalPageResources( document.dispatchEvent(event) `) } - - let wsUrl = `ws://localhost:${ctx.argv.wsPort}` - - if (ctx.argv.remoteDevHost) { - wsUrl = `wss://${ctx.argv.remoteDevHost}:${ctx.argv.wsPort}` - } - - if (reloadScript) { - staticResources.js.push({ - loadTime: "afterDOMReady", - contentType: "inline", - script: ` - const socket = new WebSocket('${wsUrl}') - // reload(true) ensures resources like images and scripts are fetched again in firefox - socket.addEventListener('message', () => document.location.reload(true)) - `, - }) - } } -interface Options { - fontOrigin: "googleFonts" | "local" -} - -const defaultOptions: Options = { - fontOrigin: "googleFonts", -} - -export const ComponentResources: QuartzEmitterPlugin = (opts?: Partial) => { - const { fontOrigin } = { ...defaultOptions, ...opts } +// This emitter should not update the `resources` parameter. If it does, partial +// rebuilds may not work as expected. +export const ComponentResources: QuartzEmitterPlugin = () => { return { name: "ComponentResources", getQuartzComponents() { return [] }, - async getDependencyGraph(ctx, content, _resources) { - // This emitter adds static resources to the `resources` parameter. One - // important resource this emitter adds is the code to start a websocket - // connection and listen to rebuild messages, which triggers a page reload. - // The resources parameter with the reload logic is later used by the - // ContentPage emitter while creating the final html page. In order for - // the reload logic to be included, and so for partial rebuilds to work, - // we need to run this emitter for all markdown files. - const graph = new DepGraph() - - for (const [_tree, file] of content) { - const sourcePath = file.data.filePath! - const slug = file.data.slug! - graph.addEdge(sourcePath, joinSegments(ctx.argv.output, slug + ".html") as FilePath) - } - - return graph + async getDependencyGraph(_ctx, _content, _resources) { + return new DepGraph() }, - async emit(ctx, _content, resources): Promise { + async emit(ctx, _content, _resources): Promise { const promises: Promise[] = [] const cfg = ctx.cfg.configuration // component specific scripts and styles const componentResources = getComponentResources(ctx) let googleFontsStyleSheet = "" - if (fontOrigin === "local") { + if (cfg.theme.fontOrigin === "local") { // let the user do it themselves in css - } else if (fontOrigin === "googleFonts") { - if (cfg.theme.cdnCaching) { - resources.css.push(googleFontHref(cfg.theme)) - } else { - let match + } else if (cfg.theme.fontOrigin === "googleFonts" && !cfg.theme.cdnCaching) { + // when cdnCaching is true, we link to google fonts in Head.tsx + let match - const fontSourceRegex = /url\((https:\/\/fonts.gstatic.com\/s\/[^)]+\.(woff2|ttf))\)/g + const fontSourceRegex = /url\((https:\/\/fonts.gstatic.com\/s\/[^)]+\.(woff2|ttf))\)/g - googleFontsStyleSheet = await ( - await fetch(googleFontHref(ctx.cfg.configuration.theme)) - ).text() + googleFontsStyleSheet = await ( + await fetch(googleFontHref(ctx.cfg.configuration.theme)) + ).text() - while ((match = fontSourceRegex.exec(googleFontsStyleSheet)) !== null) { - // match[0] is the `url(path)`, match[1] is the `path` - const url = match[1] - // the static name of this file. - const [filename, ext] = url.split("/").pop()!.split(".") + while ((match = fontSourceRegex.exec(googleFontsStyleSheet)) !== null) { + // match[0] is the `url(path)`, match[1] is the `path` + const url = match[1] + // the static name of this file. + const [filename, ext] = url.split("/").pop()!.split(".") - googleFontsStyleSheet = googleFontsStyleSheet.replace( - url, - `https://${cfg.baseUrl}/static/fonts/${filename}.ttf`, - ) + googleFontsStyleSheet = googleFontsStyleSheet.replace( + url, + `https://${cfg.baseUrl}/static/fonts/${filename}.ttf`, + ) - promises.push( - fetch(url) - .then((res) => { - if (!res.ok) { - throw new Error(`Failed to fetch font`) - } - return res.arrayBuffer() - }) - .then((buf) => - write({ - ctx, - slug: joinSegments("static", "fonts", filename) as FullSlug, - ext: `.${ext}`, - content: Buffer.from(buf), - }), - ), - ) - } + promises.push( + fetch(url) + .then((res) => { + if (!res.ok) { + throw new Error(`Failed to fetch font`) + } + return res.arrayBuffer() + }) + .then((buf) => + write({ + ctx, + slug: joinSegments("static", "fonts", filename) as FullSlug, + ext: `.${ext}`, + content: Buffer.from(buf), + }), + ), + ) } } // important that this goes *after* component scripts // as the "nav" event gets triggered here and we should make sure // that everyone else had the chance to register a listener for it - addGlobalPageResources(ctx, resources, componentResources) + addGlobalPageResources(ctx, componentResources) const stylesheet = joinStyles( ctx.cfg.configuration.theme, diff --git a/quartz/plugins/index.ts b/quartz/plugins/index.ts index f35d053..554b117 100644 --- a/quartz/plugins/index.ts +++ b/quartz/plugins/index.ts @@ -18,6 +18,23 @@ export function getStaticResourcesFromPlugins(ctx: BuildCtx) { } } + // if serving locally, listen for rebuilds and reload the page + if (ctx.argv.serve) { + const wsUrl = ctx.argv.remoteDevHost + ? `wss://${ctx.argv.remoteDevHost}:${ctx.argv.wsPort}` + : `ws://localhost:${ctx.argv.wsPort}` + + staticResources.js.push({ + loadTime: "afterDOMReady", + contentType: "inline", + script: ` + const socket = new WebSocket('${wsUrl}') + // reload(true) ensures resources like images and scripts are fetched again in firefox + socket.addEventListener('message', () => document.location.reload(true)) + `, + }) + } + return staticResources } diff --git a/quartz/util/theme.ts b/quartz/util/theme.ts index 49cc9cc..d3bfb9a 100644 --- a/quartz/util/theme.ts +++ b/quartz/util/theme.ts @@ -22,6 +22,7 @@ export interface Theme { } cdnCaching: boolean colors: Colors + fontOrigin: "googleFonts" | "local" } export type ThemeKey = keyof Colors