From fc95f4a6ccc088acfdd58a4170e6ea36256b99b0 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 18 Dec 2024 21:10:09 +0100 Subject: [PATCH 01/41] Bundle web-vitals attribution build --- webpack.config.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/webpack.config.js b/webpack.config.js index faaaa32677..21f6a43662 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -190,6 +190,11 @@ const optimizationDetective = ( env ) => { to: `${ destination }/build/web-vitals.js`, info: { minimized: true }, }, + { + from: `${ source }/dist/web-vitals.attribution.js`, + to: `${ destination }/build/web-vitals-attribution.js`, + info: { minimized: true }, + }, { from: `${ source }/package.json`, to: `${ destination }/build/web-vitals.asset.php`, From 651577e871887483b332925271eaa6032daf1080 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 18 Dec 2024 21:10:29 +0100 Subject: [PATCH 02/41] Allow loading web-vitals attribution build --- plugins/optimization-detective/detection.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/plugins/optimization-detective/detection.php b/plugins/optimization-detective/detection.php index 2fa2a6dee7..13ba6bcdac 100644 --- a/plugins/optimization-detective/detection.php +++ b/plugins/optimization-detective/detection.php @@ -70,8 +70,22 @@ function od_get_cache_purge_post_id(): ?int { * @param OD_URL_Metric_Group_Collection $group_collection URL Metric group collection. */ function od_get_detection_script( string $slug, OD_URL_Metric_Group_Collection $group_collection ): string { + $use_attribution_build = WP_DEBUG || wp_is_development_mode( 'plugin' ); + + /** + * Filters whether to use the web-vitals.js build with attribution. + * + * @since n.e.x.t + * + * @param bool $use_attribution_build Whether to use the attribution build. + */ + $use_attribution_build = (bool) apply_filters( 'od_use_web_vitals_attribution_build', $use_attribution_build ); + $web_vitals_lib_data = require __DIR__ . '/build/web-vitals.asset.php'; - $web_vitals_lib_src = add_query_arg( 'ver', $web_vitals_lib_data['version'], plugin_dir_url( __FILE__ ) . 'build/web-vitals.js' ); + $web_vitals_lib_src = $use_attribution_build ? + plugin_dir_url( __FILE__ ) . 'build/web-vitals-attribution.js' : + plugin_dir_url( __FILE__ ) . 'build/web-vitals.js'; + $web_vitals_lib_src = add_query_arg( 'ver', $web_vitals_lib_data['version'], $web_vitals_lib_src ); /** * Filters the list of extension script module URLs to import when performing detection. From ff3f1202f65c56a2f0865c6a76ee7960f49ef8eb Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 18 Dec 2024 21:11:09 +0100 Subject: [PATCH 03/41] Collect some INP data for testing --- plugins/optimization-detective/detect.js | 34 ++++++++++++++++++++++-- plugins/optimization-detective/types.ts | 7 +++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index ded7ff898b..2dad8fceb8 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -1,5 +1,8 @@ /** * @typedef {import("web-vitals").LCPMetric} LCPMetric + * @typedef {import("web-vitals").LCPMetricWithAttribution} LCPMetricWithAttribution + * @typedef {import("web-vitals").INPMetric} INPMetric + * @typedef {import("web-vitals").INPMetricWithAttribution} INPMetricWithAttribution * @typedef {import("./types.ts").ElementData} ElementData * @typedef {import("./types.ts").OnTTFBFunction} OnTTFBFunction * @typedef {import("./types.ts").OnFCPFunction} OnFCPFunction @@ -490,13 +493,17 @@ export default async function detect( { } ); } - /** @type {LCPMetric[]} */ + /** @type {(LCPMetric|LCPMetricWithAttribution)[]} */ const lcpMetricCandidates = []; // Obtain at least one LCP candidate. More may be reported before the page finishes loading. await new Promise( ( resolve ) => { onLCP( - ( /** @type LCPMetric */ metric ) => { + /** + * + * @param {LCPMetric|LCPMetricWithAttribution} metric + */ + ( metric ) => { lcpMetricCandidates.push( metric ); resolve(); }, @@ -511,6 +518,26 @@ export default async function detect( { // Stop observing. disconnectIntersectionObserver(); + + const inpData = []; + + onINP( + /** + * + * @param {INPMetric|INPMetricWithAttribution} metric + */ + ( metric ) => { + if ( 'attribution' in metric ) { + // TODO: Store xpath instead? + inpData.push( { + value: metric.value, + rating: metric.rating, + interactionTarget: metric.attribution.interactionTarget, + } ); + } + } + ); + if ( isDebug ) { log( 'Detection is stopping.' ); } @@ -522,6 +549,7 @@ export default async function detect( { height: win.innerHeight, }, elements: [], + inpData: [], }; const lcpMetric = lcpMetricCandidates.at( -1 ); @@ -581,6 +609,8 @@ export default async function detect( { ); } ); + urlMetric.inpData = inpData; + // Only proceed with submitting the URL Metric if viewport stayed the same size. Changing the viewport size (e.g. due // to resizing a window or changing the orientation of a device) will result in unexpected metrics being collected. if ( didWindowResize ) { diff --git a/plugins/optimization-detective/types.ts b/plugins/optimization-detective/types.ts index d92c532143..d87d443ca5 100644 --- a/plugins/optimization-detective/types.ts +++ b/plugins/optimization-detective/types.ts @@ -14,6 +14,12 @@ export interface ElementData { export type ExtendedElementData = ExcludeProps< ElementData >; +export interface INPData { + value: number; + rating: string; + interactionTarget: string; +} + export interface URLMetric { url: string; viewport: { @@ -21,6 +27,7 @@ export interface URLMetric { height: number; }; elements: ElementData[]; + inpData: INPData[]; } export type ExtendedRootData = ExcludeProps< URLMetric >; From 9dd8159f87d27e279e72383dd830f95efbcfeb22 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 18 Dec 2024 21:11:38 +0100 Subject: [PATCH 04/41] Add debugging helper for LCP elements --- ...timization-detective-debug-tag-visitor.php | 99 ++++++++++++ plugins/optimization-detective/debug.php | 150 ++++++++++++++++++ plugins/optimization-detective/load.php | 4 + 3 files changed, 253 insertions(+) create mode 100644 plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php create mode 100644 plugins/optimization-detective/debug.php diff --git a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php new file mode 100644 index 0000000000..455e044a4b --- /dev/null +++ b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php @@ -0,0 +1,99 @@ +processor; + + if ( ! $context->url_metric_group_collection->is_any_group_populated() ) { + return false; + } + + $xpath = $processor->get_xpath(); + + $visited = false; + + /** + * @var OD_URL_Metric_Group $group + */ + foreach ( $context->url_metric_group_collection as $group ) { + // This is the LCP element for this group. + if ( $group->get_lcp_element() instanceof OD_Element && $xpath === $group->get_lcp_element()->get_xpath() ) { + $uuid = wp_generate_uuid4(); + + $processor->set_meta_attribute( + 'viewport', + $group->get_minimum_viewport_width() + ); + + $processor->set_attribute( + 'style', + "--anchor-name: --od-debug-element-$uuid;" . $processor->get_attribute( 'style' ) ?? '' + ); + + $processor->set_meta_attribute( + 'debug-is-lcp', + true + ); + + $anchor_text = __( 'Optimization Detective', 'optimization-detective' ); + $popover_text = __( 'LCP Element', 'optimization-detective' ); + + $processor->append_body_html( + << + $anchor_text + +
+ $popover_text +
+HTML + + ); + + $visited = true; + } + } + + return $visited; + } +} diff --git a/plugins/optimization-detective/debug.php b/plugins/optimization-detective/debug.php new file mode 100644 index 0000000000..d7cb1ac1cc --- /dev/null +++ b/plugins/optimization-detective/debug.php @@ -0,0 +1,150 @@ +register( 'optimization-detective/debug', $debug_visitor ); +} + +add_action( 'od_register_tag_visitors', 'od_debug_register_tag_visitors', PHP_INT_MAX ); + + +/** + * Filters additional properties for the element item schema for Optimization Detective. + * + * @since n.e.x.t + * + * @param array $additional_properties Additional properties. + * @return array Additional properties. + */ +function od_debug_add_inp_schema_properties( array $additional_properties ): array { + $additional_properties['inpData'] = array( + 'description' => __( 'INP metrics', 'optimization-detective' ), + 'type' => 'array', + 'required' => true, + 'items' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array( + 'value' => array( + 'type' => 'number', + 'required' => true, + ), + 'rating' => array( + 'type' => 'string', + 'enum' => array( 'good', 'needs-improvement', 'poor' ), + 'required' => true, + ), + 'interactionTarget' => array( + 'type' => 'string', + 'required' => true, + ), + ), + ), + ); + return $additional_properties; +} + +add_filter( 'od_url_metric_schema_root_additional_properties', 'od_debug_add_inp_schema_properties' ); + +/** + * Adds a new admin bar menu item for Optimization Detective debug mode. + * + * @since n.e.x.t + * + * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance, passed by reference. + */ +function od_debug_add_admin_bar_menu_item( WP_Admin_Bar &$wp_admin_bar ) { + if ( ! current_user_can( 'customize' ) && ! wp_is_development_mode( 'plugin' ) ) { + return; + } + + if ( is_admin() ) { + return; + } + + $wp_admin_bar->add_menu( array( + 'id' => 'optimization-detective-debug', + 'parent' => null, + 'group' => null, + 'title' => __( 'Optimization Detective', 'optimization-detective' ), + 'meta' => array( + 'onclick' => 'document.body.classList.toggle("od-debug");', + ) + ) ); +} + +add_action( 'admin_bar_menu', 'od_debug_add_admin_bar_menu_item', 100 ); + +/** + * Adds inline JS & CSS for debugging. + */ +function od_debug_add_assets() { + if ( ! od_can_optimize_response() ) { + return; + } + ?> + + + Date: Wed, 18 Dec 2024 21:50:48 +0100 Subject: [PATCH 05/41] some lint fixes --- ...timization-detective-debug-tag-visitor.php | 1 - plugins/optimization-detective/debug.php | 43 ++++++++++--------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php index 455e044a4b..c30ecdf326 100644 --- a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php +++ b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php @@ -87,7 +87,6 @@ class="od-debug-popover" $popover_text HTML - ); $visited = true; diff --git a/plugins/optimization-detective/debug.php b/plugins/optimization-detective/debug.php index d7cb1ac1cc..d19235644b 100644 --- a/plugins/optimization-detective/debug.php +++ b/plugins/optimization-detective/debug.php @@ -37,16 +37,17 @@ function od_debug_add_inp_schema_properties( array $additional_properties ): arr $additional_properties['inpData'] = array( 'description' => __( 'INP metrics', 'optimization-detective' ), 'type' => 'array', - 'required' => true, + // All extended properties must be optional so that URL Metrics are not all immediately invalidated once an extension is deactivated. + 'required' => false, 'items' => array( - 'type' => 'object', - 'required' => true, + 'type' => 'object', + 'required' => true, 'properties' => array( - 'value' => array( + 'value' => array( 'type' => 'number', 'required' => true, ), - 'rating' => array( + 'rating' => array( 'type' => 'string', 'enum' => array( 'good', 'needs-improvement', 'poor' ), 'required' => true, @@ -70,7 +71,7 @@ function od_debug_add_inp_schema_properties( array $additional_properties ): arr * * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance, passed by reference. */ -function od_debug_add_admin_bar_menu_item( WP_Admin_Bar &$wp_admin_bar ) { +function od_debug_add_admin_bar_menu_item( WP_Admin_Bar &$wp_admin_bar ): void { if ( ! current_user_can( 'customize' ) && ! wp_is_development_mode( 'plugin' ) ) { return; } @@ -79,15 +80,17 @@ function od_debug_add_admin_bar_menu_item( WP_Admin_Bar &$wp_admin_bar ) { return; } - $wp_admin_bar->add_menu( array( - 'id' => 'optimization-detective-debug', - 'parent' => null, - 'group' => null, - 'title' => __( 'Optimization Detective', 'optimization-detective' ), - 'meta' => array( - 'onclick' => 'document.body.classList.toggle("od-debug");', + $wp_admin_bar->add_menu( + array( + 'id' => 'optimization-detective-debug', + 'parent' => null, + 'group' => null, + 'title' => __( 'Optimization Detective', 'optimization-detective' ), + 'meta' => array( + 'onclick' => 'document.body.classList.toggle("od-debug");', + ), ) - ) ); + ); } add_action( 'admin_bar_menu', 'od_debug_add_admin_bar_menu_item', 100 ); @@ -95,11 +98,11 @@ function od_debug_add_admin_bar_menu_item( WP_Admin_Bar &$wp_admin_bar ) { /** * Adds inline JS & CSS for debugging. */ -function od_debug_add_assets() { - if ( ! od_can_optimize_response() ) { - return; - } - ?> +function od_debug_add_assets(): void { + if ( ! od_can_optimize_response() ) { + return; + } + ?> @@ -147,4 +150,4 @@ function od_debug_add_assets() { Date: Wed, 18 Dec 2024 22:42:03 +0100 Subject: [PATCH 06/41] Add INP elements with JS --- plugins/optimization-detective/debug.php | 70 +++++++++++++++++++++++- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/plugins/optimization-detective/debug.php b/plugins/optimization-detective/debug.php index d19235644b..39d6866be3 100644 --- a/plugins/optimization-detective/debug.php +++ b/plugins/optimization-detective/debug.php @@ -102,9 +102,73 @@ function od_debug_add_assets(): void { if ( ! od_can_optimize_response() ) { return; } + + $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); + $post = OD_URL_Metrics_Post_Type::get_post( $slug ); + + global $wp_the_query; + + $tag_visitor_registry = new OD_Tag_Visitor_Registry(); + + $current_etag = od_get_current_url_metrics_etag( $tag_visitor_registry, $wp_the_query, od_get_current_theme_template() ); + $group_collection = new OD_URL_Metric_Group_Collection( + $post instanceof WP_Post ? OD_URL_Metrics_Post_Type::get_url_metrics_from_post( $post ) : array(), + $current_etag, + od_get_breakpoint_max_widths(), + od_get_url_metrics_breakpoint_sample_size(), + od_get_url_metric_freshness_ttl() + ); + + $inp_dots = array(); + + /** + * @var OD_URL_Metric_Group $group + */ + foreach ( $group_collection as $group ) { + /** + * @var OD_URL_Metric $url_metric + */ + foreach ( $group as $url_metric ) { + foreach ( $url_metric->get( 'inpData' ) as $inp_data ) { + if ( isset( $inp_dots[ $inp_data['interactionTarget'] ] ) ) { + $inp_dots[ $inp_data['interactionTarget'] ][] = $inp_data; + } else { + $inp_dots[ $inp_data['interactionTarget'] ] = array( $inp_data ); + } + } + } + } + ?> From e9bbe99efc9226c299731d95348cf8d39bd06c67 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 19 Dec 2024 15:41:35 +0100 Subject: [PATCH 07/41] Apply suggestions from code review Co-authored-by: Weston Ruter --- .../class-optimization-detective-debug-tag-visitor.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php index c30ecdf326..0832aa5548 100644 --- a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php +++ b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php @@ -27,7 +27,7 @@ final class Optimization_Detective_Debug_Tag_Visitor { * @since n.e.x.t * * @param OD_Tag_Visitor_Context $context Tag visitor context. - * @return bool Whether the tag should be tracked in URL Metrics. + * @return false This tag visitor doesn't itself request elements to be tracked in URL Metrics, but will reuse tracking that other tag visitors have opted-in to. */ public function __invoke( OD_Tag_Visitor_Context $context ): bool { $processor = $context->processor; @@ -38,8 +38,6 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { $xpath = $processor->get_xpath(); - $visited = false; - /** * @var OD_URL_Metric_Group $group */ @@ -88,11 +86,7 @@ class="od-debug-popover" HTML ); - - $visited = true; } } - - return $visited; } } From ac4d1531e23a55c77f26185e08cbca7dcaad330a Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 19 Dec 2024 15:46:08 +0100 Subject: [PATCH 08/41] Add missing return statement --- .../class-optimization-detective-debug-tag-visitor.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php index 0832aa5548..1439426548 100644 --- a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php +++ b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php @@ -24,10 +24,12 @@ final class Optimization_Detective_Debug_Tag_Visitor { /** * Visits a tag. * + * This tag visitor doesn't itself request elements to be tracked in URL Metrics, but will reuse tracking that other tag visitors have opted-in to. + * * @since n.e.x.t * * @param OD_Tag_Visitor_Context $context Tag visitor context. - * @return false This tag visitor doesn't itself request elements to be tracked in URL Metrics, but will reuse tracking that other tag visitors have opted-in to. + * @return false Always returns false. */ public function __invoke( OD_Tag_Visitor_Context $context ): bool { $processor = $context->processor; @@ -88,5 +90,7 @@ class="od-debug-popover" ); } } + + return false; } } From 098191c780ff98df45ca83e06ae1a7a180d69413 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 19 Dec 2024 15:49:25 +0100 Subject: [PATCH 09/41] Lint fixes --- .../class-optimization-detective-debug-tag-visitor.php | 3 --- plugins/optimization-detective/debug.php | 6 ------ 2 files changed, 9 deletions(-) diff --git a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php index 1439426548..a86b1edff4 100644 --- a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php +++ b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php @@ -40,9 +40,6 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { $xpath = $processor->get_xpath(); - /** - * @var OD_URL_Metric_Group $group - */ foreach ( $context->url_metric_group_collection as $group ) { // This is the LCP element for this group. if ( $group->get_lcp_element() instanceof OD_Element && $xpath === $group->get_lcp_element()->get_xpath() ) { diff --git a/plugins/optimization-detective/debug.php b/plugins/optimization-detective/debug.php index 39d6866be3..05fd4c36a0 100644 --- a/plugins/optimization-detective/debug.php +++ b/plugins/optimization-detective/debug.php @@ -121,13 +121,7 @@ function od_debug_add_assets(): void { $inp_dots = array(); - /** - * @var OD_URL_Metric_Group $group - */ foreach ( $group_collection as $group ) { - /** - * @var OD_URL_Metric $url_metric - */ foreach ( $group as $url_metric ) { foreach ( $url_metric->get( 'inpData' ) as $inp_data ) { if ( isset( $inp_dots[ $inp_data['interactionTarget'] ] ) ) { From 9288025b3fb89dd5118a848424e4fcc84d4ba03c Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 19 Dec 2024 15:58:16 +0100 Subject: [PATCH 10/41] Clarify comment Co-authored-by: Weston Ruter --- plugins/optimization-detective/debug.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/optimization-detective/debug.php b/plugins/optimization-detective/debug.php index 05fd4c36a0..bf2b8dfde9 100644 --- a/plugins/optimization-detective/debug.php +++ b/plugins/optimization-detective/debug.php @@ -37,7 +37,8 @@ function od_debug_add_inp_schema_properties( array $additional_properties ): arr $additional_properties['inpData'] = array( 'description' => __( 'INP metrics', 'optimization-detective' ), 'type' => 'array', - // All extended properties must be optional so that URL Metrics are not all immediately invalidated once an extension is deactivated. + // All extended properties must be optional so that URL Metrics are not all immediately invalidated once an extension is deactivated. + // Also, no INP data will be sent if the user never interacted with the page. 'required' => false, 'items' => array( 'type' => 'object', From 2f507a0eec09c09f8218b2795cdc7945d1f39e34 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 19 Dec 2024 16:04:07 +0100 Subject: [PATCH 11/41] PHPStan fixes --- .../class-optimization-detective-debug-tag-visitor.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php index a86b1edff4..d47a60b3ec 100644 --- a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php +++ b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php @@ -47,12 +47,14 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { $processor->set_meta_attribute( 'viewport', - $group->get_minimum_viewport_width() + (string) $group->get_minimum_viewport_width() ); + $style = $processor->get_attribute( 'style' ); + $style = is_string( $style ) ? $style : ''; $processor->set_attribute( 'style', - "--anchor-name: --od-debug-element-$uuid;" . $processor->get_attribute( 'style' ) ?? '' + "--anchor-name: --od-debug-element-$uuid;" . $style ); $processor->set_meta_attribute( From 8ead9ae5803a8e2e49dbedde329b672bfaa76d40 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 19 Dec 2024 16:12:13 +0100 Subject: [PATCH 12/41] Multiline comment --- plugins/optimization-detective/debug.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/debug.php b/plugins/optimization-detective/debug.php index bf2b8dfde9..06c0595774 100644 --- a/plugins/optimization-detective/debug.php +++ b/plugins/optimization-detective/debug.php @@ -37,8 +37,10 @@ function od_debug_add_inp_schema_properties( array $additional_properties ): arr $additional_properties['inpData'] = array( 'description' => __( 'INP metrics', 'optimization-detective' ), 'type' => 'array', - // All extended properties must be optional so that URL Metrics are not all immediately invalidated once an extension is deactivated. - // Also, no INP data will be sent if the user never interacted with the page. + /* + * All extended properties must be optional so that URL Metrics are not all immediately invalidated once an extension is deactivated. + * Also, no INP data will be sent if the user never interacted with the page. + */ 'required' => false, 'items' => array( 'type' => 'object', From 3e4d648740fc2e9ead091e300c0d49df2441c0b3 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 19 Dec 2024 16:21:57 +0100 Subject: [PATCH 13/41] Add empty line --- plugins/optimization-detective/debug.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/optimization-detective/debug.php b/plugins/optimization-detective/debug.php index 06c0595774..d04cdc1d1c 100644 --- a/plugins/optimization-detective/debug.php +++ b/plugins/optimization-detective/debug.php @@ -37,6 +37,7 @@ function od_debug_add_inp_schema_properties( array $additional_properties ): arr $additional_properties['inpData'] = array( 'description' => __( 'INP metrics', 'optimization-detective' ), 'type' => 'array', + /* * All extended properties must be optional so that URL Metrics are not all immediately invalidated once an extension is deactivated. * Also, no INP data will be sent if the user never interacted with the page. From 36430ad3e9c51b41eb9d2b56699d0a5ab0e7cc0c Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 10 Jan 2025 14:42:54 +0100 Subject: [PATCH 14/41] Only load assets if admin bar is showing --- plugins/optimization-detective/debug.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/optimization-detective/debug.php b/plugins/optimization-detective/debug.php index d04cdc1d1c..8cf9708de8 100644 --- a/plugins/optimization-detective/debug.php +++ b/plugins/optimization-detective/debug.php @@ -80,6 +80,10 @@ function od_debug_add_admin_bar_menu_item( WP_Admin_Bar &$wp_admin_bar ): void { return; } + if ( ! is_admin_bar_showing() ) { + return; + } + if ( is_admin() ) { return; } @@ -103,7 +107,7 @@ function od_debug_add_admin_bar_menu_item( WP_Admin_Bar &$wp_admin_bar ): void { * Adds inline JS & CSS for debugging. */ function od_debug_add_assets(): void { - if ( ! od_can_optimize_response() ) { + if ( ! current_user_can( 'customize' ) && ! wp_is_development_mode( 'plugin' ) ) { return; } From a229ba6c6164b0d3bd4424f4d006252a81a02b43 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 10 Jan 2025 14:43:29 +0100 Subject: [PATCH 15/41] Extract `od_get_group_collection()` helper --- plugins/optimization-detective/debug.php | 17 +++-------- plugins/optimization-detective/helper.php | 28 +++++++++++++++++++ .../optimization-detective/optimization.php | 14 +--------- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/plugins/optimization-detective/debug.php b/plugins/optimization-detective/debug.php index 8cf9708de8..d2fe202631 100644 --- a/plugins/optimization-detective/debug.php +++ b/plugins/optimization-detective/debug.php @@ -111,21 +111,12 @@ function od_debug_add_assets(): void { return; } - $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); - $post = OD_URL_Metrics_Post_Type::get_post( $slug ); - - global $wp_the_query; - $tag_visitor_registry = new OD_Tag_Visitor_Registry(); - $current_etag = od_get_current_url_metrics_etag( $tag_visitor_registry, $wp_the_query, od_get_current_theme_template() ); - $group_collection = new OD_URL_Metric_Group_Collection( - $post instanceof WP_Post ? OD_URL_Metrics_Post_Type::get_url_metrics_from_post( $post ) : array(), - $current_etag, - od_get_breakpoint_max_widths(), - od_get_url_metrics_breakpoint_sample_size(), - od_get_url_metric_freshness_ttl() - ); + /** This action is documented in optimization.php. */ + do_action( 'od_register_tag_visitors', $tag_visitor_registry ); + + $group_collection = od_get_group_collection( $tag_visitor_registry ); $inp_dots = array(); diff --git a/plugins/optimization-detective/helper.php b/plugins/optimization-detective/helper.php index 27073205d1..8851107db2 100644 --- a/plugins/optimization-detective/helper.php +++ b/plugins/optimization-detective/helper.php @@ -107,3 +107,31 @@ function od_get_asset_path( string $src_path, ?string $min_path = null ): string return $min_path; } + +/** + * Get the group collection for the current request. + * + * @since n.e.x.t + * @access private + * + * @global WP_Query $wp_the_query WP_Query object. + * + * @param OD_Tag_Visitor_Registry $tag_visitor_registry Tag visitor registry. + * @return OD_URL_Metric_Group_Collection Group collection instance. + */ +function od_get_group_collection( OD_Tag_Visitor_Registry $tag_visitor_registry ): OD_URL_Metric_Group_Collection { + global $wp_the_query; + + $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); + $post = OD_URL_Metrics_Post_Type::get_post( $slug ); + + $current_etag = od_get_current_url_metrics_etag( $tag_visitor_registry, $wp_the_query, od_get_current_theme_template() ); + + return new OD_URL_Metric_Group_Collection( + $post instanceof WP_Post ? OD_URL_Metrics_Post_Type::get_url_metrics_from_post( $post ) : array(), + $current_etag, + od_get_breakpoint_max_widths(), + od_get_url_metrics_breakpoint_sample_size(), + od_get_url_metric_freshness_ttl() + ); +} diff --git a/plugins/optimization-detective/optimization.php b/plugins/optimization-detective/optimization.php index 40b5002a53..b0f7e0933b 100644 --- a/plugins/optimization-detective/optimization.php +++ b/plugins/optimization-detective/optimization.php @@ -170,14 +170,10 @@ function od_is_response_html_content_type(): bool { * @since 0.1.0 * @access private * - * @global WP_Query $wp_the_query WP_Query object. - * * @param string $buffer Template output buffer. * @return string Filtered template output buffer. */ function od_optimize_template_output_buffer( string $buffer ): string { - global $wp_the_query; - // If the content-type is not HTML or the output does not start with '<', then abort since the buffer is definitely not HTML. if ( ! od_is_response_html_content_type() || @@ -197,7 +193,6 @@ function od_optimize_template_output_buffer( string $buffer ): string { } $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); - $post = OD_URL_Metrics_Post_Type::get_post( $slug ); $tag_visitor_registry = new OD_Tag_Visitor_Registry(); @@ -210,14 +205,7 @@ function od_optimize_template_output_buffer( string $buffer ): string { */ do_action( 'od_register_tag_visitors', $tag_visitor_registry ); - $current_etag = od_get_current_url_metrics_etag( $tag_visitor_registry, $wp_the_query, od_get_current_theme_template() ); - $group_collection = new OD_URL_Metric_Group_Collection( - $post instanceof WP_Post ? OD_URL_Metrics_Post_Type::get_url_metrics_from_post( $post ) : array(), - $current_etag, - od_get_breakpoint_max_widths(), - od_get_url_metrics_breakpoint_sample_size(), - od_get_url_metric_freshness_ttl() - ); + $group_collection = od_get_group_collection( $tag_visitor_registry ); $link_collection = new OD_Link_Collection(); $tag_visitor_context = new OD_Tag_Visitor_Context( $processor, $group_collection, $link_collection ); $current_tag_bookmark = 'optimization_detective_current_tag'; From ba48bd939759cb3ea00c629651111c35496fa513 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 10 Jan 2025 14:47:44 +0100 Subject: [PATCH 16/41] Don't add visitor if not showing admin bar --- ...ass-optimization-detective-debug-tag-visitor.php | 6 +++--- plugins/optimization-detective/debug.php | 13 ++++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php index d47a60b3ec..6bb9836f56 100644 --- a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php +++ b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php @@ -1,8 +1,8 @@ register( 'optimization-detective/debug', $debug_visitor ); } add_action( 'od_register_tag_visitors', 'od_debug_register_tag_visitors', PHP_INT_MAX ); - /** * Filters additional properties for the element item schema for Optimization Detective. * @@ -111,6 +118,10 @@ function od_debug_add_assets(): void { return; } + if ( ! is_admin_bar_showing() ) { + return; + } + $tag_visitor_registry = new OD_Tag_Visitor_Registry(); /** This action is documented in optimization.php. */ From 581ffbc5a91a5ec21b029684e368c4177a0632d1 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 10 Jan 2025 15:00:18 +0100 Subject: [PATCH 17/41] Add `additionalProperties` --- plugins/optimization-detective/debug.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/optimization-detective/debug.php b/plugins/optimization-detective/debug.php index a56da98e9e..dc594b2f5f 100644 --- a/plugins/optimization-detective/debug.php +++ b/plugins/optimization-detective/debug.php @@ -51,9 +51,9 @@ function od_debug_add_inp_schema_properties( array $additional_properties ): arr */ 'required' => false, 'items' => array( - 'type' => 'object', - 'required' => true, - 'properties' => array( + 'type' => 'object', + 'required' => true, + 'properties' => array( 'value' => array( 'type' => 'number', 'required' => true, @@ -68,6 +68,7 @@ function od_debug_add_inp_schema_properties( array $additional_properties ): arr 'required' => true, ), ), + 'additionalProperties' => true, ), ); return $additional_properties; From eabd8c8ee4ddac490c669015c943a78984277029 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 10 Jan 2025 15:41:02 +0100 Subject: [PATCH 18/41] Fix anchors and popovers --- ...-optimization-detective-debug-tag-visitor.php | 8 ++++---- plugins/optimization-detective/debug.php | 16 ++++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php index 6bb9836f56..65c66a7f3e 100644 --- a/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php +++ b/plugins/optimization-detective/class-optimization-detective-debug-tag-visitor.php @@ -54,7 +54,7 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { $style = is_string( $style ) ? $style : ''; $processor->set_attribute( 'style', - "--anchor-name: --od-debug-element-$uuid;" . $style + "anchor-name: --od-debug-element-$uuid;" . $style ); $processor->set_meta_attribute( @@ -68,14 +68,14 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { $processor->append_body_html( << - $anchor_text
Date: Fri, 10 Jan 2025 16:09:08 +0100 Subject: [PATCH 19/41] Re-enable css --- plugins/optimization-detective/debug.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/optimization-detective/debug.php b/plugins/optimization-detective/debug.php index 6e860cd43c..9d7a233340 100644 --- a/plugins/optimization-detective/debug.php +++ b/plugins/optimization-detective/debug.php @@ -180,7 +180,7 @@ function od_debug_add_assets(): void { - Date: Mon, 13 Jan 2025 22:49:02 +0100 Subject: [PATCH 32/41] Undo tests change too --- .../optimization-detective/tests/test-class-od-url-metric.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/tests/test-class-od-url-metric.php b/plugins/optimization-detective/tests/test-class-od-url-metric.php index 8c1cf5fc5d..15f34899be 100644 --- a/plugins/optimization-detective/tests/test-class-od-url-metric.php +++ b/plugins/optimization-detective/tests/test-class-od-url-metric.php @@ -796,8 +796,8 @@ public function test_get_json_schema_extensibility( Closure $set_up, Closure $as */ protected function check_schema_subset( array $schema, string $path, bool $extended = false ): void { $this->assertArrayHasKey( 'required', $schema, $path ); - // Skipping the check for 'root/etag' as it is currently optional. Also adding 'root/inpData' which is experimental and may be put into an extension rather than be a core property. - if ( ! $extended && 'root/etag' !== $path && 'root/inpData' !== $path ) { + // Skipping the check for 'root/etag' as it is currently optional. + if ( ! $extended && 'root/etag' !== $path ) { $this->assertTrue( $schema['required'], $path ); } $this->assertArrayHasKey( 'type', $schema, $path ); From 2802b57aff4bb2a78bf54c05626e8ef71e353da4 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 14 Jan 2025 10:36:15 +0100 Subject: [PATCH 33/41] Remove JS parts now as well --- plugins/optimization-detective/detect.js | 22 ---------------------- plugins/optimization-detective/types.ts | 7 ------- 2 files changed, 29 deletions(-) diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index 2dad8fceb8..2ae37a03a8 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -519,25 +519,6 @@ export default async function detect( { // Stop observing. disconnectIntersectionObserver(); - const inpData = []; - - onINP( - /** - * - * @param {INPMetric|INPMetricWithAttribution} metric - */ - ( metric ) => { - if ( 'attribution' in metric ) { - // TODO: Store xpath instead? - inpData.push( { - value: metric.value, - rating: metric.rating, - interactionTarget: metric.attribution.interactionTarget, - } ); - } - } - ); - if ( isDebug ) { log( 'Detection is stopping.' ); } @@ -549,7 +530,6 @@ export default async function detect( { height: win.innerHeight, }, elements: [], - inpData: [], }; const lcpMetric = lcpMetricCandidates.at( -1 ); @@ -609,8 +589,6 @@ export default async function detect( { ); } ); - urlMetric.inpData = inpData; - // Only proceed with submitting the URL Metric if viewport stayed the same size. Changing the viewport size (e.g. due // to resizing a window or changing the orientation of a device) will result in unexpected metrics being collected. if ( didWindowResize ) { diff --git a/plugins/optimization-detective/types.ts b/plugins/optimization-detective/types.ts index d87d443ca5..d92c532143 100644 --- a/plugins/optimization-detective/types.ts +++ b/plugins/optimization-detective/types.ts @@ -14,12 +14,6 @@ export interface ElementData { export type ExtendedElementData = ExcludeProps< ElementData >; -export interface INPData { - value: number; - rating: string; - interactionTarget: string; -} - export interface URLMetric { url: string; viewport: { @@ -27,7 +21,6 @@ export interface URLMetric { height: number; }; elements: ElementData[]; - inpData: INPData[]; } export type ExtendedRootData = ExcludeProps< URLMetric >; From 5fd68910bcb4035c09bd0512eb6be4bc97914ea2 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 14 Jan 2025 10:55:33 +0100 Subject: [PATCH 34/41] Undo type import --- plugins/optimization-detective/detect.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index 2ae37a03a8..6119c5fd1c 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -1,8 +1,6 @@ /** * @typedef {import("web-vitals").LCPMetric} LCPMetric * @typedef {import("web-vitals").LCPMetricWithAttribution} LCPMetricWithAttribution - * @typedef {import("web-vitals").INPMetric} INPMetric - * @typedef {import("web-vitals").INPMetricWithAttribution} INPMetricWithAttribution * @typedef {import("./types.ts").ElementData} ElementData * @typedef {import("./types.ts").OnTTFBFunction} OnTTFBFunction * @typedef {import("./types.ts").OnFCPFunction} OnFCPFunction From 8dd8af6b5a0f79aed1ec28ebfe3b9b79905384ee Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 14 Jan 2025 18:36:01 +0100 Subject: [PATCH 35/41] Don't conditionally load by default Co-authored-by: Weston Ruter --- plugins/optimization-detective/detection.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/optimization-detective/detection.php b/plugins/optimization-detective/detection.php index b92665ade0..f344c528f7 100644 --- a/plugins/optimization-detective/detection.php +++ b/plugins/optimization-detective/detection.php @@ -70,7 +70,6 @@ function od_get_cache_purge_post_id(): ?int { * @param OD_URL_Metric_Group_Collection $group_collection URL Metric group collection. */ function od_get_detection_script( string $slug, OD_URL_Metric_Group_Collection $group_collection ): string { - $use_attribution_build = WP_DEBUG || wp_is_development_mode( 'plugin' ); /** * Filters whether to use the web-vitals.js build with attribution. @@ -79,7 +78,7 @@ function od_get_detection_script( string $slug, OD_URL_Metric_Group_Collection $ * * @param bool $use_attribution_build Whether to use the attribution build. */ - $use_attribution_build = (bool) apply_filters( 'od_use_web_vitals_attribution_build', $use_attribution_build ); + $use_attribution_build = (bool) apply_filters( 'od_use_web_vitals_attribution_build', false ); $web_vitals_lib_data = require __DIR__ . '/build/web-vitals.asset.php'; $web_vitals_lib_src = $use_attribution_build ? From c6f53fa5fda1f36a1762cbb9b136180804f6b6c0 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 14 Jan 2025 18:36:52 +0100 Subject: [PATCH 36/41] Remove newline Co-authored-by: Weston Ruter --- plugins/optimization-detective/detect.js | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index 6119c5fd1c..6865e0f1af 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -516,7 +516,6 @@ export default async function detect( { // Stop observing. disconnectIntersectionObserver(); - if ( isDebug ) { log( 'Detection is stopping.' ); } From fd7fa9f0c30ca395b133ecb8443b63dde3585488 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 14 Jan 2025 09:51:27 -0800 Subject: [PATCH 37/41] Add jsdoc description --- plugins/optimization-detective/detect.js | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index 6865e0f1af..65041624bb 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -498,6 +498,7 @@ export default async function detect( { await new Promise( ( resolve ) => { onLCP( /** + * Handles an LCP metric being reported. * * @param {LCPMetric|LCPMetricWithAttribution} metric */ From 3614c137afeff9275bbb1c5390d22e588fcd8280 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 14 Jan 2025 10:05:32 -0800 Subject: [PATCH 38/41] Add typing for attribution report functions --- plugins/optimization-detective/detect.js | 15 ++++++++++----- plugins/optimization-detective/types.ts | 22 +++++++++++++++++----- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index 65041624bb..cabcbe6c61 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -7,6 +7,11 @@ * @typedef {import("./types.ts").OnLCPFunction} OnLCPFunction * @typedef {import("./types.ts").OnINPFunction} OnINPFunction * @typedef {import("./types.ts").OnCLSFunction} OnCLSFunction + * @typedef {import("./types.ts").OnTTFBWithAttributionFunction} OnTTFBWithAttributionFunction + * @typedef {import("./types.ts").OnFCPWithAttributionFunction} OnFCPWithAttributionFunction + * @typedef {import("./types.ts").OnLCPWithAttributionFunction} OnLCPWithAttributionFunction + * @typedef {import("./types.ts").OnINPWithAttributionFunction} OnINPWithAttributionFunction + * @typedef {import("./types.ts").OnCLSWithAttributionFunction} OnCLSWithAttributionFunction * @typedef {import("./types.ts").URLMetric} URLMetric * @typedef {import("./types.ts").URLMetricGroupStatus} URLMetricGroupStatus * @typedef {import("./types.ts").Extension} Extension @@ -361,11 +366,11 @@ export default async function detect( { ); const { - /** @type OnTTFBFunction */ onTTFB, - /** @type OnFCPFunction */ onFCP, - /** @type OnLCPFunction */ onLCP, - /** @type OnINPFunction */ onINP, - /** @type OnCLSFunction */ onCLS, + /** @type {OnTTFBFunction|OnTTFBWithAttributionFunction} */ onTTFB, + /** @type {OnFCPFunction|OnFCPWithAttributionFunction} */ onFCP, + /** @type {OnLCPFunction|OnLCPWithAttributionFunction} */ onLCP, + /** @type {OnINPFunction|OnINPWithAttributionFunction} */ onINP, + /** @type {OnCLSFunction|OnCLSWithAttributionFunction} */ onCLS, } = await import( webVitalsLibrarySrc ); // TODO: Does this make sense here? diff --git a/plugins/optimization-detective/types.ts b/plugins/optimization-detective/types.ts index d92c532143..cf3e647eda 100644 --- a/plugins/optimization-detective/types.ts +++ b/plugins/optimization-detective/types.ts @@ -2,6 +2,13 @@ type ExcludeProps< T > = { [ k: string ]: any } & { [ K in keyof T ]?: never }; import { onTTFB, onFCP, onLCP, onINP, onCLS } from 'web-vitals'; +import { + onTTFB as onTTFBWithAttribution, + onFCP as onFCPWithAttribution, + onLCP as onLCPWithAttribution, + onINP as onINPWithAttribution, + onCLS as onCLSWithAttribution, +} from 'web-vitals/attribution'; export interface ElementData { isLCP: boolean; @@ -35,14 +42,19 @@ export type OnFCPFunction = typeof onFCP; export type OnLCPFunction = typeof onLCP; export type OnINPFunction = typeof onINP; export type OnCLSFunction = typeof onCLS; +export type OnTTFBWithAttributionFunction = typeof onTTFBWithAttribution; +export type OnFCPWithAttributionFunction = typeof onFCPWithAttribution; +export type OnLCPWithAttributionFunction = typeof onLCPWithAttribution; +export type OnINPWithAttributionFunction = typeof onINPWithAttribution; +export type OnCLSWithAttributionFunction = typeof onCLSWithAttribution; export type InitializeArgs = { readonly isDebug: boolean; - readonly onTTFB: OnTTFBFunction; - readonly onFCP: OnFCPFunction; - readonly onLCP: OnLCPFunction; - readonly onINP: OnINPFunction; - readonly onCLS: OnCLSFunction; + readonly onTTFB: OnTTFBFunction | OnTTFBWithAttributionFunction; + readonly onFCP: OnFCPFunction | OnFCPWithAttributionFunction; + readonly onLCP: OnLCPFunction | OnLCPWithAttributionFunction; + readonly onINP: OnINPFunction | OnINPWithAttributionFunction; + readonly onCLS: OnCLSFunction | OnCLSWithAttributionFunction; }; export type InitializeCallback = ( args: InitializeArgs ) => Promise< void >; From 3a30027261115d9a6c54fe4c0e31c072c9c4037a Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 14 Jan 2025 10:33:48 -0800 Subject: [PATCH 39/41] Add docs for the od_use_web_vitals_attribution_build filter --- plugins/optimization-detective/detection.php | 11 +++++++++++ plugins/optimization-detective/docs/hooks.md | 17 +++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/plugins/optimization-detective/detection.php b/plugins/optimization-detective/detection.php index f344c528f7..b7d4402f2e 100644 --- a/plugins/optimization-detective/detection.php +++ b/plugins/optimization-detective/detection.php @@ -74,6 +74,17 @@ function od_get_detection_script( string $slug, OD_URL_Metric_Group_Collection $ /** * Filters whether to use the web-vitals.js build with attribution. * + * When using the attribution build of web-vitals, the metric object passed to report callbacks registered via + * `onTTFB`, `onFCP`, `onLCP`, `onCLS`, and `onINP` will include an additional {@link https://github.com/GoogleChrome/web-vitals#attribution attribution property}. + * For details, please refer to the {@link https://github.com/GoogleChrome/web-vitals web-vitals documentation}. + * + * For example, to opt in to using the attribution build: + * + * add_filter( 'od_use_web_vitals_attribution_build', '__return_true' ); + * + * Note that the attribution build is slightly larger than the standard build, so this is why it is not used by default. + * The additional attribution data is made available to client-side extension script modules registered via the `od_extension_module_urls` filter. + * * @since n.e.x.t * * @param bool $use_attribution_build Whether to use the attribution build. diff --git a/plugins/optimization-detective/docs/hooks.md b/plugins/optimization-detective/docs/hooks.md index 6b53bbd3d0..231b6e5d98 100644 --- a/plugins/optimization-detective/docs/hooks.md +++ b/plugins/optimization-detective/docs/hooks.md @@ -53,6 +53,23 @@ The supplied context object includes these properties: ## Filters +### Filter: `od_use_web_vitals_attribution_build` (default: `false`) + +Filters whether to use the web-vitals.js build with attribution. + +When using the attribution build of web-vitals, the metric object passed to report callbacks registered via +`onTTFB`, `onFCP`, `onLCP`, `onCLS`, and `onINP` will include an additional [attribution property](https://github.com/GoogleChrome/web-vitals#attribution). +For details, please refer to the [web-vitals documentation](https://github.com/GoogleChrome/web-vitals documentation). + +For example, to opt in to using the attribution build: + +```php +add_filter( 'od_use_web_vitals_attribution_build', '__return_true' ); +``` + +Note that the attribution build is slightly larger than the standard build, so this is why it is not used by default. +The additional attribution data is made available to client-side extension script modules registered via the `od_extension_module_urls` filter. + ### Filter: `od_breakpoint_max_widths` (default: `array(480, 600, 782)`) Filters the breakpoint max widths to group URL Metrics for various viewports. Each number represents the maximum width (inclusive) for a given breakpoint. So if there is one number, 480, then this means there will be two viewport groupings, one for 0\<=480, and another \>480. If instead there are the two breakpoints defined, 480 and 782, then this means there will be three viewport groups of URL Metrics, one for 0\<=480 (i.e. mobile), another 481\<=782 (i.e. phablet/tablet), and another \>782 (i.e. desktop). From a75dd53cf7c96b02384d9d120957987d44eae53d Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 14 Jan 2025 10:48:21 -0800 Subject: [PATCH 40/41] Add test for od_use_web_vitals_attribution_build filter --- .../tests/test-detection.php | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/plugins/optimization-detective/tests/test-detection.php b/plugins/optimization-detective/tests/test-detection.php index a159591115..e78c7231d9 100644 --- a/plugins/optimization-detective/tests/test-detection.php +++ b/plugins/optimization-detective/tests/test-detection.php @@ -80,19 +80,20 @@ public function test_od_get_cache_purge_post_id( Closure $set_up, bool $expected /** * Data provider. * - * @return array}> + * @return array, expected_standard_build: bool}> */ public function data_provider_od_get_detection_script(): array { return array( 'unfiltered' => array( - 'set_up' => static function (): void {}, - 'expected_exports' => array( + 'set_up' => static function (): void {}, + 'expected_exports' => array( 'storageLockTTL' => MINUTE_IN_SECONDS, 'extensionModuleUrls' => array(), ), + 'expected_standard_build' => true, ), 'filtered' => array( - 'set_up' => static function (): void { + 'set_up' => static function (): void { add_filter( 'od_url_metric_storage_lock_ttl', static function (): int { @@ -106,11 +107,13 @@ static function ( array $urls ): array { return $urls; } ); + add_filter( 'od_use_web_vitals_attribution_build', '__return_true' ); }, - 'expected_exports' => array( + 'expected_exports' => array( 'storageLockTTL' => HOUR_IN_SECONDS, 'extensionModuleUrls' => array( home_url( '/my-extension.js', 'https' ) ), ), + 'expected_standard_build' => false, ), ); } @@ -122,10 +125,11 @@ static function ( array $urls ): array { * * @dataProvider data_provider_od_get_detection_script * - * @param Closure $set_up Set up callback. - * @param array}> $expected_exports Expected exports. + * @param Closure $set_up Set up callback. + * @param array $expected_exports Expected exports. + * @param bool $expected_standard_build Expected standard build. */ - public function test_od_get_detection_script_returns_script( Closure $set_up, array $expected_exports ): void { + public function test_od_get_detection_script_returns_script( Closure $set_up, array $expected_exports, bool $expected_standard_build ): void { $set_up(); $slug = od_get_url_metrics_slug( array( 'p' => '1' ) ); $current_etag = md5( '' ); @@ -140,6 +144,12 @@ public function test_od_get_detection_script_returns_script( Closure $set_up, ar foreach ( $expected_exports as $key => $value ) { $this->assertStringContainsString( sprintf( '%s:%s', wp_json_encode( $key ), wp_json_encode( $value ) ), $script ); } + $this->assertSame( 1, preg_match( '/"webVitalsLibrarySrc":("[^"]+?")/', $script, $matches ) ); + $web_vitals_library_src = json_decode( $matches[1] ); + $this->assertStringContainsString( + $expected_standard_build ? '/web-vitals.' : '/web-vitals-attribution.', + $web_vitals_library_src + ); $this->assertStringContainsString( '"minimumViewportWidth":0', $script ); $this->assertStringContainsString( '"minimumViewportWidth":481', $script ); $this->assertStringContainsString( '"minimumViewportWidth":601', $script ); From 960007805433d7a40c3e1cd5d01da88f97bd25f1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 14 Jan 2025 10:52:20 -0800 Subject: [PATCH 41/41] fixup! Add docs for the od_use_web_vitals_attribution_build filter --- plugins/optimization-detective/docs/hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/optimization-detective/docs/hooks.md b/plugins/optimization-detective/docs/hooks.md index 231b6e5d98..09e20bad44 100644 --- a/plugins/optimization-detective/docs/hooks.md +++ b/plugins/optimization-detective/docs/hooks.md @@ -59,7 +59,7 @@ Filters whether to use the web-vitals.js build with attribution. When using the attribution build of web-vitals, the metric object passed to report callbacks registered via `onTTFB`, `onFCP`, `onLCP`, `onCLS`, and `onINP` will include an additional [attribution property](https://github.com/GoogleChrome/web-vitals#attribution). -For details, please refer to the [web-vitals documentation](https://github.com/GoogleChrome/web-vitals documentation). +For details, please refer to the [web-vitals documentation](https://github.com/GoogleChrome/web-vitals). For example, to opt in to using the attribution build: