From 4811500b1b9c169aac82254d771677cd6dc2a86c Mon Sep 17 00:00:00 2001
From: Jacky Zhao <j.zhao2k19@gmail.com>
Date: Mon, 24 Jul 2023 01:20:43 +0000
Subject: [PATCH] make component resources a proper emitter
---
quartz/plugins/emitters/componentResources.ts | 154 +++++++++++++++++++++++++
quartz/plugins/index.ts | 77 ------------
content/features/upcoming features.md | 1
quartz/processors/emit.ts | 98 ----------------
quartz/plugins/emitters/index.ts | 1
quartz/bootstrap-cli.mjs | 8
quartz.config.ts | 1
7 files changed, 163 insertions(+), 177 deletions(-)
diff --git a/content/features/upcoming features.md b/content/features/upcoming features.md
index 8e7711e..5c57dba 100644
--- a/content/features/upcoming features.md
+++ b/content/features/upcoming features.md
@@ -4,7 +4,6 @@
## high priority
-- component resources should be emitted by an emitter
- https://help.obsidian.md/Editing+and+formatting/Tags#Nested+tags nested tags??
- watch mode for config/source code
- https://help.obsidian.md/Editing+and+formatting/Basic+formatting+syntax#Task+lists task list styling
diff --git a/quartz.config.ts b/quartz.config.ts
index 0bbc181..5378a8e 100644
--- a/quartz.config.ts
+++ b/quartz.config.ts
@@ -95,6 +95,7 @@
filters: [Plugin.RemoveDrafts()],
emitters: [
Plugin.AliasRedirects(),
+ Plugin.ComponentResources(),
Plugin.ContentPage({
...sharedPageComponents,
...contentPageLayout,
diff --git a/quartz/bootstrap-cli.mjs b/quartz/bootstrap-cli.mjs
index 475e624..aa26ffc 100755
--- a/quartz/bootstrap-cli.mjs
+++ b/quartz/bootstrap-cli.mjs
@@ -82,8 +82,8 @@
bundleInfo: {
boolean: true,
default: false,
- describe: "show detailed bundle information"
- }
+ describe: "show detailed bundle information",
+ },
}
function escapePath(fp) {
@@ -351,9 +351,9 @@
console.log(
`Successfully transpiled ${Object.keys(meta.inputs).length} files (${prettyBytes(
meta.bytes,
- )})`)
- console.log(await esbuild.analyzeMetafile(result.metafile, { color: true })
+ )})`,
)
+ console.log(await esbuild.analyzeMetafile(result.metafile, { color: true }))
}
const { default: buildQuartz } = await import(cacheFile)
diff --git a/quartz/plugins/emitters/componentResources.ts b/quartz/plugins/emitters/componentResources.ts
new file mode 100644
index 0000000..99f9657
--- /dev/null
+++ b/quartz/plugins/emitters/componentResources.ts
@@ -0,0 +1,154 @@
+import { FilePath, ServerSlug } from "../../path"
+import { PluginTypes, QuartzEmitterPlugin } from "../types"
+
+// @ts-ignore
+import spaRouterScript from "../../components/scripts/spa.inline"
+// @ts-ignore
+import plausibleScript from "../../components/scripts/plausible.inline"
+// @ts-ignore
+import popoverScript from "../../components/scripts/popover.inline"
+import styles from "../../styles/base.scss"
+import popoverStyle from "../../components/styles/popover.scss"
+import { BuildCtx } from "../../ctx"
+import { StaticResources } from "../../resources"
+import { QuartzComponent } from "../../components/types"
+import { googleFontHref, joinStyles } from "../../theme"
+
+type ComponentResources = {
+ css: string[]
+ beforeDOMLoaded: string[]
+ afterDOMLoaded: string[]
+}
+
+function getComponentResources(plugins: PluginTypes): ComponentResources {
+ const allComponents: Set<QuartzComponent> = new Set()
+ for (const emitter of plugins.emitters) {
+ const components = emitter.getQuartzComponents()
+ for (const component of components) {
+ allComponents.add(component)
+ }
+ }
+
+ const componentResources = {
+ css: new Set<string>(),
+ beforeDOMLoaded: new Set<string>(),
+ afterDOMLoaded: new Set<string>(),
+ }
+
+ for (const component of allComponents) {
+ const { css, beforeDOMLoaded, afterDOMLoaded } = component
+ if (css) {
+ componentResources.css.add(css)
+ }
+ if (beforeDOMLoaded) {
+ componentResources.beforeDOMLoaded.add(beforeDOMLoaded)
+ }
+ if (afterDOMLoaded) {
+ componentResources.afterDOMLoaded.add(afterDOMLoaded)
+ }
+ }
+
+ return {
+ css: [...componentResources.css],
+ beforeDOMLoaded: [...componentResources.beforeDOMLoaded],
+ afterDOMLoaded: [...componentResources.afterDOMLoaded],
+ }
+}
+
+function joinScripts(scripts: string[]): string {
+ // wrap with iife to prevent scope collision
+ return scripts.map((script) => `(function () {${script}})();`).join("\n")
+}
+
+function addGlobalPageResources(
+ ctx: BuildCtx,
+ staticResources: StaticResources,
+ componentResources: ComponentResources,
+) {
+ const cfg = ctx.cfg.configuration
+ const reloadScript = ctx.argv.serve
+ staticResources.css.push(googleFontHref(cfg.theme))
+
+ // popovers
+ if (cfg.enablePopovers) {
+ componentResources.afterDOMLoaded.push(popoverScript)
+ componentResources.css.push(popoverStyle)
+ }
+
+ 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(`
+ window.dataLayer = window.dataLayer || [];
+ function gtag() { dataLayer.push(arguments); }
+ gtag(\`js\`, new Date());
+ gtag(\`config\`, \`${tagId}\`, { send_page_view: false });
+
+ document.addEventListener(\`nav\`, () => {
+ gtag(\`event\`, \`page_view\`, {
+ page_title: document.title,
+ page_location: location.href,
+ });
+ });`)
+ } else if (cfg.analytics?.provider === "plausible") {
+ componentResources.afterDOMLoaded.push(plausibleScript)
+ }
+
+ // spa
+ if (cfg.enableSPA) {
+ componentResources.afterDOMLoaded.push(spaRouterScript)
+ } else {
+ componentResources.afterDOMLoaded.push(`
+ window.spaNavigate = (url, _) => window.location.assign(url)
+ const event = new CustomEvent("nav", { detail: { slug: document.body.dataset.slug } })
+ document.dispatchEvent(event)`)
+ }
+
+ if (reloadScript) {
+ staticResources.js.push({
+ loadTime: "afterDOMReady",
+ contentType: "inline",
+ script: `
+ const socket = new WebSocket('ws://localhost:3001')
+ socket.addEventListener('message', () => document.location.reload())
+ `,
+ })
+ }
+}
+
+export const ComponentResources: QuartzEmitterPlugin = () => ({
+ name: "ComponentResources",
+ getQuartzComponents() {
+ return []
+ },
+ async emit(ctx, _content, resources, emit): Promise<FilePath[]> {
+ // component specific scripts and styles
+ const componentResources = getComponentResources(ctx.cfg.plugins)
+ // 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)
+ const fps = await Promise.all([
+ emit({
+ slug: "index" as ServerSlug,
+ ext: ".css",
+ content: joinStyles(ctx.cfg.configuration.theme, styles, ...componentResources.css),
+ }),
+ emit({
+ slug: "prescript" as ServerSlug,
+ ext: ".js",
+ content: joinScripts(componentResources.beforeDOMLoaded),
+ }),
+ emit({
+ slug: "postscript" as ServerSlug,
+ ext: ".js",
+ content: joinScripts(componentResources.afterDOMLoaded),
+ }),
+ ])
+ return fps
+ },
+})
diff --git a/quartz/plugins/emitters/index.ts b/quartz/plugins/emitters/index.ts
index bb18361..d1149f7 100644
--- a/quartz/plugins/emitters/index.ts
+++ b/quartz/plugins/emitters/index.ts
@@ -5,3 +5,4 @@
export { AliasRedirects } from "./aliases"
export { Assets } from "./assets"
export { Static } from "./static"
+export { ComponentResources } from "./componentResources"
\ No newline at end of file
diff --git a/quartz/plugins/index.ts b/quartz/plugins/index.ts
index a6231db..a8208e3 100644
--- a/quartz/plugins/index.ts
+++ b/quartz/plugins/index.ts
@@ -1,82 +1,7 @@
-import { GlobalConfiguration } from "../cfg"
-import { QuartzComponent } from "../components/types"
import { StaticResources } from "../resources"
-import { joinStyles } from "../theme"
-import { EmitCallback, PluginTypes } from "./types"
-import styles from "../styles/base.scss"
+import { PluginTypes } from "./types"
import { FilePath, ServerSlug } from "../path"
-export type ComponentResources = {
- css: string[]
- beforeDOMLoaded: string[]
- afterDOMLoaded: string[]
-}
-
-export function getComponentResources(plugins: PluginTypes): ComponentResources {
- const allComponents: Set<QuartzComponent> = new Set()
- for (const emitter of plugins.emitters) {
- const components = emitter.getQuartzComponents()
- for (const component of components) {
- allComponents.add(component)
- }
- }
-
- const componentResources = {
- css: new Set<string>(),
- beforeDOMLoaded: new Set<string>(),
- afterDOMLoaded: new Set<string>(),
- }
-
- for (const component of allComponents) {
- const { css, beforeDOMLoaded, afterDOMLoaded } = component
- if (css) {
- componentResources.css.add(css)
- }
- if (beforeDOMLoaded) {
- componentResources.beforeDOMLoaded.add(beforeDOMLoaded)
- }
- if (afterDOMLoaded) {
- componentResources.afterDOMLoaded.add(afterDOMLoaded)
- }
- }
-
- return {
- css: [...componentResources.css],
- beforeDOMLoaded: [...componentResources.beforeDOMLoaded],
- afterDOMLoaded: [...componentResources.afterDOMLoaded],
- }
-}
-
-function joinScripts(scripts: string[]): string {
- // wrap with iife to prevent scope collision
- return scripts.map((script) => `(function () {${script}})();`).join("\n")
-}
-
-export async function emitComponentResources(
- cfg: GlobalConfiguration,
- res: ComponentResources,
- emit: EmitCallback,
-): Promise<FilePath[]> {
- const fps = await Promise.all([
- emit({
- slug: "index" as ServerSlug,
- ext: ".css",
- content: joinStyles(cfg.theme, styles, ...res.css),
- }),
- emit({
- slug: "prescript" as ServerSlug,
- ext: ".js",
- content: joinScripts(res.beforeDOMLoaded),
- }),
- emit({
- slug: "postscript" as ServerSlug,
- ext: ".js",
- content: joinScripts(res.afterDOMLoaded),
- }),
- ])
- return fps
-}
-
export function getStaticResourcesFromPlugins(plugins: PluginTypes) {
const staticResources: StaticResources = {
css: [],
diff --git a/quartz/processors/emit.ts b/quartz/processors/emit.ts
index ea7fda9..570f505 100644
--- a/quartz/processors/emit.ts
+++ b/quartz/processors/emit.ts
@@ -1,89 +1,14 @@
import path from "path"
import fs from "fs"
import { PerfTimer } from "../perf"
-import {
- ComponentResources,
- emitComponentResources,
- getComponentResources,
- getStaticResourcesFromPlugins,
-} from "../plugins"
+import { getStaticResourcesFromPlugins } from "../plugins"
import { EmitCallback } from "../plugins/types"
import { ProcessedContent } from "../plugins/vfile"
import { FilePath } from "../path"
-
-// @ts-ignore
-import spaRouterScript from "../components/scripts/spa.inline"
-// @ts-ignore
-import plausibleScript from "../components/scripts/plausible.inline"
-// @ts-ignore
-import popoverScript from "../components/scripts/popover.inline"
-import popoverStyle from "../components/styles/popover.scss"
-import { StaticResources } from "../resources"
import { QuartzLogger } from "../log"
-import { googleFontHref } from "../theme"
import { trace } from "../trace"
import { BuildCtx } from "../ctx"
-function addGlobalPageResources(
- ctx: BuildCtx,
- staticResources: StaticResources,
- componentResources: ComponentResources,
-) {
- const cfg = ctx.cfg.configuration
- const reloadScript = ctx.argv.serve
- staticResources.css.push(googleFontHref(cfg.theme))
-
- // popovers
- if (cfg.enablePopovers) {
- componentResources.afterDOMLoaded.push(popoverScript)
- componentResources.css.push(popoverStyle)
- }
-
- 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(`
- window.dataLayer = window.dataLayer || [];
- function gtag() { dataLayer.push(arguments); }
- gtag(\`js\`, new Date());
- gtag(\`config\`, \`${tagId}\`, { send_page_view: false });
-
- document.addEventListener(\`nav\`, () => {
- gtag(\`event\`, \`page_view\`, {
- page_title: document.title,
- page_location: location.href,
- });
- });`)
- } else if (cfg.analytics?.provider === "plausible") {
- componentResources.afterDOMLoaded.push(plausibleScript)
- }
-
- // spa
- if (cfg.enableSPA) {
- componentResources.afterDOMLoaded.push(spaRouterScript)
- } else {
- componentResources.afterDOMLoaded.push(`
- window.spaNavigate = (url, _) => window.location.assign(url)
- const event = new CustomEvent("nav", { detail: { slug: document.body.dataset.slug } })
- document.dispatchEvent(event)`)
- }
-
- if (reloadScript) {
- staticResources.js.push({
- loadTime: "afterDOMReady",
- contentType: "inline",
- script: `
- const socket = new WebSocket('ws://localhost:3001')
- socket.addEventListener('message', () => document.location.reload())
- `,
- })
- }
-}
-
export async function emitContent(ctx: BuildCtx, content: ProcessedContent[]) {
const { argv, cfg } = ctx
const perf = new PerfTimer()
@@ -98,27 +23,8 @@
return pathToPage
}
- // initialize from plugins
- const staticResources = getStaticResourcesFromPlugins(cfg.plugins)
-
- // component specific scripts and styles
- const componentResources = getComponentResources(cfg.plugins)
-
- // 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, staticResources, componentResources)
-
let emittedFiles = 0
- const emittedResources = await emitComponentResources(cfg.configuration, componentResources, emit)
- if (argv.verbose) {
- for (const file of emittedResources) {
- emittedFiles += 1
- console.log(`[emit:Resources] ${file}`)
- }
- }
-
- // emitter plugins
+ const staticResources = getStaticResourcesFromPlugins(cfg.plugins)
for (const emitter of cfg.plugins.emitters) {
try {
const emitted = await emitter.emit(ctx, content, staticResources, emit)
--
Gitblit v1.10.0