diff --git a/.gitignore b/.gitignore
index ca0f3c4f..8b349dbb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@
# production
/build
/public/umami.js
+/public/snippet.js
/lang-compiled
/lang-formatted
diff --git a/components/forms/TrackingCodeForm.js b/components/forms/TrackingCodeForm.js
index a98b471d..36dda30e 100644
--- a/components/forms/TrackingCodeForm.js
+++ b/components/forms/TrackingCodeForm.js
@@ -17,12 +17,15 @@ export default function TrackingCodeForm({ values, onClose }) {
/>
+ {/* Run `npm run build-snippet, and copy paste here the content of public/snippet.js */}
+ {/* TODO: use webpack importing function to import the content of the file here ? */}
diff --git a/package.json b/package.json
index e6dc21ec..e600611d 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"start": "next start",
"build-app": "next build",
"build-tracker": "rollup -c rollup.tracker.config.js",
+ "build-snippet": "rollup -c rollup.snippet.config.js",
"copy-db-schema": "node scripts/copy-db-schema.js",
"build-db-schema": "dotenv prisma introspect",
"build-db-client": "dotenv prisma generate",
diff --git a/rollup.snippet.config.js b/rollup.snippet.config.js
new file mode 100644
index 00000000..c5925988
--- /dev/null
+++ b/rollup.snippet.config.js
@@ -0,0 +1,19 @@
+import 'dotenv/config';
+import buble from '@rollup/plugin-buble';
+import replace from '@rollup/plugin-replace';
+import resolve from '@rollup/plugin-node-resolve';
+import { terser } from 'rollup-plugin-terser';
+
+export default {
+ input: 'tracker/snippet.js',
+ output: {
+ file: 'public/snippet.js',
+ format: 'iife',
+ },
+ plugins: [
+ replace({ __DNT__: !!process.env.ENABLE_DNT }),
+ resolve(),
+ buble(),
+ terser({ compress: { evaluate: false } }),
+ ],
+};
diff --git a/tracker/index.js b/tracker/index.js
index ff7f0995..6d6886fb 100644
--- a/tracker/index.js
+++ b/tracker/index.js
@@ -3,6 +3,13 @@ import 'unfetch/polyfill';
import { doNotTrack, hook, post } from '../lib/web';
import { removeTrailingSlash } from '../lib/url';
+function log(isDebug, messageFn) {
+ if (isDebug) {
+ return;
+ }
+ console.log(messageFn());
+}
+
(window => {
const {
screen: { width, height },
@@ -20,6 +27,7 @@ import { removeTrailingSlash } from '../lib/url';
const website = script.getAttribute('data-website-id');
const hostUrl = script.getAttribute('data-host-url');
const skipAuto = script.getAttribute('data-skip-auto');
+ const isDebug = script.getAttribute('data-debug');
const root = hostUrl
? removeTrailingSlash(hostUrl)
: new URL(script.src).href.split('/').slice(0, -1).join('/');
@@ -72,45 +80,54 @@ import { removeTrailingSlash } from '../lib/url';
});
};
- if (!window.umami) {
- window.umami = event_value => window.umami.event('custom', event_value, currentUrl);
- window.umami.collect = (type, params, id) => {
- if (!id) {
- id = website;
- }
- const payload = {
- website: id,
- hostname,
- screen,
- language,
- };
- if (params) {
- Object.keys(params).forEach(key => {
- payload[key] = params[key];
- });
- }
+ const scheduledCalls = window.umami.calls;
+ window.umami = {};
+ window.umami.collect = (type, params, id) => {
+ if (!id) {
+ id = website;
+ }
+ const payload = {
+ website: id,
+ hostname,
+ screen,
+ language,
+ };
- return post(`${root}/api/collect`, {
- type,
- payload,
+ if (params) {
+ Object.keys(params).forEach(key => {
+ payload[key] = params[key];
});
- };
- window.umami.pageView = (url = currentUrl, referrer = currentRef) => window.umami.collect('pageview', {
- url,
- referrer,
+ }
+
+ log(isDebug, () => `Umami, collect ${type} with payload: ${JSON.stringify(payload, null, 2)}`);
+ return post(`${root}/api/collect`, {
+ type,
+ payload,
});
- window.umami.event = (event_type, event_value, url = currentUrl) => window.umami.collect('event', {
- url,
- event_type,
- event_value,
- });
- window.umami.registerAutoEvents = () => {
- history.pushState = hook(history, 'pushState', handlePush);
- history.replaceState = hook(history, 'replaceState', handlePush);
- return pageViewWithAutoEvents(currentUrl, currentRef);
- };
- }
+ };
+ window.umami.pageView = (url = currentUrl, referrer = currentRef) => window.umami.collect('pageview', {
+ url,
+ referrer,
+ });
+ window.umami.event = (event_type, event_value, url = currentUrl) => window.umami.collect('event', {
+ url,
+ event_type,
+ event_value,
+ });
+ window.umami.registerAutoEvents = () => {
+ history.pushState = hook(history, 'pushState', handlePush);
+ history.replaceState = hook(history, 'replaceState', handlePush);
+ return pageViewWithAutoEvents(currentUrl, currentRef);
+ };
+
+ log(isDebug, () => 'Umami, calling scheduled invocations');
+ log(isDebug, () => scheduledCalls);
+
+ scheduledCalls.forEach(([fnName, ...params]) => {
+ log(isDebug, () => `Umami, calling ${fnName} fn with params: ${JSON.stringify(params)}`);
+ window.umami[fnName].apply(window.umami, params);
+ });
/* Start */
if (!skipAuto) {
diff --git a/tracker/snippet.js b/tracker/snippet.js
new file mode 100644
index 00000000..ffb89423
--- /dev/null
+++ b/tracker/snippet.js
@@ -0,0 +1,42 @@
+(window => {
+ const umami = window.umami = window.umami || [];
+ if (!umami.registerAutoEvents) {
+ if (umami.invoked) {
+ window.console && console.error && console.error('Umami snippet included twice.');
+ } else {
+ umami.invoked = true;
+ umami.calls = [];
+ umami.methods = ['registerAutoEvents', 'event', 'pageView'];
+ umami.factory = t => {
+ return function() {
+ const e = Array.prototype.slice.call(arguments);
+ e.unshift(t);
+ umami.calls.push(e);
+ return umami;
+ };
+ };
+ for (let t = 0; t < umami.methods.length; t++) {
+ let e = umami.methods[t];
+ umami[e] = umami.factory(e);
+ }
+ umami.load = function(umamiScript, umamiUUID, skipAuto, isDebug) {
+ const scriptElement = document.createElement('script');
+ scriptElement.type = 'text/javascript';
+ scriptElement.defer = true;
+ scriptElement.async = true;
+ scriptElement.setAttribute('data-website-id', umamiUUID);
+ if (skipAuto) {
+ scriptElement.setAttribute('data-skip-auto', 'true');
+ }
+ if (isDebug) {
+ scriptElement.setAttribute('data-debug', 'true');
+ }
+ scriptElement.src = umamiScript;
+ const otherScript = document.getElementsByTagName('script')[0];
+ otherScript.parentNode.insertBefore(scriptElement, otherScript);
+ };
+
+ umami.load('${document.location.origin}/umami.js', '${values.website_uuid}', false, false);
+ }
+ }
+})(window);
\ No newline at end of file