diff --git a/.github/workflows/tracker-script-npm-release.yml b/.github/workflows/tracker-script-npm-release.yml index 87136c46a1..503c79aac4 100644 --- a/.github/workflows/tracker-script-npm-release.yml +++ b/.github/workflows/tracker-script-npm-release.yml @@ -58,7 +58,9 @@ jobs: uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 with: message: "Released tracker script version ${{ steps.package.outputs.version }}" - add: "tracker/npm_package" + github_token: ${{ secrets.PLAUSIBLE_BOT_GITHUB_TOKEN }} + add: | + - tracker/npm_package - name: Notify team on success if: ${{ success() }} diff --git a/tracker/npm_package/CHANGELOG.md b/tracker/npm_package/CHANGELOG.md index 5ef949544d..db718beaf9 100644 --- a/tracker/npm_package/CHANGELOG.md +++ b/tracker/npm_package/CHANGELOG.md @@ -7,4 +7,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -Initial release. +- Support for `config.transformRequest` +- Support for passing `url` as option when calling `track` diff --git a/tracker/npm_package/README.md b/tracker/npm_package/README.md index 65903b8b81..f545ad755d 100644 --- a/tracker/npm_package/README.md +++ b/tracker/npm_package/README.md @@ -50,6 +50,7 @@ See also [plausible.d.ts](https://github.com/plausible/analytics/blob/master/tra | `formSubmissions` | Whether to track form submissions. | `false` | | `captureOnLocalhost` | Whether to capture events on localhost. | `false` | | `customProperties` | Object or function that returns custom properties for a given event. | `{}` | +| `transformRequest` | Function that allows transforming or ignoring requests | | #### Using `customProperties` diff --git a/tracker/npm_package/package.json b/tracker/npm_package/package.json index 38171574e4..e114c9f911 100644 --- a/tracker/npm_package/package.json +++ b/tracker/npm_package/package.json @@ -1,6 +1,6 @@ { "name": "macobo-test-tracker", - "version": "0.1.6", + "version": "0.2.0", "description": "Plausible Analytics official frontend tracking library", "scripts": { "test": "echo \"Error: Testing done in the tracker folder\" && exit 1" diff --git a/tracker/npm_package/plausible.d.ts b/tracker/npm_package/plausible.d.ts index ac33c5e7b3..c0eef8ab1a 100644 --- a/tracker/npm_package/plausible.d.ts +++ b/tracker/npm_package/plausible.d.ts @@ -34,6 +34,13 @@ export interface PlausibleConfig { // Custom properties to add to all events tracked. // If passed as a function, it will be called when `track` is called. customProperties?: CustomProperties | ((eventName: string) => CustomProperties) + + // A function that can be used to transform the payload before it is sent to the API. + // If the function returns null or any other falsy value, the event will be ignored. + // + // This can be used to avoid sending certain types of events, or modifying any event + // parameters, e.g. to clean URLs of values that should not be recorded. + transformRequest?: (payload: PlausibleRequestPayload) => PlausibleRequestPayload | null } export interface PlausibleEventOptions { @@ -56,7 +63,7 @@ export interface PlausibleEventOptions { // Overrides the URL of the page that the event is being tracked on. // If not provided, `location.href` will be used. - u?: string + url?: string } export type CustomProperties = Record @@ -67,3 +74,20 @@ export type PlausibleEventRevenue = { // Currency is an ISO 4217 string representing the currency code, e.g. "USD" or "EUR" currency: string } + +export type PlausibleRequestPayload = { + // Event name + n: string, + // URL of the event + u: string, + // Domain of the event + d: string, + // Referrer + r?: string | null, + // Custom properties + p?: CustomProperties, + // Revenue information + $?: PlausibleEventRevenue, + // Whether the event is interactive + i?: boolean, +} & Record diff --git a/tracker/package.json b/tracker/package.json index 55cb3d05ca..b5d9a13f8e 100644 --- a/tracker/package.json +++ b/tracker/package.json @@ -1,5 +1,5 @@ { - "tracker_script_version": 16, + "tracker_script_version": 17, "type": "module", "scripts": { "deploy": "node compile.js", diff --git a/tracker/src/track.js b/tracker/src/track.js index 7fb4498983..18ad2a59c8 100644 --- a/tracker/src/track.js +++ b/tracker/src/track.js @@ -55,7 +55,7 @@ export function track(eventName, options) { payload.v = COMPILE_TRACKER_SCRIPT_VERSION if (COMPILE_MANUAL) { - var customURL = options && options.u + var customURL = options && (options.u || options.url) payload.u = customURL ? customURL : location.href } else { @@ -111,6 +111,14 @@ export function track(eventName, options) { payload.h = 1 } + if ((COMPILE_PLAUSIBLE_WEB || COMPILE_PLAUSIBLE_NPM) && typeof config.transformRequest === 'function') { + payload = config.transformRequest(payload) + + if (!payload) { + return onIgnoredEvent(eventName, options, 'transformRequest') + } + } + if (isPageview) { postPageviewTrack(payload) } diff --git a/tracker/test/fixtures/plausible-npm.html b/tracker/test/fixtures/plausible-npm.html index 08c468ce2f..288a1857cb 100644 --- a/tracker/test/fixtures/plausible-npm.html +++ b/tracker/test/fixtures/plausible-npm.html @@ -19,7 +19,8 @@ Outbound link - Manual pageview + Manual pageview + Manual pageview 2 Custom event diff --git a/tracker/test/fixtures/plausible-web.html b/tracker/test/fixtures/plausible-web.html index 4bb29b30fa..d9d9e3263f 100644 --- a/tracker/test/fixtures/plausible-web.html +++ b/tracker/test/fixtures/plausible-web.html @@ -14,7 +14,8 @@ Outbound link - Manual pageview + Manual pageview + Manual pageview 2 Custom event diff --git a/tracker/test/shared-configuration-tests.js b/tracker/test/shared-configuration-tests.js index af2f456718..7664c77ff7 100644 --- a/tracker/test/shared-configuration-tests.js +++ b/tracker/test/shared-configuration-tests.js @@ -11,15 +11,21 @@ import { test } from '@playwright/test' // Wrapper around calling `plausible.init` in the page context for users of `testPlausibleConfiguration` export async function callInit(page, config, parent) { - // Stringify the customProperties function to work around evaluate not being able to serialize functions + // Stringify the customProperties and transformRequest functions to work around evaluate not being able to serialize functions if (config && typeof config.customProperties === 'function') { config.customProperties = { "_wrapFunction": config.customProperties.toString() } } + if (config && typeof config.transformRequest === 'function') { + config.transformRequest = { "_wrapFunction": config.transformRequest.toString() } + } await page.evaluate(({ config, parent }) => { if (config && config.customProperties && config.customProperties._wrapFunction) { config.customProperties = new Function(`return (${config.customProperties._wrapFunction})`)(); } + if (config && config.transformRequest && config.transformRequest._wrapFunction) { + config.transformRequest = new Function(`return (${config.transformRequest._wrapFunction})`)(); + } eval(parent).init(config) }, { config, parent }) } @@ -187,12 +193,15 @@ export function testPlausibleConfiguration({ openPage, initPlausible, fixtureNam action: async () => { await openPage(page, {}, { skipPlausibleInit: true }) await initPlausible(page, { autoCapturePageviews: false }) - await page.click('#manual-pageview') + await page.click('#manual-pageview-1') + await page.click('#manual-pageview-2') await hideAndShowCurrentTab(page, { delay: 200 }) }, expectedRequests: [ { n: 'pageview', u: '/:test-plausible', d: 'example.com' }, { n: 'engagement', u: '/:test-plausible', d: 'example.com' }, + { n: 'pageview', u: '/:test-plausible-2', d: 'example.com' }, + { n: 'engagement', u: '/:test-plausible-2', d: 'example.com' }, ], }) }) @@ -232,5 +241,64 @@ export function testPlausibleConfiguration({ openPage, initPlausible, fixtureNam expectedRequests: [{ n: 'pageview', d: 'example.com', u: expecting.stringContaining(fixtureName)}] }) }) + + test('supports ignoring requests with `transformRequest`', async ({ page }) => { + await expectPlausibleInAction(page, { + action: async () => { + await openPage(page, {}, { skipPlausibleInit: true }) + await initPlausible(page, { transformRequest: () => null }) + await page.click('#custom-event') + }, + expectedRequests: [], + refutedRequests: [{ n: 'Custom event' }, { n: 'pageview' }] + }) + }) + + test('supports modifying the request payload with `transformRequest`', async ({ page }) => { + const transformRequest = (payload) => { + payload.p = payload.p || {} + payload.p.eventName = payload.n + payload.i = false + + return payload + } + + await expectPlausibleInAction(page, { + action: async () => { + await openPage(page, {}, { skipPlausibleInit: true }) + await initPlausible(page, { transformRequest }) + await page.click('#custom-event') + }, + expectedRequests: [ + { n: 'pageview', p: { eventName: 'pageview' }, i: false }, + { n: 'Custom event', p: { eventName: 'Custom event', author: 'Karl' }, i: false } + ] + }) + }) + + test('transformRequest props are sent with engagement events', async ({ page }) => { + const transformRequest = (payload) => { + payload.p = payload.p || {} + + window.requestCount = (window.requestCount || 0) + 1 + payload.p.requestCount = window.requestCount + + return payload + } + + await expectPlausibleInAction(page, { + action: async () => { + await openPage(page, {}, { skipPlausibleInit: true }) + await initPlausible(page, { transformRequest }) + await page.click('#custom-event') + await hideAndShowCurrentTab(page, { delay: 200 }) + }, + expectedRequests: [ + { n: 'pageview', p: { requestCount: 1 } }, + { n: 'Custom event', p: { author: 'Karl', requestCount: 2 } }, + { n: 'engagement', p: { requestCount: 1 } } + ] + }) + }) }) }