feat(fonts): fetch before build (#817)
* feat: fetch google fonts before build Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * Update quartz/plugins/emitters/componentResources.ts * fix: fetching wolff2 Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * chore: remove request stylesheet Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * fix: race condition Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * chore: remove preconnect for static fonts since we are already downloading fonts into public folder Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * chore: remove deadcode Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * chore: add options to gate for cdn caching Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * chore: apply jacky's suggestion Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * chore: add docs and only use one promise Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * fix: fmt Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> * chore: remove deadcode * chore: final touches Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com> * revert: changes in theme.ts * fix: styles and remove deadcode Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> --------- Signed-off-by: Aaron <29749331+aarnphm@users.noreply.github.com> Co-authored-by: Jacky Zhao <j.zhao2k19@gmail.com>
This commit is contained in:
		@@ -34,6 +34,7 @@ This part of the configuration concerns anything that can affect the whole site.
 | 
			
		||||
- `ignorePatterns`: a list of [glob](<https://en.wikipedia.org/wiki/Glob_(programming)>) patterns that Quartz should ignore and not search through when looking for files inside the `content` folder. See [[private pages]] for more details.
 | 
			
		||||
- `defaultDateType`: whether to use created, modified, or published as the default date to display on pages and page listings.
 | 
			
		||||
- `theme`: configure how the site looks.
 | 
			
		||||
  - `cdnCaching`: Whether to use Google CDN to cache the fonts (generally will be faster). Disable this if you want Quartz to be self-contained. Default to `true`
 | 
			
		||||
  - `typography`: what fonts to use. Any font available on [Google Fonts](https://fonts.google.com/) works here.
 | 
			
		||||
    - `header`: Font to use for headers
 | 
			
		||||
    - `code`: Font for inline and block quotes.
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ const config: QuartzConfig = {
 | 
			
		||||
    ignorePatterns: ["private", "templates", ".obsidian"],
 | 
			
		||||
    defaultDateType: "created",
 | 
			
		||||
    theme: {
 | 
			
		||||
      cdnCaching: true,
 | 
			
		||||
      typography: {
 | 
			
		||||
        header: "Schibsted Grotesk",
 | 
			
		||||
        body: "Source Sans Pro",
 | 
			
		||||
 
 | 
			
		||||
@@ -30,8 +30,12 @@ export default (() => {
 | 
			
		||||
        <link rel="icon" href={iconPath} />
 | 
			
		||||
        <meta name="description" content={description} />
 | 
			
		||||
        <meta name="generator" content="Quartz" />
 | 
			
		||||
        {cfg.theme.cdnCaching && (
 | 
			
		||||
          <>
 | 
			
		||||
            <link rel="preconnect" href="https://fonts.googleapis.com" />
 | 
			
		||||
            <link rel="preconnect" href="https://fonts.gstatic.com" />
 | 
			
		||||
          </>
 | 
			
		||||
        )}
 | 
			
		||||
        {css.map((href) => (
 | 
			
		||||
          <link key={href} href={href} rel="stylesheet" type="text/css" spa-preserve />
 | 
			
		||||
        ))}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import { FilePath, FullSlug } from "../../util/path"
 | 
			
		||||
import { FilePath, FullSlug, joinSegments } from "../../util/path"
 | 
			
		||||
import { QuartzEmitterPlugin } from "../types"
 | 
			
		||||
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
@@ -172,27 +172,72 @@ export const ComponentResources: QuartzEmitterPlugin<Options> = (opts?: Partial<
 | 
			
		||||
      return []
 | 
			
		||||
    },
 | 
			
		||||
    async emit(ctx, _content, resources): Promise<FilePath[]> {
 | 
			
		||||
      const promises: Promise<FilePath>[] = []
 | 
			
		||||
      const cfg = ctx.cfg.configuration
 | 
			
		||||
      // component specific scripts and styles
 | 
			
		||||
      const componentResources = getComponentResources(ctx)
 | 
			
		||||
      // 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
 | 
			
		||||
 | 
			
		||||
      if (fontOrigin === "googleFonts") {
 | 
			
		||||
        resources.css.push(googleFontHref(ctx.cfg.configuration.theme))
 | 
			
		||||
      } else if (fontOrigin === "local") {
 | 
			
		||||
      let googleFontsStyleSheet = ""
 | 
			
		||||
      if (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
 | 
			
		||||
 | 
			
		||||
          const fontSourceRegex = /url\((https:\/\/fonts.gstatic.com\/s\/[^)]+\.(woff2|ttf))\)/g
 | 
			
		||||
 | 
			
		||||
          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(".")
 | 
			
		||||
 | 
			
		||||
            googleFontsStyleSheet = googleFontsStyleSheet.replace(url, `/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("fonts", filename) as FullSlug,
 | 
			
		||||
                    ext: `.${ext}`,
 | 
			
		||||
                    content: Buffer.from(buf),
 | 
			
		||||
                  }),
 | 
			
		||||
                ),
 | 
			
		||||
            )
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      addGlobalPageResources(ctx, resources, componentResources)
 | 
			
		||||
 | 
			
		||||
      const stylesheet = joinStyles(ctx.cfg.configuration.theme, ...componentResources.css, styles)
 | 
			
		||||
      const stylesheet = joinStyles(
 | 
			
		||||
        ctx.cfg.configuration.theme,
 | 
			
		||||
        ...componentResources.css,
 | 
			
		||||
        googleFontsStyleSheet,
 | 
			
		||||
        styles,
 | 
			
		||||
      )
 | 
			
		||||
      const [prescript, postscript] = await Promise.all([
 | 
			
		||||
        joinScripts(componentResources.beforeDOMLoaded),
 | 
			
		||||
        joinScripts(componentResources.afterDOMLoaded),
 | 
			
		||||
      ])
 | 
			
		||||
 | 
			
		||||
      const fps = await Promise.all([
 | 
			
		||||
      promises.push(
 | 
			
		||||
        write({
 | 
			
		||||
          ctx,
 | 
			
		||||
          slug: "index" as FullSlug,
 | 
			
		||||
@@ -223,8 +268,9 @@ export const ComponentResources: QuartzEmitterPlugin<Options> = (opts?: Partial<
 | 
			
		||||
          ext: ".js",
 | 
			
		||||
          content: postscript,
 | 
			
		||||
        }),
 | 
			
		||||
      ])
 | 
			
		||||
      return fps
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      return await Promise.all(promises)
 | 
			
		||||
    },
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ type WriteOptions = {
 | 
			
		||||
  ctx: BuildCtx
 | 
			
		||||
  slug: FullSlug
 | 
			
		||||
  ext: `.${string}` | ""
 | 
			
		||||
  content: string
 | 
			
		||||
  content: string | Buffer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const write = async ({ ctx, slug, ext, content }: WriteOptions): Promise<FilePath> => {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ export interface Theme {
 | 
			
		||||
    body: string
 | 
			
		||||
    code: string
 | 
			
		||||
  }
 | 
			
		||||
  cdnCaching: boolean
 | 
			
		||||
  colors: {
 | 
			
		||||
    lightMode: ColorScheme
 | 
			
		||||
    darkMode: ColorScheme
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user