From e918f1165220c70dcd25c10fc251d3df1f269b65 Mon Sep 17 00:00:00 2001
From: John Barker <john@johnrbarker.com>
Date: Wed, 13 Aug 2025 03:56:04 +0000
Subject: [PATCH] feat: add support for matomo analytics (#2051)

---
 quartz/plugins/emitters/componentResources.ts |   27 +++++++++++++++++++++++++++
 docs/configuration.md                         |    1 +
 quartz/cfg.ts                                 |    5 +++++
 3 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/docs/configuration.md b/docs/configuration.md
index 6e9f9fb..6a8068a 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -34,6 +34,7 @@
   - `{ provider: 'tinylytics', siteId: '<your-site-id>' }`: use [Tinylytics](https://tinylytics.app/);
   - `{ provider: 'cabin' }` or `{ provider: 'cabin', host: 'https://cabin.example.com' }` (custom domain): use [Cabin](https://withcabin.com);
   - `{provider: 'clarity', projectId: '<your-clarity-id-code' }`: use [Microsoft clarity](https://clarity.microsoft.com/). The project id can be found on top of the overview page.
+  - `{ provider: 'matomo', siteId: '<your-matomo-id-code', host: 'matomo.example.com' }`: use [Matomo](https://matomo.org/), without protocol.
 - `locale`: used for [[i18n]] and date formatting
 - `baseUrl`: this is used for sitemaps and RSS feeds that require an absolute URL to know where the canonical 'home' of your site lives. This is normally the deployed URL of your site (e.g. `quartz.jzhao.xyz` for this site). Do not include the protocol (i.e. `https://`) or any leading or trailing slashes.
   - This should also include the subpath if you are [[hosting]] on GitHub pages without a custom domain. For example, if my repository is `jackyzha0/quartz`, GitHub pages would deploy to `https://jackyzha0.github.io/quartz` and the `baseUrl` would be `jackyzha0.github.io/quartz`.
diff --git a/quartz/cfg.ts b/quartz/cfg.ts
index b5de75d..016bce6 100644
--- a/quartz/cfg.ts
+++ b/quartz/cfg.ts
@@ -42,6 +42,11 @@
       provider: "clarity"
       projectId?: string
     }
+  | {
+      provider: "matomo"
+      host: string
+      siteId: string
+    }
 
 export interface GlobalConfiguration {
   pageTitle: string
diff --git a/quartz/plugins/emitters/componentResources.ts b/quartz/plugins/emitters/componentResources.ts
index 61e2ba8..857f4d3 100644
--- a/quartz/plugins/emitters/componentResources.ts
+++ b/quartz/plugins/emitters/componentResources.ts
@@ -201,6 +201,33 @@
       })(window, document, "clarity", "script", "${cfg.analytics.projectId}");\`
       document.head.appendChild(clarityScript)
     `)
+  } else if (cfg.analytics?.provider === "matomo") {
+    componentResources.afterDOMLoaded.push(`
+      const matomoScript = document.createElement("script");
+      matomoScript.innerHTML = \`
+      let _paq = window._paq = window._paq || [];
+
+      // Track SPA navigation
+      // https://developer.matomo.org/guides/spa-tracking
+      document.addEventListener("nav", () => {
+        _paq.push(['setCustomUrl', location.pathname]);
+        _paq.push(['setDocumentTitle', document.title]);
+        _paq.push(['trackPageView']);
+      });
+
+      _paq.push(['trackPageView']);
+      _paq.push(['enableLinkTracking']);
+      (function() {
+        const u="//${cfg.analytics.host}/";
+        _paq.push(['setTrackerUrl', u+'matomo.php']);
+        _paq.push(['setSiteId', ${cfg.analytics.siteId}]);
+        const d=document, g=d.createElement('script'), s=d.getElementsByTagName
+('script')[0];
+        g.type='text/javascript'; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s);
+      })();
+      \`
+      document.head.appendChild(matomoScript);
+    `)
   }
 
   if (cfg.enableSPA) {

--
Gitblit v1.10.0