Files
fox/src/.vitepress/matomo.ts
T

149 lines
3.8 KiB
TypeScript

/**
* A library to add Matomo tracking to vitepress router.
*
* @remarks
* This injects Matomo default script to the page, while handling SSR.
* It requires access to Vitepress router to hook into `onAfterRouteChanged` event.
*
* @packageDocumentation
*/
import type { Router } from "vitepress";
declare global {
interface Window {
_paq?: any[][] // eslint-disable-line @typescript-eslint/no-explicit-any
}
}
/**
* Interface for plugin parameters
* @public
*/
export interface IParameters {
/**
* Enable/disable link click tracking, defaults to true
* @defaultValue true
*/
enableLinkTracking?: boolean;
/**
* Remember consent
*
* @remarks not working right now
*
* @defaultValue false
*/
rememberConsent?: boolean;
/**
* Requires user consent before sending events
*
* @remarks not working right now
*
* @defaultValue false
*/
requireConsent?: boolean;
/**
* Vitepress router component
*/
router: Router;
/**
* Matomo numeric site ID of the site you want to track
*/
siteID: number;
/**
* Name of the js file to call on the matomo server
* @defaultValue "piwik.js"
*/
trackerJsFile?: string;
/**
* Name of the php file to call on the matomo server
* @defaultValue "piwik.php"
*/
trackerPhpFile?: string;
/**
* URL where the piwik.php/piwik.js files can be found
*/
trackerUrl: string;
}
/**
* Load Matomo in your vitepress project.
*
* @remarks
* This is mostly a generalized version of the basic matomo
* tracker code you'd insert in a JS page. However, since vuepress is SSR, it
* requires some special workarounds to make sure paq object storage happens
* correctly.
*
* @public
*/
export default function(parameters: IParameters) {
const {
router,
trackerUrl,
rememberConsent = false,
requireConsent = false,
siteID,
trackerJsFile = "piwik.js",
trackerPhpFile = "piwik.php",
enableLinkTracking = true
} = parameters;
if (process.env.NODE_ENV === 'production' && typeof window !== 'undefined' &&
siteID && trackerUrl) {
// We're in SSR space here, meaning that we have to explictly attach _paq to
// the window in order to store it globally.
if (window._paq == undefined) {
window._paq = [];
}
// Create convenience variable here, but don't expect it to last. Use
// window._paq elsewhere when needed, including closure scopes.
const _paq = window._paq;
// If user requests consent checking, do this before we actually track.
// Note: this doesn't work at the moment because the user has no way to set
// whether consent was given. Oops.
if (requireConsent) {
_paq.push(['requireConsent']);
if (rememberConsent) {
_paq.push(['rememberConsentGiven']);
}
}
if (enableLinkTracking) {
_paq.push(['enableLinkTracking']);
}
(function() {
let u=trackerUrl;
// Make sure URLs end in a slash
if (u.length > 0 && !u.endsWith("/")) {
u = u.concat("/");
}
_paq.push(['setTrackerUrl', u+trackerPhpFile]);
_paq.push(['setSiteId', siteID]);
const g = document.createElement('script');
g.type='text/javascript';
g.async=true;
g.defer=true;
g.src=u+trackerJsFile;
document.body.insertBefore(g, document.body.firstChild);
})();
let existingCallback: typeof router.onAfterRouteChanged;
if(router.onAfterRouteChanged) {
existingCallback = router.onAfterRouteChanged;
}
router.onAfterRouteChanged = (to) => {
if(existingCallback) {
existingCallback(to); // eslint-disable-line @typescript-eslint/no-floating-promises
}
window._paq?.push(['setDocumentTitle', document.title]);
window._paq?.push(['setCustomUrl', to]);
window._paq?.push(['trackPageView']);
};
}
}