diff --git a/package-lock.json b/package-lock.json
index 0ea3e53..1d3867b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6,6 +6,9 @@
"": {
"name": "schmelczer-dev",
"license": "GPL-3.0-or-later",
+ "dependencies": {
+ "@plausible-analytics/tracker": "^0.4.5"
+ },
"devDependencies": {
"@astrojs/check": "^0.9.9",
"@astrojs/rss": "^4.0.18",
@@ -1442,6 +1445,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@plausible-analytics/tracker": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/@plausible-analytics/tracker/-/tracker-0.4.5.tgz",
+ "integrity": "sha512-6BfAGejXY+YA3Cw6LYT2Zpn4hTxDtPQAawFsYUsQCOg78wIS5C4deAGXTfJffa5VleMWITv5lpJ/EYuQBl1tPA==",
+ "license": "MIT"
+ },
"node_modules/@rollup/pluginutils": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
@@ -7184,6 +7193,11 @@
"integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==",
"dev": true
},
+ "@plausible-analytics/tracker": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/@plausible-analytics/tracker/-/tracker-0.4.5.tgz",
+ "integrity": "sha512-6BfAGejXY+YA3Cw6LYT2Zpn4hTxDtPQAawFsYUsQCOg78wIS5C4deAGXTfJffa5VleMWITv5lpJ/EYuQBl1tPA=="
+ },
"@rollup/pluginutils": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
diff --git a/package.json b/package.json
index 6ff283a..c282e3d 100644
--- a/package.json
+++ b/package.json
@@ -55,5 +55,8 @@
},
"overrides": {
"yaml": "^2.9.0"
+ },
+ "dependencies": {
+ "@plausible-analytics/tracker": "^0.4.5"
}
}
diff --git a/scripts/check-no-js.mjs b/scripts/check-no-js.mjs
index a19fc72..7098320 100644
--- a/scripts/check-no-js.mjs
+++ b/scripts/check-no-js.mjs
@@ -27,7 +27,14 @@ try {
}
const files = await walk(dist);
-const jsFiles = files.filter((file) => file.endsWith('.js'));
+const ALLOWED_JS_ASSET_PATTERNS = [
+ /[/\\]_astro[/\\]Analytics\.astro_astro_type_script_index_0_lang\.[\w-]+\.js$/,
+];
+const jsFiles = files.filter(
+ (file) =>
+ file.endsWith('.js') &&
+ !ALLOWED_JS_ASSET_PATTERNS.some((pattern) => pattern.test(file))
+);
if (jsFiles.length > 0) {
failures.push(
@@ -36,17 +43,21 @@ if (jsFiles.length > 0) {
}
// Script tags are only allowed if they declare one of these safe `type`
-// attributes (or are tagged with `data-theme-script`). All other scripts,
-// including untyped ones, which default to executable JavaScript, are flagged.
+// attributes or match one of the known executable scripts below. All other
+// scripts, including untyped ones, which default to executable JavaScript, are
+// flagged.
const SAFE_SCRIPT_TYPES = new Set([
'application/ld+json',
'importmap',
'speculationrules',
]);
+const ANALYTICS_SCRIPT_SRC_PATTERN =
+ /\bsrc=["']\/_astro\/Analytics\.astro_astro_type_script_index_0_lang\.[\w-]+\.js["']/i;
function isSafeScriptTag(tag) {
if (tag.includes('data-theme-script')) return true;
if (tag.includes('data-thumbnail-iframe-script')) return true;
+ if (ANALYTICS_SCRIPT_SRC_PATTERN.test(tag)) return true;
const typeMatch = tag.match(/\btype=["']([^"']+)["']/i);
if (!typeMatch) return false;
return SAFE_SCRIPT_TYPES.has(typeMatch[1].trim().toLowerCase());
diff --git a/src/components/Analytics.astro b/src/components/Analytics.astro
new file mode 100644
index 0000000..4182235
--- /dev/null
+++ b/src/components/Analytics.astro
@@ -0,0 +1,3 @@
+
diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro
index b911cdc..ecfd500 100644
--- a/src/layouts/Base.astro
+++ b/src/layouts/Base.astro
@@ -1,6 +1,7 @@
---
import Footer from '../components/Footer.astro';
import Header from '../components/Header.astro';
+import Analytics from '../components/Analytics.astro';
import { absoluteUrl, optimizeOgImage, site } from '../lib/site';
import defaultOg from '../assets/og-default.jpg';
import themeInit from '../scripts/theme-init.js?raw';
@@ -167,6 +168,7 @@ const jsonLdStrings = jsonLdEntries.map((entry) =>
))
}
+