fix(analytics): spa tracking for other providers

This commit is contained in:
Jacky Zhao 2025-03-05 16:27:08 -08:00
parent c6f10b44f6
commit a1162b978a
4 changed files with 34 additions and 16 deletions

View file

@ -2,13 +2,13 @@ import { QuartzConfig } from "./quartz/cfg"
import * as Plugin from "./quartz/plugins" import * as Plugin from "./quartz/plugins"
/** /**
* Quartz 4.0 Configuration * Quartz 4 Configuration
* *
* See https://quartz.jzhao.xyz/configuration for more information. * See https://quartz.jzhao.xyz/configuration for more information.
*/ */
const config: QuartzConfig = { const config: QuartzConfig = {
configuration: { configuration: {
pageTitle: "🪴 Quartz 4.0", pageTitle: "🪴 Quartz 4",
pageTitleSuffix: "", pageTitleSuffix: "",
enableSPA: true, enableSPA: true,
enablePopovers: true, enablePopovers: true,

View file

@ -116,35 +116,53 @@ function addGlobalPageResources(ctx: BuildCtx, componentResources: ComponentReso
const umamiScript = document.createElement("script") const umamiScript = document.createElement("script")
umamiScript.src = "${cfg.analytics.host ?? "https://analytics.umami.is"}/script.js" umamiScript.src = "${cfg.analytics.host ?? "https://analytics.umami.is"}/script.js"
umamiScript.setAttribute("data-website-id", "${cfg.analytics.websiteId}") umamiScript.setAttribute("data-website-id", "${cfg.analytics.websiteId}")
umamiScript.setAttribute("data-auto-track", "false")
umamiScript.async = true umamiScript.async = true
document.head.appendChild(umamiScript) document.head.appendChild(umamiScript)
document.addEventListener("nav", () => {
umami.track();
})
`) `)
} else if (cfg.analytics?.provider === "goatcounter") { } else if (cfg.analytics?.provider === "goatcounter") {
componentResources.afterDOMLoaded.push(` componentResources.afterDOMLoaded.push(`
const goatcounterScript = document.createElement("script")
goatcounterScript.src = "${cfg.analytics.scriptSrc ?? "https://gc.zgo.at/count.js"}"
goatcounterScript.async = true
goatcounterScript.setAttribute("data-goatcounter",
"https://${cfg.analytics.websiteId}.${cfg.analytics.host ?? "goatcounter.com"}/count")
document.head.appendChild(goatcounterScript)
window.goatcounter = { no_onload: true }
document.addEventListener("nav", () => { document.addEventListener("nav", () => {
const goatcounterScript = document.createElement("script") goatcounter.count({ path: location.pathname })
goatcounterScript.src = "${cfg.analytics.scriptSrc ?? "https://gc.zgo.at/count.js"}"
goatcounterScript.async = true
goatcounterScript.setAttribute("data-goatcounter",
"https://${cfg.analytics.websiteId}.${cfg.analytics.host ?? "goatcounter.com"}/count")
document.head.appendChild(goatcounterScript)
}) })
`) `)
} else if (cfg.analytics?.provider === "posthog") { } else if (cfg.analytics?.provider === "posthog") {
componentResources.afterDOMLoaded.push(` componentResources.afterDOMLoaded.push(`
const posthogScript = document.createElement("script") const posthogScript = document.createElement("script")
posthogScript.innerHTML= \`!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys onSessionId".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]); posthogScript.innerHTML= \`!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getFeatureFlag getFeatureFlagPayload reloadFeatureFlags group updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures getActiveMatchingSurveys getSurveys onSessionId".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init('${cfg.analytics.apiKey}',{api_host:'${cfg.analytics.host ?? "https://app.posthog.com"}'})\` posthog.init('${cfg.analytics.apiKey}', {
api_host: '${cfg.analytics.host ?? "https://app.posthog.com"}',
capture_pageview: false,
})\`
document.head.appendChild(posthogScript) document.head.appendChild(posthogScript)
document.addEventListener("nav", () => {
posthog.capture('$pageview', { path: location.pathname })
})
`) `)
} else if (cfg.analytics?.provider === "tinylytics") { } else if (cfg.analytics?.provider === "tinylytics") {
const siteId = cfg.analytics.siteId const siteId = cfg.analytics.siteId
componentResources.afterDOMLoaded.push(` componentResources.afterDOMLoaded.push(`
const tinylyticsScript = document.createElement("script") const tinylyticsScript = document.createElement("script")
tinylyticsScript.src = "https://tinylytics.app/embed/${siteId}.js" tinylyticsScript.src = "https://tinylytics.app/embed/${siteId}.js?spa"
tinylyticsScript.defer = true tinylyticsScript.defer = true
document.head.appendChild(tinylyticsScript) document.head.appendChild(tinylyticsScript)
document.addEventListener("nav", () => {
window.tinylytics.triggerUpdate()
})
`) `)
} else if (cfg.analytics?.provider === "cabin") { } else if (cfg.analytics?.provider === "cabin") {
componentResources.afterDOMLoaded.push(` componentResources.afterDOMLoaded.push(`

View file

@ -9,7 +9,7 @@ import { write } from "./helpers"
import { i18n } from "../../i18n" import { i18n } from "../../i18n"
import DepGraph from "../../depgraph" import DepGraph from "../../depgraph"
export type ContentIndex = Map<FullSlug, ContentDetails> export type ContentIndexMap = Map<FullSlug, ContentDetails>
export type ContentDetails = { export type ContentDetails = {
title: string title: string
links: SimpleSlug[] links: SimpleSlug[]
@ -38,7 +38,7 @@ const defaultOptions: Options = {
includeEmptyFiles: true, includeEmptyFiles: true,
} }
function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndex): string { function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndexMap): string {
const base = cfg.baseUrl ?? "" const base = cfg.baseUrl ?? ""
const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<url> const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<url>
<loc>https://${joinSegments(base, encodeURI(slug))}</loc> <loc>https://${joinSegments(base, encodeURI(slug))}</loc>
@ -50,7 +50,7 @@ function generateSiteMap(cfg: GlobalConfiguration, idx: ContentIndex): string {
return `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">${urls}</urlset>` return `<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">${urls}</urlset>`
} }
function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndex, limit?: number): string { function generateRSSFeed(cfg: GlobalConfiguration, idx: ContentIndexMap, limit?: number): string {
const base = cfg.baseUrl ?? "" const base = cfg.baseUrl ?? ""
const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<item> const createURLEntry = (slug: SimpleSlug, content: ContentDetails): string => `<item>
@ -118,7 +118,7 @@ export const ContentIndex: QuartzEmitterPlugin<Partial<Options>> = (opts) => {
async emit(ctx, content, _resources) { async emit(ctx, content, _resources) {
const cfg = ctx.cfg.configuration const cfg = ctx.cfg.configuration
const emitted: FilePath[] = [] const emitted: FilePath[] = []
const linkIndex: ContentIndex = new Map() const linkIndex: ContentIndexMap = new Map()
for (const [tree, file] of content) { for (const [tree, file] of content) {
const slug = file.data.slug! const slug = file.data.slug!
const date = getDate(ctx.cfg.configuration, file.data) ?? new Date() const date = getDate(ctx.cfg.configuration, file.data) ?? new Date()

View file

@ -1,7 +1,7 @@
export { ContentPage } from "./contentPage" export { ContentPage } from "./contentPage"
export { TagPage } from "./tagPage" export { TagPage } from "./tagPage"
export { FolderPage } from "./folderPage" export { FolderPage } from "./folderPage"
export { ContentIndex } from "./contentIndex" export { ContentIndex as ContentIndex } from "./contentIndex"
export { AliasRedirects } from "./aliases" export { AliasRedirects } from "./aliases"
export { Assets } from "./assets" export { Assets } from "./assets"
export { Static } from "./static" export { Static } from "./static"