diff --git a/README.md b/README.md index 31f36bb..de17e49 100644 --- a/README.md +++ b/README.md @@ -20,22 +20,6 @@ The plugin must be configured by setting the `site_performance_tracker_vitals` t To send Web Vitals metrics to Google Analytics in a format compatible with the [Web Vitals Report](https://web-vitals-report.web.app), enable the following theme support and passing in the ID: -[Google Analytics (analytics.js)](https://support.google.com/analytics/answer/7476135) is supported, requires passing the ID using `ga_id`: - -```php -add_theme_support( 'site_performance_tracker_vitals', array( - 'ga_id' => 'UA-XXXXXXXX-Y', -) ); -``` - -[Global Site Tag](https://support.google.com/analytics/answer/1008080) is supported, requires passing the Analytics ID (starting with `UA-` not `GTM-`) using `gtag_id`: - -```php -add_theme_support( 'site_performance_tracker_vitals', array( - 'gtag_id' => 'UA-XXXXXXXX-Y', -) ); -``` - [GA4 Analytics](https://support.google.com/analytics/answer/9304153) is supported, requires passing the ID using `ga4_id`: ```php @@ -44,17 +28,6 @@ add_theme_support( 'site_performance_tracker_vitals', array( ) ); ``` -If you need to override the Google Analytics dimensions (defaults to `dimensions1` through `dimension3`) to store these under, pass them along on the add theme support initialisation: - -```php -add_theme_support( 'site_performance_tracker_vitals', array( - 'gtag_id' => 'UA-XXXXXXXX-Y', - 'measurementVersion' => 'dimension7', - 'eventMeta' => 'dimension8', - 'eventDebug' => 'dimension9', -) ); -``` - The following hooks can be added to a theme or a custom plugin to configure the plugin, alternatively you can configure the plugin through the settings screen, in case of duplication, plugin will take programmatically set settings. To confirm they were applied look for the `webVitalsAnalyticsData` global variable in the page source. ### Limit the number of events sent @@ -99,6 +72,11 @@ All contributions are welcome! Please create [an issue](https://github.com/xwp/s ### Changelog +#### 1.3.3 - October 30th, 2023 + +- Deprecated GA3 support. +- Used attribution build of web vitals. + #### 1.3.2 - October 30th, 2023 - Improved admin interface for GA4. diff --git a/js/dist/module/web-vitals-analytics.asset.php b/js/dist/module/web-vitals-analytics.asset.php index c607f14..f68e81f 100644 --- a/js/dist/module/web-vitals-analytics.asset.php +++ b/js/dist/module/web-vitals-analytics.asset.php @@ -1 +1 @@ - array(), 'version' => 'fce1f2f2d706b26fa13d359ef5102663'); + array(), 'version' => 'df22e0939b3993085571f1a1e05ab969'); diff --git a/js/dist/module/web-vitals-analytics.df22e0939b3993085571f1a1e05ab969.js b/js/dist/module/web-vitals-analytics.df22e0939b3993085571f1a1e05ab969.js new file mode 100644 index 0000000..9c0acfd --- /dev/null +++ b/js/dist/module/web-vitals-analytics.df22e0939b3993085571f1a1e05ab969.js @@ -0,0 +1 @@ +(()=>{"use strict";function t({name:t,value:e,delta:n,id:r,attribution:i,rating:a}){const o=window.webVitalsAnalyticsData[0],c={value:n,metric_id:r,metric_value:e,metric_delta:Math.round("CLS"===t?1e3*n:n),metric_rating:a};switch(t){case"CLS":c.debug_target=i.largestShiftTarget;break;case"INP":const{processingDuration:t,presentationDelay:e,interactionTarget:n,interactionType:r}=i,a=i.longAnimationFrameEntries.at(-1),o=a?.scripts?.sort(((t,e)=>e.duration-t.duration))[0];if(c.processingDuration=Math.round(t),c.presentationDelay=Math.round(e),c.debug_target=n,c.interactionType=r,o){const{invokerType:t,invoker:e,sourceURL:n,sourceCharPosition:r,sourceFunctionName:i}=o,{startTime:u,duration:s,styleAndLayoutStart:f}=a,d=u+s-f;c.invokerType=t,c.invoker=e,c.sourceURL=n,c.sourceCharPosition=r,c.sourceFunctionName=i,c.styleLayoutDuration=Math.round(d)}break;case"LCP":c.debug_target=i.element;break;default:return"(not set)"}o&&o.ga4_id&&(window["gtag"]||console.log)("event",t,c)}var e,n,r=function(){var t=self.performance&&performance.getEntriesByType&&performance.getEntriesByType("navigation")[0];if(t&&t.responseStart>0&&t.responseStart(e||100)-1)return n||i;if(n=n?i+">"+n:i,r.id)break;t=r.parentNode}}catch(t){}return n},c=-1,u=function(){return c},s=function(t){addEventListener("pageshow",(function(e){e.persisted&&(c=e.timeStamp,t(e))}),!0)},f=function(){var t=r();return t&&t.activationStart||0},d=function(t,e){var n=r(),i="navigate";return u()>=0?i="back-forward-cache":n&&(document.prerendering||f()>0?i="prerender":document.wasDiscarded?i="restore":n.type&&(i=n.type.replace(/_/g,"-"))),{name:t,value:void 0===e?-1:e,rating:"good",delta:0,entries:[],id:"v4-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12),navigationType:i}},l=function(t,e,n){try{if(PerformanceObserver.supportedEntryTypes.includes(t)){var r=new PerformanceObserver((function(t){Promise.resolve().then((function(){e(t.getEntries())}))}));return r.observe(Object.assign({type:t,buffered:!0},n||{})),r}}catch(t){}},m=function(t,e,n,r){var i,a;return function(o){e.value>=0&&(o||r)&&((a=e.value-(i||0))||void 0===i)&&(i=e.value,e.delta=a,e.rating=function(t,e){return t>e[1]?"poor":t>e[0]?"needs-improvement":"good"}(e.value,n),t(e))}},p=function(t){requestAnimationFrame((function(){return requestAnimationFrame((function(){return t()}))}))},v=function(t){document.addEventListener("visibilitychange",(function(){"hidden"===document.visibilityState&&t()}))},g=function(t){var e=!1;return function(){e||(t(),e=!0)}},h=-1,y=function(){return"hidden"!==document.visibilityState||document.prerendering?1/0:0},T=function(t){"hidden"===document.visibilityState&&h>-1&&(h="visibilitychange"===t.type?t.timeStamp:0,S())},E=function(){addEventListener("visibilitychange",T,!0),addEventListener("prerenderingchange",T,!0)},S=function(){removeEventListener("visibilitychange",T,!0),removeEventListener("prerenderingchange",T,!0)},b=function(){return h<0&&(h=y(),E(),s((function(){setTimeout((function(){h=y(),E()}),0)}))),{get firstHiddenTime(){return h}}},C=function(t){document.prerendering?addEventListener("prerenderingchange",(function(){return t()}),!0):t()},w=[1800,3e3],L=function(t,e){e=e||{},C((function(){var n,r=b(),i=d("FCP"),a=l("paint",(function(t){t.forEach((function(t){"first-contentful-paint"===t.name&&(a.disconnect(),t.startTimee.latency){if(n)t.duration>n.latency?(n.entries=[t],n.latency=t.duration):t.duration===n.latency&&t.startTime===n.entries[0].startTime&&n.entries.push(t);else{var r={id:t.interactionId,latency:t.duration,entries:[t]};F.set(r.id,r),x.push(r)}x.sort((function(t,e){return e.latency-t.latency})),x.length>10&&x.splice(10).forEach((function(t){return F.delete(t.id)}))}}},O=function(t){var e=self.requestIdleCallback||self.setTimeout,n=-1;return t=g(t),"hidden"===document.visibilityState?t():(n=e(t),v(t)),n},j=[200,500],N=[],H=new Map,U=[],V=new WeakMap,W=new Map,z=-1,G=function(t){t.forEach((function(t){return N.push(t)}))},J=function(){W.size>10&&W.forEach((function(t,e){F.has(e)||W.delete(e)})),U=U.slice(-50);var t=new Set(U.concat(x.map((function(t){return V.get(t.entries[0])}))));H.forEach((function(e,n){t.has(n)||H.delete(n)}));var e=new Set;H.forEach((function(t){K(t.startTime,t.processingEnd).forEach((function(t){e.add(t)}))})),N=Array.from(e),z=-1};_.push((function(t){t.interactionId&&t.target&&!W.has(t.interactionId)&&W.set(t.interactionId,t.target)}),(function(t){for(var e,n=t.startTime+t.duration,r=U.length-1;r>=0;r--)if(e=U[r],Math.abs(n-e)<=8){var i=H.get(e);i.startTime=Math.min(t.startTime,i.startTime),i.processingStart=Math.min(t.processingStart,i.processingStart),i.processingEnd=Math.max(t.processingEnd,i.processingEnd),i.entries.push(t),n=e;break}n!==e&&(U.push(n),H.set(n,{startTime:t.startTime,processingStart:t.processingStart,processingEnd:t.processingEnd,entries:[t]})),(t.interactionId||"first-input"===t.entryType)&&V.set(t,n)}),(function(){z<0&&(z=O(J))}));var K=function(t,e){for(var n,r=[],i=0;n=N[i];i++)if(!(n.startTime+n.duratione)break;r.push(n)}return r},Q=function(t,e){n||(n=l("long-animation-frame",G)),function(t,e){e=e||{},C((function(){var n;P();var r,i=d("INP"),a=function(t){t.forEach(q);var e,n=(e=Math.min(x.length-1,Math.floor(R()/50)),x[e]);n&&n.latency!==i.value&&(i.value=n.latency,i.entries=n.entries,r())},o=l("event",a,{durationThreshold:null!==(n=e.durationThreshold)&&void 0!==n?n:40});r=m(t,i,j,e.reportAllChanges),o&&("PerformanceEventTiming"in self&&"interactionId"in PerformanceEventTiming.prototype&&o.observe({type:"first-input",buffered:!0}),v((function(){a(o.takeRecords()),r(!0)})),s((function(){B=0,x.length=0,F.clear(),i=d("INP"),r=m(t,i,j,e.reportAllChanges)})))}))}((function(e){O((function(){var n=function(t){var e=t.entries[0],n=V.get(e),r=H.get(n),a=e.processingStart,c=r.processingEnd,u=r.entries.sort((function(t,e){return t.processingStart-e.processingStart})),s=K(e.startTime,c),f=t.entries.find((function(t){return t.target})),d=f&&f.target||W.get(e.interactionId),l=[e.startTime+e.duration,c].concat(s.map((function(t){return t.startTime+t.duration}))),m=Math.max.apply(Math,l),p={interactionTarget:o(d),interactionTargetElement:d,interactionType:e.name.startsWith("key")?"keyboard":"pointer",interactionTime:e.startTime,nextPaintTime:m,processedEventEntries:u,longAnimationFrameEntries:s,inputDelay:a-e.startTime,processingDuration:c-a,presentationDelay:Math.max(m-c,0),loadState:i(e.startTime)};return Object.assign(t,{attribution:p})}(e);t(n)}))}),e)},X=[2500,4e3],Y={},Z=[800,1800],$=function t(e){document.prerendering?C((function(){return t(e)})):"complete"!==document.readyState?addEventListener("load",(function(){return t(e)}),!0):setTimeout(e,0)},tt=function(t,e){e=e||{};var n=d("TTFB"),i=m(t,n,Z,e.reportAllChanges);$((function(){var a=r();a&&(n.value=Math.max(a.responseStart-f(),0),n.entries=[a],i(!0),s((function(){n=d("TTFB",0),(i=m(t,n,Z,e.reportAllChanges))(!0)})))}))};new Date;function et(){!function(t,e){!function(t,e){e=e||{},L(g((function(){var n,r=d("CLS",0),i=0,a=[],o=function(t){t.forEach((function(t){if(!t.hadRecentInput){var e=a[0],n=a[a.length-1];i&&t.startTime-n.startTime<1e3&&t.startTime-e.startTime<5e3?(i+=t.value,a.push(t)):(i=t.value,a=[t])}})),i>r.value&&(r.value=i,r.entries=a,n())},c=l("layout-shift",o);c&&(n=m(t,r,M,e.reportAllChanges),v((function(){o(c.takeRecords()),n(!0)})),s((function(){i=0,r=d("CLS",0),n=m(t,r,M,e.reportAllChanges),p((function(){return n()}))})),setTimeout(n,0))})))}((function(e){var n=function(t){var e,n={};if(t.entries.length){var r=t.entries.reduce((function(t,e){return t&&t.value>e.value?t:e}));if(r&&r.sources&&r.sources.length){var a=(e=r.sources).find((function(t){return t.node&&1===t.node.nodeType}))||e[0];a&&(n={largestShiftTarget:o(a.node),largestShiftTime:r.startTime,largestShiftValue:r.value,largestShiftSource:a,largestShiftEntry:r,loadState:i(r.startTime)})}}return Object.assign(t,{attribution:n})}(e);t(n)}),e)}(t),function(t,e){L((function(e){var n=function(t){var e={timeToFirstByte:0,firstByteToFCP:t.value,loadState:i(u())};if(t.entries.length){var n=r(),a=t.entries[t.entries.length-1];if(n){var o=n.activationStart||0,c=Math.max(0,n.responseStart-o);e={timeToFirstByte:c,firstByteToFCP:t.value-c,loadState:i(t.entries[0].startTime),navigationEntry:n,fcpEntry:a}}}return Object.assign(t,{attribution:e})}(e);t(n)}),e)}(t),function(t,e){!function(t,e){e=e||{},C((function(){var n,r=b(),i=d("LCP"),a=function(t){e.reportAllChanges||(t=t.slice(-1)),t.forEach((function(t){t.startTime{"use strict";const e={CLS:[.1,.25],FCP:[1800,3e3],FID:[100,300],LCP:[2500,4e3],INP:[200,500],TTFB:[501,1500]},n=window.webVitalsAnalyticsData[0].measurementVersion?window.webVitalsAnalyticsData[0].measurementVersion:"dimension1",t=window.webVitalsAnalyticsData[0].eventMeta?window.webVitalsAnalyticsData[0].eventMeta:"dimension2",i=window.webVitalsAnalyticsData[0].eventDebug?window.webVitalsAnalyticsData[0].eventDebug:"dimension3",r="6";let a=!1;function o(e){return window[e]||console.log}function c(e,n){return e>n[1]?"poor":e>n[0]?"ni":"good"}function u(e){try{let n=e.nodeName.toLowerCase();return"body"===n?"html>body":e.id?`${n}#${e.id}`:(e.className&&e.className.length&&(n+=`.${[...e.classList.values()].join(".")}`),`${u(e.parentElement)}>${n}`)}catch(e){return"(error)"}}function s(e,n=[]){const t=n[0],i=n[n.length-1],r=n.sort(((e,n)=>n.duration-e.duration||n.processingEnd-n.processingStart-(e.processingEnd-e.processingStart)))[0];switch(e){case"LCP":if(i)return u(i.element);break;case"FID":if(t){const{name:e}=t;return`${e}(${u(t.target)})`}break;case"INP":if(r){const{name:e}=r;return`${e}(${u(r.target)})`}break;case"CLS":if(n.length){const e=n.reduce(((e,n)=>e&&e.value>n.value?e:n));if(e&&e.sources){const n=e.sources.reduce(((e,n)=>e.node&&e.previousRect.width*e.previousRect.height>n.previousRect.width*n.previousRect.height?e:n));if(n)return u(n.node)}}break;default:return"(not set)"}}function d({name:u,value:d,delta:f,id:l,entries:v}){const m=window.webVitalsAnalyticsData[0];m&&m.gtag_id&&(a||(!function(e){"gtag"in window&>ag("config",e,{send_page_view:!1,transport_type:"beacon",measurement_version:r,custom_map:{[n]:"measurement_version",[t]:"event_meta",[i]:"event_debug"}})}(m.gtag_id),a=!0),o("gtag")("event",u,{event_category:"Web Vitals",event_label:l,value:Math.round("CLS"===u?1e3*f:f),non_interaction:!0,event_meta:c(d,e[u]),event_debug:s(u,v)})),m&&m.ga_id&&(o("ga")("create",m.ga_id,"auto"),o("ga")("send","event",{eventCategory:"Web Vitals",eventAction:u,eventLabel:l,eventValue:Math.round("CLS"===u?1e3*f:f),nonInteraction:!0,transport:"beacon",[t]:c(d,e[u]),[i]:s(u,v),[n]:r})),m&&m.ga4_id&&o("gtag")("event",u,{value:f,metric_id:l,metric_value:Math.round("CLS"===u?1e3*f:f),event_meta:c(d,e[u]),event_debug:s(u,v),measurement_version:r})}var f,l,v,m,p,g=-1,h=function(e){addEventListener("pageshow",(function(n){n.persisted&&(g=n.timeStamp,e(n))}),!0)},w=function(){return window.performance&&performance.getEntriesByType&&performance.getEntriesByType("navigation")[0]},y=function(){var e=w();return e&&e.activationStart||0},b=function(e,n){var t=w(),i="navigate";return g>=0?i="back-forward-cache":t&&(document.prerendering||y()>0?i="prerender":document.wasDiscarded?i="restore":t.type&&(i=t.type.replace(/_/g,"-"))),{name:e,value:void 0===n?-1:n,rating:"good",delta:0,entries:[],id:"v3-".concat(Date.now(),"-").concat(Math.floor(8999999999999*Math.random())+1e12),navigationType:i}},T=function(e,n,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){var i=new PerformanceObserver((function(e){Promise.resolve().then((function(){n(e.getEntries())}))}));return i.observe(Object.assign({type:e,buffered:!0},t||{})),i}}catch(e){}},E=function(e,n,t,i){var r,a;return function(o){n.value>=0&&(o||i)&&((a=n.value-(r||0))||void 0===r)&&(r=n.value,n.delta=a,n.rating=function(e,n){return e>n[1]?"poor":e>n[0]?"needs-improvement":"good"}(n.value,t),e(n))}},C=function(e){requestAnimationFrame((function(){return requestAnimationFrame((function(){return e()}))}))},L=function(e){var n=function(n){"pagehide"!==n.type&&"hidden"!==document.visibilityState||e(n)};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},S=function(e){var n=!1;return function(t){n||(e(t),n=!0)}},_=-1,A=function(){return"hidden"!==document.visibilityState||document.prerendering?1/0:0},D=function(e){"hidden"===document.visibilityState&&_>-1&&(_="visibilitychange"===e.type?e.timeStamp:0,M())},I=function(){addEventListener("visibilitychange",D,!0),addEventListener("prerenderingchange",D,!0)},M=function(){removeEventListener("visibilitychange",D,!0),removeEventListener("prerenderingchange",D,!0)},P=function(){return _<0&&(_=A(),I(),h((function(){setTimeout((function(){_=A(),I()}),0)}))),{get firstHiddenTime(){return _}}},k=function(e){document.prerendering?addEventListener("prerenderingchange",(function(){return e()}),!0):e()},V=[1800,3e3],F=function(e,n){n=n||{},k((function(){var t,i=P(),r=b("FCP"),a=T("paint",(function(e){e.forEach((function(e){"first-contentful-paint"===e.name&&(a.disconnect(),e.startTime=0&&l1e12?new Date:performance.now())-e.timeStamp;"pointerdown"==e.type?function(e,n){var t=function(){x(e,n),r()},i=function(){r()},r=function(){removeEventListener("pointerup",t,$),removeEventListener("pointercancel",i,$)};addEventListener("pointerup",t,$),addEventListener("pointercancel",i,$)}(n,e):x(n,e)}},H=function(e){["mousedown","keydown","touchstart","pointerdown"].forEach((function(n){return e(n,q,$)}))},j=[100,300],O=0,W=1/0,z=0,G=function(e){e.forEach((function(e){e.interactionId&&(W=Math.min(W,e.interactionId),z=Math.max(z,e.interactionId),O=z?(z-W)/7+1:0)}))},J=function(){return p?O:performance.interactionCount||0},K=function(){"interactionCount"in performance||p||(p=T("event",G,{type:"event",buffered:!0,durationThreshold:0}))},Q=[200,500],U=0,X=function(){return J()-U},Y=[],Z={},ee=function(e){var n=Y[Y.length-1],t=Z[e.interactionId];if(t||Y.length<10||e.duration>n.latency){if(t)t.entries.push(e),t.latency=Math.max(t.latency,e.duration);else{var i={id:e.interactionId,latency:e.duration,entries:[e]};Z[i.id]=i,Y.push(i)}Y.sort((function(e,n){return n.latency-e.latency})),Y.splice(10).forEach((function(e){delete Z[e.id]}))}},ne=[2500,4e3],te={},ie=[800,1800],re=function e(n){document.prerendering?k((function(){return e(n)})):"complete"!==document.readyState?addEventListener("load",(function(){return e(n)}),!0):setTimeout(n,0)},ae=function(e,n){n=n||{};var t=b("TTFB"),i=E(e,t,ie,n.reportAllChanges);re((function(){var r=w();if(r){var a=r.responseStart;if(a<=0||a>performance.now())return;t.value=Math.max(a-y(),0),t.entries=[r],i(!0),h((function(){t=b("TTFB",0),(i=E(e,t,ie,n.reportAllChanges))(!0)}))}}))};function oe(){!function(e,n){n=n||{},F(S((function(){var t,i=b("CLS",0),r=0,a=[],o=function(e){e.forEach((function(e){if(!e.hadRecentInput){var n=a[0],t=a[a.length-1];r&&e.startTime-t.startTime<1e3&&e.startTime-n.startTime<5e3?(r+=e.value,a.push(e)):(r=e.value,a=[e])}})),r>i.value&&(i.value=r,i.entries=a,t())},c=T("layout-shift",o);c&&(t=E(e,i,R,n.reportAllChanges),L((function(){o(c.takeRecords()),t(!0)})),h((function(){r=0,i=b("CLS",0),t=E(e,i,R,n.reportAllChanges),C((function(){return t()}))})),setTimeout(t,0))})))}(d),F(d),function(e,n){n=n||{},k((function(){var t,i=P(),r=b("FID"),a=function(e){e.startTime0&&(r.value=0,r.entries=[]),i(!0)})),h((function(){Y=[],U=J(),r=b("INP"),i=E(e,r,Q,n.reportAllChanges)})))}))}(d)}"requestIdleCallback"in window&&"object"==typeof window.webVitalsAnalyticsData&&window.requestIdleCallback(oe)})(); \ No newline at end of file diff --git a/js/src/measure-web-vitals.js b/js/src/measure-web-vitals.js index a1066c5..39df88e 100644 --- a/js/src/measure-web-vitals.js +++ b/js/src/measure-web-vitals.js @@ -1,10 +1,9 @@ import { sendToAnalytics } from './send-to-analytics'; -import { onCLS, onFCP, onFID, onLCP, onTTFB, onINP } from 'web-vitals'; +import { onCLS, onFCP, onLCP, onTTFB, onINP } from 'web-vitals/attribution'; export function measureWebVitals() { onCLS( sendToAnalytics ); onFCP( sendToAnalytics ); - onFID( sendToAnalytics ); onLCP( sendToAnalytics ); onTTFB( sendToAnalytics ); onINP( sendToAnalytics ); diff --git a/js/src/send-to-analytics.js b/js/src/send-to-analytics.js index 6f2a49a..fa01b46 100644 --- a/js/src/send-to-analytics.js +++ b/js/src/send-to-analytics.js @@ -14,176 +14,57 @@ * limitations under the License. */ -/* global gtag */ - -const vitalThresholds = { - CLS: [ 0.1, 0.25 ], - FCP: [ 1800, 3000 ], - FID: [ 100, 300 ], - LCP: [ 2500, 4000 ], - INP: [ 200, 500 ], - TTFB: [ 501, 1500 ], -}; - -const uaDimMeasurementVersion = window.webVitalsAnalyticsData[ 0 ].measurementVersion - ? window.webVitalsAnalyticsData[ 0 ].measurementVersion - : 'dimension1'; -const uaDimEventMeta = window.webVitalsAnalyticsData[ 0 ].eventMeta - ? window.webVitalsAnalyticsData[ 0 ].eventMeta - : 'dimension2'; -const uaDimEventDebug = window.webVitalsAnalyticsData[ 0 ].eventDebug - ? window.webVitalsAnalyticsData[ 0 ].eventDebug - : 'dimension3'; - -const measurementVersion = '6'; - -let gtagConfigured = false; - function getDeliveryFunction( type ) { // eslint-disable-next-line no-console return window[ type ] || console.log; } -function configureGtag( id ) { - if ( 'gtag' in window ) { - gtag( 'config', id, { - send_page_view: false, - transport_type: 'beacon', - measurement_version: measurementVersion, - custom_map: { - [ uaDimMeasurementVersion ]: 'measurement_version', - [ uaDimEventMeta ]: 'event_meta', - [ uaDimEventDebug ]: 'event_debug', - }, - } ); - } -} +export function sendToAnalytics( { name, value, delta, id, attribution, rating } ) { + const analyticsData = window.webVitalsAnalyticsData[ 0 ]; + const eventParams = { + value: delta, + metric_id: id, + metric_value: value, + metric_delta: Math.round( name === 'CLS' ? delta * 1000 : delta ), + metric_rating: rating, + }; -function getRating( value, thresholds ) { - if ( value > thresholds[ 1 ] ) { - return 'poor'; - } - if ( value > thresholds[ 0 ] ) { - return 'ni'; - } - return 'good'; -} + switch ( name ) { + case 'CLS': + eventParams.debug_target = attribution.largestShiftTarget; + break; + case 'INP': + const { processingDuration, presentationDelay, interactionTarget, interactionType } = attribution; + const loaf = attribution.longAnimationFrameEntries.at( -1 ); + const script = loaf?.scripts?.sort( ( a, b ) => b.duration - a.duration )[ 0 ]; -function getNodePath( node ) { - try { - let name = node.nodeName.toLowerCase(); - if ( name === 'body' ) { - return 'html>body'; - } - if ( node.id ) { - return `${ name }#${ node.id }`; - } - if ( node.className && node.className.length ) { - name += `.${ [ ...node.classList.values() ].join( '.' ) }`; - } - return `${ getNodePath( node.parentElement ) }>${ name }`; - } catch ( error ) { - return '(error)'; - } -} + eventParams.processingDuration = Math.round( processingDuration ); + eventParams.presentationDelay = Math.round( presentationDelay ); + eventParams.debug_target = interactionTarget; + eventParams.interactionType = interactionType; -function getDebugInfo( metricName, entries = [] ) { - const firstEntry = entries[ 0 ]; - const lastEntry = entries[ entries.length - 1 ]; - const longestEntry = entries.sort( ( a, b ) => { - // Sort by: 1) duration (DESC), then 2) processing time (DESC) - return ( - b.duration - a.duration || - b.processingEnd - - b.processingStart - - ( a.processingEnd - a.processingStart ) - ); - } )[ 0 ]; + if ( script ) { + const { invokerType, invoker, sourceURL, sourceCharPosition, sourceFunctionName } = script; + const { startTime, duration, styleAndLayoutStart } = loaf; + const endTime = startTime + duration; + const styleLayoutDuration = endTime - styleAndLayoutStart; - switch ( metricName ) { - case 'LCP': - if ( lastEntry ) { - return getNodePath( lastEntry.element ); - } - break; - case 'FID': - if ( firstEntry ) { - const { name } = firstEntry; - return `${ name }(${ getNodePath( firstEntry.target ) })`; - } - break; - case 'INP': - if ( longestEntry ) { - const { name } = longestEntry; - return `${ name }(${ getNodePath( longestEntry.target ) })`; + eventParams.invokerType = invokerType; + eventParams.invoker = invoker; + eventParams.sourceURL = sourceURL; + eventParams.sourceCharPosition = sourceCharPosition; + eventParams.sourceFunctionName = sourceFunctionName; + eventParams.styleLayoutDuration = Math.round( styleLayoutDuration ); } break; - case 'CLS': - if ( entries.length ) { - const largestShift = entries.reduce( ( a, b ) => { - return a && a.value > b.value ? a : b; - } ); - if ( largestShift && largestShift.sources ) { - const largestSource = largestShift.sources.reduce( - ( a, b ) => { - return a.node && - a.previousRect.width * a.previousRect.height > - b.previousRect.width * b.previousRect.height - ? a - : b; - } - ); - if ( largestSource ) { - return getNodePath( largestSource.node ); - } - } - } + case 'LCP': + eventParams.debug_target = attribution.element; break; default: return '(not set)'; } -} - -export function sendToAnalytics( { name, value, delta, id, entries } ) { - const analyticsData = window.webVitalsAnalyticsData[ 0 ]; - if ( analyticsData && analyticsData.gtag_id ) { - if ( ! gtagConfigured ) { - configureGtag( analyticsData.gtag_id ); - gtagConfigured = true; - } - - getDeliveryFunction( 'gtag' )( 'event', name, { - event_category: 'Web Vitals', - event_label: id, - value: Math.round( name === 'CLS' ? delta * 1000 : delta ), - non_interaction: true, - event_meta: getRating( value, vitalThresholds[ name ] ), - event_debug: getDebugInfo( name, entries ), - } ); - } - if ( analyticsData && analyticsData.ga_id ) { - getDeliveryFunction( 'ga' )( 'create', analyticsData.ga_id, 'auto' ); - getDeliveryFunction( 'ga' )( 'send', 'event', { - eventCategory: 'Web Vitals', - eventAction: name, - eventLabel: id, - eventValue: Math.round( name === 'CLS' ? delta * 1000 : delta ), - nonInteraction: true, - transport: 'beacon', - [ uaDimEventMeta ]: getRating( value, vitalThresholds[ name ] ), - [ uaDimEventDebug ]: getDebugInfo( name, entries ), - [ uaDimMeasurementVersion ]: measurementVersion, - } ); - } if ( analyticsData && analyticsData.ga4_id ) { - getDeliveryFunction( 'gtag' )( 'event', name, { - value: delta, - metric_id: id, - metric_value: Math.round( name === 'CLS' ? delta * 1000 : delta ), - event_meta: getRating( value, vitalThresholds[ name ] ), - event_debug: getDebugInfo( name, entries ), - measurement_version: measurementVersion, - } ); + getDeliveryFunction( 'gtag' )( 'event', name, eventParams ); } } diff --git a/package-lock.json b/package-lock.json index 5f24ac4..dbeefd5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,12 +8,12 @@ "hasInstallScript": true, "license": "GPL-2.0-or-later", "dependencies": { - "web-vitals": "^3.5.0" + "web-vitals": "^4.0.1" }, "devDependencies": { "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@wordpress/dependency-extraction-webpack-plugin": "^4.27.0", - "@wordpress/env": "^5.7.0", + "@wordpress/env": "^9.3.1", "@wordpress/scripts": "^22.5.0", "gulp": "^4.0.2", "gulp-rename": "^2.0.0", @@ -4473,14 +4473,14 @@ } }, "node_modules/@wordpress/env": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-5.16.0.tgz", - "integrity": "sha512-zx6UO8PuJBrQ34cfeedK1HlGHLFaj7oWzTo9tTt+noB79Ttqc4+a0lYwDqBLLJhlHU+cWgcyOP2lB6TboXH0xA==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-9.10.0.tgz", + "integrity": "sha512-GqUg1XdrUXI3l5NhHhEZisrccW+VPqJSU5xO1IXybI6KOvmSecidxWEqlMj26vzu2P5aLCWZcx28QkrrY3jvdg==", "dev": true, "dependencies": { "chalk": "^4.0.0", "copy-dir": "^1.3.0", - "docker-compose": "^0.22.2", + "docker-compose": "^0.24.3", "extract-zip": "^1.6.7", "got": "^11.8.5", "inquirer": "^7.1.0", @@ -6645,9 +6645,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001558", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001558.tgz", - "integrity": "sha512-/Et7DwLqpjS47JPEcz6VnxU9PwcIdVi0ciLXRWBQdj1XFye68pSQYpV0QtPTfUKWuOaEig+/Vez2l74eDc1tPQ==", + "version": "1.0.30001628", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001628.tgz", + "integrity": "sha512-S3BnR4Kh26TBxbi5t5kpbcUlLJb9lhtDXISDPwOfI+JoC+ik0QksvkZtUVyikw3hjnkgkMPSJ8oIM9yMm9vflA==", "dev": true, "funding": [ { @@ -8505,14 +8505,29 @@ } }, "node_modules/docker-compose": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.22.2.tgz", - "integrity": "sha512-iXWb5+LiYmylIMFXvGTYsjI1F+Xyx78Jm/uj1dxwwZLbWkUdH6yOXY5Nr3RjbYX15EgbGJCq78d29CmWQQQMPg==", + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz", + "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==", "dev": true, + "dependencies": { + "yaml": "^2.2.2" + }, "engines": { "node": ">= 6.0.0" } }, + "node_modules/docker-compose/node_modules/yaml": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", + "dev": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -23618,9 +23633,9 @@ } }, "node_modules/web-vitals": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.5.0.tgz", - "integrity": "sha512-f5YnCHVG9Y6uLCePD4tY8bO/Ge15NPEQWtvm3tPzDKygloiqtb4SVqRHBcrIAqo2ztqX5XueqDn97zHF0LdT6w==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.0.1.tgz", + "integrity": "sha512-AW6qT/vXK3pbf+WgVcEBXY//AWCpXjVKgdb6rt0cARSUdtT+NUtZCOeo+CSLUX7PjSQ275DmxfkAs7QlPbtR6w==" }, "node_modules/webidl-conversions": { "version": "6.1.0", @@ -27991,14 +28006,14 @@ } }, "@wordpress/env": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-5.16.0.tgz", - "integrity": "sha512-zx6UO8PuJBrQ34cfeedK1HlGHLFaj7oWzTo9tTt+noB79Ttqc4+a0lYwDqBLLJhlHU+cWgcyOP2lB6TboXH0xA==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-9.10.0.tgz", + "integrity": "sha512-GqUg1XdrUXI3l5NhHhEZisrccW+VPqJSU5xO1IXybI6KOvmSecidxWEqlMj26vzu2P5aLCWZcx28QkrrY3jvdg==", "dev": true, "requires": { "chalk": "^4.0.0", "copy-dir": "^1.3.0", - "docker-compose": "^0.22.2", + "docker-compose": "^0.24.3", "extract-zip": "^1.6.7", "got": "^11.8.5", "inquirer": "^7.1.0", @@ -29592,9 +29607,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001558", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001558.tgz", - "integrity": "sha512-/Et7DwLqpjS47JPEcz6VnxU9PwcIdVi0ciLXRWBQdj1XFye68pSQYpV0QtPTfUKWuOaEig+/Vez2l74eDc1tPQ==", + "version": "1.0.30001628", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001628.tgz", + "integrity": "sha512-S3BnR4Kh26TBxbi5t5kpbcUlLJb9lhtDXISDPwOfI+JoC+ik0QksvkZtUVyikw3hjnkgkMPSJ8oIM9yMm9vflA==", "dev": true }, "capital-case": { @@ -31016,10 +31031,21 @@ } }, "docker-compose": { - "version": "0.22.2", - "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.22.2.tgz", - "integrity": "sha512-iXWb5+LiYmylIMFXvGTYsjI1F+Xyx78Jm/uj1dxwwZLbWkUdH6yOXY5Nr3RjbYX15EgbGJCq78d29CmWQQQMPg==", - "dev": true + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz", + "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==", + "dev": true, + "requires": { + "yaml": "^2.2.2" + }, + "dependencies": { + "yaml": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", + "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", + "dev": true + } + } }, "doctrine": { "version": "3.0.0", @@ -42546,9 +42572,9 @@ } }, "web-vitals": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-3.5.0.tgz", - "integrity": "sha512-f5YnCHVG9Y6uLCePD4tY8bO/Ge15NPEQWtvm3tPzDKygloiqtb4SVqRHBcrIAqo2ztqX5XueqDn97zHF0LdT6w==" + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-4.0.1.tgz", + "integrity": "sha512-AW6qT/vXK3pbf+WgVcEBXY//AWCpXjVKgdb6rt0cARSUdtT+NUtZCOeo+CSLUX7PjSQ275DmxfkAs7QlPbtR6w==" }, "webidl-conversions": { "version": "6.1.0", diff --git a/package.json b/package.json index 7751d43..c1bdc0a 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "devDependencies": { "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@wordpress/dependency-extraction-webpack-plugin": "^4.27.0", - "@wordpress/env": "^5.7.0", + "@wordpress/env": "^9.3.1", "@wordpress/scripts": "^22.5.0", "gulp": "^4.0.2", "gulp-rename": "^2.0.0", @@ -58,11 +58,11 @@ "format:php": "composer lint-fix", "test": "npm-run-all test:*", "test:js": "wp-scripts test-unit-js ./js/src", - "test:php": "wp-env run tests-cli 'bash -c \"cd wp-content/plugins/site-performance-tracker && vendor/bin/phpunit\"'", + "test:php": "wp-env run tests-cli --env-cwd=/var/www/html/wp-content/plugins/site-performance-tracker/ vendor/bin/phpunit", "release": "npm-run-all build zip", "zip": "gulp zip" }, "dependencies": { - "web-vitals": "^3.5.0" + "web-vitals": "^4.0.1" } } diff --git a/php/src/class-analyticsidfield.php b/php/src/class-analyticsidfield.php index 8bb13e6..02b0a28 100644 --- a/php/src/class-analyticsidfield.php +++ b/php/src/class-analyticsidfield.php @@ -56,20 +56,11 @@ public function render() { $display_theme_override_warning = false; $property_name = self::OPTION_TAG_ID; - if ( isset( $options[ self::OPTION_ANALYTICS_ID ] ) ) { - $options[ self::OPTION_TAG_ID ] = $options[ self::OPTION_ANALYTICS_ID ]; - } elseif ( isset( $options[ self::OPTION_GA4_ID ] ) ) { + if ( isset( $options[ self::OPTION_GA4_ID ] ) ) { $options[ self::OPTION_TAG_ID ] = $options[ self::OPTION_GA4_ID ]; } - if ( isset( $hardcoded_tracker_config[ self::OPTION_ANALYTICS_ID ] ) ) { - $options[ self::OPTION_TAG_ID ] = $hardcoded_tracker_config[ self::OPTION_ANALYTICS_ID ]; - $property_name = self::OPTION_ANALYTICS_ID; - $display_theme_override_warning = true; - } elseif ( isset( $hardcoded_tracker_config[ self::OPTION_TAG_ID ] ) ) { - $options[ self::OPTION_TAG_ID ] = $hardcoded_tracker_config[ self::OPTION_TAG_ID ]; - $display_theme_override_warning = true; - } elseif ( isset( $hardcoded_tracker_config[ self::OPTION_GA4_ID ] ) ) { + if ( isset( $hardcoded_tracker_config[ self::OPTION_GA4_ID ] ) ) { $options[ self::OPTION_TAG_ID ] = $hardcoded_tracker_config[ self::OPTION_GA4_ID ]; $property_name = self::OPTION_GA4_ID; $display_theme_override_warning = true; diff --git a/php/src/class-analyticstypesfield.php b/php/src/class-analyticstypesfield.php index 2dfee66..47060b0 100644 --- a/php/src/class-analyticstypesfield.php +++ b/php/src/class-analyticstypesfield.php @@ -40,24 +40,12 @@ public function render() { $options = $this->settings->get_settings(); $hardcoded_tracker_config = $this->settings->get_hardcoded_tracker_config(); $display_theme_override_warning = false; - if ( isset( $hardcoded_tracker_config[ AnalyticsIdField::OPTION_ANALYTICS_ID ] ) ) { - $options[ self::OPTION_ANALYTICS_TYPES ] = AnalyticsIdField::OPTION_ANALYTICS_ID; - $display_theme_override_warning = true; - } elseif ( isset( $hardcoded_tracker_config[ AnalyticsIdField::OPTION_TAG_ID ] ) ) { - $options[ self::OPTION_ANALYTICS_TYPES ] = 'gtm'; - $display_theme_override_warning = true; - } elseif ( isset( $hardcoded_tracker_config[ AnalyticsIdField::OPTION_GA4_ID ] ) ) { + if ( isset( $hardcoded_tracker_config[ AnalyticsIdField::OPTION_GA4_ID ] ) ) { $options[ self::OPTION_ANALYTICS_TYPES ] = 'ga4'; $display_theme_override_warning = true; } ?> print_readonly( $option_name ); ?>> - show_theme_warning( $display_theme_override_warning ); - } - - /** - * Get current field classes. - */ - protected function get_classes() { - return array( - 'dimension', - $this->get_id(), - ); - } -} diff --git a/php/src/class-eventdebugdimensionfield.php b/php/src/class-eventdebugdimensionfield.php deleted file mode 100644 index 090970b..0000000 --- a/php/src/class-eventdebugdimensionfield.php +++ /dev/null @@ -1,63 +0,0 @@ -fields = array( new AnalyticsTypesField(), new AnalyticsIdField(), - new MeasurementVersionDimensionField(), - new EventMetaDimensionField(), - new EventDebugDimensionField(), new WebVitalsTrackingRatioField(), ); } @@ -132,7 +129,7 @@ public function render_settings_page() {