From f5357db006b99952f5100517fae4f39b5f35e491 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Wed, 10 May 2023 17:40:15 -0600 Subject: [PATCH 01/12] Add Web Vitals, initial structure --- assets/web-vitals.js | 1 + collectors/web-vitals.php | 41 ++++++++++++++++++ data/web_vitals.php | 37 ++++++++++++++++ dispatchers/Html.php | 9 ++++ output/html/web_vitals.php | 86 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 174 insertions(+) create mode 100644 assets/web-vitals.js create mode 100644 collectors/web-vitals.php create mode 100644 data/web_vitals.php create mode 100644 output/html/web_vitals.php diff --git a/assets/web-vitals.js b/assets/web-vitals.js new file mode 100644 index 000000000..24345e132 --- /dev/null +++ b/assets/web-vitals.js @@ -0,0 +1 @@ +var webVitals=function(e){"use strict";var n,t,r,i,o,a=-1,c=function(e){addEventListener("pageshow",(function(n){n.persisted&&(a=n.timeStamp,e(n))}),!0)},u=function(){return window.performance&&performance.getEntriesByType&&performance.getEntriesByType("navigation")[0]},s=function(){var e=u();return e&&e.activationStart||0},f=function(e,n){var t=u(),r="navigate";return a>=0?r="back-forward-cache":t&&(r=document.prerendering||s()>0?"prerender":document.wasDiscarded?"restore":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:r}},d=function(e,n,t){try{if(PerformanceObserver.supportedEntryTypes.includes(e)){var r=new PerformanceObserver((function(e){Promise.resolve().then((function(){n(e.getEntries())}))}));return r.observe(Object.assign({type:e,buffered:!0},t||{})),r}}catch(e){}},l=function(e,n,t,r){var i,o;return function(a){n.value>=0&&(a||r)&&((o=n.value-(i||0))||void 0===i)&&(i=n.value,n.delta=o,n.rating=function(e,n){return e>n[1]?"poor":e>n[0]?"needs-improvement":"good"}(n.value,t),e(n))}},v=function(e){requestAnimationFrame((function(){return requestAnimationFrame((function(){return e()}))}))},p=function(e){var n=function(n){"pagehide"!==n.type&&"hidden"!==document.visibilityState||e(n)};addEventListener("visibilitychange",n,!0),addEventListener("pagehide",n,!0)},m=function(e){var n=!1;return function(t){n||(e(t),n=!0)}},h=-1,g=function(){return"hidden"!==document.visibilityState||document.prerendering?1/0:0},T=function(e){"hidden"===document.visibilityState&&h>-1&&(h="visibilitychange"===e.type?e.timeStamp:0,C())},y=function(){addEventListener("visibilitychange",T,!0),addEventListener("prerenderingchange",T,!0)},C=function(){removeEventListener("visibilitychange",T,!0),removeEventListener("prerenderingchange",T,!0)},E=function(){return h<0&&(h=g(),y(),c((function(){setTimeout((function(){h=g(),y()}),0)}))),{get firstHiddenTime(){return h}}},L=function(e){document.prerendering?addEventListener("prerenderingchange",(function(){return e()}),!0):e()},b=[1800,3e3],S=function(e,n){n=n||{},L((function(){var t,r=E(),i=f("FCP"),o=d("paint",(function(e){e.forEach((function(e){"first-contentful-paint"===e.name&&(o.disconnect(),e.startTimer.value&&(r.value=i,r.entries=o,t())},u=d("layout-shift",a);u&&(t=l(e,r,w,n.reportAllChanges),p((function(){a(u.takeRecords()),t(!0)})),c((function(){i=0,r=f("CLS",0),t=l(e,r,w,n.reportAllChanges),v((function(){return t()}))})),setTimeout(t,0))})))},F={passive:!0,capture:!0},I=new Date,A=function(e,i){n||(n=i,t=e,r=new Date,k(removeEventListener),M())},M=function(){if(t>=0&&t1e12?new Date:performance.now())-e.timeStamp;"pointerdown"==e.type?function(e,n){var t=function(){A(e,n),i()},r=function(){i()},i=function(){removeEventListener("pointerup",t,F),removeEventListener("pointercancel",r,F)};addEventListener("pointerup",t,F),addEventListener("pointercancel",r,F)}(n,e):A(n,e)}},k=function(e){["mousedown","keydown","touchstart","pointerdown"].forEach((function(n){return e(n,D,F)}))},B=[100,300],x=function(e,r){r=r||{},L((function(){var o,a=E(),u=f("FID"),s=function(e){e.startTimen.latency){if(t)t.entries.push(e),t.latency=Math.max(t.latency,e.duration);else{var r={id:e.interactionId,latency:e.duration,entries:[e]};J[r.id]=r,G.push(r)}G.sort((function(e,n){return n.latency-e.latency})),G.splice(10).forEach((function(e){delete J[e.id]}))}},Q=function(e,n){n=n||{},L((function(){j();var t,r=f("INP"),i=function(e){e.forEach((function(e){(e.interactionId&&K(e),"first-input"===e.entryType)&&(!G.some((function(n){return n.entries.some((function(n){return e.duration===n.duration&&e.startTime===n.startTime}))}))&&K(e))}));var n,i=(n=Math.min(G.length-1,Math.floor(z()/50)),G[n]);i&&i.latency!==r.value&&(r.value=i.latency,r.entries=i.entries,t())},o=d("event",i,{durationThreshold:n.durationThreshold||40});t=l(e,r,q,n.reportAllChanges),o&&(o.observe({type:"first-input",buffered:!0}),p((function(){i(o.takeRecords()),r.value<0&&z()>0&&(r.value=0,r.entries=[]),t(!0)})),c((function(){G=[],V=_(),r=f("INP"),t=l(e,r,q,n.reportAllChanges)})))}))},U=[2500,4e3],W={},X=function(e,n){n=n||{},L((function(){var t,r=E(),i=f("LCP"),o=function(e){var n=e[e.length-1];n&&n.startTimeperformance.now())return;t.value=Math.max(o-s(),0),t.entries=[i],r(!0),c((function(){t=f("TTFB",0),(r=l(e,t,Y,n.reportAllChanges))(!0)}))}}))};return e.CLSThresholds=w,e.FCPThresholds=b,e.FIDThresholds=B,e.INPThresholds=q,e.LCPThresholds=U,e.TTFBThresholds=Y,e.getCLS=P,e.getFCP=S,e.getFID=x,e.getINP=Q,e.getLCP=X,e.getTTFB=$,e.onCLS=P,e.onFCP=S,e.onFID=x,e.onINP=Q,e.onLCP=X,e.onTTFB=$,Object.defineProperty(e,"__esModule",{value:!0}),e}({}); diff --git a/collectors/web-vitals.php b/collectors/web-vitals.php new file mode 100644 index 000000000..2a03f88a3 --- /dev/null +++ b/collectors/web-vitals.php @@ -0,0 +1,41 @@ + + */ +class QM_Collector_Web_Vitals extends QM_DataCollector { + + /** + * @var string + */ + public $id = 'web-vitals'; + + /** + * @var bool + */ + protected static $hide_core; + + public function get_storage(): QM_Data { + return new QM_Data_Hooks(); + } + + /** + * @return void + */ + public function process() { + + } + +} + +# Load early to catch all hooks +QM_Collectors::add( new QM_Collector_Web_Vitals() ); diff --git a/data/web_vitals.php b/data/web_vitals.php new file mode 100644 index 000000000..5637dbe24 --- /dev/null +++ b/data/web_vitals.php @@ -0,0 +1,37 @@ +> + * @phpstan-var list, + * }>, + * parts: list, + * components: array, + * }> + */ + public $web_vitals; + + /** + * @var array + */ + public $parts; + + /** + * @var array + */ + public $components; + + /** + * @var bool + */ + public $all_web_vitals; +} diff --git a/dispatchers/Html.php b/dispatchers/Html.php index 06cd4c85b..cc4ac0ae0 100644 --- a/dispatchers/Html.php +++ b/dispatchers/Html.php @@ -233,6 +233,15 @@ public function enqueue_assets() { $deps = array(); } + // Measure Web Vitals with the web-vitals.js script. See https://github.com/GoogleChrome/web-vitals. + wp_enqueue_script( + 'web-vitals', + $this->qm->plugin_url( 'assets/web-vitals.js' ), + array(), + QM_VERSION + ); + + wp_enqueue_style( 'query-monitor', $this->qm->plugin_url( 'assets/query-monitor.css' ), diff --git a/output/html/web_vitals.php b/output/html/web_vitals.php new file mode 100644 index 000000000..595632432 --- /dev/null +++ b/output/html/web_vitals.php @@ -0,0 +1,86 @@ +before_tabular_output(); + echo ''; + echo ''; + echo ''; + echo '' . esc_html__( 'Metric', 'query-monitor' ) . ''; + echo '' . esc_html__( 'Score', 'query-monitor' ) . ''; + echo '' . esc_html__( 'Rating', 'query-monitor' ) . ''; + echo ''; + echo ''; + echo ''; + + echo ''; + echo ''; + echo ''; + echo ''; + + $this->after_tabular_output(); + } + + +} + +/** + * @param array $output + * @param QM_Collectors $collectors + * @return array + */ +function register_qm_output_web_vitals( array $output, QM_Collectors $collectors ) { + $collector = QM_Collectors::get( 'web-vitals' ); + if ( $collector ) { + $output['web-vitals'] = new QM_Output_Web_Vitals_Hooks( $collector ); + } + return $output; +} + +add_filter( 'qm/outputter/html', 'register_qm_output_web_vitals', 80, 2 ); From 144a40b55072be95e3955af977e9dc8cb2c2696a Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 11 May 2023 13:33:41 -0600 Subject: [PATCH 02/12] Clean up web-vitals data/collector --- collectors/web-vitals.php | 7 +++---- data/web_vitals.php | 11 +---------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/collectors/web-vitals.php b/collectors/web-vitals.php index 2a03f88a3..686e99ecf 100644 --- a/collectors/web-vitals.php +++ b/collectors/web-vitals.php @@ -1,6 +1,6 @@ + * @extends QM_DataCollector */ class QM_Collector_Web_Vitals extends QM_DataCollector { @@ -25,7 +25,7 @@ class QM_Collector_Web_Vitals extends QM_DataCollector { protected static $hide_core; public function get_storage(): QM_Data { - return new QM_Data_Hooks(); + return new QM_Data_Web_Vitals(); } /** @@ -37,5 +37,4 @@ public function process() { } -# Load early to catch all hooks QM_Collectors::add( new QM_Collector_Web_Vitals() ); diff --git a/data/web_vitals.php b/data/web_vitals.php index 5637dbe24..3b6e5de7d 100644 --- a/data/web_vitals.php +++ b/data/web_vitals.php @@ -7,16 +7,7 @@ class QM_Data_Web_Vitals extends QM_Data { /** - * @var array> - * @phpstan-var list, - * }>, - * parts: list, - * components: array, - * }> + * Not used, data is generated for each load and not stored. */ public $web_vitals; From 347f2a4bff8fad34ef8c687617e2a65db2157f28 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Thu, 11 May 2023 13:34:24 -0600 Subject: [PATCH 03/12] Improve output, include all available metrics --- output/html/web_vitals.php | 44 ++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/output/html/web_vitals.php b/output/html/web_vitals.php index 595632432..f3241a38b 100644 --- a/output/html/web_vitals.php +++ b/output/html/web_vitals.php @@ -9,7 +9,7 @@ exit; } -class QM_Output_Web_Vitals_Hooks extends QM_Output_Html { +class QM_Output_Web_Vitals extends QM_Output_Html { /** * Collector instance. @@ -36,35 +36,41 @@ public function name() { public function output() { $this->before_tabular_output(); echo ''; - echo ''; echo ''; echo '' . esc_html__( 'Metric', 'query-monitor' ) . ''; echo '' . esc_html__( 'Score', 'query-monitor' ) . ''; echo '' . esc_html__( 'Rating', 'query-monitor' ) . ''; + echo '' . esc_html__( 'Details', 'query-monitor' ) . ''; echo ''; echo ''; echo ''; - echo ''; - echo ''; - echo ''; + echo ''; echo ''; $this->after_tabular_output(); + echo ''; } @@ -78,7 +84,7 @@ function insertWebVital( webVital ) { function register_qm_output_web_vitals( array $output, QM_Collectors $collectors ) { $collector = QM_Collectors::get( 'web-vitals' ); if ( $collector ) { - $output['web-vitals'] = new QM_Output_Web_Vitals_Hooks( $collector ); + $output['web-vitals'] = new QM_Output_Web_Vitals( $collector ); } return $output; } From 4ada665c48dfdfd0d2017ae347b4f89cb6582ab7 Mon Sep 17 00:00:00 2001 From: adamsilverstein Date: Thu, 11 May 2023 20:06:56 -0600 Subject: [PATCH 04/12] remove extra empty line --- dispatchers/Html.php | 1 - 1 file changed, 1 deletion(-) diff --git a/dispatchers/Html.php b/dispatchers/Html.php index cc4ac0ae0..fb9c82284 100644 --- a/dispatchers/Html.php +++ b/dispatchers/Html.php @@ -241,7 +241,6 @@ public function enqueue_assets() { QM_VERSION ); - wp_enqueue_style( 'query-monitor', $this->qm->plugin_url( 'assets/query-monitor.css' ), From 0aaf29943350b9e6a6ec18dd80c1163590699428 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 3 Jul 2023 13:29:07 -0600 Subject: [PATCH 05/12] Only enqueue web-vitals.js if admin bar visible --- dispatchers/Html.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/dispatchers/Html.php b/dispatchers/Html.php index fb9c82284..e6d8dd04a 100644 --- a/dispatchers/Html.php +++ b/dispatchers/Html.php @@ -234,12 +234,15 @@ public function enqueue_assets() { } // Measure Web Vitals with the web-vitals.js script. See https://github.com/GoogleChrome/web-vitals. - wp_enqueue_script( - 'web-vitals', - $this->qm->plugin_url( 'assets/web-vitals.js' ), - array(), - QM_VERSION - ); + // Only load if the wp-admin bar is visible. + if ( is_admin_bar_showing() ) { + wp_enqueue_script( + 'web-vitals', + $this->qm->plugin_url( 'assets/web-vitals.js' ), + array(), + QM_VERSION + ); + } wp_enqueue_style( 'query-monitor', From 641818b2688e9d960b2375e8d4afd42b23c92c19 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Mon, 3 Jul 2023 13:38:45 -0600 Subject: [PATCH 06/12] Move enqueue to the footer --- dispatchers/Html.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dispatchers/Html.php b/dispatchers/Html.php index e6d8dd04a..6c82b4bfd 100644 --- a/dispatchers/Html.php +++ b/dispatchers/Html.php @@ -240,7 +240,8 @@ public function enqueue_assets() { 'web-vitals', $this->qm->plugin_url( 'assets/web-vitals.js' ), array(), - QM_VERSION + QM_VERSION, + true ); } From 65cc438dfbd1b55dea24e75615515637e44d4e81 Mon Sep 17 00:00:00 2001 From: Caleb Stauffer Date: Wed, 19 Jul 2023 13:37:34 -0400 Subject: [PATCH 07/12] add waiting notice and support missing script --- output/html/web_vitals.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/output/html/web_vitals.php b/output/html/web_vitals.php index f3241a38b..c8443dd69 100644 --- a/output/html/web_vitals.php +++ b/output/html/web_vitals.php @@ -34,6 +34,15 @@ public function name() { * @return void */ public function output() { + if ( ! wp_script_is( 'web-vitals', 'enqueued' ) && ! wp_script_is( 'web-vitals', 'done' ) ) { + $this->before_non_tabular_output(); + /* translators: %s: Script handle. */ + $notice = sprintf( __( 'Script %s is not available.' ), 'query-monitor' ), 'web-vitals' ); + echo $this->build_notice( $notice ); + $this->after_non_tabular_output(); + return; + } + $this->before_tabular_output(); echo ''; @@ -47,19 +56,24 @@ public function output() { echo ''; echo ''; + echo ''; + echo ''; + echo '

Waiting for data...

'; + echo ''; + echo ''; echo ''; $this->after_tabular_output(); echo '