diff --git a/modules/index.js b/modules/index.js index ba4ca7e..a0b5edc 100644 --- a/modules/index.js +++ b/modules/index.js @@ -14,26 +14,65 @@ import { KNOWN_PROPERTIES, DEFAULT_TRACKING_EVENTS } from './defaults.js'; import { urlSanitizers } from './utils.js'; import { targetSelector, sourceSelector } from './dom.js'; -import { - addAdsParametersTracking, - addCookieConsentTracking, - addEmailParameterTracking, - addUTMParametersTracking, -} from './martech.js'; import { fflags } from './fflags.js'; const { sampleRUM, queue, isSelected } = (window.hlx && window.hlx.rum) ? window.hlx.rum /* c8 ignore next */ : {}; -// blocks mutation observer -// eslint-disable-next-line no-use-before-define, max-len -const blocksMO = window.MutationObserver ? new MutationObserver(blocksMCB) - /* c8 ignore next */ : {}; +const createMO = (cb) => (window.MutationObserver ? new MutationObserver(cb) +/* c8 ignore next */ : {}); -// media mutation observer -// eslint-disable-next-line no-use-before-define, max-len -const mediaMO = window.MutationObserver ? new MutationObserver(mediaMCB) - /* c8 ignore next */ : {}; +// blocks & media mutation observer +// eslint-disable-next-line no-use-before-define +const [blocksMO, mediaMO] = [blocksMCB, mediaMCB].map(createMO); + +// Check for the presence of a given cookie +const hasCookieKey = (key) => () => document.cookie.split(';').some((c) => c.trim().startsWith(`${key}=`)); + +// Set the base path for the plugins +const pluginBasePath = new URL(document.currentScript.src).href.replace(/index(\.map)?\.js/, 'plugins'); + +const PLUGINS = { + cwv: `${pluginBasePath}/cwv.js`, + // Interactive elements + form: { url: `${pluginBasePath}/form.js`, when: () => document.querySelector('form'), isBlockDependent: true }, + video: { url: `${pluginBasePath}/video.js`, when: () => document.querySelector('video'), isBlockDependent: true }, + // Martech + martech: { url: `${pluginBasePath}/martech.js`, when: ({ urlParameters }) => urlParameters.size > 0 }, + onetrust: { url: `${pluginBasePath}/onetrust.js`, when: () => (document.querySelector('#onetrust-consent-sdk') || hasCookieKey('OptanonAlertBoxClosed')), isBlockDependent: true }, + // test: broken-plugin +}; + +const PLUGIN_PARAMETERS = { + context: document.body, + fflags, + sampleRUM, + sourceSelector, + targetSelector, +}; + +const pluginCache = new Map(); + +function loadPlugin(key, params) { + const plugin = PLUGINS[key]; + const usp = new URLSearchParams(window.location.search); + if (!pluginCache.has(key) && plugin.when && !plugin.when({ urlParameters: usp })) { + return null; + } + if (!pluginCache.has(key)) { + pluginCache.set(key, import(`${plugin.url || plugin}`)); + } + const pluginLoadPromise = pluginCache.get(key); + return pluginLoadPromise + .then((p) => (p.default && p.default(params)) || (typeof p === 'function' && p(params))) + .catch(() => { /* silent plugin error catching */ }); +} + +function loadPlugins(filter = () => true, params = PLUGIN_PARAMETERS) { + Object.entries(PLUGINS) + .filter(([, plugin]) => filter(plugin)) + .map(([key]) => loadPlugin(key, params)); +} function trackCheckpoint(checkpoint, data, t) { const { weight, id } = window.hlx.rum; @@ -64,55 +103,9 @@ function processQueue() { } } -function addCWVTracking() { - setTimeout(() => { - try { - const cwvScript = new URL('.rum/web-vitals/dist/web-vitals.iife.js', sampleRUM.baseURL).href; - if (document.querySelector(`script[src="${cwvScript}"]`)) { - // web vitals script has been loaded already - return; - } - const script = document.createElement('script'); - script.src = cwvScript; - script.onload = () => { - const storeCWV = (measurement) => { - const data = { cwv: {} }; - data.cwv[measurement.name] = measurement.value; - if (measurement.name === 'LCP' && measurement.entries.length > 0) { - const { element } = measurement.entries.pop(); - data.target = targetSelector(element); - data.source = sourceSelector(element) || (element && element.outerHTML.slice(0, 30)); - } - sampleRUM('cwv', data); - }; - - const isEager = (metric) => ['CLS', 'LCP'].includes(metric); - - // When loading `web-vitals` using a classic script, all the public - // methods can be found on the `webVitals` global namespace. - ['INP', 'TTFB', 'CLS', 'LCP'].forEach((metric) => { - const metricFn = window.webVitals[`on${metric}`]; - if (typeof metricFn === 'function') { - let opts = {}; - fflags.enabled('eagercwv', () => { - opts = { reportAllChanges: isEager(metric) }; - }); - metricFn(storeCWV, opts); - } - }); - }; - document.head.appendChild(script); - /* c8 ignore next 3 */ - } catch (error) { - // something went wrong - } - }, 2000); // wait for delayed -} - function addNavigationTracking() { // enter checkpoint when referrer is not the current page url const navigate = (source, type, redirectCount) => { - // target can be 'visible', 'hidden' (background tab) or 'prerendered' (speculation rules) const payload = { source, target: document.visibilityState }; /* c8 ignore next 13 */ // prerendering cannot be tested yet with headless browsers @@ -211,8 +204,6 @@ function getIntersectionObsever(checkpoint) { if (!window.IntersectionObserver) { return null; } - activateBlocksMO(); - activateMediaMO(); const observer = new IntersectionObserver((entries) => { try { entries @@ -251,30 +242,6 @@ function addViewMediaTracking(parent) { } } -function addFormTracking(parent) { - activateBlocksMO(); - activateMediaMO(); - parent.querySelectorAll('form').forEach((form) => { - form.addEventListener('submit', (e) => sampleRUM('formsubmit', { target: targetSelector(e.target), source: sourceSelector(e.target) }), { once: true }); - let lastSource; - form.addEventListener('change', (e) => { - if (e.target.checkVisibility()) { - const source = sourceSelector(e.target); - if (source !== lastSource) { - sampleRUM('fill', { source }); - lastSource = source; - } - } - }); - form.addEventListener('focusin', (e) => { - if (['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'].includes(e.target.tagName) - || e.target.getAttribute('contenteditable') === 'true') { - sampleRUM('click', { source: sourceSelector(e.target) }); - } - }); - }); -} - function addObserver(ck, fn, block) { return DEFAULT_TRACKING_EVENTS.includes(ck) && fn(block); } @@ -286,7 +253,7 @@ function blocksMCB(mutations) { .filter((m) => m.type === 'attributes' && m.attributeName === 'data-block-status') .filter((m) => m.target.dataset.blockStatus === 'loaded') .forEach((m) => { - addObserver('form', addFormTracking, m.target); + addObserver('form', (el) => loadPlugins((p) => p.isBlockDependent, { ...PLUGIN_PARAMETERS, context: el }), m.target); addObserver('viewblock', addViewBlockTracking, m.target); }); } @@ -301,6 +268,9 @@ function mediaMCB(mutations) { } function addTrackingFromConfig() { + activateBlocksMO(); + activateMediaMO(); + let lastSource; let lastTarget; document.addEventListener('click', (event) => { @@ -312,16 +282,16 @@ function addTrackingFromConfig() { lastTarget = target; } }); - addCWVTracking(); - addFormTracking(window.document.body); + + // Core tracking addNavigationTracking(); addLoadResourceTracking(); - addUTMParametersTracking(sampleRUM); - addViewBlockTracking(window.document.body); - addViewMediaTracking(window.document.body); - addCookieConsentTracking(sampleRUM); - addAdsParametersTracking(sampleRUM); - addEmailParameterTracking(sampleRUM); + addViewBlockTracking(document.body); + addViewMediaTracking(document.body); + + // Tracking extensions + loadPlugins(); + fflags.enabled('language', () => { const target = navigator.language; const source = document.documentElement.lang; diff --git a/package-lock.json b/package-lock.json index 95bdad0..24e6c2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@adobe/eslint-config-helix": "2.0.8", "@adobe/helix-rum-js": "2.10.0", "@esm-bundle/chai": "4.3.4-fix.0", + "@rollup/plugin-babel": "6.0.4", "@semantic-release/changelog": "6.0.3", "@semantic-release/git": "10.0.1", "@semantic-release/npm": "12.0.1", @@ -33,7 +34,6 @@ "mocha-multi-reporters": "1.5.1", "rollup": "4.30.1", "rollup-plugin-checksum": "1.0.1", - "rollup-plugin-cleanup": "3.2.1", "rollup-plugin-eslint-bundle": "9.0.0", "semantic-release": "24.2.1", "semantic-release-slack-bot": "^4.0.2", @@ -66,19 +66,194 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.7", + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/compat-data": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", + "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.0", + "@babel/generator": "^7.26.0", + "@babel/helper-compilation-targets": "^7.25.9", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.0", + "@babel/parser": "^7.26.0", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.26.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/helper-string-parser": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", @@ -99,29 +274,40 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", + "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", "dev": true, + "license": "MIT", + "peer": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", - "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.5.tgz", + "integrity": "sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -130,10 +316,54 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.5.tgz", + "integrity": "sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.5", + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.5", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.5.tgz", + "integrity": "sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==", "dev": true, "license": "MIT", "dependencies": { @@ -395,6 +625,21 @@ "node": ">=8" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -404,6 +649,16 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", @@ -686,6 +941,33 @@ "node": ">=18" } }, + "node_modules/@rollup/plugin-babel": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.0.4.tgz", + "integrity": "sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@rollup/pluginutils": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + }, + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-node-resolve": { "version": "15.2.3", "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", @@ -2666,6 +2948,40 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -2840,6 +3156,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001692", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", + "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0", + "peer": true + }, "node_modules/catharsis": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", @@ -4185,6 +4523,14 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "dev": true }, + "node_modules/electron-to-chromium": { + "version": "1.5.83", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.83.tgz", + "integrity": "sha512-LcUDPqSt+V0QmI47XLzZrz5OqILSMGsPFkDYus22rIbgorSvBYEFqq854ltTmUdHkY92FSdAAvsh4jWEULMdfQ==", + "dev": true, + "license": "ISC", + "peer": true + }, "node_modules/emoji-regex": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", @@ -4552,10 +4898,11 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -5518,6 +5865,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -6815,25 +7173,12 @@ "node": ">= 0.6.0" } }, - "node_modules/js-cleanup": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/js-cleanup/-/js-cleanup-1.2.0.tgz", - "integrity": "sha512-JeDD0yiiSt80fXzAVa/crrS0JDPQljyBG/RpOtaSbyDq03VHa9szJWMaWOYU/bcTn412uMN2MxApXq8v79cUiQ==", - "dev": true, - "dependencies": { - "magic-string": "^0.25.7", - "perf-regexes": "^1.0.1", - "skip-regex": "^1.0.2" - }, - "engines": { - "node": "^10.14.2 || >=12.0.0" - } - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "3.14.1", @@ -7072,6 +7417,19 @@ "node": ">=8" } }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -8058,15 +8416,6 @@ "node": ">=16.14" } }, - "node_modules/magic-string": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", - "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - } - }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -9038,6 +9387,14 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/normalize-package-data": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", @@ -12229,20 +12586,12 @@ "dev": true, "license": "MIT" }, - "node_modules/perf-regexes": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/perf-regexes/-/perf-regexes-1.0.1.tgz", - "integrity": "sha512-L7MXxUDtqr4PUaLFCDCXBfGV/6KLIuSEccizDI7JxT+c9x1G1v04BQ4+4oag84SHaCdrBgQAIs/Cqn+flwFPng==", - "dev": true, - "engines": { - "node": ">=6.14" - } - }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -13081,22 +13430,6 @@ "rollup": ">=1.26.0" } }, - "node_modules/rollup-plugin-cleanup": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/rollup-plugin-cleanup/-/rollup-plugin-cleanup-3.2.1.tgz", - "integrity": "sha512-zuv8EhoO3TpnrU8MX8W7YxSbO4gmOR0ny06Lm3nkFfq0IVKdBUtHwhVzY1OAJyNCIAdLiyPnOrU0KnO0Fri1GQ==", - "dev": true, - "dependencies": { - "js-cleanup": "^1.2.0", - "rollup-pluginutils": "^2.8.2" - }, - "engines": { - "node": "^10.14.2 || >=12.0.0" - }, - "peerDependencies": { - "rollup": ">=2.0" - } - }, "node_modules/rollup-plugin-eslint-bundle": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/rollup-plugin-eslint-bundle/-/rollup-plugin-eslint-bundle-9.0.0.tgz", @@ -13123,21 +13456,6 @@ "@jridgewell/sourcemap-codec": "^1.4.15" } }, - "node_modules/rollup-pluginutils": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", - "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", - "dev": true, - "dependencies": { - "estree-walker": "^0.6.1" - } - }, - "node_modules/rollup-pluginutils/node_modules/estree-walker": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", - "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", - "dev": true - }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -13775,15 +14093,6 @@ "node": ">=8" } }, - "node_modules/skip-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/skip-regex/-/skip-regex-1.0.2.tgz", - "integrity": "sha512-pEjMUbwJ5Pl/6Vn6FsamXHXItJXSRftcibixDmNCWbWhic0hzHrwkMZo0IZ7fMRH9KxcWDFSkzhccB4285PutA==", - "dev": true, - "engines": { - "node": ">=4.2" - } - }, "node_modules/slackify-markdown": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/slackify-markdown/-/slackify-markdown-4.4.0.tgz", @@ -13928,13 +14237,6 @@ "node": ">= 8" } }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "dev": true - }, "node_modules/spawn-error-forwarder": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/spawn-error-forwarder/-/spawn-error-forwarder-1.0.0.tgz", @@ -15010,6 +15312,38 @@ "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -15451,6 +15785,14 @@ "node": ">=10" } }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC", + "peer": true + }, "node_modules/yaml": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", diff --git a/package.json b/package.json index 74fcc9a..5a4a2c3 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "@adobe/eslint-config-helix": "2.0.8", "@adobe/helix-rum-js": "2.10.0", "@esm-bundle/chai": "4.3.4-fix.0", + "@rollup/plugin-babel": "6.0.4", "@semantic-release/changelog": "6.0.3", "@semantic-release/git": "10.0.1", "@semantic-release/npm": "12.0.1", @@ -57,7 +58,6 @@ "mocha-multi-reporters": "1.5.1", "rollup": "4.30.1", "rollup-plugin-checksum": "1.0.1", - "rollup-plugin-cleanup": "3.2.1", "rollup-plugin-eslint-bundle": "9.0.0", "semantic-release": "24.2.1", "semantic-release-slack-bot": "^4.0.2", diff --git a/plugins/cwv.js b/plugins/cwv.js new file mode 100644 index 0000000..1a22421 --- /dev/null +++ b/plugins/cwv.js @@ -0,0 +1,62 @@ +/* + * Copyright 2024 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +// eslint-disable-next-line max-len +// eslint-disable-next-line import/no-extraneous-dependencies, import/no-relative-packages, no-unused-vars +import * as webVitals from '../node_modules/web-vitals/dist/web-vitals.iife.js'; + +export default function addCWVTracking({ + sampleRUM, sourceSelector, targetSelector, fflags, +}) { + setTimeout(() => { + try { + const cwvScript = new URL('.rum/web-vitals/dist/web-vitals.iife.js', sampleRUM.baseURL).href; + /* c8 ignore next 3 */ + if (document.querySelector(`script[src="${cwvScript}"]`)) { + // web vitals script has been loaded already + return; + } + const script = document.createElement('script'); + script.src = cwvScript; + script.onload = () => { + const storeCWV = (measurement) => { + const data = { cwv: {} }; + data.cwv[measurement.name] = measurement.value; + if (measurement.name === 'LCP' && measurement.entries.length > 0) { + const { element } = measurement.entries.pop(); + data.target = targetSelector(element); + data.source = sourceSelector(element) || (element && element.outerHTML.slice(0, 30)); + } + sampleRUM('cwv', data); + }; + + const isEager = (metric) => ['CLS', 'LCP'].includes(metric); + + // When loading `web-vitals` using a classic script, all the public + // methods can be found on the `webVitals` global namespace. + ['INP', 'TTFB', 'CLS', 'LCP'].forEach((metric) => { + const metricFn = window.webVitals[`on${metric}`]; + if (typeof metricFn === 'function') { + let opts = {}; + fflags.enabled('eagercwv', () => { + opts = { reportAllChanges: isEager(metric) }; + }); + metricFn(storeCWV, opts); + } + }); + }; + document.head.appendChild(script); + /* c8 ignore next 3 */ + } catch (error) { + // something went wrong + } + }, 2000); // wait for delayed +} diff --git a/plugins/form.js b/plugins/form.js new file mode 100644 index 0000000..7a5258f --- /dev/null +++ b/plugins/form.js @@ -0,0 +1,34 @@ +/* + * Copyright 2024 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +export default function addFormTracking({ + sampleRUM, sourceSelector, targetSelector, context, +}) { + context.querySelectorAll('form').forEach((form) => { + form.addEventListener('submit', (e) => sampleRUM('formsubmit', { target: targetSelector(e.target), source: sourceSelector(e.target) }), { once: true }); + let lastSource; + form.addEventListener('change', (e) => { + if (e.target.checkVisibility()) { + const source = sourceSelector(e.target); + if (source !== lastSource) { + sampleRUM('fill', { source }); + lastSource = source; + } + } + }); + form.addEventListener('focusin', (e) => { + if (['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'].includes(e.target.tagName) + || e.target.getAttribute('contenteditable') === 'true') { + sampleRUM('click', { source: sourceSelector(e.target) }); + } + }); + }); +} diff --git a/plugins/martech.js b/plugins/martech.js new file mode 100644 index 0000000..6685312 --- /dev/null +++ b/plugins/martech.js @@ -0,0 +1,54 @@ +/* + * Copyright 2024 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ +function addAdsParametersTracking(usp, { sampleRUM }) { + const networks = { + google: /gclid|gclsrc|wbraid|gbraid/, + doubleclick: /dclid/, + microsoft: /msclkid/, + facebook: /fb(cl|ad_|pxl_)id/, + twitter: /tw(clid|src|term)/, + linkedin: /li_fat_id/, + pinterest: /epik/, + tiktok: /ttclid/, + }; + const params = Array.from(usp.keys()); + Object.entries(networks).forEach(([network, regex]) => { + params.filter((param) => regex.test(param)).forEach((param) => sampleRUM('paid', { source: network, target: param })); + }); +} + +function addEmailParameterTracking(usp, { sampleRUM }) { + const networks = { + mailchimp: /mc_(c|e)id/, + marketo: /mkt_tok/, + }; + const params = Array.from(usp.keys()); + Object.entries(networks).forEach(([network, regex]) => { + params.filter((param) => regex.test(param)).forEach((param) => sampleRUM('email', { source: network, target: param })); + }); +} + +function addUTMParametersTracking(usp, { sampleRUM }) { + Array.from(usp.entries()) + .filter(([key]) => key.startsWith('utm_')) + // exclude keys that may leak PII + .filter(([key]) => key !== 'utm_id') + .filter(([key]) => key !== 'utm_term') + .forEach(([source, target]) => sampleRUM('utm', { source, target })); +} + +export default function addMartechTraking({ sampleRUM }) { + const usp = new URLSearchParams(window.location.search); + addAdsParametersTracking(usp, { sampleRUM }); + addEmailParameterTracking(usp, { sampleRUM }); + addUTMParametersTracking(usp, { sampleRUM }); +} diff --git a/modules/martech.js b/plugins/onetrust.js similarity index 55% rename from modules/martech.js rename to plugins/onetrust.js index 6fb623d..747912b 100644 --- a/modules/martech.js +++ b/plugins/onetrust.js @@ -9,7 +9,7 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ -export function addCookieConsentTracking(sampleRUM) { +export default function addCookieConsentTracking({ sampleRUM }) { const cmpCookie = document.cookie.split(';') .map((c) => c.trim()) .find((cookie) => cookie.startsWith('OptanonAlertBoxClosed=')); @@ -50,39 +50,3 @@ export function addCookieConsentTracking(sampleRUM) { } } } - -export function addUTMParametersTracking(sampleRUM) { - const usp = new URLSearchParams(window.location.search); - [...usp.entries()] - .filter(([key]) => key.startsWith('utm_')) - // exclude keys that may leak PII - .filter(([key]) => key !== 'utm_id') - .filter(([key]) => key !== 'utm_term') - .forEach(([source, target]) => sampleRUM('utm', { source, target })); -} -export function addAdsParametersTracking(sampleRUM) { - const networks = { - google: /gclid|gclsrc|wbraid|gbraid/, - doubleclick: /dclid/, - microsoft: /msclkid/, - facebook: /fb(cl|ad_|pxl_)id/, - twitter: /tw(clid|src|term)/, - linkedin: /li_fat_id/, - pinterest: /epik/, - tiktok: /ttclid/, - }; - const params = Array.from(new URLSearchParams(window.location.search).keys()); - Object.entries(networks).forEach(([network, regex]) => { - params.filter((param) => regex.test(param)).forEach((param) => sampleRUM('paid', { source: network, target: param })); - }); -} -export function addEmailParameterTracking(sampleRUM) { - const networks = { - mailchimp: /mc_(c|e)id/, - marketo: /mkt_tok/, - }; - const params = Array.from(new URLSearchParams(window.location.search).keys()); - Object.entries(networks).forEach(([network, regex]) => { - params.filter((param) => regex.test(param)).forEach((param) => sampleRUM('email', { source: network, target: param })); - }); -} diff --git a/plugins/video.js b/plugins/video.js new file mode 100644 index 0000000..ff3b9fd --- /dev/null +++ b/plugins/video.js @@ -0,0 +1,17 @@ +/* + * Copyright 2025 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +export default function addVideoTracking({ context }) { + context.querySelectorAll('video').forEach(() => { + // TODO: Add video tracking + }); +} diff --git a/rollup.config.js b/rollup.config.js index 1c4803f..814290e 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -10,7 +10,8 @@ * governing permissions and limitations under the License. */ -import cleanup from 'rollup-plugin-cleanup'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { babel } from '@rollup/plugin-babel'; import pkg from 'rollup-plugin-checksum'; const checksum = pkg.default; @@ -28,34 +29,41 @@ const banner = `/* */`; const bundles = [ + // Core library { source: 'modules/index.js', outputFile: 'src/index', }, + // Library plugins + ...['cwv', 'form', 'martech', 'onetrust', 'video'].map((plugin) => ({ + source: `plugins/${plugin}.js`, + outputFile: `src/plugins/${plugin}`, + format: 'es', + })), ]; -export default [...bundles.map(({ outputFile, source }) => ({ +export default [...bundles.map(({ outputFile, source, format }) => ({ input: source, output: [ { file: `${outputFile}.map.js`, - format: 'iife', + format: format || 'iife', sourcemap: 'inline', exports: 'auto', banner, }, { file: `${outputFile}.js`, - format: 'iife', + format: format || 'iife', sourcemap: false, exports: 'auto', banner, }, ], plugins: [ - cleanup({ - comments: [], - maxEmptyLines: 0, + babel({ + babelHelpers: 'bundled', + comments: false, }), checksum({ filename: `${outputFile.split('/').pop()}.md5`, diff --git a/test/it/broken-plugin.test.html b/test/it/broken-plugin.test.html new file mode 100644 index 0000000..d70bde3 --- /dev/null +++ b/test/it/broken-plugin.test.html @@ -0,0 +1,62 @@ + + + + Test Runner + + + +

Some content

+ + + + \ No newline at end of file diff --git a/test/it/size.test.html b/test/it/size.test.html index 756de96..cda996d 100644 --- a/test/it/size.test.html +++ b/test/it/size.test.html @@ -62,7 +62,7 @@ setTimeout(resolve, 1000); }); expect(transferSize).to.be.greaterThan(0, 'the file should not be empty'); - expect(transferSize).to.be.lessThan(17 * 1024, 'we limit the size of the file to 17KB'); + expect(transferSize).to.be.lessThan(15 * 1024, 'we limit the size of the file to 15KB'); }); diff --git a/web-test-runner.config.js b/web-test-runner.config.js index d2950ea..346645c 100644 --- a/web-test-runner.config.js +++ b/web-test-runner.config.js @@ -47,7 +47,25 @@ export default { if (context.url.startsWith('/src/index.map.js')) { await next(); context.body = context.body - .replace(/navigator\.sendBeacon/g, 'fakeSendBeacon'); + .replace(/navigator\.sendBeacon/g, 'fakeSendBeacon') + // rewriting dynamic plugins base url + .replace(/document\.currentScript\.src/g, '"http://localhost:8000/plugins"'); + return true; + } else if (context.url.startsWith('/src/index.js') + || context.url.startsWith('/modules/index.js')) { + await next(); + context.body = context.body + // rewriting dynamic plugins base url + .replace(/document\.currentScript\.src/g, '"http://localhost:8000/plugins"'); + return true; + } else if (context.url.startsWith('/modules/index-broken.js')) { + const [_, search] = context.url.split('?'); + context.url = `/modules/index.js?${search}`; + await next(); + context.body = context.body + // rewriting dynamic plugins base url + .replace(/document\.currentScript\.src/g, '"http://localhost:8000/plugins"') + .replace(/\/\/ test: broken-plugin/g, 'foo: "foo.js",'); return true; } else if (context.url.startsWith('/.rum')) { if (context.url.startsWith('/.rum/@adobe/helix-rum-js@%5E2/dist/')