From 3d61681e9f6747fa16376602aceeb2b557bf6970 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Tue, 22 Oct 2024 20:29:53 +0530 Subject: [PATCH 001/237] Update existing code with new approach --- plugins/auto-sizes/hooks.php | 174 ++++++++++++------ .../auto-sizes/tests/test-improve-sizes.php | 2 - 2 files changed, 115 insertions(+), 61 deletions(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index b77c06903b..28b1951acc 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -139,79 +139,147 @@ function auto_sizes_get_width( string $layout_width, int $image_width ): string } /** - * Filter the sizes attribute for images to improve the default calculation. + * Primes attachment into the cache with a single database query. * - * @since 1.1.0 + * @since n.e.x.t * - * @param string $content The block content about to be rendered. - * @param array{ attrs?: array{ align?: string, width?: string } } $parsed_block The parsed block. - * @return string The updated block content. + * @param string $content The HTML content. + * @param string $context Optional. Additional context to pass to the filters. + * Defaults to `current_filter()` when not set. + * @return string The HTML content. */ -function auto_sizes_filter_image_tag( string $content, array $parsed_block ): string { - $processor = new WP_HTML_Tag_Processor( $content ); - $has_image = $processor->next_tag( array( 'tag_name' => 'img' ) ); +function auto_sizes_prime_attachment_caches( string $content, string $context = null ): string { + if ( null === $context ) { + $context = current_filter(); + } - // Only update the markup if an image is found. - if ( $has_image ) { - $processor->set_attribute( 'data-needs-sizes-update', true ); - if ( isset( $parsed_block['attrs']['align'] ) ) { - $processor->set_attribute( 'data-align', $parsed_block['attrs']['align'] ); - } + if ( false === preg_match_all( '/]+>/', $content, $matches, PREG_SET_ORDER ) ) { + return $content; + } + + // List of the unique `img` tags found in $content. + $images = array(); + + foreach ( $matches as $match ) { + list( $tag_name ) = $match; - // Resize image width. - if ( isset( $parsed_block['attrs']['width'] ) ) { - $processor->set_attribute( 'data-resize-width', $parsed_block['attrs']['width'] ); + if ( preg_match( '/wp-image-([0-9]+)/i', $tag_name, $class_id ) === 1 ) { + $attachment_id = absint( $class_id[1] ); + + if ( $attachment_id > 0 ) { + /* + * If exactly the same image tag is used more than once, overwrite it. + * All identical tags will be replaced later with 'str_replace()'. + */ + $images[ $tag_name ] = $attachment_id; + } } + $images[ $tag_name ] = 0; + } - $content = $processor->get_updated_html(); + // Reduce the array to unique attachment IDs. + $attachment_ids = array_unique( array_filter( array_values( $images ) ) ); + + if ( count( $attachment_ids ) > 1 ) { + /* + * Warm the object cache with post and meta information for all found + * images to avoid making individual database calls. + */ + _prime_post_caches( $attachment_ids, false, true ); } + return $content; } -add_filter( 'render_block_core/image', 'auto_sizes_filter_image_tag', 10, 2 ); -add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 2 ); +add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 6 ); /** * Filter the sizes attribute for images to improve the default calculation. * * @since 1.1.0 * - * @param string $content The block content about to be rendered. + * @param string $content The block content about to be rendered. + * @param array{ attrs?: array{ align?: string, width?: string } } $parsed_block The parsed block. + * @param WP_Block $block Block instance. * @return string The updated block content. */ -function auto_sizes_improve_image_sizes_attributes( string $content ): string { +function auto_sizes_filter_image_tag( string $content, array $parsed_block, WP_Block $block ): string { $processor = new WP_HTML_Tag_Processor( $content ); - if ( ! $processor->next_tag( array( 'tag_name' => 'img' ) ) ) { - return $content; - } + $has_image = $processor->next_tag( array( 'tag_name' => 'img' ) ); + + // Only update the markup if an image is found. + if ( $has_image ) { + + /** + * Callback for calculating image sizes attribute value for an image block. + * + * This is a workaround to use block context data when calculating the img sizes attribute. + * + * @since n.e.x.t + * + * @param string $sizes The image sizes attribute value. + * @param string $size The image size data. + */ + $filter = static function ( $sizes, $size ) use ( $block ) { + $id = $block->attributes['id'] ?? 0; + $alignment = $block->attributes['align'] ?? ''; + $width = $block->attributes['width'] ?? ''; + + // Hypotehtical function to calculate better sizes. + $sizes = auto_sizes_calculate_better_sizes( $id, $size, $alignment, $width ); + + return $sizes; + }; + + // Hook this filter early, before default fitlers are run. + add_filter( 'wp_calculate_image_sizes', $filter, 9, 2 ); + + $sizes = wp_calculate_image_sizes( + // If we don't have a size slug, assume the full size was used. + $parsed_block['attrs']['sizeSlug'] ?? 'full', + null, + null, + $parsed_block['attrs']['id'] ?? 0 + ); + + remove_filter( 'wp_calculate_image_sizes', $filter, 9 ); + + // Bail early if sizes are not calculated. + if ( false === $sizes ) { + return $content; + } - $remove_data_attributes = static function () use ( $processor ): void { - $processor->remove_attribute( 'data-needs-sizes-update' ); - $processor->remove_attribute( 'data-align' ); - $processor->remove_attribute( 'data-resize-width' ); - }; + $processor->set_attribute( 'sizes', $sizes ); - // Bail early if the responsive images are disabled. - if ( null === $processor->get_attribute( 'sizes' ) ) { - $remove_data_attributes(); return $processor->get_updated_html(); } - // Skips second time parsing if already processed. - if ( null === $processor->get_attribute( 'data-needs-sizes-update' ) ) { - return $content; - } + return $content; +} +add_filter( 'render_block_core/image', 'auto_sizes_filter_image_tag', 10, 3 ); +add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 3 ); - $align = $processor->get_attribute( 'data-align' ); +/** + * Hypothetical function to calculate better sizes. + * + * @param int $id The image id. + * @param string $size The image size data. + * @param string $align The image alignment. + * @param string $resize_width Resize image width. + * @return string The sizes attribute value. + */ +function auto_sizes_calculate_better_sizes( int $id, string $size, string $align, string $resize_width ): string { + $sizes = ''; + $image = wp_get_attachment_image_src( $id, $size ); - // Retrieve width from the image tag itself. - $image_width = $processor->get_attribute( 'width' ); - if ( ! is_string( $image_width ) && ! in_array( $align, array( 'full', 'wide' ), true ) ) { - return $content; + if ( false === $image ) { + return $sizes; } + // Retrieve width from the image tag itself. + $image_width = '' !== $resize_width ? (int) $resize_width : $image[1]; + $layout = wp_get_global_settings( array( 'layout' ) ); - $sizes = null; // Handle different alignment use cases. switch ( $align ) { case 'full': @@ -227,28 +295,16 @@ function auto_sizes_improve_image_sizes_attributes( string $content ): string { case 'left': case 'right': case 'center': - // Resize image width. - $image_width = $processor->get_attribute( 'data-resize-width' ) ?? $image_width; - $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $image_width ); + $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $image_width ); break; default: if ( array_key_exists( 'contentSize', $layout ) ) { - // Resize image width. - $image_width = $processor->get_attribute( 'data-resize-width' ) ?? $image_width; - $width = auto_sizes_get_width( $layout['contentSize'], (int) $image_width ); - $sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $width ); + $width = auto_sizes_get_width( $layout['contentSize'], $image_width ); + $sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $width ); } break; } - if ( is_string( $sizes ) ) { - $processor->set_attribute( 'sizes', $sizes ); - } - - $remove_data_attributes(); - - return $processor->get_updated_html(); + return $sizes; } -// Run filter prior to auto sizes "auto_sizes_update_content_img_tag" filter. -add_filter( 'wp_content_img_tag', 'auto_sizes_improve_image_sizes_attributes', 9 ); diff --git a/plugins/auto-sizes/tests/test-improve-sizes.php b/plugins/auto-sizes/tests/test-improve-sizes.php index 943de98381..5c306d12e4 100644 --- a/plugins/auto-sizes/tests/test-improve-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-sizes.php @@ -35,8 +35,6 @@ public function set_up(): void { /** * Test that if disable responsive image then it will not add sizes attribute. - * - * @covers ::auto_sizes_improve_image_sizes_attributes */ public function test_that_if_disable_responsive_image_then_it_will_not_add_sizes_attribute(): void { // Disable responsive images. From 4396c7857b5ac1a29c7fc24a0dae2088913257ee Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Wed, 23 Oct 2024 14:46:04 +0530 Subject: [PATCH 002/237] Fix spell --- plugins/auto-sizes/hooks.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index 28b1951acc..5b04bc7c66 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -224,13 +224,13 @@ function auto_sizes_filter_image_tag( string $content, array $parsed_block, WP_B $alignment = $block->attributes['align'] ?? ''; $width = $block->attributes['width'] ?? ''; - // Hypotehtical function to calculate better sizes. + // Hypothetical function to calculate better sizes. $sizes = auto_sizes_calculate_better_sizes( $id, $size, $alignment, $width ); return $sizes; }; - // Hook this filter early, before default fitlers are run. + // Hook this filter early, before default filters are run. add_filter( 'wp_calculate_image_sizes', $filter, 9, 2 ); $sizes = wp_calculate_image_sizes( From af890e236ce16fc4d08e1a99089e00b20524ce9f Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Wed, 23 Oct 2024 15:04:43 +0530 Subject: [PATCH 003/237] Disable lazy loading attribute for unit tests --- plugins/auto-sizes/tests/test-improve-sizes.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/auto-sizes/tests/test-improve-sizes.php b/plugins/auto-sizes/tests/test-improve-sizes.php index 5c306d12e4..73d23fb8e7 100644 --- a/plugins/auto-sizes/tests/test-improve-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-sizes.php @@ -31,6 +31,9 @@ public function set_up(): void { // Disable auto sizes. remove_filter( 'wp_content_img_tag', 'auto_sizes_update_content_img_tag' ); + + // Disable lazy loading attribute. + add_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); } /** From d9af56993243bc71b7c72a29ae8fa4db60455927 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Thu, 24 Oct 2024 15:24:06 +0530 Subject: [PATCH 004/237] Fix comment indention --- plugins/auto-sizes/hooks.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index 5b04bc7c66..c46d4a7cd6 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -168,9 +168,9 @@ function auto_sizes_prime_attachment_caches( string $content, string $context = if ( $attachment_id > 0 ) { /* - * If exactly the same image tag is used more than once, overwrite it. - * All identical tags will be replaced later with 'str_replace()'. - */ + * If exactly the same image tag is used more than once, overwrite it. + * All identical tags will be replaced later with 'str_replace()'. + */ $images[ $tag_name ] = $attachment_id; } } From b87adcfa0481c964ea61ab0d5053a2ada6957369 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Thu, 24 Oct 2024 15:49:38 +0530 Subject: [PATCH 005/237] Use HTML API for attachment cache --- plugins/auto-sizes/hooks.php | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index c46d4a7cd6..c9a31eb8a0 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -153,32 +153,27 @@ function auto_sizes_prime_attachment_caches( string $content, string $context = $context = current_filter(); } - if ( false === preg_match_all( '/]+>/', $content, $matches, PREG_SET_ORDER ) ) { - return $content; - } + $processor = new WP_HTML_Tag_Processor( $content ); - // List of the unique `img` tags found in $content. $images = array(); + while ( $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) { + $class = $processor->get_attribute( 'class' ); - foreach ( $matches as $match ) { - list( $tag_name ) = $match; + // Only apply the dominant color to images that have a src attribute. + if ( ! is_string( $class ) ) { + continue; + } - if ( preg_match( '/wp-image-([0-9]+)/i', $tag_name, $class_id ) === 1 ) { + if ( preg_match( '/wp-image-([0-9]+)/i', $class, $class_id ) === 1 ) { $attachment_id = absint( $class_id[1] ); - if ( $attachment_id > 0 ) { - /* - * If exactly the same image tag is used more than once, overwrite it. - * All identical tags will be replaced later with 'str_replace()'. - */ - $images[ $tag_name ] = $attachment_id; + $images[] = $attachment_id; } } - $images[ $tag_name ] = 0; } // Reduce the array to unique attachment IDs. - $attachment_ids = array_unique( array_filter( array_values( $images ) ) ); + $attachment_ids = array_unique( array_filter( $images ) ); if ( count( $attachment_ids ) > 1 ) { /* From bfdab101b23cb6e624c38bfcf05c1faa8a84ca16 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Fri, 25 Oct 2024 09:10:59 +0530 Subject: [PATCH 006/237] Address review feedback --- plugins/auto-sizes/hooks.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index c9a31eb8a0..906863e81e 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -159,7 +159,6 @@ function auto_sizes_prime_attachment_caches( string $content, string $context = while ( $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) { $class = $processor->get_attribute( 'class' ); - // Only apply the dominant color to images that have a src attribute. if ( ! is_string( $class ) ) { continue; } @@ -199,7 +198,7 @@ function auto_sizes_prime_attachment_caches( string $content, string $context = */ function auto_sizes_filter_image_tag( string $content, array $parsed_block, WP_Block $block ): string { $processor = new WP_HTML_Tag_Processor( $content ); - $has_image = $processor->next_tag( array( 'tag_name' => 'img' ) ); + $has_image = $processor->next_tag( array( 'tag_name' => 'IMG' ) ); // Only update the markup if an image is found. if ( $has_image ) { @@ -209,8 +208,6 @@ function auto_sizes_filter_image_tag( string $content, array $parsed_block, WP_B * * This is a workaround to use block context data when calculating the img sizes attribute. * - * @since n.e.x.t - * * @param string $sizes The image sizes attribute value. * @param string $size The image size data. */ From 97dcb8f51367e256af2756fc5958127c8b383fc7 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 28 Oct 2024 15:03:32 +0530 Subject: [PATCH 007/237] Apply suggestions from code review Co-authored-by: Weston Ruter --- plugins/auto-sizes/hooks.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index 906863e81e..2d08ab752a 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -143,12 +143,15 @@ function auto_sizes_get_width( string $layout_width, int $image_width ): string * * @since n.e.x.t * - * @param string $content The HTML content. - * @param string $context Optional. Additional context to pass to the filters. - * Defaults to `current_filter()` when not set. + * @param string|mixed $content The HTML content. + * @param string $context Optional. Additional context to pass to the filters. + * Defaults to `current_filter()` when not set. * @return string The HTML content. */ -function auto_sizes_prime_attachment_caches( string $content, string $context = null ): string { +function auto_sizes_prime_attachment_caches( $content, string $context = null ): string { + if ( ! is_string( $content ) ) { + return ''; + } if ( null === $context ) { $context = current_filter(); } @@ -191,12 +194,15 @@ function auto_sizes_prime_attachment_caches( string $content, string $context = * * @since 1.1.0 * - * @param string $content The block content about to be rendered. + * @param string|mixed $content The block content about to be rendered. * @param array{ attrs?: array{ align?: string, width?: string } } $parsed_block The parsed block. * @param WP_Block $block Block instance. * @return string The updated block content. */ -function auto_sizes_filter_image_tag( string $content, array $parsed_block, WP_Block $block ): string { +function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $block ): string { + if ( ! is_string( $content ) ) { + return ''; + } $processor = new WP_HTML_Tag_Processor( $content ); $has_image = $processor->next_tag( array( 'tag_name' => 'IMG' ) ); From 2cbf39d75bf285223f41d6c8c0d708fa18730a99 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 28 Oct 2024 15:13:52 +0530 Subject: [PATCH 008/237] Add types for function variables Co-authored-by: Weston Ruter --- plugins/auto-sizes/hooks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index 2d08ab752a..8c241bb876 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -223,7 +223,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b $width = $block->attributes['width'] ?? ''; // Hypothetical function to calculate better sizes. - $sizes = auto_sizes_calculate_better_sizes( $id, $size, $alignment, $width ); + $sizes = auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string), $alignment, (string) $width ); return $sizes; }; From 4c7faa062085c1cc20c4497b042ba68ad4f598b4 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 28 Oct 2024 15:29:42 +0530 Subject: [PATCH 009/237] Fix lint PHP --- plugins/auto-sizes/hooks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index 8c241bb876..e073100c4f 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -223,7 +223,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b $width = $block->attributes['width'] ?? ''; // Hypothetical function to calculate better sizes. - $sizes = auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string), $alignment, (string) $width ); + $sizes = auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width ); return $sizes; }; From dd0e12b36f69e2e00cb769da3d2aa4e0afa5ce65 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Wed, 6 Nov 2024 16:39:24 +0530 Subject: [PATCH 010/237] Apply suggestions from code review Co-authored-by: Weston Ruter --- plugins/auto-sizes/hooks.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index e073100c4f..4ce7d38043 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -166,8 +166,8 @@ function auto_sizes_prime_attachment_caches( $content, string $context = null ): continue; } - if ( preg_match( '/wp-image-([0-9]+)/i', $class, $class_id ) === 1 ) { - $attachment_id = absint( $class_id[1] ); + if ( preg_match( '/(?:^|\s)wp-image-([1-9][0-9]*)(?:\s|$)/', $class, $class_id ) === 1 ) { + $attachment_id = (int) $class_id[1]; if ( $attachment_id > 0 ) { $images[] = $attachment_id; } From fad4e87df15ccea0949e0d0f5e982b29cba61875 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Wed, 6 Nov 2024 16:43:56 +0530 Subject: [PATCH 011/237] Remove unused parameter from auto_sizes_prime_attachment_caches() --- plugins/auto-sizes/hooks.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index 4ce7d38043..4cbe91a58e 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -144,17 +144,12 @@ function auto_sizes_get_width( string $layout_width, int $image_width ): string * @since n.e.x.t * * @param string|mixed $content The HTML content. - * @param string $context Optional. Additional context to pass to the filters. - * Defaults to `current_filter()` when not set. * @return string The HTML content. */ -function auto_sizes_prime_attachment_caches( $content, string $context = null ): string { +function auto_sizes_prime_attachment_caches( $content ): string { if ( ! is_string( $content ) ) { return ''; } - if ( null === $context ) { - $context = current_filter(); - } $processor = new WP_HTML_Tag_Processor( $content ); From 198f2cd20c3d8899b194389ac9b430b7f65d6391 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Wed, 6 Nov 2024 16:55:26 +0530 Subject: [PATCH 012/237] Fix PHPStan error --- plugins/auto-sizes/hooks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index 4cbe91a58e..d4a8fa382e 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -170,7 +170,7 @@ function auto_sizes_prime_attachment_caches( $content ): string { } // Reduce the array to unique attachment IDs. - $attachment_ids = array_unique( array_filter( $images ) ); + $attachment_ids = array_unique( $images ); if ( count( $attachment_ids ) > 1 ) { /* From de20e126503cb4e50db24e15b5a44acbee67f1dd Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Fri, 15 Nov 2024 20:15:43 +0530 Subject: [PATCH 013/237] Added inline comment for filter priority --- plugins/auto-sizes/hooks.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index d4a8fa382e..73f1188efd 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -182,6 +182,11 @@ function auto_sizes_prime_attachment_caches( $content ): string { return $content; } + +/* + * The priority 6 is used to ensure the new filter runs right after the "wp-image-$attachment_id" class + * is added to the img tag at priority 5, allowing modifications that rely on this class being in place. + */ add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 6 ); /** From 9b07e99bafabbb407630aa13ca147d63dbe79ba4 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Mon, 18 Nov 2024 11:13:55 -0800 Subject: [PATCH 014/237] Only check plugin version integrity in deployment workflow if the plugin actually requires to be deployed. --- .github/workflows/deploy-plugins.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-plugins.yml b/.github/workflows/deploy-plugins.yml index 7a3c286d39..4cd7d41bcf 100644 --- a/.github/workflows/deploy-plugins.yml +++ b/.github/workflows/deploy-plugins.yml @@ -69,9 +69,6 @@ jobs: - name: Install npm dependencies run: npm ci - - name: Check plugin versions - run: npm run versions -- --plugin=${{ matrix.plugin }} - - name: Build plugin run: npm run build:plugin:${{ matrix.plugin }} @@ -101,6 +98,10 @@ jobs: echo "deploy=true" >> $GITHUB_OUTPUT + - name: Check plugin version integrity + if: steps.check-deployment.outputs.deploy == 'true' + run: npm run versions -- --plugin=${{ matrix.plugin }} + - name: Create zip file if: steps.check-deployment.outputs.deploy == 'true' run: | From 3bb88e452dc71dce45b480118b2d7c68b14f08c5 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Mon, 18 Nov 2024 11:14:13 -0800 Subject: [PATCH 015/237] Revert "Remove problematic n.e.x.t readme changelog entries." This reverts commit a58aaf4fbc55447abdb6c3842873827a98556abb. --- plugins/embed-optimizer/readme.txt | 6 ++++++ plugins/image-prioritizer/readme.txt | 6 ++++++ plugins/web-worker-offloading/readme.txt | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/plugins/embed-optimizer/readme.txt b/plugins/embed-optimizer/readme.txt index b02756cfbb..cc07740daa 100644 --- a/plugins/embed-optimizer/readme.txt +++ b/plugins/embed-optimizer/readme.txt @@ -67,6 +67,12 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += n.e.x.t = + +**Enhancements** + +* Serve unminified scripts when `SCRIPT_DEBUG` is enabled. ([1643](https://github.com/WordPress/performance/pull/1643)) + = 0.3.0 = **Enhancements** diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt index a7925be3ab..cb5a67c0eb 100644 --- a/plugins/image-prioritizer/readme.txt +++ b/plugins/image-prioritizer/readme.txt @@ -62,6 +62,12 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += n.e.x.t = + +**Enhancements** + +* Serve unminified scripts when `SCRIPT_DEBUG` is enabled. ([1643](https://github.com/WordPress/performance/pull/1643)) + = 0.2.0 = **Enhancements** diff --git a/plugins/web-worker-offloading/readme.txt b/plugins/web-worker-offloading/readme.txt index db93c984cd..4c1e62a7ed 100644 --- a/plugins/web-worker-offloading/readme.txt +++ b/plugins/web-worker-offloading/readme.txt @@ -94,6 +94,12 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += n.e.x.t = + +**Enhancements** + +* Serve unminified scripts when `SCRIPT_DEBUG` is enabled. ([1643](https://github.com/WordPress/performance/pull/1643)) + = 0.1.1 = **Enhancements** From d55cf6b4127bd72e35b555babca06711bae66030 Mon Sep 17 00:00:00 2001 From: b1ink0 Date: Wed, 20 Nov 2024 13:52:07 +0530 Subject: [PATCH 016/237] Update 'perflab_query_plugin_info' function to disregard transient cache when a plugin is absent --- .../includes/admin/plugins.php | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index 1e91c66cf2..8afa014a34 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -23,14 +23,16 @@ function perflab_query_plugin_info( string $plugin_slug ) { $plugins = get_transient( $transient_key ); if ( is_array( $plugins ) ) { - // If the specific plugin_slug is not in the cache, return an error. - if ( ! isset( $plugins[ $plugin_slug ] ) ) { - return new WP_Error( - 'plugin_not_found', - __( 'Plugin not found in cached API response.', 'performance-lab' ) - ); + if ( isset( $plugins[ $plugin_slug ] ) ) { + if ( false === $plugins[ $plugin_slug ] ) { + // Plugin was requested before and not found. + return new WP_Error( + 'plugin_not_found', + __( 'Plugin not found in previous API response.', 'performance-lab' ) + ); + } + return $plugins[ $plugin_slug ]; // Return cached plugin info if found. } - return $plugins[ $plugin_slug ]; // Return cached plugin info if found. } $fields = array( @@ -83,9 +85,14 @@ function perflab_query_plugin_info( string $plugin_slug ) { $plugins[ $plugin_data['slug'] ] = wp_array_slice_assoc( $plugin_data, $fields ); } + if ( ! isset( $plugins[ $plugin_slug ] ) ) { + // Cache the fact that the plugin was not found. + $plugins[ $plugin_slug ] = false; + } + set_transient( $transient_key, $plugins, HOUR_IN_SECONDS ); - if ( ! isset( $plugins[ $plugin_slug ] ) ) { + if ( false === $plugins[ $plugin_slug ] ) { return new WP_Error( 'plugin_not_found', __( 'Plugin not found in API response.', 'performance-lab' ) From 21ee145ebc5119390b4cdbbbd851ec52c0a35cec Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 20 Nov 2024 16:41:22 -0800 Subject: [PATCH 017/237] Integrate Web Worker Offloading with Rank Math SEO --- plugins/web-worker-offloading/third-party.php | 6 +- .../third-party/seo-by-rank-math.php | 78 +++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 plugins/web-worker-offloading/third-party/seo-by-rank-math.php diff --git a/plugins/web-worker-offloading/third-party.php b/plugins/web-worker-offloading/third-party.php index d6916a3fbb..d1830d81cf 100644 --- a/plugins/web-worker-offloading/third-party.php +++ b/plugins/web-worker-offloading/third-party.php @@ -40,11 +40,13 @@ static function ( $to_do ) use ( $script_handles ) { function plwwo_load_third_party_integrations(): void { $plugins_with_integrations = array( // TODO: google-site-kit. - // TODO: seo-by-rank-math. - 'woocommerce' => static function (): bool { + 'woocommerce' => static function (): bool { // See . return class_exists( 'WooCommerce' ); }, + 'seo-by-rank-math' => static function (): bool { + return class_exists( 'RankMath' ); + }, ); foreach ( $plugins_with_integrations as $plugin_slug => $active_callback ) { diff --git a/plugins/web-worker-offloading/third-party/seo-by-rank-math.php b/plugins/web-worker-offloading/third-party/seo-by-rank-math.php new file mode 100644 index 0000000000..4162fdb5f7 --- /dev/null +++ b/plugins/web-worker-offloading/third-party/seo-by-rank-math.php @@ -0,0 +1,78 @@ +|mixed $configuration Configuration. + * @return array Configuration. + */ +function plwwo_rank_math_configure( $configuration ): array { + $configuration = (array) $configuration; + + $configuration['globalFns'][] = 'gtag'; // Because gtag() is defined in one script and called in another. + $configuration['forward'][] = 'dataLayer.push'; // Because the Partytown integration has this in its example config. + return $configuration; +} +add_filter( 'plwwo_configuration', 'plwwo_rank_math_configure' ); + +/* + * Note: The following integration is not targeting the \RankMath\Analytics\GTag::enqueue_gtag_js() code which is only + * used for WP<5.7. In WP 5.7, the wp_script_attributes and wp_inline_script_attributes filters were introduced, and + * Rank Math then deemed it preferable to use wp_print_script_tag() and wp_print_inline_script_tag() rather than + * wp_enqueue_script() and wp_add_inline_script(), respectively. Since Web Worker Offloading requires WP 6.5+, there + * is no point to integrate with the pre-5.7 code in Rank Math. + */ + +/** + * Filters script attributes to offload Rank Math's GTag script tag to Partytown. + * + * @since n.e.x.t + * @access private + * @link https://github.com/rankmath/seo-by-rank-math/blob/c78adba6f78079f27ff1430fabb75c6ac3916240/includes/modules/analytics/class-gtag.php#L161-L167 + * + * @param array|mixed $attributes Script attributes. + * @return array|mixed Filtered script attributes. + */ +function plwwo_rank_math_filter_script_attributes( $attributes ) { + if ( isset( $attributes['id'] ) && 'google_gtagjs' === $attributes['id'] ) { + wp_enqueue_script( 'web-worker-offloading' ); + $attributes['type'] = 'text/partytown'; + } + return $attributes; +} + +add_filter( 'wp_script_attributes', 'plwwo_rank_math_filter_script_attributes' ); + +/** + * Filters inline script attributes to offload Rank Math's GTag script tag to Partytown. + * + * @since n.e.x.t + * @access private + * @link https://github.com/rankmath/seo-by-rank-math/blob/c78adba6f78079f27ff1430fabb75c6ac3916240/includes/modules/analytics/class-gtag.php#L169-L174 + * + * @param array|mixed $attributes Script attributes. + * @return array|mixed Filtered inline script attributes. + */ +function plwwo_rank_math_filter_inline_script_attributes( $attributes ) { + if ( isset( $attributes['id'] ) && 'google_gtagjs-inline' === $attributes['id'] ) { + wp_enqueue_script( 'web-worker-offloading' ); + $attributes['type'] = 'text/partytown'; + } + return $attributes; +} + +add_filter( 'wp_inline_script_attributes', 'plwwo_rank_math_filter_inline_script_attributes' ); From 5670047da880a7488a55afa10ba08ef69da30361 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 20 Nov 2024 17:58:49 -0800 Subject: [PATCH 018/237] Integrate WWO with Site Kit --- plugins/web-worker-offloading/third-party.php | 10 +++-- .../third-party/google-site-kit.php | 37 +++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) create mode 100644 plugins/web-worker-offloading/third-party/google-site-kit.php diff --git a/plugins/web-worker-offloading/third-party.php b/plugins/web-worker-offloading/third-party.php index d1830d81cf..51b7ab0a03 100644 --- a/plugins/web-worker-offloading/third-party.php +++ b/plugins/web-worker-offloading/third-party.php @@ -39,14 +39,16 @@ static function ( $to_do ) use ( $script_handles ) { */ function plwwo_load_third_party_integrations(): void { $plugins_with_integrations = array( - // TODO: google-site-kit. - 'woocommerce' => static function (): bool { - // See . - return class_exists( 'WooCommerce' ); + 'google-site-kit' => static function (): bool { + return defined( 'GOOGLESITEKIT_VERSION' ); }, 'seo-by-rank-math' => static function (): bool { return class_exists( 'RankMath' ); }, + 'woocommerce' => static function (): bool { + // See . + return class_exists( 'WooCommerce' ); + }, ); foreach ( $plugins_with_integrations as $plugin_slug => $active_callback ) { diff --git a/plugins/web-worker-offloading/third-party/google-site-kit.php b/plugins/web-worker-offloading/third-party/google-site-kit.php new file mode 100644 index 0000000000..e9099a82c7 --- /dev/null +++ b/plugins/web-worker-offloading/third-party/google-site-kit.php @@ -0,0 +1,37 @@ +|mixed $configuration Configuration. + * @return array Configuration. + */ +function plwwo_google_site_kit_configure( $configuration ): array { + $configuration = (array) $configuration; + + $configuration['globalFns'][] = 'gtag'; // Because gtag() is defined in one script and called in another. + $configuration['forward'][] = 'dataLayer.push'; // Because the Partytown integration has this in its example config. + + return $configuration; +} +add_filter( 'plwwo_configuration', 'plwwo_google_site_kit_configure' ); + +plwwo_mark_scripts_for_offloading( + array( + 'google_gtagjs', + ) +); From c62e6a49adbc8c27141697df9c87c8e8754f2f71 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 20 Nov 2024 18:03:02 -0800 Subject: [PATCH 019/237] Integrate with Site Kit's consent mode --- .../third-party/google-site-kit.php | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/plugins/web-worker-offloading/third-party/google-site-kit.php b/plugins/web-worker-offloading/third-party/google-site-kit.php index e9099a82c7..2f4098259b 100644 --- a/plugins/web-worker-offloading/third-party/google-site-kit.php +++ b/plugins/web-worker-offloading/third-party/google-site-kit.php @@ -26,6 +26,10 @@ function plwwo_google_site_kit_configure( $configuration ): array { $configuration['globalFns'][] = 'gtag'; // Because gtag() is defined in one script and called in another. $configuration['forward'][] = 'dataLayer.push'; // Because the Partytown integration has this in its example config. + // See . + $configuration['mainWindowAccessors'][] = '_googlesitekitConsentCategoryMap'; + $configuration['mainWindowAccessors'][] = '_googlesitekitConsents'; + return $configuration; } add_filter( 'plwwo_configuration', 'plwwo_google_site_kit_configure' ); @@ -35,3 +39,23 @@ function plwwo_google_site_kit_configure( $configuration ): array { 'google_gtagjs', ) ); + +/** + * Filters inline script attributes to offload Rank Math's GTag script tag to Partytown. + * + * @since n.e.x.t + * @access private + * @link https://github.com/rankmath/seo-by-rank-math/blob/c78adba6f78079f27ff1430fabb75c6ac3916240/includes/modules/analytics/class-gtag.php#L169-L174 + * + * @param array|mixed $attributes Script attributes. + * @return array|mixed Filtered inline script attributes. + */ +function plwwo_google_site_kit_filter_inline_script_attributes( $attributes ) { + if ( isset( $attributes['id'] ) && 'google_gtagjs-js-consent-mode-data-layer' === $attributes['id'] ) { + wp_enqueue_script( 'web-worker-offloading' ); + $attributes['type'] = 'text/partytown'; + } + return $attributes; +} + +add_filter( 'wp_inline_script_attributes', 'plwwo_google_site_kit_filter_inline_script_attributes' ); From 0e3e00d58eead47858a3d227e437e712d71c9710 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 20 Nov 2024 18:32:47 -0800 Subject: [PATCH 020/237] Offload googlesitekit-consent-mode to worker as well --- plugins/web-worker-offloading/third-party/google-site-kit.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/web-worker-offloading/third-party/google-site-kit.php b/plugins/web-worker-offloading/third-party/google-site-kit.php index 2f4098259b..5b95aab283 100644 --- a/plugins/web-worker-offloading/third-party/google-site-kit.php +++ b/plugins/web-worker-offloading/third-party/google-site-kit.php @@ -37,6 +37,7 @@ function plwwo_google_site_kit_configure( $configuration ): array { plwwo_mark_scripts_for_offloading( array( 'google_gtagjs', + 'googlesitekit-consent-mode', ) ); From ad51973b0b87e9fe32b796d22046b7e618dff655 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Fri, 22 Nov 2024 10:04:42 +0530 Subject: [PATCH 021/237] Update priority for prime attachement cache --- plugins/auto-sizes/hooks.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index 73f1188efd..0557577a2d 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -183,11 +183,8 @@ function auto_sizes_prime_attachment_caches( $content ): string { return $content; } -/* - * The priority 6 is used to ensure the new filter runs right after the "wp-image-$attachment_id" class - * is added to the img tag at priority 5, allowing modifications that rely on this class being in place. - */ -add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 6 ); +// This must run before 'do_blocks', which runs at priority 9. +add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 9 ); /** * Filter the sizes attribute for images to improve the default calculation. From 6b8747cbd6302c2aba9d26a6dddf56e707063ff1 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Fri, 22 Nov 2024 10:10:43 +0530 Subject: [PATCH 022/237] Remove message --- plugins/auto-sizes/hooks.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index 0557577a2d..e48dcd9510 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -219,10 +219,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b $alignment = $block->attributes['align'] ?? ''; $width = $block->attributes['width'] ?? ''; - // Hypothetical function to calculate better sizes. - $sizes = auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width ); - - return $sizes; + return auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width ); }; // Hook this filter early, before default filters are run. From c06b4e7104bd281b013e8e319b3a52c69c3f4340 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Fri, 22 Nov 2024 10:11:49 +0530 Subject: [PATCH 023/237] Update function doc --- plugins/auto-sizes/hooks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index e48dcd9510..abe53b8360 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -251,7 +251,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 3 ); /** - * Hypothetical function to calculate better sizes. + * Modifies the sizes attribute of an image based on layout context. * * @param int $id The image id. * @param string $size The image size data. From 54532b54e705801c9b4b593a54df4a338bafc357 Mon Sep 17 00:00:00 2001 From: b1ink0 Date: Fri, 22 Nov 2024 19:15:58 +0530 Subject: [PATCH 024/237] Add logic for preventing re-request if dependencies not found --- plugins/performance-lab/includes/admin/plugins.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index f31f422ca5..49a3180b26 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -28,7 +28,7 @@ function perflab_query_plugin_info( string $plugin_slug ) { // Plugin was requested before and not found. return new WP_Error( 'plugin_not_found', - __( 'Plugin not found in previous API response.', 'performance-lab' ) + __( 'Plugin not found in API response.', 'performance-lab' ) ); } return $plugins[ $plugin_slug ]; // Return cached plugin info if found. @@ -88,10 +88,8 @@ function perflab_query_plugin_info( string $plugin_slug ) { } if ( ! isset( $all_performance_plugins[ $current_plugin_slug ] ) ) { - return new WP_Error( - 'plugin_not_found', - __( 'Plugin not found in WordPress.org API response.', 'performance-lab' ) - ); + $plugins[ $current_plugin_slug ] = false; + continue; } $plugin_data = $all_performance_plugins[ $current_plugin_slug ]; From c923c97c8bfa0b0a63adb70e52b0db6c609c0248 Mon Sep 17 00:00:00 2001 From: b1ink0 Date: Fri, 22 Nov 2024 19:36:40 +0530 Subject: [PATCH 025/237] Update the error message --- plugins/performance-lab/includes/admin/plugins.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index 49a3180b26..9395e4b954 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -28,7 +28,7 @@ function perflab_query_plugin_info( string $plugin_slug ) { // Plugin was requested before and not found. return new WP_Error( 'plugin_not_found', - __( 'Plugin not found in API response.', 'performance-lab' ) + __( 'Plugin not found in cached API response.', 'performance-lab' ) ); } return $plugins[ $plugin_slug ]; // Return cached plugin info if found. @@ -88,6 +88,7 @@ function perflab_query_plugin_info( string $plugin_slug ) { } if ( ! isset( $all_performance_plugins[ $current_plugin_slug ] ) ) { + // Cache the fact that the plugin was not found. $plugins[ $current_plugin_slug ] = false; continue; } From 8439753830ee0763c7b1e719d826f0c3f9f3c059 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 22 Nov 2024 11:40:27 -0800 Subject: [PATCH 026/237] Move lazy-load script func from hooks.php to helper.php --- plugins/image-prioritizer/helper.php | 19 +++++++++++++++++++ plugins/image-prioritizer/hooks.php | 19 ------------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 9096fac63b..6b6a0ee41d 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -76,3 +76,22 @@ function image_prioritizer_register_tag_visitors( OD_Tag_Visitor_Registry $regis $video_visitor = new Image_Prioritizer_Video_Tag_Visitor(); $registry->register( 'image-prioritizer/video', $video_visitor ); } + +/** + * Gets the script to lazy-load videos. + * + * Load a video and its poster image when it approaches the viewport using an IntersectionObserver. + * + * Handles 'autoplay' and 'preload' attributes accordingly. + * + * @since 0.2.0 + */ +function image_prioritizer_get_lazy_load_script(): string { + $script = file_get_contents( __DIR__ . sprintf( '/lazy-load%s.js', wp_scripts_get_suffix() ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- It's a local filesystem path not a remote request. + + if ( false === $script ) { + return ''; + } + + return $script; +} diff --git a/plugins/image-prioritizer/hooks.php b/plugins/image-prioritizer/hooks.php index 9e962ce8f1..62d2fd3158 100644 --- a/plugins/image-prioritizer/hooks.php +++ b/plugins/image-prioritizer/hooks.php @@ -11,22 +11,3 @@ } add_action( 'od_init', 'image_prioritizer_init' ); - -/** - * Gets the script to lazy-load videos. - * - * Load a video and its poster image when it approaches the viewport using an IntersectionObserver. - * - * Handles 'autoplay' and 'preload' attributes accordingly. - * - * @since 0.2.0 - */ -function image_prioritizer_get_lazy_load_script(): string { - $script = file_get_contents( __DIR__ . sprintf( '/lazy-load%s.js', wp_scripts_get_suffix() ) ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- It's a local filesystem path not a remote request. - - if ( false === $script ) { - return ''; - } - - return $script; -} From cee5c243d3c65396a5a65b25f5cc19420d5c6aa9 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 22 Nov 2024 14:05:44 -0800 Subject: [PATCH 027/237] Export webVitalsLibrarySrc to extensions --- plugins/optimization-detective/detect.js | 3 ++- plugins/optimization-detective/types.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index e5e6cc8ab3..ba5ea4b47d 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -347,7 +347,8 @@ export default async function detect( { extensions.set( extensionModuleUrl, extension ); // TODO: There should to be a way to pass additional args into the module. Perhaps extensionModuleUrls should be a mapping of URLs to args. It's important to pass webVitalsLibrarySrc to the extension so that onLCP, onCLS, or onINP can be obtained. if ( extension.initialize instanceof Function ) { - extension.initialize( { isDebug } ); + // TODO: Should initialize be an async function like finalize is? Probably not because we do not want to wait for it. + extension.initialize( { isDebug, webVitalsLibrarySrc } ); } } catch ( err ) { error( diff --git a/plugins/optimization-detective/types.ts b/plugins/optimization-detective/types.ts index fc4e375b60..ee1f73f8e3 100644 --- a/plugins/optimization-detective/types.ts +++ b/plugins/optimization-detective/types.ts @@ -30,6 +30,7 @@ export interface URLMetricGroupStatus { export type InitializeArgs = { readonly isDebug: boolean; + readonly webVitalsLibrarySrc: string; }; export type InitializeCallback = ( args: InitializeArgs ) => void; From da12ebd9172bb0fb57bcde8955797533b3125d5b Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 22 Nov 2024 14:14:13 -0800 Subject: [PATCH 028/237] Add client-side extension to Image Prioritizer to detect LCP external background images --- plugins/image-prioritizer/detect.js | 164 +++++++++++++++++++++++++++ plugins/image-prioritizer/helper.php | 47 ++++++++ plugins/image-prioritizer/hooks.php | 2 + webpack.config.js | 4 + 4 files changed, 217 insertions(+) create mode 100644 plugins/image-prioritizer/detect.js diff --git a/plugins/image-prioritizer/detect.js b/plugins/image-prioritizer/detect.js new file mode 100644 index 0000000000..eb591096ae --- /dev/null +++ b/plugins/image-prioritizer/detect.js @@ -0,0 +1,164 @@ +/** + * Image Prioritizer module for Optimization Detective + * + * TODO: Description. + */ + +const consoleLogPrefix = '[Image Prioritizer]'; + +/** + * Detected LCP external background image candidates. + * + * @type {Array<{url: string, tagName: string, parentTagName: string, id: string, className: string}>} + */ +const externalBackgroundImages = []; + +/** + * @typedef {import("web-vitals").LCPMetric} LCPMetric + * @typedef {import("../optimization-detective/types.ts").InitializeCallback} InitializeCallback + * @typedef {import("../optimization-detective/types.ts").InitializeArgs} InitializeArgs + * @typedef {import("../optimization-detective/types.ts").FinalizeArgs} FinalizeArgs + * @typedef {import("../optimization-detective/types.ts").FinalizeCallback} FinalizeCallback + */ + +/** + * Logs a message. + * + * @since n.e.x.t + * + * @param {...*} message + */ +function log( ...message ) { + // eslint-disable-next-line no-console + console.log( consoleLogPrefix, ...message ); +} + +/** + * Initializes extension. + * + * @since n.e.x.t + * + * @type {InitializeCallback} + * @param {InitializeArgs} args Args. + */ +export function initialize( { isDebug, webVitalsLibrarySrc } ) { + import( webVitalsLibrarySrc ).then( ( { onLCP } ) => { + onLCP( + ( /** @type {LCPMetric} */ metric ) => { + handleLCPMetric( metric, isDebug ); + }, + { + // This avoids needing to click to finalize LCP candidate. While this is helpful for testing, it also + // ensures that we always get an LCP candidate reported. Otherwise, the callback may never fire if the + // user never does a click or keydown, per . + reportAllChanges: true, + } + ); + } ); +} + +/** + * Gets the performance resource entry for a given URL. + * + * @since n.e.x.t + * + * @param {string} url - Resource URL. + * @return {PerformanceResourceTiming|null} Resource entry or null. + */ +function getPerformanceResourceByURL( url ) { + const entries = + /** @type PerformanceResourceTiming[] */ performance.getEntriesByType( + 'resource' + ); + for ( const entry of entries ) { + if ( entry.name === url ) { + return entry; + } + } + return null; +} + +/** + * Handles a new LCP metric being reported. + * + * @since n.e.x.t + * + * @param {LCPMetric} metric - LCP Metric. + * @param {boolean} isDebug - Whether in debug mode. + */ +function handleLCPMetric( metric, isDebug ) { + for ( const entry of metric.entries ) { + // Look only for LCP entries that have a URL and a corresponding element which is not an IMG or VIDEO. + if ( + ! entry.url || + ! ( entry.element instanceof HTMLElement ) || + entry.element instanceof HTMLImageElement || + entry.element instanceof HTMLVideoElement + ) { + continue; + } + + // Always ignore data: URLs. + if ( entry.url.startsWith( 'data:' ) ) { + continue; + } + + // Skip elements that have the background image defined inline. + // These are handled by Image_Prioritizer_Background_Image_Styled_Tag_Visitor. + if ( entry.element.style.backgroundImage ) { + continue; + } + + // Now only consider proceeding with the URL if its loading was initiated with CSS. + const resourceEntry = getPerformanceResourceByURL( entry.url ); + if ( ! resourceEntry || resourceEntry.initiatorType !== 'css' ) { + return; + } + + // The id and className allow the tag visitor to detect whether the element is still in the document. + // This is used instead of having a full XPath which is likely not available since the tag visitor would not + // know to return true for this element since it has no awareness of which elements have external backgrounds. + const externalBackgroundImage = { + url: entry.url, + tagName: entry.element.tagName, + parentTagName: entry.element.parentElement.tagName, + id: entry.id, + className: entry.element.className, + }; + + if ( isDebug ) { + log( + 'Detected external LCP background image:', + externalBackgroundImage + ); + } + + externalBackgroundImages.push( externalBackgroundImage ); + } +} + +/** + * Finalizes extension. + * + * @since n.e.x.t + * + * @type {FinalizeCallback} + * @param {FinalizeArgs} args Args. + */ +export async function finalize( { extendRootData, isDebug } ) { + if ( externalBackgroundImages.length === 0 ) { + return; + } + + // Get the last detected external background image which is going to be for the LCP element (or very likely will be). + const lcpElementExternalBackgroundImage = externalBackgroundImages.pop(); + + if ( isDebug ) { + log( + 'Sending external background image for LCP element:', + lcpElementExternalBackgroundImage + ); + } + + extendRootData( { lcpElementExternalBackgroundImage } ); +} diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 6b6a0ee41d..4c43c24841 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -95,3 +95,50 @@ function image_prioritizer_get_lazy_load_script(): string { return $script; } + +/** + * Filters the list of Optimization Detective extension module URLs to include the extension for Image Prioritizer. + * + * @since n.e.x.t + * + * @param string[]|mixed $extension_module_urls Extension module URLs. + * @return string[] Extension module URLs. + */ +function image_prioritizer_filter_extension_module_urls( $extension_module_urls ): array { + if ( ! is_array( $extension_module_urls ) ) { + $extension_module_urls = array(); + } + $extension_module_urls[] = add_query_arg( 'ver', IMAGE_PRIORITIZER_VERSION, plugin_dir_url( __FILE__ ) . sprintf( 'detect%s.js', wp_scripts_get_suffix() ) ); + return $extension_module_urls; +} + +/** + * 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 image_prioritizer_add_element_item_schema_properties( array $additional_properties ): array { + // TODO: Validation of the URL. + $additional_properties['lcpElementExternalBackgroundImage'] = array( + 'type' => 'object', + 'properties' => array_fill_keys( + array( + 'url', + 'tagName', + 'parentTagName', + 'id', + 'className', + ), + array( + // TODO: Add constraints on length. + // TODO: Add constraints on formats and patterns. + 'type' => 'string', + 'required' => true, + ) + ), + ); + return $additional_properties; +} diff --git a/plugins/image-prioritizer/hooks.php b/plugins/image-prioritizer/hooks.php index 62d2fd3158..7587e9e67b 100644 --- a/plugins/image-prioritizer/hooks.php +++ b/plugins/image-prioritizer/hooks.php @@ -11,3 +11,5 @@ } add_action( 'od_init', 'image_prioritizer_init' ); +add_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ); +add_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ); diff --git a/webpack.config.js b/webpack.config.js index 409f70c37b..38572a5be8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -97,6 +97,10 @@ const imagePrioritizer = ( env ) => { plugins: [ new CopyWebpackPlugin( { patterns: [ + { + from: `${ pluginDir }/detect.js`, + to: `${ pluginDir }/detect.min.js`, + }, { from: `${ pluginDir }/lazy-load.js`, to: `${ pluginDir }/lazy-load.min.js`, From cf4b6e924b037d5c6bf0704848eb361e2fb9341c Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 22 Nov 2024 18:05:52 -0800 Subject: [PATCH 029/237] Add preload links for external background images logged in URL Metrics --- ...er-background-image-styled-tag-visitor.php | 132 ++++++++++++++++-- plugins/image-prioritizer/detect.js | 41 +++++- plugins/image-prioritizer/helper.php | 35 +++-- 3 files changed, 177 insertions(+), 31 deletions(-) diff --git a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php index 798d42f1f6..c807ddc80c 100644 --- a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php +++ b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php @@ -14,11 +14,25 @@ /** * Tag visitor that optimizes elements with background-image styles. * + * @phpstan-type LcpElementExternalBackgroundImage array{ + * url: non-empty-string, + * tag: non-empty-string, + * id: string|null, + * class: string|null, + * } + * * @since 0.1.0 * @access private */ final class Image_Prioritizer_Background_Image_Styled_Tag_Visitor extends Image_Prioritizer_Tag_Visitor { + /** + * Tuples of URL Metric group and the common LCP element external background image. + * + * @var array + */ + private $group_common_lcp_element_external_background_images = array(); + /** * Visits a tag. * @@ -49,6 +63,7 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { } if ( is_null( $background_image_url ) ) { + $this->maybe_preload_external_lcp_background_image( $context ); return false; } @@ -56,21 +71,114 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { // If this element is the LCP (for a breakpoint group), add a preload link for it. foreach ( $context->url_metric_group_collection->get_groups_by_lcp_element( $xpath ) as $group ) { - $link_attributes = array( + $this->add_preload_link( $context->link_collection, $group, $background_image_url ); + } + + return true; + } + + /** + * Gets the common LCP element external background image for a URL Metric group. + * + * @since n.e.x.t + * + * @param OD_URL_Metric_Group $group Group. + * @return LcpElementExternalBackgroundImage|null + */ + private function get_common_lcp_element_external_background_image( OD_URL_Metric_Group $group ): ?array { + + // If the group is not fully populated, we don't have enough URL Metrics to reliably know whether the background image is consistent across page loads. + // This is intentionally not using $group->is_complete() because we still will use stale URL Metrics in the calculation. + // TODO: There should be a $group->get_sample_size() method. + if ( $group->count() !== od_get_url_metrics_breakpoint_sample_size() ) { + return null; + } + + $previous_lcp_element_external_background_image = null; + foreach ( $group as $url_metric ) { + /** + * Stored data. + * + * @var LcpElementExternalBackgroundImage|null $lcp_element_external_background_image + */ + $lcp_element_external_background_image = $url_metric->get( 'lcpElementExternalBackgroundImage' ); + if ( ! is_array( $lcp_element_external_background_image ) ) { + return null; + } + if ( null !== $previous_lcp_element_external_background_image && $previous_lcp_element_external_background_image !== $lcp_element_external_background_image ) { + return null; + } + $previous_lcp_element_external_background_image = $lcp_element_external_background_image; + } + + return $previous_lcp_element_external_background_image; + } + + /** + * Maybe preloads external background image. + * + * @since n.e.x.t + * + * @param OD_Tag_Visitor_Context $context Context. + */ + private function maybe_preload_external_lcp_background_image( OD_Tag_Visitor_Context $context ): void { + static $did_collect_data = false; + if ( false === $did_collect_data ) { + foreach ( $context->url_metric_group_collection as $group ) { + $common = $this->get_common_lcp_element_external_background_image( $group ); + if ( is_array( $common ) ) { + $this->group_common_lcp_element_external_background_images[] = array( $group, $common ); + } + } + $did_collect_data = true; + } + + // There are no common LCP background images, so abort. + if ( count( $this->group_common_lcp_element_external_background_images ) === 0 ) { + return; + } + + $processor = $context->processor; + $tag_name = strtoupper( (string) $processor->get_tag() ); + foreach ( $this->group_common_lcp_element_external_background_images as $i => list( $group, $common ) ) { + if ( + // Note that the browser may send a lower-case tag name in the case of XHTML or embedded SVG/MathML, but + // the HTML Tag Processor is currently normalizing to all upper-case. The HTML Processor on the other + // hand may return the expected case. + strtoupper( $common['tag'] ) === $tag_name + && + $processor->get_attribute( 'id' ) === $common['id'] // May be checking equality with null. + && + $processor->get_attribute( 'class' ) === $common['class'] // May be checking equality with null. + ) { + $this->add_preload_link( $context->link_collection, $group, $common['url'] ); + + // Now that the preload link has been added, eliminate the entry to stop looking for it while iterating over the rest of the document. + unset( $this->group_common_lcp_element_external_background_images[ $i ] ); + } + } + } + + /** + * Adds an image preload link for the group. + * + * @since n.e.x.t + * + * @param OD_Link_Collection $link_collection Link collection. + * @param OD_URL_Metric_Group $group URL Metric group. + * @param non-empty-string $url Image URL. + */ + private function add_preload_link( OD_Link_Collection $link_collection, OD_URL_Metric_Group $group, string $url ): void { + $link_collection->add_link( + array( 'rel' => 'preload', 'fetchpriority' => 'high', 'as' => 'image', - 'href' => $background_image_url, + 'href' => $url, 'media' => 'screen', - ); - - $context->link_collection->add_link( - $link_attributes, - $group->get_minimum_viewport_width(), - $group->get_maximum_viewport_width() - ); - } - - return true; + ), + $group->get_minimum_viewport_width(), + $group->get_maximum_viewport_width() + ); } } diff --git a/plugins/image-prioritizer/detect.js b/plugins/image-prioritizer/detect.js index eb591096ae..73f61fd69a 100644 --- a/plugins/image-prioritizer/detect.js +++ b/plugins/image-prioritizer/detect.js @@ -9,7 +9,7 @@ const consoleLogPrefix = '[Image Prioritizer]'; /** * Detected LCP external background image candidates. * - * @type {Array<{url: string, tagName: string, parentTagName: string, id: string, className: string}>} + * @type {Array<{url: string, tag: string, id: string, class: string}>} */ const externalBackgroundImages = []; @@ -111,19 +111,48 @@ function handleLCPMetric( metric, isDebug ) { // Now only consider proceeding with the URL if its loading was initiated with CSS. const resourceEntry = getPerformanceResourceByURL( entry.url ); - if ( ! resourceEntry || resourceEntry.initiatorType !== 'css' ) { + if ( + ! resourceEntry || + ! [ 'css', 'link' ].includes( resourceEntry.initiatorType ) // TODO: When is it css and when is it link? + ) { + if ( isDebug ) { + // eslint-disable-next-line no-console + console.warn( + consoleLogPrefix, + 'Skipped considering URL do due to resource initiatorType:', + entry.url, + resourceEntry.initiatorType + ); + } + return; + } + + // Skip URLs that are excessively long. This is the maxLength defined in image_prioritizer_add_element_item_schema_properties(). + if ( entry.url.length > 500 ) { + if ( isDebug ) { + log( `Skipping very long URL: ${ entry.url }` ); + } return; } + // Note that getAttribute() is used instead of properties so that null can be returned in case of an absent attribute. + let id = entry.element.getAttribute( 'id' ); + if ( null !== id ) { + id = id.substring( 0, 100 ); // This is the maxLength defined in image_prioritizer_add_element_item_schema_properties(). + } + let className = entry.element.getAttribute( 'class' ); + if ( null !== className ) { + className = className.substring( 0, 500 ); // This is the maxLength defined in image_prioritizer_add_element_item_schema_properties(). + } + // The id and className allow the tag visitor to detect whether the element is still in the document. // This is used instead of having a full XPath which is likely not available since the tag visitor would not // know to return true for this element since it has no awareness of which elements have external backgrounds. const externalBackgroundImage = { url: entry.url, - tagName: entry.element.tagName, - parentTagName: entry.element.parentElement.tagName, - id: entry.id, - className: entry.element.className, + tag: entry.element.tagName, + id, + class: className, }; if ( isDebug ) { diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 4c43c24841..74fb9a3b06 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -124,20 +124,29 @@ function image_prioritizer_add_element_item_schema_properties( array $additional // TODO: Validation of the URL. $additional_properties['lcpElementExternalBackgroundImage'] = array( 'type' => 'object', - 'properties' => array_fill_keys( - array( - 'url', - 'tagName', - 'parentTagName', - 'id', - 'className', + 'properties' => array( + 'url' => array( + 'type' => 'string', + 'format' => 'uri', + 'required' => true, + 'maxLength' => 500, // Image URLs can be quite long. + ), + 'tag' => array( + 'type' => 'string', + 'required' => true, + 'minLength' => 1, + 'pattern' => '^[a-zA-Z0-9\-]+$', // Technically emoji can be allowed in a custom element's tag name, but this is not supported here. + ), + 'id' => array( + 'type' => array( 'string', 'null' ), + 'maxLength' => 100, // A reasonable upper-bound length for a long ID. The client will must truncate anything longer. + 'required' => true, + ), + 'class' => array( + 'type' => array( 'string', 'null' ), + 'maxLength' => 500, // There can be a ton of class names on an element. The client will must truncate anything longer. + 'required' => true, ), - array( - // TODO: Add constraints on length. - // TODO: Add constraints on formats and patterns. - 'type' => 'string', - 'required' => true, - ) ), ); return $additional_properties; From a75b94f803ff51b0eacf0122aa011d04b76db1d1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 22 Nov 2024 18:13:20 -0800 Subject: [PATCH 030/237] Prevent submitting URL Metric if viewport size changed --- plugins/optimization-detective/detect.js | 25 ++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index ba5ea4b47d..f7382a8c7e 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -127,6 +127,12 @@ function recursiveFreeze( obj ) { Object.freeze( obj ); } +// This needs to be captured early in case the user later resizes the window. +const viewport = { + width: win.innerWidth, + height: win.innerHeight, +}; + /** * URL Metric being assembled for submission. * @@ -442,10 +448,7 @@ export default async function detect( { urlMetric = { url: currentUrl, - viewport: { - width: win.innerWidth, - height: win.innerHeight, - }, + viewport, elements: [], }; @@ -506,6 +509,20 @@ export default async function detect( { ); } ); + // 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 ( + window.innerWidth !== urlMetric.viewport.width || + window.innerHeight !== urlMetric.viewport.height + ) { + if ( isDebug ) { + log( + 'Aborting URL Metric collection due to viewport size change.' + ); + } + return; + } + if ( extensions.size > 0 ) { for ( const [ extensionModuleUrl, From f2959cd60d32c9c5bfddd6d5e0575098c27f8af9 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 24 Nov 2024 20:29:41 -0800 Subject: [PATCH 031/237] Introduce od_store_url_metric_validity filter so Image Prioritizer can validate background-image URL --- plugins/image-prioritizer/helper.php | 63 ++++++++++++++++++- plugins/image-prioritizer/hooks.php | 1 + .../image-prioritizer/tests/test-helper.php | 2 +- .../storage/rest-api.php | 32 +++++++++- 4 files changed, 94 insertions(+), 4 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 74fb9a3b06..dccbf82760 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -18,7 +18,7 @@ * @param string $optimization_detective_version Current version of the optimization detective plugin. */ function image_prioritizer_init( string $optimization_detective_version ): void { - $required_od_version = '0.7.0'; + $required_od_version = '0.9.0'; if ( ! version_compare( (string) strtok( $optimization_detective_version, '-' ), $required_od_version, '>=' ) ) { add_action( 'admin_notices', @@ -121,7 +121,6 @@ function image_prioritizer_filter_extension_module_urls( $extension_module_urls * @return array Additional properties. */ function image_prioritizer_add_element_item_schema_properties( array $additional_properties ): array { - // TODO: Validation of the URL. $additional_properties['lcpElementExternalBackgroundImage'] = array( 'type' => 'object', 'properties' => array( @@ -151,3 +150,63 @@ function image_prioritizer_add_element_item_schema_properties( array $additional ); return $additional_properties; } + +/** + * Validates that the provided background image URL is valid. + * + * @since n.e.x.t + * + * @param bool|WP_Error|mixed $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. + * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. + * @return bool|WP_Error Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. + */ +function image_prioritizer_filter_store_url_metric_validity( $validity, OD_Strict_URL_Metric $url_metric ) { + if ( ! is_bool( $validity ) && ! ( $validity instanceof WP_Error ) ) { + $validity = (bool) $validity; + } + + $data = $url_metric->get( 'lcpElementExternalBackgroundImage' ); + if ( ! is_array( $data ) ) { + return $validity; + } + + $r = wp_safe_remote_head( + $data['url'], + array( + 'redirection' => 3, // Allow up to 3 redirects. + ) + ); + if ( $r instanceof WP_Error ) { + return new WP_Error( + WP_DEBUG ? $r->get_error_code() : 'head_request_failure', + __( 'HEAD request for background image URL failed.', 'image-prioritizer' ) . ( WP_DEBUG ? ' ' . $r->get_error_message() : '' ), + array( + 'code' => 500, + ) + ); + } + $response_code = wp_remote_retrieve_response_code( $r ); + if ( $response_code < 200 || $response_code >= 400 ) { + return new WP_Error( + 'background_image_response_not_ok', + __( 'HEAD request for background image URL did not return with a success status code.', 'image-prioritizer' ), + array( + 'code' => WP_DEBUG ? $response_code : 400, + ) + ); + } + + $content_type = wp_remote_retrieve_header( $r, 'Content-Type' ); + if ( ! is_string( $content_type ) || ! str_starts_with( $content_type, 'image/' ) ) { + return new WP_Error( + 'background_image_response_not_image', + __( 'HEAD request for background image URL did not return an image Content-Type.', 'image-prioritizer' ), + array( + 'code' => 400, + ) + ); + } + + // TODO: Check for the Content-Length and return invalid if it is gigantic? + return $validity; +} diff --git a/plugins/image-prioritizer/hooks.php b/plugins/image-prioritizer/hooks.php index 7587e9e67b..4a47f35647 100644 --- a/plugins/image-prioritizer/hooks.php +++ b/plugins/image-prioritizer/hooks.php @@ -13,3 +13,4 @@ add_action( 'od_init', 'image_prioritizer_init' ); add_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ); add_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ); +add_filter( 'od_store_url_metric_validity', 'image_prioritizer_filter_store_url_metric_validity', 10, 2 ); diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index ac67ac867e..867c70b0e5 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -20,7 +20,7 @@ public function data_provider_to_test_image_prioritizer_init(): array { 'expected' => false, ), 'with_new_version' => array( - 'version' => '0.7.0', + 'version' => '99.0.0', 'expected' => true, ), ); diff --git a/plugins/optimization-detective/storage/rest-api.php b/plugins/optimization-detective/storage/rest-api.php index fe622be468..7d8be56df0 100644 --- a/plugins/optimization-detective/storage/rest-api.php +++ b/plugins/optimization-detective/storage/rest-api.php @@ -84,7 +84,7 @@ function od_register_endpoint(): void { return new WP_Error( 'url_metric_storage_locked', __( 'URL Metric storage is presently locked for the current IP.', 'optimization-detective' ), - array( 'status' => 403 ) + array( 'status' => 403 ) // TODO: Consider 423 Locked status code. ); } return true; @@ -152,6 +152,7 @@ function od_handle_rest_request( WP_REST_Request $request ) { $request->get_param( 'viewport' )['width'] ); } catch ( InvalidArgumentException $exception ) { + // Note: This should never happen because an exception only occurs if a viewport width is less than zero, and the JSON Schema enforces that the viewport.width have a minimum of zero. return new WP_Error( 'invalid_viewport_width', $exception->getMessage() ); } if ( $url_metric_group->is_complete() ) { @@ -197,6 +198,35 @@ function od_handle_rest_request( WP_REST_Request $request ) { ); } + /** + * Filters whether a URL Metric is valid for storage. + * + * This allows for custom validation constraints to be applied beyond what can be expressed in JSON Schema. This is + * also necessary because the 'validate_callback' key in a JSON Schema is not respected when gathering the REST API + * endpoint args via the {@see rest_get_endpoint_args_for_schema()} function. Besides this, the REST API doesn't + * support 'validate_callback' for any nested arguments in any case, meaning that custom constraints would be able + * to be applied to multidimensional objects, such as the items inside 'elements'. + * + * This filter only applies when storing a URL Metric via the REST API. It does not run when a stored URL Metric + * loaded from the od_url_metric post type. This means that validation logic enforced via this filter can be more + * expensive, such as doing filesystem checks or HTTP requests. + * + * @since n.e.x.t + * + * @param bool|WP_Error $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. + * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. + */ + $validity = apply_filters( 'od_store_url_metric_validity', true, $url_metric ); + if ( false === $validity || ( $validity instanceof WP_Error && $validity->has_errors() ) ) { + if ( false === $validity ) { + $validity = new WP_Error( 'invalid_url_metric', __( 'Validity of URL Metric was rejected by filter.', 'optimization-detective' ) ); + } + if ( ! isset( $validity->error_data['code'] ) ) { + $validity->error_data['code'] = 400; + } + return $validity; + } + // TODO: This should be changed from store_url_metric($slug, $url_metric) instead be update_post( $slug, $group_collection ). As it stands, store_url_metric() is duplicating logic here. $result = OD_URL_Metrics_Post_Type::store_url_metric( $request->get_param( 'slug' ), From f4b766aba66257028e8d0788436909d3116a0b82 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 24 Nov 2024 20:42:50 -0800 Subject: [PATCH 032/237] Implement get_sample_size() and get_freshness_ttl() on OD_URL_Metric_Group --- ...er-background-image-styled-tag-visitor.php | 3 +-- .../class-od-url-metric-group.php | 23 +++++++++++++++++++ .../tests/test-class-od-url-metrics-group.php | 4 ++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php index c807ddc80c..3a901a1f7c 100644 --- a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php +++ b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php @@ -89,8 +89,7 @@ private function get_common_lcp_element_external_background_image( OD_URL_Metric // If the group is not fully populated, we don't have enough URL Metrics to reliably know whether the background image is consistent across page loads. // This is intentionally not using $group->is_complete() because we still will use stale URL Metrics in the calculation. - // TODO: There should be a $group->get_sample_size() method. - if ( $group->count() !== od_get_url_metrics_breakpoint_sample_size() ) { + if ( $group->count() !== $group->get_sample_size() ) { return null; } diff --git a/plugins/optimization-detective/class-od-url-metric-group.php b/plugins/optimization-detective/class-od-url-metric-group.php index 963f5b8840..94ab910309 100644 --- a/plugins/optimization-detective/class-od-url-metric-group.php +++ b/plugins/optimization-detective/class-od-url-metric-group.php @@ -163,6 +163,29 @@ public function get_maximum_viewport_width(): int { return $this->maximum_viewport_width; } + /** + * Gets the sample size for URL Metrics for a given breakpoint. + * + * @todo Eliminate in favor of readonly public property. + * @phpstan-return positive-int + * @return int Sample size. + */ + public function get_sample_size(): int { + return $this->sample_size; + } + + /** + * Gets the freshness age (TTL) for a given URL Metric. + * + * @todo Eliminate in favor of readonly public property. + * + * @phpstan-return 0|positive-int + * @return int Freshness age. + */ + public function get_freshness_ttl(): int { + return $this->freshness_ttl; + } + /** * Checks whether the provided viewport width is within the minimum/maximum range for * diff --git a/plugins/optimization-detective/tests/test-class-od-url-metrics-group.php b/plugins/optimization-detective/tests/test-class-od-url-metrics-group.php index ef59513515..0afc78b485 100644 --- a/plugins/optimization-detective/tests/test-class-od-url-metrics-group.php +++ b/plugins/optimization-detective/tests/test-class-od-url-metrics-group.php @@ -95,6 +95,8 @@ public function data_provider_test_construction(): array { * @covers ::__construct * @covers ::get_minimum_viewport_width * @covers ::get_maximum_viewport_width + * @covers ::get_sample_size + * @covers ::get_freshness_ttl * @covers ::getIterator * @covers ::count * @@ -116,6 +118,8 @@ public function test_construction( array $url_metrics, int $minimum_viewport_wid $this->assertCount( count( $url_metrics ), $group ); $this->assertSame( $minimum_viewport_width, $group->get_minimum_viewport_width() ); $this->assertSame( $maximum_viewport_width, $group->get_maximum_viewport_width() ); + $this->assertSame( $sample_size, $group->get_sample_size() ); + $this->assertSame( $freshness_ttl, $group->get_freshness_ttl() ); $this->assertCount( count( $url_metrics ), $group ); $this->assertSame( $url_metrics, iterator_to_array( $group ) ); } From dcb7a5cee3712282c81d93ede00980d5241107e6 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 24 Nov 2024 20:46:09 -0800 Subject: [PATCH 033/237] Eliminate use of static variable --- ...ge-prioritizer-background-image-styled-tag-visitor.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php index 3a901a1f7c..f59f04f9d1 100644 --- a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php +++ b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php @@ -31,7 +31,7 @@ final class Image_Prioritizer_Background_Image_Styled_Tag_Visitor extends Image_ * * @var array */ - private $group_common_lcp_element_external_background_images = array(); + private $group_common_lcp_element_external_background_images; /** * Visits a tag. @@ -121,15 +121,15 @@ private function get_common_lcp_element_external_background_image( OD_URL_Metric * @param OD_Tag_Visitor_Context $context Context. */ private function maybe_preload_external_lcp_background_image( OD_Tag_Visitor_Context $context ): void { - static $did_collect_data = false; - if ( false === $did_collect_data ) { + // Gather the tuples of URL Metric group and the common LCP element external background image. + if ( ! is_array( $this->group_common_lcp_element_external_background_images ) ) { + $this->group_common_lcp_element_external_background_images = array(); foreach ( $context->url_metric_group_collection as $group ) { $common = $this->get_common_lcp_element_external_background_image( $group ); if ( is_array( $common ) ) { $this->group_common_lcp_element_external_background_images[] = array( $group, $common ); } } - $did_collect_data = true; } // There are no common LCP background images, so abort. From f078723c8d2068f5bb94ed24b98ac14c9db95efc Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 24 Nov 2024 20:54:05 -0800 Subject: [PATCH 034/237] Write up description, add warn helper function, and remove todo --- plugins/image-prioritizer/detect.js | 32 +++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/plugins/image-prioritizer/detect.js b/plugins/image-prioritizer/detect.js index 73f61fd69a..7cf60ba829 100644 --- a/plugins/image-prioritizer/detect.js +++ b/plugins/image-prioritizer/detect.js @@ -1,7 +1,12 @@ /** * Image Prioritizer module for Optimization Detective * - * TODO: Description. + * This extension to Optimization Detective captures the LCP element's CSS background image which is not defined with + * an inline style attribute but rather in either an external stylesheet loaded with a LINK tag or by stylesheet in + * a STYLE element. The URL for this LCP background image and the tag's name, ID, and class are all amended to the + * stored URL Metric so that a responsive preload link with fetchpriority=high will be added for that background image + * once a URL Metric group is fully populated with URL Metrics that all agree on that being the LCP image, and if the + * document has a tag with the same name, ID, and class. */ const consoleLogPrefix = '[Image Prioritizer]'; @@ -33,6 +38,18 @@ function log( ...message ) { console.log( consoleLogPrefix, ...message ); } +/** + * Logs a warning. + * + * @since n.e.x.t + * + * @param {...*} message + */ +function warn( ...message ) { + // eslint-disable-next-line no-console + console.warn( consoleLogPrefix, ...message ); +} + /** * Initializes extension. * @@ -109,19 +126,16 @@ function handleLCPMetric( metric, isDebug ) { continue; } - // Now only consider proceeding with the URL if its loading was initiated with CSS. + // Now only consider proceeding with the URL if its loading was initiated with stylesheet or preload link. const resourceEntry = getPerformanceResourceByURL( entry.url ); if ( ! resourceEntry || - ! [ 'css', 'link' ].includes( resourceEntry.initiatorType ) // TODO: When is it css and when is it link? + ! [ 'css', 'link' ].includes( resourceEntry.initiatorType ) ) { if ( isDebug ) { - // eslint-disable-next-line no-console - console.warn( - consoleLogPrefix, - 'Skipped considering URL do due to resource initiatorType:', - entry.url, - resourceEntry.initiatorType + warn( + `Skipped considering URL (${ entry.url }) due to unexpected performance resource timing entry:`, + resourceEntry ); } return; From d3339f0ac58d0e57f0c1ee2988fdae2628c2873b Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 24 Nov 2024 21:39:14 -0800 Subject: [PATCH 035/237] Let extension initialize functions always be async --- plugins/embed-optimizer/detect.js | 2 +- plugins/image-prioritizer/detect.js | 27 ++++++++++----------- plugins/optimization-detective/detect.js | 30 +++++++++++++++++++++--- plugins/optimization-detective/types.ts | 2 +- 4 files changed, 42 insertions(+), 19 deletions(-) diff --git a/plugins/embed-optimizer/detect.js b/plugins/embed-optimizer/detect.js index 1aab3d0838..38068387b0 100644 --- a/plugins/embed-optimizer/detect.js +++ b/plugins/embed-optimizer/detect.js @@ -51,7 +51,7 @@ const loadedElementContentRects = new Map(); * @type {InitializeCallback} * @param {InitializeArgs} args Args. */ -export function initialize( { isDebug } ) { +export async function initialize( { isDebug } ) { /** @type NodeListOf */ const embedWrappers = document.querySelectorAll( '.wp-block-embed > .wp-block-embed__wrapper[data-od-xpath]' diff --git a/plugins/image-prioritizer/detect.js b/plugins/image-prioritizer/detect.js index 7cf60ba829..8f548fd8e5 100644 --- a/plugins/image-prioritizer/detect.js +++ b/plugins/image-prioritizer/detect.js @@ -58,20 +58,19 @@ function warn( ...message ) { * @type {InitializeCallback} * @param {InitializeArgs} args Args. */ -export function initialize( { isDebug, webVitalsLibrarySrc } ) { - import( webVitalsLibrarySrc ).then( ( { onLCP } ) => { - onLCP( - ( /** @type {LCPMetric} */ metric ) => { - handleLCPMetric( metric, isDebug ); - }, - { - // This avoids needing to click to finalize LCP candidate. While this is helpful for testing, it also - // ensures that we always get an LCP candidate reported. Otherwise, the callback may never fire if the - // user never does a click or keydown, per . - reportAllChanges: true, - } - ); - } ); +export async function initialize( { isDebug, webVitalsLibrarySrc } ) { + const { onLCP } = await import( webVitalsLibrarySrc ); + onLCP( + ( /** @type {LCPMetric} */ metric ) => { + handleLCPMetric( metric, isDebug ); + }, + { + // This avoids needing to click to finalize LCP candidate. While this is helpful for testing, it also + // ensures that we always get an LCP candidate reported. Otherwise, the callback may never fire if the + // user never does a click or keydown, per . + reportAllChanges: true, + } + ); } /** diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index f7382a8c7e..d21af2742a 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -346,15 +346,23 @@ export default async function detect( { /** @type {Map} */ const extensions = new Map(); + + /** @type {Promise[]} */ + const extensionInitializePromises = []; + for ( const extensionModuleUrl of extensionModuleUrls ) { try { /** @type {Extension} */ const extension = await import( extensionModuleUrl ); extensions.set( extensionModuleUrl, extension ); - // TODO: There should to be a way to pass additional args into the module. Perhaps extensionModuleUrls should be a mapping of URLs to args. It's important to pass webVitalsLibrarySrc to the extension so that onLCP, onCLS, or onINP can be obtained. + // TODO: There should to be a way to pass additional args into the module. Perhaps extensionModuleUrls should be a mapping of URLs to args. if ( extension.initialize instanceof Function ) { - // TODO: Should initialize be an async function like finalize is? Probably not because we do not want to wait for it. - extension.initialize( { isDebug, webVitalsLibrarySrc } ); + extensionInitializePromises.push( + extension.initialize( { + isDebug, + webVitalsLibrarySrc, + } ) + ); } } catch ( err ) { error( @@ -364,6 +372,22 @@ export default async function detect( { } } + // Wait for all extensions to finish initializing. + const settledInitializePromises = await Promise.allSettled( + extensionInitializePromises + ); + for ( const [ + i, + settledInitializePromise, + ] of settledInitializePromises.entries() ) { + if ( settledInitializePromise.status === 'rejected' ) { + error( + `Failed to initialize extension '${ extensionModuleUrls[ i ] }':`, + settledInitializePromise.reason + ); + } + } + const breadcrumbedElements = doc.body.querySelectorAll( '[data-od-xpath]' ); /** @type {Map} */ diff --git a/plugins/optimization-detective/types.ts b/plugins/optimization-detective/types.ts index ee1f73f8e3..cafd6f9d3a 100644 --- a/plugins/optimization-detective/types.ts +++ b/plugins/optimization-detective/types.ts @@ -33,7 +33,7 @@ export type InitializeArgs = { readonly webVitalsLibrarySrc: string; }; -export type InitializeCallback = ( args: InitializeArgs ) => void; +export type InitializeCallback = ( args: InitializeArgs ) => Promise< void >; export type FinalizeArgs = { readonly getRootData: () => URLMetric; From ecac93772467e960d6b97ad5b4795d8057e6a771 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 24 Nov 2024 22:06:25 -0800 Subject: [PATCH 036/237] Improve logic for initializing and finalizing extensions --- plugins/optimization-detective/detect.js | 49 +++++++++++++++++++----- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index d21af2742a..53feba11df 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -350,6 +350,9 @@ export default async function detect( { /** @type {Promise[]} */ const extensionInitializePromises = []; + /** @type {string[]} */ + const initializingExtensionModuleUrls = []; + for ( const extensionModuleUrl of extensionModuleUrls ) { try { /** @type {Extension} */ @@ -363,10 +366,11 @@ export default async function detect( { webVitalsLibrarySrc, } ) ); + initializingExtensionModuleUrls.push( extensionModuleUrl ); } } catch ( err ) { error( - `Failed to initialize extension '${ extensionModuleUrl }':`, + `Failed to start initializing extension '${ extensionModuleUrl }':`, err ); } @@ -382,7 +386,7 @@ export default async function detect( { ] of settledInitializePromises.entries() ) { if ( settledInitializePromise.status === 'rejected' ) { error( - `Failed to initialize extension '${ extensionModuleUrls[ i ] }':`, + `Failed to initialize extension '${ initializingExtensionModuleUrls[ i ] }':`, settledInitializePromise.reason ); } @@ -548,27 +552,52 @@ export default async function detect( { } if ( extensions.size > 0 ) { + /** @type {Promise[]} */ + const extensionFinalizePromises = []; + + /** @type {string[]} */ + const finalizingExtensionModuleUrls = []; + for ( const [ extensionModuleUrl, extension, ] of extensions.entries() ) { if ( extension.finalize instanceof Function ) { try { - await extension.finalize( { - isDebug, - getRootData, - getElementData, - extendElementData, - extendRootData, - } ); + extensionFinalizePromises.push( + extension.finalize( { + isDebug, + getRootData, + getElementData, + extendElementData, + extendRootData, + } ) + ); + finalizingExtensionModuleUrls.push( extensionModuleUrl ); } catch ( err ) { error( - `Unable to finalize module '${ extensionModuleUrl }':`, + `Unable to start finalizing extension '${ extensionModuleUrl }':`, err ); } } } + + // Wait for all extensions to finish finalizing. + const settledFinalizePromises = await Promise.allSettled( + extensionFinalizePromises + ); + for ( const [ + i, + settledFinalizePromise, + ] of settledFinalizePromises.entries() ) { + if ( settledFinalizePromise.status === 'rejected' ) { + error( + `Failed to finalize extension '${ finalizingExtensionModuleUrls[ i ] }':`, + settledFinalizePromise.reason + ); + } + } } // Even though the server may reject the REST API request, we still have to set the storage lock From 494e4aa2b2fb9610da587466c5745b834f902fb2 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 24 Nov 2024 22:14:51 -0800 Subject: [PATCH 037/237] Add missing since tags --- .../class-od-url-metric-group.php | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/class-od-url-metric-group.php b/plugins/optimization-detective/class-od-url-metric-group.php index 94ab910309..eb1f29db3d 100644 --- a/plugins/optimization-detective/class-od-url-metric-group.php +++ b/plugins/optimization-detective/class-od-url-metric-group.php @@ -24,6 +24,8 @@ final class OD_URL_Metric_Group implements IteratorAggregate, Countable, JsonSer /** * URL Metrics. * + * @since 0.1.0 + * * @var OD_URL_Metric[] */ private $url_metrics; @@ -31,6 +33,8 @@ final class OD_URL_Metric_Group implements IteratorAggregate, Countable, JsonSer /** * Minimum possible viewport width for the group (inclusive). * + * @since 0.1.0 + * * @var int * @phpstan-var 0|positive-int */ @@ -39,6 +43,8 @@ final class OD_URL_Metric_Group implements IteratorAggregate, Countable, JsonSer /** * Maximum possible viewport width for the group (inclusive). * + * @since 0.1.0 + * * @var int * @phpstan-var positive-int */ @@ -47,6 +53,8 @@ final class OD_URL_Metric_Group implements IteratorAggregate, Countable, JsonSer /** * Sample size for URL Metrics for a given breakpoint. * + * @since 0.1.0 + * * @var int * @phpstan-var positive-int */ @@ -55,6 +63,8 @@ final class OD_URL_Metric_Group implements IteratorAggregate, Countable, JsonSer /** * Freshness age (TTL) for a given URL Metric. * + * @since 0.1.0 + * * @var int * @phpstan-var 0|positive-int */ @@ -63,6 +73,8 @@ final class OD_URL_Metric_Group implements IteratorAggregate, Countable, JsonSer /** * Collection that this instance belongs to. * + * @since 0.3.0 + * * @var OD_URL_Metric_Group_Collection|null */ private $collection; @@ -70,6 +82,8 @@ final class OD_URL_Metric_Group implements IteratorAggregate, Countable, JsonSer /** * Result cache. * + * @since 0.3.0 + * * @var array{ * get_lcp_element?: OD_Element|null, * is_complete?: bool, @@ -146,6 +160,8 @@ public function __construct( array $url_metrics, int $minimum_viewport_width, in /** * Gets the minimum possible viewport width (inclusive). * + * @since 0.1.0 + * * @todo Eliminate in favor of readonly public property. * @return int<0, max> Minimum viewport width. */ @@ -156,6 +172,8 @@ public function get_minimum_viewport_width(): int { /** * Gets the maximum possible viewport width (inclusive). * + * @since 0.1.0 + * * @todo Eliminate in favor of readonly public property. * @return int<1, max> Minimum viewport width. */ @@ -166,6 +184,8 @@ public function get_maximum_viewport_width(): int { /** * Gets the sample size for URL Metrics for a given breakpoint. * + * @since n.e.x.t + * * @todo Eliminate in favor of readonly public property. * @phpstan-return positive-int * @return int Sample size. @@ -177,8 +197,9 @@ public function get_sample_size(): int { /** * Gets the freshness age (TTL) for a given URL Metric. * - * @todo Eliminate in favor of readonly public property. + * @since n.e.x.t * + * @todo Eliminate in favor of readonly public property. * @phpstan-return 0|positive-int * @return int Freshness age. */ @@ -187,7 +208,9 @@ public function get_freshness_ttl(): int { } /** - * Checks whether the provided viewport width is within the minimum/maximum range for + * Checks whether the provided viewport width is within the minimum/maximum range for. + * + * @since 0.1.0 * * @param int $viewport_width Viewport width. * @return bool Whether the viewport width is in range. @@ -202,6 +225,8 @@ public function is_viewport_width_in_range( int $viewport_width ): bool { /** * Adds a URL Metric to the group. * + * @since 0.1.0 + * * @throws InvalidArgumentException If the viewport width of the URL Metric is not within the min/max bounds of the group. * * @param OD_URL_Metric $url_metric URL Metric. @@ -243,6 +268,8 @@ static function ( OD_URL_Metric $a, OD_URL_Metric $b ): int { * A group is complete if it has the full sample size of URL Metrics * and all of these URL Metrics are fresh. * + * @since 0.1.0 + * * @return bool Whether complete. */ public function is_complete(): bool { @@ -271,6 +298,8 @@ public function is_complete(): bool { /** * Gets the LCP element in the viewport group. * + * @since 0.3.0 + * * @return OD_Element|null LCP element data or null if not available, either because there are no URL Metrics or * the LCP element type is not supported. */ @@ -414,6 +443,8 @@ public function get_element_max_intersection_ratio( string $xpath ): ?float { /** * Returns an iterator for the URL Metrics in the group. * + * @since 0.1.0 + * * @return ArrayIterator ArrayIterator for OD_URL_Metric instances. */ public function getIterator(): ArrayIterator { @@ -423,6 +454,8 @@ public function getIterator(): ArrayIterator { /** * Counts the URL Metrics in the group. * + * @since 0.1.0 + * * @return int<0, max> URL Metric count. */ public function count(): int { From 134814537d43bb617cd8e2cfbb9b3c6b0622eda4 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 24 Nov 2024 22:21:18 -0800 Subject: [PATCH 038/237] Add maxLength constraint for tag name --- plugins/image-prioritizer/detect.js | 10 ++++++++++ plugins/image-prioritizer/helper.php | 1 + 2 files changed, 11 insertions(+) diff --git a/plugins/image-prioritizer/detect.js b/plugins/image-prioritizer/detect.js index 8f548fd8e5..1b07e1c5d4 100644 --- a/plugins/image-prioritizer/detect.js +++ b/plugins/image-prioritizer/detect.js @@ -148,6 +148,16 @@ function handleLCPMetric( metric, isDebug ) { return; } + // Also skip Custom Elements which have excessively long tag names. + if ( entry.element.tagName.length > 25 ) { + if ( isDebug ) { + log( + `Skipping very long tag name: ${ entry.element.tagName }` + ); + } + return; + } + // Note that getAttribute() is used instead of properties so that null can be returned in case of an absent attribute. let id = entry.element.getAttribute( 'id' ); if ( null !== id ) { diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index dccbf82760..1b1e7f33a3 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -134,6 +134,7 @@ function image_prioritizer_add_element_item_schema_properties( array $additional 'type' => 'string', 'required' => true, 'minLength' => 1, + 'maxLength' => 25, // The longest HTML tag name is 10 characters (BLOCKQUOTE and FIGCAPTION). This maxLength accounts for possible Custom Elements that are even longer. 'pattern' => '^[a-zA-Z0-9\-]+$', // Technically emoji can be allowed in a custom element's tag name, but this is not supported here. ), 'id' => array( From 89b4c7f1cb00f2bf34fae491ef012445bb25d41c Mon Sep 17 00:00:00 2001 From: b1ink0 Date: Mon, 25 Nov 2024 19:56:43 +0530 Subject: [PATCH 039/237] Refactor to store error instead of false --- .../includes/admin/plugins.php | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index 9395e4b954..f25a90bd70 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -22,17 +22,15 @@ function perflab_query_plugin_info( string $plugin_slug ) { $transient_key = 'perflab_plugins_info'; $plugins = get_transient( $transient_key ); - if ( is_array( $plugins ) ) { - if ( isset( $plugins[ $plugin_slug ] ) ) { - if ( false === $plugins[ $plugin_slug ] ) { - // Plugin was requested before and not found. - return new WP_Error( - 'plugin_not_found', - __( 'Plugin not found in cached API response.', 'performance-lab' ) - ); - } - return $plugins[ $plugin_slug ]; // Return cached plugin info if found. + if ( is_array( $plugins ) && isset( $plugins[ $plugin_slug ] ) ) { + if ( isset( $plugins[ $plugin_slug ]['error'] ) ) { + // Plugin was requested before and not found. + return new WP_Error( + $plugins[ $plugin_slug ]['error']['code'], + $plugins[ $plugin_slug ]['error']['message'] + ); } + return $plugins[ $plugin_slug ]; // Return cached plugin info if found. } $fields = array( @@ -89,7 +87,12 @@ function perflab_query_plugin_info( string $plugin_slug ) { if ( ! isset( $all_performance_plugins[ $current_plugin_slug ] ) ) { // Cache the fact that the plugin was not found. - $plugins[ $current_plugin_slug ] = false; + $plugins[ $current_plugin_slug ] = array( + 'error' => array( + 'code' => 'plugin_not_found', + 'message' => __( 'Plugin not found in API response.', 'performance-lab' ), + ), + ); continue; } @@ -104,15 +107,20 @@ function perflab_query_plugin_info( string $plugin_slug ) { if ( ! isset( $plugins[ $plugin_slug ] ) ) { // Cache the fact that the plugin was not found. - $plugins[ $plugin_slug ] = false; + $plugins[ $plugin_slug ] = array( + 'error' => array( + 'code' => 'plugin_not_found', + 'message' => __( 'Plugin not found in API response.', 'performance-lab' ), + ), + ); } set_transient( $transient_key, $plugins, HOUR_IN_SECONDS ); - if ( false === $plugins[ $plugin_slug ] ) { + if ( isset( $plugins[ $plugin_slug ]['error'] ) ) { return new WP_Error( - 'plugin_not_found', - __( 'Plugin not found in API response.', 'performance-lab' ) + $plugins[ $plugin_slug ]['error']['code'], + $plugins[ $plugin_slug ]['error']['message'] ); } From 2a344c9a287401bf4a0cf10bef747038f0be0087 Mon Sep 17 00:00:00 2001 From: b1ink0 Date: Mon, 25 Nov 2024 20:05:09 +0530 Subject: [PATCH 040/237] Adjust transient expiration time based on plugin availability --- plugins/performance-lab/includes/admin/plugins.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index f25a90bd70..c403da80cc 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -113,9 +113,11 @@ function perflab_query_plugin_info( string $plugin_slug ) { 'message' => __( 'Plugin not found in API response.', 'performance-lab' ), ), ); - } - set_transient( $transient_key, $plugins, HOUR_IN_SECONDS ); + set_transient( $transient_key, $plugins, MINUTE_IN_SECONDS ); + } else { + set_transient( $transient_key, $plugins, HOUR_IN_SECONDS ); + } if ( isset( $plugins[ $plugin_slug ]['error'] ) ) { return new WP_Error( From 94b3feed9f1b83bd66ccf55b1c9cc1b16c16c583 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 25 Nov 2024 09:27:57 -0800 Subject: [PATCH 041/237] Account for initialize or finalize not returning a Promise --- plugins/optimization-detective/detect.js | 38 +++++++++++++----------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index 53feba11df..c8a2faae34 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -360,13 +360,14 @@ export default async function detect( { extensions.set( extensionModuleUrl, extension ); // TODO: There should to be a way to pass additional args into the module. Perhaps extensionModuleUrls should be a mapping of URLs to args. if ( extension.initialize instanceof Function ) { - extensionInitializePromises.push( - extension.initialize( { - isDebug, - webVitalsLibrarySrc, - } ) - ); - initializingExtensionModuleUrls.push( extensionModuleUrl ); + const initializePromise = extension.initialize( { + isDebug, + webVitalsLibrarySrc, + } ); + if ( initializePromise instanceof Promise ) { + extensionInitializePromises.push( initializePromise ); + initializingExtensionModuleUrls.push( extensionModuleUrl ); + } } } catch ( err ) { error( @@ -564,16 +565,19 @@ export default async function detect( { ] of extensions.entries() ) { if ( extension.finalize instanceof Function ) { try { - extensionFinalizePromises.push( - extension.finalize( { - isDebug, - getRootData, - getElementData, - extendElementData, - extendRootData, - } ) - ); - finalizingExtensionModuleUrls.push( extensionModuleUrl ); + const finalizePromise = extension.finalize( { + isDebug, + getRootData, + getElementData, + extendElementData, + extendRootData, + } ); + if ( finalizePromise instanceof Promise ) { + extensionFinalizePromises.push( finalizePromise ); + finalizingExtensionModuleUrls.push( + extensionModuleUrl + ); + } } catch ( err ) { error( `Unable to start finalizing extension '${ extensionModuleUrl }':`, From 74f9241d483b85d04a813c03c10b1b753011f7ac Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 25 Nov 2024 09:32:37 -0800 Subject: [PATCH 042/237] Improve typing for externalBackgroundImages --- plugins/image-prioritizer/detect.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/image-prioritizer/detect.js b/plugins/image-prioritizer/detect.js index 1b07e1c5d4..a3ff1503db 100644 --- a/plugins/image-prioritizer/detect.js +++ b/plugins/image-prioritizer/detect.js @@ -14,7 +14,12 @@ const consoleLogPrefix = '[Image Prioritizer]'; /** * Detected LCP external background image candidates. * - * @type {Array<{url: string, tag: string, id: string, class: string}>} + * @type {Array<{ + * url: string, + * tag: string, + * id: string|null, + * class: string|null, + * }>} */ const externalBackgroundImages = []; From 94e527b0d24ef20195d33d59be604f411964978a Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 25 Nov 2024 09:38:50 -0800 Subject: [PATCH 043/237] Eliminate truncation of ID and className --- plugins/image-prioritizer/detect.js | 18 ++++++++++++------ plugins/image-prioritizer/helper.php | 4 ++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/plugins/image-prioritizer/detect.js b/plugins/image-prioritizer/detect.js index a3ff1503db..e04a81d0dd 100644 --- a/plugins/image-prioritizer/detect.js +++ b/plugins/image-prioritizer/detect.js @@ -164,13 +164,19 @@ function handleLCPMetric( metric, isDebug ) { } // Note that getAttribute() is used instead of properties so that null can be returned in case of an absent attribute. - let id = entry.element.getAttribute( 'id' ); - if ( null !== id ) { - id = id.substring( 0, 100 ); // This is the maxLength defined in image_prioritizer_add_element_item_schema_properties(). + const id = entry.element.getAttribute( 'id' ); + if ( typeof id === 'string' && id.length > 100 ) { + if ( isDebug ) { + log( `Skipping very long ID: ${ id }` ); + } + return; } - let className = entry.element.getAttribute( 'class' ); - if ( null !== className ) { - className = className.substring( 0, 500 ); // This is the maxLength defined in image_prioritizer_add_element_item_schema_properties(). + const className = entry.element.getAttribute( 'class' ); + if ( typeof className === 'string' && className.length > 500 ) { + if ( isDebug ) { + log( `Skipping very long className: ${ className }` ); + } + return; } // The id and className allow the tag visitor to detect whether the element is still in the document. diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 1b1e7f33a3..d01ca64537 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -139,12 +139,12 @@ function image_prioritizer_add_element_item_schema_properties( array $additional ), 'id' => array( 'type' => array( 'string', 'null' ), - 'maxLength' => 100, // A reasonable upper-bound length for a long ID. The client will must truncate anything longer. + 'maxLength' => 100, // A reasonable upper-bound length for a long ID. 'required' => true, ), 'class' => array( 'type' => array( 'string', 'null' ), - 'maxLength' => 500, // There can be a ton of class names on an element. The client will must truncate anything longer. + 'maxLength' => 500, // There can be a ton of class names on an element. 'required' => true, ), ), From 63b254479677392f02690749f2390887749d706c Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Mon, 25 Nov 2024 14:42:58 -0600 Subject: [PATCH 044/237] Separate file structure by feature --- plugins/auto-sizes/auto-sizes.php | 2 + plugins/auto-sizes/hooks.php | 276 +----------------- plugins/auto-sizes/inc/auto-sizes.php | 97 ++++++ .../inc/improve-calculate-sizes.php | 184 ++++++++++++ ...s.php => test-improve-calculate-sizes.php} | 2 +- 5 files changed, 291 insertions(+), 270 deletions(-) create mode 100644 plugins/auto-sizes/inc/auto-sizes.php create mode 100644 plugins/auto-sizes/inc/improve-calculate-sizes.php rename plugins/auto-sizes/tests/{test-improve-sizes.php => test-improve-calculate-sizes.php} (99%) diff --git a/plugins/auto-sizes/auto-sizes.php b/plugins/auto-sizes/auto-sizes.php index 48dbe6a46d..b7b311e764 100644 --- a/plugins/auto-sizes/auto-sizes.php +++ b/plugins/auto-sizes/auto-sizes.php @@ -27,4 +27,6 @@ define( 'IMAGE_AUTO_SIZES_VERSION', '1.3.0' ); +require_once __DIR__ . '/inc/auto-sizes.php'; +require_once __DIR__ . '/inc/improve-calculate-sizes.php'; require_once __DIR__ . '/hooks.php'; diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index abe53b8360..45612d9af2 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -10,102 +10,6 @@ exit; // Exit if accessed directly. } -/** - * Adds auto to the sizes attribute to the image, if applicable. - * - * @since 1.0.0 - * - * @param array|mixed $attr Attributes for the image markup. - * @return array The filtered attributes for the image markup. - */ -function auto_sizes_update_image_attributes( $attr ): array { - if ( ! is_array( $attr ) ) { - $attr = array(); - } - - // Bail early if the image is not lazy-loaded. - if ( ! isset( $attr['loading'] ) || 'lazy' !== $attr['loading'] ) { - return $attr; - } - - // Bail early if the image is not responsive. - if ( ! isset( $attr['sizes'] ) ) { - return $attr; - } - - // Don't add 'auto' to the sizes attribute if it already exists. - if ( auto_sizes_attribute_includes_valid_auto( $attr['sizes'] ) ) { - return $attr; - } - - $attr['sizes'] = 'auto, ' . $attr['sizes']; - - return $attr; -} - -/** - * Adds auto to the sizes attribute to the image, if applicable. - * - * @since 1.0.0 - * - * @param string|mixed $html The HTML image tag markup being filtered. - * @return string The filtered HTML image tag markup. - */ -function auto_sizes_update_content_img_tag( $html ): string { - if ( ! is_string( $html ) ) { - $html = ''; - } - - $processor = new WP_HTML_Tag_Processor( $html ); - - // Bail if there is no IMG tag. - if ( ! $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) { - return $html; - } - - // Bail early if the image is not lazy-loaded. - $value = $processor->get_attribute( 'loading' ); - if ( ! is_string( $value ) || 'lazy' !== strtolower( trim( $value, " \t\f\r\n" ) ) ) { - return $html; - } - - $sizes = $processor->get_attribute( 'sizes' ); - - // Bail early if the image is not responsive. - if ( ! is_string( $sizes ) ) { - return $html; - } - - // Don't add 'auto' to the sizes attribute if it already exists. - if ( auto_sizes_attribute_includes_valid_auto( $sizes ) ) { - return $html; - } - - $processor->set_attribute( 'sizes', "auto, $sizes" ); - return $processor->get_updated_html(); -} - -// Skip loading plugin filters if WordPress Core already loaded the functionality. -if ( ! function_exists( 'wp_sizes_attribute_includes_valid_auto' ) ) { - add_filter( 'wp_get_attachment_image_attributes', 'auto_sizes_update_image_attributes' ); - add_filter( 'wp_content_img_tag', 'auto_sizes_update_content_img_tag' ); -} - -/** - * Checks whether the given 'sizes' attribute includes the 'auto' keyword as the first item in the list. - * - * Per the HTML spec, if present it must be the first entry. - * - * @since 1.2.0 - * - * @param string $sizes_attr The 'sizes' attribute value. - * @return bool True if the 'auto' keyword is present, false otherwise. - */ -function auto_sizes_attribute_includes_valid_auto( string $sizes_attr ): bool { - list( $first_size ) = explode( ',', $sizes_attr, 2 ); - return 'auto' === strtolower( trim( $first_size, " \t\f\r\n" ) ); -} - /** * Displays the HTML generator tag for the plugin. * @@ -120,183 +24,17 @@ function auto_sizes_render_generator(): void { add_action( 'wp_head', 'auto_sizes_render_generator' ); /** - * Gets the smaller image size if the layout width is bigger. - * - * It will return the smaller image size and return "px" if the layout width - * is something else, e.g. min(640px, 90vw) or 90vw. - * - * @since 1.1.0 - * - * @param string $layout_width The layout width. - * @param int $image_width The image width. - * @return string The proper width after some calculations. - */ -function auto_sizes_get_width( string $layout_width, int $image_width ): string { - if ( str_ends_with( $layout_width, 'px' ) ) { - return $image_width > (int) $layout_width ? $layout_width : $image_width . 'px'; - } - return $image_width . 'px'; -} - -/** - * Primes attachment into the cache with a single database query. - * - * @since n.e.x.t - * - * @param string|mixed $content The HTML content. - * @return string The HTML content. + * Filters related to the auto-sizes functionality. */ -function auto_sizes_prime_attachment_caches( $content ): string { - if ( ! is_string( $content ) ) { - return ''; - } - - $processor = new WP_HTML_Tag_Processor( $content ); - - $images = array(); - while ( $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) { - $class = $processor->get_attribute( 'class' ); - - if ( ! is_string( $class ) ) { - continue; - } - - if ( preg_match( '/(?:^|\s)wp-image-([1-9][0-9]*)(?:\s|$)/', $class, $class_id ) === 1 ) { - $attachment_id = (int) $class_id[1]; - if ( $attachment_id > 0 ) { - $images[] = $attachment_id; - } - } - } - - // Reduce the array to unique attachment IDs. - $attachment_ids = array_unique( $images ); - - if ( count( $attachment_ids ) > 1 ) { - /* - * Warm the object cache with post and meta information for all found - * images to avoid making individual database calls. - */ - _prime_post_caches( $attachment_ids, false, true ); - } - - return $content; +// Skip loading plugin filters if WordPress Core already loaded the functionality. +if ( ! function_exists( 'wp_img_tag_add_auto_sizes' ) ) { + add_filter( 'wp_get_attachment_image_attributes', 'auto_sizes_update_image_attributes' ); + add_filter( 'wp_content_img_tag', 'auto_sizes_update_content_img_tag' ); } -// This must run before 'do_blocks', which runs at priority 9. -add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 9 ); - /** - * Filter the sizes attribute for images to improve the default calculation. - * - * @since 1.1.0 - * - * @param string|mixed $content The block content about to be rendered. - * @param array{ attrs?: array{ align?: string, width?: string } } $parsed_block The parsed block. - * @param WP_Block $block Block instance. - * @return string The updated block content. + * Filters related to the improved image sizes functionality. */ -function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $block ): string { - if ( ! is_string( $content ) ) { - return ''; - } - $processor = new WP_HTML_Tag_Processor( $content ); - $has_image = $processor->next_tag( array( 'tag_name' => 'IMG' ) ); - - // Only update the markup if an image is found. - if ( $has_image ) { - - /** - * Callback for calculating image sizes attribute value for an image block. - * - * This is a workaround to use block context data when calculating the img sizes attribute. - * - * @param string $sizes The image sizes attribute value. - * @param string $size The image size data. - */ - $filter = static function ( $sizes, $size ) use ( $block ) { - $id = $block->attributes['id'] ?? 0; - $alignment = $block->attributes['align'] ?? ''; - $width = $block->attributes['width'] ?? ''; - - return auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width ); - }; - - // Hook this filter early, before default filters are run. - add_filter( 'wp_calculate_image_sizes', $filter, 9, 2 ); - - $sizes = wp_calculate_image_sizes( - // If we don't have a size slug, assume the full size was used. - $parsed_block['attrs']['sizeSlug'] ?? 'full', - null, - null, - $parsed_block['attrs']['id'] ?? 0 - ); - - remove_filter( 'wp_calculate_image_sizes', $filter, 9 ); - - // Bail early if sizes are not calculated. - if ( false === $sizes ) { - return $content; - } - - $processor->set_attribute( 'sizes', $sizes ); - - return $processor->get_updated_html(); - } - - return $content; -} +add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 9 ); // This must run before 'do_blocks', which runs at priority 9. add_filter( 'render_block_core/image', 'auto_sizes_filter_image_tag', 10, 3 ); add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 3 ); - -/** - * Modifies the sizes attribute of an image based on layout context. - * - * @param int $id The image id. - * @param string $size The image size data. - * @param string $align The image alignment. - * @param string $resize_width Resize image width. - * @return string The sizes attribute value. - */ -function auto_sizes_calculate_better_sizes( int $id, string $size, string $align, string $resize_width ): string { - $sizes = ''; - $image = wp_get_attachment_image_src( $id, $size ); - - if ( false === $image ) { - return $sizes; - } - - // Retrieve width from the image tag itself. - $image_width = '' !== $resize_width ? (int) $resize_width : $image[1]; - - $layout = wp_get_global_settings( array( 'layout' ) ); - - // Handle different alignment use cases. - switch ( $align ) { - case 'full': - $sizes = '100vw'; - break; - - case 'wide': - if ( array_key_exists( 'wideSize', $layout ) ) { - $sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $layout['wideSize'] ); - } - break; - - case 'left': - case 'right': - case 'center': - $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $image_width ); - break; - - default: - if ( array_key_exists( 'contentSize', $layout ) ) { - $width = auto_sizes_get_width( $layout['contentSize'], $image_width ); - $sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $width ); - } - break; - } - - return $sizes; -} diff --git a/plugins/auto-sizes/inc/auto-sizes.php b/plugins/auto-sizes/inc/auto-sizes.php new file mode 100644 index 0000000000..44df012d42 --- /dev/null +++ b/plugins/auto-sizes/inc/auto-sizes.php @@ -0,0 +1,97 @@ +|mixed $attr Attributes for the image markup. + * @return array The filtered attributes for the image markup. + */ +function auto_sizes_update_image_attributes( $attr ): array { + if ( ! is_array( $attr ) ) { + $attr = array(); + } + + // Bail early if the image is not lazy-loaded. + if ( ! isset( $attr['loading'] ) || 'lazy' !== $attr['loading'] ) { + return $attr; + } + + // Bail early if the image is not responsive. + if ( ! isset( $attr['sizes'] ) ) { + return $attr; + } + + // Don't add 'auto' to the sizes attribute if it already exists. + if ( auto_sizes_attribute_includes_valid_auto( $attr['sizes'] ) ) { + return $attr; + } + + $attr['sizes'] = 'auto, ' . $attr['sizes']; + + return $attr; +} + +/** + * Adds auto to the sizes attribute to the image, if applicable. + * + * @since 1.0.0 + * + * @param string|mixed $html The HTML image tag markup being filtered. + * @return string The filtered HTML image tag markup. + */ +function auto_sizes_update_content_img_tag( $html ): string { + if ( ! is_string( $html ) ) { + $html = ''; + } + + $processor = new WP_HTML_Tag_Processor( $html ); + + // Bail if there is no IMG tag. + if ( ! $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) { + return $html; + } + + // Bail early if the image is not lazy-loaded. + $value = $processor->get_attribute( 'loading' ); + if ( ! is_string( $value ) || 'lazy' !== strtolower( trim( $value, " \t\f\r\n" ) ) ) { + return $html; + } + + $sizes = $processor->get_attribute( 'sizes' ); + + // Bail early if the image is not responsive. + if ( ! is_string( $sizes ) ) { + return $html; + } + + // Don't add 'auto' to the sizes attribute if it already exists. + if ( auto_sizes_attribute_includes_valid_auto( $sizes ) ) { + return $html; + } + + $processor->set_attribute( 'sizes', "auto, $sizes" ); + return $processor->get_updated_html(); +} + +/** + * Checks whether the given 'sizes' attribute includes the 'auto' keyword as the first item in the list. + * + * Per the HTML spec, if present it must be the first entry. + * + * @since 1.2.0 + * + * @param string $sizes_attr The 'sizes' attribute value. + * @return bool True if the 'auto' keyword is present, false otherwise. + */ +function auto_sizes_attribute_includes_valid_auto( string $sizes_attr ): bool { + list( $first_size ) = explode( ',', $sizes_attr, 2 ); + return 'auto' === strtolower( trim( $first_size, " \t\f\r\n" ) ); +} diff --git a/plugins/auto-sizes/inc/improve-calculate-sizes.php b/plugins/auto-sizes/inc/improve-calculate-sizes.php new file mode 100644 index 0000000000..61df526a23 --- /dev/null +++ b/plugins/auto-sizes/inc/improve-calculate-sizes.php @@ -0,0 +1,184 @@ + (int) $layout_width ? $layout_width : $image_width . 'px'; + } + return $image_width . 'px'; +} + +/** + * Primes attachment into the cache with a single database query. + * + * @since n.e.x.t + * + * @param string|mixed $content The HTML content. + * @return string The HTML content. + */ +function auto_sizes_prime_attachment_caches( $content ): string { + if ( ! is_string( $content ) ) { + return ''; + } + + $processor = new WP_HTML_Tag_Processor( $content ); + + $images = array(); + while ( $processor->next_tag( array( 'tag_name' => 'IMG' ) ) ) { + $class = $processor->get_attribute( 'class' ); + + if ( ! is_string( $class ) ) { + continue; + } + + if ( preg_match( '/(?:^|\s)wp-image-([1-9][0-9]*)(?:\s|$)/', $class, $class_id ) === 1 ) { + $attachment_id = (int) $class_id[1]; + if ( $attachment_id > 0 ) { + $images[] = $attachment_id; + } + } + } + + // Reduce the array to unique attachment IDs. + $attachment_ids = array_unique( $images ); + + if ( count( $attachment_ids ) > 1 ) { + /* + * Warm the object cache with post and meta information for all found + * images to avoid making individual database calls. + */ + _prime_post_caches( $attachment_ids, false, true ); + } + + return $content; +} + +/** + * Filter the sizes attribute for images to improve the default calculation. + * + * @since 1.1.0 + * + * @param string|mixed $content The block content about to be rendered. + * @param array{ attrs?: array{ align?: string, width?: string } } $parsed_block The parsed block. + * @param WP_Block $block Block instance. + * @return string The updated block content. + */ +function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $block ): string { + if ( ! is_string( $content ) ) { + return ''; + } + $processor = new WP_HTML_Tag_Processor( $content ); + $has_image = $processor->next_tag( array( 'tag_name' => 'IMG' ) ); + + // Only update the markup if an image is found. + if ( $has_image ) { + + /** + * Callback for calculating image sizes attribute value for an image block. + * + * This is a workaround to use block context data when calculating the img sizes attribute. + * + * @param string $sizes The image sizes attribute value. + * @param string $size The image size data. + */ + $filter = static function ( $sizes, $size ) use ( $block ) { + $id = $block->attributes['id'] ?? 0; + $alignment = $block->attributes['align'] ?? ''; + $width = $block->attributes['width'] ?? ''; + + return auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width ); + }; + + // Hook this filter early, before default filters are run. + add_filter( 'wp_calculate_image_sizes', $filter, 9, 2 ); + + $sizes = wp_calculate_image_sizes( + // If we don't have a size slug, assume the full size was used. + $parsed_block['attrs']['sizeSlug'] ?? 'full', + null, + null, + $parsed_block['attrs']['id'] ?? 0 + ); + + remove_filter( 'wp_calculate_image_sizes', $filter, 9 ); + + // Bail early if sizes are not calculated. + if ( false === $sizes ) { + return $content; + } + + $processor->set_attribute( 'sizes', $sizes ); + + return $processor->get_updated_html(); + } + + return $content; +} + +/** + * Modifies the sizes attribute of an image based on layout context. + * + * @param int $id The image id. + * @param string $size The image size data. + * @param string $align The image alignment. + * @param string $resize_width Resize image width. + * @return string The sizes attribute value. + */ +function auto_sizes_calculate_better_sizes( int $id, string $size, string $align, string $resize_width ): string { + $sizes = ''; + $image = wp_get_attachment_image_src( $id, $size ); + + if ( false === $image ) { + return $sizes; + } + + // Retrieve width from the image tag itself. + $image_width = '' !== $resize_width ? (int) $resize_width : $image[1]; + + $layout = wp_get_global_settings( array( 'layout' ) ); + + // Handle different alignment use cases. + switch ( $align ) { + case 'full': + $sizes = '100vw'; + break; + + case 'wide': + if ( array_key_exists( 'wideSize', $layout ) ) { + $sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $layout['wideSize'] ); + } + break; + + case 'left': + case 'right': + case 'center': + $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $image_width ); + break; + + default: + if ( array_key_exists( 'contentSize', $layout ) ) { + $width = auto_sizes_get_width( $layout['contentSize'], $image_width ); + $sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $width ); + } + break; + } + + return $sizes; +} diff --git a/plugins/auto-sizes/tests/test-improve-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php similarity index 99% rename from plugins/auto-sizes/tests/test-improve-sizes.php rename to plugins/auto-sizes/tests/test-improve-calculate-sizes.php index 73d23fb8e7..fcd0a2d9dd 100644 --- a/plugins/auto-sizes/tests/test-improve-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -3,7 +3,7 @@ * Tests for the improve sizes for Images. * * @package auto-sizes - * @group improve-sizes + * @group improve-calculate-sizes */ class Tests_Improve_Sizes extends WP_UnitTestCase { From 7cd300aa185116cc3740e4aab8b1ac20edcdcd31 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Mon, 25 Nov 2024 14:49:47 -0600 Subject: [PATCH 045/237] Rename /inc to /includes --- plugins/auto-sizes/auto-sizes.php | 4 ++-- plugins/auto-sizes/{inc => includes}/auto-sizes.php | 0 .../auto-sizes/{inc => includes}/improve-calculate-sizes.php | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename plugins/auto-sizes/{inc => includes}/auto-sizes.php (100%) rename plugins/auto-sizes/{inc => includes}/improve-calculate-sizes.php (100%) diff --git a/plugins/auto-sizes/auto-sizes.php b/plugins/auto-sizes/auto-sizes.php index b7b311e764..994dc2fbcc 100644 --- a/plugins/auto-sizes/auto-sizes.php +++ b/plugins/auto-sizes/auto-sizes.php @@ -27,6 +27,6 @@ define( 'IMAGE_AUTO_SIZES_VERSION', '1.3.0' ); -require_once __DIR__ . '/inc/auto-sizes.php'; -require_once __DIR__ . '/inc/improve-calculate-sizes.php'; +require_once __DIR__ . '/includes/auto-sizes.php'; +require_once __DIR__ . '/includes/improve-calculate-sizes.php'; require_once __DIR__ . '/hooks.php'; diff --git a/plugins/auto-sizes/inc/auto-sizes.php b/plugins/auto-sizes/includes/auto-sizes.php similarity index 100% rename from plugins/auto-sizes/inc/auto-sizes.php rename to plugins/auto-sizes/includes/auto-sizes.php diff --git a/plugins/auto-sizes/inc/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php similarity index 100% rename from plugins/auto-sizes/inc/improve-calculate-sizes.php rename to plugins/auto-sizes/includes/improve-calculate-sizes.php From 11a63e235d42b4326d696b41d1d7fe3dd701c68d Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Mon, 25 Nov 2024 14:55:51 -0600 Subject: [PATCH 046/237] Rename test class --- plugins/auto-sizes/tests/test-improve-calculate-sizes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php index fcd0a2d9dd..1e8bd819ce 100644 --- a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -6,7 +6,7 @@ * @group improve-calculate-sizes */ -class Tests_Improve_Sizes extends WP_UnitTestCase { +class Tests_Improve_Calculate_Sizes extends WP_UnitTestCase { /** * Attachment ID. From 042232f6314c7fac66cfe556d3725105d99787a0 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Mon, 25 Nov 2024 17:12:20 -0600 Subject: [PATCH 047/237] Add tests for passing parent alignment context to images --- .../tests/test-improve-calculate-sizes.php | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php index 1e8bd819ce..ca19201626 100644 --- a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -385,4 +385,69 @@ public function test_no_image(): void { $this->assertStringContainsString( '

No image here

', $result ); } + + /** + * Test that the layout property of a group block is passed by context to the image block. + * + * @group test + */ + public function test_ancestor_layout_is_passed_by_context(): void { + $block_content = $this->get_group_block_markup( + $this->get_image_block_markup( self::$image_id, 'large', 'full' ) + ); + + $result = apply_filters( 'the_content', $block_content ); + + $this->assertStringContainsString( 'sizes="(max-width: 620px) 100vw, 620px" ', $result ); + } + + + /** + * Helper to generate image block markup. + * + * @param int $attachment_id Attachment ID. + * @param string $size Optional. Image size. Default 'full'. + * @param string $align Optional. Image alignment. Default null. + * @return string Image block markup. + */ + public function get_image_block_markup( int $attachment_id, string $size = 'full', string $align = null ): string { + $image_url = wp_get_attachment_image_url( $attachment_id, $size ); + + $atts = wp_parse_args( + array( + 'id' => $attachment_id, + 'sizeSlug' => $size, + 'align' => $align, + ), + array( + 'id' => $attachment_id, + 'sizeSlug' => 'large', + 'linkDestination' => 'none', + ) + ); + + return '
'; + } + + /** + * Helper to generate group block markup. + * + * @param string $content Block content. + * @param array $atts Optional. Block attributes. Default empty array. + * @return string Group block markup. + */ + public function get_group_block_markup( string $content, array $atts = array() ): string { + $atts = wp_parse_args( + $atts, + array( + 'layout' => array( + 'type' => 'constrained', + ), + ) + ); + + return ' +
' . $content . '
+ '; + } } From 47c677912c46cf17da934f288cbe07c7dfc33943 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Tue, 26 Nov 2024 15:27:18 +0530 Subject: [PATCH 048/237] POC - Pass group block alignment context to image block --- .../includes/improve-calculate-sizes.php | 109 +++++++++++++--- .../tests/test-improve-calculate-sizes.php | 119 +++++++++++++++++- 2 files changed, 209 insertions(+), 19 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 61df526a23..072effdfff 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -84,6 +84,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b if ( ! is_string( $content ) ) { return ''; } + $processor = new WP_HTML_Tag_Processor( $content ); $has_image = $processor->next_tag( array( 'tag_name' => 'IMG' ) ); @@ -99,11 +100,13 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b * @param string $size The image size data. */ $filter = static function ( $sizes, $size ) use ( $block ) { - $id = $block->attributes['id'] ?? 0; - $alignment = $block->attributes['align'] ?? ''; - $width = $block->attributes['width'] ?? ''; + $id = $block->attributes['id'] ?? 0; + $alignment = $block->attributes['align'] ?? ''; + $width = $block->attributes['width'] ?? ''; + $is_parent_block = $block->context['is_parent_block'] ?? false; + $ancestor_block_align = $block->context['ancestor_block_align'] ?? ''; - return auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width ); + return auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width, (bool) $is_parent_block, (string) $ancestor_block_align ); }; // Hook this filter early, before default filters are run. @@ -135,50 +138,124 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b /** * Modifies the sizes attribute of an image based on layout context. * - * @param int $id The image id. - * @param string $size The image size data. - * @param string $align The image alignment. - * @param string $resize_width Resize image width. + * @since n.e.x.t + * + * @param int $id The image id. + * @param string $size The image size data. + * @param string $align The image alignment. + * @param string $resize_width Resize image width. + * @param bool $is_parent_block Check if image block has parent block. + * @param string $ancestor_block_align The ancestor block alignment. * @return string The sizes attribute value. */ -function auto_sizes_calculate_better_sizes( int $id, string $size, string $align, string $resize_width ): string { - $sizes = ''; +function auto_sizes_calculate_better_sizes( int $id, string $size, string $align, string $resize_width, bool $is_parent_block, string $ancestor_block_align ): string { $image = wp_get_attachment_image_src( $id, $size ); if ( false === $image ) { - return $sizes; + return ''; } // Retrieve width from the image tag itself. $image_width = '' !== $resize_width ? (int) $resize_width : $image[1]; + if ( $is_parent_block ) { + if ( 'full' === $ancestor_block_align && 'full' === $align ) { + return auto_sizes_get_sizes_by_block_alignments( $align, $image_width, true ); + } elseif ( 'full' !== $ancestor_block_align && 'full' === $align ) { + return auto_sizes_get_sizes_by_block_alignments( $ancestor_block_align, $image_width, true ); + } elseif ( 'full' !== $ancestor_block_align ) { + $parent_block_alignment_width = auto_sizes_get_sizes_by_block_alignments( $ancestor_block_align, $image_width ); + $block_alignment_width = auto_sizes_get_sizes_by_block_alignments( $align, $image_width ); + if ( (int) $parent_block_alignment_width < (int) $block_alignment_width ) { + return sprintf( '(max-width: %1$s) 100vw, %1$s', $parent_block_alignment_width ); + } else { + return sprintf( '(max-width: %1$s) 100vw, %1$s', $block_alignment_width ); + } + } + } + + return auto_sizes_get_sizes_by_block_alignments( $align, $image_width, true ); +} + +/** + * Generates the `sizes` attribute value based on block information. + * + * @since n.e.x.t + * + * @param string $alignment The alignment. + * @param int $image_width The image width. + * @param bool $print_sizes Print the sizes attribute. Default is false. + * @return string The sizes attribute value. + */ +function auto_sizes_get_sizes_by_block_alignments( string $alignment, int $image_width, bool $print_sizes = false ): string { + $sizes = ''; + $layout = wp_get_global_settings( array( 'layout' ) ); // Handle different alignment use cases. - switch ( $align ) { + switch ( $alignment ) { case 'full': $sizes = '100vw'; break; case 'wide': if ( array_key_exists( 'wideSize', $layout ) ) { - $sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $layout['wideSize'] ); + $sizes = $layout['wideSize']; } break; case 'left': case 'right': case 'center': - $sizes = sprintf( '(max-width: %1$dpx) 100vw, %1$dpx', $image_width ); + $sizes = auto_sizes_get_width( '', $image_width ); break; default: if ( array_key_exists( 'contentSize', $layout ) ) { - $width = auto_sizes_get_width( $layout['contentSize'], $image_width ); - $sizes = sprintf( '(max-width: %1$s) 100vw, %1$s', $width ); + $sizes = auto_sizes_get_width( $layout['contentSize'], $image_width ); } break; } + if ( $print_sizes ) { + $sizes = 'full' === $alignment ? $sizes : sprintf( '(max-width: %1$s) 100vw, %1$s', $sizes ); + } + return $sizes; } + +/** + * Filters the context keys that a block type uses. + * + * @since n.e.x.t + * + * @param array $uses_context Array of registered uses context for a block type. + * @param WP_Block_Type $block_type The full block type object. + * @return array The filtered context keys used by the block type. + */ +function auto_sizes_allowed_uses_context_for_image_blocks( array $uses_context, WP_Block_Type $block_type ): array { + if ( 'core/image' === $block_type->name ) { + // Use array_values to reset the array keys after merging. + return array_values( array_unique( array_merge( $uses_context, array( 'is_parent_block', 'ancestor_block_align' ) ) ) ); + } + return $uses_context; +} +add_filter( 'get_block_type_uses_context', 'auto_sizes_allowed_uses_context_for_image_blocks', 10, 2 ); + +/** + * Modifies the block context during rendering to blocks. + * + * @since n.e.x.t + * + * @param array $context Current block context. + * @param array $block The block being rendered. + * @return array Modified block context. + */ +function auto_sizes_modify_render_block_context( array $context, array $block ): array { + if ( 'core/group' === $block['blockName'] || 'core/columns' === $block['blockName'] ) { + $context['is_parent_block'] = true; + $context['ancestor_block_align'] = $block['attrs']['align'] ?? ''; + } + return $context; +} +add_filter( 'render_block_context', 'auto_sizes_modify_render_block_context', 10, 2 ); diff --git a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php index ca19201626..d3e378b463 100644 --- a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -390,17 +390,130 @@ public function test_no_image(): void { * Test that the layout property of a group block is passed by context to the image block. * * @group test + * + * @dataProvider data_ancestor_and_image_block_alignment + * + * @param string $ancestor_block_alignment Ancestor block alignment. + * @param string $image_block_alignment Image block alignment. + * @param string $expected Expected output. */ - public function test_ancestor_layout_is_passed_by_context(): void { + public function test_ancestor_layout_is_passed_by_context( string $ancestor_block_alignment, string $image_block_alignment, string $expected ): void { $block_content = $this->get_group_block_markup( - $this->get_image_block_markup( self::$image_id, 'large', 'full' ) + $this->get_image_block_markup( self::$image_id, 'large', $image_block_alignment ), + array( + 'align' => $ancestor_block_alignment, + ) ); $result = apply_filters( 'the_content', $block_content ); - $this->assertStringContainsString( 'sizes="(max-width: 620px) 100vw, 620px" ', $result ); + $this->assertStringContainsString( $expected, $result ); } + /** + * Data provider. + * + * @return array> The ancestor and image alignments. + */ + public function data_ancestor_and_image_block_alignment(): array { + return array( + // Parent defaule alignment. + 'Return contentSize 620px, parent block default alignment, image block default alignment' => array( + '', + '', + 'sizes="(max-width: 620px) 100vw, 620px" ', + ), + 'Return contentSize 620px, parent block default alignment, image block wide alignment' => array( + '', + 'wide', + 'sizes="(max-width: 620px) 100vw, 620px" ', + ), + 'Return contentSize 620px, parent block default alignment, image block full alignment' => array( + '', + 'full', + 'sizes="(max-width: 620px) 100vw, 620px" ', + ), + 'Return contentSize 620px, parent block default alignment, image block left alignment' => array( + '', + 'left', + 'sizes="(max-width: 620px) 100vw, 620px" ', + ), + 'Return contentSize 620px, parent block default alignment, image block center alignment' => array( + '', + 'center', + 'sizes="(max-width: 620px) 100vw, 620px" ', + ), + 'Return contentSize 620px, parent block default alignment, image block right alignment' => array( + '', + 'right', + 'sizes="(max-width: 620px) 100vw, 620px" ', + ), + + // Parent wide alignment. + 'Return contentSize 620px, parent block wide alignment, image block default alignment' => array( + 'wide', + '', + 'sizes="(max-width: 620px) 100vw, 620px" ', + ), + 'Return wideSize 1280px, parent block wide alignment, image block wide alignment' => array( + 'wide', + 'wide', + 'sizes="(max-width: 1280px) 100vw, 1280px" ', + ), + 'Return wideSize 1280px, parent block wide alignment, image block full alignment' => array( + 'wide', + 'full', + 'sizes="(max-width: 1280px) 100vw, 1280px" ', + ), + 'Return image size 1024px, parent block wide alignment, image block left alignment' => array( + 'wide', + 'left', + 'sizes="(max-width: 1024px) 100vw, 1024px" ', + ), + 'Return image size 1024px, parent block wide alignment, image block center alignment' => array( + 'wide', + 'center', + 'sizes="(max-width: 1024px) 100vw, 1024px" ', + ), + 'Return image size 1024px, parent block wide alignment, image block right alignment' => array( + 'wide', + 'right', + 'sizes="(max-width: 1024px) 100vw, 1024px" ', + ), + + // Parent full alignment. + 'Return contentSize 620px, parent block full alignment, image block default alignment' => array( + 'full', + '', + 'sizes="(max-width: 620px) 100vw, 620px" ', + ), + 'Return wideSize 1280px, parent block full alignment, image block wide alignment' => array( + 'full', + 'wide', + 'sizes="(max-width: 1280px) 100vw, 1280px" ', + ), + 'Return full size, parent block full alignment, image block full alignment' => array( + 'full', + 'full', + 'sizes="100vw" ', + ), + 'Return image size 1024px, parent block full alignment, image block left alignment' => array( + 'full', + 'left', + 'sizes="(max-width: 1024px) 100vw, 1024px" ', + ), + 'Return image size 1024px, parent block full alignment, image block center alignment' => array( + 'full', + 'center', + 'sizes="(max-width: 1024px) 100vw, 1024px" ', + ), + 'Return image size 1024px, parent block full alignment, image block right alignment' => array( + 'full', + 'right', + 'sizes="(max-width: 1024px) 100vw, 1024px" ', + ), + ); + } /** * Helper to generate image block markup. From 894ed572c4da6b7730fde4331c5920478bbb817f Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Tue, 26 Nov 2024 15:43:46 +0530 Subject: [PATCH 049/237] Fix spell --- plugins/auto-sizes/tests/test-improve-calculate-sizes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php index d3e378b463..4fa3079757 100644 --- a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -417,7 +417,7 @@ public function test_ancestor_layout_is_passed_by_context( string $ancestor_bloc */ public function data_ancestor_and_image_block_alignment(): array { return array( - // Parent defaule alignment. + // Parent default alignment. 'Return contentSize 620px, parent block default alignment, image block default alignment' => array( '', '', From cf8691a0ed189a2959e6864cad8cb1618f1f2cc1 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Tue, 26 Nov 2024 16:17:21 +0530 Subject: [PATCH 050/237] Clean up --- .../auto-sizes/tests/test-improve-calculate-sizes.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php index 4fa3079757..7f39c8bbce 100644 --- a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -520,21 +520,17 @@ public function data_ancestor_and_image_block_alignment(): array { * * @param int $attachment_id Attachment ID. * @param string $size Optional. Image size. Default 'full'. - * @param string $align Optional. Image alignment. Default null. + * @param string $align Optional. Image alignment. Default null. * @return string Image block markup. */ public function get_image_block_markup( int $attachment_id, string $size = 'full', string $align = null ): string { $image_url = wp_get_attachment_image_url( $attachment_id, $size ); $atts = wp_parse_args( - array( - 'id' => $attachment_id, - 'sizeSlug' => $size, - 'align' => $align, - ), array( 'id' => $attachment_id, - 'sizeSlug' => 'large', + 'sizeSlug' => $size, + 'align' => $align, 'linkDestination' => 'none', ) ); From ff1b4409a9e3faa819ab3b5a2796d972643f3080 Mon Sep 17 00:00:00 2001 From: b1ink0 Date: Tue, 26 Nov 2024 23:01:01 +0530 Subject: [PATCH 051/237] Add missing errors to cache, Shorten cache duration for any error --- .../includes/admin/plugins.php | 99 +++++++++++-------- 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index ad65e51351..52d50bbed7 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -54,77 +54,94 @@ function perflab_query_plugin_info( string $plugin_slug ) { ) ); + $has_errors = false; + $plugins = array(); + if ( is_wp_error( $response ) ) { - return new WP_Error( - 'api_error', - sprintf( - /* translators: %s: API error message */ - __( 'Failed to retrieve plugins data from WordPress.org API: %s', 'performance-lab' ), - $response->get_error_message() - ) + $plugins[ $plugin_slug ] = array( + 'error' => array( + 'code' => 'api_error', + 'message' => sprintf( + /* translators: %s: API error message */ + __( 'Failed to retrieve plugins data from WordPress.org API: %s', 'performance-lab' ), + $response->get_error_message() + ), + ), ); + + $has_errors = true; } // Check if the response contains plugins. - if ( ! ( is_object( $response ) && property_exists( $response, 'plugins' ) ) ) { - return new WP_Error( 'no_plugins', __( 'No plugins found in the API response.', 'performance-lab' ) ); + if ( ! $has_errors && ! ( is_object( $response ) && property_exists( $response, 'plugins' ) ) ) { + $plugins[ $plugin_slug ] = array( + 'error' => array( + 'code' => 'no_plugins', + 'message' => __( 'No plugins found in the API response.', 'performance-lab' ), + ), + ); + + $has_errors = true; } - $plugins = array(); - $plugin_queue = perflab_get_standalone_plugins(); + if ( ! $has_errors && is_object( $response ) && property_exists( $response, 'plugins' ) ) { + $plugin_queue = perflab_get_standalone_plugins(); - // Index the plugins from the API response by their slug for efficient lookup. - $all_performance_plugins = array_column( $response->plugins, null, 'slug' ); + // Index the plugins from the API response by their slug for efficient lookup. + $all_performance_plugins = array_column( $response->plugins, null, 'slug' ); - // Start processing the plugins using a queue-based approach. - while ( count( $plugin_queue ) > 0 ) { // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found - $current_plugin_slug = array_shift( $plugin_queue ); + // Start processing the plugins using a queue-based approach. + while ( count( $plugin_queue ) > 0 ) { // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found + $current_plugin_slug = array_shift( $plugin_queue ); - if ( isset( $plugins[ $current_plugin_slug ] ) ) { - continue; + if ( isset( $plugins[ $current_plugin_slug ] ) ) { + continue; + } + + if ( ! isset( $all_performance_plugins[ $current_plugin_slug ] ) ) { + // Cache the fact that the plugin was not found. + $plugins[ $current_plugin_slug ] = array( + 'error' => array( + 'code' => 'plugin_not_found', + 'message' => __( 'Plugin not found in API response.', 'performance-lab' ), + ), + ); + continue; + } + + $plugin_data = $all_performance_plugins[ $current_plugin_slug ]; + $plugins[ $current_plugin_slug ] = wp_array_slice_assoc( $plugin_data, $fields ); + + // Enqueue the required plugins slug by adding it to the queue. + if ( isset( $plugin_data['requires_plugins'] ) && is_array( $plugin_data['requires_plugins'] ) ) { + $plugin_queue = array_merge( $plugin_queue, $plugin_data['requires_plugins'] ); + } } - if ( ! isset( $all_performance_plugins[ $current_plugin_slug ] ) ) { + if ( ! isset( $plugins[ $plugin_slug ] ) ) { // Cache the fact that the plugin was not found. - $plugins[ $current_plugin_slug ] = array( + $plugins[ $plugin_slug ] = array( 'error' => array( 'code' => 'plugin_not_found', 'message' => __( 'Plugin not found in API response.', 'performance-lab' ), ), ); - continue; - } - $plugin_data = $all_performance_plugins[ $current_plugin_slug ]; - $plugins[ $current_plugin_slug ] = wp_array_slice_assoc( $plugin_data, $fields ); - - // Enqueue the required plugins slug by adding it to the queue. - if ( isset( $plugin_data['requires_plugins'] ) && is_array( $plugin_data['requires_plugins'] ) ) { - $plugin_queue = array_merge( $plugin_queue, $plugin_data['requires_plugins'] ); + $has_errors = true; } } - if ( ! isset( $plugins[ $plugin_slug ] ) ) { - // Cache the fact that the plugin was not found. - $plugins[ $plugin_slug ] = array( - 'error' => array( - 'code' => 'plugin_not_found', - 'message' => __( 'Plugin not found in API response.', 'performance-lab' ), - ), - ); - + if ( $has_errors ) { set_transient( $transient_key, $plugins, MINUTE_IN_SECONDS ); - } else { - set_transient( $transient_key, $plugins, HOUR_IN_SECONDS ); - } - if ( isset( $plugins[ $plugin_slug ]['error'] ) ) { return new WP_Error( $plugins[ $plugin_slug ]['error']['code'], $plugins[ $plugin_slug ]['error']['message'] ); } + set_transient( $transient_key, $plugins, HOUR_IN_SECONDS ); + /** * Validated (mostly) plugin data. * From 93d3f08be42e97b74cf301a05895002f8fd8d922 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Thu, 28 Nov 2024 09:31:37 +0530 Subject: [PATCH 052/237] Remove test group from unit test --- plugins/auto-sizes/tests/test-improve-calculate-sizes.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php index 7f39c8bbce..dd578800b7 100644 --- a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -389,8 +389,6 @@ public function test_no_image(): void { /** * Test that the layout property of a group block is passed by context to the image block. * - * @group test - * * @dataProvider data_ancestor_and_image_block_alignment * * @param string $ancestor_block_alignment Ancestor block alignment. From f1ec64d43e50694917c4e0e83d3b59803ef92b1b Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Thu, 28 Nov 2024 09:39:35 +0530 Subject: [PATCH 053/237] Move add_filter() calls in hooks.php --- plugins/auto-sizes/hooks.php | 2 ++ plugins/auto-sizes/includes/improve-calculate-sizes.php | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index 45612d9af2..21899cd332 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -38,3 +38,5 @@ function auto_sizes_render_generator(): void { add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 9 ); // This must run before 'do_blocks', which runs at priority 9. add_filter( 'render_block_core/image', 'auto_sizes_filter_image_tag', 10, 3 ); add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 3 ); +add_filter( 'get_block_type_uses_context', 'auto_sizes_allowed_uses_context_for_image_blocks', 10, 2 ); +add_filter( 'render_block_context', 'auto_sizes_modify_render_block_context', 10, 2 ); diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 072effdfff..4adc86555e 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -240,7 +240,6 @@ function auto_sizes_allowed_uses_context_for_image_blocks( array $uses_context, } return $uses_context; } -add_filter( 'get_block_type_uses_context', 'auto_sizes_allowed_uses_context_for_image_blocks', 10, 2 ); /** * Modifies the block context during rendering to blocks. @@ -258,4 +257,3 @@ function auto_sizes_modify_render_block_context( array $context, array $block ): } return $context; } -add_filter( 'render_block_context', 'auto_sizes_modify_render_block_context', 10, 2 ); From 27357137e6afc1e5378b7fefbf204dee01ef2ce8 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Thu, 28 Nov 2024 09:43:42 +0530 Subject: [PATCH 054/237] Remove unused wp_parse_args from unit test --- .../tests/test-improve-calculate-sizes.php | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php index dd578800b7..188d73065d 100644 --- a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -524,13 +524,11 @@ public function data_ancestor_and_image_block_alignment(): array { public function get_image_block_markup( int $attachment_id, string $size = 'full', string $align = null ): string { $image_url = wp_get_attachment_image_url( $attachment_id, $size ); - $atts = wp_parse_args( - array( - 'id' => $attachment_id, - 'sizeSlug' => $size, - 'align' => $align, - 'linkDestination' => 'none', - ) + $atts = array( + 'id' => $attachment_id, + 'sizeSlug' => $size, + 'align' => $align, + 'linkDestination' => 'none', ); return '
'; From b84d33e5399b03ed9a25883be6e6e15c02c774cd Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Thu, 28 Nov 2024 10:26:28 +0530 Subject: [PATCH 055/237] Remove parent block context instead use existing key for check --- .../includes/improve-calculate-sizes.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 4adc86555e..12bc6e83e8 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -99,14 +99,14 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b * @param string $sizes The image sizes attribute value. * @param string $size The image size data. */ - $filter = static function ( $sizes, $size ) use ( $block ) { + $filter = static function ( $sizes, $size ) use ( $block, $parsed_block ) { $id = $block->attributes['id'] ?? 0; $alignment = $block->attributes['align'] ?? ''; $width = $block->attributes['width'] ?? ''; - $is_parent_block = $block->context['is_parent_block'] ?? false; + $has_parent_block = isset( $parsed_block['parentLayout'] ); $ancestor_block_align = $block->context['ancestor_block_align'] ?? ''; - return auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width, (bool) $is_parent_block, (string) $ancestor_block_align ); + return auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width, $has_parent_block, (string) $ancestor_block_align ); }; // Hook this filter early, before default filters are run. @@ -144,11 +144,11 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b * @param string $size The image size data. * @param string $align The image alignment. * @param string $resize_width Resize image width. - * @param bool $is_parent_block Check if image block has parent block. + * @param bool $has_parent_block Check if image block has parent block. * @param string $ancestor_block_align The ancestor block alignment. * @return string The sizes attribute value. */ -function auto_sizes_calculate_better_sizes( int $id, string $size, string $align, string $resize_width, bool $is_parent_block, string $ancestor_block_align ): string { +function auto_sizes_calculate_better_sizes( int $id, string $size, string $align, string $resize_width, bool $has_parent_block, string $ancestor_block_align ): string { $image = wp_get_attachment_image_src( $id, $size ); if ( false === $image ) { @@ -158,7 +158,7 @@ function auto_sizes_calculate_better_sizes( int $id, string $size, string $align // Retrieve width from the image tag itself. $image_width = '' !== $resize_width ? (int) $resize_width : $image[1]; - if ( $is_parent_block ) { + if ( $has_parent_block ) { if ( 'full' === $ancestor_block_align && 'full' === $align ) { return auto_sizes_get_sizes_by_block_alignments( $align, $image_width, true ); } elseif ( 'full' !== $ancestor_block_align && 'full' === $align ) { @@ -236,7 +236,7 @@ function auto_sizes_get_sizes_by_block_alignments( string $alignment, int $image function auto_sizes_allowed_uses_context_for_image_blocks( array $uses_context, WP_Block_Type $block_type ): array { if ( 'core/image' === $block_type->name ) { // Use array_values to reset the array keys after merging. - return array_values( array_unique( array_merge( $uses_context, array( 'is_parent_block', 'ancestor_block_align' ) ) ) ); + return array_values( array_unique( array_merge( $uses_context, array( 'ancestor_block_align' ) ) ) ); } return $uses_context; } @@ -252,7 +252,6 @@ function auto_sizes_allowed_uses_context_for_image_blocks( array $uses_context, */ function auto_sizes_modify_render_block_context( array $context, array $block ): array { if ( 'core/group' === $block['blockName'] || 'core/columns' === $block['blockName'] ) { - $context['is_parent_block'] = true; $context['ancestor_block_align'] = $block['attrs']['align'] ?? ''; } return $context; From 5a9ce372df91190a8baa32411766164a5bdc6f48 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Thu, 28 Nov 2024 11:09:10 +0530 Subject: [PATCH 056/237] Bump unit test WP version to 6.6 --- .github/workflows/php-test-plugins.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/php-test-plugins.yml b/.github/workflows/php-test-plugins.yml index 3594515140..97e862abfd 100644 --- a/.github/workflows/php-test-plugins.yml +++ b/.github/workflows/php-test-plugins.yml @@ -46,7 +46,7 @@ jobs: coverage: [false] include: - php: '7.4' - wp: '6.5' + wp: '6.6' - php: '8.3' wp: 'trunk' - php: '8.2' From 1d3ef59a2f98be8dcc13e58f9f5086f6868a0105 Mon Sep 17 00:00:00 2001 From: b1ink0 Date: Thu, 28 Nov 2024 13:50:59 +0530 Subject: [PATCH 057/237] Cache error for standalone plugins and update error message for missing plugins --- plugins/performance-lab/includes/admin/plugins.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index 52d50bbed7..8ffaa35fb2 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -84,6 +84,13 @@ function perflab_query_plugin_info( string $plugin_slug ) { $has_errors = true; } + // Cache error for all standalone plugins. + if ( $has_errors ) { + foreach ( perflab_get_standalone_plugins() as $standalone_plugin ) { + $plugins[ $standalone_plugin ] = $plugins[ $plugin_slug ]; + } + } + if ( ! $has_errors && is_object( $response ) && property_exists( $response, 'plugins' ) ) { $plugin_queue = perflab_get_standalone_plugins(); @@ -123,7 +130,7 @@ function perflab_query_plugin_info( string $plugin_slug ) { $plugins[ $plugin_slug ] = array( 'error' => array( 'code' => 'plugin_not_found', - 'message' => __( 'Plugin not found in API response.', 'performance-lab' ), + 'message' => __( 'The requested plugin is not part of Performance Lab plugins.', 'performance-lab' ), ), ); From de77338cd9228272462164ae1542f2582ee16008 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 2 Dec 2024 15:44:34 +0530 Subject: [PATCH 058/237] implement static cache --- .../includes/improve-calculate-sizes.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 12bc6e83e8..9069029a2f 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -190,7 +190,7 @@ function auto_sizes_calculate_better_sizes( int $id, string $size, string $align function auto_sizes_get_sizes_by_block_alignments( string $alignment, int $image_width, bool $print_sizes = false ): string { $sizes = ''; - $layout = wp_get_global_settings( array( 'layout' ) ); + $layout = auto_sizes_get_layout_settings(); // Handle different alignment use cases. switch ( $alignment ) { @@ -256,3 +256,18 @@ function auto_sizes_modify_render_block_context( array $context, array $block ): } return $context; } + +/** + * Retrieves the layout settings defined in theme.json. + * + * @since n.e.x.t + * + * @return array Associative array of layout settings. + */ +function auto_sizes_get_layout_settings(): array { + static $layout = array(); + if ( count( $layout ) === 0 ) { + $layout = wp_get_global_settings( array( 'layout' ) ); + } + return $layout; +} From d2e90d36e448bdb2ff325fbdcf2af832febf78fa Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 2 Dec 2024 09:53:57 -0800 Subject: [PATCH 059/237] Increase tag max length to 100 --- plugins/image-prioritizer/detect.js | 5 +++-- plugins/image-prioritizer/helper.php | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/image-prioritizer/detect.js b/plugins/image-prioritizer/detect.js index e04a81d0dd..acc72d8a78 100644 --- a/plugins/image-prioritizer/detect.js +++ b/plugins/image-prioritizer/detect.js @@ -153,8 +153,8 @@ function handleLCPMetric( metric, isDebug ) { return; } - // Also skip Custom Elements which have excessively long tag names. - if ( entry.element.tagName.length > 25 ) { + // Also skip Custom Elements which have excessively long tag names. This is the maxLength defined in image_prioritizer_add_element_item_schema_properties(). + if ( entry.element.tagName.length > 100 ) { if ( isDebug ) { log( `Skipping very long tag name: ${ entry.element.tagName }` @@ -164,6 +164,7 @@ function handleLCPMetric( metric, isDebug ) { } // Note that getAttribute() is used instead of properties so that null can be returned in case of an absent attribute. + // The maxLengths are defined in image_prioritizer_add_element_item_schema_properties(). const id = entry.element.getAttribute( 'id' ); if ( typeof id === 'string' && id.length > 100 ) { if ( isDebug ) { diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 5667a97d2e..6b9a6d4236 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -135,8 +135,11 @@ function image_prioritizer_add_element_item_schema_properties( array $additional 'type' => 'string', 'required' => true, 'minLength' => 1, - 'maxLength' => 25, // The longest HTML tag name is 10 characters (BLOCKQUOTE and FIGCAPTION). This maxLength accounts for possible Custom Elements that are even longer. - 'pattern' => '^[a-zA-Z0-9\-]+$', // Technically emoji can be allowed in a custom element's tag name, but this is not supported here. + // The longest HTML tag name is 10 characters (BLOCKQUOTE and FIGCAPTION), but SVG tag names can be longer + // (e.g. feComponentTransfer). This maxLength accounts for possible Custom Elements that are even longer, + // although the longest known Custom Element from HTTP Archive is 32 characters. See data from . + 'maxLength' => 100, + 'pattern' => '^[a-zA-Z0-9\-]+\z', // Technically emoji can be allowed in a custom element's tag name, but this is not supported here. ), 'id' => array( 'type' => array( 'string', 'null' ), From f54673128558bae000b2d4cfb7781bd462991a70 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 2 Dec 2024 11:35:33 -0800 Subject: [PATCH 060/237] Remove erroneous function from merge conflict in e642c9945 --- plugins/image-prioritizer/helper.php | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 6b9a6d4236..f569ecbb06 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -77,26 +77,6 @@ function image_prioritizer_register_tag_visitors( OD_Tag_Visitor_Registry $regis $registry->register( 'image-prioritizer/video', $video_visitor ); } -/** - * Gets the script to lazy-load videos. - * - * Load a video and its poster image when it approaches the viewport using an IntersectionObserver. - * - * Handles 'autoplay' and 'preload' attributes accordingly. - * - * @since 0.2.0 - */ -function image_prioritizer_get_lazy_load_script(): string { - $path = image_prioritizer_get_asset_path( 'lazy-load.js' ); - $script = file_get_contents( __DIR__ . '/' . $path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- It's a local filesystem path not a remote request. - - if ( false === $script ) { - return ''; - } - - return $script; -} - /** * Filters the list of Optimization Detective extension module URLs to include the extension for Image Prioritizer. * From ec59d85fab7d02d49de49b1b4d9fd899894908eb Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 2 Dec 2024 11:46:57 -0800 Subject: [PATCH 061/237] Reduce concern about unsetting array keys during iteration --- ...s-image-prioritizer-background-image-styled-tag-visitor.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php index beffe5ae48..293593e808 100644 --- a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php +++ b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php @@ -157,7 +157,8 @@ private function maybe_preload_external_lcp_background_image( OD_Tag_Visitor_Con $processor = $context->processor; $tag_name = strtoupper( (string) $processor->get_tag() ); - foreach ( $this->group_common_lcp_element_external_background_images as $i => list( $group, $common ) ) { + foreach ( array_keys( $this->group_common_lcp_element_external_background_images ) as $i ) { + list( $group, $common ) = $this->group_common_lcp_element_external_background_images[ $i ]; if ( // Note that the browser may send a lower-case tag name in the case of XHTML or embedded SVG/MathML, but // the HTML Tag Processor is currently normalizing to all upper-case. The HTML Processor on the other From 514c51344f993c010edff305c27084c65e3da70d Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 2 Dec 2024 13:17:02 -0800 Subject: [PATCH 062/237] Remove od_store_url_metric_validity filter to be re-added in follow-up PR --- plugins/image-prioritizer/helper.php | 60 ------------------- plugins/image-prioritizer/hooks.php | 1 - .../storage/rest-api.php | 29 --------- 3 files changed, 90 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index f569ecbb06..c93153dcfa 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -136,66 +136,6 @@ function image_prioritizer_add_element_item_schema_properties( array $additional return $additional_properties; } -/** - * Validates that the provided background image URL is valid. - * - * @since n.e.x.t - * - * @param bool|WP_Error|mixed $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. - * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. - * @return bool|WP_Error Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. - */ -function image_prioritizer_filter_store_url_metric_validity( $validity, OD_Strict_URL_Metric $url_metric ) { - if ( ! is_bool( $validity ) && ! ( $validity instanceof WP_Error ) ) { - $validity = (bool) $validity; - } - - $data = $url_metric->get( 'lcpElementExternalBackgroundImage' ); - if ( ! is_array( $data ) ) { - return $validity; - } - - $r = wp_safe_remote_head( - $data['url'], - array( - 'redirection' => 3, // Allow up to 3 redirects. - ) - ); - if ( $r instanceof WP_Error ) { - return new WP_Error( - WP_DEBUG ? $r->get_error_code() : 'head_request_failure', - __( 'HEAD request for background image URL failed.', 'image-prioritizer' ) . ( WP_DEBUG ? ' ' . $r->get_error_message() : '' ), - array( - 'code' => 500, - ) - ); - } - $response_code = wp_remote_retrieve_response_code( $r ); - if ( $response_code < 200 || $response_code >= 400 ) { - return new WP_Error( - 'background_image_response_not_ok', - __( 'HEAD request for background image URL did not return with a success status code.', 'image-prioritizer' ), - array( - 'code' => WP_DEBUG ? $response_code : 400, - ) - ); - } - - $content_type = wp_remote_retrieve_header( $r, 'Content-Type' ); - if ( ! is_string( $content_type ) || ! str_starts_with( $content_type, 'image/' ) ) { - return new WP_Error( - 'background_image_response_not_image', - __( 'HEAD request for background image URL did not return an image Content-Type.', 'image-prioritizer' ), - array( - 'code' => 400, - ) - ); - } - - // TODO: Check for the Content-Length and return invalid if it is gigantic? - return $validity; -} - /** * Gets the path to a script or stylesheet. * diff --git a/plugins/image-prioritizer/hooks.php b/plugins/image-prioritizer/hooks.php index 4a47f35647..7587e9e67b 100644 --- a/plugins/image-prioritizer/hooks.php +++ b/plugins/image-prioritizer/hooks.php @@ -13,4 +13,3 @@ add_action( 'od_init', 'image_prioritizer_init' ); add_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ); add_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ); -add_filter( 'od_store_url_metric_validity', 'image_prioritizer_filter_store_url_metric_validity', 10, 2 ); diff --git a/plugins/optimization-detective/storage/rest-api.php b/plugins/optimization-detective/storage/rest-api.php index 8e4828bec4..38f3c9ddaa 100644 --- a/plugins/optimization-detective/storage/rest-api.php +++ b/plugins/optimization-detective/storage/rest-api.php @@ -210,35 +210,6 @@ function od_handle_rest_request( WP_REST_Request $request ) { ); } - /** - * Filters whether a URL Metric is valid for storage. - * - * This allows for custom validation constraints to be applied beyond what can be expressed in JSON Schema. This is - * also necessary because the 'validate_callback' key in a JSON Schema is not respected when gathering the REST API - * endpoint args via the {@see rest_get_endpoint_args_for_schema()} function. Besides this, the REST API doesn't - * support 'validate_callback' for any nested arguments in any case, meaning that custom constraints would be able - * to be applied to multidimensional objects, such as the items inside 'elements'. - * - * This filter only applies when storing a URL Metric via the REST API. It does not run when a stored URL Metric - * loaded from the od_url_metric post type. This means that validation logic enforced via this filter can be more - * expensive, such as doing filesystem checks or HTTP requests. - * - * @since n.e.x.t - * - * @param bool|WP_Error $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. - * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. - */ - $validity = apply_filters( 'od_store_url_metric_validity', true, $url_metric ); - if ( false === $validity || ( $validity instanceof WP_Error && $validity->has_errors() ) ) { - if ( false === $validity ) { - $validity = new WP_Error( 'invalid_url_metric', __( 'Validity of URL Metric was rejected by filter.', 'optimization-detective' ) ); - } - if ( ! isset( $validity->error_data['code'] ) ) { - $validity->error_data['code'] = 400; - } - return $validity; - } - // TODO: This should be changed from store_url_metric($slug, $url_metric) instead be update_post( $slug, $group_collection ). As it stands, store_url_metric() is duplicating logic here. $result = OD_URL_Metrics_Post_Type::store_url_metric( $request->get_param( 'slug' ), From 57a35e4fb61ac5c7826c7fada942ddca609ec792 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 2 Dec 2024 13:18:35 -0800 Subject: [PATCH 063/237] Revert "Remove od_store_url_metric_validity filter to be re-added in follow-up PR" This reverts commit 514c51344f993c010edff305c27084c65e3da70d. --- plugins/image-prioritizer/helper.php | 60 +++++++++++++++++++ plugins/image-prioritizer/hooks.php | 1 + .../storage/rest-api.php | 29 +++++++++ 3 files changed, 90 insertions(+) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index c93153dcfa..f569ecbb06 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -136,6 +136,66 @@ function image_prioritizer_add_element_item_schema_properties( array $additional return $additional_properties; } +/** + * Validates that the provided background image URL is valid. + * + * @since n.e.x.t + * + * @param bool|WP_Error|mixed $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. + * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. + * @return bool|WP_Error Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. + */ +function image_prioritizer_filter_store_url_metric_validity( $validity, OD_Strict_URL_Metric $url_metric ) { + if ( ! is_bool( $validity ) && ! ( $validity instanceof WP_Error ) ) { + $validity = (bool) $validity; + } + + $data = $url_metric->get( 'lcpElementExternalBackgroundImage' ); + if ( ! is_array( $data ) ) { + return $validity; + } + + $r = wp_safe_remote_head( + $data['url'], + array( + 'redirection' => 3, // Allow up to 3 redirects. + ) + ); + if ( $r instanceof WP_Error ) { + return new WP_Error( + WP_DEBUG ? $r->get_error_code() : 'head_request_failure', + __( 'HEAD request for background image URL failed.', 'image-prioritizer' ) . ( WP_DEBUG ? ' ' . $r->get_error_message() : '' ), + array( + 'code' => 500, + ) + ); + } + $response_code = wp_remote_retrieve_response_code( $r ); + if ( $response_code < 200 || $response_code >= 400 ) { + return new WP_Error( + 'background_image_response_not_ok', + __( 'HEAD request for background image URL did not return with a success status code.', 'image-prioritizer' ), + array( + 'code' => WP_DEBUG ? $response_code : 400, + ) + ); + } + + $content_type = wp_remote_retrieve_header( $r, 'Content-Type' ); + if ( ! is_string( $content_type ) || ! str_starts_with( $content_type, 'image/' ) ) { + return new WP_Error( + 'background_image_response_not_image', + __( 'HEAD request for background image URL did not return an image Content-Type.', 'image-prioritizer' ), + array( + 'code' => 400, + ) + ); + } + + // TODO: Check for the Content-Length and return invalid if it is gigantic? + return $validity; +} + /** * Gets the path to a script or stylesheet. * diff --git a/plugins/image-prioritizer/hooks.php b/plugins/image-prioritizer/hooks.php index 7587e9e67b..4a47f35647 100644 --- a/plugins/image-prioritizer/hooks.php +++ b/plugins/image-prioritizer/hooks.php @@ -13,3 +13,4 @@ add_action( 'od_init', 'image_prioritizer_init' ); add_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ); add_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ); +add_filter( 'od_store_url_metric_validity', 'image_prioritizer_filter_store_url_metric_validity', 10, 2 ); diff --git a/plugins/optimization-detective/storage/rest-api.php b/plugins/optimization-detective/storage/rest-api.php index 38f3c9ddaa..8e4828bec4 100644 --- a/plugins/optimization-detective/storage/rest-api.php +++ b/plugins/optimization-detective/storage/rest-api.php @@ -210,6 +210,35 @@ function od_handle_rest_request( WP_REST_Request $request ) { ); } + /** + * Filters whether a URL Metric is valid for storage. + * + * This allows for custom validation constraints to be applied beyond what can be expressed in JSON Schema. This is + * also necessary because the 'validate_callback' key in a JSON Schema is not respected when gathering the REST API + * endpoint args via the {@see rest_get_endpoint_args_for_schema()} function. Besides this, the REST API doesn't + * support 'validate_callback' for any nested arguments in any case, meaning that custom constraints would be able + * to be applied to multidimensional objects, such as the items inside 'elements'. + * + * This filter only applies when storing a URL Metric via the REST API. It does not run when a stored URL Metric + * loaded from the od_url_metric post type. This means that validation logic enforced via this filter can be more + * expensive, such as doing filesystem checks or HTTP requests. + * + * @since n.e.x.t + * + * @param bool|WP_Error $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. + * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. + */ + $validity = apply_filters( 'od_store_url_metric_validity', true, $url_metric ); + if ( false === $validity || ( $validity instanceof WP_Error && $validity->has_errors() ) ) { + if ( false === $validity ) { + $validity = new WP_Error( 'invalid_url_metric', __( 'Validity of URL Metric was rejected by filter.', 'optimization-detective' ) ); + } + if ( ! isset( $validity->error_data['code'] ) ) { + $validity->error_data['code'] = 400; + } + return $validity; + } + // TODO: This should be changed from store_url_metric($slug, $url_metric) instead be update_post( $slug, $group_collection ). As it stands, store_url_metric() is duplicating logic here. $result = OD_URL_Metrics_Post_Type::store_url_metric( $request->get_param( 'slug' ), From 57df74084bd36805187e439f971086ccb49aa726 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 2 Dec 2024 13:19:58 -0800 Subject: [PATCH 064/237] Use 'status' key instead of 'code' Co-authored-by: felixarntz --- plugins/optimization-detective/storage/rest-api.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/storage/rest-api.php b/plugins/optimization-detective/storage/rest-api.php index 8e4828bec4..bafbbb7e1c 100644 --- a/plugins/optimization-detective/storage/rest-api.php +++ b/plugins/optimization-detective/storage/rest-api.php @@ -233,8 +233,8 @@ function od_handle_rest_request( WP_REST_Request $request ) { if ( false === $validity ) { $validity = new WP_Error( 'invalid_url_metric', __( 'Validity of URL Metric was rejected by filter.', 'optimization-detective' ) ); } - if ( ! isset( $validity->error_data['code'] ) ) { - $validity->error_data['code'] = 400; + if ( ! isset( $validity->error_data['status'] ) ) { + $validity->error_data['status'] = 400; } return $validity; } From fbb607664d7198fe70b8f535be42efa4820b0a59 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 2 Dec 2024 13:38:00 -0800 Subject: [PATCH 065/237] Rename add_preload_link to add_image_preload_link Co-authored-by: felixarntz --- ...age-prioritizer-background-image-styled-tag-visitor.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php index 293593e808..91c20ec0a1 100644 --- a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php +++ b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php @@ -45,6 +45,7 @@ final class Image_Prioritizer_Background_Image_Styled_Tag_Visitor extends Image_ /** * Tuples of URL Metric group and the common LCP element external background image. * + * @since n.e.x.t * @var array */ private $group_common_lcp_element_external_background_images; @@ -87,7 +88,7 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { // If this element is the LCP (for a breakpoint group), add a preload link for it. foreach ( $context->url_metric_group_collection->get_groups_by_lcp_element( $xpath ) as $group ) { - $this->add_preload_link( $context->link_collection, $group, $background_image_url ); + $this->add_image_preload_link( $context->link_collection, $group, $background_image_url ); } $this->lazy_load_bg_images( $context ); @@ -169,7 +170,7 @@ private function maybe_preload_external_lcp_background_image( OD_Tag_Visitor_Con && $processor->get_attribute( 'class' ) === $common['class'] // May be checking equality with null. ) { - $this->add_preload_link( $context->link_collection, $group, $common['url'] ); + $this->add_image_preload_link( $context->link_collection, $group, $common['url'] ); // Now that the preload link has been added, eliminate the entry to stop looking for it while iterating over the rest of the document. unset( $this->group_common_lcp_element_external_background_images[ $i ] ); @@ -186,7 +187,7 @@ private function maybe_preload_external_lcp_background_image( OD_Tag_Visitor_Con * @param OD_URL_Metric_Group $group URL Metric group. * @param non-empty-string $url Image URL. */ - private function add_preload_link( OD_Link_Collection $link_collection, OD_URL_Metric_Group $group, string $url ): void { + private function add_image_preload_link( OD_Link_Collection $link_collection, OD_URL_Metric_Group $group, string $url ): void { $link_collection->add_link( array( 'rel' => 'preload', From ae82ccd0e76d36dec1e3d4313b883d8b22499e5b Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Tue, 3 Dec 2024 16:07:58 +0530 Subject: [PATCH 066/237] Separate function for width calculation and format sizes attribute --- .../includes/improve-calculate-sizes.php | 70 +++++++++++++++++-- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 9069029a2f..c5fdf78b4c 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -160,21 +160,77 @@ function auto_sizes_calculate_better_sizes( int $id, string $size, string $align if ( $has_parent_block ) { if ( 'full' === $ancestor_block_align && 'full' === $align ) { - return auto_sizes_get_sizes_by_block_alignments( $align, $image_width, true ); + $width = auto_sizes_calculate_width( $align, $image_width ); + return auto_sizes_format_sizes_attribute( $align, $width ); } elseif ( 'full' !== $ancestor_block_align && 'full' === $align ) { - return auto_sizes_get_sizes_by_block_alignments( $ancestor_block_align, $image_width, true ); + $width = auto_sizes_calculate_width( $ancestor_block_align, $image_width ); + return auto_sizes_format_sizes_attribute( $ancestor_block_align, $width ); } elseif ( 'full' !== $ancestor_block_align ) { - $parent_block_alignment_width = auto_sizes_get_sizes_by_block_alignments( $ancestor_block_align, $image_width ); - $block_alignment_width = auto_sizes_get_sizes_by_block_alignments( $align, $image_width ); + $parent_block_alignment_width = auto_sizes_calculate_width( $ancestor_block_align, $image_width ); + $block_alignment_width = auto_sizes_calculate_width( $align, $image_width ); if ( (int) $parent_block_alignment_width < (int) $block_alignment_width ) { - return sprintf( '(max-width: %1$s) 100vw, %1$s', $parent_block_alignment_width ); + return auto_sizes_format_sizes_attribute( $ancestor_block_align, $parent_block_alignment_width ); } else { - return sprintf( '(max-width: %1$s) 100vw, %1$s', $block_alignment_width ); + return auto_sizes_format_sizes_attribute( $align, $block_alignment_width ); } } } - return auto_sizes_get_sizes_by_block_alignments( $align, $image_width, true ); + $width = auto_sizes_calculate_width( $align, $image_width ); + return auto_sizes_format_sizes_attribute( $align, $width ); +} + +/** + * Calculates the width value for the `sizes` attribute based on block information. + * + * @since n.e.x.t + * + * @param string $alignment The alignment. + * @param int $image_width The image width. + * @return string The calculated width value. + */ +function auto_sizes_calculate_width( string $alignment, int $image_width ): string { + $sizes = ''; + $layout = auto_sizes_get_layout_settings(); + + // Handle different alignment use cases. + switch ( $alignment ) { + case 'full': + $sizes = '100vw'; + break; + + case 'wide': + $sizes = array_key_exists( 'wideSize', $layout ) ? $layout['wideSize'] : ''; + break; + + case 'left': + case 'right': + case 'center': + $sizes = auto_sizes_get_width( '', $image_width ); + break; + + default: + $sizes = array_key_exists( 'contentSize', $layout ) + ? auto_sizes_get_width( $layout['contentSize'], $image_width ) + : ''; + break; + } + return $sizes; +} + +/** + * Formats the `sizes` attribute value. + * + * @since n.e.x.t + * + * @param string $alignment The alignment. + * @param string $width The calculated width value. + * @return string The formatted sizes attribute value. + */ +function auto_sizes_format_sizes_attribute( string $alignment, string $width ): string { + return 'full' === $alignment + ? $width + : sprintf( '(max-width: %1$s) 100vw, %1$s', $width ); } /** From 8189d71f63d66b35d541658c37ad23eaad6cf050 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Tue, 3 Dec 2024 16:25:50 +0530 Subject: [PATCH 067/237] Remove unused old function --- .../includes/improve-calculate-sizes.php | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index c5fdf78b4c..7112da9be5 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -233,53 +233,6 @@ function auto_sizes_format_sizes_attribute( string $alignment, string $width ): : sprintf( '(max-width: %1$s) 100vw, %1$s', $width ); } -/** - * Generates the `sizes` attribute value based on block information. - * - * @since n.e.x.t - * - * @param string $alignment The alignment. - * @param int $image_width The image width. - * @param bool $print_sizes Print the sizes attribute. Default is false. - * @return string The sizes attribute value. - */ -function auto_sizes_get_sizes_by_block_alignments( string $alignment, int $image_width, bool $print_sizes = false ): string { - $sizes = ''; - - $layout = auto_sizes_get_layout_settings(); - - // Handle different alignment use cases. - switch ( $alignment ) { - case 'full': - $sizes = '100vw'; - break; - - case 'wide': - if ( array_key_exists( 'wideSize', $layout ) ) { - $sizes = $layout['wideSize']; - } - break; - - case 'left': - case 'right': - case 'center': - $sizes = auto_sizes_get_width( '', $image_width ); - break; - - default: - if ( array_key_exists( 'contentSize', $layout ) ) { - $sizes = auto_sizes_get_width( $layout['contentSize'], $image_width ); - } - break; - } - - if ( $print_sizes ) { - $sizes = 'full' === $alignment ? $sizes : sprintf( '(max-width: %1$s) 100vw, %1$s', $sizes ); - } - - return $sizes; -} - /** * Filters the context keys that a block type uses. * From 25d95ea72da9fce577832b5f5ad17ae6fd248f9f Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Tue, 3 Dec 2024 11:40:07 -0600 Subject: [PATCH 068/237] Rename filter callbacks for consistency --- plugins/auto-sizes/hooks.php | 4 ++-- plugins/auto-sizes/includes/improve-calculate-sizes.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/auto-sizes/hooks.php b/plugins/auto-sizes/hooks.php index 21899cd332..55cbe9dec2 100644 --- a/plugins/auto-sizes/hooks.php +++ b/plugins/auto-sizes/hooks.php @@ -38,5 +38,5 @@ function auto_sizes_render_generator(): void { add_filter( 'the_content', 'auto_sizes_prime_attachment_caches', 9 ); // This must run before 'do_blocks', which runs at priority 9. add_filter( 'render_block_core/image', 'auto_sizes_filter_image_tag', 10, 3 ); add_filter( 'render_block_core/cover', 'auto_sizes_filter_image_tag', 10, 3 ); -add_filter( 'get_block_type_uses_context', 'auto_sizes_allowed_uses_context_for_image_blocks', 10, 2 ); -add_filter( 'render_block_context', 'auto_sizes_modify_render_block_context', 10, 2 ); +add_filter( 'get_block_type_uses_context', 'auto_sizes_filter_uses_context', 10, 2 ); +add_filter( 'render_block_context', 'auto_sizes_filter_render_block_context', 10, 2 ); diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 7112da9be5..6a840c3280 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -242,7 +242,7 @@ function auto_sizes_format_sizes_attribute( string $alignment, string $width ): * @param WP_Block_Type $block_type The full block type object. * @return array The filtered context keys used by the block type. */ -function auto_sizes_allowed_uses_context_for_image_blocks( array $uses_context, WP_Block_Type $block_type ): array { +function auto_sizes_filter_uses_context( array $uses_context, WP_Block_Type $block_type ): array { if ( 'core/image' === $block_type->name ) { // Use array_values to reset the array keys after merging. return array_values( array_unique( array_merge( $uses_context, array( 'ancestor_block_align' ) ) ) ); @@ -259,7 +259,7 @@ function auto_sizes_allowed_uses_context_for_image_blocks( array $uses_context, * @param array $block The block being rendered. * @return array Modified block context. */ -function auto_sizes_modify_render_block_context( array $context, array $block ): array { +function auto_sizes_filter_render_block_context( array $context, array $block ): array { if ( 'core/group' === $block['blockName'] || 'core/columns' === $block['blockName'] ) { $context['ancestor_block_align'] = $block['attrs']['align'] ?? ''; } From 49692cb76c4e14e200d6dba25088bd7b7de3d68f Mon Sep 17 00:00:00 2001 From: b1ink0 Date: Tue, 3 Dec 2024 23:38:20 +0530 Subject: [PATCH 069/237] Change transient cache duration if any error is encountered --- plugins/performance-lab/includes/admin/plugins.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index 8ffaa35fb2..6e586112cf 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -113,6 +113,8 @@ function perflab_query_plugin_info( string $plugin_slug ) { 'message' => __( 'Plugin not found in API response.', 'performance-lab' ), ), ); + + $has_errors = true; continue; } @@ -140,15 +142,17 @@ function perflab_query_plugin_info( string $plugin_slug ) { if ( $has_errors ) { set_transient( $transient_key, $plugins, MINUTE_IN_SECONDS ); + } else { + set_transient( $transient_key, $plugins, HOUR_IN_SECONDS ); + } + if ( isset( $plugins[ $plugin_slug ]['error'] ) ) { return new WP_Error( $plugins[ $plugin_slug ]['error']['code'], $plugins[ $plugin_slug ]['error']['message'] ); } - set_transient( $transient_key, $plugins, HOUR_IN_SECONDS ); - /** * Validated (mostly) plugin data. * From 2ab00520c16eaff8a7c12200781601ba80711534 Mon Sep 17 00:00:00 2001 From: Paul Bearne Date: Tue, 3 Dec 2024 13:36:07 -0500 Subject: [PATCH 070/237] Enhance admin UI with dominant color support. Added inline CSS and JavaScript to display dominant colors and transparency in the WordPress admin area for attachment previews. This enhancement allows the media library to utilize dominant color and transparency data, providing visual feedback on image properties. Updated version to 1.1.2 to reflect these changes. --- plugins/dominant-color-images/hooks.php | 84 ++++++++++++++++++++++++ plugins/dominant-color-images/load.php | 4 +- plugins/dominant-color-images/readme.txt | 8 ++- 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index 1eb930dba0..bb41da22d4 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -193,3 +193,87 @@ function dominant_color_render_generator(): void { echo '' . "\n"; } add_action( 'wp_head', 'dominant_color_render_generator' ); + + + + +/** + * Adds inline CSS for dominant color styling in the WordPress admin area. + * + * This function registers and enqueues a custom style handle, then adds inline CSS + * to apply background color based on the dominant color for attachment previews + * in the WordPress admin interface. + * + * @since 1.1.2 + * + * @return void + */ +function dominant_color_admin_inline_style() { + $handle = 'dominant-color-admin-styles'; + // PHPCS ignore reason: Version not used since this handle is only registered for adding an inline style. + // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion + wp_register_style( $handle, false ); + wp_enqueue_style( $handle ); + $custom_css = '.wp-core-ui .attachment-preview[data-dominant-color]:not(.has-transparency) { background-color: var(--dominant-color); }'; + wp_add_inline_style( $handle, $custom_css ); +}; +add_action( 'admin_enqueue_scripts', 'dominant_color_admin_inline_style' ); + +/** + * Adds a script to the admin footer to modify the attachment template. + * + * This function injects a JavaScript snippet into the admin footer that modifies + * the attachment template. It adds attributes for dominant color and transparency + * to the template, allowing these properties to be displayed in the media library. + * + * @since 1.1.2 + * + * @return void + */ + function dominant_color_admin_script(){ + ?> + + Date: Tue, 3 Dec 2024 13:40:16 -0500 Subject: [PATCH 071/237] Bump version to 1.1.3 for dominant color images plugin Update version numbers and associated comments from 1.1.2 to 1.1.3 for consistency across the plugin files. This change ensures that the codebase reflects the correct version for recent updates or bug fixes included in this release. --- plugins/dominant-color-images/hooks.php | 6 +++--- plugins/dominant-color-images/load.php | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index 3d1fe5fb10..2d571e19a6 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -197,7 +197,7 @@ function dominant_color_render_generator(): void { * to apply background color based on the dominant color for attachment previews * in the WordPress admin interface. * - * @since 1.1.2 + * @since 1.1.3 * * @return void */ @@ -219,7 +219,7 @@ function dominant_color_admin_inline_style() { * the attachment template. It adds attributes for dominant color and transparency * to the template, allowing these properties to be displayed in the media library. * - * @since 1.1.2 + * @since 1.1.3 * * @return void */ @@ -245,7 +245,7 @@ function dominant_color_admin_script(){ * the dominant color and transparency of the image. It modifies the response array to include * these additional properties, which can be used in the media library interface. * - * @since 1.1.2 + * @since 1.1.3 * * @param array $response The current response array for the attachment. * @param WP_Post $attachment The attachment post object. diff --git a/plugins/dominant-color-images/load.php b/plugins/dominant-color-images/load.php index e2e3c0c1bc..c85ef3ea7b 100644 --- a/plugins/dominant-color-images/load.php +++ b/plugins/dominant-color-images/load.php @@ -5,7 +5,7 @@ * Description: Displays placeholders based on an image's dominant color while the image is loading. * Requires at least: 6.6 * Requires PHP: 7.2 - * Version: 1.1.2 + * Version: 1.1.3 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -25,7 +25,7 @@ return; } -define( 'DOMINANT_COLOR_IMAGES_VERSION', '1.1.2' ); +define( 'DOMINANT_COLOR_IMAGES_VERSION', '1.1.3' ); require_once __DIR__ . '/helper.php'; require_once __DIR__ . '/hooks.php'; From d6d96e4b83b0119debb72da85e8205fc6a133998 Mon Sep 17 00:00:00 2001 From: Paul Bearne Date: Tue, 3 Dec 2024 13:40:50 -0500 Subject: [PATCH 072/237] Update hooks.php --- plugins/dominant-color-images/hooks.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index 2d571e19a6..ee9a293d3b 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -187,9 +187,6 @@ function dominant_color_render_generator(): void { } add_action( 'wp_head', 'dominant_color_render_generator' ); - - - /** * Adds inline CSS for dominant color styling in the WordPress admin area. * @@ -237,7 +234,6 @@ function dominant_color_admin_script(){ }; add_action( 'admin_print_footer_scripts', 'dominant_color_admin_script', 1000 ); - /** * Prepares attachment data for JavaScript, adding dominant color and transparency information. * From 4abdda43e121bd5d1a136eeb494e2c1ed1d266f4 Mon Sep 17 00:00:00 2001 From: Paul Bearne Date: Tue, 3 Dec 2024 13:49:02 -0500 Subject: [PATCH 073/237] Fix code indentation and spacing issues Corrected inconsistent indentation and spacing to improve code readability and maintainability in hooks.php. These changes ensure a cleaner code style without altering the functionality or logic. --- plugins/dominant-color-images/hooks.php | 42 ++++++++++++------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index ee9a293d3b..dfb90170fc 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -200,12 +200,12 @@ function dominant_color_render_generator(): void { */ function dominant_color_admin_inline_style() { $handle = 'dominant-color-admin-styles'; - // PHPCS ignore reason: Version not used since this handle is only registered for adding an inline style. - // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion - wp_register_style( $handle, false ); - wp_enqueue_style( $handle ); - $custom_css = '.wp-core-ui .attachment-preview[data-dominant-color]:not(.has-transparency) { background-color: var(--dominant-color); }'; - wp_add_inline_style( $handle, $custom_css ); + // PHPCS ignore reason: Version not used since this handle is only registered for adding an inline style. + // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion + wp_register_style( $handle, false ); + wp_enqueue_style( $handle ); + $custom_css = '.wp-core-ui .attachment-preview[data-dominant-color]:not(.has-transparency) { background-color: var(--dominant-color); }'; + wp_add_inline_style( $handle, $custom_css ); }; add_action( 'admin_enqueue_scripts', 'dominant_color_admin_inline_style' ); @@ -220,7 +220,7 @@ function dominant_color_admin_inline_style() { * * @return void */ - function dominant_color_admin_script(){ +function dominant_color_admin_script() { ?> Date: Tue, 3 Dec 2024 13:52:31 -0500 Subject: [PATCH 074/237] Fix code indentation and spacing issues Corrected inconsistent indentation and spacing to improve code readability and maintainability in hooks.php. These changes ensure a cleaner code style without altering the functionality or logic. --- plugins/dominant-color-images/hooks.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index dfb90170fc..d3f838e111 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -199,14 +199,14 @@ function dominant_color_render_generator(): void { * @return void */ function dominant_color_admin_inline_style() { - $handle = 'dominant-color-admin-styles'; + $handle = 'dominant-color-admin-styles'; // PHPCS ignore reason: Version not used since this handle is only registered for adding an inline style. // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion wp_register_style( $handle, false ); wp_enqueue_style( $handle ); $custom_css = '.wp-core-ui .attachment-preview[data-dominant-color]:not(.has-transparency) { background-color: var(--dominant-color); }'; wp_add_inline_style( $handle, $custom_css ); -}; +} add_action( 'admin_enqueue_scripts', 'dominant_color_admin_inline_style' ); /** @@ -230,7 +230,7 @@ function dominant_color_admin_script() { }()); Date: Tue, 3 Dec 2024 13:56:17 -0500 Subject: [PATCH 075/237] Add return types to dominant color functions This commit specifies return types for several functions in the dominant-color-images plugin. The `dominant_color_admin_inline_style` and `dominant_color_admin_script` functions now explicitly return void, and the `dominant_color_prepare_attachment_for_js` function now returns an array. Adding these return types improves code clarity and aligns with current PHP best practices. --- plugins/dominant-color-images/hooks.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index d3f838e111..c86376ca98 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -198,7 +198,7 @@ function dominant_color_render_generator(): void { * * @return void */ -function dominant_color_admin_inline_style() { +function dominant_color_admin_inline_style(): void { $handle = 'dominant-color-admin-styles'; // PHPCS ignore reason: Version not used since this handle is only registered for adding an inline style. // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion @@ -220,7 +220,7 @@ function dominant_color_admin_inline_style() { * * @return void */ -function dominant_color_admin_script() { +function dominant_color_admin_script(): void { ?> @@ -256,7 +257,6 @@ function dominant_color_prepare_attachment_for_js( $response, WP_Post $attachmen $response['hasTransparencyClass'] = ''; if ( isset( $meta['has_transparency'] ) ) { $response['hasTransparency'] = (bool) $meta['has_transparency']; - $response['hasTransparencyClass'] = $meta['has_transparency'] ? 'has-transparency' : 'not-transparent'; } return $response; From 5dfd56eceafb8ec6651b282389f3606f1c08a274 Mon Sep 17 00:00:00 2001 From: Paul Bearne Date: Fri, 6 Dec 2024 11:01:43 -0500 Subject: [PATCH 103/237] Fix inconsistent spacing in hooks.php file Remove unnecessary whitespace around the assignment operator in the 'hasTransparency' key modification. This change ensures code consistency and readability in the dominant-color-images plugin. --- plugins/dominant-color-images/hooks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index 2891fd4746..bf13d1dfae 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -256,7 +256,7 @@ function dominant_color_prepare_attachment_for_js( $response, WP_Post $attachmen $response['hasTransparency'] = ''; $response['hasTransparencyClass'] = ''; if ( isset( $meta['has_transparency'] ) ) { - $response['hasTransparency'] = (bool) $meta['has_transparency']; + $response['hasTransparency'] = (bool) $meta['has_transparency']; } return $response; From 2039994a4d70682bdef2f9dfec3b33ed015607aa Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Fri, 6 Dec 2024 21:38:07 +0530 Subject: [PATCH 104/237] Consider an LCP element as common if common in first and last groups and other groups are empty Signed-off-by: Shyamsundar Gadde --- .../class-od-url-metric-group-collection.php | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/plugins/optimization-detective/class-od-url-metric-group-collection.php b/plugins/optimization-detective/class-od-url-metric-group-collection.php index 9b50846eb6..204c205c5d 100644 --- a/plugins/optimization-detective/class-od-url-metric-group-collection.php +++ b/plugins/optimization-detective/class-od-url-metric-group-collection.php @@ -437,38 +437,41 @@ public function get_common_lcp_element(): ?OD_Element { $result = ( function () { - // If every group isn't populated, then we can't say whether there is a common LCP element across every viewport group. - if ( ! $this->is_every_group_populated() ) { + // Ensure both the narrowest (first) and widest (last) viewport groups are populated. + $first_group = $this->get_first_group(); + $last_group = $this->get_last_group(); + if ( $first_group->count() === 0 || $last_group->count() === 0 ) { return null; } - // Look at the LCP elements across all the viewport groups. - $groups_by_lcp_element_xpath = array(); - $lcp_elements_by_xpath = array(); - $group_has_unknown_lcp_element = false; - foreach ( $this->groups as $group ) { - $lcp_element = $group->get_lcp_element(); - if ( $lcp_element instanceof OD_Element ) { - $groups_by_lcp_element_xpath[ $lcp_element->get_xpath() ][] = $group; - $lcp_elements_by_xpath[ $lcp_element->get_xpath() ][] = $lcp_element; - } else { - $group_has_unknown_lcp_element = true; - } - } + $first_group_lcp_element = $first_group->get_lcp_element(); + $last_group_lcp_element = $last_group->get_lcp_element(); + // Validate LCP elements exist and have matching XPaths in the extreme viewport groups. if ( - // All breakpoints share the same LCP element. - 1 === count( $groups_by_lcp_element_xpath ) - && - // The breakpoints don't share a common lack of a detected LCP element. - ! $group_has_unknown_lcp_element + ! $first_group_lcp_element instanceof OD_Element + || + ! $last_group_lcp_element instanceof OD_Element + || + $first_group_lcp_element->get_xpath() !== $last_group_lcp_element->get_xpath() ) { - $xpath = key( $lcp_elements_by_xpath ); + return null; // No common LCP element across the narrowest and widest viewports. + } - return $lcp_elements_by_xpath[ $xpath ][0]; + // Check intermediate viewport groups for conflicting LCP elements. + $num_groups = count( $this->groups ); + for ( $i = 1; $i < $num_groups - 1; $i++ ) { + $group_lcp_element = $this->groups[ $i ]->get_lcp_element(); + if ( + $group_lcp_element instanceof OD_Element + && + $group_lcp_element->get_xpath() !== $first_group_lcp_element->get_xpath() + ) { + return null; // Conflicting LCP element found in an intermediate group. + } } - return null; + return $first_group_lcp_element; } )(); $this->result_cache[ __FUNCTION__ ] = $result; From ac723e140dfc81480cfc35326da582657f6f1e1d Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Fri, 6 Dec 2024 22:06:32 +0530 Subject: [PATCH 105/237] Add test case for new logic Signed-off-by: Shyamsundar Gadde --- ...th-url-metrics-missing-in-other-groups.php | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 plugins/image-prioritizer/tests/test-cases/fetch-priority-high-on-lcp-image-common-on-mobile-and-desktop-with-url-metrics-missing-in-other-groups.php diff --git a/plugins/image-prioritizer/tests/test-cases/fetch-priority-high-on-lcp-image-common-on-mobile-and-desktop-with-url-metrics-missing-in-other-groups.php b/plugins/image-prioritizer/tests/test-cases/fetch-priority-high-on-lcp-image-common-on-mobile-and-desktop-with-url-metrics-missing-in-other-groups.php new file mode 100644 index 0000000000..251a550fad --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/fetch-priority-high-on-lcp-image-common-on-mobile-and-desktop-with-url-metrics-missing-in-other-groups.php @@ -0,0 +1,68 @@ + static function ( Test_Image_Prioritizer_Helper $test_case ): void { + $breakpoint_max_widths = array( 480, 600, 782 ); + + add_filter( + 'od_breakpoint_max_widths', + static function () use ( $breakpoint_max_widths ) { + return $breakpoint_max_widths; + } + ); + + OD_URL_Metrics_Post_Type::store_url_metric( + od_get_url_metrics_slug( od_get_normalized_query_vars() ), + $test_case->get_sample_url_metric( + array( + 'viewport_width' => 375, + 'elements' => array( + array( + 'xpath' => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]', + 'isLCP' => true, + ), + ), + ) + ) + ); + + OD_URL_Metrics_Post_Type::store_url_metric( + od_get_url_metrics_slug( od_get_normalized_query_vars() ), + $test_case->get_sample_url_metric( + array( + 'viewport_width' => 1000, + 'elements' => array( + array( + 'xpath' => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]', + 'isLCP' => true, + ), + ), + ) + ) + ); + }, + 'buffer' => ' + + + + ... + + + Foo + + + ', + 'expected' => ' + + + + ... + + + + + Foo + + + + ', +); From 55ac18ab55186a229c4879eedbb20a01953410a7 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Fri, 6 Dec 2024 22:06:55 +0530 Subject: [PATCH 106/237] Add missing since tag Signed-off-by: Shyamsundar Gadde --- .../class-od-url-metric-group-collection.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/optimization-detective/class-od-url-metric-group-collection.php b/plugins/optimization-detective/class-od-url-metric-group-collection.php index 204c205c5d..b80d85b26b 100644 --- a/plugins/optimization-detective/class-od-url-metric-group-collection.php +++ b/plugins/optimization-detective/class-od-url-metric-group-collection.php @@ -427,6 +427,7 @@ public function get_groups_by_lcp_element( string $xpath ): array { * Gets common LCP element. * * @since 0.3.0 + * @since n.e.x.t An LCP element is also considered common if it is the same in the narrowest and widest viewport groups, and all intermediate groups are empty. * * @return OD_Element|null Common LCP element if it exists. */ From 25cb61cd55e3db89ddab79a0610a98d731b06db3 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Fri, 6 Dec 2024 23:23:10 +0530 Subject: [PATCH 107/237] Refactor LCP conflict check to use `foreach` Co-authored-by: Weston Ruter --- .../class-od-url-metric-group-collection.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/plugins/optimization-detective/class-od-url-metric-group-collection.php b/plugins/optimization-detective/class-od-url-metric-group-collection.php index b80d85b26b..ed7fe6928c 100644 --- a/plugins/optimization-detective/class-od-url-metric-group-collection.php +++ b/plugins/optimization-detective/class-od-url-metric-group-collection.php @@ -456,19 +456,18 @@ public function get_common_lcp_element(): ?OD_Element { || $first_group_lcp_element->get_xpath() !== $last_group_lcp_element->get_xpath() ) { - return null; // No common LCP element across the narrowest and widest viewports. + return null; // No common LCP element across the narrowest and widest viewports. } // Check intermediate viewport groups for conflicting LCP elements. - $num_groups = count( $this->groups ); - for ( $i = 1; $i < $num_groups - 1; $i++ ) { - $group_lcp_element = $this->groups[ $i ]->get_lcp_element(); + foreach ( array_slice( $this->groups, 1, -1 ) as $group ) { + $group_lcp_element = $group->get_lcp_element(); if ( $group_lcp_element instanceof OD_Element && $group_lcp_element->get_xpath() !== $first_group_lcp_element->get_xpath() ) { - return null; // Conflicting LCP element found in an intermediate group. + return null; // Conflicting LCP element found in an intermediate group. } } From 3540d9ee3a202286218f97a47124b068a037c1d5 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Sat, 7 Dec 2024 07:33:10 +0530 Subject: [PATCH 108/237] Update test case test_get_common_lcp_element Signed-off-by: Shyamsundar Gadde --- ...-class-od-url-metrics-group-collection.php | 124 ++++++++++++++---- 1 file changed, 102 insertions(+), 22 deletions(-) diff --git a/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php b/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php index 066f34f8b2..abbfd37569 100644 --- a/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php +++ b/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php @@ -721,45 +721,125 @@ public function test_get_groups_by_lcp_element(): void { $this->assertNull( $group_collection->get_common_lcp_element() ); } + /** + * Data provider. + * + * @return array + */ + public function data_provider_test_get_common_lcp_element(): array { + $xpath1 = '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]/*[1]'; + $xpath2 = '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]/*[2]'; + + $get_sample_url_metric = function ( int $viewport_width, string $lcp_element_xpath, bool $is_lcp ): OD_URL_Metric { + return $this->get_sample_url_metric( + array( + 'viewport_width' => $viewport_width, + 'element' => array( + 'isLCP' => $is_lcp, + 'xpath' => $lcp_element_xpath, + ), + ) + ); + }; + + return array( + 'all_groups_have_common_lcp' => array( + 'url_metrics' => array( + $get_sample_url_metric( 400, $xpath1, true ), + $get_sample_url_metric( 600, $xpath1, true ), + $get_sample_url_metric( 1000, $xpath1, true ), + ), + 'expected' => array( + 'type' => OD_Element::class, + 'xpath' => $xpath1, + ), + ), + 'no_url_metrics' => array( + 'url_metrics' => array(), + 'expected' => null, + ), + 'empty_first_group' => array( + 'url_metrics' => array( + $get_sample_url_metric( 600, $xpath1, true ), + $get_sample_url_metric( 1000, $xpath1, true ), + ), + 'expected' => null, + ), + 'empty_last_group' => array( + 'url_metrics' => array( + $get_sample_url_metric( 400, $xpath1, true ), + $get_sample_url_metric( 600, $xpath1, true ), + ), + 'expected' => null, + ), + 'first_and_last_common_lcp_others_empty' => array( + 'url_metrics' => array( + $get_sample_url_metric( 400, $xpath1, true ), + $get_sample_url_metric( 1000, $xpath1, true ), + ), + 'expected' => array( + 'type' => OD_Element::class, + 'xpath' => $xpath1, + ), + ), + 'intermediate_groups_conflict' => array( + 'url_metrics' => array( + $get_sample_url_metric( 400, $xpath1, true ), + $get_sample_url_metric( 600, $xpath2, true ), + $get_sample_url_metric( 1000, $xpath1, true ), + ), + 'expected' => null, + ), + 'first_and_last_lcp_mismatch' => array( + 'url_metrics' => array( + $get_sample_url_metric( 400, $xpath1, true ), + $get_sample_url_metric( 600, $xpath1, true ), + $get_sample_url_metric( 1000, $xpath2, true ), + ), + 'expected' => null, + ), + 'no_lcp_metrics' => array( + 'url_metrics' => array( + $get_sample_url_metric( 400, $xpath1, false ), + $get_sample_url_metric( 600, $xpath1, false ), + $get_sample_url_metric( 1000, $xpath1, false ), + ), + 'expected' => null, + ), + ); + } + /** * Test get_common_lcp_element(). * * @covers ::get_common_lcp_element + * + * @dataProvider data_provider_test_get_common_lcp_element + * + * @param OD_URL_Metric[] $url_metrics URL Metrics. + * @param mixed $expected Expected. */ - public function test_get_common_lcp_element(): void { + public function test_get_common_lcp_element( array $url_metrics, $expected ): void { $breakpoints = array( 480, 800 ); $sample_size = 3; $current_etag = md5( '' ); $group_collection = new OD_URL_Metric_Group_Collection( - array(), + $url_metrics, $current_etag, $breakpoints, $sample_size, HOUR_IN_SECONDS ); - $lcp_element_xpath = '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]/*[1]'; - - foreach ( array_merge( $breakpoints, array( 1000 ) ) as $viewport_width ) { - for ( $i = 0; $i < $sample_size; $i++ ) { - $group_collection->add_url_metric( - $this->get_sample_url_metric( - array( - 'viewport_width' => $viewport_width, - 'element' => array( - 'isLCP' => true, - 'xpath' => $lcp_element_xpath, - ), - ) - ) - ); - } - } - $this->assertCount( 3, $group_collection ); + $common_lcp_element = $group_collection->get_common_lcp_element(); - $this->assertInstanceOf( OD_Element::class, $common_lcp_element ); - $this->assertSame( $lcp_element_xpath, $common_lcp_element['xpath'] ); + if ( is_array( $expected ) ) { + $this->assertInstanceOf( $expected['type'], $common_lcp_element ); + $this->assertSame( $expected['xpath'], $common_lcp_element->get_xpath() ); + } else { + $this->assertNull( $common_lcp_element ); + } } /** From 8651d0512fb395ee93f84108c779e648ebb72f43 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Sat, 7 Dec 2024 10:46:44 +0530 Subject: [PATCH 109/237] Add `queried_object` to ETag data Signed-off-by: Shyamsundar Gadde --- plugins/optimization-detective/storage/data.php | 13 +++++++------ .../tests/storage/test-data.php | 3 +++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 095ba5fc61..a4158e2b38 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -143,9 +143,9 @@ function od_get_url_metrics_slug( array $query_vars ): string { /** * Gets the current ETag for URL Metrics. * - * Generates a hash based on the IDs of registered tag visitors, queried posts, - * and theme information in the current environment. This ETag is used to assess - * if the URL Metrics are stale when its value changes. + * Generates a hash based on the IDs of registered tag visitors, the queried object, + * posts in The Loop, and theme information in the current environment. This ETag + * is used to assess if the URL Metrics are stale when its value changes. * * @since n.e.x.t * @access private @@ -173,9 +173,10 @@ static function ( WP_Post $post ): array { } $data = array( - 'tag_visitors' => array_keys( iterator_to_array( $tag_visitor_registry ) ), - 'queried_posts' => $queried_posts, - 'active_theme' => array( + 'tag_visitors' => array_keys( iterator_to_array( $tag_visitor_registry ) ), + 'queried_object' => get_queried_object(), + 'queried_posts' => $queried_posts, + 'active_theme' => array( 'template' => get_template(), 'template_version' => wp_get_theme( get_template() )->get( 'Version' ), 'stylesheet' => get_stylesheet(), diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index ec8da8ea28..5287346903 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -312,6 +312,7 @@ static function ( array $data ) use ( &$captured_etag_data ) { $this->assertSame( array( 'tag_visitors' => array(), + 'queried_object' => null, 'queried_posts' => array(), 'active_theme' => array( 'template' => 'default', @@ -334,6 +335,7 @@ static function ( array $data ) use ( &$captured_etag_data ) { $this->assertSame( array( 'tag_visitors' => array( 'foo', 'bar', 'baz' ), + 'queried_object' => null, 'queried_posts' => array(), 'active_theme' => array( 'template' => 'default', @@ -363,6 +365,7 @@ static function ( $data ): array { $this->assertSame( array( 'tag_visitors' => array( 'foo', 'bar', 'baz' ), + 'queried_object' => null, 'queried_posts' => array( array( 'ID' => 99, From 952f04c28281dca3a2d2d85b0bd16f1effd10bbc Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Mon, 9 Dec 2024 10:33:36 +0530 Subject: [PATCH 110/237] Set default parameter value for test case helper function Signed-off-by: Shyamsundar Gadde --- ...-class-od-url-metrics-group-collection.php | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php b/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php index abbfd37569..026048df05 100644 --- a/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php +++ b/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php @@ -730,7 +730,7 @@ public function data_provider_test_get_common_lcp_element(): array { $xpath1 = '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]/*[1]'; $xpath2 = '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]/*[2]'; - $get_sample_url_metric = function ( int $viewport_width, string $lcp_element_xpath, bool $is_lcp ): OD_URL_Metric { + $get_sample_url_metric = function ( int $viewport_width, string $lcp_element_xpath, bool $is_lcp = true ): OD_URL_Metric { return $this->get_sample_url_metric( array( 'viewport_width' => $viewport_width, @@ -745,9 +745,9 @@ public function data_provider_test_get_common_lcp_element(): array { return array( 'all_groups_have_common_lcp' => array( 'url_metrics' => array( - $get_sample_url_metric( 400, $xpath1, true ), - $get_sample_url_metric( 600, $xpath1, true ), - $get_sample_url_metric( 1000, $xpath1, true ), + $get_sample_url_metric( 400, $xpath1 ), + $get_sample_url_metric( 600, $xpath1 ), + $get_sample_url_metric( 1000, $xpath1 ), ), 'expected' => array( 'type' => OD_Element::class, @@ -760,22 +760,22 @@ public function data_provider_test_get_common_lcp_element(): array { ), 'empty_first_group' => array( 'url_metrics' => array( - $get_sample_url_metric( 600, $xpath1, true ), - $get_sample_url_metric( 1000, $xpath1, true ), + $get_sample_url_metric( 600, $xpath1 ), + $get_sample_url_metric( 1000, $xpath1 ), ), 'expected' => null, ), 'empty_last_group' => array( 'url_metrics' => array( - $get_sample_url_metric( 400, $xpath1, true ), - $get_sample_url_metric( 600, $xpath1, true ), + $get_sample_url_metric( 400, $xpath1 ), + $get_sample_url_metric( 600, $xpath1 ), ), 'expected' => null, ), 'first_and_last_common_lcp_others_empty' => array( 'url_metrics' => array( - $get_sample_url_metric( 400, $xpath1, true ), - $get_sample_url_metric( 1000, $xpath1, true ), + $get_sample_url_metric( 400, $xpath1 ), + $get_sample_url_metric( 1000, $xpath1 ), ), 'expected' => array( 'type' => OD_Element::class, @@ -784,17 +784,17 @@ public function data_provider_test_get_common_lcp_element(): array { ), 'intermediate_groups_conflict' => array( 'url_metrics' => array( - $get_sample_url_metric( 400, $xpath1, true ), - $get_sample_url_metric( 600, $xpath2, true ), - $get_sample_url_metric( 1000, $xpath1, true ), + $get_sample_url_metric( 400, $xpath1 ), + $get_sample_url_metric( 600, $xpath2 ), + $get_sample_url_metric( 1000, $xpath1 ), ), 'expected' => null, ), 'first_and_last_lcp_mismatch' => array( 'url_metrics' => array( - $get_sample_url_metric( 400, $xpath1, true ), - $get_sample_url_metric( 600, $xpath1, true ), - $get_sample_url_metric( 1000, $xpath2, true ), + $get_sample_url_metric( 400, $xpath1 ), + $get_sample_url_metric( 600, $xpath1 ), + $get_sample_url_metric( 1000, $xpath2 ), ), 'expected' => null, ), From 5d9895d981bea1294c7e31d45e451693551a38e2 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 9 Dec 2024 10:54:34 +0530 Subject: [PATCH 111/237] Convert WebP to AVIF on upload --- plugins/webp-uploads/helper.php | 2 +- plugins/webp-uploads/tests/test-helper.php | 4 +-- plugins/webp-uploads/tests/test-load.php | 34 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/plugins/webp-uploads/helper.php b/plugins/webp-uploads/helper.php index 95fc15e18e..b89e442ba9 100644 --- a/plugins/webp-uploads/helper.php +++ b/plugins/webp-uploads/helper.php @@ -28,7 +28,7 @@ function webp_uploads_get_upload_image_mime_transforms(): array { $default_transforms = array( 'image/jpeg' => array( 'image/' . $output_format ), - 'image/webp' => array( 'image/webp' ), + 'image/webp' => array( 'image/' . $output_format ), 'image/avif' => array( 'image/avif' ), 'image/png' => array( 'image/' . $output_format ), ); diff --git a/plugins/webp-uploads/tests/test-helper.php b/plugins/webp-uploads/tests/test-helper.php index a66154b647..720654bcc5 100644 --- a/plugins/webp-uploads/tests/test-helper.php +++ b/plugins/webp-uploads/tests/test-helper.php @@ -365,14 +365,14 @@ public function test_it_should_return_default_transforms_when_filter_returns_non $this->set_image_output_type( 'avif' ); $default_transforms = array( 'image/jpeg' => array( 'image/avif' ), - 'image/webp' => array( 'image/webp' ), + 'image/webp' => array( 'image/avif' ), 'image/avif' => array( 'image/avif' ), 'image/png' => array( 'image/avif' ), ); } else { $default_transforms = array( 'image/jpeg' => array( 'image/webp' ), - 'image/webp' => array( 'image/webp' ), + 'image/webp' => array( 'image/avif' ), 'image/avif' => array( 'image/avif' ), 'image/png' => array( 'image/webp' ), ); diff --git a/plugins/webp-uploads/tests/test-load.php b/plugins/webp-uploads/tests/test-load.php index 8935827664..70e1af117a 100644 --- a/plugins/webp-uploads/tests/test-load.php +++ b/plugins/webp-uploads/tests/test-load.php @@ -1087,4 +1087,38 @@ public function test_it_should_generate_fallback_images_for_all_sizes_when_gener wp_delete_attachment( $attachment_id ); } + + /** + * Create the original mime type for WebP images. + */ + public function test_that_it_should_convert_webp_to_avif_on_upload(): void { + $mime_type = 'image/avif'; + + // Ensure the MIME type is supported; skip the test if not. + if ( ! webp_uploads_mime_type_supported( $mime_type ) ) { + $this->markTestSkipped( 'Mime type ' . $mime_type . ' is not supported.' ); + } + + $this->set_image_output_type( 'avif' ); + + $attachment_id = self::factory()->attachment->create_upload_object( TESTS_PLUGIN_DIR . '/tests/data/images/balloons.webp' ); + + // There should be a AVIF source, but no WebP source for the full image. + $this->assertImageNotHasSource( $attachment_id, 'image/webp' ); + $this->assertImageHasSource( $attachment_id, 'image/avif' ); + + $metadata = wp_get_attachment_metadata( $attachment_id ); + + // The full image should be a AVIF. + $this->assertArrayHasKey( 'file', $metadata ); + $this->assertStringEndsWith( $metadata['sources']['image/avif']['file'], $metadata['file'] ); + $this->assertStringEndsWith( $metadata['sources']['image/avif']['file'], get_attached_file( $attachment_id ) ); + + // There should be a WebP source, but no JPEG source for all sizes. + foreach ( array_keys( $metadata['sizes'] ) as $size_name ) { + $this->assertImageNotHasSizeSource( $attachment_id, $size_name, 'image/webp' ); + $this->assertImageHasSizeSource( $attachment_id, $size_name, 'image/avif' ); + } + wp_delete_attachment( $attachment_id ); + } } From a2943faeef8c5cb476efb0086942d1e3aea74092 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 9 Dec 2024 10:59:37 +0530 Subject: [PATCH 112/237] Update inline comment --- plugins/webp-uploads/tests/test-load.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/webp-uploads/tests/test-load.php b/plugins/webp-uploads/tests/test-load.php index 70e1af117a..b834828587 100644 --- a/plugins/webp-uploads/tests/test-load.php +++ b/plugins/webp-uploads/tests/test-load.php @@ -1114,7 +1114,7 @@ public function test_that_it_should_convert_webp_to_avif_on_upload(): void { $this->assertStringEndsWith( $metadata['sources']['image/avif']['file'], $metadata['file'] ); $this->assertStringEndsWith( $metadata['sources']['image/avif']['file'], get_attached_file( $attachment_id ) ); - // There should be a WebP source, but no JPEG source for all sizes. + // There should be a AVIF source, but no WebP source for all sizes. foreach ( array_keys( $metadata['sizes'] ) as $size_name ) { $this->assertImageNotHasSizeSource( $attachment_id, $size_name, 'image/webp' ); $this->assertImageHasSizeSource( $attachment_id, $size_name, 'image/avif' ); From f1395f63d8696db12d1eec2ad3bcd7333611e1d9 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 9 Dec 2024 11:15:48 +0530 Subject: [PATCH 113/237] Fallback to WebP if PHP does not supported AVIF --- plugins/webp-uploads/tests/test-helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/webp-uploads/tests/test-helper.php b/plugins/webp-uploads/tests/test-helper.php index 720654bcc5..5165aaa21a 100644 --- a/plugins/webp-uploads/tests/test-helper.php +++ b/plugins/webp-uploads/tests/test-helper.php @@ -372,7 +372,7 @@ public function test_it_should_return_default_transforms_when_filter_returns_non } else { $default_transforms = array( 'image/jpeg' => array( 'image/webp' ), - 'image/webp' => array( 'image/avif' ), + 'image/webp' => array( 'image/webp' ), 'image/avif' => array( 'image/avif' ), 'image/png' => array( 'image/webp' ), ); From 56cbe8ecba5af8ed08367c0ff9844d3d94df9502 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Mon, 9 Dec 2024 17:57:42 +0530 Subject: [PATCH 114/237] Use dependency injection instead of relying on globals Signed-off-by: Shyamsundar Gadde --- .../optimization-detective/optimization.php | 14 ++++++- .../optimization-detective/storage/data.php | 38 ++++--------------- .../tests/storage/test-data.php | 18 +++++---- .../tests/test-optimization.php | 3 ++ ...ss-optimization-detective-test-helpers.php | 4 +- 5 files changed, 36 insertions(+), 41 deletions(-) diff --git a/plugins/optimization-detective/optimization.php b/plugins/optimization-detective/optimization.php index 8e5e524744..a0a6fa29f7 100644 --- a/plugins/optimization-detective/optimization.php +++ b/plugins/optimization-detective/optimization.php @@ -170,6 +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. + * @global string $_wp_current_template_id Current template ID. + * @global string $template Template file path. + * * @param string $buffer Template output buffer. * @return string Filtered template output buffer. */ @@ -206,7 +210,15 @@ 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 ); + if ( wp_is_block_theme() ) { + // Extract the template slug from $_wp_current_template_id, which has the format 'theme_slug//template_slug'. + $parts = explode( '//', $GLOBALS['_wp_current_template_id'] ); + $current_template = ( 2 === count( $parts ) ) ? $parts[1] : ''; + } else { + $current_template = basename( $GLOBALS['template'] ); + } + + $current_etag = od_get_current_url_metrics_etag( $tag_visitor_registry, $GLOBALS['wp_the_query'], $current_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, diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index a4158e2b38..1b66630480 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -150,47 +150,25 @@ function od_get_url_metrics_slug( array $query_vars ): string { * @since n.e.x.t * @access private * - * @global WP_Query $wp_the_query Global WP_Query instance. - * @global string $_wp_current_template_id Current template ID. - * @global string $template Template file path. - * * @param OD_Tag_Visitor_Registry $tag_visitor_registry Tag visitor registry. + * @param WP_Query $wp_query The WP_Query instance. + * @param string $current_template The current template being used. * @return non-empty-string Current ETag. */ -function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_registry ): string { - if ( isset( $GLOBALS['wp_the_query']->posts ) && is_array( $GLOBALS['wp_the_query']->posts ) ) { - $queried_posts = array_map( - static function ( WP_Post $post ): array { - return array( - 'ID' => $post->ID, - 'last_modified' => $post->post_modified_gmt, - ); - }, - $GLOBALS['wp_the_query']->posts - ); - } else { - $queried_posts = array(); - } - +function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_registry, WP_Query $wp_query, string $current_template ): string { $data = array( - 'tag_visitors' => array_keys( iterator_to_array( $tag_visitor_registry ) ), - 'queried_object' => get_queried_object(), - 'queried_posts' => $queried_posts, - 'active_theme' => array( + 'tag_visitors' => array_keys( iterator_to_array( $tag_visitor_registry ) ), + 'queried_object' => $wp_query->get_queried_object(), + 'queried_posts' => wp_list_pluck( $wp_query->posts, 'post_modified_gmt', 'ID' ), + 'active_theme' => array( 'template' => get_template(), 'template_version' => wp_get_theme( get_template() )->get( 'Version' ), 'stylesheet' => get_stylesheet(), 'stylesheet_version' => wp_get_theme()->get( 'Version' ), ), + 'current_template' => $current_template, ); - if ( wp_is_block_theme() ) { - // Extract the template slug from $_wp_current_template_id, which has the format 'theme_slug//template_slug'. - $data['current_template'] = explode( '//', $GLOBALS['_wp_current_template_id'] ?? '' )[1] ?? ''; - } else { - $data['current_template'] = basename( $GLOBALS['template'] ?? '' ); - } - /** * Filters the data that goes into computing the current ETag for URL Metrics. * diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index 5287346903..50fbe8d89d 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -293,7 +293,9 @@ public function test_od_get_url_metrics_slug(): void { */ public function test_od_get_current_url_metrics_etag(): void { remove_all_filters( 'od_current_url_metrics_etag_data' ); - $registry = new OD_Tag_Visitor_Registry(); + $registry = new OD_Tag_Visitor_Registry(); + $wp_the_query = new WP_Query(); + $current_template = 'index.php'; $captured_etag_data = array(); add_filter( @@ -304,9 +306,9 @@ static function ( array $data ) use ( &$captured_etag_data ) { }, PHP_INT_MAX ); - $etag1 = od_get_current_url_metrics_etag( $registry ); + $etag1 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); $this->assertMatchesRegularExpression( '/^[a-z0-9]{32}\z/', $etag1 ); - $etag2 = od_get_current_url_metrics_etag( $registry ); + $etag2 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); $this->assertSame( $etag1, $etag2 ); $this->assertCount( 2, $captured_etag_data ); $this->assertSame( @@ -320,7 +322,7 @@ static function ( array $data ) use ( &$captured_etag_data ) { 'stylesheet' => 'default', 'stylesheet_version' => '1.6', ), - 'current_template' => '', + 'current_template' => 'index.php', ), $captured_etag_data[0] ); @@ -329,7 +331,7 @@ static function ( array $data ) use ( &$captured_etag_data ) { $registry->register( 'foo', static function (): void {} ); $registry->register( 'bar', static function (): void {} ); $registry->register( 'baz', static function (): void {} ); - $etag3 = od_get_current_url_metrics_etag( $registry ); + $etag3 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); $this->assertNotEquals( $etag2, $etag3 ); $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); $this->assertSame( @@ -343,7 +345,7 @@ static function ( array $data ) use ( &$captured_etag_data ) { 'stylesheet' => 'default', 'stylesheet_version' => '1.6', ), - 'current_template' => '', + 'current_template' => 'index.php', ), $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); @@ -359,7 +361,7 @@ static function ( $data ): array { return $data; } ); - $etag4 = od_get_current_url_metrics_etag( $registry ); + $etag4 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); $this->assertNotEquals( $etag3, $etag4 ); $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); $this->assertSame( @@ -378,7 +380,7 @@ static function ( $data ): array { 'stylesheet' => 'default', 'stylesheet_version' => '1.6', ), - 'current_template' => '', + 'current_template' => 'index.php', ), $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); diff --git a/plugins/optimization-detective/tests/test-optimization.php b/plugins/optimization-detective/tests/test-optimization.php index a76d3f0452..98690d88c9 100644 --- a/plugins/optimization-detective/tests/test-optimization.php +++ b/plugins/optimization-detective/tests/test-optimization.php @@ -291,6 +291,7 @@ public function data_provider_test_od_optimize_template_output_buffer(): array { * @dataProvider data_provider_test_od_optimize_template_output_buffer */ public function test_od_optimize_template_output_buffer( Closure $set_up, string $buffer, string $expected ): void { + $GLOBALS['template'] = '/path/to/theme/index.php'; $set_up( $this ); add_action( @@ -336,5 +337,7 @@ function ( OD_Tag_Visitor_Context $context ): bool { $this->remove_initial_tabs( $buffer ), "Buffer snapshot:\n$buffer" ); + + unset( $GLOBALS['template'] ); } } diff --git a/tests/class-optimization-detective-test-helpers.php b/tests/class-optimization-detective-test-helpers.php index c7924480a0..c6734e7d38 100644 --- a/tests/class-optimization-detective-test-helpers.php +++ b/tests/class-optimization-detective-test-helpers.php @@ -27,7 +27,7 @@ trait Optimization_Detective_Test_Helpers { */ public function populate_url_metrics( array $elements, bool $complete = true ): void { $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); - $etag = od_get_current_url_metrics_etag( new OD_Tag_Visitor_Registry() ); // Note: Tests rely on the od_current_url_metrics_etag_data filter to set the desired value. + $etag = od_get_current_url_metrics_etag( new OD_Tag_Visitor_Registry(), new WP_Query(), 'index.php' ); // Note: Tests rely on the od_current_url_metrics_etag_data filter to set the desired value. $sample_size = $complete ? od_get_url_metrics_breakpoint_sample_size() : 1; foreach ( array_merge( od_get_breakpoint_max_widths(), array( 1000 ) ) as $viewport_width ) { for ( $i = 0; $i < $sample_size; $i++ ) { @@ -81,7 +81,7 @@ public function get_sample_dom_rect(): array { public function get_sample_url_metric( array $params ): OD_URL_Metric { $params = array_merge( array( - 'etag' => od_get_current_url_metrics_etag( new OD_Tag_Visitor_Registry() ), // Note: Tests rely on the od_current_url_metrics_etag_data filter to set the desired value. + 'etag' => od_get_current_url_metrics_etag( new OD_Tag_Visitor_Registry(), new WP_Query(), 'index.php' ), // Note: Tests rely on the od_current_url_metrics_etag_data filter to set the desired value. 'url' => home_url( '/' ), 'viewport_width' => 480, 'elements' => array(), From 2f46b74487391bb8af779e5fb8c053be12bd3bc1 Mon Sep 17 00:00:00 2001 From: Paul Bearne Date: Mon, 9 Dec 2024 09:35:44 -0500 Subject: [PATCH 115/237] Update plugins/dominant-color-images/hooks.php Co-authored-by: Weston Ruter --- plugins/dominant-color-images/hooks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index bf13d1dfae..cad357d0a1 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -222,7 +222,7 @@ function dominant_color_admin_script(): void { (function() { var s = jQuery( '#tmpl-attachment' ) var transparencyClassName = 'true' === data.hasTransparency ? 'has-transparency' : 'not-transparent'; - var n = s[0].innerText.replace( '{{ data.orientation }}"', '{{ data.orientation }} {{ transparencyClassName }}" data-dominant-color="{{ data.dominantColor }}" data-has-transparency="{{ data.hasTransparency }}" style="--dominant-color: #{{ data.dominantColor }};"') + var n = s[0].innerText.replace( '{{ data.orientation }}"', '{{ data.orientation }} {{ data.hasTransparency ? 'has-transparency' : 'not-transparent' }}" data-dominant-color="{{ data.dominantColor }}" data-has-transparency="{{ data.hasTransparency }}" style="--dominant-color: #{{ data.dominantColor }};"') s.replaceWith( ' From 6bc708a6af95dc76b6dc4ba2198b27051b8bcda8 Mon Sep 17 00:00:00 2001 From: Paul Bearne Date: Mon, 9 Dec 2024 09:36:13 -0500 Subject: [PATCH 116/237] Update plugins/dominant-color-images/hooks.php Co-authored-by: Weston Ruter --- plugins/dominant-color-images/hooks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index cad357d0a1..d96026edcb 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -250,7 +250,7 @@ function dominant_color_prepare_attachment_for_js( $response, WP_Post $attachmen } $response['dominantColor'] = ''; - if ( isset( $meta['dominant_color'] ) && str_contains( '#', $meta['dominant_color'] ) ) { + if ( isset( $meta['dominant_color'] ) && str_starts_with( '#', $meta['dominant_color'] ) ) { $response['dominantColor'] = sanitize_hex_color( $meta['dominant_color'] ); } $response['hasTransparency'] = ''; From bd2d72f7298bdcebc1dd629ab934c3fb4d9809b9 Mon Sep 17 00:00:00 2001 From: Paul Bearne Date: Mon, 9 Dec 2024 09:39:48 -0500 Subject: [PATCH 117/237] Update hooks.php cleaner JS code --- plugins/dominant-color-images/hooks.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index d96026edcb..6b8947d429 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -218,13 +218,14 @@ function dominant_color_admin_inline_style(): void { */ function dominant_color_admin_script(): void { ?> - Date: Mon, 9 Dec 2024 21:47:42 +0530 Subject: [PATCH 118/237] Update test_od_get_current_url_metrics_etag Signed-off-by: Shyamsundar Gadde --- .../tests/storage/test-data.php | 56 +++++++++++++------ 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index 50fbe8d89d..3a56e27b6b 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -293,6 +293,7 @@ public function test_od_get_url_metrics_slug(): void { */ public function test_od_get_current_url_metrics_etag(): void { remove_all_filters( 'od_current_url_metrics_etag_data' ); + $post_ids = self::factory()->post->create_many( 3 ); $registry = new OD_Tag_Visitor_Registry(); $wp_the_query = new WP_Query(); $current_template = 'index.php'; @@ -306,8 +307,11 @@ static function ( array $data ) use ( &$captured_etag_data ) { }, PHP_INT_MAX ); + + $wp_the_query->query( array() ); $etag1 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); $this->assertMatchesRegularExpression( '/^[a-z0-9]{32}\z/', $etag1 ); + $etag2 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); $this->assertSame( $etag1, $etag2 ); $this->assertCount( 2, $captured_etag_data ); @@ -315,7 +319,7 @@ static function ( array $data ) use ( &$captured_etag_data ) { array( 'tag_visitors' => array(), 'queried_object' => null, - 'queried_posts' => array(), + 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( 'template' => 'default', 'template_version' => '1.6', @@ -328,17 +332,37 @@ static function ( array $data ) use ( &$captured_etag_data ) { ); $this->assertSame( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); + // Add new post. + $new_post_id = self::factory()->post->create(); + $post_ids[] = $new_post_id; + $wp_the_query->query( array() ); + $etag3 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); + $this->assertNotEquals( $etag2, $etag3 ); // Etag should change. + + // Update the post content. + usleep( 1000000 ); // Sleep for 1 second to ensure the post_modified_gmt is different from the previous value. + wp_update_post( + array( + 'ID' => $post_ids[0], + 'post_content' => 'Updated post content', + ) + ); + $wp_the_query->query( array() ); + $etag4 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); + $this->assertNotEquals( $etag3, $etag4 ); + + // Register new tag visitors. $registry->register( 'foo', static function (): void {} ); $registry->register( 'bar', static function (): void {} ); $registry->register( 'baz', static function (): void {} ); - $etag3 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); - $this->assertNotEquals( $etag2, $etag3 ); + $etag5 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); + $this->assertNotEquals( $etag4, $etag5 ); $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); $this->assertSame( array( 'tag_visitors' => array( 'foo', 'bar', 'baz' ), 'queried_object' => null, - 'queried_posts' => array(), + 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( 'template' => 'default', 'template_version' => '1.6', @@ -349,31 +373,29 @@ static function ( array $data ) use ( &$captured_etag_data ) { ), $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); + + // Modify data using filter. add_filter( 'od_current_url_metrics_etag_data', static function ( $data ): array { - $data['queried_posts'] = array( - array( - 'ID' => 99, - 'last_modified' => '2024-03-02T01:00:00', - ), + $data['queried_object'] = array( + 'ID' => 99, + 'last_modified' => '2024-03-02T01:00:00', ); return $data; } ); - $etag4 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); - $this->assertNotEquals( $etag3, $etag4 ); + $etag6 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); + $this->assertNotEquals( $etag5, $etag6 ); $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); $this->assertSame( array( 'tag_visitors' => array( 'foo', 'bar', 'baz' ), - 'queried_object' => null, - 'queried_posts' => array( - array( - 'ID' => 99, - 'last_modified' => '2024-03-02T01:00:00', - ), + 'queried_object' => array( + 'ID' => 99, + 'last_modified' => '2024-03-02T01:00:00', ), + 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( 'template' => 'default', 'template_version' => '1.6', From d61a8bc88e22d769e4ab64d90efedca94f5ca9cb Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Mon, 9 Dec 2024 22:00:08 +0530 Subject: [PATCH 119/237] Fix failing test cases due to missing globals Signed-off-by: Shyamsundar Gadde --- .../embed-optimizer/tests/test-optimization-detective.php | 3 +++ plugins/image-prioritizer/tests/test-helper.php | 6 ++++++ plugins/optimization-detective/tests/storage/test-data.php | 3 +-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/plugins/embed-optimizer/tests/test-optimization-detective.php b/plugins/embed-optimizer/tests/test-optimization-detective.php index 9994859e1d..91b972b050 100644 --- a/plugins/embed-optimizer/tests/test-optimization-detective.php +++ b/plugins/embed-optimizer/tests/test-optimization-detective.php @@ -101,6 +101,7 @@ public function data_provider_test_od_optimize_template_output_buffer(): array { * @dataProvider data_provider_test_od_optimize_template_output_buffer */ public function test_od_optimize_template_output_buffer( Closure $set_up, string $buffer, string $expected ): void { + $GLOBALS['template'] = '/path/to/theme/index.php'; $set_up( $this ); $buffer = od_optimize_template_output_buffer( $buffer ); @@ -122,5 +123,7 @@ static function ( $matches ) { $this->remove_initial_tabs( $buffer ), "Buffer snapshot:\n$buffer" ); + + unset( $GLOBALS['template'] ); } } diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index b6054fbb17..10428cfebe 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -98,6 +98,7 @@ public function data_provider_test_filter_tag_visitors(): array { * @param callable|string $expected Expected content after. */ public function test_image_prioritizer_register_tag_visitors( callable $set_up, $buffer, $expected ): void { + $GLOBALS['template'] = '/path/to/theme/index.php'; $set_up( $this, $this::factory() ); $buffer = is_string( $buffer ) ? $buffer : $buffer(); @@ -125,6 +126,8 @@ static function ( $matches ) { $this->remove_initial_tabs( $buffer ), "Buffer snapshot:\n$buffer" ); + + unset( $GLOBALS['template'] ); } /** @@ -220,6 +223,7 @@ public function data_provider_test_auto_sizes(): array { * @phpstan-param array{ xpath: string, isLCP: bool, intersectionRatio: int } $element_metrics */ public function test_auto_sizes( array $element_metrics, string $buffer, string $expected ): void { + $GLOBALS['template'] = '/path/to/theme/index.php'; $this->populate_url_metrics( array( $element_metrics ) ); $html_start_doc = '...'; @@ -234,6 +238,8 @@ public function test_auto_sizes( array $element_metrics, string $buffer, string $this->remove_initial_tabs( $buffer ), "Buffer snapshot:\n$buffer" ); + + unset( $GLOBALS['template'] ); } /** diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index 3a56e27b6b..c3f38d38e9 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -333,8 +333,7 @@ static function ( array $data ) use ( &$captured_etag_data ) { $this->assertSame( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); // Add new post. - $new_post_id = self::factory()->post->create(); - $post_ids[] = $new_post_id; + $post_ids[] = self::factory()->post->create(); $wp_the_query->query( array() ); $etag3 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); $this->assertNotEquals( $etag2, $etag3 ); // Etag should change. From b8b79deae6f3ba3ef9d26310a8df2a67badeb40d Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Mon, 9 Dec 2024 08:51:56 -0800 Subject: [PATCH 120/237] Clarify comment about URL metric groups not changing across a single page load. Co-authored-by: Weston Ruter --- ...ass-image-prioritizer-background-image-styled-tag-visitor.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php index 91c20ec0a1..c6570d4d2c 100644 --- a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php +++ b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php @@ -141,6 +141,7 @@ private function get_common_lcp_element_external_background_image( OD_URL_Metric */ private function maybe_preload_external_lcp_background_image( OD_Tag_Visitor_Context $context ): void { // Gather the tuples of URL Metric group and the common LCP element external background image. + // Note the groups of URL Metrics do not change across invocations, we just need to compute this once for all. if ( ! is_array( $this->group_common_lcp_element_external_background_images ) ) { $this->group_common_lcp_element_external_background_images = array(); foreach ( $context->url_metric_group_collection as $group ) { From 5af021de189af16615c4ecff23f75d2cb9888ecc Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Mon, 9 Dec 2024 23:29:59 +0530 Subject: [PATCH 121/237] Simplify data provider for `test_comment_lcp_element` Co-authored-by: Weston Ruter --- ...-class-od-url-metrics-group-collection.php | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php b/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php index 026048df05..179d282c43 100644 --- a/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php +++ b/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php @@ -749,10 +749,7 @@ public function data_provider_test_get_common_lcp_element(): array { $get_sample_url_metric( 600, $xpath1 ), $get_sample_url_metric( 1000, $xpath1 ), ), - 'expected' => array( - 'type' => OD_Element::class, - 'xpath' => $xpath1, - ), + 'expected' => $xpath1, ), 'no_url_metrics' => array( 'url_metrics' => array(), @@ -777,10 +774,7 @@ public function data_provider_test_get_common_lcp_element(): array { $get_sample_url_metric( 400, $xpath1 ), $get_sample_url_metric( 1000, $xpath1 ), ), - 'expected' => array( - 'type' => OD_Element::class, - 'xpath' => $xpath1, - ), + 'expected' => $xpath1, ), 'intermediate_groups_conflict' => array( 'url_metrics' => array( @@ -817,9 +811,9 @@ public function data_provider_test_get_common_lcp_element(): array { * @dataProvider data_provider_test_get_common_lcp_element * * @param OD_URL_Metric[] $url_metrics URL Metrics. - * @param mixed $expected Expected. + * @param string|null $expected Expected. */ - public function test_get_common_lcp_element( array $url_metrics, $expected ): void { + public function test_get_common_lcp_element( array $url_metrics, ?string $expected ): void { $breakpoints = array( 480, 800 ); $sample_size = 3; $current_etag = md5( '' ); @@ -834,9 +828,9 @@ public function test_get_common_lcp_element( array $url_metrics, $expected ): vo $this->assertCount( 3, $group_collection ); $common_lcp_element = $group_collection->get_common_lcp_element(); - if ( is_array( $expected ) ) { - $this->assertInstanceOf( $expected['type'], $common_lcp_element ); - $this->assertSame( $expected['xpath'], $common_lcp_element->get_xpath() ); + if ( is_string( $expected ) ) { + $this->assertInstanceOf( OD_Element::class, $common_lcp_element ); + $this->assertSame( $expected, $common_lcp_element->get_xpath() ); } else { $this->assertNull( $common_lcp_element ); } From 072140b9873bfc94ea1f4f43cb1c415376525272 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Tue, 10 Dec 2024 00:36:41 +0530 Subject: [PATCH 122/237] Introduce filter `od_current_url_metrics_etag_active_theme` to improve testability Signed-off-by: Shyamsundar Gadde --- .../optimization-detective/storage/data.php | 27 ++++++++++---- .../tests/storage/test-data.php | 36 ++++++++++++------- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 1b66630480..73b4c00b30 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -156,16 +156,31 @@ function od_get_url_metrics_slug( array $query_vars ): string { * @return non-empty-string Current ETag. */ function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_registry, WP_Query $wp_query, string $current_template ): string { + $active_theme = array( + 'template' => array( + 'name' => get_template(), + 'version' => wp_get_theme( get_template() )->get( 'Version' ), + ), + 'stylesheet' => array( + 'name' => get_stylesheet(), + 'version' => wp_get_theme()->get( 'Version' ), + ), + ); + + /** + * Filters the active theme details used for computing the current ETag. + * + * @since n.e.x.t + * + * @param array> $active_theme Active theme details. + */ + $active_theme = apply_filters( 'od_current_url_metrics_etag_active_theme', $active_theme ); + $data = array( 'tag_visitors' => array_keys( iterator_to_array( $tag_visitor_registry ) ), 'queried_object' => $wp_query->get_queried_object(), 'queried_posts' => wp_list_pluck( $wp_query->posts, 'post_modified_gmt', 'ID' ), - 'active_theme' => array( - 'template' => get_template(), - 'template_version' => wp_get_theme( get_template() )->get( 'Version' ), - 'stylesheet' => get_stylesheet(), - 'stylesheet_version' => wp_get_theme()->get( 'Version' ), - ), + 'active_theme' => $active_theme, 'current_template' => $current_template, ); diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index c3f38d38e9..7003602e1e 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -321,10 +321,14 @@ static function ( array $data ) use ( &$captured_etag_data ) { 'queried_object' => null, 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( - 'template' => 'default', - 'template_version' => '1.6', - 'stylesheet' => 'default', - 'stylesheet_version' => '1.6', + 'template' => array( + 'name' => 'default', + 'version' => '1.6', + ), + 'stylesheet' => array( + 'name' => 'default', + 'version' => '1.6', + ), ), 'current_template' => 'index.php', ), @@ -363,10 +367,14 @@ static function ( array $data ) use ( &$captured_etag_data ) { 'queried_object' => null, 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( - 'template' => 'default', - 'template_version' => '1.6', - 'stylesheet' => 'default', - 'stylesheet_version' => '1.6', + 'template' => array( + 'name' => 'default', + 'version' => '1.6', + ), + 'stylesheet' => array( + 'name' => 'default', + 'version' => '1.6', + ), ), 'current_template' => 'index.php', ), @@ -396,10 +404,14 @@ static function ( $data ): array { ), 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( - 'template' => 'default', - 'template_version' => '1.6', - 'stylesheet' => 'default', - 'stylesheet_version' => '1.6', + 'template' => array( + 'name' => 'default', + 'version' => '1.6', + ), + 'stylesheet' => array( + 'name' => 'default', + 'version' => '1.6', + ), ), 'current_template' => 'index.php', ), From 4ffb79f8229573bd7f2b0b1b8ca2738bef384fdf Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Tue, 10 Dec 2024 01:13:47 +0530 Subject: [PATCH 123/237] Update test_od_get_current_url_metrics_etag Signed-off-by: Shyamsundar Gadde --- .../tests/storage/test-data.php | 79 +++++++++++++++---- 1 file changed, 64 insertions(+), 15 deletions(-) diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index 7003602e1e..dc89bf73f6 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -307,6 +307,22 @@ static function ( array $data ) use ( &$captured_etag_data ) { }, PHP_INT_MAX ); + add_filter( + 'od_current_url_metrics_etag_active_theme', + static function ( $active_theme ) { + $active_theme = array( + 'template' => array( + 'name' => 'od-theme', + 'version' => '1.0.0', + ), + 'stylesheet' => array( + 'name' => 'od-theme', + 'version' => '1.0.0', + ), + ); + return $active_theme; + } + ); $wp_the_query->query( array() ); $etag1 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); @@ -322,12 +338,12 @@ static function ( array $data ) use ( &$captured_etag_data ) { 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( 'template' => array( - 'name' => 'default', - 'version' => '1.6', + 'name' => 'od-theme', + 'version' => '1.0.0', ), 'stylesheet' => array( - 'name' => 'default', - 'version' => '1.6', + 'name' => 'od-theme', + 'version' => '1.0.0', ), ), 'current_template' => 'index.php', @@ -341,6 +357,7 @@ static function ( array $data ) use ( &$captured_etag_data ) { $wp_the_query->query( array() ); $etag3 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); $this->assertNotEquals( $etag2, $etag3 ); // Etag should change. + $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); // Update the post content. usleep( 1000000 ); // Sleep for 1 second to ensure the post_modified_gmt is different from the previous value. @@ -353,6 +370,7 @@ static function ( array $data ) use ( &$captured_etag_data ) { $wp_the_query->query( array() ); $etag4 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); $this->assertNotEquals( $etag3, $etag4 ); + $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); // Register new tag visitors. $registry->register( 'foo', static function (): void {} ); @@ -368,12 +386,44 @@ static function ( array $data ) use ( &$captured_etag_data ) { 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( 'template' => array( - 'name' => 'default', - 'version' => '1.6', + 'name' => 'od-theme', + 'version' => '1.0.0', + ), + 'stylesheet' => array( + 'name' => 'od-theme', + 'version' => '1.0.0', + ), + ), + 'current_template' => 'index.php', + ), + $captured_etag_data[ count( $captured_etag_data ) - 1 ] + ); + + // Modify data using filters. + add_filter( + 'od_current_url_metrics_etag_active_theme', + static function ( $active_theme ) { + $active_theme['template']['version'] = '2.0.0'; + $active_theme['stylesheet']['version'] = '2.0.0'; + return $active_theme; + } + ); + $etag6 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); + $this->assertNotEquals( $etag5, $etag6 ); + $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); + $this->assertSame( + array( + 'tag_visitors' => array( 'foo', 'bar', 'baz' ), + 'queried_object' => null, + 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), + 'active_theme' => array( + 'template' => array( + 'name' => 'od-theme', + 'version' => '2.0.0', ), 'stylesheet' => array( - 'name' => 'default', - 'version' => '1.6', + 'name' => 'od-theme', + 'version' => '2.0.0', ), ), 'current_template' => 'index.php', @@ -381,7 +431,6 @@ static function ( array $data ) use ( &$captured_etag_data ) { $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); - // Modify data using filter. add_filter( 'od_current_url_metrics_etag_data', static function ( $data ): array { @@ -392,8 +441,8 @@ static function ( $data ): array { return $data; } ); - $etag6 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); - $this->assertNotEquals( $etag5, $etag6 ); + $etag7 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); + $this->assertNotEquals( $etag6, $etag7 ); $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); $this->assertSame( array( @@ -405,12 +454,12 @@ static function ( $data ): array { 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( 'template' => array( - 'name' => 'default', - 'version' => '1.6', + 'name' => 'od-theme', + 'version' => '2.0.0', ), 'stylesheet' => array( - 'name' => 'default', - 'version' => '1.6', + 'name' => 'od-theme', + 'version' => '2.0.0', ), ), 'current_template' => 'index.php', From 1794cff41a530aa6e876dfb950c6f61d0e6ab9ac Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Tue, 10 Dec 2024 01:53:56 +0530 Subject: [PATCH 124/237] Factor in Block Template object Signed-off-by: Shyamsundar Gadde --- plugins/optimization-detective/optimization.php | 5 ++--- plugins/optimization-detective/storage/data.php | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/optimization-detective/optimization.php b/plugins/optimization-detective/optimization.php index a0a6fa29f7..c0f9d41983 100644 --- a/plugins/optimization-detective/optimization.php +++ b/plugins/optimization-detective/optimization.php @@ -211,9 +211,8 @@ function od_optimize_template_output_buffer( string $buffer ): string { do_action( 'od_register_tag_visitors', $tag_visitor_registry ); if ( wp_is_block_theme() ) { - // Extract the template slug from $_wp_current_template_id, which has the format 'theme_slug//template_slug'. - $parts = explode( '//', $GLOBALS['_wp_current_template_id'] ); - $current_template = ( 2 === count( $parts ) ) ? $parts[1] : ''; + $current_template = get_block_template( $GLOBALS['_wp_current_template_id'], 'wp_template' ); + $current_template = $current_template ?? basename( $GLOBALS['template'] ); } else { $current_template = basename( $GLOBALS['template'] ); } diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 73b4c00b30..83034c080b 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -150,12 +150,12 @@ function od_get_url_metrics_slug( array $query_vars ): string { * @since n.e.x.t * @access private * - * @param OD_Tag_Visitor_Registry $tag_visitor_registry Tag visitor registry. - * @param WP_Query $wp_query The WP_Query instance. - * @param string $current_template The current template being used. + * @param OD_Tag_Visitor_Registry $tag_visitor_registry Tag visitor registry. + * @param WP_Query $wp_query The WP_Query instance. + * @param string|WP_Block_Template $current_template The current template being used. * @return non-empty-string Current ETag. */ -function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_registry, WP_Query $wp_query, string $current_template ): string { +function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_registry, WP_Query $wp_query, $current_template ): string { $active_theme = array( 'template' => array( 'name' => get_template(), From 47d06dc669b34ef2cffaef01ad8be2d11cbe06ca Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 9 Dec 2024 12:36:42 -0800 Subject: [PATCH 125/237] Export web-vitals callback functions to extensions rather than webVitalsLibrarySrc --- plugins/image-prioritizer/detect.js | 5 ++--- plugins/optimization-detective/detect.js | 21 ++++++++++++++++++--- plugins/optimization-detective/types.ts | 14 +++++++++++++- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/plugins/image-prioritizer/detect.js b/plugins/image-prioritizer/detect.js index acc72d8a78..6a73074c24 100644 --- a/plugins/image-prioritizer/detect.js +++ b/plugins/image-prioritizer/detect.js @@ -63,10 +63,9 @@ function warn( ...message ) { * @type {InitializeCallback} * @param {InitializeArgs} args Args. */ -export async function initialize( { isDebug, webVitalsLibrarySrc } ) { - const { onLCP } = await import( webVitalsLibrarySrc ); +export async function initialize( { isDebug, onLCP } ) { onLCP( - ( /** @type {LCPMetric} */ metric ) => { + ( metric ) => { handleLCPMetric( metric, isDebug ); }, { diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index efbaca7476..fe4518c9ef 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -1,6 +1,11 @@ /** * @typedef {import("web-vitals").LCPMetric} LCPMetric * @typedef {import("./types.ts").ElementData} ElementData + * @typedef {import("./types.ts").OnTTFBFunction} OnTTFBFunction + * @typedef {import("./types.ts").OnFCPFunction} OnFCPFunction + * @typedef {import("./types.ts").OnLCPFunction} OnLCPFunction + * @typedef {import("./types.ts").OnINPFunction} OnINPFunction + * @typedef {import("./types.ts").OnCLSFunction} OnCLSFunction * @typedef {import("./types.ts").URLMetric} URLMetric * @typedef {import("./types.ts").URLMetricGroupStatus} URLMetricGroupStatus * @typedef {import("./types.ts").Extension} Extension @@ -335,6 +340,14 @@ export default async function detect( { { once: true } ); + const { + /** @type OnTTFBFunction */ onTTFB, + /** @type OnFCPFunction */ onFCP, + /** @type OnLCPFunction */ onLCP, + /** @type OnINPFunction */ onINP, + /** @type OnCLSFunction */ onCLS, + } = await import( webVitalsLibrarySrc ); + // TODO: Does this make sense here? // Prevent detection when page is not scrolled to the initial viewport. if ( doc.documentElement.scrollTop > 0 ) { @@ -368,7 +381,11 @@ export default async function detect( { if ( extension.initialize instanceof Function ) { const initializePromise = extension.initialize( { isDebug, - webVitalsLibrarySrc, + onTTFB, + onFCP, + onLCP, + onINP, + onCLS, } ); if ( initializePromise instanceof Promise ) { extensionInitializePromises.push( initializePromise ); @@ -454,8 +471,6 @@ export default async function detect( { } ); } - const { onLCP } = await import( webVitalsLibrarySrc ); - /** @type {LCPMetric[]} */ const lcpMetricCandidates = []; diff --git a/plugins/optimization-detective/types.ts b/plugins/optimization-detective/types.ts index cafd6f9d3a..d92c532143 100644 --- a/plugins/optimization-detective/types.ts +++ b/plugins/optimization-detective/types.ts @@ -1,6 +1,8 @@ // h/t https://stackoverflow.com/a/59801602/93579 type ExcludeProps< T > = { [ k: string ]: any } & { [ K in keyof T ]?: never }; +import { onTTFB, onFCP, onLCP, onINP, onCLS } from 'web-vitals'; + export interface ElementData { isLCP: boolean; isLCPCandidate: boolean; @@ -28,9 +30,19 @@ export interface URLMetricGroupStatus { complete: boolean; } +export type OnTTFBFunction = typeof onTTFB; +export type OnFCPFunction = typeof onFCP; +export type OnLCPFunction = typeof onLCP; +export type OnINPFunction = typeof onINP; +export type OnCLSFunction = typeof onCLS; + export type InitializeArgs = { readonly isDebug: boolean; - readonly webVitalsLibrarySrc: string; + readonly onTTFB: OnTTFBFunction; + readonly onFCP: OnFCPFunction; + readonly onLCP: OnLCPFunction; + readonly onINP: OnINPFunction; + readonly onCLS: OnCLSFunction; }; export type InitializeCallback = ( args: InitializeArgs ) => Promise< void >; From 2f65d47b63ecd2d2a4f0cf7197208e99395cdfb3 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Tue, 10 Dec 2024 09:50:34 +0530 Subject: [PATCH 126/237] Remove $mime_type variable --- plugins/webp-uploads/tests/test-load.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/webp-uploads/tests/test-load.php b/plugins/webp-uploads/tests/test-load.php index b834828587..b25bf44b96 100644 --- a/plugins/webp-uploads/tests/test-load.php +++ b/plugins/webp-uploads/tests/test-load.php @@ -1092,11 +1092,9 @@ public function test_it_should_generate_fallback_images_for_all_sizes_when_gener * Create the original mime type for WebP images. */ public function test_that_it_should_convert_webp_to_avif_on_upload(): void { - $mime_type = 'image/avif'; - - // Ensure the MIME type is supported; skip the test if not. - if ( ! webp_uploads_mime_type_supported( $mime_type ) ) { - $this->markTestSkipped( 'Mime type ' . $mime_type . ' is not supported.' ); + // Ensure the AVIF MIME type is supported; skip the test if not. + if ( ! webp_uploads_mime_type_supported( 'image/avif' ) ) { + $this->markTestSkipped( 'Mime type image/avif is not supported.' ); } $this->set_image_output_type( 'avif' ); From 1c0bdac2119518c1e8a6b0e671cb0f4d61bf2999 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Wed, 11 Dec 2024 00:22:17 +0530 Subject: [PATCH 127/237] Remove unnecessary filter `od_current_url_metrics_etag_active_theme` Signed-off-by: Shyamsundar Gadde --- .../optimization-detective/storage/data.php | 31 ++++++------------- .../tests/storage/test-data.php | 18 +++++------ 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 83034c080b..dd70995ca1 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -156,31 +156,20 @@ function od_get_url_metrics_slug( array $query_vars ): string { * @return non-empty-string Current ETag. */ function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_registry, WP_Query $wp_query, $current_template ): string { - $active_theme = array( - 'template' => array( - 'name' => get_template(), - 'version' => wp_get_theme( get_template() )->get( 'Version' ), - ), - 'stylesheet' => array( - 'name' => get_stylesheet(), - 'version' => wp_get_theme()->get( 'Version' ), - ), - ); - - /** - * Filters the active theme details used for computing the current ETag. - * - * @since n.e.x.t - * - * @param array> $active_theme Active theme details. - */ - $active_theme = apply_filters( 'od_current_url_metrics_etag_active_theme', $active_theme ); - $data = array( 'tag_visitors' => array_keys( iterator_to_array( $tag_visitor_registry ) ), 'queried_object' => $wp_query->get_queried_object(), 'queried_posts' => wp_list_pluck( $wp_query->posts, 'post_modified_gmt', 'ID' ), - 'active_theme' => $active_theme, + 'active_theme' => array( + 'template' => array( + 'name' => get_template(), + 'version' => wp_get_theme( get_template() )->get( 'Version' ), + ), + 'stylesheet' => array( + 'name' => get_stylesheet(), + 'version' => wp_get_theme()->get( 'Version' ), + ), + ), 'current_template' => $current_template, ); diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index dc89bf73f6..68ae7703e6 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -308,9 +308,9 @@ static function ( array $data ) use ( &$captured_etag_data ) { PHP_INT_MAX ); add_filter( - 'od_current_url_metrics_etag_active_theme', - static function ( $active_theme ) { - $active_theme = array( + 'od_current_url_metrics_etag_data', + static function ( $data ) { + $data['active_theme'] = array( 'template' => array( 'name' => 'od-theme', 'version' => '1.0.0', @@ -320,7 +320,7 @@ static function ( $active_theme ) { 'version' => '1.0.0', ), ); - return $active_theme; + return $data; } ); @@ -401,11 +401,11 @@ static function ( $active_theme ) { // Modify data using filters. add_filter( - 'od_current_url_metrics_etag_active_theme', - static function ( $active_theme ) { - $active_theme['template']['version'] = '2.0.0'; - $active_theme['stylesheet']['version'] = '2.0.0'; - return $active_theme; + 'od_current_url_metrics_etag_data', + static function ( $data ) { + $data['active_theme']['template']['version'] = '2.0.0'; + $data['active_theme']['stylesheet']['version'] = '2.0.0'; + return $data; } ); $etag6 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); From 8ca8b2730feac8140195eded8cb06c7346df362b Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Tue, 10 Dec 2024 16:32:50 -0600 Subject: [PATCH 128/237] WIP: fix center alignments and cover blocks --- .../includes/improve-calculate-sizes.php | 12 ++++++------ .../tests/test-improve-calculate-sizes.php | 19 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index d6174c9c47..564a382dd4 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -162,15 +162,15 @@ function auto_sizes_calculate_better_sizes( int $id, string $size, string $align /* * Map alignment values to a weighting value so they can be compared. - * Note that 'left', 'right', and 'center' alignments are only constrained by default containers. + * Note that 'left' and 'right' alignments are only constrained by max alignment. */ $constraints = array( 'full' => 0, - 'wide' => 1, - 'left' => 2, - 'right' => 2, - 'center' => 2, + 'left' => 1, + 'right' => 1, + 'wide' => 2, 'default' => 3, + 'center' => 3, ); $alignment = $constraints[ $align ] > $constraints[ $max_alignment ] ? $align : $max_alignment; @@ -187,12 +187,12 @@ function auto_sizes_calculate_better_sizes( int $id, string $size, string $align case 'left': case 'right': - case 'center': // These alignment get constrained by the wide layout size but do not get stretched. $alignment = auto_sizes_get_layout_width( 'wide' ); $layout_width = sprintf( '%1$spx', min( (int) $alignment, $image_width ) ); break; + case 'center': default: $alignment = auto_sizes_get_layout_width( 'default' ); $layout_width = sprintf( '%1$spx', min( (int) $alignment, $image_width ) ); diff --git a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php index 81529b27b3..6de672be97 100644 --- a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -273,14 +273,14 @@ public function data_image_sizes_for_left_right_center_alignment(): array { 'sizes="(max-width: 300px) 100vw, 300px" ', 'center', ), - 'Return large image size 1024px with center alignment' => array( + 'Return large image size 620px with center alignment' => array( 'large', - 'sizes="(max-width: 1024px) 100vw, 1024px" ', + 'sizes="(max-width: 620px) 100vw, 620px" ', 'center', ), - 'Return full image size 1080px with center alignment' => array( + 'Return full image size 620px with center alignment' => array( 'full', - 'sizes="(max-width: 1080px) 100vw, 1080px" ', + 'sizes="(max-width: 620px) 100vw, 620px" ', 'center', ), 'Return resized size 100px instead of medium image size 300px with left alignment' => array( @@ -346,8 +346,9 @@ public function data_image_sizes_for_left_right_center_alignment(): array { * @dataProvider data_image_left_right_center_alignment * * @param string $alignment Alignment of the image. + * @param string $expected Expected output. */ - public function test_cover_block_with_left_right_center_alignment( string $alignment ): void { + public function test_cover_block_with_left_right_center_alignment( string $alignment, string $expected ): void { $image_url = wp_get_attachment_image_url( self::$image_id, 'full' ); $block_content = '
@@ -357,7 +358,7 @@ public function test_cover_block_with_left_right_center_alignment( string $align $result = apply_filters( 'the_content', $block_content ); - $this->assertStringContainsString( 'sizes="(max-width: 1080px) 100vw, 1080px" ', $result ); + $this->assertStringContainsString( $expected, $result ); } /** @@ -367,9 +368,9 @@ public function test_cover_block_with_left_right_center_alignment( string $align */ public function data_image_left_right_center_alignment(): array { return array( - array( 'left' ), - array( 'right' ), - array( 'center' ), + array( 'left', 'sizes="(max-width: 420px) 100vw, 420px' ), + array( 'right', 'sizes="(max-width: 420px) 100vw, 420px' ), + array( 'center', 'sizes="(max-width: 620px) 100vw, 620px' ), ); } From 89c62b3a9a75d2e4b78c1a09e289503c7ee5a4dd Mon Sep 17 00:00:00 2001 From: Paul Bearne Date: Tue, 10 Dec 2024 17:36:13 -0500 Subject: [PATCH 129/237] Update plugins/dominant-color-images/hooks.php Co-authored-by: Weston Ruter --- plugins/dominant-color-images/hooks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index 6b8947d429..1e7e58e1aa 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -229,7 +229,7 @@ function dominant_color_admin_script(): void { Date: Tue, 10 Dec 2024 17:36:45 -0500 Subject: [PATCH 130/237] Update plugins/dominant-color-images/hooks.php Co-authored-by: Weston Ruter --- plugins/dominant-color-images/hooks.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index 1e7e58e1aa..14d05ecb3c 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -221,10 +221,13 @@ function dominant_color_admin_script(): void { Date: Wed, 11 Dec 2024 10:10:10 +0530 Subject: [PATCH 131/237] Return accurate sizes for center alignment --- .../includes/improve-calculate-sizes.php | 23 ++++++++++++------- .../tests/test-improve-calculate-sizes.php | 14 +++++------ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 564a382dd4..813ac4de4f 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -82,12 +82,13 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b */ $filter = static function ( $sizes, $size ) use ( $block ) { + $block_name = $block->name; $id = $block->attributes['id'] ?? 0; $alignment = $block->attributes['align'] ?? ''; $width = $block->attributes['width'] ?? ''; $max_alignment = $block->context['max_alignment']; - $better_sizes = auto_sizes_calculate_better_sizes( (int) $id, (string) $size, (string) $alignment, (string) $width, (string) $max_alignment ); + $better_sizes = auto_sizes_calculate_better_sizes( (string) $block_name, (int) $id, (string) $size, (string) $alignment, (string) $width, (string) $max_alignment ); // If better sizes can't be calculated, use the default sizes. return false !== $better_sizes ? $better_sizes : $sizes; @@ -124,6 +125,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b * * @since n.e.x.t * + * @param string $block_name The block name. * @param int $id The image id. * @param string $size The image size data. * @param string $align The image alignment. @@ -131,7 +133,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b * @param string $max_alignment The maximum usable layout alignment. * @return string|false An improved sizes attribute or false if a better size cannot be calculated. */ -function auto_sizes_calculate_better_sizes( int $id, string $size, string $align, string $resize_width, string $max_alignment ) { +function auto_sizes_calculate_better_sizes( string $block_name, int $id, string $size, string $align, string $resize_width, string $max_alignment ) { // Without an image ID or a resize width, we cannot calculate a better size. if ( ! (bool) $id && ! (bool) $resize_width ) { return false; @@ -166,9 +168,9 @@ function auto_sizes_calculate_better_sizes( int $id, string $size, string $align */ $constraints = array( 'full' => 0, - 'left' => 1, - 'right' => 1, - 'wide' => 2, + 'wide' => 1, + 'left' => 2, + 'right' => 2, 'default' => 3, 'center' => 3, ); @@ -187,9 +189,14 @@ function auto_sizes_calculate_better_sizes( int $id, string $size, string $align case 'left': case 'right': - // These alignment get constrained by the wide layout size but do not get stretched. - $alignment = auto_sizes_get_layout_width( 'wide' ); - $layout_width = sprintf( '%1$spx', min( (int) $alignment, $image_width ) ); + /* + * Update width for cover block. + * See https://github.com/WordPress/gutenberg/blob/938720602082dc50a1746bd2e33faa3d3a6096d4/packages/block-library/src/cover/style.scss#L82-L87. + */ + if ( 'core/cover' === $block_name ) { + $image_width = $image_width * 0.5; + } + $layout_width = sprintf( '%1$spx', $image_width ); break; case 'center': diff --git a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php index 6de672be97..58a692edd5 100644 --- a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -341,7 +341,7 @@ public function data_image_sizes_for_left_right_center_alignment(): array { } /** - * Test the cover block with left and right alignment. + * Test the cover block with left, right and center alignment. * * @dataProvider data_image_left_right_center_alignment * @@ -368,8 +368,8 @@ public function test_cover_block_with_left_right_center_alignment( string $align */ public function data_image_left_right_center_alignment(): array { return array( - array( 'left', 'sizes="(max-width: 420px) 100vw, 420px' ), - array( 'right', 'sizes="(max-width: 420px) 100vw, 420px' ), + array( 'left', 'sizes="(max-width: 540px) 100vw, 540px' ), + array( 'right', 'sizes="(max-width: 540px) 100vw, 540px' ), array( 'center', 'sizes="(max-width: 620px) 100vw, 620px' ), ); } @@ -469,10 +469,10 @@ public function data_ancestor_and_image_block_alignment(): array { 'left', 'sizes="(max-width: 1024px) 100vw, 1024px" ', ), - 'Return image size 1024px, parent block wide alignment, image block center alignment' => array( + 'Return image size 620px, parent block wide alignment, image block center alignment' => array( 'wide', 'center', - 'sizes="(max-width: 1024px) 100vw, 1024px" ', + 'sizes="(max-width: 620px) 100vw, 620px" ', ), 'Return image size 1024px, parent block wide alignment, image block right alignment' => array( 'wide', @@ -501,10 +501,10 @@ public function data_ancestor_and_image_block_alignment(): array { 'left', 'sizes="(max-width: 1024px) 100vw, 1024px" ', ), - 'Return image size 1024px, parent block full alignment, image block center alignment' => array( + 'Return image size 620px, parent block full alignment, image block center alignment' => array( 'full', 'center', - 'sizes="(max-width: 1024px) 100vw, 1024px" ', + 'sizes="(max-width: 620px) 100vw, 620px" ', ), 'Return image size 1024px, parent block full alignment, image block right alignment' => array( 'full', From 6f588eef022354d9f98c950df9146586c5425e66 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Wed, 11 Dec 2024 13:42:21 +0530 Subject: [PATCH 132/237] Update composer lockfile Signed-off-by: Shyamsundar Gadde --- composer.lock | 129 +++++++++++++++++++++++++------------------------- 1 file changed, 65 insertions(+), 64 deletions(-) diff --git a/composer.lock b/composer.lock index f2daba96fe..f398772282 100644 --- a/composer.lock +++ b/composer.lock @@ -157,16 +157,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.0", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", - "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -205,7 +205,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -213,7 +213,7 @@ "type": "tidelift" } ], - "time": "2024-06-12T14:39:25+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "phar-io/manifest", @@ -335,27 +335,28 @@ }, { "name": "php-stubs/wordpress-stubs", - "version": "v6.5.3", + "version": "v6.7.1", "source": { "type": "git", "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "e611a83292d02055a25f83291a98fadd0c21e092" + "reference": "83448e918bf06d1ed3d67ceb6a985fc266a02fd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/e611a83292d02055a25f83291a98fadd0c21e092", - "reference": "e611a83292d02055a25f83291a98fadd0c21e092", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/83448e918bf06d1ed3d67ceb6a985fc266a02fd1", + "reference": "83448e918bf06d1ed3d67ceb6a985fc266a02fd1", "shasum": "" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "nikic/php-parser": "^4.13", - "php": "^7.4 || ~8.0.0", + "php": "^7.4 || ^8.0", "php-stubs/generator": "^0.8.3", - "phpdocumentor/reflection-docblock": "5.3", - "phpstan/phpstan": "^1.10.49", + "phpdocumentor/reflection-docblock": "^5.4.1", + "phpstan/phpstan": "^1.11", "phpunit/phpunit": "^9.5", - "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^0.11" + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", + "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" }, "suggest": { "paragonie/sodium_compat": "Pure PHP implementation of libsodium", @@ -376,9 +377,9 @@ ], "support": { "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.5.3" + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.7.1" }, - "time": "2024-05-08T02:12:31+00:00" + "time": "2024-11-24T03:57:09+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -522,22 +523,22 @@ }, { "name": "phpcsstandards/phpcsutils", - "version": "1.0.11", + "version": "1.0.12", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", - "reference": "c457da9dabb60eb7106dd5e3c05132b1a6539c6a" + "reference": "87b233b00daf83fb70f40c9a28692be017ea7c6c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/c457da9dabb60eb7106dd5e3c05132b1a6539c6a", - "reference": "c457da9dabb60eb7106dd5e3c05132b1a6539c6a", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/87b233b00daf83fb70f40c9a28692be017ea7c6c", + "reference": "87b233b00daf83fb70f40c9a28692be017ea7c6c", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.9.0 || 4.0.x-dev@dev" + "squizlabs/php_codesniffer": "^3.10.0 || 4.0.x-dev@dev" }, "require-dev": { "ext-filter": "*", @@ -606,7 +607,7 @@ "type": "open_collective" } ], - "time": "2024-04-24T11:47:18+00:00" + "time": "2024-05-20T13:34:27+00:00" }, { "name": "phpstan/extension-installer", @@ -690,16 +691,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.29.1", + "version": "1.33.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" + "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/82a311fd3690fb2bf7b64d5c98f912b3dd746140", + "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140", "shasum": "" }, "require": { @@ -731,22 +732,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.33.0" }, - "time": "2024-05-31T08:52:43+00:00" + "time": "2024-10-13T11:25:22+00:00" }, { "name": "phpstan/phpstan", - "version": "1.12.7", + "version": "1.12.12", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0" + "reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", - "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0", + "reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0", "shasum": "" }, "require": { @@ -791,7 +792,7 @@ "type": "github" } ], - "time": "2024-10-18T11:12:07+00:00" + "time": "2024-11-28T22:13:23+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -842,21 +843,21 @@ }, { "name": "phpstan/phpstan-phpunit", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "f3ea021866f4263f07ca3636bf22c64be9610c11" + "reference": "11d4235fbc6313ecbf93708606edfd3222e44949" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/f3ea021866f4263f07ca3636bf22c64be9610c11", - "reference": "f3ea021866f4263f07ca3636bf22c64be9610c11", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/11d4235fbc6313ecbf93708606edfd3222e44949", + "reference": "11d4235fbc6313ecbf93708606edfd3222e44949", "shasum": "" }, "require": { "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.11" + "phpstan/phpstan": "^1.12" }, "conflict": { "phpunit/phpunit": "<7.0" @@ -888,9 +889,9 @@ "description": "PHPUnit extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.4.0" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.4.1" }, - "time": "2024-04-20T06:39:00+00:00" + "time": "2024-11-12T12:43:59+00:00" }, { "name": "phpstan/phpstan-strict-rules", @@ -1240,16 +1241,16 @@ }, { "name": "phpunit/phpunit", - "version": "8.5.39", + "version": "8.5.41", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "172ba97bcf97ae6ef86ca256adf77aece8a143fe" + "reference": "d843cb5bcf0bf9ae3484016444fe0c5b6ec7e4fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/172ba97bcf97ae6ef86ca256adf77aece8a143fe", - "reference": "172ba97bcf97ae6ef86ca256adf77aece8a143fe", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d843cb5bcf0bf9ae3484016444fe0c5b6ec7e4fa", + "reference": "d843cb5bcf0bf9ae3484016444fe0c5b6ec7e4fa", "shasum": "" }, "require": { @@ -1260,7 +1261,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.0", + "myclabs/deep-copy": "^1.12.1", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.2", @@ -1318,7 +1319,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.39" + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.41" }, "funding": [ { @@ -1334,7 +1335,7 @@ "type": "tidelift" } ], - "time": "2024-07-10T11:43:00+00:00" + "time": "2024-12-05T13:44:26+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -2211,26 +2212,26 @@ }, { "name": "symfony/polyfill-php73", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "ec444d3f3f6505bb28d11afa41e75faadebc10a1" + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/ec444d3f3f6505bb28d11afa41e75faadebc10a1", - "reference": "ec444d3f3f6505bb28d11afa41e75faadebc10a1", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -2267,7 +2268,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" }, "funding": [ { @@ -2283,7 +2284,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "szepeviktor/phpstan-wordpress", @@ -2514,16 +2515,16 @@ }, { "name": "yoast/phpunit-polyfills", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "4a088f125c970d6d6ea52c927f96fe39b330d0f1" + "reference": "562f449a2ec8ab92fe7b30d94da9622c7b1345fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/4a088f125c970d6d6ea52c927f96fe39b330d0f1", - "reference": "4a088f125c970d6d6ea52c927f96fe39b330d0f1", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/562f449a2ec8ab92fe7b30d94da9622c7b1345fe", + "reference": "562f449a2ec8ab92fe7b30d94da9622c7b1345fe", "shasum": "" }, "require": { @@ -2538,7 +2539,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -2573,19 +2574,19 @@ "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", "source": "https://github.com/Yoast/PHPUnit-Polyfills" }, - "time": "2024-04-05T16:36:44+00:00" + "time": "2024-09-06T22:38:28+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^7.2 || ^8.0", "ext-json": "*" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "7.2" }, From 069fc734abafd7db685d36b2f486d61a73c1253c Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Wed, 11 Dec 2024 13:45:11 +0530 Subject: [PATCH 133/237] Remove redundant bootstrap files from phpstan config Signed-off-by: Shyamsundar Gadde --- phpstan.neon.dist | 1 - 1 file changed, 1 deletion(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index c66cc423a5..5695724a95 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -10,7 +10,6 @@ parameters: - plugins/performance-lab/load.php bootstrapFiles: - tools/phpstan/constants.php - - plugins/performance-lab/load.php - plugins/webp-uploads/load.php scanDirectories: - vendor/wp-phpunit/wp-phpunit/ From ca19c6b32919f65aff7df82e1027297cc7396468 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Wed, 11 Dec 2024 16:43:24 +0530 Subject: [PATCH 134/237] Update test doc --- plugins/webp-uploads/tests/test-load.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/webp-uploads/tests/test-load.php b/plugins/webp-uploads/tests/test-load.php index b25bf44b96..1aa09f5626 100644 --- a/plugins/webp-uploads/tests/test-load.php +++ b/plugins/webp-uploads/tests/test-load.php @@ -1089,7 +1089,7 @@ public function test_it_should_generate_fallback_images_for_all_sizes_when_gener } /** - * Create the original mime type for WebP images. + * Convert WebP to AVIF on uploads. */ public function test_that_it_should_convert_webp_to_avif_on_upload(): void { // Ensure the AVIF MIME type is supported; skip the test if not. From bafba80034e4da43c477d8bc0082b3fbd44bbd77 Mon Sep 17 00:00:00 2001 From: Paul Bearne Date: Wed, 11 Dec 2024 09:07:15 -0500 Subject: [PATCH 135/237] Update plugins/dominant-color-images/hooks.php Co-authored-by: Weston Ruter --- plugins/dominant-color-images/hooks.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index 14d05ecb3c..f97cf1dc49 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -221,13 +221,13 @@ function dominant_color_admin_script(): void { Date: Wed, 11 Dec 2024 09:08:31 -0500 Subject: [PATCH 136/237] Update hooks.php --- plugins/dominant-color-images/hooks.php | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index f97cf1dc49..d31bc6af01 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -258,7 +258,6 @@ function dominant_color_prepare_attachment_for_js( $response, WP_Post $attachmen $response['dominantColor'] = sanitize_hex_color( $meta['dominant_color'] ); } $response['hasTransparency'] = ''; - $response['hasTransparencyClass'] = ''; if ( isset( $meta['has_transparency'] ) ) { $response['hasTransparency'] = (bool) $meta['has_transparency']; } From 181cad50e8975e86a5638b616e36cb2e25d38c69 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Wed, 11 Dec 2024 19:58:58 +0530 Subject: [PATCH 137/237] Revert "Update composer lockfile" This reverts commit 6f588eef022354d9f98c950df9146586c5425e66. --- composer.lock | 129 +++++++++++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 65 deletions(-) diff --git a/composer.lock b/composer.lock index f398772282..f2daba96fe 100644 --- a/composer.lock +++ b/composer.lock @@ -157,16 +157,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.12.1", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", - "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", + "reference": "3a6b9a42cd8f8771bd4295d13e1423fa7f3d942c", "shasum": "" }, "require": { @@ -205,7 +205,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.0" }, "funding": [ { @@ -213,7 +213,7 @@ "type": "tidelift" } ], - "time": "2024-11-08T17:47:46+00:00" + "time": "2024-06-12T14:39:25+00:00" }, { "name": "phar-io/manifest", @@ -335,28 +335,27 @@ }, { "name": "php-stubs/wordpress-stubs", - "version": "v6.7.1", + "version": "v6.5.3", "source": { "type": "git", "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "83448e918bf06d1ed3d67ceb6a985fc266a02fd1" + "reference": "e611a83292d02055a25f83291a98fadd0c21e092" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/83448e918bf06d1ed3d67ceb6a985fc266a02fd1", - "reference": "83448e918bf06d1ed3d67ceb6a985fc266a02fd1", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/e611a83292d02055a25f83291a98fadd0c21e092", + "reference": "e611a83292d02055a25f83291a98fadd0c21e092", "shasum": "" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "nikic/php-parser": "^4.13", - "php": "^7.4 || ^8.0", + "php": "^7.4 || ~8.0.0", "php-stubs/generator": "^0.8.3", - "phpdocumentor/reflection-docblock": "^5.4.1", - "phpstan/phpstan": "^1.11", + "phpdocumentor/reflection-docblock": "5.3", + "phpstan/phpstan": "^1.10.49", "phpunit/phpunit": "^9.5", - "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", - "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^0.11" }, "suggest": { "paragonie/sodium_compat": "Pure PHP implementation of libsodium", @@ -377,9 +376,9 @@ ], "support": { "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.7.1" + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.5.3" }, - "time": "2024-11-24T03:57:09+00:00" + "time": "2024-05-08T02:12:31+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -523,22 +522,22 @@ }, { "name": "phpcsstandards/phpcsutils", - "version": "1.0.12", + "version": "1.0.11", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", - "reference": "87b233b00daf83fb70f40c9a28692be017ea7c6c" + "reference": "c457da9dabb60eb7106dd5e3c05132b1a6539c6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/87b233b00daf83fb70f40c9a28692be017ea7c6c", - "reference": "87b233b00daf83fb70f40c9a28692be017ea7c6c", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/c457da9dabb60eb7106dd5e3c05132b1a6539c6a", + "reference": "c457da9dabb60eb7106dd5e3c05132b1a6539c6a", "shasum": "" }, "require": { "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.10.0 || 4.0.x-dev@dev" + "squizlabs/php_codesniffer": "^3.9.0 || 4.0.x-dev@dev" }, "require-dev": { "ext-filter": "*", @@ -607,7 +606,7 @@ "type": "open_collective" } ], - "time": "2024-05-20T13:34:27+00:00" + "time": "2024-04-24T11:47:18+00:00" }, { "name": "phpstan/extension-installer", @@ -691,16 +690,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.33.0", + "version": "1.29.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140" + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/82a311fd3690fb2bf7b64d5c98f912b3dd746140", - "reference": "82a311fd3690fb2bf7b64d5c98f912b3dd746140", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", "shasum": "" }, "require": { @@ -732,22 +731,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.33.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" }, - "time": "2024-10-13T11:25:22+00:00" + "time": "2024-05-31T08:52:43+00:00" }, { "name": "phpstan/phpstan", - "version": "1.12.12", + "version": "1.12.7", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0" + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0", - "reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", + "reference": "dc2b9976bd8b0f84ec9b0e50cc35378551de7af0", "shasum": "" }, "require": { @@ -792,7 +791,7 @@ "type": "github" } ], - "time": "2024-11-28T22:13:23+00:00" + "time": "2024-10-18T11:12:07+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -843,21 +842,21 @@ }, { "name": "phpstan/phpstan-phpunit", - "version": "1.4.1", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "11d4235fbc6313ecbf93708606edfd3222e44949" + "reference": "f3ea021866f4263f07ca3636bf22c64be9610c11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/11d4235fbc6313ecbf93708606edfd3222e44949", - "reference": "11d4235fbc6313ecbf93708606edfd3222e44949", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/f3ea021866f4263f07ca3636bf22c64be9610c11", + "reference": "f3ea021866f4263f07ca3636bf22c64be9610c11", "shasum": "" }, "require": { "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.12" + "phpstan/phpstan": "^1.11" }, "conflict": { "phpunit/phpunit": "<7.0" @@ -889,9 +888,9 @@ "description": "PHPUnit extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.4.1" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.4.0" }, - "time": "2024-11-12T12:43:59+00:00" + "time": "2024-04-20T06:39:00+00:00" }, { "name": "phpstan/phpstan-strict-rules", @@ -1241,16 +1240,16 @@ }, { "name": "phpunit/phpunit", - "version": "8.5.41", + "version": "8.5.39", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "d843cb5bcf0bf9ae3484016444fe0c5b6ec7e4fa" + "reference": "172ba97bcf97ae6ef86ca256adf77aece8a143fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d843cb5bcf0bf9ae3484016444fe0c5b6ec7e4fa", - "reference": "d843cb5bcf0bf9ae3484016444fe0c5b6ec7e4fa", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/172ba97bcf97ae6ef86ca256adf77aece8a143fe", + "reference": "172ba97bcf97ae6ef86ca256adf77aece8a143fe", "shasum": "" }, "require": { @@ -1261,7 +1260,7 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.12.1", + "myclabs/deep-copy": "^1.12.0", "phar-io/manifest": "^2.0.4", "phar-io/version": "^3.2.1", "php": ">=7.2", @@ -1319,7 +1318,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.41" + "source": "https://github.com/sebastianbergmann/phpunit/tree/8.5.39" }, "funding": [ { @@ -1335,7 +1334,7 @@ "type": "tidelift" } ], - "time": "2024-12-05T13:44:26+00:00" + "time": "2024-07-10T11:43:00+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -2212,26 +2211,26 @@ }, { "name": "symfony/polyfill-php73", - "version": "v1.31.0", + "version": "v1.30.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" + "reference": "ec444d3f3f6505bb28d11afa41e75faadebc10a1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", - "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/ec444d3f3f6505bb28d11afa41e75faadebc10a1", + "reference": "ec444d3f3f6505bb28d11afa41e75faadebc10a1", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.1" }, "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } }, "autoload": { @@ -2268,7 +2267,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.30.0" }, "funding": [ { @@ -2284,7 +2283,7 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-05-31T15:07:36+00:00" }, { "name": "szepeviktor/phpstan-wordpress", @@ -2515,16 +2514,16 @@ }, { "name": "yoast/phpunit-polyfills", - "version": "2.0.2", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "562f449a2ec8ab92fe7b30d94da9622c7b1345fe" + "reference": "4a088f125c970d6d6ea52c927f96fe39b330d0f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/562f449a2ec8ab92fe7b30d94da9622c7b1345fe", - "reference": "562f449a2ec8ab92fe7b30d94da9622c7b1345fe", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/4a088f125c970d6d6ea52c927f96fe39b330d0f1", + "reference": "4a088f125c970d6d6ea52c927f96fe39b330d0f1", "shasum": "" }, "require": { @@ -2539,7 +2538,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.x-dev" + "dev-main": "2.x-dev" } }, "autoload": { @@ -2574,19 +2573,19 @@ "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", "source": "https://github.com/Yoast/PHPUnit-Polyfills" }, - "time": "2024-09-06T22:38:28+00:00" + "time": "2024-04-05T16:36:44+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^7.2 || ^8.0", "ext-json": "*" }, - "platform-dev": {}, + "platform-dev": [], "platform-overrides": { "php": "7.2" }, From 989eb59133fc6f8fba44d97cc314e8e23057bd3d Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Wed, 11 Dec 2024 20:00:16 +0530 Subject: [PATCH 138/237] Bump php-stubs/wordpress-stubs from 6.5.3 to 6.7.1 Signed-off-by: Shyamsundar Gadde --- composer.lock | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/composer.lock b/composer.lock index f2daba96fe..07069b38ca 100644 --- a/composer.lock +++ b/composer.lock @@ -335,27 +335,28 @@ }, { "name": "php-stubs/wordpress-stubs", - "version": "v6.5.3", + "version": "v6.7.1", "source": { "type": "git", "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "e611a83292d02055a25f83291a98fadd0c21e092" + "reference": "83448e918bf06d1ed3d67ceb6a985fc266a02fd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/e611a83292d02055a25f83291a98fadd0c21e092", - "reference": "e611a83292d02055a25f83291a98fadd0c21e092", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/83448e918bf06d1ed3d67ceb6a985fc266a02fd1", + "reference": "83448e918bf06d1ed3d67ceb6a985fc266a02fd1", "shasum": "" }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "nikic/php-parser": "^4.13", - "php": "^7.4 || ~8.0.0", + "php": "^7.4 || ^8.0", "php-stubs/generator": "^0.8.3", - "phpdocumentor/reflection-docblock": "5.3", - "phpstan/phpstan": "^1.10.49", + "phpdocumentor/reflection-docblock": "^5.4.1", + "phpstan/phpstan": "^1.11", "phpunit/phpunit": "^9.5", - "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^0.11" + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", + "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" }, "suggest": { "paragonie/sodium_compat": "Pure PHP implementation of libsodium", @@ -376,9 +377,9 @@ ], "support": { "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.5.3" + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.7.1" }, - "time": "2024-05-08T02:12:31+00:00" + "time": "2024-11-24T03:57:09+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -2578,14 +2579,14 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^7.2 || ^8.0", "ext-json": "*" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "7.2" }, From 2add13307c54ccbfb9a1d72836b0e135b30b5562 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 11 Dec 2024 09:10:37 -0800 Subject: [PATCH 139/237] Add tests for hooks being added --- plugins/image-prioritizer/tests/test-hooks.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 plugins/image-prioritizer/tests/test-hooks.php diff --git a/plugins/image-prioritizer/tests/test-hooks.php b/plugins/image-prioritizer/tests/test-hooks.php new file mode 100644 index 0000000000..740c71a3d5 --- /dev/null +++ b/plugins/image-prioritizer/tests/test-hooks.php @@ -0,0 +1,18 @@ +assertEquals( 10, has_action( 'od_init', 'image_prioritizer_init' ) ); + $this->assertEquals( 10, has_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ) ); + $this->assertEquals( 10, has_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ) ); + } +} From 5c773867fc74ff1601b1ceb665b58de0f476ee15 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Wed, 11 Dec 2024 23:14:28 +0530 Subject: [PATCH 140/237] Add guidance for managing Performance feature plugins Signed-off-by: Shyamsundar Gadde --- plugins/performance-lab/includes/admin/plugins.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index 2458792c1a..a687f69865 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -291,6 +291,16 @@ static function ( string $error_message ): string {
+

+ ', + '' + ); + ?> +

Date: Wed, 11 Dec 2024 23:45:50 +0530 Subject: [PATCH 141/237] Replace hardcoded URL with `add_query_arg()` Signed-off-by: Shyamsundar Gadde --- plugins/performance-lab/includes/admin/plugins.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index a687f69865..377f984ba3 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -296,7 +296,15 @@ static function ( string $error_message ): string { printf( /* translators: %1$s: opening anchor tag, %2$s: closing anchor tag */ esc_html__( 'Performance features are installed as plugins. To update features or remove them, %1$s manage them on the plugins screen. %2$s', 'performance-lab' ), - '', + '', '' ); ?> From 3abf92af715aaf7c3f185b72b147e18a5a8bd3be Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 11 Dec 2024 10:50:22 -0800 Subject: [PATCH 142/237] Add missing unit tests for functions --- plugins/image-prioritizer/helper.php | 3 +- .../image-prioritizer/tests/test-helper.php | 246 +++++++++++++++++- 2 files changed, 242 insertions(+), 7 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index c93153dcfa..1651dcf555 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -107,7 +107,8 @@ function image_prioritizer_add_element_item_schema_properties( array $additional 'properties' => array( 'url' => array( 'type' => 'string', - 'format' => 'uri', + 'format' => 'uri', // Note: This is excessively lax, as it is used exclusively in rest_sanitize_value_from_schema() and not in rest_validate_value_from_schema(). + 'pattern' => '^https?://', 'required' => true, 'maxLength' => 500, // Image URLs can be quite long. ), diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index e579d54035..180a2d807f 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -84,7 +84,7 @@ public function data_provider_test_filter_tag_visitors(): array { } /** - * Test image_prioritizer_register_tag_visitors(). + * Test end-to-end. * * @covers ::image_prioritizer_register_tag_visitors * @covers Image_Prioritizer_Tag_Visitor @@ -97,7 +97,7 @@ public function data_provider_test_filter_tag_visitors(): array { * @param callable|string $buffer Content before. * @param callable|string $expected Expected content after. */ - public function test_image_prioritizer_register_tag_visitors( callable $set_up, $buffer, $expected ): void { + public function test_end_to_end( callable $set_up, $buffer, $expected ): void { $set_up( $this, $this::factory() ); $buffer = is_string( $buffer ) ? $buffer : $buffer(); @@ -219,7 +219,7 @@ public function data_provider_test_auto_sizes(): array { * @dataProvider data_provider_test_auto_sizes * @phpstan-param array{ xpath: string, isLCP: bool, intersectionRatio: int } $element_metrics */ - public function test_auto_sizes( array $element_metrics, string $buffer, string $expected ): void { + public function test_auto_sizes_end_to_end( array $element_metrics, string $buffer, string $expected ): void { $this->populate_url_metrics( array( $element_metrics ) ); $html_start_doc = '...'; @@ -236,30 +236,264 @@ public function test_auto_sizes( array $element_metrics, string $buffer, string ); } + /** + * Test image_prioritizer_register_tag_visitors. + * + * @covers ::image_prioritizer_register_tag_visitors + */ + public function test_image_prioritizer_register_tag_visitors(): void { + $registry = new OD_Tag_Visitor_Registry(); + image_prioritizer_register_tag_visitors( $registry ); + $this->assertTrue( $registry->is_registered( 'image-prioritizer/img' ) ); + $this->assertTrue( $registry->is_registered( 'image-prioritizer/background-image' ) ); + $this->assertTrue( $registry->is_registered( 'image-prioritizer/video' ) ); + } + + /** + * Test image_prioritizer_filter_extension_module_urls. + * + * @covers ::image_prioritizer_filter_extension_module_urls + */ + public function test_image_prioritizer_filter_extension_module_urls(): void { + $initial_modules = array( + home_url( '/module.js' ), + ); + $filtered_modules = image_prioritizer_filter_extension_module_urls( $initial_modules ); + $this->assertCount( 2, $filtered_modules ); + $this->assertSame( $initial_modules[0], $filtered_modules[0] ); + $this->assertStringContainsString( 'detect.', $filtered_modules[1] ); + } + + /** + * Test image_prioritizer_add_element_item_schema_properties. + * + * @covers ::image_prioritizer_add_element_item_schema_properties + */ + public function test_image_prioritizer_add_element_item_schema_properties(): void { + $initial_schema = array( + 'foo' => array( + 'type' => 'string', + ), + ); + $filtered_schema = image_prioritizer_add_element_item_schema_properties( $initial_schema ); + $this->assertCount( 2, $filtered_schema ); + $this->assertArrayHasKey( 'foo', $filtered_schema ); + $this->assertArrayHasKey( 'lcpElementExternalBackgroundImage', $filtered_schema ); + $this->assertSame( 'object', $filtered_schema['lcpElementExternalBackgroundImage']['type'] ); + $this->assertSameSets( array( 'url', 'id', 'tag', 'class' ), array_keys( $filtered_schema['lcpElementExternalBackgroundImage']['properties'] ) ); + } + + /** + * @return array + */ + public function data_provider_for_test_image_prioritizer_add_element_item_schema_properties_inputs(): array { + return array( + 'bad_type' => array( + 'input_value' => 'not_an_object', + 'expected_exception' => 'OD_URL_Metric[lcpElementExternalBackgroundImage] is not of type object.', + 'output_value' => null, + ), + 'missing_props' => array( + 'input_value' => array(), + 'expected_exception' => 'url is a required property of OD_URL_Metric[lcpElementExternalBackgroundImage].', + 'output_value' => null, + ), + 'bad_url_protocol' => array( + 'input_value' => array( + 'url' => 'javascript:alert(1)', + 'tag' => 'DIV', + 'id' => null, + 'class' => null, + ), + 'expected_exception' => 'OD_URL_Metric[lcpElementExternalBackgroundImage][url] does not match pattern ^https?://.', + 'output_value' => null, + ), + 'bad_url_format' => array( + 'input_value' => array( + 'url' => 'https://not a valid URL!!!', + 'tag' => 'DIV', + 'id' => null, + 'class' => null, + ), + 'expected_exception' => null, + 'output_value' => array( + 'url' => 'https://not%20a%20valid%20URL!!!', // This is due to sanitize_url() being used in core. More validation is needed. + 'tag' => 'DIV', + 'id' => null, + 'class' => null, + ), + ), + 'bad_url_length' => array( + 'input_value' => array( + 'url' => 'https://example.com/' . str_repeat( 'a', 501 ), + 'tag' => 'DIV', + 'id' => null, + 'class' => null, + ), + 'expected_exception' => 'OD_URL_Metric[lcpElementExternalBackgroundImage][url] must be at most 500 characters long.', + 'output_value' => null, + ), + 'bad_null_tag' => array( + 'input_value' => array( + 'url' => 'https://example.com/', + 'tag' => null, + 'id' => null, + 'class' => null, + ), + 'expected_exception' => 'OD_URL_Metric[lcpElementExternalBackgroundImage][tag] is not of type string.', + 'output_value' => null, + ), + 'bad_format_tag' => array( + 'input_value' => array( + 'url' => 'https://example.com/', + 'tag' => 'bad tag name!!', + 'id' => null, + 'class' => null, + ), + 'expected_exception' => 'OD_URL_Metric[lcpElementExternalBackgroundImage][tag] does not match pattern ^[a-zA-Z0-9\-]+\z.', + 'output_value' => null, + ), + 'bad_length_tag' => array( + 'input_value' => array( + 'url' => 'https://example.com/', + 'tag' => str_repeat( 'a', 101 ), + 'id' => null, + 'class' => null, + ), + 'expected_exception' => 'OD_URL_Metric[lcpElementExternalBackgroundImage][tag] must be at most 100 characters long.', + 'output_value' => null, + ), + 'bad_type_id' => array( + 'input_value' => array( + 'url' => 'https://example.com/', + 'tag' => 'DIV', + 'id' => array( 'bad' ), + 'class' => null, + ), + 'expected_exception' => 'OD_URL_Metric[lcpElementExternalBackgroundImage][id] is not of type string,null.', + 'output_value' => null, + ), + 'bad_length_id' => array( + 'input_value' => array( + 'url' => 'https://example.com/', + 'tag' => 'DIV', + 'id' => str_repeat( 'a', 101 ), + 'class' => null, + ), + 'expected_exception' => 'OD_URL_Metric[lcpElementExternalBackgroundImage][id] must be at most 100 characters long.', + 'output_value' => null, + ), + 'bad_type_class' => array( + 'input_value' => array( + 'url' => 'https://example.com/', + 'tag' => 'DIV', + 'id' => 'main', + 'class' => array( 'bad' ), + ), + 'expected_exception' => 'OD_URL_Metric[lcpElementExternalBackgroundImage][class] is not of type string,null.', + 'output_value' => null, + ), + 'bad_length_class' => array( + 'input_value' => array( + 'url' => 'https://example.com/', + 'tag' => 'DIV', + 'id' => 'main', + 'class' => str_repeat( 'a', 501 ), + ), + 'expected_exception' => 'OD_URL_Metric[lcpElementExternalBackgroundImage][class] must be at most 500 characters long.', + 'output_value' => null, + ), + 'ok_minimal' => array( + 'input_value' => array( + 'url' => 'https://example.com/bg.jpg', + 'tag' => 'DIV', + 'id' => null, + 'class' => null, + ), + 'expected_exception' => null, + 'output_value' => array( + 'url' => 'https://example.com/bg.jpg', + 'tag' => 'DIV', + 'id' => null, + 'class' => null, + ), + ), + 'ok_maximal' => array( + 'input_value' => array( + 'url' => 'https://example.com/' . str_repeat( 'a', 476 ) . '.jpg', + 'tag' => str_repeat( 'a', 100 ), + 'id' => str_repeat( 'b', 100 ), + 'class' => str_repeat( 'c', 500 ), + ), + 'expected_exception' => null, + 'output_value' => array( + 'url' => 'https://example.com/' . str_repeat( 'a', 476 ) . '.jpg', + 'tag' => str_repeat( 'a', 100 ), + 'id' => str_repeat( 'b', 100 ), + 'class' => str_repeat( 'c', 500 ), + ), + ), + ); + } + + /** + * Test image_prioritizer_add_element_item_schema_properties for various inputs. + * + * @covers ::image_prioritizer_add_element_item_schema_properties + * + * @dataProvider data_provider_for_test_image_prioritizer_add_element_item_schema_properties_inputs + * + * @param mixed $input_value Input value. + * @param string|null $expected_exception Expected exception message. + * @param array|null $output_value Output value. + */ + public function test_image_prioritizer_add_element_item_schema_properties_inputs( $input_value, ?string $expected_exception, ?array $output_value ): void { + $data = $this->get_sample_url_metric( array() )->jsonSerialize(); + $data['lcpElementExternalBackgroundImage'] = $input_value; + $exception_message = null; + try { + $url_metric = new OD_URL_Metric( $data ); + } catch ( OD_Data_Validation_Exception $e ) { + $exception_message = $e->getMessage(); + } + + $this->assertSame( + $expected_exception, + $exception_message, + isset( $url_metric ) ? 'Data: ' . wp_json_encode( $url_metric->jsonSerialize(), JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ) : '' + ); + if ( isset( $url_metric ) ) { + $this->assertSame( $output_value, $url_metric->jsonSerialize()['lcpElementExternalBackgroundImage'] ); + } + } + /** * Test image_prioritizer_get_video_lazy_load_script. * * @covers ::image_prioritizer_get_video_lazy_load_script + * @covers ::image_prioritizer_get_asset_path */ public function test_image_prioritizer_get_video_lazy_load_script(): void { - $this->assertGreaterThan( 0, strlen( image_prioritizer_get_video_lazy_load_script() ) ); + $this->assertStringContainsString( 'new IntersectionObserver', image_prioritizer_get_video_lazy_load_script() ); } /** * Test image_prioritizer_get_lazy_load_bg_image_script. * * @covers ::image_prioritizer_get_lazy_load_bg_image_script + * @covers ::image_prioritizer_get_asset_path */ public function test_image_prioritizer_get_lazy_load_bg_image_script(): void { - $this->assertGreaterThan( 0, strlen( image_prioritizer_get_lazy_load_bg_image_script() ) ); + $this->assertStringContainsString( 'new IntersectionObserver', image_prioritizer_get_lazy_load_bg_image_script() ); } /** * Test image_prioritizer_get_lazy_load_bg_image_stylesheet. * * @covers ::image_prioritizer_get_lazy_load_bg_image_stylesheet + * @covers ::image_prioritizer_get_asset_path */ public function test_image_prioritizer_get_lazy_load_bg_image_stylesheet(): void { - $this->assertGreaterThan( 0, strlen( image_prioritizer_get_lazy_load_bg_image_stylesheet() ) ); + $this->assertStringContainsString( '.od-lazy-bg-image', image_prioritizer_get_lazy_load_bg_image_stylesheet() ); } } From 02c6673be016b59b1fca7f0f3cd89a5f552ebc8c Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 11 Dec 2024 11:29:15 -0800 Subject: [PATCH 143/237] Add test cases for external background images --- ...ge-complete-samples-but-element-absent.php | 74 +++++++++++++ ...n-document-and-fully-populated-samples.php | 77 +++++++++++++ ...cument-and-partially-populated-samples.php | 101 ++++++++++++++++++ ...ss-optimization-detective-test-helpers.php | 7 +- 4 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 plugins/image-prioritizer/tests/test-cases/lcp-element-external-background-image-complete-samples-but-element-absent.php create mode 100644 plugins/image-prioritizer/tests/test-cases/lcp-element-external-background-image-present-in-document-and-fully-populated-samples.php create mode 100644 plugins/image-prioritizer/tests/test-cases/lcp-element-external-background-image-present-in-document-and-partially-populated-samples.php diff --git a/plugins/image-prioritizer/tests/test-cases/lcp-element-external-background-image-complete-samples-but-element-absent.php b/plugins/image-prioritizer/tests/test-cases/lcp-element-external-background-image-complete-samples-but-element-absent.php new file mode 100644 index 0000000000..f4047e739a --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/lcp-element-external-background-image-complete-samples-but-element-absent.php @@ -0,0 +1,74 @@ + static function ( Test_Image_Prioritizer_Helper $test_case ): void { + add_filter( + 'od_breakpoint_max_widths', + static function () { + return array( 480, 600, 782 ); + } + ); + + $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); + $sample_size = od_get_url_metrics_breakpoint_sample_size(); + + $bg_images = array( + 'https://example.com/mobile.jpg', + 'https://example.com/tablet.jpg', + 'https://example.com/phablet.jpg', + 'https://example.com/desktop.jpg', + ); + + // Fully populate all viewport groups, but for all except desktop record that the LCP element had a different tag, id, or class. + foreach ( array_merge( od_get_breakpoint_max_widths(), array( 1000 ) ) as $i => $viewport_width ) { + for ( $j = 0; $j < $sample_size; $j++ ) { + OD_URL_Metrics_Post_Type::store_url_metric( + $slug, + $test_case->get_sample_url_metric( + array( + 'viewport_width' => $viewport_width, + 'elements' => array(), + 'extended_root' => array( + 'lcpElementExternalBackgroundImage' => array( + 'url' => $bg_images[ $i ], + 'tag' => 0 === $i ? 'DIV' : 'HEADER', + 'id' => 1 === $i ? 'foo' : 'masthead', + 'class' => 2 === $i ? 'bar' : 'banner', + ), + ), + ) + ) + ); + } + } + }, + 'buffer' => ' + + + + ... + + + + + + + ', + 'expected' => ' + + + + ... + + + + + + + + ', +); diff --git a/plugins/image-prioritizer/tests/test-cases/lcp-element-external-background-image-present-in-document-and-fully-populated-samples.php b/plugins/image-prioritizer/tests/test-cases/lcp-element-external-background-image-present-in-document-and-fully-populated-samples.php new file mode 100644 index 0000000000..d73c475ec3 --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/lcp-element-external-background-image-present-in-document-and-fully-populated-samples.php @@ -0,0 +1,77 @@ + static function ( Test_Image_Prioritizer_Helper $test_case ): void { + add_filter( + 'od_breakpoint_max_widths', + static function () { + return array( 480, 600, 782 ); + } + ); + + $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); + $sample_size = od_get_url_metrics_breakpoint_sample_size(); + + $bg_images = array( + 'https://example.com/mobile.jpg', + 'https://example.com/tablet.jpg', + 'https://example.com/phablet.jpg', + 'https://example.com/desktop.jpg', + ); + + // Fully populate all viewport groups. + foreach ( array_merge( od_get_breakpoint_max_widths(), array( 1000 ) ) as $i => $viewport_width ) { + for ( $j = 0; $j < $sample_size; $j++ ) { + OD_URL_Metrics_Post_Type::store_url_metric( + $slug, + $test_case->get_sample_url_metric( + array( + 'viewport_width' => $viewport_width, + 'elements' => array(), + 'extended_root' => array( + 'lcpElementExternalBackgroundImage' => array( + 'url' => $bg_images[ $i ], + 'tag' => 'HEADER', + 'id' => 'masthead', + 'class' => 'banner', + ), + ), + ) + ) + ); + } + } + }, + 'buffer' => ' + + + + ... + + + + + + + ', + 'expected' => ' + + + + ... + + + + + + + + + + + ', +); diff --git a/plugins/image-prioritizer/tests/test-cases/lcp-element-external-background-image-present-in-document-and-partially-populated-samples.php b/plugins/image-prioritizer/tests/test-cases/lcp-element-external-background-image-present-in-document-and-partially-populated-samples.php new file mode 100644 index 0000000000..c967d5e9c9 --- /dev/null +++ b/plugins/image-prioritizer/tests/test-cases/lcp-element-external-background-image-present-in-document-and-partially-populated-samples.php @@ -0,0 +1,101 @@ + static function ( Test_Image_Prioritizer_Helper $test_case ): void { + add_filter( + 'od_breakpoint_max_widths', + static function () { + return array( 480, 600, 782 ); + } + ); + + $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); + $sample_size = od_get_url_metrics_breakpoint_sample_size(); + + $bg_images = array( + 'https://example.com/mobile.jpg', + 'https://example.com/tablet.jpg', + 'https://example.com/phablet.jpg', + 'https://example.com/desktop.jpg', + ); + + $viewport_sample_sizes = array( + $sample_size, + $sample_size - 1, + 0, + $sample_size, + ); + + // Partially populate all viewport groups. + foreach ( array_merge( od_get_breakpoint_max_widths(), array( 1000 ) ) as $i => $viewport_width ) { + for ( $j = 0; $j < $viewport_sample_sizes[ $i ]; $j++ ) { + OD_URL_Metrics_Post_Type::store_url_metric( + $slug, + $test_case->get_sample_url_metric( + array( + 'viewport_width' => $viewport_width, + 'elements' => array(), + 'extended_root' => array( + 'lcpElementExternalBackgroundImage' => array( + 'url' => $bg_images[ $i ], + 'tag' => 'HEADER', + 'id' => 'masthead', + 'class' => 'banner', + ), + ), + ) + ) + ); + } + } + + // Store one more URL metric for desktop which has a different background image. + OD_URL_Metrics_Post_Type::store_url_metric( + $slug, + $test_case->get_sample_url_metric( + array( + 'viewport_width' => 1000, + 'elements' => array(), + 'extended_root' => array( + 'lcpElementExternalBackgroundImage' => array( + 'url' => 'https://example.com/desktop-alt.jpg', + 'tag' => 'HEADER', + 'id' => 'masthead', + 'class' => 'banner', + ), + ), + ) + ) + ); + }, + 'buffer' => ' + + + + ... + + + + + + + ', + 'expected' => ' + + + + ... + + + + + + + + + ', +); diff --git a/tests/class-optimization-detective-test-helpers.php b/tests/class-optimization-detective-test-helpers.php index c7924480a0..0ddda0adb5 100644 --- a/tests/class-optimization-detective-test-helpers.php +++ b/tests/class-optimization-detective-test-helpers.php @@ -86,6 +86,7 @@ public function get_sample_url_metric( array $params ): OD_URL_Metric { 'viewport_width' => 480, 'elements' => array(), 'timestamp' => microtime( true ), + 'extended_root' => array(), ), $params ); @@ -94,7 +95,7 @@ public function get_sample_url_metric( array $params ): OD_URL_Metric { $params['elements'][] = $params['element']; } - return new OD_URL_Metric( + $data = array_merge( array( 'etag' => $params['etag'], 'url' => $params['url'], @@ -118,8 +119,10 @@ function ( array $element ): array { }, $params['elements'] ), - ) + ), + $params['extended_root'] ); + return new OD_URL_Metric( $data ); } /** From 9de74b35a55974c1b82721c08c845836eb63615d Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 11 Dec 2024 12:55:29 -0800 Subject: [PATCH 144/237] Add clear_cache() method to OD_URL_Metric_Group --- .../class-od-url-metric-group.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/class-od-url-metric-group.php b/plugins/optimization-detective/class-od-url-metric-group.php index b32f4b8b69..29496a1bbd 100644 --- a/plugins/optimization-detective/class-od-url-metric-group.php +++ b/plugins/optimization-detective/class-od-url-metric-group.php @@ -237,8 +237,7 @@ public function add_url_metric( OD_URL_Metric $url_metric ): void { ); } - $this->result_cache = array(); - $this->collection->clear_cache(); + $this->clear_cache(); $url_metric->set_group( $this ); $this->url_metrics[] = $url_metric; @@ -471,6 +470,16 @@ public function count(): int { return count( $this->url_metrics ); } + /** + * Clear result cache. + * + * @since n.e.x.t + */ + public function clear_cache(): void { + $this->result_cache = array(); + $this->collection->clear_cache(); + } + /** * Specifies data which should be serialized to JSON. * From d3a60d0ab386fde7724f4f7e7de0fa6f102c6b6b Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Wed, 11 Dec 2024 18:55:12 -0600 Subject: [PATCH 145/237] Improve cover block left/right alignment --- .../includes/improve-calculate-sizes.php | 31 +++++++++---------- .../tests/test-improve-calculate-sizes.php | 4 +-- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 813ac4de4f..8394924296 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -82,13 +82,20 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b */ $filter = static function ( $sizes, $size ) use ( $block ) { - $block_name = $block->name; $id = $block->attributes['id'] ?? 0; $alignment = $block->attributes['align'] ?? ''; $width = $block->attributes['width'] ?? ''; $max_alignment = $block->context['max_alignment']; - $better_sizes = auto_sizes_calculate_better_sizes( (string) $block_name, (int) $id, (string) $size, (string) $alignment, (string) $width, (string) $max_alignment ); + /* + * Update width for cover block. + * See https://github.com/WordPress/gutenberg/blob/938720602082dc50a1746bd2e33faa3d3a6096d4/packages/block-library/src/cover/style.scss#L82-L87. + */ + if ( 'core/cover' === $block->name && in_array( $alignment, array( 'left', 'right' ), true ) ) { + $size = array( 420, 420 ); + } + + $better_sizes = auto_sizes_calculate_better_sizes( (int) $id, $size, (string) $alignment, (string) $width, (string) $max_alignment ); // If better sizes can't be calculated, use the default sizes. return false !== $better_sizes ? $better_sizes : $sizes; @@ -125,15 +132,14 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b * * @since n.e.x.t * - * @param string $block_name The block name. - * @param int $id The image id. - * @param string $size The image size data. - * @param string $align The image alignment. - * @param string $resize_width Resize image width. - * @param string $max_alignment The maximum usable layout alignment. + * @param int $id The image id. + * @param string|int[] $size The image size data. + * @param string $align The image alignment. + * @param string $resize_width Resize image width. + * @param string $max_alignment The maximum usable layout alignment. * @return string|false An improved sizes attribute or false if a better size cannot be calculated. */ -function auto_sizes_calculate_better_sizes( string $block_name, int $id, string $size, string $align, string $resize_width, string $max_alignment ) { +function auto_sizes_calculate_better_sizes( int $id, $size, string $align, string $resize_width, string $max_alignment ) { // Without an image ID or a resize width, we cannot calculate a better size. if ( ! (bool) $id && ! (bool) $resize_width ) { return false; @@ -189,13 +195,6 @@ function auto_sizes_calculate_better_sizes( string $block_name, int $id, string case 'left': case 'right': - /* - * Update width for cover block. - * See https://github.com/WordPress/gutenberg/blob/938720602082dc50a1746bd2e33faa3d3a6096d4/packages/block-library/src/cover/style.scss#L82-L87. - */ - if ( 'core/cover' === $block_name ) { - $image_width = $image_width * 0.5; - } $layout_width = sprintf( '%1$spx', $image_width ); break; diff --git a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php index 58a692edd5..1ef56fd2c6 100644 --- a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -368,8 +368,8 @@ public function test_cover_block_with_left_right_center_alignment( string $align */ public function data_image_left_right_center_alignment(): array { return array( - array( 'left', 'sizes="(max-width: 540px) 100vw, 540px' ), - array( 'right', 'sizes="(max-width: 540px) 100vw, 540px' ), + array( 'left', 'sizes="(max-width: 420px) 100vw, 420px' ), + array( 'right', 'sizes="(max-width: 420px) 100vw, 420px' ), array( 'center', 'sizes="(max-width: 620px) 100vw, 620px' ), ); } From c58a3556e7dc0f8dfe5c237432263b80c4ecb546 Mon Sep 17 00:00:00 2001 From: Paul Bearne Date: Thu, 12 Dec 2024 08:20:27 -0500 Subject: [PATCH 146/237] Update plugins/dominant-color-images/hooks.php Co-authored-by: Weston Ruter --- plugins/dominant-color-images/hooks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index d31bc6af01..3b9520afeb 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -257,7 +257,7 @@ function dominant_color_prepare_attachment_for_js( $response, WP_Post $attachmen if ( isset( $meta['dominant_color'] ) && str_starts_with( '#', $meta['dominant_color'] ) ) { $response['dominantColor'] = sanitize_hex_color( $meta['dominant_color'] ); } - $response['hasTransparency'] = ''; + $response['hasTransparency'] = ''; if ( isset( $meta['has_transparency'] ) ) { $response['hasTransparency'] = (bool) $meta['has_transparency']; } From 7b8312790f9b7fe44e570370bfb96b37c3e84b6c Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Thu, 12 Dec 2024 21:14:22 +0530 Subject: [PATCH 147/237] Refactor plugins screen message to use `wp_kses` Co-authored-by: Weston Ruter --- .../includes/admin/plugins.php | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index 377f984ba3..bbfc41a32a 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -293,20 +293,23 @@ static function ( string $error_message ): string {

'WordPress Performance Team', - 'plugin_status' => 'all', - ), - admin_url( 'plugins.php' ) + $plugins_url = add_query_arg( + array( + 's' => 'WordPress Performance Team', + 'plugin_status' => 'all', + ), + admin_url( 'plugins.php' ) + ); + echo wp_kses( + sprintf( + /* translators: %s is the URL to the plugins screen */ + __( 'Performance features are installed as plugins. To update features or remove them, manage them on the plugins screen.', 'performance-lab' ), + esc_url( $plugins_url ) + ), + array( + 'a' => array( 'href' => true ), ) - ) . '">', - '' - ); + ); ?>

Date: Thu, 12 Dec 2024 12:50:07 -0600 Subject: [PATCH 148/237] Initial review feedback improvements Co-authored-by: Weston Ruter --- .../includes/improve-calculate-sizes.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 8394924296..af45fb8f63 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -85,7 +85,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b $id = $block->attributes['id'] ?? 0; $alignment = $block->attributes['align'] ?? ''; $width = $block->attributes['width'] ?? ''; - $max_alignment = $block->context['max_alignment']; + $max_alignment = $block->context['max_alignment'] ?? ''; /* * Update width for cover block. @@ -132,11 +132,11 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b * * @since n.e.x.t * - * @param int $id The image id. - * @param string|int[] $size The image size data. - * @param string $align The image alignment. - * @param string $resize_width Resize image width. - * @param string $max_alignment The maximum usable layout alignment. + * @param int $id The image attachment post ID. + * @param string|array{int, int} $size Image size name or array of width and height. + * @param string $align The image alignment. + * @param string $resize_width Resize image width. + * @param string $max_alignment The maximum usable layout alignment. * @return string|false An improved sizes attribute or false if a better size cannot be calculated. */ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, string $resize_width, string $max_alignment ) { @@ -166,7 +166,7 @@ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, strin } // Normalize default alignment values. - $align = (bool) $align ? $align : 'default'; + $align = '' !== $align ? $align : 'default'; /* * Map alignment values to a weighting value so they can be compared. @@ -238,9 +238,9 @@ function auto_sizes_get_layout_width( string $alignment ): string { * * @since n.e.x.t * - * @param array $uses_context Array of registered uses context for a block type. + * @param string[] $uses_context Array of registered uses context for a block type. * @param WP_Block_Type $block_type The full block type object. - * @return array The filtered context keys used by the block type. + * @return string[] The filtered context keys used by the block type. */ function auto_sizes_filter_uses_context( array $uses_context, WP_Block_Type $block_type ): array { // The list of blocks that can consume outer layout context. From aecb3de9431ebba81eb9750e8e2dbfa6acb1ec78 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Thu, 12 Dec 2024 13:00:56 -0600 Subject: [PATCH 149/237] Force resize width to be an int --- .../includes/improve-calculate-sizes.php | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index af45fb8f63..9b8459571f 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -82,9 +82,9 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b */ $filter = static function ( $sizes, $size ) use ( $block ) { - $id = $block->attributes['id'] ?? 0; + $id = isset( $block->attributes['id'] ) ? (int) $block->attributes['id'] : 0; $alignment = $block->attributes['align'] ?? ''; - $width = $block->attributes['width'] ?? ''; + $width = isset( $block->attributes['width'] ) ? (int) $block->attributes['width'] : 0; $max_alignment = $block->context['max_alignment'] ?? ''; /* @@ -95,7 +95,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b $size = array( 420, 420 ); } - $better_sizes = auto_sizes_calculate_better_sizes( (int) $id, $size, (string) $alignment, (string) $width, (string) $max_alignment ); + $better_sizes = auto_sizes_calculate_better_sizes( $id, $size, $alignment, $width, $max_alignment ); // If better sizes can't be calculated, use the default sizes. return false !== $better_sizes ? $better_sizes : $sizes; @@ -135,23 +135,22 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b * @param int $id The image attachment post ID. * @param string|array{int, int} $size Image size name or array of width and height. * @param string $align The image alignment. - * @param string $resize_width Resize image width. + * @param int $resize_width Resize image width. * @param string $max_alignment The maximum usable layout alignment. * @return string|false An improved sizes attribute or false if a better size cannot be calculated. */ -function auto_sizes_calculate_better_sizes( int $id, $size, string $align, string $resize_width, string $max_alignment ) { +function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $resize_width, string $max_alignment ) { // Without an image ID or a resize width, we cannot calculate a better size. - if ( ! (bool) $id && ! (bool) $resize_width ) { + if ( 0 === $id && 0 === $resize_width ) { return false; } $image_data = wp_get_attachment_image_src( $id, $size ); - $resize_width = (int) $resize_width; - $image_width = false !== $image_data ? $image_data[1] : 0; + $image_width = false !== $image_data ? $image_data[1] : 0; // If we don't have an image width or a resize width, we cannot calculate a better size. - if ( ! ( (bool) $image_width || (bool) $resize_width ) ) { + if ( 0 === $image_width && 0 === $resize_width ) { return false; } @@ -159,9 +158,9 @@ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, strin * If we don't have an image width, use the resize width. * If we have both an image width and a resize width, use the smaller of the two. */ - if ( ! (bool) $image_width ) { + if ( 0 === $image_width ) { $image_width = $resize_width; - } elseif ( (bool) $resize_width ) { + } elseif ( 0 !== $resize_width ) { $image_width = min( $image_width, $resize_width ); } From 73e451969f3bc0658b77088fb5ba0f443b99f378 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Fri, 13 Dec 2024 01:00:13 +0530 Subject: [PATCH 150/237] Refine `$current_template` assignment logic for block and classic themes Co-authored-by: Weston Ruter --- plugins/optimization-detective/optimization.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/optimization-detective/optimization.php b/plugins/optimization-detective/optimization.php index c0f9d41983..39cbe1462e 100644 --- a/plugins/optimization-detective/optimization.php +++ b/plugins/optimization-detective/optimization.php @@ -210,10 +210,11 @@ function od_optimize_template_output_buffer( string $buffer ): string { */ do_action( 'od_register_tag_visitors', $tag_visitor_registry ); - if ( wp_is_block_theme() ) { + $current_template = null; + if ( wp_is_block_theme() && isset( $GLOBALS['_wp_current_template_id'] ) ) { $current_template = get_block_template( $GLOBALS['_wp_current_template_id'], 'wp_template' ); - $current_template = $current_template ?? basename( $GLOBALS['template'] ); - } else { + } + if ( null === $current_template && isset( $GLOBALS['template'] ) && is_string( $GLOBALS['template'] ) ) { $current_template = basename( $GLOBALS['template'] ); } From 59879f0a36d2df2a4b05dc9b16f5ede3d9f53d7f Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Fri, 13 Dec 2024 01:08:19 +0530 Subject: [PATCH 151/237] Update `$current_template` type for static analysis compliance Co-authored-by: Weston Ruter --- plugins/optimization-detective/storage/data.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index dd70995ca1..5bf192c075 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -150,9 +150,9 @@ function od_get_url_metrics_slug( array $query_vars ): string { * @since n.e.x.t * @access private * - * @param OD_Tag_Visitor_Registry $tag_visitor_registry Tag visitor registry. - * @param WP_Query $wp_query The WP_Query instance. - * @param string|WP_Block_Template $current_template The current template being used. + * @param OD_Tag_Visitor_Registry $tag_visitor_registry Tag visitor registry. + * @param WP_Query $wp_query The WP_Query instance. + * @param string|WP_Block_Template|null $current_template The current template being used. * @return non-empty-string Current ETag. */ function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_registry, WP_Query $wp_query, $current_template ): string { From df306e1f3f2bad8236babbf4b004541f0ba587e4 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Fri, 13 Dec 2024 01:08:56 +0530 Subject: [PATCH 152/237] Fix indentation Signed-off-by: Shyamsundar Gadde --- plugins/optimization-detective/storage/data.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 5bf192c075..7ad5b918e7 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -150,8 +150,8 @@ function od_get_url_metrics_slug( array $query_vars ): string { * @since n.e.x.t * @access private * - * @param OD_Tag_Visitor_Registry $tag_visitor_registry Tag visitor registry. - * @param WP_Query $wp_query The WP_Query instance. + * @param OD_Tag_Visitor_Registry $tag_visitor_registry Tag visitor registry. + * @param WP_Query $wp_query The WP_Query instance. * @param string|WP_Block_Template|null $current_template The current template being used. * @return non-empty-string Current ETag. */ From 7fb1962b6a28ddfcb65321ff7442331f1b230897 Mon Sep 17 00:00:00 2001 From: Joe McGill <801097+joemcgill@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:58:49 -0600 Subject: [PATCH 153/237] Additional type casting fixes Co-authored-by: Weston Ruter --- plugins/auto-sizes/includes/improve-calculate-sizes.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 9b8459571f..40b50a0e46 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -278,9 +278,9 @@ function auto_sizes_filter_render_block_context( array $context, array $block ): $alignment = $block['attrs']['align'] ?? ''; // If the container block doesn't have alignment, it's assumed to be 'default'. - if ( ! (bool) $alignment ) { + if ( '' === $alignment ) { $context['max_alignment'] = 'default'; - } elseif ( 'wide' === $block['attrs']['align'] ) { + } elseif ( 'wide' === $alignment ) { $context['max_alignment'] = 'wide'; } } From c9c0dd8726cda88110f957854646e6717550aacc Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Fri, 13 Dec 2024 01:33:09 +0530 Subject: [PATCH 154/237] Move global setup to set_up and cleanup to tear_down Signed-off-by: Shyamsundar Gadde --- .../tests/test-optimization-detective.php | 12 +++++++++--- plugins/image-prioritizer/tests/test-helper.php | 15 +++++++++------ .../tests/test-optimization.php | 7 +++---- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/plugins/embed-optimizer/tests/test-optimization-detective.php b/plugins/embed-optimizer/tests/test-optimization-detective.php index 91b972b050..61ac7e2b4e 100644 --- a/plugins/embed-optimizer/tests/test-optimization-detective.php +++ b/plugins/embed-optimizer/tests/test-optimization-detective.php @@ -18,6 +18,7 @@ public function set_up(): void { if ( ! defined( 'OPTIMIZATION_DETECTIVE_VERSION' ) ) { $this->markTestSkipped( 'Optimization Detective is not active.' ); } + $GLOBALS['template'] = '/path/to/theme/index.php'; // Normalize the data for computing the current URL Metrics ETag to work around the issue where there is no // global variable storing the OD_Tag_Visitor_Registry instance along with any registered tag visitors, so @@ -26,6 +27,14 @@ public function set_up(): void { add_filter( 'od_current_url_metrics_etag_data', '__return_empty_array' ); } + /** + * Runs the routine after each test is executed. + */ + public function tear_down(): void { + unset( $GLOBALS['template'] ); + parent::tear_down(); + } + /** * Tests embed_optimizer_register_tag_visitors(). * @@ -101,7 +110,6 @@ public function data_provider_test_od_optimize_template_output_buffer(): array { * @dataProvider data_provider_test_od_optimize_template_output_buffer */ public function test_od_optimize_template_output_buffer( Closure $set_up, string $buffer, string $expected ): void { - $GLOBALS['template'] = '/path/to/theme/index.php'; $set_up( $this ); $buffer = od_optimize_template_output_buffer( $buffer ); @@ -123,7 +131,5 @@ static function ( $matches ) { $this->remove_initial_tabs( $buffer ), "Buffer snapshot:\n$buffer" ); - - unset( $GLOBALS['template'] ); } } diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index abed1fea35..3bd02d68d2 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -15,6 +15,7 @@ class Test_Image_Prioritizer_Helper extends WP_UnitTestCase { */ public function set_up(): void { parent::set_up(); + $GLOBALS['template'] = '/path/to/theme/index.php'; // Normalize the data for computing the current URL Metrics ETag to work around the issue where there is no // global variable storing the OD_Tag_Visitor_Registry instance along with any registered tag visitors, so @@ -23,6 +24,14 @@ public function set_up(): void { add_filter( 'od_current_url_metrics_etag_data', '__return_empty_array' ); } + /** + * Runs the routine after each test is executed. + */ + public function tear_down(): void { + unset( $GLOBALS['template'] ); + parent::tear_down(); + } + /** * @return array> */ @@ -98,7 +107,6 @@ public function data_provider_test_filter_tag_visitors(): array { * @param callable|string $expected Expected content after. */ public function test_end_to_end( callable $set_up, $buffer, $expected ): void { - $GLOBALS['template'] = '/path/to/theme/index.php'; $set_up( $this, $this::factory() ); $buffer = is_string( $buffer ) ? $buffer : $buffer(); @@ -126,8 +134,6 @@ static function ( $matches ) { $this->remove_initial_tabs( $buffer ), "Buffer snapshot:\n$buffer" ); - - unset( $GLOBALS['template'] ); } /** @@ -223,7 +229,6 @@ public function data_provider_test_auto_sizes(): array { * @phpstan-param array{ xpath: string, isLCP: bool, intersectionRatio: int } $element_metrics */ public function test_auto_sizes_end_to_end( array $element_metrics, string $buffer, string $expected ): void { - $GLOBALS['template'] = '/path/to/theme/index.php'; $this->populate_url_metrics( array( $element_metrics ) ); $html_start_doc = '...'; @@ -238,8 +243,6 @@ public function test_auto_sizes_end_to_end( array $element_metrics, string $buff $this->remove_initial_tabs( $buffer ), "Buffer snapshot:\n$buffer" ); - - unset( $GLOBALS['template'] ); } /** diff --git a/plugins/optimization-detective/tests/test-optimization.php b/plugins/optimization-detective/tests/test-optimization.php index 98690d88c9..8eb3030473 100644 --- a/plugins/optimization-detective/tests/test-optimization.php +++ b/plugins/optimization-detective/tests/test-optimization.php @@ -27,10 +27,11 @@ class Test_OD_Optimization extends WP_UnitTestCase { private $default_mimetype; public function set_up(): void { + parent::set_up(); $this->original_request_uri = $_SERVER['REQUEST_URI']; $this->original_request_method = $_SERVER['REQUEST_METHOD']; $this->default_mimetype = (string) ini_get( 'default_mimetype' ); - parent::set_up(); + $GLOBALS['template'] = '/path/to/theme/index.php'; } public function tear_down(): void { @@ -38,6 +39,7 @@ public function tear_down(): void { $_SERVER['REQUEST_METHOD'] = $this->original_request_method; ini_set( 'default_mimetype', $this->default_mimetype ); // phpcs:ignore WordPress.PHP.IniSet.Risky unset( $GLOBALS['wp_customize'] ); + unset( $GLOBALS['template'] ); parent::tear_down(); } @@ -291,7 +293,6 @@ public function data_provider_test_od_optimize_template_output_buffer(): array { * @dataProvider data_provider_test_od_optimize_template_output_buffer */ public function test_od_optimize_template_output_buffer( Closure $set_up, string $buffer, string $expected ): void { - $GLOBALS['template'] = '/path/to/theme/index.php'; $set_up( $this ); add_action( @@ -337,7 +338,5 @@ function ( OD_Tag_Visitor_Context $context ): bool { $this->remove_initial_tabs( $buffer ), "Buffer snapshot:\n$buffer" ); - - unset( $GLOBALS['template'] ); } } From 4632350d257e2f6452bbd7842f87933134db7150 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Fri, 13 Dec 2024 01:57:02 +0530 Subject: [PATCH 155/237] Replace `$GLOBALS` access with explicit global variable declarations Signed-off-by: Shyamsundar Gadde --- plugins/optimization-detective/optimization.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/optimization-detective/optimization.php b/plugins/optimization-detective/optimization.php index 39cbe1462e..8eabb24952 100644 --- a/plugins/optimization-detective/optimization.php +++ b/plugins/optimization-detective/optimization.php @@ -178,6 +178,8 @@ function od_is_response_html_content_type(): bool { * @return string Filtered template output buffer. */ function od_optimize_template_output_buffer( string $buffer ): string { + global $wp_the_query, $_wp_current_template_id, $template; + // 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() || @@ -211,14 +213,14 @@ function od_optimize_template_output_buffer( string $buffer ): string { do_action( 'od_register_tag_visitors', $tag_visitor_registry ); $current_template = null; - if ( wp_is_block_theme() && isset( $GLOBALS['_wp_current_template_id'] ) ) { - $current_template = get_block_template( $GLOBALS['_wp_current_template_id'], 'wp_template' ); + if ( wp_is_block_theme() && $_wp_current_template_id ) { + $current_template = get_block_template( $_wp_current_template_id, 'wp_template' ); } - if ( null === $current_template && isset( $GLOBALS['template'] ) && is_string( $GLOBALS['template'] ) ) { - $current_template = basename( $GLOBALS['template'] ); + if ( null === $current_template && $template && is_string( $template ) ) { + $current_template = basename( $template ); } - $current_etag = od_get_current_url_metrics_etag( $tag_visitor_registry, $GLOBALS['wp_the_query'], $current_template ); + $current_etag = od_get_current_url_metrics_etag( $tag_visitor_registry, $wp_the_query, $current_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, From ad1d9eab562454fe7ff97a8bcdd4f8079a3160a0 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 12 Dec 2024 13:28:09 -0800 Subject: [PATCH 156/237] Add ability to unset an extended property on OD_URL_Metric and OD_Element --- .../class-od-element.php | 45 ++++++++-- .../class-od-url-metric.php | 38 ++++++++- .../tests/test-class-od-element.php | 73 +++++++++++++--- .../tests/test-class-od-url-metric.php | 25 ++++++ ...-class-od-url-metrics-group-collection.php | 83 +++++++++++++++++++ 5 files changed, 245 insertions(+), 19 deletions(-) diff --git a/plugins/optimization-detective/class-od-element.php b/plugins/optimization-detective/class-od-element.php index c7f243d99b..be8400ce40 100644 --- a/plugins/optimization-detective/class-od-element.php +++ b/plugins/optimization-detective/class-od-element.php @@ -208,18 +208,53 @@ public function offsetSet( $offset, $value ): void { } /** - * Offset to unset. + * Unsets a property. * - * This is disallowed. Attempting to unset a property will throw an error. + * This is particularly useful in conjunction with the `od_url_metric_schema_element_item_additional_properties` filter. + * This will throw an exception if the property is required by the schema. * - * @since 0.7.0 + * @since n.e.x.t + * + * @param string $key Property. + * + * @throws OD_Data_Validation_Exception When attempting to unset a property required by the JSON Schema. + */ + public function unset( string $key ): void { + $schema = OD_URL_Metric::get_json_schema(); // TODO: This should be a non-static method once the URL Metric is instantiated. + if ( + isset( $schema['properties']['elements']['items']['properties'][ $key ]['required'] ) + && true === $schema['properties']['elements']['items']['properties'][ $key ]['required'] + ) { + throw new OD_Data_Validation_Exception( + esc_html( + sprintf( + /* translators: %s is the property key. */ + __( 'The %s key is required for an item of elements in a URL Metric.', 'optimization-detective' ), + $key + ) + ) + ); + } + unset( $this->data[ $key ] ); // @phpstan-ignore assign.propertyType (Above required check ensures $key is not isLCP, isLCPCandidate, xpath, intersectionRatio, intersectionRect, boundingClientRect.) + $group = $this->url_metric->get_group(); + if ( $group instanceof OD_URL_Metric_Group ) { + $group->clear_cache(); + } + } + + /** + * Unsets an offset. + * + * This will throw an exception if the offset is required by the schema. + * + * @since n.e.x.t * * @param mixed $offset Offset. * - * @throws Exception When attempting to unset a property. + * @throws OD_Data_Validation_Exception When attempting to unset a property required by the JSON Schema. */ public function offsetUnset( $offset ): void { - throw new Exception( 'Element data may not be unset.' ); + $this->unset( (string) $offset ); } /** diff --git a/plugins/optimization-detective/class-od-url-metric.php b/plugins/optimization-detective/class-od-url-metric.php index d3b0d984f7..b68045e6ef 100644 --- a/plugins/optimization-detective/class-od-url-metric.php +++ b/plugins/optimization-detective/class-od-url-metric.php @@ -503,6 +503,33 @@ function ( array $element ): OD_Element { return $this->elements; } + /** + * Unsets a property from the URL Metric. + * + * @since n.e.x.t + * + * @param string $key Key to unset. + * @throws OD_Data_Validation_Exception If the property is required an exception will be thrown. + */ + public function unset( string $key ): void { + $schema = self::get_json_schema(); // TODO: Consider capturing the schema as a private member variable once the URL Metric is constructed. + if ( isset( $schema['properties'][ $key ]['required'] ) && true === $schema['properties'][ $key ]['required'] ) { + throw new OD_Data_Validation_Exception( + esc_html( + sprintf( + /* translators: %s is the property key. */ + __( 'The %s key is required at the root of a URL Metric.', 'optimization-detective' ), + $key + ) + ) + ); + } + unset( $this->data[ $key ] ); // @phpstan-ignore assign.propertyType (Above required check ensures $key is not uuid, url, timestamp, viewport, or elements.) + if ( $this->group instanceof OD_URL_Metric_Group ) { + $this->group->clear_cache(); + } + } + /** * Specifies data which should be serialized to JSON. * @@ -511,6 +538,15 @@ function ( array $element ): OD_Element { * @return Data Exports to be serialized by json_encode(). */ public function jsonSerialize(): array { - return $this->data; + $data = $this->data; + + $data['elements'] = array_map( + static function ( OD_Element $element ): array { + return $element->jsonSerialize(); + }, + $this->get_elements() + ); + + return $data; } } diff --git a/plugins/optimization-detective/tests/test-class-od-element.php b/plugins/optimization-detective/tests/test-class-od-element.php index 52a4cc43ff..1b980d421a 100644 --- a/plugins/optimization-detective/tests/test-class-od-element.php +++ b/plugins/optimization-detective/tests/test-class-od-element.php @@ -24,6 +24,7 @@ class Test_OD_Element extends WP_UnitTestCase { * @covers ::offsetExists * @covers ::offsetGet * @covers ::offsetSet + * @covers ::unset * @covers ::offsetUnset * @covers ::jsonSerialize */ @@ -130,22 +131,68 @@ static function ( array $schema ): array { $this->assertTrue( isset( $element['customProp'] ) ); $this->assertTrue( $element->offsetExists( 'customProp' ) ); - $this->assertEquals( $element_data, $element->jsonSerialize() ); + // Try setting property (which is not currently supported). + $functions = array( + static function ( OD_Element $element ): void { + $element['isLCP'] = true; + }, + static function ( OD_Element $element ): void { + $element->offsetSet( 'isLCP', true ); + }, + ); + foreach ( $functions as $function ) { + $exception = null; + try { + $function( $element ); + } catch ( Exception $e ) { + $exception = $e; + } + $this->assertInstanceOf( Exception::class, $exception ); + $this->assertFalse( $element->get( 'isLCP' ) ); + } - $exception = null; - try { - $element['isLCP'] = true; - } catch ( Exception $e ) { - $exception = $e; + // Try unsetting a required property. + $functions = array( + static function ( OD_Element $element ): void { + unset( $element['isLCP'] ); + }, + static function ( OD_Element $element ): void { + $element->unset( 'isLCP' ); + }, + static function ( OD_Element $element ): void { + $element->offsetUnset( 'isLCP' ); + }, + ); + foreach ( $functions as $function ) { + $exception = null; + try { + $function( $element ); + } catch ( Exception $e ) { + $exception = $e; + } + $this->assertInstanceOf( Exception::class, $exception ); + $this->assertArrayHasKey( 'isLCP', $element->jsonSerialize() ); } - $this->assertInstanceOf( Exception::class, $exception ); - $exception = null; - try { - unset( $element['isLCP'] ); - } catch ( Exception $e ) { // @phpstan-ignore catch.neverThrown (It is thrown by offsetUnset actually.) - $exception = $e; + $this->assertEquals( $element_data, $element->jsonSerialize() ); + + // Try unsetting a non-required property. + $functions = array( + static function ( OD_Element $element ): void { + unset( $element['customProp'] ); + }, + static function ( OD_Element $element ): void { + $element->unset( 'customProp' ); + }, + static function ( OD_Element $element ): void { + $element->offsetUnset( 'customProp' ); + }, + ); + foreach ( $functions as $function ) { + $cloned_element = clone $element; + $function( $cloned_element ); + $this->assertFalse( $cloned_element->offsetExists( 'customProp' ) ); + $this->assertArrayNotHasKey( 'customProp', $cloned_element->jsonSerialize() ); } - $this->assertInstanceOf( Exception::class, $exception ); // @phpstan-ignore method.impossibleType (It is thrown by offsetUnset actually.) } } 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 51b23c243e..df2fa01442 100644 --- a/plugins/optimization-detective/tests/test-class-od-url-metric.php +++ b/plugins/optimization-detective/tests/test-class-od-url-metric.php @@ -277,6 +277,7 @@ static function ( $value ) { * @covers ::get_json_schema * @covers ::set_group * @covers ::get_group + * @covers ::unset * * @dataProvider data_provider_to_test_constructor * @@ -336,6 +337,15 @@ static function ( OD_Element $element ) { $this->assertTrue( wp_is_uuid( $url_metric->get_uuid() ) ); $this->assertSame( $url_metric->get_uuid(), $url_metric->get( 'uuid' ) ); + $exception = null; + try { + $url_metric->unset( 'elements' ); + } catch ( OD_Data_Validation_Exception $e ) { + $exception = $e; + } + $this->assertInstanceOf( OD_Data_Validation_Exception::class, $exception ); + $url_metric->unset( 'does_not_exist' ); + $serialized = $url_metric->jsonSerialize(); if ( ! array_key_exists( 'uuid', $data ) ) { $this->assertTrue( wp_is_uuid( $serialized['uuid'] ) ); @@ -397,6 +407,12 @@ static function ( array $properties ): array { $this->assertArrayHasKey( 'isTouch', $extended_data ); $this->assertTrue( $extended_data['isTouch'] ); $this->assertTrue( $extended_url_metric->get( 'isTouch' ) ); + + $this->assertTrue( $extended_url_metric->get( 'isTouch' ) ); + $extended_url_metric->unset( 'isTouch' ); + $this->assertNull( $extended_url_metric->get( 'isTouch' ) ); + $extended_data = $extended_url_metric->jsonSerialize(); + $this->assertArrayNotHasKey( 'isTouch', $extended_data ); }, ), @@ -489,6 +505,13 @@ static function ( array $properties ): array { $this->assertArrayHasKey( 'isColorful', $extended_data['elements'][0] ); $this->assertFalse( $extended_data['elements'][0]['isColorful'] ); $this->assertFalse( $extended_url_metric->get_elements()[0]['isColorful'] ); + + $element = $extended_url_metric->get_elements()[0]; + $this->assertFalse( $element->get( 'isColorful' ) ); + $element->unset( 'isColorful' ); + $this->assertNull( $element->get( 'isColorful' ) ); + $extended_data = $extended_url_metric->jsonSerialize(); + $this->assertArrayNotHasKey( 'isColorful', $extended_data['elements'][0] ); }, ), @@ -554,6 +577,8 @@ static function ( array $properties ): array { * Tests construction with extended schema. * * @covers ::get_json_schema + * @covers ::unset + * @covers OD_Element::unset * * @dataProvider data_provider_to_test_constructor_with_extended_schema * diff --git a/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php b/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php index 179d282c43..034e506615 100644 --- a/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php +++ b/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php @@ -199,6 +199,89 @@ public function data_provider_sample_size_and_breakpoints(): array { ); } + /** + * Test clear_cache(). + * + * @covers ::clear_cache + * @covers OD_URL_Metric_Group::clear_cache + * @covers OD_URL_Metric::unset + * @covers OD_Element::unset + */ + public function test_clear_cache(): void { + $collection = new OD_URL_Metric_Group_Collection( array(), md5( '' ), array(), 1, DAY_IN_SECONDS ); + $populated_value = array( 'foo' => true ); + $group = $collection->get_first_group(); + + // Get private members. + $collection_result_cache_reflection_property = new ReflectionProperty( OD_URL_Metric_Group_Collection::class, 'result_cache' ); + $collection_result_cache_reflection_property->setAccessible( true ); + $this->assertSame( array(), $collection_result_cache_reflection_property->getValue( $collection ) ); + $group_result_cache_reflection_property = new ReflectionProperty( OD_URL_Metric_Group::class, 'result_cache' ); + $group_result_cache_reflection_property->setAccessible( true ); + $this->assertSame( array(), $group_result_cache_reflection_property->getValue( $group ) ); + + // Test clear_cache() on collection. + $collection_result_cache_reflection_property->setValue( $collection, $populated_value ); + $collection->clear_cache(); + $this->assertSame( array(), $collection_result_cache_reflection_property->getValue( $collection ) ); + + // Test that adding a URL metric to a collection clears the caches. + $collection_result_cache_reflection_property->setValue( $collection, $populated_value ); + $group_result_cache_reflection_property->setValue( $group, $populated_value ); + + add_filter( + 'od_url_metric_schema_root_additional_properties', + static function ( $schema ) { + $schema['new_prop_at_root'] = array( + 'type' => 'string', + ); + return $schema; + } + ); + add_filter( + 'od_url_metric_schema_element_additional_properties', + static function ( $schema ) { + $schema['new_prop_at_element'] = array( + 'type' => 'string', + ); + return $schema; + } + ); + + $collection->add_url_metric( + $this->get_sample_url_metric( + array( + 'element' => array( + 'xpath' => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]', + 'new_prop_at_element' => 'hey there', + ), + 'extended_root' => array( + 'new_prop_at_root' => 'hola', + ), + ) + ) + ); + $url_metric = $group->getIterator()->current(); + $this->assertInstanceOf( OD_URL_Metric::class, $url_metric ); + $this->assertSame( array(), $collection_result_cache_reflection_property->getValue( $collection ) ); + $this->assertSame( array(), $group_result_cache_reflection_property->getValue( $group ) ); + + // Test that modifying a URL Metric empties the cache of the collection and the group. + $collection_result_cache_reflection_property->setValue( $collection, $populated_value ); + $group_result_cache_reflection_property->setValue( $group, $populated_value ); + $url_metric->unset( 'new_prop_at_root' ); + $this->assertSame( array(), $collection_result_cache_reflection_property->getValue( $collection ) ); + $this->assertSame( array(), $group_result_cache_reflection_property->getValue( $group ) ); + + // Test that modifying a URL Metric element empties the cache of the collection and the group. + $element = $url_metric->get_elements()[0]; + $collection_result_cache_reflection_property->setValue( $collection, $populated_value ); + $group_result_cache_reflection_property->setValue( $group, $populated_value ); + $element->unset( 'new_prop_at_element' ); + $this->assertSame( array(), $collection_result_cache_reflection_property->getValue( $collection ) ); + $this->assertSame( array(), $group_result_cache_reflection_property->getValue( $group ) ); + } + /** * Test add_url_metric(). * From 5b4e791e7b0096d63a85c1a6beb96741db59f7b6 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Fri, 13 Dec 2024 03:24:04 +0530 Subject: [PATCH 157/237] Limit queried_object fields to id, type, and last_modified Signed-off-by: Shyamsundar Gadde --- .../optimization-detective/storage/data.php | 23 ++++++++++++++++++- .../tests/storage/test-data.php | 18 ++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 7ad5b918e7..afe3dc9931 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -156,9 +156,30 @@ function od_get_url_metrics_slug( array $query_vars ): string { * @return non-empty-string Current ETag. */ function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_registry, WP_Query $wp_query, $current_template ): string { + $queried_object = $wp_query->get_queried_object(); + $queried_object_data = array( + 'id' => null, + 'type' => null, + 'last_modified' => null, + ); + + if ( $queried_object instanceof WP_Post ) { + $queried_object_data['id'] = $queried_object->ID; + $queried_object_data['type'] = 'post'; + $queried_object_data['last_modified'] = $queried_object->post_modified_gmt; + } elseif ( $queried_object instanceof WP_Term ) { + $queried_object_data['id'] = $queried_object->term_id; + $queried_object_data['type'] = 'term'; + } elseif ( $queried_object instanceof WP_User ) { + $queried_object_data['id'] = $queried_object->ID; + $queried_object_data['type'] = 'user'; + } elseif ( $wp_query->is_post_type_archive() ) { + $queried_object_data['type'] = get_query_var( 'post_type' ); + } + $data = array( 'tag_visitors' => array_keys( iterator_to_array( $tag_visitor_registry ) ), - 'queried_object' => $wp_query->get_queried_object(), + 'queried_object' => $queried_object_data, 'queried_posts' => wp_list_pluck( $wp_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( 'template' => array( diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index 68ae7703e6..d1ab4efc21 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -334,7 +334,11 @@ static function ( $data ) { $this->assertSame( array( 'tag_visitors' => array(), - 'queried_object' => null, + 'queried_object' => array( + 'id' => null, + 'type' => null, + 'last_modified' => null, + ), 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( 'template' => array( @@ -382,7 +386,11 @@ static function ( $data ) { $this->assertSame( array( 'tag_visitors' => array( 'foo', 'bar', 'baz' ), - 'queried_object' => null, + 'queried_object' => array( + 'id' => null, + 'type' => null, + 'last_modified' => null, + ), 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( 'template' => array( @@ -414,7 +422,11 @@ static function ( $data ) { $this->assertSame( array( 'tag_visitors' => array( 'foo', 'bar', 'baz' ), - 'queried_object' => null, + 'queried_object' => array( + 'id' => null, + 'type' => null, + 'last_modified' => null, + ), 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( 'template' => array( From 1073780d95c90ef54fea87711298487de0766fae Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Fri, 13 Dec 2024 03:31:35 +0530 Subject: [PATCH 158/237] Use WP_Query method instead of global function for determining post type Signed-off-by: Shyamsundar Gadde --- plugins/optimization-detective/storage/data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index afe3dc9931..9391e695cf 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -174,7 +174,7 @@ function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_r $queried_object_data['id'] = $queried_object->ID; $queried_object_data['type'] = 'user'; } elseif ( $wp_query->is_post_type_archive() ) { - $queried_object_data['type'] = get_query_var( 'post_type' ); + $queried_object_data['type'] = $wp_query->get( 'post_type' ); } $data = array( From 501c65e276a748821f23c8627134c04aad39522b Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Thu, 12 Dec 2024 16:18:59 -0600 Subject: [PATCH 159/237] Initial concept for supporting relative alingment widths --- .../includes/improve-calculate-sizes.php | 84 ++++++++++--------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 40b50a0e46..0a92c76c75 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -194,13 +194,21 @@ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $ case 'left': case 'right': - $layout_width = sprintf( '%1$spx', $image_width ); - break; - case 'center': default: - $alignment = auto_sizes_get_layout_width( 'default' ); - $layout_width = sprintf( '%1$spx', min( (int) $alignment, $image_width ) ); + $layout_alignment = in_array( $alignment, array( 'left', 'right' ), true ) ? 'wide' : 'default'; + $layout_width = auto_sizes_get_layout_width( $layout_alignment ); + + /* + * If the layout width is in pixels, we can compare against the image width + * on the server. Otherwise, we need to rely on CSS functions. + */ + if ( str_ends_with( $layout_width, 'px' ) ) { + $layout_width = sprintf( '%1$spx', min( (int) $layout_width, $image_width ) ); + } else { + $layout_width = sprintf( 'min(%1$s, %2$spx)', $layout_width, $image_width ); + } + break; } @@ -212,14 +220,14 @@ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $ return $layout_width; } -/** - * Retrieves the layout width for an alignment defined in theme.json. - * - * @since n.e.x.t - * - * @param string $alignment The alignment value. - * @return string The alignment width based. - */ + /** + * Retrieves the layout width for an alignment defined in theme.json. + * + * @since n.e.x.t + * + * @param string $alignment The alignment value. + * @return string The alignment width based. + */ function auto_sizes_get_layout_width( string $alignment ): string { $layout = auto_sizes_get_layout_settings(); @@ -232,15 +240,15 @@ function auto_sizes_get_layout_width( string $alignment ): string { return $layout_widths[ $alignment ] ?? ''; } -/** - * Filters the context keys that a block type uses. - * - * @since n.e.x.t - * - * @param string[] $uses_context Array of registered uses context for a block type. - * @param WP_Block_Type $block_type The full block type object. - * @return string[] The filtered context keys used by the block type. - */ + /** + * Filters the context keys that a block type uses. + * + * @since n.e.x.t + * + * @param string[] $uses_context Array of registered uses context for a block type. + * @param WP_Block_Type $block_type The full block type object. + * @return string[] The filtered context keys used by the block type. + */ function auto_sizes_filter_uses_context( array $uses_context, WP_Block_Type $block_type ): array { // The list of blocks that can consume outer layout context. $consumer_blocks = array( @@ -255,15 +263,15 @@ function auto_sizes_filter_uses_context( array $uses_context, WP_Block_Type $blo return $uses_context; } -/** - * Modifies the block context during rendering to blocks. - * - * @since n.e.x.t - * - * @param array $context Current block context. - * @param array $block The block being rendered. - * @return array Modified block context. - */ + /** + * Modifies the block context during rendering to blocks. + * + * @since n.e.x.t + * + * @param array $context Current block context. + * @param array $block The block being rendered. + * @return array Modified block context. + */ function auto_sizes_filter_render_block_context( array $context, array $block ): array { // When no max alignment is set, the maximum is assumed to be 'full'. $context['max_alignment'] = $context['max_alignment'] ?? 'full'; @@ -288,13 +296,13 @@ function auto_sizes_filter_render_block_context( array $context, array $block ): return $context; } -/** - * Retrieves the layout settings defined in theme.json. - * - * @since n.e.x.t - * - * @return array Associative array of layout settings. - */ + /** + * Retrieves the layout settings defined in theme.json. + * + * @since n.e.x.t + * + * @return array Associative array of layout settings. + */ function auto_sizes_get_layout_settings(): array { static $layout = array(); if ( count( $layout ) === 0 ) { From 3f96e3ff68ee1d40498d3b1f5d75db658ac49fd0 Mon Sep 17 00:00:00 2001 From: Paul Bearne Date: Thu, 12 Dec 2024 17:27:20 -0500 Subject: [PATCH 160/237] Update plugins/dominant-color-images/hooks.php Co-authored-by: Weston Ruter --- plugins/dominant-color-images/hooks.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index 3b9520afeb..d49058c991 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -225,7 +225,15 @@ function dominant_color_admin_script(): void { let replaced = match.replace( /\sclass="/, " class=\"{{ data.hasTransparency ? \'has-transparency\' : \'not-transparent\' }} " ); replaced += ' data-dominant-color="{{ data.dominantColor }}"'; replaced += ' data-has-transparency="{{ data.hasTransparency }}"'; - replaced += ' style="--dominant-color: #{{ data.dominantColor }};"'; // TODO: Potentially there could be a style attribute as well! + let hasStyleAttr = false; + const colorStyle = '--dominant-color: #{{ data.dominantColor }};'; + replaced = replaced.replace( /\sstyle="/, ( styleMatch ) => { + hasStyleAttr = true; + return styleMatch + colorStyle; + } ); + if ( ! hasStyleAttr ) { + replaced += ` style="${colorStyle}"`; + } return replaced; } ); } From 8fba89a0c403e5e08e6d176ee5d69c656f4a3d6f Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 12 Dec 2024 15:37:29 -0800 Subject: [PATCH 161/237] Suppress erroneous IDE warnings --- plugins/image-prioritizer/helper.php | 6 ++++++ .../optimization-detective/class-od-html-tag-processor.php | 7 +++++++ plugins/optimization-detective/helper.php | 7 +++++++ .../storage/class-od-url-metrics-post-type.php | 6 ++++++ 4 files changed, 26 insertions(+) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 7300b95933..6340f50520 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -205,6 +205,7 @@ function image_prioritizer_filter_store_url_metric_validity( $validity, OD_Stric * @param string $src_path Source path, relative to plugin root. * @param string|null $min_path Minified path. If not supplied, then '.min' is injected before the file extension in the source path. * @return string URL to script or stylesheet. + * @noinspection PhpDocMissingThrowsInspection */ function image_prioritizer_get_asset_path( string $src_path, ?string $min_path = null ): string { if ( null === $min_path ) { @@ -215,6 +216,11 @@ function image_prioritizer_get_asset_path( string $src_path, ?string $min_path = $force_src = false; if ( WP_DEBUG && ! file_exists( trailingslashit( __DIR__ ) . $min_path ) ) { $force_src = true; + /** + * No WP_Exception is thrown by wp_trigger_error() since E_USER_ERROR is not passed as the error level. + * + * @noinspection PhpUnhandledExceptionInspection + */ wp_trigger_error( __FUNCTION__, sprintf( diff --git a/plugins/optimization-detective/class-od-html-tag-processor.php b/plugins/optimization-detective/class-od-html-tag-processor.php index 7fc8967118..16952a23fd 100644 --- a/plugins/optimization-detective/class-od-html-tag-processor.php +++ b/plugins/optimization-detective/class-od-html-tag-processor.php @@ -645,8 +645,15 @@ public function get_updated_html(): string { * * @param string $function_name Function name. * @param string $message Warning message. + * + * @noinspection PhpDocMissingThrowsInspection */ private function warn( string $function_name, string $message ): void { + /** + * No WP_Exception is thrown by wp_trigger_error() since E_USER_ERROR is not passed as the error level. + * + * @noinspection PhpUnhandledExceptionInspection + */ wp_trigger_error( $function_name, esc_html( $message ) diff --git a/plugins/optimization-detective/helper.php b/plugins/optimization-detective/helper.php index b9dc348f94..bc9bbfb7fe 100644 --- a/plugins/optimization-detective/helper.php +++ b/plugins/optimization-detective/helper.php @@ -73,6 +73,8 @@ function od_render_generator_meta_tag(): void { * @param string $src_path Source path, relative to plugin root. * @param string|null $min_path Minified path. If not supplied, then '.min' is injected before the file extension in the source path. * @return string URL to script or stylesheet. + * + * @noinspection PhpDocMissingThrowsInspection */ function od_get_asset_path( string $src_path, ?string $min_path = null ): string { if ( null === $min_path ) { @@ -83,6 +85,11 @@ function od_get_asset_path( string $src_path, ?string $min_path = null ): string $force_src = false; if ( WP_DEBUG && ! file_exists( trailingslashit( __DIR__ ) . $min_path ) ) { $force_src = true; + /** + * No WP_Exception is thrown by wp_trigger_error() since E_USER_ERROR is not passed as the error level. + * + * @noinspection PhpUnhandledExceptionInspection + */ wp_trigger_error( __FUNCTION__, sprintf( diff --git a/plugins/optimization-detective/storage/class-od-url-metrics-post-type.php b/plugins/optimization-detective/storage/class-od-url-metrics-post-type.php index 814abaac94..8bf337691f 100644 --- a/plugins/optimization-detective/storage/class-od-url-metrics-post-type.php +++ b/plugins/optimization-detective/storage/class-od-url-metrics-post-type.php @@ -115,6 +115,7 @@ public static function get_post( string $slug ): ?WP_Post { * * @param WP_Post $post URL Metrics post. * @return OD_URL_Metric[] URL Metrics. + * @noinspection PhpDocMissingThrowsInspection */ public static function get_url_metrics_from_post( WP_Post $post ): array { $this_function = __METHOD__; @@ -123,6 +124,11 @@ public static function get_url_metrics_from_post( WP_Post $post ): array { if ( ! in_array( $error_level, array( E_USER_NOTICE, E_USER_WARNING, E_USER_ERROR, E_USER_DEPRECATED ), true ) ) { $error_level = E_USER_NOTICE; } + /** + * No WP_Exception is thrown by wp_trigger_error() since E_USER_ERROR is not passed as the error level. + * + * @noinspection PhpUnhandledExceptionInspection + */ wp_trigger_error( $this_function, esc_html( $message ), $error_level ); }; From 8f2af8766f6ef99e66cc7db634c674352a913f6d Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 12 Dec 2024 17:12:22 -0800 Subject: [PATCH 162/237] Unset lcpElementExternalBackgroundImage if URL is invalid --- plugins/image-prioritizer/helper.php | 124 ++++++++++++++---- .../image-prioritizer/tests/test-hooks.php | 1 + .../storage/rest-api.php | 31 ++++- 3 files changed, 127 insertions(+), 29 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 6340f50520..56c7f10c0b 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -138,62 +138,134 @@ function image_prioritizer_add_element_item_schema_properties( array $additional } /** - * Validates that the provided background image URL is valid. + * Validates URL for a background image. * * @since n.e.x.t * - * @param bool|WP_Error|mixed $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. - * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. - * @return bool|WP_Error Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. + * @param string $url Background image URL. + * @return true|WP_Error Validity. */ -function image_prioritizer_filter_store_url_metric_validity( $validity, OD_Strict_URL_Metric $url_metric ) { - if ( ! is_bool( $validity ) && ! ( $validity instanceof WP_Error ) ) { - $validity = (bool) $validity; +function image_prioritizer_validate_background_image_url( string $url ) { + $parsed_url = wp_parse_url( $url ); + if ( false === $parsed_url || ! isset( $parsed_url['host'] ) ) { + return new WP_Error( + 'background_image_url_lacks_host', + __( 'Supplied background image URL does not have a host.', 'image-prioritizer' ) + ); } - $data = $url_metric->get( 'lcpElementExternalBackgroundImage' ); - if ( ! is_array( $data ) ) { - return $validity; + $allowed_hosts = array_map( + static function ( $host ) { + return wp_parse_url( $host, PHP_URL_HOST ); + }, + get_allowed_http_origins() + ); + + // Obtain the host of an image attachment's URL in case a CDN is pointing all images to an origin other than the home or site URLs. + $image_attachment_query = new WP_Query( + array( + 'post_type' => 'attachment', + 'post_mime_type' => 'image', + 'post_status' => 'inherit', + 'posts_per_page' => 1, + 'fields' => 'ids', + ) + ); + if ( isset( $image_attachment_query->posts[0] ) && is_int( $image_attachment_query->posts[0] ) ) { + $src = wp_get_attachment_image_src( $image_attachment_query->posts[0] ); + if ( is_array( $src ) ) { + $attachment_image_src_host = wp_parse_url( $src[0], PHP_URL_HOST ); + if ( is_string( $attachment_image_src_host ) ) { + $allowed_hosts[] = $attachment_image_src_host; + } + } } + // Validate that the background image URL is for an allowed host. + if ( ! in_array( $parsed_url['host'], $allowed_hosts, true ) ) { + return new WP_Error( + 'disallowed_background_image_url_host', + sprintf( + /* translators: %s is the list of allowed hosts */ + __( 'Background image URL host is not among allowed: %s.', 'image-prioritizer' ), + join( ', ', $allowed_hosts ) + ) + ); + } + + // Validate that the URL points to a valid resource. $r = wp_safe_remote_head( - $data['url'], + $url, array( 'redirection' => 3, // Allow up to 3 redirects. ) ); if ( $r instanceof WP_Error ) { - return new WP_Error( - WP_DEBUG ? $r->get_error_code() : 'head_request_failure', - __( 'HEAD request for background image URL failed.', 'image-prioritizer' ) . ( WP_DEBUG ? ' ' . $r->get_error_message() : '' ), - array( - 'code' => 500, - ) - ); + return $r; } $response_code = wp_remote_retrieve_response_code( $r ); if ( $response_code < 200 || $response_code >= 400 ) { return new WP_Error( 'background_image_response_not_ok', - __( 'HEAD request for background image URL did not return with a success status code.', 'image-prioritizer' ), - array( - 'code' => WP_DEBUG ? $response_code : 400, + sprintf( + /* translators: %s is the HTTP status code */ + __( 'HEAD request for background image URL did not return with a success status code: %s.', 'image-prioritizer' ), + $response_code ) ); } - $content_type = wp_remote_retrieve_header( $r, 'Content-Type' ); - if ( ! is_string( $content_type ) || ! str_starts_with( $content_type, 'image/' ) ) { + // Validate that the Content-Type is an image. + $content_type = (array) wp_remote_retrieve_header( $r, 'Content-Type' ); + if ( ! is_string( $content_type[0] ) || ! str_starts_with( $content_type[0], 'image/' ) ) { return new WP_Error( 'background_image_response_not_image', - __( 'HEAD request for background image URL did not return an image Content-Type.', 'image-prioritizer' ), - array( - 'code' => 400, + sprintf( + /* translators: %s is the content type of the response */ + __( 'HEAD request for background image URL did not return an image Content-Type: %s.', 'image-prioritizer' ), + $content_type[0] ) ); } // TODO: Check for the Content-Length and return invalid if it is gigantic? + return true; +} + +/** + * Filters the validity of a URL Metric with an LCP element background image. + * + * This removes the lcpElementExternalBackgroundImage from the URL Metric prior to it being stored if the background + * image URL is not valid. Removal of the property is preferable to + * + * @since n.e.x.t + * + * @param bool|WP_Error|mixed $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. + * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. + * @return bool|WP_Error Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. + * + * @noinspection PhpDocMissingThrowsInspection + * @throws OD_Data_Validation_Exception Except it won't because lcpElementExternalBackgroundImage is not a required property. + */ +function image_prioritizer_filter_store_url_metric_validity( $validity, OD_Strict_URL_Metric $url_metric ) { + if ( ! is_bool( $validity ) && ! ( $validity instanceof WP_Error ) ) { + $validity = (bool) $validity; + } + + $data = $url_metric->get( 'lcpElementExternalBackgroundImage' ); + if ( is_array( $data ) && isset( $data['url'] ) && is_string( $data['url'] ) ) { // Note: The isset() and is_string() checks aren't necessary since the JSON Schema enforces them to be set. + $validity = image_prioritizer_validate_background_image_url( $data['url'] ); + if ( is_wp_error( $validity ) ) { + /** + * No WP_Exception is thrown by wp_trigger_error() since E_USER_ERROR is not passed as the error level. + * + * @noinspection PhpUnhandledExceptionInspection + */ + wp_trigger_error( __FUNCTION__, $validity->get_error_message() . ' Background image URL: ' . $data['url'] ); + $url_metric->unset( 'lcpElementExternalBackgroundImage' ); + } + } + return $validity; } diff --git a/plugins/image-prioritizer/tests/test-hooks.php b/plugins/image-prioritizer/tests/test-hooks.php index 740c71a3d5..2821807686 100644 --- a/plugins/image-prioritizer/tests/test-hooks.php +++ b/plugins/image-prioritizer/tests/test-hooks.php @@ -14,5 +14,6 @@ public function test_hooks_added(): void { $this->assertEquals( 10, has_action( 'od_init', 'image_prioritizer_init' ) ); $this->assertEquals( 10, has_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ) ); $this->assertEquals( 10, has_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ) ); + $this->assertEquals( 10, has_filter( 'od_store_url_metric_validity', 'image_prioritizer_filter_store_url_metric_validity' ) ); } } diff --git a/plugins/optimization-detective/storage/rest-api.php b/plugins/optimization-detective/storage/rest-api.php index bafbbb7e1c..35fd27a992 100644 --- a/plugins/optimization-detective/storage/rest-api.php +++ b/plugins/optimization-detective/storage/rest-api.php @@ -223,12 +223,37 @@ function od_handle_rest_request( WP_REST_Request $request ) { * loaded from the od_url_metric post type. This means that validation logic enforced via this filter can be more * expensive, such as doing filesystem checks or HTTP requests. * + * In addition to having the filter return `false` or a non-empty `WP_Error` to block storing the URL Metric, a + * plugin may also mutate the OD_URL_Metric instance passed by reference to the filter callback. This is useful + * for plugins in particular to unset extended properties which couldn't be validated using JSON Schema alone. + * * @since n.e.x.t * - * @param bool|WP_Error $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. - * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. + * @param bool|WP_Error $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. + * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. + * @param array $url_metric_data Original URL Metric data before any mutations. */ - $validity = apply_filters( 'od_store_url_metric_validity', true, $url_metric ); + try { + $validity = apply_filters( 'od_store_url_metric_validity', true, $url_metric, $url_metric->jsonSerialize() ); // TODO: A better name might be `od_url_metric_storage_validity`. + } catch ( Exception $e ) { + $error_data = null; + if ( WP_DEBUG ) { + $error_data = array( + 'exception_class' => get_class( $e ), + 'exception_message' => $e->getMessage(), + 'exception_code' => $e->getCode(), + ); + } + $validity = new WP_Error( + 'exception', + sprintf( + /* translators: %s is the filter name 'od_store_url_metric_validity' */ + __( 'An %s filter callback threw an exception.', 'optimization-detective' ), + 'od_store_url_metric_validity' + ), + $error_data + ); + } if ( false === $validity || ( $validity instanceof WP_Error && $validity->has_errors() ) ) { if ( false === $validity ) { $validity = new WP_Error( 'invalid_url_metric', __( 'Validity of URL Metric was rejected by filter.', 'optimization-detective' ) ); From 42005e6e41d9dd0a055b3b98c968aeaddc63c69c Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 12 Dec 2024 17:31:54 -0800 Subject: [PATCH 163/237] Improve docs and tidiness --- plugins/image-prioritizer/hooks.php | 2 +- .../image-prioritizer/tests/test-hooks.php | 2 +- .../class-od-url-metric-group-collection.php | 4 +- .../class-od-url-metric-group.php | 2 +- plugins/optimization-detective/readme.txt | 14 +++++ .../storage/rest-api.php | 52 +++++++++---------- ...-class-od-url-metrics-group-collection.php | 6 +-- 7 files changed, 47 insertions(+), 35 deletions(-) diff --git a/plugins/image-prioritizer/hooks.php b/plugins/image-prioritizer/hooks.php index 4a47f35647..fbfe8c6b36 100644 --- a/plugins/image-prioritizer/hooks.php +++ b/plugins/image-prioritizer/hooks.php @@ -13,4 +13,4 @@ add_action( 'od_init', 'image_prioritizer_init' ); add_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ); add_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ); -add_filter( 'od_store_url_metric_validity', 'image_prioritizer_filter_store_url_metric_validity', 10, 2 ); +add_filter( 'od_url_metric_storage_validity', 'image_prioritizer_filter_store_url_metric_validity', 10, 2 ); diff --git a/plugins/image-prioritizer/tests/test-hooks.php b/plugins/image-prioritizer/tests/test-hooks.php index 2821807686..3c2e020145 100644 --- a/plugins/image-prioritizer/tests/test-hooks.php +++ b/plugins/image-prioritizer/tests/test-hooks.php @@ -14,6 +14,6 @@ public function test_hooks_added(): void { $this->assertEquals( 10, has_action( 'od_init', 'image_prioritizer_init' ) ); $this->assertEquals( 10, has_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ) ); $this->assertEquals( 10, has_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ) ); - $this->assertEquals( 10, has_filter( 'od_store_url_metric_validity', 'image_prioritizer_filter_store_url_metric_validity' ) ); + $this->assertEquals( 10, has_filter( 'od_url_metric_storage_validity', 'image_prioritizer_filter_store_url_metric_validity' ) ); } } diff --git a/plugins/optimization-detective/class-od-url-metric-group-collection.php b/plugins/optimization-detective/class-od-url-metric-group-collection.php index ed7fe6928c..c87ac93e33 100644 --- a/plugins/optimization-detective/class-od-url-metric-group-collection.php +++ b/plugins/optimization-detective/class-od-url-metric-group-collection.php @@ -225,7 +225,7 @@ public function get_last_group(): OD_URL_Metric_Group { } /** - * Clear result cache. + * Clears result cache. * * @since 0.3.0 */ @@ -234,7 +234,7 @@ public function clear_cache(): void { } /** - * Create groups. + * Creates groups. * * @since 0.1.0 * diff --git a/plugins/optimization-detective/class-od-url-metric-group.php b/plugins/optimization-detective/class-od-url-metric-group.php index 29496a1bbd..f8772eca75 100644 --- a/plugins/optimization-detective/class-od-url-metric-group.php +++ b/plugins/optimization-detective/class-od-url-metric-group.php @@ -471,7 +471,7 @@ public function count(): int { } /** - * Clear result cache. + * Clears result cache. * * @since n.e.x.t */ diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index dbe6afd220..c1530f56fa 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -246,6 +246,20 @@ The ETag is a unique identifier that changes whenever the underlying data used t When the ETag for URL Metrics in a complete viewport group no longer matches the current environment's ETag, new URL Metrics will then begin to be collected until there are no more stored URL Metrics with the old ETag. These new URL Metrics will include data relevant to the newly activated plugins and their tag visitors. +**Filter:** `od_url_metric_storage_validity` (default args: true) + +Filters whether a URL Metric is valid for storage. + +Three paramters are passed to this filter: + +1. `$validity` (`bool|WP_Error`): Validity. Invalid if false or a WP_Error with errors. +2. `$url_metric` (`OD_Strict_URL_Metric`): URL Metric, already validated against the JSON Schema. +3. `$url_metric_data` (`array`): Original URL Metric data before any mutations. + +This allows for custom validation constraints to be applied beyond what can be expressed in JSON Schema. This filter only applies when storing a URL Metric via the REST API. It does not run when a stored URL Metric is loaded from the `od_url_metrics` post type. This means that validation logic enforced via this filter can be more expensive, such as doing filesystem checks or HTTP requests. + +In addition to having the filter return `false` or a non-empty `WP_Error` to block storing the URL Metric, a plugin may also mutate the `OD_URL_Metric` instance passed by reference to the filter callback. This is useful for plugins in particular to unset extended properties which couldn't be validated using JSON Schema alone. + **Action:** `od_url_metric_stored` (argument: `OD_URL_Metric_Store_Request_Context`) Fires whenever a URL Metric was successfully stored. diff --git a/plugins/optimization-detective/storage/rest-api.php b/plugins/optimization-detective/storage/rest-api.php index 35fd27a992..b8e33cf1e3 100644 --- a/plugins/optimization-detective/storage/rest-api.php +++ b/plugins/optimization-detective/storage/rest-api.php @@ -210,31 +210,31 @@ function od_handle_rest_request( WP_REST_Request $request ) { ); } - /** - * Filters whether a URL Metric is valid for storage. - * - * This allows for custom validation constraints to be applied beyond what can be expressed in JSON Schema. This is - * also necessary because the 'validate_callback' key in a JSON Schema is not respected when gathering the REST API - * endpoint args via the {@see rest_get_endpoint_args_for_schema()} function. Besides this, the REST API doesn't - * support 'validate_callback' for any nested arguments in any case, meaning that custom constraints would be able - * to be applied to multidimensional objects, such as the items inside 'elements'. - * - * This filter only applies when storing a URL Metric via the REST API. It does not run when a stored URL Metric - * loaded from the od_url_metric post type. This means that validation logic enforced via this filter can be more - * expensive, such as doing filesystem checks or HTTP requests. - * - * In addition to having the filter return `false` or a non-empty `WP_Error` to block storing the URL Metric, a - * plugin may also mutate the OD_URL_Metric instance passed by reference to the filter callback. This is useful - * for plugins in particular to unset extended properties which couldn't be validated using JSON Schema alone. - * - * @since n.e.x.t - * - * @param bool|WP_Error $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. - * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. - * @param array $url_metric_data Original URL Metric data before any mutations. - */ try { - $validity = apply_filters( 'od_store_url_metric_validity', true, $url_metric, $url_metric->jsonSerialize() ); // TODO: A better name might be `od_url_metric_storage_validity`. + /** + * Filters whether a URL Metric is valid for storage. + * + * This allows for custom validation constraints to be applied beyond what can be expressed in JSON Schema. This is + * also necessary because the 'validate_callback' key in a JSON Schema is not respected when gathering the REST API + * endpoint args via the {@see rest_get_endpoint_args_for_schema()} function. Besides this, the REST API doesn't + * support 'validate_callback' for any nested arguments in any case, meaning that custom constraints would be able + * to be applied to multidimensional objects, such as the items inside 'elements'. + * + * This filter only applies when storing a URL Metric via the REST API. It does not run when a stored URL Metric is + * loaded from the od_url_metrics post type. This means that validation logic enforced via this filter can be more + * expensive, such as doing filesystem checks or HTTP requests. + * + * In addition to having the filter return `false` or a non-empty `WP_Error` to block storing the URL Metric, a + * plugin may also mutate the OD_URL_Metric instance passed by reference to the filter callback. This is useful + * for plugins in particular to unset extended properties which couldn't be validated using JSON Schema alone. + * + * @since n.e.x.t + * + * @param bool|WP_Error $validity Validity. Invalid if false or a WP_Error with errors. + * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. + * @param array $url_metric_data Original URL Metric data before any mutations. + */ + $validity = apply_filters( 'od_url_metric_storage_validity', true, $url_metric, $url_metric->jsonSerialize() ); } catch ( Exception $e ) { $error_data = null; if ( WP_DEBUG ) { @@ -247,9 +247,9 @@ function od_handle_rest_request( WP_REST_Request $request ) { $validity = new WP_Error( 'exception', sprintf( - /* translators: %s is the filter name 'od_store_url_metric_validity' */ + /* translators: %s is the filter name 'od_url_metric_storage_validity' */ __( 'An %s filter callback threw an exception.', 'optimization-detective' ), - 'od_store_url_metric_validity' + 'od_url_metric_storage_validity' ), $error_data ); diff --git a/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php b/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php index 034e506615..957acd1a44 100644 --- a/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php +++ b/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php @@ -226,9 +226,6 @@ public function test_clear_cache(): void { $this->assertSame( array(), $collection_result_cache_reflection_property->getValue( $collection ) ); // Test that adding a URL metric to a collection clears the caches. - $collection_result_cache_reflection_property->setValue( $collection, $populated_value ); - $group_result_cache_reflection_property->setValue( $group, $populated_value ); - add_filter( 'od_url_metric_schema_root_additional_properties', static function ( $schema ) { @@ -247,7 +244,8 @@ static function ( $schema ) { return $schema; } ); - + $collection_result_cache_reflection_property->setValue( $collection, $populated_value ); + $group_result_cache_reflection_property->setValue( $group, $populated_value ); $collection->add_url_metric( $this->get_sample_url_metric( array( From e69b2afc17f2903ea3da409440808da591020747 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Fri, 13 Dec 2024 10:24:41 +0530 Subject: [PATCH 164/237] Revert unwanted doc indention changes --- .../includes/improve-calculate-sizes.php | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 0a92c76c75..b7c36be17d 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -220,14 +220,14 @@ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $ return $layout_width; } - /** - * Retrieves the layout width for an alignment defined in theme.json. - * - * @since n.e.x.t - * - * @param string $alignment The alignment value. - * @return string The alignment width based. - */ +/** + * Retrieves the layout width for an alignment defined in theme.json. + * + * @since n.e.x.t + * + * @param string $alignment The alignment value. + * @return string The alignment width based. + */ function auto_sizes_get_layout_width( string $alignment ): string { $layout = auto_sizes_get_layout_settings(); @@ -240,15 +240,15 @@ function auto_sizes_get_layout_width( string $alignment ): string { return $layout_widths[ $alignment ] ?? ''; } - /** - * Filters the context keys that a block type uses. - * - * @since n.e.x.t - * - * @param string[] $uses_context Array of registered uses context for a block type. - * @param WP_Block_Type $block_type The full block type object. - * @return string[] The filtered context keys used by the block type. - */ +/** + * Filters the context keys that a block type uses. + * + * @since n.e.x.t + * + * @param string[] $uses_context Array of registered uses context for a block type. + * @param WP_Block_Type $block_type The full block type object. + * @return string[] The filtered context keys used by the block type. + */ function auto_sizes_filter_uses_context( array $uses_context, WP_Block_Type $block_type ): array { // The list of blocks that can consume outer layout context. $consumer_blocks = array( @@ -263,15 +263,15 @@ function auto_sizes_filter_uses_context( array $uses_context, WP_Block_Type $blo return $uses_context; } - /** - * Modifies the block context during rendering to blocks. - * - * @since n.e.x.t - * - * @param array $context Current block context. - * @param array $block The block being rendered. - * @return array Modified block context. - */ +/** + * Modifies the block context during rendering to blocks. + * + * @since n.e.x.t + * + * @param array $context Current block context. + * @param array $block The block being rendered. + * @return array Modified block context. + */ function auto_sizes_filter_render_block_context( array $context, array $block ): array { // When no max alignment is set, the maximum is assumed to be 'full'. $context['max_alignment'] = $context['max_alignment'] ?? 'full'; @@ -296,13 +296,13 @@ function auto_sizes_filter_render_block_context( array $context, array $block ): return $context; } - /** - * Retrieves the layout settings defined in theme.json. - * - * @since n.e.x.t - * - * @return array Associative array of layout settings. - */ +/** + * Retrieves the layout settings defined in theme.json. + * + * @since n.e.x.t + * + * @return array Associative array of layout settings. + */ function auto_sizes_get_layout_settings(): array { static $layout = array(); if ( count( $layout ) === 0 ) { From 0d584b711803e68b847329c4859827f470b925f2 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 12 Dec 2024 22:56:26 -0800 Subject: [PATCH 165/237] Add tests for od_url_metric_storage_validity filter --- .../storage/rest-api.php | 33 ++- .../tests/storage/test-rest-api.php | 201 ++++++++++++++---- 2 files changed, 182 insertions(+), 52 deletions(-) diff --git a/plugins/optimization-detective/storage/rest-api.php b/plugins/optimization-detective/storage/rest-api.php index b8e33cf1e3..198b572846 100644 --- a/plugins/optimization-detective/storage/rest-api.php +++ b/plugins/optimization-detective/storage/rest-api.php @@ -236,12 +236,14 @@ function od_handle_rest_request( WP_REST_Request $request ) { */ $validity = apply_filters( 'od_url_metric_storage_validity', true, $url_metric, $url_metric->jsonSerialize() ); } catch ( Exception $e ) { - $error_data = null; + $error_data = array( + 'status' => 500, + ); if ( WP_DEBUG ) { - $error_data = array( - 'exception_class' => get_class( $e ), - 'exception_message' => $e->getMessage(), - 'exception_code' => $e->getCode(), + $error_data['exception'] = array( + 'class' => get_class( $e ), + 'message' => $e->getMessage(), + 'code' => $e->getCode(), ); } $validity = new WP_Error( @@ -254,12 +256,13 @@ function od_handle_rest_request( WP_REST_Request $request ) { $error_data ); } - if ( false === $validity || ( $validity instanceof WP_Error && $validity->has_errors() ) ) { + if ( false === (bool) $validity || ( $validity instanceof WP_Error && $validity->has_errors() ) ) { if ( false === $validity ) { $validity = new WP_Error( 'invalid_url_metric', __( 'Validity of URL Metric was rejected by filter.', 'optimization-detective' ) ); } - if ( ! isset( $validity->error_data['status'] ) ) { - $validity->error_data['status'] = 400; + $error_code = $validity->get_error_code(); + if ( ! isset( $validity->error_data[ $error_code ]['status'] ) ) { + $validity->error_data[ $error_code ]['status'] = 400; } return $validity; } @@ -269,9 +272,19 @@ function od_handle_rest_request( WP_REST_Request $request ) { $request->get_param( 'slug' ), $url_metric ); - if ( $result instanceof WP_Error ) { - return $result; + $error_data = array( + 'status' => 500, + ); + if ( WP_DEBUG ) { + $error_data['code'] = $result->get_error_code(); + $error_data['message'] = $result->get_error_message(); + } + return new WP_Error( + 'unable_to_store_url_metric', + __( 'Unable to store URL Metric.', 'optimization-detective' ), + $error_data + ); } $post_id = $result; diff --git a/plugins/optimization-detective/tests/storage/test-rest-api.php b/plugins/optimization-detective/tests/storage/test-rest-api.php index ad514aa487..0caf8b5a3c 100644 --- a/plugins/optimization-detective/tests/storage/test-rest-api.php +++ b/plugins/optimization-detective/tests/storage/test-rest-api.php @@ -28,21 +28,74 @@ public function test_od_register_endpoint_hooked(): void { * @return array */ public function data_provider_to_test_rest_request_good_params(): array { + $add_root_extra_property = static function ( string $property_name ): void { + add_filter( + 'od_url_metric_schema_root_additional_properties', + static function ( array $properties ) use ( $property_name ): array { + $properties[ $property_name ] = array( + 'type' => 'string', + ); + return $properties; + } + ); + }; + return array( - 'not_extended' => array( - 'set_up' => function (): array { + 'not_extended' => array( + 'set_up' => function (): array { return $this->get_valid_params(); }, + 'expect_stored' => true, + 'expected_status' => 200, ), - 'extended' => array( - 'set_up' => function (): array { + 'extended' => array( + 'set_up' => function () use ( $add_root_extra_property ): array { + $add_root_extra_property( 'extra' ); + $params = $this->get_valid_params(); + $params['extra'] = 'foo'; + return $params; + }, + 'expect_stored' => true, + 'expected_status' => 200, + ), + 'extended_but_unset' => array( + 'set_up' => function () use ( $add_root_extra_property ): array { + $add_root_extra_property( 'unset_prop' ); add_filter( - 'od_url_metric_schema_root_additional_properties', - static function ( array $properties ): array { - $properties['extra'] = array( - 'type' => 'string', - ); - return $properties; + 'od_url_metric_storage_validity', + static function ( $validity, OD_URL_Metric $url_metric ) { + $url_metric->unset( 'extra' ); + return $validity; + }, + 10, + 2 + ); + $params = $this->get_valid_params(); + $params['unset_prop'] = 'bar'; + return $params; + }, + 'expect_stored' => true, + 'expected_status' => 200, + ), + 'extended_and_rejected_by_returning_false' => array( + 'set_up' => function () use ( $add_root_extra_property ): array { + $add_root_extra_property( 'extra' ); + add_filter( 'od_url_metric_storage_validity', '__return_false' ); + + $params = $this->get_valid_params(); + $params['extra'] = 'foo'; + return $params; + }, + 'expect_stored' => false, + 'expected_status' => 400, + ), + 'extended_and_rejected_by_returning_wp_error' => array( + 'set_up' => function () use ( $add_root_extra_property ): array { + $add_root_extra_property( 'extra' ); + add_filter( + 'od_url_metric_storage_validity', + static function () { + return new WP_Error( 'rejected', 'Rejected!' ); } ); @@ -50,9 +103,40 @@ static function ( array $properties ): array { $params['extra'] = 'foo'; return $params; }, + 'expect_stored' => false, + 'expected_status' => 400, ), - 'with_cache_purge_post_id' => array( - 'set_up' => function (): array { + 'rejected_by_returning_wp_error_with_forbidden_status' => array( + 'set_up' => function (): array { + add_filter( + 'od_url_metric_storage_validity', + static function () { + return new WP_Error( 'forbidden', 'Forbidden!', array( 'status' => 403 ) ); + } + ); + return $this->get_valid_params(); + }, + 'expect_stored' => false, + 'expected_status' => 403, + ), + 'rejected_by_exception' => array( + 'set_up' => function (): array { + add_filter( + 'od_url_metric_storage_validity', + static function ( $validity, OD_URL_Metric $url_metric ) { + $url_metric->unset( 'viewport' ); + return $validity; + }, + 10, + 2 + ); + return $this->get_valid_params(); + }, + 'expect_stored' => false, + 'expected_status' => 500, + ), + 'with_cache_purge_post_id' => array( + 'set_up' => function (): array { $params = $this->get_valid_params(); $params['cache_purge_post_id'] = self::factory()->post->create(); $params['url'] = get_permalink( $params['cache_purge_post_id'] ); @@ -60,6 +144,8 @@ static function ( array $properties ): array { $params['hmac'] = od_get_url_metrics_storage_hmac( $params['slug'], $params['current_etag'], $params['url'], $params['cache_purge_post_id'] ); return $params; }, + 'expect_stored' => true, + 'expected_status' => 200, ), ); } @@ -73,7 +159,25 @@ static function ( array $properties ): array { * @covers ::od_handle_rest_request * @covers ::od_trigger_page_cache_invalidation */ - public function test_rest_request_good_params( Closure $set_up ): void { + public function test_rest_request_good_params( Closure $set_up, bool $expect_stored, int $expected_status ): void { + $filtered_url_metric_obj = null; + $filtered_url_metric_data = null; + $filter_called = 0; + add_filter( + 'od_url_metric_storage_validity', + function ( $validity, $url_metric, $url_metric_data ) use ( &$filter_called, &$filtered_url_metric_obj, &$filtered_url_metric_data ) { + $this->assertTrue( $validity ); + $this->assertInstanceOf( OD_URL_Metric::class, $url_metric ); + $this->assertIsArray( $url_metric_data ); + $filtered_url_metric_obj = $url_metric; + $filtered_url_metric_data = $url_metric_data; + $filter_called++; + return $validity; + }, + 0, + 3 + ); + $stored_context = null; add_action( 'od_url_metric_stored', @@ -96,38 +200,51 @@ function ( OD_URL_Metric_Store_Request_Context $context ) use ( &$stored_context $this->assertCount( 0, get_posts( array( 'post_type' => OD_URL_Metrics_Post_Type::SLUG ) ) ); $request = $this->create_request( $valid_params ); $response = rest_get_server()->dispatch( $request ); - $this->assertSame( 200, $response->get_status(), 'Response: ' . wp_json_encode( $response ) ); - - $data = $response->get_data(); - $this->assertTrue( $data['success'] ); - - $this->assertCount( 1, get_posts( array( 'post_type' => OD_URL_Metrics_Post_Type::SLUG ) ) ); - $post = OD_URL_Metrics_Post_Type::get_post( $valid_params['slug'] ); - $this->assertInstanceOf( WP_Post::class, $post ); - - $url_metrics = OD_URL_Metrics_Post_Type::get_url_metrics_from_post( $post ); - $this->assertCount( 1, $url_metrics, 'Expected number of URL Metrics stored.' ); - $this->assertSame( $valid_params['elements'], $this->get_array_json_data( $url_metrics[0]->get( 'elements' ) ) ); - $this->assertSame( $valid_params['viewport']['width'], $url_metrics[0]->get_viewport_width() ); - - $expected_data = $valid_params; - unset( $expected_data['hmac'], $expected_data['slug'], $expected_data['current_etag'], $expected_data['cache_purge_post_id'] ); - $this->assertSame( - $expected_data, - wp_array_slice_assoc( $url_metrics[0]->jsonSerialize(), array_keys( $expected_data ) ) - ); - $this->assertSame( 1, did_action( 'od_url_metric_stored' ) ); - $this->assertInstanceOf( OD_URL_Metric_Store_Request_Context::class, $stored_context ); + $this->assertSame( 1, $filter_called ); + $this->assertSame( $expect_stored ? 1 : 0, did_action( 'od_url_metric_stored' ) ); - // Now check that od_trigger_page_cache_invalidation() cleaned caches as expected. - $this->assertSame( $url_metrics[0]->jsonSerialize(), $stored_context->url_metric->jsonSerialize() ); - $cache_purge_post_id = $stored_context->request->get_param( 'cache_purge_post_id' ); + $this->assertSame( $expected_status, $response->get_status(), 'Response: ' . wp_json_encode( $response ) ); + $data = $response->get_data(); + $this->assertCount( $expect_stored ? 1 : 0, get_posts( array( 'post_type' => OD_URL_Metrics_Post_Type::SLUG ) ) ); + + if ( ! $expect_stored ) { + $this->assertArrayHasKey( 'code', $data ); + $this->assertArrayHasKey( 'message', $data ); + $this->assertNull( $stored_context ); + $this->assertNull( OD_URL_Metrics_Post_Type::get_post( $valid_params['slug'] ) ); + } else { + $this->assertTrue( $data['success'] ); + + $post = OD_URL_Metrics_Post_Type::get_post( $valid_params['slug'] ); + $this->assertInstanceOf( WP_Post::class, $post ); + + $url_metrics = OD_URL_Metrics_Post_Type::get_url_metrics_from_post( $post ); + $this->assertCount( 1, $url_metrics, 'Expected number of URL Metrics stored.' ); + $this->assertSame( $valid_params['elements'], $this->get_array_json_data( $url_metrics[0]->get( 'elements' ) ) ); + $this->assertSame( $valid_params['viewport']['width'], $url_metrics[0]->get_viewport_width() ); + + $expected_data = $valid_params; + unset( $expected_data['hmac'], $expected_data['slug'], $expected_data['current_etag'], $expected_data['cache_purge_post_id'] ); + unset( $expected_data['unset_prop'] ); + $this->assertSame( + $expected_data, + wp_array_slice_assoc( $url_metrics[0]->jsonSerialize(), array_keys( $expected_data ) ) + ); - if ( isset( $valid_params['cache_purge_post_id'] ) ) { - $scheduled = wp_next_scheduled( 'od_trigger_page_cache_invalidation', array( $valid_params['cache_purge_post_id'] ) ); - $this->assertIsInt( $scheduled ); - $this->assertGreaterThan( time(), $scheduled ); + $this->assertInstanceOf( OD_URL_Metric_Store_Request_Context::class, $stored_context ); + $this->assertSame( $stored_context->url_metric, $filtered_url_metric_obj ); + $this->assertSame( $stored_context->url_metric->jsonSerialize(), $filtered_url_metric_data ); + + // Now check that od_trigger_page_cache_invalidation() cleaned caches as expected. + $this->assertSame( $url_metrics[0]->jsonSerialize(), $stored_context->url_metric->jsonSerialize() ); + if ( isset( $valid_params['cache_purge_post_id'] ) ) { + $cache_purge_post_id = $stored_context->request->get_param( 'cache_purge_post_id' ); + $this->assertSame( $valid_params['cache_purge_post_id'], $cache_purge_post_id ); + $scheduled = wp_next_scheduled( 'od_trigger_page_cache_invalidation', array( $cache_purge_post_id ) ); + $this->assertIsInt( $scheduled ); + $this->assertGreaterThan( time(), $scheduled ); + } } } From e40b3f177de220fc6572d2a3c331cdae7a4542eb Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Thu, 12 Dec 2024 23:12:11 -0800 Subject: [PATCH 166/237] Fix typo in readme --- plugins/optimization-detective/readme.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index c1530f56fa..f0a8cc1476 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -250,7 +250,7 @@ When the ETag for URL Metrics in a complete viewport group no longer matches the Filters whether a URL Metric is valid for storage. -Three paramters are passed to this filter: +Three parameters are passed to this filter: 1. `$validity` (`bool|WP_Error`): Validity. Invalid if false or a WP_Error with errors. 2. `$url_metric` (`OD_Strict_URL_Metric`): URL Metric, already validated against the JSON Schema. From 9a1911300dadb208a680c2b5c1aa57becdfc17ed Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Fri, 13 Dec 2024 13:03:21 +0530 Subject: [PATCH 167/237] Add isset checks for global variables Co-authored-by: Weston Ruter --- plugins/optimization-detective/optimization.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/optimization.php b/plugins/optimization-detective/optimization.php index 8eabb24952..ea6241df93 100644 --- a/plugins/optimization-detective/optimization.php +++ b/plugins/optimization-detective/optimization.php @@ -213,10 +213,10 @@ function od_optimize_template_output_buffer( string $buffer ): string { do_action( 'od_register_tag_visitors', $tag_visitor_registry ); $current_template = null; - if ( wp_is_block_theme() && $_wp_current_template_id ) { + if ( wp_is_block_theme() && isset( $_wp_current_template_id ) ) { $current_template = get_block_template( $_wp_current_template_id, 'wp_template' ); } - if ( null === $current_template && $template && is_string( $template ) ) { + if ( null === $current_template && isset( $template ) && is_string( $template ) ) { $current_template = basename( $template ); } From 7dd60d5532fcb34d06ecb0ac27cdf1bd29502d13 Mon Sep 17 00:00:00 2001 From: Joe McGill <801097+joemcgill@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:01:28 -0600 Subject: [PATCH 168/237] Chance typing for `sprintf()` placeholder Co-authored-by: Weston Ruter --- plugins/auto-sizes/includes/improve-calculate-sizes.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index b7c36be17d..a354e08f13 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -204,7 +204,7 @@ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $ * on the server. Otherwise, we need to rely on CSS functions. */ if ( str_ends_with( $layout_width, 'px' ) ) { - $layout_width = sprintf( '%1$spx', min( (int) $layout_width, $image_width ) ); + $layout_width = sprintf( '%dpx', min( (int) $layout_width, $image_width ) ); } else { $layout_width = sprintf( 'min(%1$s, %2$spx)', $layout_width, $image_width ); } From 35988a41382022cdccd22433f1d6f1cdb5f70f5b Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Fri, 13 Dec 2024 10:53:41 -0600 Subject: [PATCH 169/237] Add tests for relative alignment --- .../includes/improve-calculate-sizes.php | 6 +- .../tests/test-improve-calculate-sizes.php | 122 ++++++++++++++++++ 2 files changed, 123 insertions(+), 5 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index a354e08f13..1e0c5113fd 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -304,9 +304,5 @@ function auto_sizes_filter_render_block_context( array $context, array $block ): * @return array Associative array of layout settings. */ function auto_sizes_get_layout_settings(): array { - static $layout = array(); - if ( count( $layout ) === 0 ) { - $layout = wp_get_global_settings( array( 'layout' ) ); - } - return $layout; + return wp_get_global_settings( array( 'layout' ) ); } diff --git a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php index 1ef56fd2c6..484fb3f954 100644 --- a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -34,6 +34,9 @@ public function set_up(): void { // Disable lazy loading attribute. add_filter( 'wp_img_tag_add_loading_attr', '__return_false' ); + + // Run each test with fresh WP_Theme_JSON data so we can filter layout values. + wp_clean_theme_json_cache(); } /** @@ -514,6 +517,125 @@ public function data_ancestor_and_image_block_alignment(): array { ); } + /** + * Test sizes attributes when alignments use relative units. + * + * @dataProvider data_image_blocks_with_relative_alignment + * + * @param string $ancestor_alignment Ancestor alignment. + * @param string $image_alignment Image alignment. + * @param string $expected Expected output. + */ + public function test_sizes_with_relative_layout_sizes( string $ancestor_alignment, string $image_alignment, string $expected ): void { + add_filter( 'wp_theme_json_data_user', array( $this, 'filter_theme_json_layout_sizes' ) ); + + $block_content = $this->get_group_block_markup( + $this->get_image_block_markup( self::$image_id, 'large', $image_alignment ), + array( + 'align' => $ancestor_alignment, + ) + ); + + $result = apply_filters( 'the_content', $block_content ); + + $this->assertStringContainsString( $expected, $result ); + } + + /** + * Data provider. + * + * @return array> The ancestor and image alignments. + */ + public function data_image_blocks_with_relative_alignment(): array { + return array( + // Parent default alignment. + 'Return contentSize 50vw, parent block default alignment, image block default alignment' => array( + '', + '', + 'sizes="(max-width: min(50vw, 1024px)) 100vw, min(50vw, 1024px)" ', + ), + 'Return contentSize 50vw, parent block default alignment, image block wide alignment' => array( + '', + 'wide', + 'sizes="(max-width: min(50vw, 1024px)) 100vw, min(50vw, 1024px)" ', + ), + 'Return contentSize 50vw, parent block default alignment, image block full alignment' => array( + '', + 'full', + 'sizes="(max-width: min(50vw, 1024px)) 100vw, min(50vw, 1024px)" ', + ), + 'Return contentSize 50vw, parent block default alignment, image block left alignment' => array( + '', + 'left', + 'sizes="(max-width: min(50vw, 1024px)) 100vw, min(50vw, 1024px)" ', + ), + 'Return contentSize 50vw, parent block default alignment, image block center alignment' => array( + '', + 'center', + 'sizes="(max-width: min(50vw, 1024px)) 100vw, min(50vw, 1024px)" ', + ), + 'Return contentSize 50vw, parent block default alignment, image block right alignment' => array( + '', + 'right', + 'sizes="(max-width: min(50vw, 1024px)) 100vw, min(50vw, 1024px)" ', + ), + + // Parent wide alignment. + 'Return contentSize 50vw, parent block wide alignment, image block default alignment' => array( + 'wide', + '', + 'sizes="(max-width: min(50vw, 1024px)) 100vw, min(50vw, 1024px)" ', + ), + 'Return wideSize 75vw, parent block wide alignment, image block wide alignment' => array( + 'wide', + 'wide', + 'sizes="(max-width: 75vw) 100vw, 75vw" ', + ), + 'Return wideSize 75vw, parent block wide alignment, image block full alignment' => array( + 'wide', + 'full', + 'sizes="(max-width: 75vw) 100vw, 75vw" ', + ), + 'Return image size 1024px, parent block wide alignment, image block left alignment' => array( + 'wide', + 'left', + 'sizes="(max-width: min(75vw, 1024px)) 100vw, min(75vw, 1024px)" ', + ), + 'Return image size 620px, parent block wide alignment, image block center alignment' => array( + 'wide', + 'center', + 'sizes="(max-width: min(50vw, 1024px)) 100vw, min(50vw, 1024px)" ', + ), + 'Return image size 1024px, parent block wide alignment, image block right alignment' => array( + 'wide', + 'right', + 'sizes="(max-width: min(75vw, 1024px)) 100vw, min(75vw, 1024px)" ', + ), + ); + } + + /** + * Filter the theme.json data to include relative layout sizes. + * + * @param WP_Theme_JSON_Data $theme_json Theme JSON object. + * @return WP_Theme_JSON_Data Updated theme JSON object. + */ + public function filter_theme_json_layout_sizes( WP_Theme_JSON_Data $theme_json ): WP_Theme_JSON_Data { + $data = array( + 'version' => 2, + 'settings' => array( + 'layout' => array( + 'contentSize' => '50vw', + 'wideSize' => '75vw', + ), + ), + ); + + $theme_json = $theme_json->update_with( $data ); + + return $theme_json; + } + /** * Helper to generate image block markup. * From 199e71903d1ff1d459b21190b87570ea1d7fdd10 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 10:40:52 -0800 Subject: [PATCH 170/237] Explain why forward config is used with link to Partytown docs --- plugins/web-worker-offloading/third-party/seo-by-rank-math.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/web-worker-offloading/third-party/seo-by-rank-math.php b/plugins/web-worker-offloading/third-party/seo-by-rank-math.php index 4162fdb5f7..0ad2bebd20 100644 --- a/plugins/web-worker-offloading/third-party/seo-by-rank-math.php +++ b/plugins/web-worker-offloading/third-party/seo-by-rank-math.php @@ -24,7 +24,7 @@ function plwwo_rank_math_configure( $configuration ): array { $configuration = (array) $configuration; $configuration['globalFns'][] = 'gtag'; // Because gtag() is defined in one script and called in another. - $configuration['forward'][] = 'dataLayer.push'; // Because the Partytown integration has this in its example config. + $configuration['forward'][] = 'dataLayer.push'; // See . return $configuration; } add_filter( 'plwwo_configuration', 'plwwo_rank_math_configure' ); From c0c4f2781993aadf8b290b42695cd1652203effb Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 10:42:16 -0800 Subject: [PATCH 171/237] Clarify why forward config is used --- plugins/web-worker-offloading/third-party/google-site-kit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/web-worker-offloading/third-party/google-site-kit.php b/plugins/web-worker-offloading/third-party/google-site-kit.php index 5b95aab283..7c29bec85e 100644 --- a/plugins/web-worker-offloading/third-party/google-site-kit.php +++ b/plugins/web-worker-offloading/third-party/google-site-kit.php @@ -24,7 +24,7 @@ function plwwo_google_site_kit_configure( $configuration ): array { $configuration = (array) $configuration; $configuration['globalFns'][] = 'gtag'; // Because gtag() is defined in one script and called in another. - $configuration['forward'][] = 'dataLayer.push'; // Because the Partytown integration has this in its example config. + $configuration['forward'][] = 'dataLayer.push'; // See . // See . $configuration['mainWindowAccessors'][] = '_googlesitekitConsentCategoryMap'; From c2a80b015b7c8285d28febe6b73aa255413426f5 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 11:03:03 -0800 Subject: [PATCH 172/237] Update permalink to Consent_Mode.php --- plugins/web-worker-offloading/third-party/google-site-kit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/web-worker-offloading/third-party/google-site-kit.php b/plugins/web-worker-offloading/third-party/google-site-kit.php index 7c29bec85e..7d100f4b3b 100644 --- a/plugins/web-worker-offloading/third-party/google-site-kit.php +++ b/plugins/web-worker-offloading/third-party/google-site-kit.php @@ -26,7 +26,7 @@ function plwwo_google_site_kit_configure( $configuration ): array { $configuration['globalFns'][] = 'gtag'; // Because gtag() is defined in one script and called in another. $configuration['forward'][] = 'dataLayer.push'; // See . - // See . + // See . $configuration['mainWindowAccessors'][] = '_googlesitekitConsentCategoryMap'; $configuration['mainWindowAccessors'][] = '_googlesitekitConsents'; From be572252dbf6e18701d7791bfd64c87b3baed0e7 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 11:05:34 -0800 Subject: [PATCH 173/237] Combine comments --- plugins/dominant-color-images/hooks.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index d49058c991..94b27b140a 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -198,8 +198,7 @@ function dominant_color_render_generator(): void { */ function dominant_color_admin_inline_style(): void { $handle = 'dominant-color-admin-styles'; - // PHPCS ignore reason: Version not used since this handle is only registered for adding an inline style. - // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion + // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- Version not used since this handle is only registered for adding an inline style. wp_register_style( $handle, false ); wp_enqueue_style( $handle ); $custom_css = '.wp-core-ui .attachment-preview[data-dominant-color]:not(.has-transparency) { background-color: var(--dominant-color); }'; From 0162d90a0fe8b9b1e914ba32f31ce1907abfca47 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 11:16:31 -0800 Subject: [PATCH 174/237] Add missing WP Consent API globals --- .../web-worker-offloading/third-party/google-site-kit.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/web-worker-offloading/third-party/google-site-kit.php b/plugins/web-worker-offloading/third-party/google-site-kit.php index 7d100f4b3b..45b0fb3770 100644 --- a/plugins/web-worker-offloading/third-party/google-site-kit.php +++ b/plugins/web-worker-offloading/third-party/google-site-kit.php @@ -24,11 +24,17 @@ function plwwo_google_site_kit_configure( $configuration ): array { $configuration = (array) $configuration; $configuration['globalFns'][] = 'gtag'; // Because gtag() is defined in one script and called in another. + $configuration['globalFns'][] = 'wp_has_consent'; // See . $configuration['forward'][] = 'dataLayer.push'; // See . - // See . + // See , + // and . $configuration['mainWindowAccessors'][] = '_googlesitekitConsentCategoryMap'; $configuration['mainWindowAccessors'][] = '_googlesitekitConsents'; + $configuration['mainWindowAccessors'][] = 'wp_consent_type'; + $configuration['mainWindowAccessors'][] = 'wp_fallback_consent_type'; + $configuration['mainWindowAccessors'][] = 'wp_has_consent'; + $configuration['mainWindowAccessors'][] = 'waitfor_consent_hook'; return $configuration; } From 6c3f1aef5ff44cfc7d294b22fb13c644c3d316bc Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 11:31:52 -0800 Subject: [PATCH 175/237] Account for dominant_color not being prefixed by hash --- plugins/dominant-color-images/hooks.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index 94b27b140a..e48044d545 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -261,14 +261,20 @@ function dominant_color_prepare_attachment_for_js( $response, WP_Post $attachmen } $response['dominantColor'] = ''; - if ( isset( $meta['dominant_color'] ) && str_starts_with( '#', $meta['dominant_color'] ) ) { - $response['dominantColor'] = sanitize_hex_color( $meta['dominant_color'] ); + if ( + isset( $meta['dominant_color'] ) + && + 1 === preg_match( '/^[0-9A-F]+$/', $meta['dominant_color'] ) // See format returned by dominant_color_rgb_to_hex(). + ) { + $response['dominantColor'] = $meta['dominant_color']; } $response['hasTransparency'] = ''; if ( isset( $meta['has_transparency'] ) ) { $response['hasTransparency'] = (bool) $meta['has_transparency']; } + $response['testMeta'] = $meta; + return $response; } add_filter( 'wp_prepare_attachment_for_js', 'dominant_color_prepare_attachment_for_js', 10, 3 ); From e4fb28f4e0ac43bf0701ed4b52fda3d0d8c7970f Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 11:40:58 -0800 Subject: [PATCH 176/237] Avoid printing --dominant-color style when there is no dominant color --- plugins/dominant-color-images/hooks.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index e48044d545..dd35052db8 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -221,11 +221,11 @@ function dominant_color_admin_script(): void { const tmpl = document.getElementById( 'tmpl-attachment' ); if ( tmpl ) { tmpl.textContent = tmpl.textContent.replace( /^\s*]*?(?=>)/, ( match ) => { - let replaced = match.replace( /\sclass="/, " class=\"{{ data.hasTransparency ? \'has-transparency\' : \'not-transparent\' }} " ); + let replaced = match.replace( /\sclass="/, " class=\"{{ data.hasTransparency ? 'has-transparency' : 'not-transparent' }} " ); replaced += ' data-dominant-color="{{ data.dominantColor }}"'; replaced += ' data-has-transparency="{{ data.hasTransparency }}"'; let hasStyleAttr = false; - const colorStyle = '--dominant-color: #{{ data.dominantColor }};'; + const colorStyle = "{{ data.dominantColor ? '--dominant-color: #' + data.dominantColor + ';' : '' }}"; replaced = replaced.replace( /\sstyle="/, ( styleMatch ) => { hasStyleAttr = true; return styleMatch + colorStyle; From 92553f2743e8b27cbf190f62d8e87fe62dad1e11 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 11:47:43 -0800 Subject: [PATCH 177/237] Fix regex for dominant_color --- plugins/dominant-color-images/hooks.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index dd35052db8..c411909e59 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -264,7 +264,7 @@ function dominant_color_prepare_attachment_for_js( $response, WP_Post $attachmen if ( isset( $meta['dominant_color'] ) && - 1 === preg_match( '/^[0-9A-F]+$/', $meta['dominant_color'] ) // See format returned by dominant_color_rgb_to_hex(). + 1 === preg_match( '/^[0-9a-f]+$/', $meta['dominant_color'] ) // See format returned by dominant_color_rgb_to_hex(). ) { $response['dominantColor'] = $meta['dominant_color']; } @@ -273,8 +273,6 @@ function dominant_color_prepare_attachment_for_js( $response, WP_Post $attachmen $response['hasTransparency'] = (bool) $meta['has_transparency']; } - $response['testMeta'] = $meta; - return $response; } add_filter( 'wp_prepare_attachment_for_js', 'dominant_color_prepare_attachment_for_js', 10, 3 ); From 2da00d76803ff11bcc450e773caa16fd6cf975b4 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 12:00:28 -0800 Subject: [PATCH 178/237] Add reference to related core function --- plugins/dominant-color-images/hooks.php | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index c411909e59..a841a6f1f0 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -214,6 +214,7 @@ function dominant_color_admin_inline_style(): void { * to the template, allowing these properties to be displayed in the media library. * * @since n.e.x.t + * @see wp_print_media_templates() */ function dominant_color_admin_script(): void { ?> From f182ec795b7a22d507ead7f4dc6452acee30b1de Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 15 Nov 2024 15:17:03 -0800 Subject: [PATCH 179/237] Log out URL metrics in reverse chronological order when WP_DEBUG --- plugins/optimization-detective/detect.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/detect.js b/plugins/optimization-detective/detect.js index fe4518c9ef..ded7ff898b 100644 --- a/plugins/optimization-detective/detect.js +++ b/plugins/optimization-detective/detect.js @@ -233,6 +233,11 @@ function extendElementData( xpath, properties ) { Object.assign( elementData, properties ); } +/** + * @typedef {{timestamp: number, creationDate: Date}} UrlMetricDebugData + * @typedef {{groups: Array<{url_metrics: Array}>}} CollectionDebugData + */ + /** * Detects the LCP element, loaded images, client viewport and store for future optimizations. * @@ -250,7 +255,7 @@ function extendElementData( xpath, properties ) { * @param {URLMetricGroupStatus[]} args.urlMetricGroupStatuses URL Metric group statuses. * @param {number} args.storageLockTTL The TTL (in seconds) for the URL Metric storage lock. * @param {string} args.webVitalsLibrarySrc The URL for the web-vitals library. - * @param {Object} [args.urlMetricGroupCollection] URL Metric group collection, when in debug mode. + * @param {CollectionDebugData} [args.urlMetricGroupCollection] URL Metric group collection, when in debug mode. */ export default async function detect( { minViewportAspectRatio, @@ -269,7 +274,21 @@ export default async function detect( { urlMetricGroupCollection, } ) { if ( isDebug ) { - log( 'Stored URL Metric group collection:', urlMetricGroupCollection ); + const allUrlMetrics = /** @type Array */ []; + for ( const group of urlMetricGroupCollection.groups ) { + for ( const otherUrlMetric of group.url_metrics ) { + otherUrlMetric.creationDate = new Date( + otherUrlMetric.timestamp * 1000 + ); + allUrlMetrics.push( otherUrlMetric ); + } + } + log( 'Stored URL Metric Group Collection:', urlMetricGroupCollection ); + allUrlMetrics.sort( ( a, b ) => b.timestamp - a.timestamp ); + log( + 'Stored URL Metrics in reverse chronological order:', + allUrlMetrics + ); } // Abort if the current viewport is not among those which need URL Metrics. From dc7ef36a01f254ca58dfa6528ceaf892170634ad Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 13:20:58 -0800 Subject: [PATCH 180/237] Fix plugin name reference Co-authored-by: Adam Silverstein --- plugins/web-worker-offloading/third-party/google-site-kit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/web-worker-offloading/third-party/google-site-kit.php b/plugins/web-worker-offloading/third-party/google-site-kit.php index 45b0fb3770..52ca540ad2 100644 --- a/plugins/web-worker-offloading/third-party/google-site-kit.php +++ b/plugins/web-worker-offloading/third-party/google-site-kit.php @@ -48,7 +48,7 @@ function plwwo_google_site_kit_configure( $configuration ): array { ); /** - * Filters inline script attributes to offload Rank Math's GTag script tag to Partytown. + * Filters inline script attributes to offload Google Site Kit's GTag script tag to Partytown. * * @since n.e.x.t * @access private From eeb4327c279c42939a7271eb47a1d5985db43125 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 13:26:24 -0800 Subject: [PATCH 181/237] Fix permalink to Site Kit on GitHub --- plugins/web-worker-offloading/third-party/google-site-kit.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/web-worker-offloading/third-party/google-site-kit.php b/plugins/web-worker-offloading/third-party/google-site-kit.php index 52ca540ad2..a10b4d9034 100644 --- a/plugins/web-worker-offloading/third-party/google-site-kit.php +++ b/plugins/web-worker-offloading/third-party/google-site-kit.php @@ -52,7 +52,7 @@ function plwwo_google_site_kit_configure( $configuration ): array { * * @since n.e.x.t * @access private - * @link https://github.com/rankmath/seo-by-rank-math/blob/c78adba6f78079f27ff1430fabb75c6ac3916240/includes/modules/analytics/class-gtag.php#L169-L174 + * @link https://github.com/google/site-kit-wp/blob/abbb74ff21f98a8779fbab0eeb9a16279a122bc4/includes/Core/Consent_Mode/Consent_Mode.php#L244-L259 * * @param array|mixed $attributes Script attributes. * @return array|mixed Filtered inline script attributes. From 6a89d7bda2871ff8a202abd10f5165d7fd7243cd Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 14:25:29 -0800 Subject: [PATCH 182/237] Ensure gtag() can be called from main thread --- .../third-party/google-site-kit.php | 9 ++++++--- .../third-party/seo-by-rank-math.php | 5 ++++- .../web-worker-offloading/third-party/woocommerce.php | 9 +++++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/plugins/web-worker-offloading/third-party/google-site-kit.php b/plugins/web-worker-offloading/third-party/google-site-kit.php index a10b4d9034..535c9cd923 100644 --- a/plugins/web-worker-offloading/third-party/google-site-kit.php +++ b/plugins/web-worker-offloading/third-party/google-site-kit.php @@ -23,9 +23,12 @@ function plwwo_google_site_kit_configure( $configuration ): array { $configuration = (array) $configuration; - $configuration['globalFns'][] = 'gtag'; // Because gtag() is defined in one script and called in another. - $configuration['globalFns'][] = 'wp_has_consent'; // See . - $configuration['forward'][] = 'dataLayer.push'; // See . + $configuration['globalFns'][] = 'gtag'; // Allow calling from other Partytown scripts. + $configuration['globalFns'][] = 'wp_has_consent'; // Allow calling function from main thread. See . + + // Expose on the main tread. See . + $configuration['forward'][] = 'dataLayer.push'; + $configuration['forward'][] = 'gtag'; // See , // and . diff --git a/plugins/web-worker-offloading/third-party/seo-by-rank-math.php b/plugins/web-worker-offloading/third-party/seo-by-rank-math.php index 0ad2bebd20..7436e924bb 100644 --- a/plugins/web-worker-offloading/third-party/seo-by-rank-math.php +++ b/plugins/web-worker-offloading/third-party/seo-by-rank-math.php @@ -24,7 +24,10 @@ function plwwo_rank_math_configure( $configuration ): array { $configuration = (array) $configuration; $configuration['globalFns'][] = 'gtag'; // Because gtag() is defined in one script and called in another. - $configuration['forward'][] = 'dataLayer.push'; // See . + + // Expose on the main tread. See . + $configuration['forward'][] = 'dataLayer.push'; + $configuration['forward'][] = 'gtag'; return $configuration; } add_filter( 'plwwo_configuration', 'plwwo_rank_math_configure' ); diff --git a/plugins/web-worker-offloading/third-party/woocommerce.php b/plugins/web-worker-offloading/third-party/woocommerce.php index d94dbb7f62..749d44d4a9 100644 --- a/plugins/web-worker-offloading/third-party/woocommerce.php +++ b/plugins/web-worker-offloading/third-party/woocommerce.php @@ -23,10 +23,15 @@ function plwwo_woocommerce_configure( $configuration ): array { $configuration = (array) $configuration; + $configuration['globalFns'][] = 'gtag'; // Allow calling from other Partytown scripts. + + // Expose on the main tread. See . + $configuration['forward'][] = 'dataLayer.push'; + $configuration['forward'][] = 'gtag'; + $configuration['mainWindowAccessors'][] = 'wp'; // Because woocommerce-google-analytics-integration needs to access wp.i18n. $configuration['mainWindowAccessors'][] = 'ga4w'; // Because woocommerce-google-analytics-integration needs to access window.ga4w. - $configuration['globalFns'][] = 'gtag'; // Because gtag() is defined in one script and called in another. - $configuration['forward'][] = 'dataLayer.push'; // Because the Partytown integration has this in its example config. + return $configuration; } add_filter( 'plwwo_configuration', 'plwwo_woocommerce_configure' ); From 8c493595cb643f9be94d5fa2501b89ea7596c9bb Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 15:16:33 -0800 Subject: [PATCH 183/237] Fix tracking events like add_to_cart in WooCommerce integration with WWO --- plugins/web-worker-offloading/third-party/woocommerce.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/plugins/web-worker-offloading/third-party/woocommerce.php b/plugins/web-worker-offloading/third-party/woocommerce.php index 749d44d4a9..4ab54b9664 100644 --- a/plugins/web-worker-offloading/third-party/woocommerce.php +++ b/plugins/web-worker-offloading/third-party/woocommerce.php @@ -29,17 +29,14 @@ function plwwo_woocommerce_configure( $configuration ): array { $configuration['forward'][] = 'dataLayer.push'; $configuration['forward'][] = 'gtag'; - $configuration['mainWindowAccessors'][] = 'wp'; // Because woocommerce-google-analytics-integration needs to access wp.i18n. - $configuration['mainWindowAccessors'][] = 'ga4w'; // Because woocommerce-google-analytics-integration needs to access window.ga4w. - return $configuration; } add_filter( 'plwwo_configuration', 'plwwo_woocommerce_configure' ); plwwo_mark_scripts_for_offloading( + // Note: 'woocommerce-google-analytics-integration' is intentionally not included because for some reason events like add_to_cart don't get tracked. array( 'google-tag-manager', - 'woocommerce-google-analytics-integration', 'woocommerce-google-analytics-integration-gtag', ) ); From 8325cb4ffe7e2c1e1db38189a60a57180720fec5 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 15:49:35 -0800 Subject: [PATCH 184/237] Avoid sleeping in favor of setting an old initial modified time --- .../tests/storage/test-data.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index d1ab4efc21..647c0b2dae 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -293,7 +293,14 @@ public function test_od_get_url_metrics_slug(): void { */ public function test_od_get_current_url_metrics_etag(): void { remove_all_filters( 'od_current_url_metrics_etag_data' ); - $post_ids = self::factory()->post->create_many( 3 ); + $force_old_post_modified_data = static function ( $data ) { + $data['post_modified'] = '1970-01-01 00:00:00'; + $data['post_modified_gmt'] = '1970-01-01 00:00:00'; + return $data; + }; + add_filter( 'wp_insert_post_data', $force_old_post_modified_data ); + $post_ids = self::factory()->post->create_many( 3 ); + remove_filter( 'wp_insert_post_data', $force_old_post_modified_data ); $registry = new OD_Tag_Visitor_Registry(); $wp_the_query = new WP_Query(); $current_template = 'index.php'; @@ -358,13 +365,13 @@ static function ( $data ) { // Add new post. $post_ids[] = self::factory()->post->create(); + $wp_the_query->query( array() ); $etag3 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); $this->assertNotEquals( $etag2, $etag3 ); // Etag should change. $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); // Update the post content. - usleep( 1000000 ); // Sleep for 1 second to ensure the post_modified_gmt is different from the previous value. wp_update_post( array( 'ID' => $post_ids[0], From 1b3ba00be4c62c0499bacad7c2d823e7253c02ca Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 15:50:32 -0800 Subject: [PATCH 185/237] Reuse post_modified_gmt in queried_object --- plugins/optimization-detective/storage/data.php | 11 +++++------ .../tests/storage/test-data.php | 15 ++++++--------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 9391e695cf..8f26700dc5 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -158,15 +158,14 @@ function od_get_url_metrics_slug( array $query_vars ): string { function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_registry, WP_Query $wp_query, $current_template ): string { $queried_object = $wp_query->get_queried_object(); $queried_object_data = array( - 'id' => null, - 'type' => null, - 'last_modified' => null, + 'id' => null, + 'type' => null, ); if ( $queried_object instanceof WP_Post ) { - $queried_object_data['id'] = $queried_object->ID; - $queried_object_data['type'] = 'post'; - $queried_object_data['last_modified'] = $queried_object->post_modified_gmt; + $queried_object_data['id'] = $queried_object->ID; + $queried_object_data['type'] = 'post'; + $queried_object_data['post_modified_gmt'] = $queried_object->post_modified_gmt; } elseif ( $queried_object instanceof WP_Term ) { $queried_object_data['id'] = $queried_object->term_id; $queried_object_data['type'] = 'term'; diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index 647c0b2dae..512dada245 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -342,9 +342,8 @@ static function ( $data ) { array( 'tag_visitors' => array(), 'queried_object' => array( - 'id' => null, - 'type' => null, - 'last_modified' => null, + 'id' => null, + 'type' => null, ), 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( @@ -394,9 +393,8 @@ static function ( $data ) { array( 'tag_visitors' => array( 'foo', 'bar', 'baz' ), 'queried_object' => array( - 'id' => null, - 'type' => null, - 'last_modified' => null, + 'id' => null, + 'type' => null, ), 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( @@ -430,9 +428,8 @@ static function ( $data ) { array( 'tag_visitors' => array( 'foo', 'bar', 'baz' ), 'queried_object' => array( - 'id' => null, - 'type' => null, - 'last_modified' => null, + 'id' => null, + 'type' => null, ), 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), 'active_theme' => array( From a71fe7620e1f0f0e490bd576333e8719db94a3d9 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Fri, 13 Dec 2024 16:12:01 -0800 Subject: [PATCH 186/237] Reuse active theme in test and reduce code duplication --- .../tests/storage/test-data.php | 81 +++++-------------- 1 file changed, 21 insertions(+), 60 deletions(-) diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index 512dada245..3ddc928a27 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -292,7 +292,17 @@ public function test_od_get_url_metrics_slug(): void { * @covers ::od_get_current_url_metrics_etag */ public function test_od_get_current_url_metrics_etag(): void { - remove_all_filters( 'od_current_url_metrics_etag_data' ); + $initial_active_theme = array( + 'template' => array( + 'name' => get_template(), + 'version' => wp_get_theme( get_template() )->get( 'Version' ), + ), + 'stylesheet' => array( + 'name' => get_stylesheet(), + 'version' => wp_get_theme( get_stylesheet() )->get( 'Version' ), + ), + ); + $force_old_post_modified_data = static function ( $data ) { $data['post_modified'] = '1970-01-01 00:00:00'; $data['post_modified_gmt'] = '1970-01-01 00:00:00'; @@ -301,6 +311,7 @@ public function test_od_get_current_url_metrics_etag(): void { add_filter( 'wp_insert_post_data', $force_old_post_modified_data ); $post_ids = self::factory()->post->create_many( 3 ); remove_filter( 'wp_insert_post_data', $force_old_post_modified_data ); + $registry = new OD_Tag_Visitor_Registry(); $wp_the_query = new WP_Query(); $current_template = 'index.php'; @@ -314,22 +325,6 @@ static function ( array $data ) use ( &$captured_etag_data ) { }, PHP_INT_MAX ); - add_filter( - 'od_current_url_metrics_etag_data', - static function ( $data ) { - $data['active_theme'] = array( - 'template' => array( - 'name' => 'od-theme', - 'version' => '1.0.0', - ), - 'stylesheet' => array( - 'name' => 'od-theme', - 'version' => '1.0.0', - ), - ); - return $data; - } - ); $wp_the_query->query( array() ); $etag1 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); @@ -346,16 +341,7 @@ static function ( $data ) { 'type' => null, ), 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), - 'active_theme' => array( - 'template' => array( - 'name' => 'od-theme', - 'version' => '1.0.0', - ), - 'stylesheet' => array( - 'name' => 'od-theme', - 'version' => '1.0.0', - ), - ), + 'active_theme' => $initial_active_theme, 'current_template' => 'index.php', ), $captured_etag_data[0] @@ -397,27 +383,20 @@ static function ( $data ) { 'type' => null, ), 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), - 'active_theme' => array( - 'template' => array( - 'name' => 'od-theme', - 'version' => '1.0.0', - ), - 'stylesheet' => array( - 'name' => 'od-theme', - 'version' => '1.0.0', - ), - ), + 'active_theme' => $initial_active_theme, 'current_template' => 'index.php', ), $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); // Modify data using filters. + $active_theme_override = $initial_active_theme; + $active_theme_override['template']['version'] = '2.0.0'; + $active_theme_override['stylesheet']['version'] = '2.0.0'; add_filter( 'od_current_url_metrics_etag_data', - static function ( $data ) { - $data['active_theme']['template']['version'] = '2.0.0'; - $data['active_theme']['stylesheet']['version'] = '2.0.0'; + static function ( $data ) use ( $active_theme_override ) { + $data['active_theme'] = $active_theme_override; return $data; } ); @@ -432,16 +411,7 @@ static function ( $data ) { 'type' => null, ), 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), - 'active_theme' => array( - 'template' => array( - 'name' => 'od-theme', - 'version' => '2.0.0', - ), - 'stylesheet' => array( - 'name' => 'od-theme', - 'version' => '2.0.0', - ), - ), + 'active_theme' => $active_theme_override, 'current_template' => 'index.php', ), $captured_etag_data[ count( $captured_etag_data ) - 1 ] @@ -468,16 +438,7 @@ static function ( $data ): array { 'last_modified' => '2024-03-02T01:00:00', ), 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), - 'active_theme' => array( - 'template' => array( - 'name' => 'od-theme', - 'version' => '2.0.0', - ), - 'stylesheet' => array( - 'name' => 'od-theme', - 'version' => '2.0.0', - ), - ), + 'active_theme' => $active_theme_override, 'current_template' => 'index.php', ), $captured_etag_data[ count( $captured_etag_data ) - 1 ] From 03ef8c86724055ab55db45eb7bf396b05ba755a0 Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Sat, 14 Dec 2024 07:03:37 +0530 Subject: [PATCH 187/237] Align post type archive check with other `instanceof` validations Co-authored-by: Weston Ruter --- plugins/optimization-detective/storage/data.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 8f26700dc5..495807094a 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -172,8 +172,8 @@ function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_r } elseif ( $queried_object instanceof WP_User ) { $queried_object_data['id'] = $queried_object->ID; $queried_object_data['type'] = 'user'; - } elseif ( $wp_query->is_post_type_archive() ) { - $queried_object_data['type'] = $wp_query->get( 'post_type' ); + } elseif ( $queried_object instanceof WP_Post_Type ) { + $queried_object_data['type'] = $queried_object->name; } $data = array( From 73edbdb02125ad75113b3fcaed1be318711ac088 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 14 Dec 2024 20:18:42 -0800 Subject: [PATCH 188/237] Add helper od_get_current_theme_template() function --- .../optimization-detective/optimization.php | 16 +++--------- .../optimization-detective/storage/data.php | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/plugins/optimization-detective/optimization.php b/plugins/optimization-detective/optimization.php index ea6241df93..74548eef12 100644 --- a/plugins/optimization-detective/optimization.php +++ b/plugins/optimization-detective/optimization.php @@ -170,15 +170,13 @@ function od_is_response_html_content_type(): bool { * @since 0.1.0 * @access private * - * @global WP_Query $wp_the_query WP_Query object. - * @global string $_wp_current_template_id Current template ID. - * @global string $template Template file path. + * @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, $_wp_current_template_id, $template; + 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 ( @@ -212,15 +210,7 @@ function od_optimize_template_output_buffer( string $buffer ): string { */ do_action( 'od_register_tag_visitors', $tag_visitor_registry ); - $current_template = null; - if ( wp_is_block_theme() && isset( $_wp_current_template_id ) ) { - $current_template = get_block_template( $_wp_current_template_id, 'wp_template' ); - } - if ( null === $current_template && isset( $template ) && is_string( $template ) ) { - $current_template = basename( $template ); - } - - $current_etag = od_get_current_url_metrics_etag( $tag_visitor_registry, $wp_the_query, $current_template ); + $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, diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 495807094a..1abbf3bb6a 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -140,6 +140,32 @@ function od_get_url_metrics_slug( array $query_vars ): string { return md5( (string) wp_json_encode( $query_vars ) ); } +/** + * Gets the current template for a block theme or a classic theme. + * + * @since n.e.x.t + * @access private + * + * @global string|null $_wp_current_template_id Current template ID. + * @global string|null $template Template file path. + * + * @return string|WP_Block_Template|null Template. + */ +function od_get_current_theme_template() { + global $template, $_wp_current_template_id; + + if ( wp_is_block_theme() && isset( $_wp_current_template_id ) ) { + $block_template = get_block_template( $_wp_current_template_id, 'wp_template' ); + if ( $block_template instanceof WP_Block_Template ) { + return $block_template; + } + } + if ( isset( $template ) && is_string( $template ) ) { + return basename( $template ); + } + return null; +} + /** * Gets the current ETag for URL Metrics. * From 55d56d330470b5ab480bd281d7b406d21d561961 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 14 Dec 2024 20:20:40 -0800 Subject: [PATCH 189/237] Refactor queried_posts data to have better guaranteed shape --- .../optimization-detective/storage/data.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 1abbf3bb6a..0dabf8c640 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -205,7 +205,23 @@ function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_r $data = array( 'tag_visitors' => array_keys( iterator_to_array( $tag_visitor_registry ) ), 'queried_object' => $queried_object_data, - 'queried_posts' => wp_list_pluck( $wp_query->posts, 'post_modified_gmt', 'ID' ), + 'queried_posts' => array_filter( + array_map( + static function ( $post ): ?array { + if ( is_int( $post ) ) { + $post = get_post( $post ); + } + if ( ! ( $post instanceof WP_Post ) ) { + return null; + } + return array( + 'id' => $post->ID, + 'post_modified_gmt' => $post->post_modified_gmt, + ); + }, + 0 === $wp_query->post_count ? array() : $wp_query->posts // Needed in case WP_Query has not been initialized. + ) + ), 'active_theme' => array( 'template' => array( 'name' => get_template(), From 63bb613f092dbbf15e68253fc1f6c930f3b46e0f Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 14 Dec 2024 20:21:20 -0800 Subject: [PATCH 190/237] Avoid adding WP_Block_Template object directly to ETag data in favor of converting to array --- plugins/optimization-detective/storage/data.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 0dabf8c640..1531fae36c 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -232,7 +232,7 @@ static function ( $post ): ?array { 'version' => wp_get_theme()->get( 'Version' ), ), ), - 'current_template' => $current_template, + 'current_template' => $current_template instanceof WP_Block_Template ? get_object_vars( $current_template ) : $current_template, ); /** From 18e047691b267f2dcf15a1384e90137f07fdf378 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 14 Dec 2024 20:23:17 -0800 Subject: [PATCH 191/237] Refactor od_get_current_url_metrics_etag to use data provider and add complete test coverage --- .../tests/data/themes/block-theme/style.css | 7 + .../themes/block-theme/templates/index.html | 0 .../tests/storage/test-data.php | 358 +++++++++++------- .../tests/test-helper.php | 2 + 4 files changed, 233 insertions(+), 134 deletions(-) create mode 100644 plugins/optimization-detective/tests/data/themes/block-theme/style.css create mode 100644 plugins/optimization-detective/tests/data/themes/block-theme/templates/index.html diff --git a/plugins/optimization-detective/tests/data/themes/block-theme/style.css b/plugins/optimization-detective/tests/data/themes/block-theme/style.css new file mode 100644 index 0000000000..72f24c1672 --- /dev/null +++ b/plugins/optimization-detective/tests/data/themes/block-theme/style.css @@ -0,0 +1,7 @@ +/* +Theme Name: Block Theme +Theme URI: https://wordpress.org/ +Description: For testing purposes only. +Version: 1.0.0 +Text Domain: block-theme +*/ diff --git a/plugins/optimization-detective/tests/data/themes/block-theme/templates/index.html b/plugins/optimization-detective/tests/data/themes/block-theme/templates/index.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index 3ddc928a27..31bf55c42e 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -22,6 +22,8 @@ public function set_up(): void { public function tear_down(): void { $_SERVER['REQUEST_URI'] = $this->original_request_uri; unset( $GLOBALS['wp_customize'] ); + unset( $GLOBALS['template'] ); + unset( $GLOBALS['_wp_current_template_id'] ); parent::tear_down(); } @@ -287,162 +289,250 @@ public function test_od_get_url_metrics_slug(): void { } /** - * Test od_get_current_url_metrics_etag(). + * Data provider. * - * @covers ::od_get_current_url_metrics_etag + * @return array */ - public function test_od_get_current_url_metrics_etag(): void { - $initial_active_theme = array( - 'template' => array( - 'name' => get_template(), - 'version' => wp_get_theme( get_template() )->get( 'Version' ), + public function data_provider_test_od_get_current_url_metrics_etag(): array { + return array( + 'homepage_one_post' => array( + 'set_up' => function (): Closure { + $post = self::factory()->post->create_and_get(); + $this->assertInstanceOf( WP_Post::class, $post ); + $this->go_to( '/' ); + $GLOBALS['template'] = 'home.php'; + + return function ( array $etag_data, Closure $get_etag ) use ( $post ): void { + $this->assertNull( $etag_data['queried_object']['id'] ); + $this->assertNull( $etag_data['queried_object']['type'] ); + $this->assertCount( 1, $etag_data['queried_posts'] ); + $this->assertSame( $post->ID, $etag_data['queried_posts'][0]['id'] ); + $this->assertSame( $post->post_modified_gmt, $etag_data['queried_posts'][0]['post_modified_gmt'] ); + $this->assertSame( 'home.php', $etag_data['current_template'] ); + + // Modify data using filters. + $etag = $get_etag(); + add_filter( + 'od_current_url_metrics_etag_data', + static function ( $data ) { + $data['custom'] = true; + return $data; + } + ); + $etag_after_filtering = $get_etag(); + $this->assertNotEquals( $etag, $etag_after_filtering ); + }; + }, ), - 'stylesheet' => array( - 'name' => get_stylesheet(), - 'version' => wp_get_theme( get_stylesheet() )->get( 'Version' ), + + 'singular_post_then_modified' => array( + 'set_up' => function (): Closure { + $force_old_post_modified_data = static function ( $data ) { + $data['post_modified'] = '1970-01-01 00:00:00'; + $data['post_modified_gmt'] = '1970-01-01 00:00:00'; + return $data; + }; + add_filter( 'wp_insert_post_data', $force_old_post_modified_data ); + $post = self::factory()->post->create_and_get(); + $this->assertInstanceOf( WP_Post::class, $post ); + remove_filter( 'wp_insert_post_data', $force_old_post_modified_data ); + $this->go_to( get_permalink( $post ) ); + $GLOBALS['template'] = 'single.php'; + + return function ( array $etag_data, Closure $get_etag ) use ( $post ): void { + $this->assertSame( $post->ID, $etag_data['queried_object']['id'] ); + $this->assertSame( 'post', $etag_data['queried_object']['type'] ); + $this->assertArrayHasKey( 'post_modified_gmt', $etag_data['queried_object'] ); + $this->assertSame( $post->post_modified_gmt, $etag_data['queried_object']['post_modified_gmt'] ); + $this->assertCount( 1, $etag_data['queried_posts'] ); + $this->assertSame( $post->ID, $etag_data['queried_posts'][0]['id'] ); + $this->assertSame( $post->post_modified_gmt, $etag_data['queried_posts'][0]['post_modified_gmt'] ); + $this->assertSame( 'single.php', $etag_data['current_template'] ); + + // Now try updating the post and re-navigating to it to verify that the modified date changes the ETag. + $previous_etag = $get_etag(); + $r = wp_update_post( + array( + 'ID' => $post->ID, + 'post_title' => 'Modified Title!', + ), + true + ); + $this->assertIsInt( $r ); + $this->go_to( get_permalink( $post ) ); + $next_etag = $get_etag(); + $this->assertNotSame( $previous_etag, $next_etag ); + }; + }, ), - ); - $force_old_post_modified_data = static function ( $data ) { - $data['post_modified'] = '1970-01-01 00:00:00'; - $data['post_modified_gmt'] = '1970-01-01 00:00:00'; - return $data; - }; - add_filter( 'wp_insert_post_data', $force_old_post_modified_data ); - $post_ids = self::factory()->post->create_many( 3 ); - remove_filter( 'wp_insert_post_data', $force_old_post_modified_data ); + 'category_archive' => array( + 'set_up' => function (): Closure { + $term = self::factory()->category->create_and_get(); + $this->assertInstanceOf( WP_Term::class, $term ); + $post_ids = self::factory()->post->create_many( 2 ); + foreach ( $post_ids as $post_id ) { + wp_set_post_terms( $post_id, array( $term->term_id ), 'category' ); + } + $this->go_to( get_category_link( $term ) ); + $GLOBALS['template'] = 'category.php'; + + return function ( array $etag_data ) use ( $term, $post_ids ): void { + $this->assertSame( $term->term_id, $etag_data['queried_object']['id'] ); + $this->assertSame( 'term', $etag_data['queried_object']['type'] ); + $this->assertCount( 2, $etag_data['queried_posts'] ); + $this->assertEqualSets( $post_ids, wp_list_pluck( $etag_data['queried_posts'], 'id' ) ); + $this->assertSame( 'category.php', $etag_data['current_template'] ); + }; + }, + ), + + 'user_archive' => array( + 'set_up' => function (): Closure { + $user = self::factory()->user->create_and_get(); + $this->assertInstanceOf( WP_User::class, $user ); + $post_ids = self::factory()->post->create_many( 3, array( 'post_author' => $user->ID ) ); + $this->go_to( get_author_posts_url( $user->ID ) ); + $GLOBALS['template'] = 'author.php'; + + return function ( array $etag_data ) use ( $user, $post_ids ): void { + $this->assertSame( $user->ID, $etag_data['queried_object']['id'] ); + $this->assertSame( 'user', $etag_data['queried_object']['type'] ); + $this->assertCount( 3, $etag_data['queried_posts'] ); + $this->assertEqualSets( $post_ids, wp_list_pluck( $etag_data['queried_posts'], 'id' ) ); + $this->assertSame( 'author.php', $etag_data['current_template'] ); + }; + }, + ), + + 'post_type_archive' => array( + 'set_up' => function (): Closure { + register_post_type( + 'book', + array( + 'public' => true, + 'has_archive' => true, + ) + ); + $post_ids = self::factory()->post->create_many( 4, array( 'post_type' => 'book' ) ); + $this->go_to( get_post_type_archive_link( 'book' ) ); + $GLOBALS['template'] = 'archive-book.php'; + + return function ( array $etag_data ) use ( $post_ids ): void { + $this->assertNull( $etag_data['queried_object']['id'] ); + $this->assertSame( 'book', $etag_data['queried_object']['type'] ); + $this->assertCount( 4, $etag_data['queried_posts'] ); + $this->assertEqualSets( $post_ids, wp_list_pluck( $etag_data['queried_posts'], 'id' ) ); + $this->assertSame( 'archive-book.php', $etag_data['current_template'] ); + }; + }, + ), + + 'page_for_posts' => array( + 'set_up' => function (): Closure { + $page_id = self::factory()->post->create( array( 'post_type' => 'page' ) ); + update_option( 'show_on_front', 'page' ); + update_option( 'page_for_posts', $page_id ); + + $post_ids = self::factory()->post->create_many( 5 ); + $this->go_to( get_page_link( $page_id ) ); + $GLOBALS['template'] = 'home.php'; + + return function ( array $etag_data ) use ( $page_id, $post_ids ): void { + $this->assertSame( $page_id, $etag_data['queried_object']['id'] ); + $this->assertSame( 'post', $etag_data['queried_object']['type'] ); + $this->assertCount( 5, $etag_data['queried_posts'] ); + $this->assertEqualSets( $post_ids, wp_list_pluck( $etag_data['queried_posts'], 'id' ) ); + $this->assertSame( 'home.php', $etag_data['current_template'] ); + }; + }, + ), - $registry = new OD_Tag_Visitor_Registry(); - $wp_the_query = new WP_Query(); - $current_template = 'index.php'; + 'block_theme' => array( + 'set_up' => function (): Closure { + self::factory()->post->create(); + register_theme_directory( __DIR__ . '/../data/themes' ); + update_option( 'template', 'block-theme' ); + update_option( 'stylesheet', 'block-theme' ); + $this->go_to( '/' ); + $GLOBALS['_wp_current_template_id'] = 'block-theme//index'; + + return function ( array $etag_data ): void { + $this->assertTrue( wp_is_block_theme() ); + $this->assertIsArray( $etag_data['current_template'] ); + $this->assertEquals( 'wp_template', $etag_data['current_template']['type'] ); + $this->assertEquals( 'block-theme//index', $etag_data['current_template']['id'] ); + $this->assertArrayHasKey( 'modified', $etag_data['current_template'] ); + }; + }, + ), + ); + } - $captured_etag_data = array(); + /** + * Test od_get_current_url_metrics_etag(). + * + * @dataProvider data_provider_test_od_get_current_url_metrics_etag + * + * @covers ::od_get_current_url_metrics_etag + * @covers ::od_get_current_theme_template + */ + public function test_od_get_current_url_metrics_etag( Closure $set_up ): void { + global $template; + $template = 'index.php'; + + $captured_etag_data = null; add_filter( 'od_current_url_metrics_etag_data', static function ( array $data ) use ( &$captured_etag_data ) { - $captured_etag_data[] = $data; + $captured_etag_data = $data; return $data; }, PHP_INT_MAX ); - $wp_the_query->query( array() ); - $etag1 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); - $this->assertMatchesRegularExpression( '/^[a-z0-9]{32}\z/', $etag1 ); - - $etag2 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); - $this->assertSame( $etag1, $etag2 ); - $this->assertCount( 2, $captured_etag_data ); - $this->assertSame( - array( - 'tag_visitors' => array(), - 'queried_object' => array( - 'id' => null, - 'type' => null, - ), - 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), - 'active_theme' => $initial_active_theme, - 'current_template' => 'index.php', - ), - $captured_etag_data[0] - ); - $this->assertSame( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); - - // Add new post. - $post_ids[] = self::factory()->post->create(); - - $wp_the_query->query( array() ); - $etag3 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); - $this->assertNotEquals( $etag2, $etag3 ); // Etag should change. - $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); - - // Update the post content. - wp_update_post( - array( - 'ID' => $post_ids[0], - 'post_content' => 'Updated post content', - ) - ); - $wp_the_query->query( array() ); - $etag4 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); - $this->assertNotEquals( $etag3, $etag4 ); - $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); - - // Register new tag visitors. + $registry = new OD_Tag_Visitor_Registry(); $registry->register( 'foo', static function (): void {} ); $registry->register( 'bar', static function (): void {} ); $registry->register( 'baz', static function (): void {} ); - $etag5 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); - $this->assertNotEquals( $etag4, $etag5 ); - $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); - $this->assertSame( - array( - 'tag_visitors' => array( 'foo', 'bar', 'baz' ), - 'queried_object' => array( - 'id' => null, - 'type' => null, - ), - 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), - 'active_theme' => $initial_active_theme, - 'current_template' => 'index.php', - ), - $captured_etag_data[ count( $captured_etag_data ) - 1 ] - ); + $get_etag = static function () use ( $registry ) { + global $wp_the_query; + return od_get_current_url_metrics_etag( $registry, $wp_the_query, od_get_current_theme_template() ); + }; - // Modify data using filters. - $active_theme_override = $initial_active_theme; - $active_theme_override['template']['version'] = '2.0.0'; - $active_theme_override['stylesheet']['version'] = '2.0.0'; - add_filter( - 'od_current_url_metrics_etag_data', - static function ( $data ) use ( $active_theme_override ) { - $data['active_theme'] = $active_theme_override; - return $data; - } - ); - $etag6 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); - $this->assertNotEquals( $etag5, $etag6 ); - $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); - $this->assertSame( - array( - 'tag_visitors' => array( 'foo', 'bar', 'baz' ), - 'queried_object' => array( - 'id' => null, - 'type' => null, - ), - 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), - 'active_theme' => $active_theme_override, - 'current_template' => 'index.php', - ), - $captured_etag_data[ count( $captured_etag_data ) - 1 ] - ); + $extra_assert = $set_up(); - add_filter( - 'od_current_url_metrics_etag_data', - static function ( $data ): array { - $data['queried_object'] = array( - 'ID' => 99, - 'last_modified' => '2024-03-02T01:00:00', - ); - return $data; - } - ); - $etag7 = od_get_current_url_metrics_etag( $registry, $wp_the_query, $current_template ); - $this->assertNotEquals( $etag6, $etag7 ); - $this->assertNotEquals( $captured_etag_data[ count( $captured_etag_data ) - 2 ], $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); - $this->assertSame( - array( - 'tag_visitors' => array( 'foo', 'bar', 'baz' ), - 'queried_object' => array( - 'ID' => 99, - 'last_modified' => '2024-03-02T01:00:00', - ), - 'queried_posts' => wp_list_pluck( $wp_the_query->posts, 'post_modified_gmt', 'ID' ), - 'active_theme' => $active_theme_override, - 'current_template' => 'index.php', + $initial_active_theme = array( + 'template' => array( + 'name' => get_template(), + 'version' => wp_get_theme( get_template() )->get( 'Version' ), + ), + 'stylesheet' => array( + 'name' => get_stylesheet(), + 'version' => wp_get_theme( get_stylesheet() )->get( 'Version' ), ), - $captured_etag_data[ count( $captured_etag_data ) - 1 ] ); + + $etag = $get_etag(); + $this->assertMatchesRegularExpression( '/^[a-z0-9]{32}\z/', $etag ); + $this->assertIsArray( $captured_etag_data ); + $expected_keys = array( 'tag_visitors', 'queried_object', 'queried_posts', 'active_theme', 'current_template' ); + foreach ( $expected_keys as $expected_key ) { + $this->assertArrayHasKey( $expected_key, $captured_etag_data ); + } + $this->assertSame( $initial_active_theme, $captured_etag_data['active_theme'] ); + $this->assertContains( 'foo', $captured_etag_data['tag_visitors'] ); + $this->assertContains( 'bar', $captured_etag_data['tag_visitors'] ); + $this->assertContains( 'baz', $captured_etag_data['tag_visitors'] ); + $this->assertArrayHasKey( 'id', $captured_etag_data['queried_object'] ); + $this->assertArrayHasKey( 'type', $captured_etag_data['queried_object'] ); + $previous_captured_etag_data = $captured_etag_data; + $this->assertSame( $etag, $get_etag() ); + $this->assertSame( $captured_etag_data, $previous_captured_etag_data ); + + if ( $extra_assert instanceof Closure ) { + $extra_assert( $captured_etag_data, $get_etag ); + } } /** diff --git a/plugins/optimization-detective/tests/test-helper.php b/plugins/optimization-detective/tests/test-helper.php index c28dfdd306..42bcf62dd8 100644 --- a/plugins/optimization-detective/tests/test-helper.php +++ b/plugins/optimization-detective/tests/test-helper.php @@ -25,6 +25,8 @@ static function ( string $version ) use ( &$passed_version ): void { } /** + * Data provider. + * * @return array> */ public function data_to_test_od_generate_media_query(): array { From 57a0ba6bb269fef07d2f2e51d828af1e50d9698b Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 14 Dec 2024 20:33:36 -0800 Subject: [PATCH 192/237] Remove unnecessary setting of template global --- .../tests/test-optimization-detective.php | 9 --------- plugins/image-prioritizer/tests/test-helper.php | 9 --------- 2 files changed, 18 deletions(-) diff --git a/plugins/embed-optimizer/tests/test-optimization-detective.php b/plugins/embed-optimizer/tests/test-optimization-detective.php index 61ac7e2b4e..9994859e1d 100644 --- a/plugins/embed-optimizer/tests/test-optimization-detective.php +++ b/plugins/embed-optimizer/tests/test-optimization-detective.php @@ -18,7 +18,6 @@ public function set_up(): void { if ( ! defined( 'OPTIMIZATION_DETECTIVE_VERSION' ) ) { $this->markTestSkipped( 'Optimization Detective is not active.' ); } - $GLOBALS['template'] = '/path/to/theme/index.php'; // Normalize the data for computing the current URL Metrics ETag to work around the issue where there is no // global variable storing the OD_Tag_Visitor_Registry instance along with any registered tag visitors, so @@ -27,14 +26,6 @@ public function set_up(): void { add_filter( 'od_current_url_metrics_etag_data', '__return_empty_array' ); } - /** - * Runs the routine after each test is executed. - */ - public function tear_down(): void { - unset( $GLOBALS['template'] ); - parent::tear_down(); - } - /** * Tests embed_optimizer_register_tag_visitors(). * diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index 3bd02d68d2..180a2d807f 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -15,7 +15,6 @@ class Test_Image_Prioritizer_Helper extends WP_UnitTestCase { */ public function set_up(): void { parent::set_up(); - $GLOBALS['template'] = '/path/to/theme/index.php'; // Normalize the data for computing the current URL Metrics ETag to work around the issue where there is no // global variable storing the OD_Tag_Visitor_Registry instance along with any registered tag visitors, so @@ -24,14 +23,6 @@ public function set_up(): void { add_filter( 'od_current_url_metrics_etag_data', '__return_empty_array' ); } - /** - * Runs the routine after each test is executed. - */ - public function tear_down(): void { - unset( $GLOBALS['template'] ); - parent::tear_down(); - } - /** * @return array> */ From 804d71e1bde1c39f3177e609243aa294f6bd4ae3 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 14 Dec 2024 20:34:34 -0800 Subject: [PATCH 193/237] Set absolute path to template --- .../tests/storage/test-data.php | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index 31bf55c42e..70e30e108c 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -300,7 +300,7 @@ public function data_provider_test_od_get_current_url_metrics_etag(): array { $post = self::factory()->post->create_and_get(); $this->assertInstanceOf( WP_Post::class, $post ); $this->go_to( '/' ); - $GLOBALS['template'] = 'home.php'; + $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'home.php'; return function ( array $etag_data, Closure $get_etag ) use ( $post ): void { $this->assertNull( $etag_data['queried_object']['id'] ); @@ -337,7 +337,7 @@ static function ( $data ) { $this->assertInstanceOf( WP_Post::class, $post ); remove_filter( 'wp_insert_post_data', $force_old_post_modified_data ); $this->go_to( get_permalink( $post ) ); - $GLOBALS['template'] = 'single.php'; + $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'single.php'; return function ( array $etag_data, Closure $get_etag ) use ( $post ): void { $this->assertSame( $post->ID, $etag_data['queried_object']['id'] ); @@ -375,7 +375,7 @@ static function ( $data ) { wp_set_post_terms( $post_id, array( $term->term_id ), 'category' ); } $this->go_to( get_category_link( $term ) ); - $GLOBALS['template'] = 'category.php'; + $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'category.php'; return function ( array $etag_data ) use ( $term, $post_ids ): void { $this->assertSame( $term->term_id, $etag_data['queried_object']['id'] ); @@ -393,7 +393,7 @@ static function ( $data ) { $this->assertInstanceOf( WP_User::class, $user ); $post_ids = self::factory()->post->create_many( 3, array( 'post_author' => $user->ID ) ); $this->go_to( get_author_posts_url( $user->ID ) ); - $GLOBALS['template'] = 'author.php'; + $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'author.php'; return function ( array $etag_data ) use ( $user, $post_ids ): void { $this->assertSame( $user->ID, $etag_data['queried_object']['id'] ); @@ -416,7 +416,7 @@ static function ( $data ) { ); $post_ids = self::factory()->post->create_many( 4, array( 'post_type' => 'book' ) ); $this->go_to( get_post_type_archive_link( 'book' ) ); - $GLOBALS['template'] = 'archive-book.php'; + $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'archive-book.php'; return function ( array $etag_data ) use ( $post_ids ): void { $this->assertNull( $etag_data['queried_object']['id'] ); @@ -436,7 +436,7 @@ static function ( $data ) { $post_ids = self::factory()->post->create_many( 5 ); $this->go_to( get_page_link( $page_id ) ); - $GLOBALS['template'] = 'home.php'; + $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'home.php'; return function ( array $etag_data ) use ( $page_id, $post_ids ): void { $this->assertSame( $page_id, $etag_data['queried_object']['id'] ); @@ -478,9 +478,6 @@ static function ( $data ) { * @covers ::od_get_current_theme_template */ public function test_od_get_current_url_metrics_etag( Closure $set_up ): void { - global $template; - $template = 'index.php'; - $captured_etag_data = null; add_filter( 'od_current_url_metrics_etag_data', From 8fe2f8f2ca2fce0bc6b8ca01dec73c999cd51444 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 14 Dec 2024 20:37:44 -0800 Subject: [PATCH 194/237] Make additional adjustments to template --- plugins/optimization-detective/tests/test-optimization.php | 2 -- tests/class-optimization-detective-test-helpers.php | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/optimization-detective/tests/test-optimization.php b/plugins/optimization-detective/tests/test-optimization.php index 8eb3030473..cba6a73837 100644 --- a/plugins/optimization-detective/tests/test-optimization.php +++ b/plugins/optimization-detective/tests/test-optimization.php @@ -31,7 +31,6 @@ public function set_up(): void { $this->original_request_uri = $_SERVER['REQUEST_URI']; $this->original_request_method = $_SERVER['REQUEST_METHOD']; $this->default_mimetype = (string) ini_get( 'default_mimetype' ); - $GLOBALS['template'] = '/path/to/theme/index.php'; } public function tear_down(): void { @@ -39,7 +38,6 @@ public function tear_down(): void { $_SERVER['REQUEST_METHOD'] = $this->original_request_method; ini_set( 'default_mimetype', $this->default_mimetype ); // phpcs:ignore WordPress.PHP.IniSet.Risky unset( $GLOBALS['wp_customize'] ); - unset( $GLOBALS['template'] ); parent::tear_down(); } diff --git a/tests/class-optimization-detective-test-helpers.php b/tests/class-optimization-detective-test-helpers.php index 29327826e6..8fe5dcd8b6 100644 --- a/tests/class-optimization-detective-test-helpers.php +++ b/tests/class-optimization-detective-test-helpers.php @@ -27,7 +27,7 @@ trait Optimization_Detective_Test_Helpers { */ public function populate_url_metrics( array $elements, bool $complete = true ): void { $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); - $etag = od_get_current_url_metrics_etag( new OD_Tag_Visitor_Registry(), new WP_Query(), 'index.php' ); // Note: Tests rely on the od_current_url_metrics_etag_data filter to set the desired value. + $etag = od_get_current_url_metrics_etag( new OD_Tag_Visitor_Registry(), new WP_Query(), trailingslashit( get_template_directory() ) . 'index.php' ); // Note: Tests rely on the od_current_url_metrics_etag_data filter to set the desired value. $sample_size = $complete ? od_get_url_metrics_breakpoint_sample_size() : 1; foreach ( array_merge( od_get_breakpoint_max_widths(), array( 1000 ) ) as $viewport_width ) { for ( $i = 0; $i < $sample_size; $i++ ) { @@ -81,7 +81,7 @@ public function get_sample_dom_rect(): array { public function get_sample_url_metric( array $params ): OD_URL_Metric { $params = array_merge( array( - 'etag' => od_get_current_url_metrics_etag( new OD_Tag_Visitor_Registry(), new WP_Query(), 'index.php' ), // Note: Tests rely on the od_current_url_metrics_etag_data filter to set the desired value. + 'etag' => od_get_current_url_metrics_etag( new OD_Tag_Visitor_Registry(), new WP_Query(), trailingslashit( get_template_directory() ) . 'index.php' ), // Note: Tests rely on the od_current_url_metrics_etag_data filter to set the desired value. 'url' => home_url( '/' ), 'viewport_width' => 480, 'elements' => array(), From 1869689386cf8197d83f1bd235527362b0e00c14 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 14 Dec 2024 20:59:13 -0800 Subject: [PATCH 195/237] Do not rely on WP_Query global being set --- plugins/optimization-detective/storage/data.php | 8 ++++---- tests/class-optimization-detective-test-helpers.php | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 1531fae36c..4c51c49ab9 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -177,12 +177,12 @@ function od_get_current_theme_template() { * @access private * * @param OD_Tag_Visitor_Registry $tag_visitor_registry Tag visitor registry. - * @param WP_Query $wp_query The WP_Query instance. + * @param WP_Query|null $wp_query The WP_Query instance. * @param string|WP_Block_Template|null $current_template The current template being used. * @return non-empty-string Current ETag. */ -function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_registry, WP_Query $wp_query, $current_template ): string { - $queried_object = $wp_query->get_queried_object(); +function od_get_current_url_metrics_etag( OD_Tag_Visitor_Registry $tag_visitor_registry, ?WP_Query $wp_query, $current_template ): string { + $queried_object = $wp_query instanceof WP_Query ? $wp_query->get_queried_object() : null; $queried_object_data = array( 'id' => null, 'type' => null, @@ -219,7 +219,7 @@ static function ( $post ): ?array { 'post_modified_gmt' => $post->post_modified_gmt, ); }, - 0 === $wp_query->post_count ? array() : $wp_query->posts // Needed in case WP_Query has not been initialized. + ( $wp_query instanceof WP_Query && $wp_query->post_count > 0 ) ? $wp_query->posts : array() ) ), 'active_theme' => array( diff --git a/tests/class-optimization-detective-test-helpers.php b/tests/class-optimization-detective-test-helpers.php index 8fe5dcd8b6..bfe551fed8 100644 --- a/tests/class-optimization-detective-test-helpers.php +++ b/tests/class-optimization-detective-test-helpers.php @@ -27,7 +27,7 @@ trait Optimization_Detective_Test_Helpers { */ public function populate_url_metrics( array $elements, bool $complete = true ): void { $slug = od_get_url_metrics_slug( od_get_normalized_query_vars() ); - $etag = od_get_current_url_metrics_etag( new OD_Tag_Visitor_Registry(), new WP_Query(), trailingslashit( get_template_directory() ) . 'index.php' ); // Note: Tests rely on the od_current_url_metrics_etag_data filter to set the desired value. + $etag = od_get_current_url_metrics_etag( new OD_Tag_Visitor_Registry(), null, null ); // Note: Tests rely on the od_current_url_metrics_etag_data filter to set the desired value. $sample_size = $complete ? od_get_url_metrics_breakpoint_sample_size() : 1; foreach ( array_merge( od_get_breakpoint_max_widths(), array( 1000 ) ) as $viewport_width ) { for ( $i = 0; $i < $sample_size; $i++ ) { @@ -81,7 +81,7 @@ public function get_sample_dom_rect(): array { public function get_sample_url_metric( array $params ): OD_URL_Metric { $params = array_merge( array( - 'etag' => od_get_current_url_metrics_etag( new OD_Tag_Visitor_Registry(), new WP_Query(), trailingslashit( get_template_directory() ) . 'index.php' ), // Note: Tests rely on the od_current_url_metrics_etag_data filter to set the desired value. + 'etag' => od_get_current_url_metrics_etag( new OD_Tag_Visitor_Registry(), null, null ), // Note: Tests rely on the od_current_url_metrics_etag_data filter to set the desired value. 'url' => home_url( '/' ), 'viewport_width' => 480, 'elements' => array(), From 7c0cfe173d494775cc658604facc264138fea302 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 14 Dec 2024 21:14:54 -0800 Subject: [PATCH 196/237] Work around strange user creation issue in factory --- .../tests/storage/test-data.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index 70e30e108c..107930add2 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -389,14 +389,14 @@ static function ( $data ) { 'user_archive' => array( 'set_up' => function (): Closure { - $user = self::factory()->user->create_and_get(); - $this->assertInstanceOf( WP_User::class, $user ); - $post_ids = self::factory()->post->create_many( 3, array( 'post_author' => $user->ID ) ); - $this->go_to( get_author_posts_url( $user->ID ) ); + $user_id = self::factory()->user->create(); + $this->assertIsInt( $user_id ); + $post_ids = self::factory()->post->create_many( 3, array( 'post_author' => $user_id ) ); + $this->go_to( get_author_posts_url( $user_id ) ); $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'author.php'; - return function ( array $etag_data ) use ( $user, $post_ids ): void { - $this->assertSame( $user->ID, $etag_data['queried_object']['id'] ); + return function ( array $etag_data ) use ( $user_id, $post_ids ): void { + $this->assertSame( $user_id, $etag_data['queried_object']['id'] ); $this->assertSame( 'user', $etag_data['queried_object']['type'] ); $this->assertCount( 3, $etag_data['queried_posts'] ); $this->assertEqualSets( $post_ids, wp_list_pluck( $etag_data['queried_posts'], 'id' ) ); From 1fc20aff5f5812ff7fd30795825ee563bf564b6c Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 14 Dec 2024 21:20:09 -0800 Subject: [PATCH 197/237] Add template conditional assertions --- .../optimization-detective/tests/storage/test-data.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index 107930add2..e360ed995a 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -303,6 +303,8 @@ public function data_provider_test_od_get_current_url_metrics_etag(): array { $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'home.php'; return function ( array $etag_data, Closure $get_etag ) use ( $post ): void { + $this->assertTrue( is_home() ); + $this->assertTrue( is_front_page() ); $this->assertNull( $etag_data['queried_object']['id'] ); $this->assertNull( $etag_data['queried_object']['type'] ); $this->assertCount( 1, $etag_data['queried_posts'] ); @@ -340,6 +342,7 @@ static function ( $data ) { $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'single.php'; return function ( array $etag_data, Closure $get_etag ) use ( $post ): void { + $this->assertTrue( is_single( $post ) ); $this->assertSame( $post->ID, $etag_data['queried_object']['id'] ); $this->assertSame( 'post', $etag_data['queried_object']['type'] ); $this->assertArrayHasKey( 'post_modified_gmt', $etag_data['queried_object'] ); @@ -378,6 +381,7 @@ static function ( $data ) { $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'category.php'; return function ( array $etag_data ) use ( $term, $post_ids ): void { + $this->assertTrue( is_category( $term ) ); $this->assertSame( $term->term_id, $etag_data['queried_object']['id'] ); $this->assertSame( 'term', $etag_data['queried_object']['type'] ); $this->assertCount( 2, $etag_data['queried_posts'] ); @@ -396,6 +400,7 @@ static function ( $data ) { $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'author.php'; return function ( array $etag_data ) use ( $user_id, $post_ids ): void { + $this->assertTrue( is_author( $user_id ), 'Expected is_author() after having gone to ' . get_author_posts_url( $user_id ) ); $this->assertSame( $user_id, $etag_data['queried_object']['id'] ); $this->assertSame( 'user', $etag_data['queried_object']['type'] ); $this->assertCount( 3, $etag_data['queried_posts'] ); @@ -419,6 +424,7 @@ static function ( $data ) { $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'archive-book.php'; return function ( array $etag_data ) use ( $post_ids ): void { + $this->assertTrue( is_post_type_archive( 'book' ) ); $this->assertNull( $etag_data['queried_object']['id'] ); $this->assertSame( 'book', $etag_data['queried_object']['type'] ); $this->assertCount( 4, $etag_data['queried_posts'] ); @@ -439,6 +445,8 @@ static function ( $data ) { $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'home.php'; return function ( array $etag_data ) use ( $page_id, $post_ids ): void { + $this->assertTrue( is_home() ); + $this->assertFalse( is_front_page() ); $this->assertSame( $page_id, $etag_data['queried_object']['id'] ); $this->assertSame( 'post', $etag_data['queried_object']['type'] ); $this->assertCount( 5, $etag_data['queried_posts'] ); @@ -455,6 +463,8 @@ static function ( $data ) { update_option( 'template', 'block-theme' ); update_option( 'stylesheet', 'block-theme' ); $this->go_to( '/' ); + $this->assertTrue( is_home() ); + $this->assertTrue( is_front_page() ); $GLOBALS['_wp_current_template_id'] = 'block-theme//index'; return function ( array $etag_data ): void { From b083a29014b53a5c897026b7f23f4cb259d67016 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 14 Dec 2024 21:35:15 -0800 Subject: [PATCH 198/237] Avoid using pretty permalink for author URL due to GHA testing problem --- .../optimization-detective/tests/storage/test-data.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/optimization-detective/tests/storage/test-data.php b/plugins/optimization-detective/tests/storage/test-data.php index e360ed995a..120779590a 100644 --- a/plugins/optimization-detective/tests/storage/test-data.php +++ b/plugins/optimization-detective/tests/storage/test-data.php @@ -396,6 +396,16 @@ static function ( $data ) { $user_id = self::factory()->user->create(); $this->assertIsInt( $user_id ); $post_ids = self::factory()->post->create_many( 3, array( 'post_author' => $user_id ) ); + + // This is a workaround because the author URL pretty permalink is failing for some reason only on GHA. + add_filter( + 'author_link', + static function ( $link, $author_id ) { + return add_query_arg( 'author', $author_id, home_url( '/' ) ); + }, + 10, + 2 + ); $this->go_to( get_author_posts_url( $user_id ) ); $GLOBALS['template'] = trailingslashit( get_template_directory() ) . 'author.php'; From d73f8cac4765b90cd0b7794a0596ef7f3469cc43 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sat, 14 Dec 2024 22:45:18 -0800 Subject: [PATCH 199/237] Scaffold new tests for Image Prioritizer --- .../image-prioritizer/tests/test-helper.php | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index 180a2d807f..91469b244f 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -467,6 +467,63 @@ public function test_image_prioritizer_add_element_item_schema_properties_inputs } } + /** + * Data provider. + * + * @return array + */ + public function data_provider_to_test_image_prioritizer_validate_background_image_url(): array { + return array( + 'url_parse_error' => array( + 'set_up' => static function (): string { + return 'https:///www.example.com'; + }, + 'expect_error' => 'background_image_url_lacks_host', + ), + 'url_no_host' => array( + 'set_up' => static function (): string { + return '/foo/bar?baz=1'; + }, + 'expect_error' => 'background_image_url_lacks_host', + ), + 'url_disallowed_origin' => array( + 'set_up' => static function (): string { + return 'https://bad.example.com/foo.jpg'; + }, + 'expect_error' => 'disallowed_background_image_url_host', + ), + // TODO: Try uploading image attachment and have it point to a CDN. + // TODO: Try a URL that returns a non image Content-Type. + ); + } + + /** + * Tests image_prioritizer_validate_background_image_url(). + * + * @covers ::image_prioritizer_validate_background_image_url + * + * @dataProvider data_provider_to_test_image_prioritizer_validate_background_image_url + */ + public function test_image_prioritizer_validate_background_image_url( Closure $set_up, ?string $expect_error ): void { + $url = $set_up(); + $validity = image_prioritizer_validate_background_image_url( $url ); + if ( null === $expect_error ) { + $this->assertTrue( $validity ); + } else { + $this->assertInstanceOf( WP_Error::class, $validity ); + $this->assertSame( $expect_error, $validity->get_error_code() ); + } + } + + /** + * Tests image_prioritizer_filter_store_url_metric_validity(). + * + * @covers ::image_prioritizer_filter_store_url_metric_validity + */ + public function test_image_prioritizer_filter_store_url_metric_validity(): void { + $this->markTestIncomplete(); + } + /** * Test image_prioritizer_get_video_lazy_load_script. * From 18553eee9733442eecdf0caeda11d9840cbe0363 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 15 Dec 2024 08:43:38 -0800 Subject: [PATCH 200/237] Add missing access private tags --- .../class-image-prioritizer-video-tag-visitor.php | 1 - plugins/image-prioritizer/helper.php | 13 ++++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php index 1e940fab01..4399d7a40e 100644 --- a/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php +++ b/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php @@ -17,7 +17,6 @@ * Image Prioritizer: Image_Prioritizer_Video_Tag_Visitor class * * @since 0.2.0 - * * @access private */ final class Image_Prioritizer_Video_Tag_Visitor extends Image_Prioritizer_Tag_Visitor { diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 56c7f10c0b..790599dece 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -14,6 +14,7 @@ * Initializes Image Prioritizer when Optimization Detective has loaded. * * @since 0.2.0 + * @access private * * @param string $optimization_detective_version Current version of the optimization detective plugin. */ @@ -52,6 +53,7 @@ static function (): void { * See {@see 'wp_head'}. * * @since 0.1.0 + * @access private */ function image_prioritizer_render_generator_meta_tag(): void { // Use the plugin slug as it is immutable. @@ -62,6 +64,7 @@ function image_prioritizer_render_generator_meta_tag(): void { * Registers tag visitors. * * @since 0.1.0 + * @access private * * @param OD_Tag_Visitor_Registry $registry Tag visitor registry. */ @@ -81,6 +84,7 @@ function image_prioritizer_register_tag_visitors( OD_Tag_Visitor_Registry $regis * Filters the list of Optimization Detective extension module URLs to include the extension for Image Prioritizer. * * @since n.e.x.t + * @access private * * @param string[]|mixed $extension_module_urls Extension module URLs. * @return string[] Extension module URLs. @@ -97,6 +101,7 @@ function image_prioritizer_filter_extension_module_urls( $extension_module_urls * Filters additional properties for the element item schema for Optimization Detective. * * @since n.e.x.t + * @access private * * @param array $additional_properties Additional properties. * @return array Additional properties. @@ -141,6 +146,7 @@ function image_prioritizer_add_element_item_schema_properties( array $additional * Validates URL for a background image. * * @since n.e.x.t + * @access private * * @param string $url Background image URL. * @return true|WP_Error Validity. @@ -238,7 +244,8 @@ static function ( $host ) { * This removes the lcpElementExternalBackgroundImage from the URL Metric prior to it being stored if the background * image URL is not valid. Removal of the property is preferable to * - * @since n.e.x.t + * @since n.e.x.t + * @access private * * @param bool|WP_Error|mixed $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. @@ -273,6 +280,7 @@ function image_prioritizer_filter_store_url_metric_validity( $validity, OD_Stric * Gets the path to a script or stylesheet. * * @since n.e.x.t + * @access private * * @param string $src_path Source path, relative to plugin root. * @param string|null $min_path Minified path. If not supplied, then '.min' is injected before the file extension in the source path. @@ -319,6 +327,7 @@ function image_prioritizer_get_asset_path( string $src_path, ?string $min_path = * Handles 'autoplay' and 'preload' attributes accordingly. * * @since 0.2.0 + * @access private * * @return string Lazy load script. */ @@ -333,6 +342,7 @@ function image_prioritizer_get_video_lazy_load_script(): string { * Load the background image when it approaches the viewport using an IntersectionObserver. * * @since n.e.x.t + * @access private * * @return string Lazy load script. */ @@ -345,6 +355,7 @@ function image_prioritizer_get_lazy_load_bg_image_script(): string { * Gets the stylesheet to lazy-load background images. * * @since n.e.x.t + * @access private * * @return string Lazy load stylesheet. */ From b7b1a4769e7d6b09cd2642d355f899cf3487d7b8 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 15 Dec 2024 13:39:25 -0800 Subject: [PATCH 201/237] Add tests for various validity conditions for external BG images --- plugins/image-prioritizer/helper.php | 4 +- .../image-prioritizer/tests/test-helper.php | 175 +++++++++++++++++- 2 files changed, 172 insertions(+), 7 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 790599dece..47751cb432 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -194,7 +194,7 @@ static function ( $host ) { sprintf( /* translators: %s is the list of allowed hosts */ __( 'Background image URL host is not among allowed: %s.', 'image-prioritizer' ), - join( ', ', $allowed_hosts ) + join( ', ', array_unique( $allowed_hosts ) ) ) ); } @@ -222,7 +222,7 @@ static function ( $host ) { } // Validate that the Content-Type is an image. - $content_type = (array) wp_remote_retrieve_header( $r, 'Content-Type' ); + $content_type = (array) wp_remote_retrieve_header( $r, 'content-type' ); if ( ! is_string( $content_type[0] ) || ! str_starts_with( $content_type[0], 'image/' ) ) { return new WP_Error( 'background_image_response_not_image', diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index 91469b244f..322ef60604 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -474,26 +474,191 @@ public function test_image_prioritizer_add_element_item_schema_properties_inputs */ public function data_provider_to_test_image_prioritizer_validate_background_image_url(): array { return array( - 'url_parse_error' => array( + 'bad_url_parse_error' => array( 'set_up' => static function (): string { return 'https:///www.example.com'; }, 'expect_error' => 'background_image_url_lacks_host', ), - 'url_no_host' => array( + 'bad_url_no_host' => array( 'set_up' => static function (): string { return '/foo/bar?baz=1'; }, 'expect_error' => 'background_image_url_lacks_host', ), - 'url_disallowed_origin' => array( + + 'bad_url_disallowed_origin' => array( 'set_up' => static function (): string { return 'https://bad.example.com/foo.jpg'; }, 'expect_error' => 'disallowed_background_image_url_host', ), - // TODO: Try uploading image attachment and have it point to a CDN. - // TODO: Try a URL that returns a non image Content-Type. + + 'good_other_origin_via_allowed_http_origins_filter' => array( + 'set_up' => static function (): string { + $image_url = 'https://other-origin.example.com/foo.jpg'; + + add_filter( + 'allowed_http_origins', + static function ( array $allowed_origins ): array { + $allowed_origins[] = 'https://other-origin.example.com'; + return $allowed_origins; + } + ); + + add_filter( + 'pre_http_request', + static function ( $pre, $parsed_args, $url ) use ( $image_url ) { + if ( 'HEAD' !== $parsed_args['method'] || $image_url !== $url ) { + return $pre; + } + return array( + 'headers' => array( + 'content-type' => 'image/jpeg', + 'content-length' => '288449', + ), + 'body' => '', + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + ); + }, + 10, + 3 + ); + + return $image_url; + }, + 'expect_error' => null, + ), + + 'good_url_allowed_cdn_origin' => array( + 'set_up' => function (): string { + $attachment_id = self::factory()->attachment->create_upload_object( TESTS_PLUGIN_DIR . '/tests/data/images/car.jpeg' ); + $this->assertIsInt( $attachment_id ); + + add_filter( + 'wp_get_attachment_image_src', + static function ( $src ): array { + $src[0] = preg_replace( '#^https?://#i', 'https://my-image-cdn.example.com/', $src[0] ); + return $src; + } + ); + + $src = wp_get_attachment_image_src( $attachment_id, 'large' ); + $this->assertIsArray( $src ); + $this->assertStringStartsWith( 'https://my-image-cdn.example.com/', $src[0] ); + + add_filter( + 'pre_http_request', + static function ( $pre, $parsed_args, $url ) use ( $src ) { + if ( 'HEAD' !== $parsed_args['method'] || $src[0] !== $url ) { + return $pre; + } + return array( + 'headers' => array( + 'content-type' => 'image/jpeg', + 'content-length' => '288449', + ), + 'body' => '', + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + ); + }, + 10, + 3 + ); + + return $src[0]; + }, + 'expect_error' => null, + ), + + 'bad_not_found' => array( + 'set_up' => static function (): string { + $image_url = home_url( '/bad.jpg' ); + + add_filter( + 'pre_http_request', + static function ( $pre, $parsed_args, $url ) use ( $image_url ) { + if ( 'HEAD' !== $parsed_args['method'] || $image_url !== $url ) { + return $pre; + } + return array( + 'headers' => array( + 'content-type' => 'text/html', + 'content-length' => 1000, + ), + 'body' => '', + 'response' => array( + 'code' => 404, + 'message' => 'Not Found', + ), + ); + }, + 10, + 3 + ); + + return $image_url; + }, + 'expect_error' => 'background_image_response_not_ok', + ), + + 'bad_content_type' => array( + 'set_up' => static function (): string { + $video_url = home_url( '/bad.mp4' ); + + add_filter( + 'pre_http_request', + static function ( $pre, $parsed_args, $url ) use ( $video_url ) { + if ( 'HEAD' !== $parsed_args['method'] || $video_url !== $url ) { + return $pre; + } + return array( + 'headers' => array( + 'content-type' => 'video/mp4', + 'content-length' => '288449000', + ), + 'body' => '', + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + ); + }, + 10, + 3 + ); + + return $video_url; + }, + 'expect_error' => 'background_image_response_not_image', + ), + + 'bad_redirect' => array( + 'set_up' => static function (): string { + $redirect_url = home_url( '/redirect.jpg' ); + + add_filter( + 'pre_http_request', + static function ( $pre, $parsed_args, $url ) use ( $redirect_url ) { + if ( $redirect_url === $url ) { + return new WP_Error( 'http_request_failed', 'Too many redirects.' ); + } + return $pre; + }, + 10, + 3 + ); + + return $redirect_url; + }, + 'expect_error' => 'http_request_failed', + ), ); } From 537323340800afcd83c735d20788339ef8c8ec01 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 15 Dec 2024 14:00:39 -0800 Subject: [PATCH 202/237] Fix handling of invalid external BG image and add tests --- plugins/image-prioritizer/helper.php | 6 +- .../image-prioritizer/tests/test-helper.php | 169 +++++++++++++++++- 2 files changed, 170 insertions(+), 5 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 47751cb432..ba7a88800e 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -261,14 +261,14 @@ function image_prioritizer_filter_store_url_metric_validity( $validity, OD_Stric $data = $url_metric->get( 'lcpElementExternalBackgroundImage' ); if ( is_array( $data ) && isset( $data['url'] ) && is_string( $data['url'] ) ) { // Note: The isset() and is_string() checks aren't necessary since the JSON Schema enforces them to be set. - $validity = image_prioritizer_validate_background_image_url( $data['url'] ); - if ( is_wp_error( $validity ) ) { + $image_validity = image_prioritizer_validate_background_image_url( $data['url'] ); + if ( is_wp_error( $image_validity ) ) { /** * No WP_Exception is thrown by wp_trigger_error() since E_USER_ERROR is not passed as the error level. * * @noinspection PhpUnhandledExceptionInspection */ - wp_trigger_error( __FUNCTION__, $validity->get_error_message() . ' Background image URL: ' . $data['url'] ); + wp_trigger_error( __FUNCTION__, $image_validity->get_error_message() . ' Background image URL: ' . $data['url'] ); $url_metric->unset( 'lcpElementExternalBackgroundImage' ); } } diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index 322ef60604..7d3d3a5db1 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -659,6 +659,37 @@ static function ( $pre, $parsed_args, $url ) use ( $redirect_url ) { }, 'expect_error' => 'http_request_failed', ), + + 'good_same_origin' => array( + 'set_up' => static function (): string { + $image_url = home_url( '/good.jpg' ); + + add_filter( + 'pre_http_request', + static function ( $pre, $parsed_args, $url ) use ( $image_url ) { + if ( 'HEAD' !== $parsed_args['method'] || $image_url !== $url ) { + return $pre; + } + return array( + 'headers' => array( + 'content-type' => 'image/jpeg', + 'content-length' => '288449', + ), + 'body' => '', + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + ); + }, + 10, + 3 + ); + + return $image_url; + }, + 'expect_error' => null, + ), ); } @@ -680,13 +711,147 @@ public function test_image_prioritizer_validate_background_image_url( Closure $s } } + /** + * Data provider. + * + * @return array + */ + public function data_provider_to_test_image_prioritizer_filter_store_url_metric_validity(): array { + return array( + 'pass_through_true' => array( + 'set_up' => static function ( array $sample_url_metric_data ): array { + $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); + return array( true, $url_metric ); + }, + 'assert' => function ( $value ): void { + $this->assertTrue( $value ); + }, + ), + + 'pass_through_false' => array( + 'set_up' => static function ( array $sample_url_metric_data ): array { + $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); + return array( false, $url_metric ); + }, + 'assert' => function ( $value ): void { + $this->assertFalse( $value ); + }, + ), + + 'pass_through_truthy_string' => array( + 'set_up' => static function ( array $sample_url_metric_data ): array { + $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); + return array( 'so true', $url_metric ); + }, + 'assert' => function ( $value ): void { + $this->assertTrue( $value ); + }, + ), + + 'pass_through_falsy_string' => array( + 'set_up' => static function ( array $sample_url_metric_data ): array { + $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); + return array( '', $url_metric ); + }, + 'assert' => function ( $value ): void { + $this->assertFalse( $value ); + }, + ), + + 'pass_through_wp_error' => array( + 'set_up' => static function ( array $sample_url_metric_data ): array { + $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); + return array( new WP_Error( 'bad', 'Evil' ), $url_metric ); + }, + 'assert' => function ( $value ): void { + $this->assertInstanceOf( WP_Error::class, $value ); + $this->assertSame( 'bad', $value->get_error_code() ); + }, + ), + + 'invalid_external_bg_image' => array( + 'set_up' => static function ( array $sample_url_metric_data ): array { + $sample_url_metric_data['lcpElementExternalBackgroundImage'] = array( + 'url' => 'https://bad-origin.example.com/image.jpg', + 'tag' => 'DIV', + 'id' => null, + 'class' => null, + ); + $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); + return array( true, $url_metric ); + }, + 'assert' => function ( $value, OD_Strict_URL_Metric $url_metric ): void { + $this->assertTrue( $value ); + $this->assertNull( $url_metric->get( 'lcpElementExternalBackgroundImage' ) ); + }, + ), + + 'valid_external_bg_image' => array( + 'set_up' => static function ( array $sample_url_metric_data ): array { + $image_url = home_url( '/good.jpg' ); + + add_filter( + 'pre_http_request', + static function ( $pre, $parsed_args, $url ) use ( $image_url ) { + if ( 'HEAD' !== $parsed_args['method'] || $image_url !== $url ) { + return $pre; + } + return array( + 'headers' => array( + 'content-type' => 'image/jpeg', + 'content-length' => '288449', + ), + 'body' => '', + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + ); + }, + 10, + 3 + ); + + $sample_url_metric_data['lcpElementExternalBackgroundImage'] = array( + 'url' => $image_url, + 'tag' => 'DIV', + 'id' => null, + 'class' => null, + ); + $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); + return array( true, $url_metric ); + }, + 'assert' => function ( $value, OD_Strict_URL_Metric $url_metric ): void { + $this->assertTrue( $value ); + $this->assertIsArray( $url_metric->get( 'lcpElementExternalBackgroundImage' ) ); + $this->assertSame( + array( + 'url' => home_url( '/good.jpg' ), + 'tag' => 'DIV', + 'id' => null, + 'class' => null, + ), + $url_metric->get( 'lcpElementExternalBackgroundImage' ) + ); + }, + ), + ); + } + /** * Tests image_prioritizer_filter_store_url_metric_validity(). * + * @dataProvider data_provider_to_test_image_prioritizer_filter_store_url_metric_validity + * * @covers ::image_prioritizer_filter_store_url_metric_validity + * @covers ::image_prioritizer_validate_background_image_url */ - public function test_image_prioritizer_filter_store_url_metric_validity(): void { - $this->markTestIncomplete(); + public function test_image_prioritizer_filter_store_url_metric_validity( Closure $set_up, Closure $assert ): void { + $sample_url_metric_data = $this->get_sample_url_metric( array() )->jsonSerialize(); + list( $validity, $url_metric ) = $set_up( $sample_url_metric_data ); + + $validity = image_prioritizer_filter_store_url_metric_validity( $validity, $url_metric ); + $assert( $validity, $url_metric ); } /** From bcefd693e3dc4944681418a6f8865220f397f0b7 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 15 Dec 2024 14:11:36 -0800 Subject: [PATCH 203/237] Avoid preloading background images larger than 2MB --- plugins/image-prioritizer/helper.php | 25 ++++++++++++++- .../image-prioritizer/tests/test-helper.php | 31 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index ba7a88800e..e80b8f41c3 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -234,7 +234,30 @@ static function ( $host ) { ); } - // TODO: Check for the Content-Length and return invalid if it is gigantic? + /* + * Validate that the Content-Length is not too massive, as it would be better to err on the side of + * not preloading something so weighty in case the image won't actually end up as LCP. + * The value of 2MB is chosen because according to Web Almanac 2022, the largest image by byte size + * on a page is 1MB at the 90th percentile: . + * The 2MB value is double this 1MB size. + */ + $content_length = (array) wp_remote_retrieve_header( $r, 'content-length' ); + if ( ! is_numeric( $content_length[0] ) ) { + return new WP_Error( + 'background_image_content_length_unknown', + __( 'HEAD request for background image URL did not include a Content-Length response header.', 'image-prioritizer' ) + ); + } elseif ( (int) $content_length[0] > 2 * MB_IN_BYTES ) { + return new WP_Error( + 'background_image_content_length_too_large', + sprintf( + /* translators: %s is the content length of the response */ + __( 'HEAD request for background image URL returned Content-Length greater than 2MB: %s.', 'image-prioritizer' ), + $content_length[0] + ) + ); + } + return true; } diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index 7d3d3a5db1..45e91cccd6 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -639,6 +639,37 @@ static function ( $pre, $parsed_args, $url ) use ( $video_url ) { 'expect_error' => 'background_image_response_not_image', ), + 'bad_content_length' => array( + 'set_up' => static function (): string { + $image_url = home_url( '/massive-image.jpg' ); + + add_filter( + 'pre_http_request', + static function ( $pre, $parsed_args, $url ) use ( $image_url ) { + if ( 'HEAD' !== $parsed_args['method'] || $image_url !== $url ) { + return $pre; + } + return array( + 'headers' => array( + 'content-type' => 'image/jpeg', + 'content-length' => (string) ( 2 * MB_IN_BYTES + 1 ), + ), + 'body' => '', + 'response' => array( + 'code' => 200, + 'message' => 'OK', + ), + ); + }, + 10, + 3 + ); + + return $image_url; + }, + 'expect_error' => 'background_image_content_length_too_large', + ), + 'bad_redirect' => array( 'set_up' => static function (): string { $redirect_url = home_url( '/redirect.jpg' ); From 6005f4a9f084825edc4f2eeeb4152b41b29ef0ce Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Sun, 15 Dec 2024 14:24:48 -0800 Subject: [PATCH 204/237] Update readme to relate features of Image Prioritizer --- plugins/image-prioritizer/readme.txt | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt index 37ea1fb918..029fe74380 100644 --- a/plugins/image-prioritizer/readme.txt +++ b/plugins/image-prioritizer/readme.txt @@ -15,13 +15,19 @@ This plugin optimizes the loading of images (and videos) with prioritization, la The current optimizations include: -1. Ensure `fetchpriority=high` is only added to an `IMG` when it is the Largest Contentful Paint (LCP) element across all responsive breakpoints. -2. Add breakpoint-specific `fetchpriority=high` preload links for the LCP elements which are `IMG` elements or elements with a CSS `background-image` inline style. -3. Apply lazy-loading to `IMG` tags based on whether they appear in any breakpoint’s initial viewport. (Additionally, [`sizes=auto`](https://make.wordpress.org/core/2024/10/18/auto-sizes-for-lazy-loaded-images-in-wordpress-6-7/) is then also correctly applied.) -4. Implement lazy-loading of CSS background images added via inline `style` attributes. -5. Add `fetchpriority=low` to `IMG` tags which appear in the initial viewport but are not visible, such as when they are subsequent carousel slides. +1. Add breakpoint-specific `fetchpriority=high` preload links (`LINK[rel=preload]`) for image URLs of LCP elements: + 1. An `IMG` element, including the `srcset`/`sizes` attributes supplied as `imagesrcset`/`imagesizes` on the `LINK`. + 2. The first `SOURCE` element with a `type` attribute in a `PICTURE` element. (Art-directed `PICTURE` elements using media queries are not supported.) + 3. An element with a CSS `background-image` inline `style` attribute. + 4. An element with a CSS `background-image` applied with a stylesheet (when the image is from an allowed origin). +2. Ensure `fetchpriority=high` is only added to an `IMG` when it is the Largest Contentful Paint (LCP) element across all responsive breakpoints. +3. Add `fetchpriority=low` to `IMG` tags which appear in the initial viewport but are not visible, such as when they are subsequent carousel slides. +4. Lazy-Loading: + 1. Apply lazy-loading to `IMG` tags based on whether they appear in any breakpoint’s initial viewport. (Additionally, [`sizes=auto`](https://make.wordpress.org/core/2024/10/18/auto-sizes-for-lazy-loaded-images-in-wordpress-6-7/) is then also correctly applied.) + 2. Implement lazy-loading of CSS background images added via inline `style` attributes. + 3. Lazy-load `VIDEO` tags by setting the appropriate attributes based on whether they appear in the initial viewport. If a `VIDEO` is the LCP element, it gets `preload=auto`; if it is in an initial viewport, the `preload=metadata` default is left; if it is not in an initial viewport, it gets `preload=none`. Lazy-loaded videos also get initial `preload`, `autoplay`, and `poster` attributes restored when the `VIDEO` is going to enter the viewport. +5. Ensure that `sizes=auto` is added to all lazy-loaded `IMG` elements. 6. Reduce the size of the `poster` image of a `VIDEO` from full size to the size appropriate for the maximum width of the video (on desktop). -7. Lazy-load `VIDEO` tags by setting the appropriate attributes based on whether they appear in the initial viewport. If a `VIDEO` is the LCP element, it gets `preload=auto`; if it is in an initial viewport, the `preload=metadata` default is left; if it is not in an initial viewport, it gets `preload=none`. Lazy-loaded videos also get initial `preload`, `autoplay`, and `poster` attributes restored when the `VIDEO` is going to enter the viewport. **This plugin requires the [Optimization Detective](https://wordpress.org/plugins/optimization-detective/) plugin as a dependency.** Please refer to that plugin for additional background on how this plugin works as well as additional developer options. From af1665bdc5a4bb7ba2a6c4b0349237c327680ebd Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 16 Dec 2024 10:10:11 +0530 Subject: [PATCH 205/237] Remove auto_sizes_get_layout_settings() --- .../auto-sizes/includes/improve-calculate-sizes.php | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 1e0c5113fd..ba2ba49d19 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -229,7 +229,7 @@ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $ * @return string The alignment width based. */ function auto_sizes_get_layout_width( string $alignment ): string { - $layout = auto_sizes_get_layout_settings(); + $layout = wp_get_global_settings( array( 'layout' ) ); $layout_widths = array( 'full' => '100vw', // Todo: incorporate useRootPaddingAwareAlignments. @@ -295,14 +295,3 @@ function auto_sizes_filter_render_block_context( array $context, array $block ): return $context; } - -/** - * Retrieves the layout settings defined in theme.json. - * - * @since n.e.x.t - * - * @return array Associative array of layout settings. - */ -function auto_sizes_get_layout_settings(): array { - return wp_get_global_settings( array( 'layout' ) ); -} From 164809caac8055a756c19dee54e8bcf15f97b3a4 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 16 Dec 2024 11:37:36 +0530 Subject: [PATCH 206/237] Check accurate sizes in classic theme --- .../tests/test-improve-calculate-sizes.php | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php index 484fb3f954..693161ee92 100644 --- a/plugins/auto-sizes/tests/test-improve-calculate-sizes.php +++ b/plugins/auto-sizes/tests/test-improve-calculate-sizes.php @@ -614,6 +614,38 @@ public function data_image_blocks_with_relative_alignment(): array { ); } + /** + * Test the image block with different alignment in classic theme. + * + * @dataProvider data_image_blocks_with_relative_alignment_for_classic_theme + * + * @param string $image_alignment Image alignment. + */ + public function test_image_block_with_different_alignment_in_classic_theme( string $image_alignment ): void { + switch_theme( 'twentytwentyone' ); + + $block_content = $this->get_image_block_markup( self::$image_id, 'large', $image_alignment ); + + $result = apply_filters( 'the_content', $block_content ); + + $this->assertStringContainsString( 'sizes="(max-width: 1024px) 100vw, 1024px" ', $result ); + } + + /** + * Data provider. + * + * @return array> The ancestor and image alignments. + */ + public function data_image_blocks_with_relative_alignment_for_classic_theme(): array { + return array( + array( '' ), + array( 'wide' ), + array( 'left' ), + array( 'center' ), + array( 'right' ), + ); + } + /** * Filter the theme.json data to include relative layout sizes. * From 82245304b64712986799a5ab36064ceed4d024f6 Mon Sep 17 00:00:00 2001 From: Mukesh Panchal Date: Mon, 16 Dec 2024 12:30:58 +0530 Subject: [PATCH 207/237] Check if block theme --- plugins/auto-sizes/includes/improve-calculate-sizes.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index 1e0c5113fd..d4f0acb747 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -140,6 +140,11 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b * @return string|false An improved sizes attribute or false if a better size cannot be calculated. */ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $resize_width, string $max_alignment ) { + // Bail early if not a block theme. + if ( ! wp_is_block_theme() ) { + return false; + } + // Without an image ID or a resize width, we cannot calculate a better size. if ( 0 === $id && 0 === $resize_width ) { return false; From 70b51ba60794e963b22935c28e3e83620b57031b Mon Sep 17 00:00:00 2001 From: Shyamsundar Gadde Date: Mon, 16 Dec 2024 13:34:09 +0530 Subject: [PATCH 208/237] Only show plugin management link to authorized users Co-authored-by: Felix Arntz --- plugins/performance-lab/includes/admin/plugins.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index bbfc41a32a..aabbd6c240 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -291,8 +291,11 @@ static function ( string $error_message ): string {
-

- +

+ 'WordPress Performance Team', @@ -310,8 +313,10 @@ static function ( string $error_message ): string { 'a' => array( 'href' => true ), ) ); - ?> -

+ ?> +

+ Date: Mon, 16 Dec 2024 13:41:31 +0530 Subject: [PATCH 209/237] Resolve PHP opening tag and whitespace violations Signed-off-by: Shyamsundar Gadde --- plugins/performance-lab/includes/admin/plugins.php | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/performance-lab/includes/admin/plugins.php b/plugins/performance-lab/includes/admin/plugins.php index aabbd6c240..003138e52e 100644 --- a/plugins/performance-lab/includes/admin/plugins.php +++ b/plugins/performance-lab/includes/admin/plugins.php @@ -317,7 +317,6 @@ static function ( string $error_message ): string {

Date: Mon, 16 Dec 2024 10:30:04 -0800 Subject: [PATCH 210/237] Replace validity filter with sanitization filter; unset unset() --- plugins/image-prioritizer/helper.php | 23 ++-- plugins/image-prioritizer/hooks.php | 2 +- .../image-prioritizer/tests/test-helper.php | 118 ++++++------------ .../image-prioritizer/tests/test-hooks.php | 2 +- .../class-od-element.php | 45 +------ .../class-od-url-metric.php | 27 ---- plugins/optimization-detective/readme.txt | 14 +-- .../storage/rest-api.php | 111 +++++++--------- .../tests/storage/test-rest-api.php | 99 ++++----------- .../tests/test-class-od-element.php | 73 ++--------- .../tests/test-class-od-url-metric.php | 25 ---- ...-class-od-url-metrics-group-collection.php | 49 +------- 12 files changed, 145 insertions(+), 443 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index e80b8f41c3..9b1c72c158 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -270,33 +270,30 @@ static function ( $host ) { * @since n.e.x.t * @access private * - * @param bool|WP_Error|mixed $validity Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. - * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. - * @return bool|WP_Error Validity. Valid if true or a WP_Error without any errors, or invalid otherwise. + * @param array|mixed $data URL Metric data. + * @return array Sanitized URL Metric data. * * @noinspection PhpDocMissingThrowsInspection - * @throws OD_Data_Validation_Exception Except it won't because lcpElementExternalBackgroundImage is not a required property. */ -function image_prioritizer_filter_store_url_metric_validity( $validity, OD_Strict_URL_Metric $url_metric ) { - if ( ! is_bool( $validity ) && ! ( $validity instanceof WP_Error ) ) { - $validity = (bool) $validity; +function image_prioritizer_filter_url_metric_data_pre_storage( $data ): array { + if ( ! is_array( $data ) ) { + $data = array(); } - $data = $url_metric->get( 'lcpElementExternalBackgroundImage' ); - if ( is_array( $data ) && isset( $data['url'] ) && is_string( $data['url'] ) ) { // Note: The isset() and is_string() checks aren't necessary since the JSON Schema enforces them to be set. - $image_validity = image_prioritizer_validate_background_image_url( $data['url'] ); + if ( isset( $data['lcpElementExternalBackgroundImage']['url'] ) && is_string( $data['lcpElementExternalBackgroundImage']['url'] ) ) { + $image_validity = image_prioritizer_validate_background_image_url( $data['lcpElementExternalBackgroundImage']['url'] ); if ( is_wp_error( $image_validity ) ) { /** * No WP_Exception is thrown by wp_trigger_error() since E_USER_ERROR is not passed as the error level. * * @noinspection PhpUnhandledExceptionInspection */ - wp_trigger_error( __FUNCTION__, $image_validity->get_error_message() . ' Background image URL: ' . $data['url'] ); - $url_metric->unset( 'lcpElementExternalBackgroundImage' ); + wp_trigger_error( __FUNCTION__, $image_validity->get_error_message() . ' Background image URL: ' . $data['lcpElementExternalBackgroundImage']['url'] ); + unset( $data['lcpElementExternalBackgroundImage'] ); } } - return $validity; + return $data; } /** diff --git a/plugins/image-prioritizer/hooks.php b/plugins/image-prioritizer/hooks.php index fbfe8c6b36..63420cd6d6 100644 --- a/plugins/image-prioritizer/hooks.php +++ b/plugins/image-prioritizer/hooks.php @@ -13,4 +13,4 @@ add_action( 'od_init', 'image_prioritizer_init' ); add_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ); add_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ); -add_filter( 'od_url_metric_storage_validity', 'image_prioritizer_filter_store_url_metric_validity', 10, 2 ); +add_filter( 'od_url_metric_data_pre_storage', 'image_prioritizer_filter_url_metric_data_pre_storage' ); diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index 45e91cccd6..05de496356 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -747,78 +747,34 @@ public function test_image_prioritizer_validate_background_image_url( Closure $s * * @return array */ - public function data_provider_to_test_image_prioritizer_filter_store_url_metric_validity(): array { + public function data_provider_to_test_image_prioritizer_filter_url_metric_data_pre_storage(): array { return array( - 'pass_through_true' => array( - 'set_up' => static function ( array $sample_url_metric_data ): array { - $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); - return array( true, $url_metric ); - }, - 'assert' => function ( $value ): void { - $this->assertTrue( $value ); - }, - ), - - 'pass_through_false' => array( - 'set_up' => static function ( array $sample_url_metric_data ): array { - $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); - return array( false, $url_metric ); - }, - 'assert' => function ( $value ): void { - $this->assertFalse( $value ); - }, - ), - - 'pass_through_truthy_string' => array( - 'set_up' => static function ( array $sample_url_metric_data ): array { - $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); - return array( 'so true', $url_metric ); - }, - 'assert' => function ( $value ): void { - $this->assertTrue( $value ); - }, - ), - - 'pass_through_falsy_string' => array( - 'set_up' => static function ( array $sample_url_metric_data ): array { - $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); - return array( '', $url_metric ); - }, - 'assert' => function ( $value ): void { - $this->assertFalse( $value ); - }, - ), - - 'pass_through_wp_error' => array( - 'set_up' => static function ( array $sample_url_metric_data ): array { - $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); - return array( new WP_Error( 'bad', 'Evil' ), $url_metric ); - }, - 'assert' => function ( $value ): void { - $this->assertInstanceOf( WP_Error::class, $value ); - $this->assertSame( 'bad', $value->get_error_code() ); - }, - ), - - 'invalid_external_bg_image' => array( - 'set_up' => static function ( array $sample_url_metric_data ): array { - $sample_url_metric_data['lcpElementExternalBackgroundImage'] = array( + 'invalid_external_bg_image' => array( + 'set_up' => static function ( array $url_metric_data ): array { + $url_metric_data['lcpElementExternalBackgroundImage'] = array( 'url' => 'https://bad-origin.example.com/image.jpg', 'tag' => 'DIV', 'id' => null, 'class' => null, ); - $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); - return array( true, $url_metric ); + $url_metric_data['viewport']['width'] = 10101; + $url_metric_data['viewport']['height'] = 20202; + return $url_metric_data; }, - 'assert' => function ( $value, OD_Strict_URL_Metric $url_metric ): void { - $this->assertTrue( $value ); - $this->assertNull( $url_metric->get( 'lcpElementExternalBackgroundImage' ) ); + 'assert' => function ( array $url_metric_data ): void { + $this->assertArrayNotHasKey( 'lcpElementExternalBackgroundImage', $url_metric_data ); + $this->assertSame( + array( + 'width' => 10101, + 'height' => 20202, + ), + $url_metric_data['viewport'] + ); }, ), - 'valid_external_bg_image' => array( - 'set_up' => static function ( array $sample_url_metric_data ): array { + 'valid_external_bg_image' => array( + 'set_up' => static function ( array $url_metric_data ): array { $image_url = home_url( '/good.jpg' ); add_filter( @@ -843,18 +799,20 @@ static function ( $pre, $parsed_args, $url ) use ( $image_url ) { 3 ); - $sample_url_metric_data['lcpElementExternalBackgroundImage'] = array( + $url_metric_data['lcpElementExternalBackgroundImage'] = array( 'url' => $image_url, 'tag' => 'DIV', 'id' => null, 'class' => null, ); - $url_metric = new OD_Strict_URL_Metric( $sample_url_metric_data ); - return array( true, $url_metric ); + + $url_metric_data['viewport']['width'] = 30303; + $url_metric_data['viewport']['height'] = 40404; + return $url_metric_data; }, - 'assert' => function ( $value, OD_Strict_URL_Metric $url_metric ): void { - $this->assertTrue( $value ); - $this->assertIsArray( $url_metric->get( 'lcpElementExternalBackgroundImage' ) ); + 'assert' => function ( array $url_metric_data ): void { + $this->assertArrayHasKey( 'lcpElementExternalBackgroundImage', $url_metric_data ); + $this->assertIsArray( $url_metric_data['lcpElementExternalBackgroundImage'] ); $this->assertSame( array( 'url' => home_url( '/good.jpg' ), @@ -862,7 +820,14 @@ static function ( $pre, $parsed_args, $url ) use ( $image_url ) { 'id' => null, 'class' => null, ), - $url_metric->get( 'lcpElementExternalBackgroundImage' ) + $url_metric_data['lcpElementExternalBackgroundImage'] + ); + $this->assertSame( + array( + 'width' => 30303, + 'height' => 40404, + ), + $url_metric_data['viewport'] ); }, ), @@ -870,19 +835,18 @@ static function ( $pre, $parsed_args, $url ) use ( $image_url ) { } /** - * Tests image_prioritizer_filter_store_url_metric_validity(). + * Tests image_prioritizer_filter_url_metric_data_pre_storage(). * - * @dataProvider data_provider_to_test_image_prioritizer_filter_store_url_metric_validity + * @dataProvider data_provider_to_test_image_prioritizer_filter_url_metric_data_pre_storage * - * @covers ::image_prioritizer_filter_store_url_metric_validity + * @covers ::image_prioritizer_filter_url_metric_data_pre_storage * @covers ::image_prioritizer_validate_background_image_url */ - public function test_image_prioritizer_filter_store_url_metric_validity( Closure $set_up, Closure $assert ): void { - $sample_url_metric_data = $this->get_sample_url_metric( array() )->jsonSerialize(); - list( $validity, $url_metric ) = $set_up( $sample_url_metric_data ); + public function test_image_prioritizer_filter_url_metric_data_pre_storage( Closure $set_up, Closure $assert ): void { + $url_metric_data = $set_up( $this->get_sample_url_metric( array() )->jsonSerialize() ); - $validity = image_prioritizer_filter_store_url_metric_validity( $validity, $url_metric ); - $assert( $validity, $url_metric ); + $url_metric_data = image_prioritizer_filter_url_metric_data_pre_storage( $url_metric_data ); + $assert( $url_metric_data ); } /** diff --git a/plugins/image-prioritizer/tests/test-hooks.php b/plugins/image-prioritizer/tests/test-hooks.php index 3c2e020145..416523cb18 100644 --- a/plugins/image-prioritizer/tests/test-hooks.php +++ b/plugins/image-prioritizer/tests/test-hooks.php @@ -14,6 +14,6 @@ public function test_hooks_added(): void { $this->assertEquals( 10, has_action( 'od_init', 'image_prioritizer_init' ) ); $this->assertEquals( 10, has_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ) ); $this->assertEquals( 10, has_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ) ); - $this->assertEquals( 10, has_filter( 'od_url_metric_storage_validity', 'image_prioritizer_filter_store_url_metric_validity' ) ); + $this->assertEquals( 10, has_filter( 'od_url_metric_data_pre_storage', 'image_prioritizer_filter_url_metric_data_pre_storage' ) ); } } diff --git a/plugins/optimization-detective/class-od-element.php b/plugins/optimization-detective/class-od-element.php index be8400ce40..c7f243d99b 100644 --- a/plugins/optimization-detective/class-od-element.php +++ b/plugins/optimization-detective/class-od-element.php @@ -208,53 +208,18 @@ public function offsetSet( $offset, $value ): void { } /** - * Unsets a property. + * Offset to unset. * - * This is particularly useful in conjunction with the `od_url_metric_schema_element_item_additional_properties` filter. - * This will throw an exception if the property is required by the schema. - * - * @since n.e.x.t - * - * @param string $key Property. + * This is disallowed. Attempting to unset a property will throw an error. * - * @throws OD_Data_Validation_Exception When attempting to unset a property required by the JSON Schema. - */ - public function unset( string $key ): void { - $schema = OD_URL_Metric::get_json_schema(); // TODO: This should be a non-static method once the URL Metric is instantiated. - if ( - isset( $schema['properties']['elements']['items']['properties'][ $key ]['required'] ) - && true === $schema['properties']['elements']['items']['properties'][ $key ]['required'] - ) { - throw new OD_Data_Validation_Exception( - esc_html( - sprintf( - /* translators: %s is the property key. */ - __( 'The %s key is required for an item of elements in a URL Metric.', 'optimization-detective' ), - $key - ) - ) - ); - } - unset( $this->data[ $key ] ); // @phpstan-ignore assign.propertyType (Above required check ensures $key is not isLCP, isLCPCandidate, xpath, intersectionRatio, intersectionRect, boundingClientRect.) - $group = $this->url_metric->get_group(); - if ( $group instanceof OD_URL_Metric_Group ) { - $group->clear_cache(); - } - } - - /** - * Unsets an offset. - * - * This will throw an exception if the offset is required by the schema. - * - * @since n.e.x.t + * @since 0.7.0 * * @param mixed $offset Offset. * - * @throws OD_Data_Validation_Exception When attempting to unset a property required by the JSON Schema. + * @throws Exception When attempting to unset a property. */ public function offsetUnset( $offset ): void { - $this->unset( (string) $offset ); + throw new Exception( 'Element data may not be unset.' ); } /** diff --git a/plugins/optimization-detective/class-od-url-metric.php b/plugins/optimization-detective/class-od-url-metric.php index b68045e6ef..f4a4e25cc9 100644 --- a/plugins/optimization-detective/class-od-url-metric.php +++ b/plugins/optimization-detective/class-od-url-metric.php @@ -503,33 +503,6 @@ function ( array $element ): OD_Element { return $this->elements; } - /** - * Unsets a property from the URL Metric. - * - * @since n.e.x.t - * - * @param string $key Key to unset. - * @throws OD_Data_Validation_Exception If the property is required an exception will be thrown. - */ - public function unset( string $key ): void { - $schema = self::get_json_schema(); // TODO: Consider capturing the schema as a private member variable once the URL Metric is constructed. - if ( isset( $schema['properties'][ $key ]['required'] ) && true === $schema['properties'][ $key ]['required'] ) { - throw new OD_Data_Validation_Exception( - esc_html( - sprintf( - /* translators: %s is the property key. */ - __( 'The %s key is required at the root of a URL Metric.', 'optimization-detective' ), - $key - ) - ) - ); - } - unset( $this->data[ $key ] ); // @phpstan-ignore assign.propertyType (Above required check ensures $key is not uuid, url, timestamp, viewport, or elements.) - if ( $this->group instanceof OD_URL_Metric_Group ) { - $this->group->clear_cache(); - } - } - /** * Specifies data which should be serialized to JSON. * diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index f0a8cc1476..d78c4864d0 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -246,19 +246,13 @@ The ETag is a unique identifier that changes whenever the underlying data used t When the ETag for URL Metrics in a complete viewport group no longer matches the current environment's ETag, new URL Metrics will then begin to be collected until there are no more stored URL Metrics with the old ETag. These new URL Metrics will include data relevant to the newly activated plugins and their tag visitors. -**Filter:** `od_url_metric_storage_validity` (default args: true) +**Filter:** `od_url_metric_data_pre_storage` (default arg: URL Metric data array) -Filters whether a URL Metric is valid for storage. +Filters the URL Metric data prior to validation for storage. -Three parameters are passed to this filter: +This allows for custom sanitization and validation constraints to be applied beyond what can be expressed in JSON Schema. This filter only applies when storing a URL Metric via the REST API. It does not run when a stored URL Metric is loaded from the od_url_metrics post type. This means that sanitization and validation logic enforced via this filter can be more expensive, such as doing filesystem checks or HTTP requests. -1. `$validity` (`bool|WP_Error`): Validity. Invalid if false or a WP_Error with errors. -2. `$url_metric` (`OD_Strict_URL_Metric`): URL Metric, already validated against the JSON Schema. -3. `$url_metric_data` (`array`): Original URL Metric data before any mutations. - -This allows for custom validation constraints to be applied beyond what can be expressed in JSON Schema. This filter only applies when storing a URL Metric via the REST API. It does not run when a stored URL Metric is loaded from the `od_url_metrics` post type. This means that validation logic enforced via this filter can be more expensive, such as doing filesystem checks or HTTP requests. - -In addition to having the filter return `false` or a non-empty `WP_Error` to block storing the URL Metric, a plugin may also mutate the `OD_URL_Metric` instance passed by reference to the filter callback. This is useful for plugins in particular to unset extended properties which couldn't be validated using JSON Schema alone. +To fail validation for the provided URL Metric data, an OD_Data_Validation_Exception exception should be thrown. Otherwise, any filter callbacks must return an array consisting of the sanitized URL Metric data. **Action:** `od_url_metric_stored` (argument: `OD_URL_Metric_Store_Request_Context`) diff --git a/plugins/optimization-detective/storage/rest-api.php b/plugins/optimization-detective/storage/rest-api.php index 198b572846..9409287692 100644 --- a/plugins/optimization-detective/storage/rest-api.php +++ b/plugins/optimization-detective/storage/rest-api.php @@ -186,85 +186,64 @@ function od_handle_rest_request( WP_REST_Request $request ) { OD_Storage_Lock::set_lock(); try { - // The "strict" URL Metric class is being used here to ensure additionalProperties of all objects are disallowed. - $url_metric = new OD_Strict_URL_Metric( - array_merge( - $data, - array( - // Now supply the readonly args which were omitted from the REST API params due to being `readonly`. - 'timestamp' => microtime( true ), - 'uuid' => wp_generate_uuid4(), - 'etag' => $request->get_param( 'current_etag' ), - ) + $data = array_merge( + $data, + array( + // Now supply the readonly args which were omitted from the REST API params due to being `readonly`. + 'timestamp' => microtime( true ), + 'uuid' => wp_generate_uuid4(), + 'etag' => $request->get_param( 'current_etag' ), ) ); - } catch ( OD_Data_Validation_Exception $e ) { - return new WP_Error( - 'rest_invalid_param', - sprintf( - /* translators: %s is exception name */ - __( 'Failed to validate URL Metric: %s', 'optimization-detective' ), - $e->getMessage() - ), - array( 'status' => 400 ) - ); - } - try { /** - * Filters whether a URL Metric is valid for storage. + * Filters the URL Metric data prior to validation for storage. * - * This allows for custom validation constraints to be applied beyond what can be expressed in JSON Schema. This is - * also necessary because the 'validate_callback' key in a JSON Schema is not respected when gathering the REST API - * endpoint args via the {@see rest_get_endpoint_args_for_schema()} function. Besides this, the REST API doesn't - * support 'validate_callback' for any nested arguments in any case, meaning that custom constraints would be able - * to be applied to multidimensional objects, such as the items inside 'elements'. + * This allows for custom sanitization and validation constraints to be applied beyond what can be expressed in + * JSON Schema. This is also necessary because the 'validate_callback' key in a JSON Schema is not respected when + * gathering the REST API endpoint args via the {@see rest_get_endpoint_args_for_schema()} function. Besides this, + * the REST API doesn't support 'validate_callback' for any nested arguments in any case, meaning that custom + * constraints would be able to be applied to multidimensional objects, such as the items inside 'elements'. * * This filter only applies when storing a URL Metric via the REST API. It does not run when a stored URL Metric is - * loaded from the od_url_metrics post type. This means that validation logic enforced via this filter can be more - * expensive, such as doing filesystem checks or HTTP requests. + * loaded from the od_url_metrics post type. This means that sanitization and validation logic enforced via this + * filter can be more expensive, such as doing filesystem checks or HTTP requests. * - * In addition to having the filter return `false` or a non-empty `WP_Error` to block storing the URL Metric, a - * plugin may also mutate the OD_URL_Metric instance passed by reference to the filter callback. This is useful - * for plugins in particular to unset extended properties which couldn't be validated using JSON Schema alone. + * To fail validation for the provided URL Metric data, an OD_Data_Validation_Exception exception should be thrown. + * Otherwise, any filter callbacks must return an array consisting of the sanitized URL Metric data. * * @since n.e.x.t * - * @param bool|WP_Error $validity Validity. Invalid if false or a WP_Error with errors. - * @param OD_Strict_URL_Metric $url_metric URL Metric, already validated against the JSON Schema. - * @param array $url_metric_data Original URL Metric data before any mutations. + * @param array $data URL Metric data. This is the Data type from OD_URL_Metric. */ - $validity = apply_filters( 'od_url_metric_storage_validity', true, $url_metric, $url_metric->jsonSerialize() ); + $data = apply_filters( 'od_url_metric_data_pre_storage', $data ); + + // The "strict" URL Metric class is being used here to ensure additionalProperties of all objects are disallowed. + $url_metric = new OD_Strict_URL_Metric( $data ); } catch ( Exception $e ) { - $error_data = array( - 'status' => 500, - ); - if ( WP_DEBUG ) { - $error_data['exception'] = array( - 'class' => get_class( $e ), - 'message' => $e->getMessage(), - 'code' => $e->getCode(), + $error_data = array(); + if ( $e instanceof OD_Data_Validation_Exception ) { + $error_message = sprintf( + /* translators: %s is exception name */ + __( 'Failed to validate URL Metric: %s', 'optimization-detective' ), + $e->getMessage() ); + $error_data['status'] = 400; + } else { + $error_message = sprintf( + /* translators: %s is exception name */ + __( 'Failed to validate URL Metric: %s', 'optimization-detective' ), + __( 'An unrecognized exception was thrown', 'optimization-detective' ) + ); + $error_data['status'] = 500; + if ( WP_DEBUG ) { + $error_data['exception_class'] = get_class( $e ); + $error_data['exception_message'] = $e->getMessage(); + $error_data['exception_code'] = $e->getCode(); + } } - $validity = new WP_Error( - 'exception', - sprintf( - /* translators: %s is the filter name 'od_url_metric_storage_validity' */ - __( 'An %s filter callback threw an exception.', 'optimization-detective' ), - 'od_url_metric_storage_validity' - ), - $error_data - ); - } - if ( false === (bool) $validity || ( $validity instanceof WP_Error && $validity->has_errors() ) ) { - if ( false === $validity ) { - $validity = new WP_Error( 'invalid_url_metric', __( 'Validity of URL Metric was rejected by filter.', 'optimization-detective' ) ); - } - $error_code = $validity->get_error_code(); - if ( ! isset( $validity->error_data[ $error_code ]['status'] ) ) { - $validity->error_data[ $error_code ]['status'] = 400; - } - return $validity; + + return new WP_Error( 'rest_invalid_param', $error_message, $error_data ); } // TODO: This should be changed from store_url_metric($slug, $url_metric) instead be update_post( $slug, $group_collection ). As it stands, store_url_metric() is duplicating logic here. @@ -277,8 +256,8 @@ function od_handle_rest_request( WP_REST_Request $request ) { 'status' => 500, ); if ( WP_DEBUG ) { - $error_data['code'] = $result->get_error_code(); - $error_data['message'] = $result->get_error_message(); + $error_data['error_code'] = $result->get_error_code(); + $error_data['error_message'] = $result->get_error_message(); } return new WP_Error( 'unable_to_store_url_metric', diff --git a/plugins/optimization-detective/tests/storage/test-rest-api.php b/plugins/optimization-detective/tests/storage/test-rest-api.php index 0caf8b5a3c..01684eb3ae 100644 --- a/plugins/optimization-detective/tests/storage/test-rest-api.php +++ b/plugins/optimization-detective/tests/storage/test-rest-api.php @@ -41,14 +41,14 @@ static function ( array $properties ) use ( $property_name ): array { }; return array( - 'not_extended' => array( + 'not_extended' => array( 'set_up' => function (): array { return $this->get_valid_params(); }, 'expect_stored' => true, 'expected_status' => 200, ), - 'extended' => array( + 'extended' => array( 'set_up' => function () use ( $add_root_extra_property ): array { $add_root_extra_property( 'extra' ); $params = $this->get_valid_params(); @@ -58,84 +58,39 @@ static function ( array $properties ) use ( $property_name ): array { 'expect_stored' => true, 'expected_status' => 200, ), - 'extended_but_unset' => array( - 'set_up' => function () use ( $add_root_extra_property ): array { - $add_root_extra_property( 'unset_prop' ); - add_filter( - 'od_url_metric_storage_validity', - static function ( $validity, OD_URL_Metric $url_metric ) { - $url_metric->unset( 'extra' ); - return $validity; - }, - 10, - 2 - ); - $params = $this->get_valid_params(); - $params['unset_prop'] = 'bar'; - return $params; - }, - 'expect_stored' => true, - 'expected_status' => 200, - ), - 'extended_and_rejected_by_returning_false' => array( - 'set_up' => function () use ( $add_root_extra_property ): array { - $add_root_extra_property( 'extra' ); - add_filter( 'od_url_metric_storage_validity', '__return_false' ); - - $params = $this->get_valid_params(); - $params['extra'] = 'foo'; - return $params; - }, - 'expect_stored' => false, - 'expected_status' => 400, - ), - 'extended_and_rejected_by_returning_wp_error' => array( - 'set_up' => function () use ( $add_root_extra_property ): array { - $add_root_extra_property( 'extra' ); - add_filter( - 'od_url_metric_storage_validity', - static function () { - return new WP_Error( 'rejected', 'Rejected!' ); - } - ); - - $params = $this->get_valid_params(); - $params['extra'] = 'foo'; - return $params; - }, - 'expect_stored' => false, - 'expected_status' => 400, - ), - 'rejected_by_returning_wp_error_with_forbidden_status' => array( + 'rejected_by_generic_exception' => array( 'set_up' => function (): array { add_filter( - 'od_url_metric_storage_validity', - static function () { - return new WP_Error( 'forbidden', 'Forbidden!', array( 'status' => 403 ) ); + 'od_url_metric_data_pre_storage', + static function ( $data ): array { + if ( count( $data ) > 0 ) { + throw new Exception( 'bad' ); + } + return $data; } ); return $this->get_valid_params(); }, 'expect_stored' => false, - 'expected_status' => 403, + 'expected_status' => 500, ), - 'rejected_by_exception' => array( + 'rejected_by_validation_exception' => array( 'set_up' => function (): array { add_filter( - 'od_url_metric_storage_validity', - static function ( $validity, OD_URL_Metric $url_metric ) { - $url_metric->unset( 'viewport' ); - return $validity; - }, - 10, - 2 + 'od_url_metric_data_pre_storage', + static function ( $data ): array { + if ( count( $data ) > 0 ) { + throw new OD_Data_Validation_Exception( 'bad' ); + } + return $data; + } ); return $this->get_valid_params(); }, 'expect_stored' => false, - 'expected_status' => 500, + 'expected_status' => 400, ), - 'with_cache_purge_post_id' => array( + 'with_cache_purge_post_id' => array( 'set_up' => function (): array { $params = $this->get_valid_params(); $params['cache_purge_post_id'] = self::factory()->post->create(); @@ -160,22 +115,17 @@ static function ( $validity, OD_URL_Metric $url_metric ) { * @covers ::od_trigger_page_cache_invalidation */ public function test_rest_request_good_params( Closure $set_up, bool $expect_stored, int $expected_status ): void { - $filtered_url_metric_obj = null; $filtered_url_metric_data = null; $filter_called = 0; add_filter( - 'od_url_metric_storage_validity', - function ( $validity, $url_metric, $url_metric_data ) use ( &$filter_called, &$filtered_url_metric_obj, &$filtered_url_metric_data ) { - $this->assertTrue( $validity ); - $this->assertInstanceOf( OD_URL_Metric::class, $url_metric ); + 'od_url_metric_data_pre_storage', + function ( $url_metric_data ) use ( &$filter_called, &$filtered_url_metric_data ) { $this->assertIsArray( $url_metric_data ); - $filtered_url_metric_obj = $url_metric; $filtered_url_metric_data = $url_metric_data; $filter_called++; - return $validity; + return $url_metric_data; }, - 0, - 3 + 0 ); $stored_context = null; @@ -233,7 +183,6 @@ function ( OD_URL_Metric_Store_Request_Context $context ) use ( &$stored_context ); $this->assertInstanceOf( OD_URL_Metric_Store_Request_Context::class, $stored_context ); - $this->assertSame( $stored_context->url_metric, $filtered_url_metric_obj ); $this->assertSame( $stored_context->url_metric->jsonSerialize(), $filtered_url_metric_data ); // Now check that od_trigger_page_cache_invalidation() cleaned caches as expected. diff --git a/plugins/optimization-detective/tests/test-class-od-element.php b/plugins/optimization-detective/tests/test-class-od-element.php index 1b980d421a..52a4cc43ff 100644 --- a/plugins/optimization-detective/tests/test-class-od-element.php +++ b/plugins/optimization-detective/tests/test-class-od-element.php @@ -24,7 +24,6 @@ class Test_OD_Element extends WP_UnitTestCase { * @covers ::offsetExists * @covers ::offsetGet * @covers ::offsetSet - * @covers ::unset * @covers ::offsetUnset * @covers ::jsonSerialize */ @@ -131,68 +130,22 @@ static function ( array $schema ): array { $this->assertTrue( isset( $element['customProp'] ) ); $this->assertTrue( $element->offsetExists( 'customProp' ) ); - // Try setting property (which is not currently supported). - $functions = array( - static function ( OD_Element $element ): void { - $element['isLCP'] = true; - }, - static function ( OD_Element $element ): void { - $element->offsetSet( 'isLCP', true ); - }, - ); - foreach ( $functions as $function ) { - $exception = null; - try { - $function( $element ); - } catch ( Exception $e ) { - $exception = $e; - } - $this->assertInstanceOf( Exception::class, $exception ); - $this->assertFalse( $element->get( 'isLCP' ) ); - } + $this->assertEquals( $element_data, $element->jsonSerialize() ); - // Try unsetting a required property. - $functions = array( - static function ( OD_Element $element ): void { - unset( $element['isLCP'] ); - }, - static function ( OD_Element $element ): void { - $element->unset( 'isLCP' ); - }, - static function ( OD_Element $element ): void { - $element->offsetUnset( 'isLCP' ); - }, - ); - foreach ( $functions as $function ) { - $exception = null; - try { - $function( $element ); - } catch ( Exception $e ) { - $exception = $e; - } - $this->assertInstanceOf( Exception::class, $exception ); - $this->assertArrayHasKey( 'isLCP', $element->jsonSerialize() ); + $exception = null; + try { + $element['isLCP'] = true; + } catch ( Exception $e ) { + $exception = $e; } + $this->assertInstanceOf( Exception::class, $exception ); - $this->assertEquals( $element_data, $element->jsonSerialize() ); - - // Try unsetting a non-required property. - $functions = array( - static function ( OD_Element $element ): void { - unset( $element['customProp'] ); - }, - static function ( OD_Element $element ): void { - $element->unset( 'customProp' ); - }, - static function ( OD_Element $element ): void { - $element->offsetUnset( 'customProp' ); - }, - ); - foreach ( $functions as $function ) { - $cloned_element = clone $element; - $function( $cloned_element ); - $this->assertFalse( $cloned_element->offsetExists( 'customProp' ) ); - $this->assertArrayNotHasKey( 'customProp', $cloned_element->jsonSerialize() ); + $exception = null; + try { + unset( $element['isLCP'] ); + } catch ( Exception $e ) { // @phpstan-ignore catch.neverThrown (It is thrown by offsetUnset actually.) + $exception = $e; } + $this->assertInstanceOf( Exception::class, $exception ); // @phpstan-ignore method.impossibleType (It is thrown by offsetUnset actually.) } } 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 df2fa01442..51b23c243e 100644 --- a/plugins/optimization-detective/tests/test-class-od-url-metric.php +++ b/plugins/optimization-detective/tests/test-class-od-url-metric.php @@ -277,7 +277,6 @@ static function ( $value ) { * @covers ::get_json_schema * @covers ::set_group * @covers ::get_group - * @covers ::unset * * @dataProvider data_provider_to_test_constructor * @@ -337,15 +336,6 @@ static function ( OD_Element $element ) { $this->assertTrue( wp_is_uuid( $url_metric->get_uuid() ) ); $this->assertSame( $url_metric->get_uuid(), $url_metric->get( 'uuid' ) ); - $exception = null; - try { - $url_metric->unset( 'elements' ); - } catch ( OD_Data_Validation_Exception $e ) { - $exception = $e; - } - $this->assertInstanceOf( OD_Data_Validation_Exception::class, $exception ); - $url_metric->unset( 'does_not_exist' ); - $serialized = $url_metric->jsonSerialize(); if ( ! array_key_exists( 'uuid', $data ) ) { $this->assertTrue( wp_is_uuid( $serialized['uuid'] ) ); @@ -407,12 +397,6 @@ static function ( array $properties ): array { $this->assertArrayHasKey( 'isTouch', $extended_data ); $this->assertTrue( $extended_data['isTouch'] ); $this->assertTrue( $extended_url_metric->get( 'isTouch' ) ); - - $this->assertTrue( $extended_url_metric->get( 'isTouch' ) ); - $extended_url_metric->unset( 'isTouch' ); - $this->assertNull( $extended_url_metric->get( 'isTouch' ) ); - $extended_data = $extended_url_metric->jsonSerialize(); - $this->assertArrayNotHasKey( 'isTouch', $extended_data ); }, ), @@ -505,13 +489,6 @@ static function ( array $properties ): array { $this->assertArrayHasKey( 'isColorful', $extended_data['elements'][0] ); $this->assertFalse( $extended_data['elements'][0]['isColorful'] ); $this->assertFalse( $extended_url_metric->get_elements()[0]['isColorful'] ); - - $element = $extended_url_metric->get_elements()[0]; - $this->assertFalse( $element->get( 'isColorful' ) ); - $element->unset( 'isColorful' ); - $this->assertNull( $element->get( 'isColorful' ) ); - $extended_data = $extended_url_metric->jsonSerialize(); - $this->assertArrayNotHasKey( 'isColorful', $extended_data['elements'][0] ); }, ), @@ -577,8 +554,6 @@ static function ( array $properties ): array { * Tests construction with extended schema. * * @covers ::get_json_schema - * @covers ::unset - * @covers OD_Element::unset * * @dataProvider data_provider_to_test_constructor_with_extended_schema * diff --git a/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php b/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php index 957acd1a44..ef05fc2ab4 100644 --- a/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php +++ b/plugins/optimization-detective/tests/test-class-od-url-metrics-group-collection.php @@ -204,8 +204,6 @@ public function data_provider_sample_size_and_breakpoints(): array { * * @covers ::clear_cache * @covers OD_URL_Metric_Group::clear_cache - * @covers OD_URL_Metric::unset - * @covers OD_Element::unset */ public function test_clear_cache(): void { $collection = new OD_URL_Metric_Group_Collection( array(), md5( '' ), array(), 1, DAY_IN_SECONDS ); @@ -226,58 +224,13 @@ public function test_clear_cache(): void { $this->assertSame( array(), $collection_result_cache_reflection_property->getValue( $collection ) ); // Test that adding a URL metric to a collection clears the caches. - add_filter( - 'od_url_metric_schema_root_additional_properties', - static function ( $schema ) { - $schema['new_prop_at_root'] = array( - 'type' => 'string', - ); - return $schema; - } - ); - add_filter( - 'od_url_metric_schema_element_additional_properties', - static function ( $schema ) { - $schema['new_prop_at_element'] = array( - 'type' => 'string', - ); - return $schema; - } - ); $collection_result_cache_reflection_property->setValue( $collection, $populated_value ); $group_result_cache_reflection_property->setValue( $group, $populated_value ); - $collection->add_url_metric( - $this->get_sample_url_metric( - array( - 'element' => array( - 'xpath' => '/*[1][self::HTML]/*[2][self::BODY]/*[1][self::IMG]', - 'new_prop_at_element' => 'hey there', - ), - 'extended_root' => array( - 'new_prop_at_root' => 'hola', - ), - ) - ) - ); + $collection->add_url_metric( $this->get_sample_url_metric( array() ) ); $url_metric = $group->getIterator()->current(); $this->assertInstanceOf( OD_URL_Metric::class, $url_metric ); $this->assertSame( array(), $collection_result_cache_reflection_property->getValue( $collection ) ); $this->assertSame( array(), $group_result_cache_reflection_property->getValue( $group ) ); - - // Test that modifying a URL Metric empties the cache of the collection and the group. - $collection_result_cache_reflection_property->setValue( $collection, $populated_value ); - $group_result_cache_reflection_property->setValue( $group, $populated_value ); - $url_metric->unset( 'new_prop_at_root' ); - $this->assertSame( array(), $collection_result_cache_reflection_property->getValue( $collection ) ); - $this->assertSame( array(), $group_result_cache_reflection_property->getValue( $group ) ); - - // Test that modifying a URL Metric element empties the cache of the collection and the group. - $element = $url_metric->get_elements()[0]; - $collection_result_cache_reflection_property->setValue( $collection, $populated_value ); - $group_result_cache_reflection_property->setValue( $group, $populated_value ); - $element->unset( 'new_prop_at_element' ); - $this->assertSame( array(), $collection_result_cache_reflection_property->getValue( $collection ) ); - $this->assertSame( array(), $group_result_cache_reflection_property->getValue( $group ) ); } /** From f74f4f4b1b08dfb71b4eb0fc25d03e8859dad330 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 16 Dec 2024 11:19:18 -0800 Subject: [PATCH 211/237] Rename filter --- plugins/image-prioritizer/helper.php | 2 +- plugins/image-prioritizer/hooks.php | 2 +- plugins/image-prioritizer/tests/test-helper.php | 12 ++++++------ plugins/image-prioritizer/tests/test-hooks.php | 2 +- plugins/optimization-detective/readme.txt | 2 +- plugins/optimization-detective/storage/rest-api.php | 2 +- .../tests/storage/test-rest-api.php | 6 +++--- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 9b1c72c158..c710ddca57 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -275,7 +275,7 @@ static function ( $host ) { * * @noinspection PhpDocMissingThrowsInspection */ -function image_prioritizer_filter_url_metric_data_pre_storage( $data ): array { +function image_prioritizer_filter_store_url_metric_data( $data ): array { if ( ! is_array( $data ) ) { $data = array(); } diff --git a/plugins/image-prioritizer/hooks.php b/plugins/image-prioritizer/hooks.php index 63420cd6d6..d257ed7353 100644 --- a/plugins/image-prioritizer/hooks.php +++ b/plugins/image-prioritizer/hooks.php @@ -13,4 +13,4 @@ add_action( 'od_init', 'image_prioritizer_init' ); add_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ); add_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ); -add_filter( 'od_url_metric_data_pre_storage', 'image_prioritizer_filter_url_metric_data_pre_storage' ); +add_filter( 'od_store_url_metric_data', 'image_prioritizer_filter_store_url_metric_data' ); diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index 05de496356..8ecdd4a335 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -747,7 +747,7 @@ public function test_image_prioritizer_validate_background_image_url( Closure $s * * @return array */ - public function data_provider_to_test_image_prioritizer_filter_url_metric_data_pre_storage(): array { + public function data_provider_to_test_image_prioritizer_filter_store_url_metric_data(): array { return array( 'invalid_external_bg_image' => array( 'set_up' => static function ( array $url_metric_data ): array { @@ -835,17 +835,17 @@ static function ( $pre, $parsed_args, $url ) use ( $image_url ) { } /** - * Tests image_prioritizer_filter_url_metric_data_pre_storage(). + * Tests image_prioritizer_filter_store_url_metric_data(). * - * @dataProvider data_provider_to_test_image_prioritizer_filter_url_metric_data_pre_storage + * @dataProvider data_provider_to_test_image_prioritizer_filter_store_url_metric_data * - * @covers ::image_prioritizer_filter_url_metric_data_pre_storage + * @covers ::image_prioritizer_filter_store_url_metric_data * @covers ::image_prioritizer_validate_background_image_url */ - public function test_image_prioritizer_filter_url_metric_data_pre_storage( Closure $set_up, Closure $assert ): void { + public function test_image_prioritizer_filter_store_url_metric_data( Closure $set_up, Closure $assert ): void { $url_metric_data = $set_up( $this->get_sample_url_metric( array() )->jsonSerialize() ); - $url_metric_data = image_prioritizer_filter_url_metric_data_pre_storage( $url_metric_data ); + $url_metric_data = image_prioritizer_filter_store_url_metric_data( $url_metric_data ); $assert( $url_metric_data ); } diff --git a/plugins/image-prioritizer/tests/test-hooks.php b/plugins/image-prioritizer/tests/test-hooks.php index 416523cb18..4d34043d3f 100644 --- a/plugins/image-prioritizer/tests/test-hooks.php +++ b/plugins/image-prioritizer/tests/test-hooks.php @@ -14,6 +14,6 @@ public function test_hooks_added(): void { $this->assertEquals( 10, has_action( 'od_init', 'image_prioritizer_init' ) ); $this->assertEquals( 10, has_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ) ); $this->assertEquals( 10, has_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ) ); - $this->assertEquals( 10, has_filter( 'od_url_metric_data_pre_storage', 'image_prioritizer_filter_url_metric_data_pre_storage' ) ); + $this->assertEquals( 10, has_filter( 'od_store_url_metric_data', 'image_prioritizer_filter_store_url_metric_data' ) ); } } diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index d78c4864d0..c21b3a523c 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -252,7 +252,7 @@ Filters the URL Metric data prior to validation for storage. This allows for custom sanitization and validation constraints to be applied beyond what can be expressed in JSON Schema. This filter only applies when storing a URL Metric via the REST API. It does not run when a stored URL Metric is loaded from the od_url_metrics post type. This means that sanitization and validation logic enforced via this filter can be more expensive, such as doing filesystem checks or HTTP requests. -To fail validation for the provided URL Metric data, an OD_Data_Validation_Exception exception should be thrown. Otherwise, any filter callbacks must return an array consisting of the sanitized URL Metric data. +To fail validation for the provided URL Metric data, an `OD_Data_Validation_Exception` exception should be thrown. Otherwise, any filter callbacks must return an array consisting of the sanitized URL Metric data. **Action:** `od_url_metric_stored` (argument: `OD_URL_Metric_Store_Request_Context`) diff --git a/plugins/optimization-detective/storage/rest-api.php b/plugins/optimization-detective/storage/rest-api.php index 9409287692..204a44140d 100644 --- a/plugins/optimization-detective/storage/rest-api.php +++ b/plugins/optimization-detective/storage/rest-api.php @@ -216,7 +216,7 @@ function od_handle_rest_request( WP_REST_Request $request ) { * * @param array $data URL Metric data. This is the Data type from OD_URL_Metric. */ - $data = apply_filters( 'od_url_metric_data_pre_storage', $data ); + $data = apply_filters( 'od_store_url_metric_data', $data ); // The "strict" URL Metric class is being used here to ensure additionalProperties of all objects are disallowed. $url_metric = new OD_Strict_URL_Metric( $data ); diff --git a/plugins/optimization-detective/tests/storage/test-rest-api.php b/plugins/optimization-detective/tests/storage/test-rest-api.php index 01684eb3ae..8817e73375 100644 --- a/plugins/optimization-detective/tests/storage/test-rest-api.php +++ b/plugins/optimization-detective/tests/storage/test-rest-api.php @@ -61,7 +61,7 @@ static function ( array $properties ) use ( $property_name ): array { 'rejected_by_generic_exception' => array( 'set_up' => function (): array { add_filter( - 'od_url_metric_data_pre_storage', + 'od_store_url_metric_data', static function ( $data ): array { if ( count( $data ) > 0 ) { throw new Exception( 'bad' ); @@ -77,7 +77,7 @@ static function ( $data ): array { 'rejected_by_validation_exception' => array( 'set_up' => function (): array { add_filter( - 'od_url_metric_data_pre_storage', + 'od_store_url_metric_data', static function ( $data ): array { if ( count( $data ) > 0 ) { throw new OD_Data_Validation_Exception( 'bad' ); @@ -118,7 +118,7 @@ public function test_rest_request_good_params( Closure $set_up, bool $expect_sto $filtered_url_metric_data = null; $filter_called = 0; add_filter( - 'od_url_metric_data_pre_storage', + 'od_store_url_metric_data', function ( $url_metric_data ) use ( &$filter_called, &$filtered_url_metric_data ) { $this->assertIsArray( $url_metric_data ); $filtered_url_metric_data = $url_metric_data; From 6856026e287becca40d4bca887fb73aef5427618 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 16 Dec 2024 14:20:11 -0800 Subject: [PATCH 212/237] Further optimize WP_Query Co-authored-by: swissspidy --- plugins/image-prioritizer/helper.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index c710ddca57..884adf0c98 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -170,11 +170,13 @@ static function ( $host ) { // Obtain the host of an image attachment's URL in case a CDN is pointing all images to an origin other than the home or site URLs. $image_attachment_query = new WP_Query( array( - 'post_type' => 'attachment', - 'post_mime_type' => 'image', - 'post_status' => 'inherit', - 'posts_per_page' => 1, - 'fields' => 'ids', + 'post_type' => 'attachment', + 'post_mime_type' => 'image', + 'post_status' => 'inherit', + 'posts_per_page' => 1, + 'fields' => 'ids', + 'no_found_rows' => true, + 'update_post_term_cache' => false, // Note that update_post_meta_cache is not included as well because wp_get_attachment_image_src() needs postmeta. ) ); if ( isset( $image_attachment_query->posts[0] ) && is_int( $image_attachment_query->posts[0] ) ) { From e1d0ac9dd935634b782d711c7e1ae85d296f44cf Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 16 Dec 2024 14:23:14 -0800 Subject: [PATCH 213/237] Improve translatability of error message Co-authored-by: swissspidy --- plugins/image-prioritizer/helper.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 884adf0c98..f16f2eb08a 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -290,7 +290,15 @@ function image_prioritizer_filter_store_url_metric_data( $data ): array { * * @noinspection PhpUnhandledExceptionInspection */ - wp_trigger_error( __FUNCTION__, $image_validity->get_error_message() . ' Background image URL: ' . $data['lcpElementExternalBackgroundImage']['url'] ); + wp_trigger_error( + __FUNCTION__, + sprintf( + /* translators: 1: error message. 2: image url */ + __( 'Error: %1$s. Background image URL: %2$s.', 'image-prioritizer' ), + rtrim( $image_validity->get_error_message(), '.' ), + $data['lcpElementExternalBackgroundImage']['url'] + ) + ); unset( $data['lcpElementExternalBackgroundImage'] ); } } From 783c32933c10181d94614c32ec3e2d91c235f11f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 23:22:23 +0000 Subject: [PATCH 214/237] Bump squizlabs/php_codesniffer from 3.11.1 to 3.11.2 Bumps [squizlabs/php_codesniffer](https://github.com/PHPCSStandards/PHP_CodeSniffer) from 3.11.1 to 3.11.2. - [Release notes](https://github.com/PHPCSStandards/PHP_CodeSniffer/releases) - [Changelog](https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/CHANGELOG.md) - [Commits](https://github.com/PHPCSStandards/PHP_CodeSniffer/compare/3.11.1...3.11.2) --- updated-dependencies: - dependency-name: squizlabs/php_codesniffer dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- composer.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/composer.lock b/composer.lock index 07069b38ca..c420d663cd 100644 --- a/composer.lock +++ b/composer.lock @@ -2132,16 +2132,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.11.1", + "version": "3.11.2", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87" + "reference": "1368f4a58c3c52114b86b1abe8f4098869cb0079" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", - "reference": "19473c30efe4f7b3cd42522d0b2e6e7f243c6f87", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/1368f4a58c3c52114b86b1abe8f4098869cb0079", + "reference": "1368f4a58c3c52114b86b1abe8f4098869cb0079", "shasum": "" }, "require": { @@ -2208,7 +2208,7 @@ "type": "open_collective" } ], - "time": "2024-11-16T12:02:36+00:00" + "time": "2024-12-11T16:04:26+00:00" }, { "name": "symfony/polyfill-php73", @@ -2579,14 +2579,14 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": {}, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^7.2 || ^8.0", "ext-json": "*" }, - "platform-dev": {}, + "platform-dev": [], "platform-overrides": { "php": "7.2" }, From a62065d2bd14a4094cc9c2019dd4308059e462cf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 00:02:27 +0000 Subject: [PATCH 215/237] Bump lint-staged from 15.2.10 to 15.2.11 Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 15.2.10 to 15.2.11. - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/master/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v15.2.10...v15.2.11) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- package-lock.json | 90 +++++++++++++++++++++++++---------------------- package.json | 2 +- 2 files changed, 49 insertions(+), 43 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c2d919131..712f788d93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "fast-glob": "^3.3.2", "fs-extra": "^11.2.0", "husky": "^9.1.7", - "lint-staged": "^15.2.10", + "lint-staged": "^15.2.11", "lodash": "4.17.21", "micromatch": "^4.0.8", "npm-run-all": "^4.1.5", @@ -6637,9 +6637,9 @@ } }, "node_modules/cli-truncate/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { "node": ">=12" @@ -6649,9 +6649,9 @@ } }, "node_modules/cli-truncate/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true }, "node_modules/cli-truncate/node_modules/string-width": { @@ -10802,9 +10802,9 @@ } }, "node_modules/get-east-asian-width": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", - "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", "dev": true, "engines": { "node": ">=18" @@ -13670,9 +13670,9 @@ } }, "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "dev": true, "engines": { "node": ">=14" @@ -13697,21 +13697,21 @@ } }, "node_modules/lint-staged": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", - "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", + "version": "15.2.11", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.11.tgz", + "integrity": "sha512-Ev6ivCTYRTGs9ychvpVw35m/bcNDuBN+mnTeObCL5h+boS5WzBEC6LHI4I9F/++sZm1m+J2LEiy0gxL/R9TBqQ==", "dev": true, "dependencies": { "chalk": "~5.3.0", "commander": "~12.1.0", - "debug": "~4.3.6", + "debug": "~4.4.0", "execa": "~8.0.1", - "lilconfig": "~3.1.2", - "listr2": "~8.2.4", + "lilconfig": "~3.1.3", + "listr2": "~8.2.5", "micromatch": "~4.0.8", "pidtree": "~0.6.0", "string-argv": "~0.3.2", - "yaml": "~2.5.0" + "yaml": "~2.6.1" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -13750,12 +13750,12 @@ } }, "node_modules/lint-staged/node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -13834,6 +13834,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lint-staged/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "node_modules/lint-staged/node_modules/npm-run-path": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", @@ -13937,9 +13943,9 @@ } }, "node_modules/lint-staged/node_modules/yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", + "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", "dev": true, "bin": { "yaml": "bin.mjs" @@ -13949,9 +13955,9 @@ } }, "node_modules/listr2": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", - "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", + "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", "dev": true, "dependencies": { "cli-truncate": "^4.0.0", @@ -13966,9 +13972,9 @@ } }, "node_modules/listr2/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { "node": ">=12" @@ -13990,9 +13996,9 @@ } }, "node_modules/listr2/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true }, "node_modules/listr2/node_modules/eventemitter3": { @@ -14221,9 +14227,9 @@ } }, "node_modules/log-update/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { "node": ">=12" @@ -14260,9 +14266,9 @@ } }, "node_modules/log-update/node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", "dev": true }, "node_modules/log-update/node_modules/is-fullwidth-code-point": { diff --git a/package.json b/package.json index 123184509d..b24eec8957 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "fast-glob": "^3.3.2", "fs-extra": "^11.2.0", "husky": "^9.1.7", - "lint-staged": "^15.2.10", + "lint-staged": "^15.2.11", "lodash": "4.17.21", "micromatch": "^4.0.8", "npm-run-all": "^4.1.5", From 5bce29caff96491dcb126c1d3d906d56f5329193 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 00:02:51 +0000 Subject: [PATCH 216/237] Bump @wordpress/scripts from 30.6.0 to 30.7.0 Bumps [@wordpress/scripts](https://github.com/WordPress/gutenberg/tree/HEAD/packages/scripts) from 30.6.0 to 30.7.0. - [Release notes](https://github.com/WordPress/gutenberg/releases) - [Changelog](https://github.com/WordPress/gutenberg/blob/trunk/packages/scripts/CHANGELOG.md) - [Commits](https://github.com/WordPress/gutenberg/commits/@wordpress/scripts@30.7.0/packages/scripts) --- updated-dependencies: - dependency-name: "@wordpress/scripts" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 1131 ++++++++++++++++++++++++++------------------- package.json | 2 +- 2 files changed, 650 insertions(+), 483 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c2d919131..3f6cd818e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@octokit/rest": "^21.0.2", "@wordpress/env": "^10.13.0", "@wordpress/prettier-config": "^4.13.0", - "@wordpress/scripts": "^30.6.0", + "@wordpress/scripts": "^30.7.0", "commander": "12.1.0", "copy-webpack-plugin": "^12.0.2", "css-minimizer-webpack-plugin": "^7.0.0", @@ -2970,6 +2970,302 @@ "@octokit/openapi-types": "^22.2.0" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.0.tgz", + "integrity": "sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.0", + "@parcel/watcher-darwin-arm64": "2.5.0", + "@parcel/watcher-darwin-x64": "2.5.0", + "@parcel/watcher-freebsd-x64": "2.5.0", + "@parcel/watcher-linux-arm-glibc": "2.5.0", + "@parcel/watcher-linux-arm-musl": "2.5.0", + "@parcel/watcher-linux-arm64-glibc": "2.5.0", + "@parcel/watcher-linux-arm64-musl": "2.5.0", + "@parcel/watcher-linux-x64-glibc": "2.5.0", + "@parcel/watcher-linux-x64-musl": "2.5.0", + "@parcel/watcher-win32-arm64": "2.5.0", + "@parcel/watcher-win32-ia32": "2.5.0", + "@parcel/watcher-win32-x64": "2.5.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz", + "integrity": "sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz", + "integrity": "sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz", + "integrity": "sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz", + "integrity": "sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz", + "integrity": "sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz", + "integrity": "sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz", + "integrity": "sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz", + "integrity": "sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz", + "integrity": "sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz", + "integrity": "sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz", + "integrity": "sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz", + "integrity": "sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz", + "integrity": "sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -3055,15 +3351,15 @@ "dev": true }, "node_modules/@puppeteer/browsers": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.0.tgz", - "integrity": "sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz", + "integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==", "dev": true, "dependencies": { - "debug": "^4.3.6", + "debug": "^4.4.0", "extract-zip": "^2.0.1", "progress": "^2.0.3", - "proxy-agent": "^6.4.0", + "proxy-agent": "^6.5.0", "semver": "^7.6.3", "tar-fs": "^3.0.6", "unbzip2-stream": "^1.4.3", @@ -3077,9 +3373,9 @@ } }, "node_modules/@puppeteer/browsers/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "dependencies": { "ms": "^2.1.3" @@ -3253,9 +3549,9 @@ "dev": true }, "node_modules/@sideway/address": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", - "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", "dev": true, "dependencies": { "@hapi/hoek": "^9.0.0" @@ -3815,6 +4111,26 @@ "@types/node": "*" } }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -4437,148 +4753,148 @@ "dev": true }, "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", - "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", - "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", - "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-opt": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1", - "@webassemblyjs/wast-printer": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", - "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", - "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", - "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", - "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "dependencies": { - "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, @@ -4890,11 +5206,10 @@ } }, "node_modules/@wordpress/scripts": { - "version": "30.6.0", - "resolved": "https://registry.npmjs.org/@wordpress/scripts/-/scripts-30.6.0.tgz", - "integrity": "sha512-2i6wqCdvCcf00/vLi7twNzHUdAAOo8Uk0lqntZiBKpVrjHyLkzjmPwT3So6W/VYso5QMzEXRXYVHVKGE4wX4rg==", + "version": "30.7.0", + "resolved": "https://registry.npmjs.org/@wordpress/scripts/-/scripts-30.7.0.tgz", + "integrity": "sha512-vwrf6Xo1GXV2ug4xdYMgZ2CVpNNfArOEJyX6w9CafIRmLOm8GkVGSza0VlEoOh1BTqQPv/awq6uiOKVMbVNB5Q==", "dev": true, - "license": "GPL-2.0-or-later", "dependencies": { "@babel/core": "7.25.7", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.11", @@ -4917,7 +5232,7 @@ "check-node-version": "^4.1.0", "clean-webpack-plugin": "^3.0.0", "copy-webpack-plugin": "^10.2.0", - "cross-spawn": "^5.1.0", + "cross-spawn": "^7.0.6", "css-loader": "^6.2.0", "cssnano": "^6.0.1", "cwd": "^0.10.0", @@ -4927,32 +5242,32 @@ "fast-glob": "^3.2.7", "filenamify": "^4.2.0", "jest": "^29.6.2", - "jest-dev-server": "^9.0.1", + "jest-dev-server": "^10.1.4", "jest-environment-jsdom": "^29.6.2", "jest-environment-node": "^29.6.2", "json2php": "^0.0.9", "markdownlint-cli": "^0.31.1", "merge-deep": "^3.0.3", - "mini-css-extract-plugin": "^2.5.1", + "mini-css-extract-plugin": "^2.9.2", "minimist": "^1.2.0", "npm-package-json-lint": "^6.4.0", "npm-packlist": "^3.0.0", "postcss": "^8.4.5", "postcss-loader": "^6.2.1", "prettier": "npm:wp-prettier@3.0.3", - "puppeteer-core": "^23.1.0", + "puppeteer-core": "^23.10.1", "react-refresh": "^0.14.0", "read-pkg-up": "^7.0.1", "resolve-bin": "^0.4.0", "rtlcss-webpack-plugin": "^4.0.7", - "sass": "^1.35.2", - "sass-loader": "^12.1.0", + "sass": "^1.50.1", + "sass-loader": "^16.0.3", "schema-utils": "^4.2.0", "source-map-loader": "^3.0.0", "stylelint": "^16.8.2", "terser-webpack-plugin": "^5.3.10", "url-loader": "^4.1.1", - "webpack": "^5.95.0", + "webpack": "^5.97.0", "webpack-bundle-analyzer": "^4.9.1", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.1" @@ -5151,9 +5466,9 @@ } }, "node_modules/acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -5172,15 +5487,6 @@ "acorn-walk": "^8.0.2" } }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -5689,12 +5995,12 @@ } }, "node_modules/axios": { - "version": "1.6.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", - "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "dev": true, "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -5955,9 +6261,9 @@ "optional": true }, "node_modules/bare-fs": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.3.tgz", - "integrity": "sha512-7RYKL+vZVCyAsMLi5SPu7QGauGGT8avnP/HO571ndEuV4MYdGXvLhtW67FuLPeEI8EiIY7zbbRR9x7x7HU0kgw==", + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.5.tgz", + "integrity": "sha512-SlE9eTxifPDJrT6YgemQ1WGFleevzwY+XAP1Xqgl56HtcrisC2CHCZ2tq6dBpcH2TnNxwUEUGhweo+lrQtYuiw==", "dev": true, "optional": true, "dependencies": { @@ -5967,9 +6273,9 @@ } }, "node_modules/bare-os": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.2.tgz", - "integrity": "sha512-HZoJwzC+rZ9lqEemTMiO0luOePoGYNBgsLLgegKR/cljiJvcDNhDZQkzC+NC5Oh0aHbdBNSOHpghwMuB5tqhjg==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.4.tgz", + "integrity": "sha512-z3UiI2yi1mK0sXeRdc4O1Kk8aOa/e+FNWZcTiPB/dfTWyLypuE99LibgRaQki914Jq//yAWylcAt+mknKdixRQ==", "dev": true, "optional": true }, @@ -5984,14 +6290,13 @@ } }, "node_modules/bare-stream": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.2.1.tgz", - "integrity": "sha512-YTB47kHwBW9zSG8LD77MIBAAQXjU2WjAkMHeeb7hUplVs6+IoM5I7uEVQNPMB7lj9r8I76UMdoMkGnCodHOLqg==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.6.1.tgz", + "integrity": "sha512-eVZbtKM+4uehzrsj49KtCy3Pbg7kO1pJ3SKZ1SFrIH/0pnj9scuGGgUlNDf/7qS8WKtGdiJY5Kyhs/ivYPTB/g==", "dev": true, "optional": true, "dependencies": { - "b4a": "^1.6.6", - "streamx": "^2.18.0" + "streamx": "^2.21.0" } }, "node_modules/base64-js": { @@ -6546,9 +6851,9 @@ } }, "node_modules/chromium-bidi": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.6.5.tgz", - "integrity": "sha512-RuLrmzYrxSb0s9SgpB+QN5jJucPduZQ/9SIe76MDxYJuecPW5mxMdacJ1f4EtgiV+R0p3sCkznTMvH0MPGFqjA==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.8.0.tgz", + "integrity": "sha512-uJydbGdTw0DEUjhoogGveneJVWX/9YuqkWePzMmkBYwtdAqo5d3J/ovNKFr+/2hWXYmYCr6it8mSSTIj6SS6Ug==", "dev": true, "dependencies": { "mitt": "3.0.1", @@ -7256,31 +7561,54 @@ } }, "node_modules/cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/cross-spawn/node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "node_modules/cross-spawn/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/cross-spawn/node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true + "node_modules/cross-spawn/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } }, "node_modules/crypto-random-string": { "version": "2.0.0", @@ -8598,6 +8926,19 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "optional": true, + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -9689,20 +10030,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/eslint/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/eslint/node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -9742,41 +10069,20 @@ "engines": { "node": ">=8" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eslint/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, "node_modules/eslint/node_modules/type-fest": { @@ -9791,21 +10097,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -9937,20 +10228,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/execa/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/execa/node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -9963,42 +10240,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/execa/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/execa/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/execa/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -11516,9 +11757,9 @@ "dev": true }, "node_modules/immutable": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.2.tgz", - "integrity": "sha512-oGXzbEDem9OOpDWZu88jGiYCvIsLHMvGw+8OXlpsvTFvIQplQbjg1B1cvKg8f7Hoch6+NGjpPsH1Fr+Mc2D1aA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", + "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", "dev": true }, "node_modules/import-fresh": { @@ -12529,18 +12770,18 @@ } }, "node_modules/jest-dev-server": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jest-dev-server/-/jest-dev-server-9.0.2.tgz", - "integrity": "sha512-Zc/JB0IlNNrpXkhBw+h86cGrde/Mey52KvF+FER2eyrtYJTHObOwW7Iarxm3rPyTKby5+3Y2QZtl8pRz/5GCxg==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/jest-dev-server/-/jest-dev-server-10.1.4.tgz", + "integrity": "sha512-bGQ6sedNGtT6AFHhCVqGTXMPz7UyJi/ZrhNBgyqsP0XU9N8acCEIfqZEA22rOaZ+NdEVsaltk6tL7UT6aXfI7w==", "dev": true, "dependencies": { "chalk": "^4.1.2", "cwd": "^0.10.0", "find-process": "^1.4.7", "prompts": "^2.4.2", - "spawnd": "^9.0.2", + "spawnd": "^10.1.4", "tree-kill": "^1.2.2", - "wait-on": "^7.2.0" + "wait-on": "^8.0.1" }, "engines": { "node": ">=16" @@ -13039,14 +13280,14 @@ } }, "node_modules/joi": { - "version": "17.11.0", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.11.0.tgz", - "integrity": "sha512-NgB+lZLNoqISVy1rZocE9PZI36bL/77ie924Ri43yEvi9GUUMPeyVIr8KdFTMUlby1p0PBYMk9spIxEUQYqrJQ==", + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", "dev": true, "dependencies": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" } @@ -13735,20 +13976,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/lint-staged/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/lint-staged/node_modules/debug": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", @@ -13876,27 +14103,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lint-staged/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/lint-staged/node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -13921,21 +14127,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/lint-staged/node_modules/yaml": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", @@ -14846,12 +15037,13 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.7.6", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz", - "integrity": "sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", + "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", "dev": true, "dependencies": { - "schema-utils": "^4.0.0" + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" }, "engines": { "node": ">= 12.13.0" @@ -14865,15 +15057,15 @@ } }, "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -14899,9 +15091,9 @@ "dev": true }, "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz", + "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", @@ -14910,7 +15102,7 @@ "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 10.13.0" }, "funding": { "type": "opencollective", @@ -15120,6 +15312,13 @@ "tslib": "^2.0.3" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "optional": true + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -16019,32 +16218,29 @@ } }, "node_modules/pac-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.2.tgz", - "integrity": "sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.1.0.tgz", + "integrity": "sha512-Z5FnLVVZSnX7WjBg0mhDtydeRZ1xMcATZThjySQUHqr+0ksP8kqaw23fNKkaaN/Z8gwLUs/W7xdl0I75eP2Xyw==", "dev": true, "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.5", + "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", - "socks-proxy-agent": "^8.0.4" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" } }, "node_modules/pac-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -16063,12 +16259,12 @@ } }, "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -17220,32 +17416,29 @@ } }, "node_modules/proxy-agent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", - "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", "dev": true, "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.3", + "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", + "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" } }, "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -17264,12 +17457,12 @@ } }, "node_modules/proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -17303,12 +17496,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true - }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", @@ -17335,15 +17522,15 @@ } }, "node_modules/puppeteer-core": { - "version": "23.3.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.3.0.tgz", - "integrity": "sha512-sB2SsVMFs4gKad5OCdv6w5vocvtEUrRl0zQqSyRPbo/cj1Ktbarmhxy02Zyb9R9HrssBcJDZbkrvBnbaesPyYg==", + "version": "23.10.4", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.10.4.tgz", + "integrity": "sha512-pQAY7+IFAndWDkDodsQGguW1/ifV5OMlGXJDspwtK49Asb7poJZ/V5rXJxVSpq57bWrJasjQBZ1X27z1oWVq4Q==", "dev": true, "dependencies": { - "@puppeteer/browsers": "2.4.0", - "chromium-bidi": "0.6.5", - "debug": "^4.3.6", - "devtools-protocol": "0.0.1330662", + "@puppeteer/browsers": "2.6.1", + "chromium-bidi": "0.8.0", + "debug": "^4.4.0", + "devtools-protocol": "0.0.1367902", "typed-query-selector": "^2.12.0", "ws": "^8.18.0" }, @@ -17352,9 +17539,9 @@ } }, "node_modules/puppeteer-core/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, "dependencies": { "ms": "^2.1.3" @@ -17369,9 +17556,9 @@ } }, "node_modules/puppeteer-core/node_modules/devtools-protocol": { - "version": "0.0.1330662", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1330662.tgz", - "integrity": "sha512-pzh6YQ8zZfz3iKlCvgzVCu22NdpZ8hNmwU6WnQjNVquh0A9iVosPtNLWDwaWVGyrntQlltPFztTMK5Cg6lfCuw==", + "version": "0.0.1367902", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz", + "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==", "dev": true }, "node_modules/puppeteer-core/node_modules/ms": { @@ -18161,13 +18348,13 @@ "dev": true }, "node_modules/sass": { - "version": "1.65.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.65.1.tgz", - "integrity": "sha512-9DINwtHmA41SEd36eVPQ9BJKpn7eKDQmUHmpI0y5Zv2Rcorrh0zS+cFrt050hdNbmmCNKTW3hV5mWfuegNRsEA==", + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.83.0.tgz", + "integrity": "sha512-qsSxlayzoOjdvXMVLkzF84DJFc2HZEL/rFyGIKbbilYtAvlCxyuzUeff9LawTn4btVnLKg75Z8MMr1lxU1lfGw==", "dev": true, "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", + "chokidar": "^4.0.0", + "immutable": "^5.0.2", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { @@ -18175,33 +18362,35 @@ }, "engines": { "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" } }, "node_modules/sass-loader": { - "version": "12.6.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", - "integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==", + "version": "16.0.4", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.4.tgz", + "integrity": "sha512-LavLbgbBGUt3wCiYzhuLLu65+fWXaXLmq7YxivLhEqmiupCFZ5sKUAipK3do6V80YSU0jvSxNhEdT13IXNr3rg==", "dev": true, "dependencies": { - "klona": "^2.0.4", "neo-async": "^2.6.2" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "@rspack/core": "0.x || 1.x", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", "sass": "^1.3.0", "sass-embedded": "*", "webpack": "^5.0.0" }, "peerDependenciesMeta": { - "fibers": { + "@rspack/core": { "optional": true }, "node-sass": { @@ -18212,9 +18401,40 @@ }, "sass-embedded": { "optional": true + }, + "webpack": { + "optional": true } } }, + "node_modules/sass/node_modules/chokidar": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.2.tgz", + "integrity": "sha512-/b57FK+bblSU+dfewfFe0rT1YjVDfOmeLQwCAuC+vwvgLkXboATqqmy+Ipux6JrF6L5joe5CBnFOw+gLWH6yKg==", + "dev": true, + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/sass/node_modules/readdirp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.0.2.tgz", + "integrity": "sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==", + "dev": true, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -18699,12 +18919,12 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", - "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "dev": true, "dependencies": { - "agent-base": "^7.1.1", + "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" }, @@ -18713,13 +18933,10 @@ } }, "node_modules/socks-proxy-agent/node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } @@ -18795,9 +19012,9 @@ } }, "node_modules/spawnd": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-9.0.2.tgz", - "integrity": "sha512-nl8DVHEDQ57IcKakzpjanspVChkMpGLuVwMR/eOn9cXE55Qr6luD2Kn06sA0ootRMdgrU4tInN6lA6ohTNvysw==", + "version": "10.1.4", + "resolved": "https://registry.npmjs.org/spawnd/-/spawnd-10.1.4.tgz", + "integrity": "sha512-drqHc0mKJmtMsiGMOCwzlc5eZ0RPtRvT7tQAluW2A0qUc0G7TQ8KLcn3E6K5qzkLkH2UkS3nYQiVGULvvsD9dw==", "dev": true, "dependencies": { "signal-exit": "^4.1.0", @@ -18968,9 +19185,9 @@ "dev": true }, "node_modules/streamx": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.20.0.tgz", - "integrity": "sha512-ZGd1LhDeGFucr1CUCTBOS58ZhEendd0ttpGT3usTvosS4ntIwKN9LJFp+OeCSprsCPL14BXVRZlHGRY1V9PVzQ==", + "version": "2.21.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.21.1.tgz", + "integrity": "sha512-PhP9wUnFLa+91CPy3N6tiQsK+gnYyUNuk15S3YG/zjYE7RuPeCjJngqnzpC31ow0lzBHQ+QGO4cNJnd0djYUsw==", "dev": true, "dependencies": { "fast-fifo": "^1.3.2", @@ -20631,13 +20848,13 @@ } }, "node_modules/wait-on": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.2.0.tgz", - "integrity": "sha512-wCQcHkRazgjG5XoAq9jbTMLpNIjoSlZslrJ2+N9MxDsGEv1HnFoVjOCexL0ESva7Y9cu350j+DWADdk54s4AFQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.1.tgz", + "integrity": "sha512-1wWQOyR2LVVtaqrcIL2+OM+x7bkpmzVROa0Nf6FryXkS+er5Sa1kzFGjzZRqLnHa3n1rACFLeTwUqE1ETL9Mig==", "dev": true, "dependencies": { - "axios": "^1.6.1", - "joi": "^17.11.0", + "axios": "^1.7.7", + "joi": "^17.13.3", "lodash": "^4.17.21", "minimist": "^1.2.8", "rxjs": "^7.8.1" @@ -20713,18 +20930,18 @@ } }, "node_modules/webpack": { - "version": "5.95.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.95.0.tgz", - "integrity": "sha512-2t3XstrKULz41MNMBF+cJ97TyHdyQ8HCt//pqErqDvNjU9YQBnZxIHa11VXsi7F3mb5/aO2tuDxdeTPdU7xu9Q==", + "version": "5.97.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", + "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", "dev": true, "dependencies": { - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.6", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.14.0", + "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", @@ -20869,56 +21086,6 @@ "node": ">=14" } }, - "node_modules/webpack-cli/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/webpack-cli/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-cli/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/webpack-cli/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/webpack-dev-middleware": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", diff --git a/package.json b/package.json index 123184509d..aeff6deea5 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "@octokit/rest": "^21.0.2", "@wordpress/env": "^10.13.0", "@wordpress/prettier-config": "^4.13.0", - "@wordpress/scripts": "^30.6.0", + "@wordpress/scripts": "^30.7.0", "commander": "12.1.0", "copy-webpack-plugin": "^12.0.2", "css-minimizer-webpack-plugin": "^7.0.0", From b4e693c4a194aa4c3ca946685828634bade3a136 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 16 Dec 2024 16:52:39 -0800 Subject: [PATCH 217/237] Update readme with links to where the performance features are implemented --- plugins/embed-optimizer/readme.txt | 6 ++--- plugins/image-prioritizer/readme.txt | 11 +++++---- plugins/optimization-detective/readme.txt | 29 ++++++++++++++++++++++- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/plugins/embed-optimizer/readme.txt b/plugins/embed-optimizer/readme.txt index b02756cfbb..a36210a5a7 100644 --- a/plugins/embed-optimizer/readme.txt +++ b/plugins/embed-optimizer/readme.txt @@ -15,9 +15,9 @@ This plugin's purpose is to optimize the performance of [embeds in WordPress](ht The current optimizations include: -1. Lazy loading embeds just before they come into view -2. Adding preconnect links for embeds in the initial viewport -3. Reserving space for embeds that resize to reduce layout shifting +1. Lazy loading embeds just before they come into view. +2. Adding preconnect links for embeds in the initial viewport. +3. Reserving space for embeds that resize to reduce layout shifting. **Lazy loading embeds** improves performance because embeds are generally very resource-intensive, so lazy loading them ensures that they don't compete with resources when the page is loading. Lazy loading of `IFRAME`\-based embeds is handled simply by adding the `loading=lazy` attribute. Lazy loading embeds that include `SCRIPT` tags is handled by using an Intersection Observer to watch for when the embed’s `FIGURE` container is going to enter the viewport and then it dynamically inserts the `SCRIPT` tag. diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt index 029fe74380..867272d30a 100644 --- a/plugins/image-prioritizer/readme.txt +++ b/plugins/image-prioritizer/readme.txt @@ -7,7 +7,7 @@ License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, optimization, image, lcp, lazy-load -Prioritizes the loading of images and videos based on how visible they are to actual visitors; adds fetchpriority and applies lazy-loading. +Prioritizes the loading of images and videos based on how visible they are to actual visitors; adds fetchpriority and applies lazy loading. == Description == @@ -20,13 +20,14 @@ The current optimizations include: 2. The first `SOURCE` element with a `type` attribute in a `PICTURE` element. (Art-directed `PICTURE` elements using media queries are not supported.) 3. An element with a CSS `background-image` inline `style` attribute. 4. An element with a CSS `background-image` applied with a stylesheet (when the image is from an allowed origin). + 5. A `VIDEO` element's `poster` image. 2. Ensure `fetchpriority=high` is only added to an `IMG` when it is the Largest Contentful Paint (LCP) element across all responsive breakpoints. 3. Add `fetchpriority=low` to `IMG` tags which appear in the initial viewport but are not visible, such as when they are subsequent carousel slides. -4. Lazy-Loading: - 1. Apply lazy-loading to `IMG` tags based on whether they appear in any breakpoint’s initial viewport. (Additionally, [`sizes=auto`](https://make.wordpress.org/core/2024/10/18/auto-sizes-for-lazy-loaded-images-in-wordpress-6-7/) is then also correctly applied.) - 2. Implement lazy-loading of CSS background images added via inline `style` attributes. +4. Lazy loading: + 1. Apply lazy loading to `IMG` tags based on whether they appear in any breakpoint’s initial viewport. + 2. Implement lazy loading of CSS background images added via inline `style` attributes. 3. Lazy-load `VIDEO` tags by setting the appropriate attributes based on whether they appear in the initial viewport. If a `VIDEO` is the LCP element, it gets `preload=auto`; if it is in an initial viewport, the `preload=metadata` default is left; if it is not in an initial viewport, it gets `preload=none`. Lazy-loaded videos also get initial `preload`, `autoplay`, and `poster` attributes restored when the `VIDEO` is going to enter the viewport. -5. Ensure that `sizes=auto` is added to all lazy-loaded `IMG` elements. +5. Ensure that [`sizes=auto`](https://make.wordpress.org/core/2024/10/18/auto-sizes-for-lazy-loaded-images-in-wordpress-6-7/) is added to all lazy-loaded `IMG` elements. 6. Reduce the size of the `poster` image of a `VIDEO` from full size to the size appropriate for the maximum width of the video (on desktop). **This plugin requires the [Optimization Detective](https://wordpress.org/plugins/optimization-detective/) plugin as a dependency.** Please refer to that plugin for additional background on how this plugin works as well as additional developer options. diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index c21b3a523c..90ef25eb29 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -17,7 +17,7 @@ This plugin is a dependency which does not provide end-user functionality on its = Background = -WordPress uses [server-side heuristics](https://make.wordpress.org/core/2023/07/13/image-performance-enhancements-in-wordpress-6-3/) to make educated guesses about which images are likely to be in the initial viewport. Likewise, it uses server-side heuristics to identify a hero image which is likely to be the Largest Contentful Paint (LCP) element. To optimize page loading, it avoids lazy-loading any of these images while also adding `fetchpriority=high` to the hero image. When these heuristics are applied successfully, the LCP metric for page loading can be improved 5-10%. Unfortunately, however, there are limitations to the heuristics that make the correct identification of which image is the LCP element only about 50% effective. See [Analyzing the Core Web Vitals performance impact of WordPress 6.3 in the field](https://make.wordpress.org/core/2023/09/19/analyzing-the-core-web-vitals-performance-impact-of-wordpress-6-3-in-the-field/). For example, it is [common](https://github.com/GoogleChromeLabs/wpp-research/pull/73) for the LCP element to vary between different viewport widths, such as desktop versus mobile. Since WordPress's heuristics are completely server-side it has no knowledge of how the page is actually laid out, and it cannot prioritize loading of images according to the client's viewport width. +WordPress uses [server-side heuristics](https://make.wordpress.org/core/2023/07/13/image-performance-enhancements-in-wordpress-6-3/) to make educated guesses about which images are likely to be in the initial viewport. Likewise, it uses server-side heuristics to identify a hero image which is likely to be the Largest Contentful Paint (LCP) element. To optimize page loading, it avoids lazy loading any of these images while also adding `fetchpriority=high` to the hero image. When these heuristics are applied successfully, the LCP metric for page loading can be improved 5-10%. Unfortunately, however, there are limitations to the heuristics that make the correct identification of which image is the LCP element only about 50% effective. See [Analyzing the Core Web Vitals performance impact of WordPress 6.3 in the field](https://make.wordpress.org/core/2023/09/19/analyzing-the-core-web-vitals-performance-impact-of-wordpress-6-3-in-the-field/). For example, it is [common](https://github.com/GoogleChromeLabs/wpp-research/pull/73) for the LCP element to vary between different viewport widths, such as desktop versus mobile. Since WordPress's heuristics are completely server-side it has no knowledge of how the page is actually laid out, and it cannot prioritize loading of images according to the client's viewport width. In order to increase the accuracy of identifying the LCP element, including across various client viewport widths, this plugin gathers metrics from real users (RUM) to detect the actual LCP element and then use this information to optimize the page for future visitors so that the loading of the LCP element is properly prioritized. This is the purpose of Optimization Detective. The approach is heavily inspired by Philip Walton’s [Dynamic LCP Priority: Learning from Past Visits](https://philipwalton.com/articles/dynamic-lcp-priority/). See also the initial exploration document that laid out this project: [Image Loading Optimization via Client-side Detection](https://docs.google.com/document/u/1/d/16qAJ7I_ljhEdx2Cn2VlK7IkiixobY9zNn8FXxN9T9Ls/view). @@ -40,6 +40,33 @@ There are currently **no settings** and no user interface for this plugin since When the `WP_DEBUG` constant is enabled, additional logging for Optimization Detective is added to the browser console. += Use Cases and Examples = + +As mentioned above, this plugin is a dependency that doesn't provide features on its own. Dependent plugins leverage the collected URL Metrics to apply optimizations. What follows us a running list of the optimizations which are enabled by Optimization Detective, along with a links to the related code used for the implementation: + +**[Image Prioritizer](https://wordpress.org/plugins/image-prioritizer/) ([GitHub](https://github.com/WordPress/performance/tree/trunk/plugins/image-prioritizer)):** + +1. Add breakpoint-specific `fetchpriority=high` preload links (`LINK[rel=preload]`) for image URLs of LCP elements: + 1. An `IMG` element, including the `srcset`/`sizes` attributes supplied as `imagesrcset`/`imagesizes` on the `LINK`. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L167-L177), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L304-L349)) + 2. The first `SOURCE` element with a `type` attribute in a `PICTURE` element. (Art-directed `PICTURE` elements using media queries are not supported.) ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L192-L275), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L304-L349)) + 3. An element with a CSS `background-image` inline `style` attribute. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L62-L92), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L182-L203)) + 4. An element with a CSS `background-image` applied with a stylesheet (when the image is from an allowed origin). ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L82-L83), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L135-L203), [3](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/helper.php#L83-L264), [4](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/detect.js)) + 5. A `VIDEO` element's `poster` image. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php#L127-L161)) +2. Ensure `fetchpriority=high` is only added to an `IMG` when it is the Largest Contentful Paint (LCP) element across all responsive breakpoints. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L65-L91), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L137-L146)) +3. Add `fetchpriority=low` to `IMG` tags which appear in the initial viewport but are not visible, such as when they are subsequent carousel slides. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L105-L123), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L137-L146)) +4. Lazy loading: + 1. Apply lazy loading to `IMG` tags based on whether they appear in any breakpoint’s initial viewport. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L124-L133)) + 2. Implement lazy loading of CSS background images added via inline `style` attributes. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L205-L238), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/helper.php#L369-L382), [3](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/lazy-load-bg-image.js)) + 3. Lazy-load `VIDEO` tags by setting the appropriate attributes based on whether they appear in the initial viewport. If a `VIDEO` is the LCP element, it gets `preload=auto`; if it is in an initial viewport, the `preload=metadata` default is left; if it is not in an initial viewport, it gets `preload=none`. Lazy-loaded videos also get initial `preload`, `autoplay`, and `poster` attributes restored when the `VIDEO` is going to enter the viewport. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php#L163-L246), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/helper.php#L352-L367), [3](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/lazy-load-video.js)) +5. Ensure that [`sizes=auto`](https://make.wordpress.org/core/2024/10/18/auto-sizes-for-lazy-loaded-images-in-wordpress-6-7/) is added to all lazy-loaded `IMG` elements. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L148-L163)) +6. Reduce the size of the `poster` image of a `VIDEO` from full size to the size appropriate for the maximum width of the video (on desktop). ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php#L84-L125)) + +**[Embed Optimizer](https://wordpress.org/plugins/embed-optimizer/) ([GitHub](https://github.com/WordPress/performance/tree/trunk/plugins/embed-optimizer)):** + +1. Lazy loading embeds just before they come into view. ([1](https://github.com/WordPress/performance/blob/ce76a6a77c15248126b5dab895bc11d0adda0baa/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php#L191-L194), [2](https://github.com/WordPress/performance/blob/ce76a6a77c15248126b5dab895bc11d0adda0baa/plugins/embed-optimizer/hooks.php#L168-L336)) +2. Adding preconnect links for embeds in the initial viewport. ([1](https://github.com/WordPress/performance/blob/ce76a6a77c15248126b5dab895bc11d0adda0baa/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php#L114-L190)) +3. Reserving space for embeds that resize to reduce layout shifting. ([1](https://github.com/WordPress/performance/blob/ce76a6a77c15248126b5dab895bc11d0adda0baa/plugins/embed-optimizer/hooks.php#L81-L144), [2](https://github.com/WordPress/performance/blob/ce76a6a77c15248126b5dab895bc11d0adda0baa/plugins/embed-optimizer/detect.js), [3](https://github.com/WordPress/performance/blob/ce76a6a77c15248126b5dab895bc11d0adda0baa/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php#L218-L285)) + = Hooks = **Action:** `od_init` (argument: plugin version) From 4b59aa3cdb373369655b57f606b54dec800e0720 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 00:57:39 +0000 Subject: [PATCH 218/237] Bump @wordpress/prettier-config from 4.13.0 to 4.14.0 Bumps [@wordpress/prettier-config](https://github.com/WordPress/gutenberg/tree/HEAD/packages/prettier-config) from 4.13.0 to 4.14.0. - [Release notes](https://github.com/WordPress/gutenberg/releases) - [Changelog](https://github.com/WordPress/gutenberg/blob/trunk/packages/prettier-config/CHANGELOG.md) - [Commits](https://github.com/WordPress/gutenberg/commits/@wordpress/prettier-config@4.14.0/packages/prettier-config) --- updated-dependencies: - dependency-name: "@wordpress/prettier-config" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 9 ++++----- package.json | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index f9bd029b51..5598582bbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "devDependencies": { "@octokit/rest": "^21.0.2", "@wordpress/env": "^10.13.0", - "@wordpress/prettier-config": "^4.13.0", + "@wordpress/prettier-config": "^4.14.0", "@wordpress/scripts": "^30.7.0", "commander": "12.1.0", "copy-webpack-plugin": "^12.0.2", @@ -5192,11 +5192,10 @@ } }, "node_modules/@wordpress/prettier-config": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@wordpress/prettier-config/-/prettier-config-4.13.0.tgz", - "integrity": "sha512-TgjsY0dU6fwtQs4Re73OlKZnxilaHbXmwb373qouDY/AzG72VkpQpQ2KcenCoJ7Do1BKdWWehzDo609nQhk/Yg==", + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@wordpress/prettier-config/-/prettier-config-4.14.0.tgz", + "integrity": "sha512-DZuASK64Jr8ycj9uaSlwsTURiaQ0sgQnu9ThgSK196jcDF1jxTll8JGoVIXgxhKgo3mFFjdtkNeBZ38DT5z6/g==", "dev": true, - "license": "GPL-2.0-or-later", "engines": { "node": ">=18.12.0", "npm": ">=8.19.2" diff --git a/package.json b/package.json index 6f0890cacd..d733c7f9e4 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "devDependencies": { "@octokit/rest": "^21.0.2", "@wordpress/env": "^10.13.0", - "@wordpress/prettier-config": "^4.13.0", + "@wordpress/prettier-config": "^4.14.0", "@wordpress/scripts": "^30.7.0", "commander": "12.1.0", "copy-webpack-plugin": "^12.0.2", From 8fec3958ff9c8e04da2c04e41d3f73310efb313c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 01:05:02 +0000 Subject: [PATCH 219/237] Bump @wordpress/env from 10.13.0 to 10.14.0 Bumps [@wordpress/env](https://github.com/WordPress/gutenberg/tree/HEAD/packages/env) from 10.13.0 to 10.14.0. - [Release notes](https://github.com/WordPress/gutenberg/releases) - [Changelog](https://github.com/WordPress/gutenberg/blob/trunk/packages/env/CHANGELOG.md) - [Commits](https://github.com/WordPress/gutenberg/commits/@wordpress/env@10.14.0/packages/env) --- updated-dependencies: - dependency-name: "@wordpress/env" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 317 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 2 +- 2 files changed, 312 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5598582bbc..19be816bc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ }, "devDependencies": { "@octokit/rest": "^21.0.2", - "@wordpress/env": "^10.13.0", + "@wordpress/env": "^10.14.0", "@wordpress/prettier-config": "^4.14.0", "@wordpress/scripts": "^30.7.0", "commander": "12.1.0", @@ -2272,6 +2272,96 @@ "deprecated": "Use @eslint/object-schema instead", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -3266,6 +3356,16 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@pkgr/core": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", @@ -5029,11 +5129,10 @@ } }, "node_modules/@wordpress/env": { - "version": "10.13.0", - "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-10.13.0.tgz", - "integrity": "sha512-Q7ay+/jZ+O/Pkc65LDJ5BzoqTT/B0+gDgvYnWMyySPiMpFz+iQ+XoQibrj3VneiQDH7nJjtk/ZuyPHu7wGdlBg==", + "version": "10.14.0", + "resolved": "https://registry.npmjs.org/@wordpress/env/-/env-10.14.0.tgz", + "integrity": "sha512-tDJyW6KaaEs9jz2XMTjY0RpGWdsjEfOCx5jeCMWtzkgrDY5N9iZr1BFjNzmFzY1BcXQshnFsrecsnYdyIfvsTA==", "dev": true, - "license": "GPL-2.0-or-later", "dependencies": { "chalk": "^4.0.0", "copy-dir": "^1.3.0", @@ -5043,7 +5142,7 @@ "inquirer": "^7.1.0", "js-yaml": "^3.13.1", "ora": "^4.0.2", - "rimraf": "^3.0.2", + "rimraf": "^5.0.10", "simple-git": "^3.5.0", "terminal-link": "^2.0.0", "yargs": "^17.3.0" @@ -5056,6 +5155,65 @@ "npm": ">=8.19.2" } }, + "node_modules/@wordpress/env/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@wordpress/env/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@wordpress/env/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@wordpress/env/node_modules/rimraf": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@wordpress/eslint-plugin": { "version": "21.3.0", "resolved": "https://registry.npmjs.org/@wordpress/eslint-plugin/-/eslint-plugin-21.3.0.tgz", @@ -9124,6 +9282,12 @@ "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -10893,6 +11057,34 @@ "node": ">=0.10.0" } }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", @@ -12619,6 +12811,21 @@ "node": ">= 0.4" } }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -15173,6 +15380,15 @@ "node": ">=0.10.0" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mitt": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", @@ -16289,6 +16505,12 @@ "node": ">= 14" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -16424,6 +16646,28 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -19248,6 +19492,36 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/string-width/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -19391,6 +19665,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", @@ -21574,6 +21861,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index d733c7f9e4..624ceae946 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "devDependencies": { "@octokit/rest": "^21.0.2", - "@wordpress/env": "^10.13.0", + "@wordpress/env": "^10.14.0", "@wordpress/prettier-config": "^4.14.0", "@wordpress/scripts": "^30.7.0", "commander": "12.1.0", From d4c9f40fb3307d11b1679da76c2e8333a80f91d0 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 09:42:05 -0800 Subject: [PATCH 220/237] Eliminate od_store_url_metric_data filter in favor of reusing rest_request_before_callbacks --- plugins/image-prioritizer/helper.php | 32 +++-- plugins/image-prioritizer/hooks.php | 2 +- .../image-prioritizer/tests/test-helper.php | 86 ++++++++--- .../image-prioritizer/tests/test-hooks.php | 2 +- plugins/optimization-detective/readme.txt | 8 -- .../storage/rest-api.php | 74 +++------- .../tests/storage/test-rest-api.php | 136 +++++------------- 7 files changed, 144 insertions(+), 196 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index f16f2eb08a..e3dcda832c 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -264,26 +264,34 @@ static function ( $host ) { } /** - * Filters the validity of a URL Metric with an LCP element background image. + * Filters the response before executing any REST API callbacks. * * This removes the lcpElementExternalBackgroundImage from the URL Metric prior to it being stored if the background - * image URL is not valid. Removal of the property is preferable to + * image URL is not valid. Removal of the property is preferable to invalidating the entire URL Metric because then + * potentially no URL Metrics would ever be collected if, for example, the background image URL is pointing to a + * disallowed origin. Then none of the other optimizations would be able to be applied. * * @since n.e.x.t * @access private * - * @param array|mixed $data URL Metric data. - * @return array Sanitized URL Metric data. + * @phpstan-param WP_REST_Request> $request * + * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. + * Usually a WP_REST_Response or WP_Error. + * @param array $handler Route handler used for the request. + * @param WP_REST_Request $request Request used to generate the response. + * + * @return WP_REST_Response|WP_HTTP_Response|WP_Error|mixed Result to send to the client. * @noinspection PhpDocMissingThrowsInspection */ -function image_prioritizer_filter_store_url_metric_data( $data ): array { - if ( ! is_array( $data ) ) { - $data = array(); +function image_prioritizer_filter_rest_request_before_callbacks( $response, array $handler, WP_REST_Request $request ) { + if ( $request->get_method() !== 'POST' || OD_REST_API_NAMESPACE . OD_URL_METRICS_ROUTE !== trim( $request->get_route(), '/' ) ) { + return $response; } - if ( isset( $data['lcpElementExternalBackgroundImage']['url'] ) && is_string( $data['lcpElementExternalBackgroundImage']['url'] ) ) { - $image_validity = image_prioritizer_validate_background_image_url( $data['lcpElementExternalBackgroundImage']['url'] ); + $lcp_external_background_image = $request['lcpElementExternalBackgroundImage']; + if ( is_array( $lcp_external_background_image ) && isset( $lcp_external_background_image['url'] ) && is_string( $lcp_external_background_image['url'] ) ) { + $image_validity = image_prioritizer_validate_background_image_url( $lcp_external_background_image['url'] ); if ( is_wp_error( $image_validity ) ) { /** * No WP_Exception is thrown by wp_trigger_error() since E_USER_ERROR is not passed as the error level. @@ -296,14 +304,14 @@ function image_prioritizer_filter_store_url_metric_data( $data ): array { /* translators: 1: error message. 2: image url */ __( 'Error: %1$s. Background image URL: %2$s.', 'image-prioritizer' ), rtrim( $image_validity->get_error_message(), '.' ), - $data['lcpElementExternalBackgroundImage']['url'] + $lcp_external_background_image['url'] ) ); - unset( $data['lcpElementExternalBackgroundImage'] ); + unset( $request['lcpElementExternalBackgroundImage'] ); } } - return $data; + return $response; } /** diff --git a/plugins/image-prioritizer/hooks.php b/plugins/image-prioritizer/hooks.php index d257ed7353..6365908758 100644 --- a/plugins/image-prioritizer/hooks.php +++ b/plugins/image-prioritizer/hooks.php @@ -13,4 +13,4 @@ add_action( 'od_init', 'image_prioritizer_init' ); add_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ); add_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ); -add_filter( 'od_store_url_metric_data', 'image_prioritizer_filter_store_url_metric_data' ); +add_filter( 'rest_request_before_callbacks', 'image_prioritizer_filter_rest_request_before_callbacks', 10, 3 ); diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index 8ecdd4a335..65a6272d23 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -747,10 +747,23 @@ public function test_image_prioritizer_validate_background_image_url( Closure $s * * @return array */ - public function data_provider_to_test_image_prioritizer_filter_store_url_metric_data(): array { + public function data_provider_to_test_image_prioritizer_filter_rest_request_before_callbacks(): array { + $get_sample_url_metric_data = function (): array { + return $this->get_sample_url_metric( array() )->jsonSerialize(); + }; + + $create_request = static function ( array $url_metric_data ): WP_REST_Request { + $request = new WP_REST_Request( 'POST', '/' . OD_REST_API_NAMESPACE . OD_URL_METRICS_ROUTE ); + $request->set_header( 'content-type', 'application/json' ); + $request->set_body( wp_json_encode( $url_metric_data ) ); + return $request; + }; + return array( 'invalid_external_bg_image' => array( - 'set_up' => static function ( array $url_metric_data ): array { + 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { + $url_metric_data = $get_sample_url_metric_data(); + $url_metric_data['lcpElementExternalBackgroundImage'] = array( 'url' => 'https://bad-origin.example.com/image.jpg', 'tag' => 'DIV', @@ -759,22 +772,23 @@ public function data_provider_to_test_image_prioritizer_filter_store_url_metric_ ); $url_metric_data['viewport']['width'] = 10101; $url_metric_data['viewport']['height'] = 20202; - return $url_metric_data; + return $create_request( $url_metric_data ); }, - 'assert' => function ( array $url_metric_data ): void { - $this->assertArrayNotHasKey( 'lcpElementExternalBackgroundImage', $url_metric_data ); + 'assert' => function ( WP_REST_Request $request ): void { + $this->assertArrayNotHasKey( 'lcpElementExternalBackgroundImage', $request ); $this->assertSame( array( 'width' => 10101, 'height' => 20202, ), - $url_metric_data['viewport'] + $request['viewport'] ); }, ), 'valid_external_bg_image' => array( - 'set_up' => static function ( array $url_metric_data ): array { + 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { + $url_metric_data = $get_sample_url_metric_data(); $image_url = home_url( '/good.jpg' ); add_filter( @@ -808,11 +822,11 @@ static function ( $pre, $parsed_args, $url ) use ( $image_url ) { $url_metric_data['viewport']['width'] = 30303; $url_metric_data['viewport']['height'] = 40404; - return $url_metric_data; + return $create_request( $url_metric_data ); }, - 'assert' => function ( array $url_metric_data ): void { - $this->assertArrayHasKey( 'lcpElementExternalBackgroundImage', $url_metric_data ); - $this->assertIsArray( $url_metric_data['lcpElementExternalBackgroundImage'] ); + 'assert' => function ( WP_REST_Request $request ): void { + $this->assertArrayHasKey( 'lcpElementExternalBackgroundImage', $request ); + $this->assertIsArray( $request['lcpElementExternalBackgroundImage'] ); $this->assertSame( array( 'url' => home_url( '/good.jpg' ), @@ -820,33 +834,63 @@ static function ( $pre, $parsed_args, $url ) use ( $image_url ) { 'id' => null, 'class' => null, ), - $url_metric_data['lcpElementExternalBackgroundImage'] + $request['lcpElementExternalBackgroundImage'] ); $this->assertSame( array( 'width' => 30303, 'height' => 40404, ), - $url_metric_data['viewport'] + $request['viewport'] ); }, ), + + 'not_store_post_request' => array( + 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { + $url_metric_data = $get_sample_url_metric_data(); + $url_metric_data['lcpElementExternalBackgroundImage'] = 'https://totally-different.example.com/'; + $request = $create_request( $url_metric_data ); + $request->set_method( 'GET' ); + return $request; + }, + 'assert' => function ( WP_REST_Request $request ): void { + $this->assertArrayHasKey( 'lcpElementExternalBackgroundImage', $request ); + $this->assertSame( 'https://totally-different.example.com/', $request['lcpElementExternalBackgroundImage'] ); + }, + ), + + 'not_store_request' => array( + 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { + $url_metric_data = $get_sample_url_metric_data(); + $url_metric_data['lcpElementExternalBackgroundImage'] = 'https://totally-different.example.com/'; + $request = $create_request( $url_metric_data ); + $request->set_route( '/foo/v2/bar' ); + return $request; + }, + 'assert' => function ( WP_REST_Request $request ): void { + $this->assertArrayHasKey( 'lcpElementExternalBackgroundImage', $request ); + $this->assertSame( 'https://totally-different.example.com/', $request['lcpElementExternalBackgroundImage'] ); + }, + ), ); } /** - * Tests image_prioritizer_filter_store_url_metric_data(). + * Tests image_prioritizer_filter_rest_request_before_callbacks(). * - * @dataProvider data_provider_to_test_image_prioritizer_filter_store_url_metric_data + * @dataProvider data_provider_to_test_image_prioritizer_filter_rest_request_before_callbacks * - * @covers ::image_prioritizer_filter_store_url_metric_data + * @covers ::image_prioritizer_filter_rest_request_before_callbacks * @covers ::image_prioritizer_validate_background_image_url */ - public function test_image_prioritizer_filter_store_url_metric_data( Closure $set_up, Closure $assert ): void { - $url_metric_data = $set_up( $this->get_sample_url_metric( array() )->jsonSerialize() ); - - $url_metric_data = image_prioritizer_filter_store_url_metric_data( $url_metric_data ); - $assert( $url_metric_data ); + public function test_image_prioritizer_filter_rest_request_before_callbacks( Closure $set_up, Closure $assert ): void { + $request = $set_up(); + $response = new WP_REST_Response(); + $handler = array(); + $filtered_response = image_prioritizer_filter_rest_request_before_callbacks( $response, $handler, $request ); + $this->assertSame( $response, $filtered_response ); + $assert( $request ); } /** diff --git a/plugins/image-prioritizer/tests/test-hooks.php b/plugins/image-prioritizer/tests/test-hooks.php index 4d34043d3f..840212da4b 100644 --- a/plugins/image-prioritizer/tests/test-hooks.php +++ b/plugins/image-prioritizer/tests/test-hooks.php @@ -14,6 +14,6 @@ public function test_hooks_added(): void { $this->assertEquals( 10, has_action( 'od_init', 'image_prioritizer_init' ) ); $this->assertEquals( 10, has_filter( 'od_extension_module_urls', 'image_prioritizer_filter_extension_module_urls' ) ); $this->assertEquals( 10, has_filter( 'od_url_metric_schema_root_additional_properties', 'image_prioritizer_add_element_item_schema_properties' ) ); - $this->assertEquals( 10, has_filter( 'od_store_url_metric_data', 'image_prioritizer_filter_store_url_metric_data' ) ); + $this->assertEquals( 10, has_filter( 'rest_request_before_callbacks', 'image_prioritizer_filter_rest_request_before_callbacks' ) ); } } diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index 90ef25eb29..1e783ef823 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -273,14 +273,6 @@ The ETag is a unique identifier that changes whenever the underlying data used t When the ETag for URL Metrics in a complete viewport group no longer matches the current environment's ETag, new URL Metrics will then begin to be collected until there are no more stored URL Metrics with the old ETag. These new URL Metrics will include data relevant to the newly activated plugins and their tag visitors. -**Filter:** `od_url_metric_data_pre_storage` (default arg: URL Metric data array) - -Filters the URL Metric data prior to validation for storage. - -This allows for custom sanitization and validation constraints to be applied beyond what can be expressed in JSON Schema. This filter only applies when storing a URL Metric via the REST API. It does not run when a stored URL Metric is loaded from the od_url_metrics post type. This means that sanitization and validation logic enforced via this filter can be more expensive, such as doing filesystem checks or HTTP requests. - -To fail validation for the provided URL Metric data, an `OD_Data_Validation_Exception` exception should be thrown. Otherwise, any filter callbacks must return an array consisting of the sanitized URL Metric data. - **Action:** `od_url_metric_stored` (argument: `OD_URL_Metric_Store_Request_Context`) Fires whenever a URL Metric was successfully stored. diff --git a/plugins/optimization-detective/storage/rest-api.php b/plugins/optimization-detective/storage/rest-api.php index 204a44140d..a67cc354ee 100644 --- a/plugins/optimization-detective/storage/rest-api.php +++ b/plugins/optimization-detective/storage/rest-api.php @@ -174,7 +174,7 @@ function od_handle_rest_request( WP_REST_Request $request ) { ); } - $data = $request->get_json_params(); + $data = $request->get_json_params(); // TODO: Why not just get_params()? if ( ! is_array( $data ) ) { return new WP_Error( 'missing_array_json_body', @@ -186,64 +186,28 @@ function od_handle_rest_request( WP_REST_Request $request ) { OD_Storage_Lock::set_lock(); try { - $data = array_merge( - $data, - array( - // Now supply the readonly args which were omitted from the REST API params due to being `readonly`. - 'timestamp' => microtime( true ), - 'uuid' => wp_generate_uuid4(), - 'etag' => $request->get_param( 'current_etag' ), + // The "strict" URL Metric class is being used here to ensure additionalProperties of all objects are disallowed. + $url_metric = new OD_Strict_URL_Metric( + array_merge( + $data, + array( + // Now supply the readonly args which were omitted from the REST API params due to being `readonly`. + 'timestamp' => microtime( true ), + 'uuid' => wp_generate_uuid4(), + 'etag' => $request->get_param( 'current_etag' ), + ) ) ); - - /** - * Filters the URL Metric data prior to validation for storage. - * - * This allows for custom sanitization and validation constraints to be applied beyond what can be expressed in - * JSON Schema. This is also necessary because the 'validate_callback' key in a JSON Schema is not respected when - * gathering the REST API endpoint args via the {@see rest_get_endpoint_args_for_schema()} function. Besides this, - * the REST API doesn't support 'validate_callback' for any nested arguments in any case, meaning that custom - * constraints would be able to be applied to multidimensional objects, such as the items inside 'elements'. - * - * This filter only applies when storing a URL Metric via the REST API. It does not run when a stored URL Metric is - * loaded from the od_url_metrics post type. This means that sanitization and validation logic enforced via this - * filter can be more expensive, such as doing filesystem checks or HTTP requests. - * - * To fail validation for the provided URL Metric data, an OD_Data_Validation_Exception exception should be thrown. - * Otherwise, any filter callbacks must return an array consisting of the sanitized URL Metric data. - * - * @since n.e.x.t - * - * @param array $data URL Metric data. This is the Data type from OD_URL_Metric. - */ - $data = apply_filters( 'od_store_url_metric_data', $data ); - - // The "strict" URL Metric class is being used here to ensure additionalProperties of all objects are disallowed. - $url_metric = new OD_Strict_URL_Metric( $data ); - } catch ( Exception $e ) { - $error_data = array(); - if ( $e instanceof OD_Data_Validation_Exception ) { - $error_message = sprintf( - /* translators: %s is exception name */ + } catch ( OD_Data_Validation_Exception $e ) { + return new WP_Error( + 'rest_invalid_param', + sprintf( + /* translators: %s is exception message */ __( 'Failed to validate URL Metric: %s', 'optimization-detective' ), $e->getMessage() - ); - $error_data['status'] = 400; - } else { - $error_message = sprintf( - /* translators: %s is exception name */ - __( 'Failed to validate URL Metric: %s', 'optimization-detective' ), - __( 'An unrecognized exception was thrown', 'optimization-detective' ) - ); - $error_data['status'] = 500; - if ( WP_DEBUG ) { - $error_data['exception_class'] = get_class( $e ); - $error_data['exception_message'] = $e->getMessage(); - $error_data['exception_code'] = $e->getCode(); - } - } - - return new WP_Error( 'rest_invalid_param', $error_message, $error_data ); + ), + array( 'status' => 400 ) + ); } // TODO: This should be changed from store_url_metric($slug, $url_metric) instead be update_post( $slug, $group_collection ). As it stands, store_url_metric() is duplicating logic here. diff --git a/plugins/optimization-detective/tests/storage/test-rest-api.php b/plugins/optimization-detective/tests/storage/test-rest-api.php index 8817e73375..3671b5784d 100644 --- a/plugins/optimization-detective/tests/storage/test-rest-api.php +++ b/plugins/optimization-detective/tests/storage/test-rest-api.php @@ -41,57 +41,21 @@ static function ( array $properties ) use ( $property_name ): array { }; return array( - 'not_extended' => array( - 'set_up' => function (): array { + 'not_extended' => array( + 'set_up' => function (): array { return $this->get_valid_params(); }, - 'expect_stored' => true, - 'expected_status' => 200, ), - 'extended' => array( - 'set_up' => function () use ( $add_root_extra_property ): array { + 'extended' => array( + 'set_up' => function () use ( $add_root_extra_property ): array { $add_root_extra_property( 'extra' ); $params = $this->get_valid_params(); $params['extra'] = 'foo'; return $params; }, - 'expect_stored' => true, - 'expected_status' => 200, ), - 'rejected_by_generic_exception' => array( - 'set_up' => function (): array { - add_filter( - 'od_store_url_metric_data', - static function ( $data ): array { - if ( count( $data ) > 0 ) { - throw new Exception( 'bad' ); - } - return $data; - } - ); - return $this->get_valid_params(); - }, - 'expect_stored' => false, - 'expected_status' => 500, - ), - 'rejected_by_validation_exception' => array( - 'set_up' => function (): array { - add_filter( - 'od_store_url_metric_data', - static function ( $data ): array { - if ( count( $data ) > 0 ) { - throw new OD_Data_Validation_Exception( 'bad' ); - } - return $data; - } - ); - return $this->get_valid_params(); - }, - 'expect_stored' => false, - 'expected_status' => 400, - ), - 'with_cache_purge_post_id' => array( - 'set_up' => function (): array { + 'with_cache_purge_post_id' => array( + 'set_up' => function (): array { $params = $this->get_valid_params(); $params['cache_purge_post_id'] = self::factory()->post->create(); $params['url'] = get_permalink( $params['cache_purge_post_id'] ); @@ -99,8 +63,6 @@ static function ( $data ): array { $params['hmac'] = od_get_url_metrics_storage_hmac( $params['slug'], $params['current_etag'], $params['url'], $params['cache_purge_post_id'] ); return $params; }, - 'expect_stored' => true, - 'expected_status' => 200, ), ); } @@ -114,20 +76,7 @@ static function ( $data ): array { * @covers ::od_handle_rest_request * @covers ::od_trigger_page_cache_invalidation */ - public function test_rest_request_good_params( Closure $set_up, bool $expect_stored, int $expected_status ): void { - $filtered_url_metric_data = null; - $filter_called = 0; - add_filter( - 'od_store_url_metric_data', - function ( $url_metric_data ) use ( &$filter_called, &$filtered_url_metric_data ) { - $this->assertIsArray( $url_metric_data ); - $filtered_url_metric_data = $url_metric_data; - $filter_called++; - return $url_metric_data; - }, - 0 - ); - + public function test_rest_request_good_params( Closure $set_up ): void { $stored_context = null; add_action( 'od_url_metric_stored', @@ -151,49 +100,40 @@ function ( OD_URL_Metric_Store_Request_Context $context ) use ( &$stored_context $request = $this->create_request( $valid_params ); $response = rest_get_server()->dispatch( $request ); - $this->assertSame( 1, $filter_called ); - $this->assertSame( $expect_stored ? 1 : 0, did_action( 'od_url_metric_stored' ) ); + $this->assertSame( 1, did_action( 'od_url_metric_stored' ) ); - $this->assertSame( $expected_status, $response->get_status(), 'Response: ' . wp_json_encode( $response ) ); + $this->assertSame( 200, $response->get_status(), 'Response: ' . wp_json_encode( $response ) ); $data = $response->get_data(); - $this->assertCount( $expect_stored ? 1 : 0, get_posts( array( 'post_type' => OD_URL_Metrics_Post_Type::SLUG ) ) ); - - if ( ! $expect_stored ) { - $this->assertArrayHasKey( 'code', $data ); - $this->assertArrayHasKey( 'message', $data ); - $this->assertNull( $stored_context ); - $this->assertNull( OD_URL_Metrics_Post_Type::get_post( $valid_params['slug'] ) ); - } else { - $this->assertTrue( $data['success'] ); - - $post = OD_URL_Metrics_Post_Type::get_post( $valid_params['slug'] ); - $this->assertInstanceOf( WP_Post::class, $post ); - - $url_metrics = OD_URL_Metrics_Post_Type::get_url_metrics_from_post( $post ); - $this->assertCount( 1, $url_metrics, 'Expected number of URL Metrics stored.' ); - $this->assertSame( $valid_params['elements'], $this->get_array_json_data( $url_metrics[0]->get( 'elements' ) ) ); - $this->assertSame( $valid_params['viewport']['width'], $url_metrics[0]->get_viewport_width() ); - - $expected_data = $valid_params; - unset( $expected_data['hmac'], $expected_data['slug'], $expected_data['current_etag'], $expected_data['cache_purge_post_id'] ); - unset( $expected_data['unset_prop'] ); - $this->assertSame( - $expected_data, - wp_array_slice_assoc( $url_metrics[0]->jsonSerialize(), array_keys( $expected_data ) ) - ); + $this->assertCount( 1, get_posts( array( 'post_type' => OD_URL_Metrics_Post_Type::SLUG ) ) ); - $this->assertInstanceOf( OD_URL_Metric_Store_Request_Context::class, $stored_context ); - $this->assertSame( $stored_context->url_metric->jsonSerialize(), $filtered_url_metric_data ); - - // Now check that od_trigger_page_cache_invalidation() cleaned caches as expected. - $this->assertSame( $url_metrics[0]->jsonSerialize(), $stored_context->url_metric->jsonSerialize() ); - if ( isset( $valid_params['cache_purge_post_id'] ) ) { - $cache_purge_post_id = $stored_context->request->get_param( 'cache_purge_post_id' ); - $this->assertSame( $valid_params['cache_purge_post_id'], $cache_purge_post_id ); - $scheduled = wp_next_scheduled( 'od_trigger_page_cache_invalidation', array( $cache_purge_post_id ) ); - $this->assertIsInt( $scheduled ); - $this->assertGreaterThan( time(), $scheduled ); - } + $this->assertTrue( $data['success'] ); + + $post = OD_URL_Metrics_Post_Type::get_post( $valid_params['slug'] ); + $this->assertInstanceOf( WP_Post::class, $post ); + + $url_metrics = OD_URL_Metrics_Post_Type::get_url_metrics_from_post( $post ); + $this->assertCount( 1, $url_metrics, 'Expected number of URL Metrics stored.' ); + $this->assertSame( $valid_params['elements'], $this->get_array_json_data( $url_metrics[0]->get( 'elements' ) ) ); + $this->assertSame( $valid_params['viewport']['width'], $url_metrics[0]->get_viewport_width() ); + + $expected_data = $valid_params; + unset( $expected_data['hmac'], $expected_data['slug'], $expected_data['current_etag'], $expected_data['cache_purge_post_id'] ); + unset( $expected_data['unset_prop'] ); + $this->assertSame( + $expected_data, + wp_array_slice_assoc( $url_metrics[0]->jsonSerialize(), array_keys( $expected_data ) ) + ); + + $this->assertInstanceOf( OD_URL_Metric_Store_Request_Context::class, $stored_context ); + + // Now check that od_trigger_page_cache_invalidation() cleaned caches as expected. + $this->assertSame( $url_metrics[0]->jsonSerialize(), $stored_context->url_metric->jsonSerialize() ); + if ( isset( $valid_params['cache_purge_post_id'] ) ) { + $cache_purge_post_id = $stored_context->request->get_param( 'cache_purge_post_id' ); + $this->assertSame( $valid_params['cache_purge_post_id'], $cache_purge_post_id ); + $scheduled = wp_next_scheduled( 'od_trigger_page_cache_invalidation', array( $cache_purge_post_id ) ); + $this->assertIsInt( $scheduled ); + $this->assertGreaterThan( time(), $scheduled ); } } From f8a00b42c9624ce601a8a801fc2c0f1b7cc99add Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 09:51:00 -0800 Subject: [PATCH 221/237] Remove todo This is using get_json_params() instead of get_params() because any URL query parameter would then be included among the params, so by just getting the params from the JSON body we avoid potential failures due to additional query parameters being injected into the request URL. --- plugins/optimization-detective/storage/rest-api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/optimization-detective/storage/rest-api.php b/plugins/optimization-detective/storage/rest-api.php index a67cc354ee..09ce02501e 100644 --- a/plugins/optimization-detective/storage/rest-api.php +++ b/plugins/optimization-detective/storage/rest-api.php @@ -174,7 +174,7 @@ function od_handle_rest_request( WP_REST_Request $request ) { ); } - $data = $request->get_json_params(); // TODO: Why not just get_params()? + $data = $request->get_json_params(); if ( ! is_array( $data ) ) { return new WP_Error( 'missing_array_json_body', From 4a8dc1675f8ee310eafc12fcc61dc8234105e9aa Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 10:14:21 -0800 Subject: [PATCH 222/237] Account for route matching being case insensitive --- plugins/image-prioritizer/helper.php | 7 ++++- .../image-prioritizer/tests/test-helper.php | 27 ++++++++++++++++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index e3dcda832c..6189c896a8 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -285,7 +285,12 @@ static function ( $host ) { * @noinspection PhpDocMissingThrowsInspection */ function image_prioritizer_filter_rest_request_before_callbacks( $response, array $handler, WP_REST_Request $request ) { - if ( $request->get_method() !== 'POST' || OD_REST_API_NAMESPACE . OD_URL_METRICS_ROUTE !== trim( $request->get_route(), '/' ) ) { + if ( + $request->get_method() !== 'POST' + || + // The strtolower() is due to \WP_REST_Server::match_request_to_handler() using case-insensitive pattern match. + OD_REST_API_NAMESPACE . OD_URL_METRICS_ROUTE !== strtolower( trim( $request->get_route(), '/' ) ) + ) { return $response; } diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index 65a6272d23..f1a7c0d1e1 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -760,7 +760,7 @@ public function data_provider_to_test_image_prioritizer_filter_rest_request_befo }; return array( - 'invalid_external_bg_image' => array( + 'invalid_external_bg_image' => array( 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { $url_metric_data = $get_sample_url_metric_data(); @@ -786,7 +786,7 @@ public function data_provider_to_test_image_prioritizer_filter_rest_request_befo }, ), - 'valid_external_bg_image' => array( + 'valid_external_bg_image' => array( 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { $url_metric_data = $get_sample_url_metric_data(); $image_url = home_url( '/good.jpg' ); @@ -846,7 +846,26 @@ static function ( $pre, $parsed_args, $url ) use ( $image_url ) { }, ), - 'not_store_post_request' => array( + 'invalid_external_bg_image_variant_route' => array( + 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { + $url_metric_data = $get_sample_url_metric_data(); + + $url_metric_data['lcpElementExternalBackgroundImage'] = array( + 'url' => 'https://bad-origin.example.com/image.jpg', + 'tag' => 'DIV', + 'id' => null, + 'class' => null, + ); + $request = $create_request( $url_metric_data ); + $request->set_route( str_replace( 'store', 'STORE', $request->get_route() ) ); + return $request; + }, + 'assert' => function ( WP_REST_Request $request ): void { + $this->assertArrayNotHasKey( 'lcpElementExternalBackgroundImage', $request ); + }, + ), + + 'not_store_post_request' => array( 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { $url_metric_data = $get_sample_url_metric_data(); $url_metric_data['lcpElementExternalBackgroundImage'] = 'https://totally-different.example.com/'; @@ -860,7 +879,7 @@ static function ( $pre, $parsed_args, $url ) use ( $image_url ) { }, ), - 'not_store_request' => array( + 'not_store_request' => array( 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { $url_metric_data = $get_sample_url_metric_data(); $url_metric_data['lcpElementExternalBackgroundImage'] = 'https://totally-different.example.com/'; From ba14c364c76754e855c42bd0fa20e7318f74ede7 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 10:31:44 -0800 Subject: [PATCH 223/237] Improve function description and further trim route --- plugins/image-prioritizer/helper.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 6189c896a8..201f03a96b 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -264,7 +264,7 @@ static function ( $host ) { } /** - * Filters the response before executing any REST API callbacks. + * Sanitizes the lcpElementExternalBackgroundImage property from the request URL Metric storage request. * * This removes the lcpElementExternalBackgroundImage from the URL Metric prior to it being stored if the background * image URL is not valid. Removal of the property is preferable to invalidating the entire URL Metric because then @@ -288,8 +288,8 @@ function image_prioritizer_filter_rest_request_before_callbacks( $response, arra if ( $request->get_method() !== 'POST' || - // The strtolower() is due to \WP_REST_Server::match_request_to_handler() using case-insensitive pattern match. - OD_REST_API_NAMESPACE . OD_URL_METRICS_ROUTE !== strtolower( trim( $request->get_route(), '/' ) ) + // The strtolower() is due to \WP_REST_Server::match_request_to_handler() using case-insensitive pattern match. + OD_REST_API_NAMESPACE . OD_URL_METRICS_ROUTE !== trim( strtolower( trim( $request->get_route(), '/' ) ) ) ) { return $response; } From 5ab7fd1a1ca07413795332a89fcf6bb3995360bb Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 11:15:02 -0800 Subject: [PATCH 224/237] Add test case for route ending in newline --- plugins/image-prioritizer/helper.php | 4 +- .../image-prioritizer/tests/test-helper.php | 72 ++++++++++++------- 2 files changed, 47 insertions(+), 29 deletions(-) diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 201f03a96b..75c3e18d43 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -288,8 +288,8 @@ function image_prioritizer_filter_rest_request_before_callbacks( $response, arra if ( $request->get_method() !== 'POST' || - // The strtolower() is due to \WP_REST_Server::match_request_to_handler() using case-insensitive pattern match. - OD_REST_API_NAMESPACE . OD_URL_METRICS_ROUTE !== trim( strtolower( trim( $request->get_route(), '/' ) ) ) + // The strtolower() and outer trim are due to \WP_REST_Server::match_request_to_handler() using case-insensitive pattern match and using '$' instead of '\z'. + OD_REST_API_NAMESPACE . OD_URL_METRICS_ROUTE !== rtrim( strtolower( ltrim( $request->get_route(), '/' ) ) ) ) { return $response; } diff --git a/plugins/image-prioritizer/tests/test-helper.php b/plugins/image-prioritizer/tests/test-helper.php index f1a7c0d1e1..dfd05b8549 100644 --- a/plugins/image-prioritizer/tests/test-helper.php +++ b/plugins/image-prioritizer/tests/test-helper.php @@ -759,17 +759,19 @@ public function data_provider_to_test_image_prioritizer_filter_rest_request_befo return $request; }; + $bad_origin_data = array( + 'url' => 'https://bad-origin.example.com/image.jpg', + 'tag' => 'DIV', + 'id' => null, + 'class' => null, + ); + return array( - 'invalid_external_bg_image' => array( - 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { + 'invalid_external_bg_image' => array( + 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request, $bad_origin_data ): WP_REST_Request { $url_metric_data = $get_sample_url_metric_data(); - $url_metric_data['lcpElementExternalBackgroundImage'] = array( - 'url' => 'https://bad-origin.example.com/image.jpg', - 'tag' => 'DIV', - 'id' => null, - 'class' => null, - ); + $url_metric_data['lcpElementExternalBackgroundImage'] = $bad_origin_data; $url_metric_data['viewport']['width'] = 10101; $url_metric_data['viewport']['height'] = 20202; return $create_request( $url_metric_data ); @@ -786,7 +788,7 @@ public function data_provider_to_test_image_prioritizer_filter_rest_request_befo }, ), - 'valid_external_bg_image' => array( + 'valid_external_bg_image' => array( 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { $url_metric_data = $get_sample_url_metric_data(); $image_url = home_url( '/good.jpg' ); @@ -846,17 +848,14 @@ static function ( $pre, $parsed_args, $url ) use ( $image_url ) { }, ), - 'invalid_external_bg_image_variant_route' => array( - 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { - $url_metric_data = $get_sample_url_metric_data(); - - $url_metric_data['lcpElementExternalBackgroundImage'] = array( - 'url' => 'https://bad-origin.example.com/image.jpg', - 'tag' => 'DIV', - 'id' => null, - 'class' => null, + 'invalid_external_bg_image_uppercase_route' => array( + 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request, $bad_origin_data ): WP_REST_Request { + $request = $create_request( + array_merge( + $get_sample_url_metric_data(), + array( 'lcpElementExternalBackgroundImage' => $bad_origin_data ) + ) ); - $request = $create_request( $url_metric_data ); $request->set_route( str_replace( 'store', 'STORE', $request->get_route() ) ); return $request; }, @@ -865,21 +864,40 @@ static function ( $pre, $parsed_args, $url ) use ( $image_url ) { }, ), - 'not_store_post_request' => array( - 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { - $url_metric_data = $get_sample_url_metric_data(); - $url_metric_data['lcpElementExternalBackgroundImage'] = 'https://totally-different.example.com/'; - $request = $create_request( $url_metric_data ); - $request->set_method( 'GET' ); + 'invalid_external_bg_image_trailing_newline_route' => array( + 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request, $bad_origin_data ): WP_REST_Request { + $request = $create_request( + array_merge( + $get_sample_url_metric_data(), + array( 'lcpElementExternalBackgroundImage' => $bad_origin_data ) + ) + ); + $request->set_route( $request->get_route() . "\n" ); return $request; }, 'assert' => function ( WP_REST_Request $request ): void { + $this->assertArrayNotHasKey( 'lcpElementExternalBackgroundImage', $request ); + }, + ), + + 'not_store_post_request' => array( + 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request, $bad_origin_data ): WP_REST_Request { + $request = $create_request( + array_merge( + $get_sample_url_metric_data(), + array( 'lcpElementExternalBackgroundImage' => $bad_origin_data ) + ) + ); + $request->set_method( 'GET' ); + return $request; + }, + 'assert' => function ( WP_REST_Request $request ) use ( $bad_origin_data ): void { $this->assertArrayHasKey( 'lcpElementExternalBackgroundImage', $request ); - $this->assertSame( 'https://totally-different.example.com/', $request['lcpElementExternalBackgroundImage'] ); + $this->assertSame( $bad_origin_data, $request['lcpElementExternalBackgroundImage'] ); }, ), - 'not_store_request' => array( + 'not_store_request' => array( 'set_up' => static function () use ( $get_sample_url_metric_data, $create_request ): WP_REST_Request { $url_metric_data = $get_sample_url_metric_data(); $url_metric_data['lcpElementExternalBackgroundImage'] = 'https://totally-different.example.com/'; From 2f4a9ea56aa60ce2113fbd5730ffc730b63a3be1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 13:46:24 -0800 Subject: [PATCH 225/237] Bump versions --- plugins/auto-sizes/auto-sizes.php | 4 ++-- plugins/auto-sizes/readme.txt | 4 +++- plugins/dominant-color-images/load.php | 4 ++-- plugins/dominant-color-images/readme.txt | 4 +++- plugins/embed-optimizer/load.php | 4 ++-- plugins/embed-optimizer/readme.txt | 4 +++- plugins/image-prioritizer/load.php | 4 ++-- plugins/image-prioritizer/readme.txt | 4 +++- plugins/optimization-detective/load.php | 4 ++-- plugins/optimization-detective/readme.txt | 4 +++- plugins/performance-lab/load.php | 4 ++-- plugins/performance-lab/readme.txt | 4 +++- plugins/web-worker-offloading/load.php | 4 ++-- plugins/web-worker-offloading/readme.txt | 4 +++- plugins/webp-uploads/load.php | 4 ++-- plugins/webp-uploads/readme.txt | 4 +++- 16 files changed, 40 insertions(+), 24 deletions(-) diff --git a/plugins/auto-sizes/auto-sizes.php b/plugins/auto-sizes/auto-sizes.php index 62e557befa..dc549e485f 100644 --- a/plugins/auto-sizes/auto-sizes.php +++ b/plugins/auto-sizes/auto-sizes.php @@ -5,7 +5,7 @@ * Description: Improves responsive images with better sizes calculations and auto-sizes for lazy-loaded images. * Requires at least: 6.6 * Requires PHP: 7.2 - * Version: 1.3.0 + * Version: 1.4.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -25,7 +25,7 @@ return; } -define( 'IMAGE_AUTO_SIZES_VERSION', '1.3.0' ); +define( 'IMAGE_AUTO_SIZES_VERSION', '1.4.0' ); require_once __DIR__ . '/includes/auto-sizes.php'; require_once __DIR__ . '/includes/improve-calculate-sizes.php'; diff --git a/plugins/auto-sizes/readme.txt b/plugins/auto-sizes/readme.txt index b5cca63074..6116afa37b 100644 --- a/plugins/auto-sizes/readme.txt +++ b/plugins/auto-sizes/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 6.7 -Stable tag: 1.3.0 +Stable tag: 1.4.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, images, auto-sizes @@ -52,6 +52,8 @@ Contributions are always welcome! Learn more about how to get involved in the [C == Changelog == += 1.4.0 = + = 1.3.0 = **Enhancements** diff --git a/plugins/dominant-color-images/load.php b/plugins/dominant-color-images/load.php index e2e3c0c1bc..56a7f51b7d 100644 --- a/plugins/dominant-color-images/load.php +++ b/plugins/dominant-color-images/load.php @@ -5,7 +5,7 @@ * Description: Displays placeholders based on an image's dominant color while the image is loading. * Requires at least: 6.6 * Requires PHP: 7.2 - * Version: 1.1.2 + * Version: 1.2.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -25,7 +25,7 @@ return; } -define( 'DOMINANT_COLOR_IMAGES_VERSION', '1.1.2' ); +define( 'DOMINANT_COLOR_IMAGES_VERSION', '1.2.0' ); require_once __DIR__ . '/helper.php'; require_once __DIR__ . '/hooks.php'; diff --git a/plugins/dominant-color-images/readme.txt b/plugins/dominant-color-images/readme.txt index b639dc4e53..63f1c6f46a 100644 --- a/plugins/dominant-color-images/readme.txt +++ b/plugins/dominant-color-images/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 6.7 -Stable tag: 1.1.2 +Stable tag: 1.2.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, images, dominant color @@ -47,6 +47,8 @@ Contributions are always welcome! Learn more about how to get involved in the [C == Changelog == += 1.2.0 = + = 1.1.2 = **Enhancements** diff --git a/plugins/embed-optimizer/load.php b/plugins/embed-optimizer/load.php index 4510fb5dc9..eda818a6ec 100644 --- a/plugins/embed-optimizer/load.php +++ b/plugins/embed-optimizer/load.php @@ -5,7 +5,7 @@ * Description: Optimizes the performance of embeds through lazy-loading, preconnecting, and reserving space to reduce layout shifts. * Requires at least: 6.6 * Requires PHP: 7.2 - * Version: 0.3.0 + * Version: 0.4.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -70,7 +70,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi } )( 'embed_optimizer_pending_plugin', - '0.3.0', + '0.4.0', static function ( string $version ): void { if ( defined( 'EMBED_OPTIMIZER_VERSION' ) ) { return; diff --git a/plugins/embed-optimizer/readme.txt b/plugins/embed-optimizer/readme.txt index a36210a5a7..8cc2bab624 100644 --- a/plugins/embed-optimizer/readme.txt +++ b/plugins/embed-optimizer/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 6.7 -Stable tag: 0.3.0 +Stable tag: 0.4.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, embeds @@ -67,6 +67,8 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 0.4.0 = + = 0.3.0 = **Enhancements** diff --git a/plugins/image-prioritizer/load.php b/plugins/image-prioritizer/load.php index 8fa0be02be..ce40dff411 100644 --- a/plugins/image-prioritizer/load.php +++ b/plugins/image-prioritizer/load.php @@ -6,7 +6,7 @@ * Requires at least: 6.6 * Requires PHP: 7.2 * Requires Plugins: optimization-detective - * Version: 0.2.0 + * Version: 0.3.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -71,7 +71,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi } )( 'image_prioritizer_pending_plugin', - '0.2.0', + '0.3.0', static function ( string $version ): void { if ( defined( 'IMAGE_PRIORITIZER_VERSION' ) ) { return; diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt index 867272d30a..8c6e28df84 100644 --- a/plugins/image-prioritizer/readme.txt +++ b/plugins/image-prioritizer/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 6.7 -Stable tag: 0.2.0 +Stable tag: 0.3.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, optimization, image, lcp, lazy-load @@ -70,6 +70,8 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 0.3.0 = + = 0.2.0 = **Enhancements** diff --git a/plugins/optimization-detective/load.php b/plugins/optimization-detective/load.php index 7000e184e8..81b60cb75f 100644 --- a/plugins/optimization-detective/load.php +++ b/plugins/optimization-detective/load.php @@ -5,7 +5,7 @@ * Description: Provides an API for leveraging real user metrics to detect optimizations to apply on the frontend to improve page performance. * Requires at least: 6.6 * Requires PHP: 7.2 - * Version: 0.9.0-alpha + * Version: 0.9.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -70,7 +70,7 @@ static function ( string $global_var_name, string $version, Closure $load ): voi } )( 'optimization_detective_pending_plugin', - '0.9.0-alpha', + '0.9.0', static function ( string $version ): void { if ( defined( 'OPTIMIZATION_DETECTIVE_VERSION' ) ) { return; diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index 1e783ef823..453a8719ce 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 6.7 -Stable tag: 0.8.0 +Stable tag: 0.9.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, optimization, rum @@ -319,6 +319,8 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 0.9.0 = + = 0.8.0 = **Enhancements** diff --git a/plugins/performance-lab/load.php b/plugins/performance-lab/load.php index 3cc0674036..8b5fbd7724 100644 --- a/plugins/performance-lab/load.php +++ b/plugins/performance-lab/load.php @@ -5,7 +5,7 @@ * Description: Performance plugin from the WordPress Performance Team, which is a collection of standalone performance features. * Requires at least: 6.6 * Requires PHP: 7.2 - * Version: 3.6.1 + * Version: 3.7.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -19,7 +19,7 @@ exit; // Exit if accessed directly. } -define( 'PERFLAB_VERSION', '3.6.1' ); +define( 'PERFLAB_VERSION', '3.7.0' ); define( 'PERFLAB_MAIN_FILE', __FILE__ ); define( 'PERFLAB_PLUGIN_DIR_PATH', plugin_dir_path( PERFLAB_MAIN_FILE ) ); define( 'PERFLAB_SCREEN', 'performance-lab' ); diff --git a/plugins/performance-lab/readme.txt b/plugins/performance-lab/readme.txt index 8cac6e2c0e..003c3cbab4 100644 --- a/plugins/performance-lab/readme.txt +++ b/plugins/performance-lab/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 6.7 -Stable tag: 3.6.1 +Stable tag: 3.7.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, site health, measurement, optimization, diagnostics @@ -71,6 +71,8 @@ Contributions are always welcome! Learn more about how to get involved in the [C == Changelog == += 3.7.0 = + = 3.6.1 = **Bug Fixes** diff --git a/plugins/web-worker-offloading/load.php b/plugins/web-worker-offloading/load.php index 831e965dab..a540a131da 100644 --- a/plugins/web-worker-offloading/load.php +++ b/plugins/web-worker-offloading/load.php @@ -5,7 +5,7 @@ * Description: Offloads select JavaScript execution to a Web Worker to reduce work on the main thread and improve the Interaction to Next Paint (INP) metric. * Requires at least: 6.6 * Requires PHP: 7.2 - * Version: 0.1.1 + * Version: 0.2.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -43,7 +43,7 @@ ); } -define( 'WEB_WORKER_OFFLOADING_VERSION', '0.1.1' ); +define( 'WEB_WORKER_OFFLOADING_VERSION', '0.2.0' ); require_once __DIR__ . '/helper.php'; require_once __DIR__ . '/hooks.php'; diff --git a/plugins/web-worker-offloading/readme.txt b/plugins/web-worker-offloading/readme.txt index db93c984cd..d2eda12c89 100644 --- a/plugins/web-worker-offloading/readme.txt +++ b/plugins/web-worker-offloading/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 6.7 -Stable tag: 0.1.1 +Stable tag: 0.2.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, JavaScript, web worker, partytown, analytics @@ -94,6 +94,8 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu == Changelog == += 0.2.0 = + = 0.1.1 = **Enhancements** diff --git a/plugins/webp-uploads/load.php b/plugins/webp-uploads/load.php index b474812a31..3ba80c299a 100644 --- a/plugins/webp-uploads/load.php +++ b/plugins/webp-uploads/load.php @@ -5,7 +5,7 @@ * Description: Converts images to more modern formats such as WebP or AVIF during upload. * Requires at least: 6.6 * Requires PHP: 7.2 - * Version: 2.3.0 + * Version: 2.4.0 * Author: WordPress Performance Team * Author URI: https://make.wordpress.org/performance/ * License: GPLv2 or later @@ -25,7 +25,7 @@ return; } -define( 'WEBP_UPLOADS_VERSION', '2.3.0' ); +define( 'WEBP_UPLOADS_VERSION', '2.4.0' ); define( 'WEBP_UPLOADS_MAIN_FILE', plugin_basename( __FILE__ ) ); require_once __DIR__ . '/helper.php'; diff --git a/plugins/webp-uploads/readme.txt b/plugins/webp-uploads/readme.txt index 9c793785c5..8695e754d9 100644 --- a/plugins/webp-uploads/readme.txt +++ b/plugins/webp-uploads/readme.txt @@ -2,7 +2,7 @@ Contributors: wordpressdotorg Tested up to: 6.7 -Stable tag: 2.3.0 +Stable tag: 2.4.0 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Tags: performance, images, webp, avif, modern image formats @@ -60,6 +60,8 @@ By default, the Modern Image Formats plugin will only generate WebP versions of == Changelog == += 2.4.0 = + = 2.3.0 = **Enhancements** From 6deb42b22df5334f39ffccde9e2f6cbe74bed394 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 13:48:18 -0800 Subject: [PATCH 226/237] Update since n.e.x.t --- plugins/auto-sizes/includes/auto-sizes.php | 2 +- .../includes/improve-calculate-sizes.php | 12 ++++++------ plugins/dominant-color-images/hooks.php | 6 +++--- .../class-embed-optimizer-tag-visitor.php | 2 +- plugins/embed-optimizer/hooks.php | 2 +- ...ritizer-background-image-styled-tag-visitor.php | 14 +++++++------- .../class-image-prioritizer-img-tag-visitor.php | 12 ++++++------ plugins/image-prioritizer/detect.js | 12 ++++++------ plugins/image-prioritizer/helper.php | 14 +++++++------- .../class-od-html-tag-processor.php | 4 ++-- .../class-od-url-metric-group-collection.php | 6 +++--- .../class-od-url-metric-group.php | 14 +++++++------- .../optimization-detective/class-od-url-metric.php | 4 ++-- plugins/optimization-detective/helper.php | 2 +- plugins/optimization-detective/optimization.php | 2 +- plugins/optimization-detective/storage/data.php | 10 +++++----- plugins/performance-lab/includes/admin/load.php | 2 +- .../third-party/google-site-kit.php | 6 +++--- .../third-party/seo-by-rank-math.php | 8 ++++---- plugins/webp-uploads/helper.php | 2 +- plugins/webp-uploads/hooks.php | 4 ++-- plugins/webp-uploads/settings.php | 2 +- 22 files changed, 71 insertions(+), 71 deletions(-) diff --git a/plugins/auto-sizes/includes/auto-sizes.php b/plugins/auto-sizes/includes/auto-sizes.php index 44df012d42..02ecad68be 100644 --- a/plugins/auto-sizes/includes/auto-sizes.php +++ b/plugins/auto-sizes/includes/auto-sizes.php @@ -3,7 +3,7 @@ * Functionality to implement auto-sizes for lazy loaded images. * * @package auto-sizes - * @since n.e.x.t + * @since 1.4.0 */ /** diff --git a/plugins/auto-sizes/includes/improve-calculate-sizes.php b/plugins/auto-sizes/includes/improve-calculate-sizes.php index db1573c43a..7b57f08e36 100644 --- a/plugins/auto-sizes/includes/improve-calculate-sizes.php +++ b/plugins/auto-sizes/includes/improve-calculate-sizes.php @@ -3,13 +3,13 @@ * Functionality to improve the calculation of image `sizes` attributes. * * @package auto-sizes - * @since n.e.x.t + * @since 1.4.0 */ /** * Primes attachment into the cache with a single database query. * - * @since n.e.x.t + * @since 1.4.0 * * @param string|mixed $content The HTML content. * @return string The HTML content. @@ -130,7 +130,7 @@ function auto_sizes_filter_image_tag( $content, array $parsed_block, WP_Block $b /** * Modifies the sizes attribute of an image based on layout context. * - * @since n.e.x.t + * @since 1.4.0 * * @param int $id The image attachment post ID. * @param string|array{int, int} $size Image size name or array of width and height. @@ -228,7 +228,7 @@ function auto_sizes_calculate_better_sizes( int $id, $size, string $align, int $ /** * Retrieves the layout width for an alignment defined in theme.json. * - * @since n.e.x.t + * @since 1.4.0 * * @param string $alignment The alignment value. * @return string The alignment width based. @@ -248,7 +248,7 @@ function auto_sizes_get_layout_width( string $alignment ): string { /** * Filters the context keys that a block type uses. * - * @since n.e.x.t + * @since 1.4.0 * * @param string[] $uses_context Array of registered uses context for a block type. * @param WP_Block_Type $block_type The full block type object. @@ -271,7 +271,7 @@ function auto_sizes_filter_uses_context( array $uses_context, WP_Block_Type $blo /** * Modifies the block context during rendering to blocks. * - * @since n.e.x.t + * @since 1.4.0 * * @param array $context Current block context. * @param array $block The block being rendered. diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index a841a6f1f0..f415f3e005 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -194,7 +194,7 @@ function dominant_color_render_generator(): void { * to apply background color based on the dominant color for attachment previews * in the WordPress admin interface. * - * @since n.e.x.t + * @since 1.2.0 */ function dominant_color_admin_inline_style(): void { $handle = 'dominant-color-admin-styles'; @@ -213,7 +213,7 @@ function dominant_color_admin_inline_style(): void { * the attachment template. It adds attributes for dominant color and transparency * to the template, allowing these properties to be displayed in the media library. * - * @since n.e.x.t + * @since 1.2.0 * @see wp_print_media_templates() */ function dominant_color_admin_script(): void { @@ -249,7 +249,7 @@ function dominant_color_admin_script(): void { * the dominant color and transparency of the image. It modifies the response array to include * these additional properties, which can be used in the media library interface. * - * @since n.e.x.t + * @since 1.2.0 * * @param array|mixed $response The current response array for the attachment. * @param WP_Post $attachment The attachment post object. diff --git a/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php b/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php index a1d02b98f1..9c30bdfc70 100644 --- a/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php +++ b/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php @@ -81,7 +81,7 @@ private function is_embed_wrapper( OD_HTML_Tag_Processor $processor ): bool { * Otherwise, if the embed is not in any initial viewport, it will add lazy-loading logic. * * @since 0.2.0 - * @since n.e.x.t Adds preconnect links for each viewport group and skips if the element is not in the viewport for that group. + * @since 0.4.0 Adds preconnect links for each viewport group and skips if the element is not in the viewport for that group. * * @param OD_Tag_Visitor_Context $context Tag visitor context. * @return bool Whether the tag should be tracked in URL Metrics. diff --git a/plugins/embed-optimizer/hooks.php b/plugins/embed-optimizer/hooks.php index 988a5a2dfd..e32e360cc3 100644 --- a/plugins/embed-optimizer/hooks.php +++ b/plugins/embed-optimizer/hooks.php @@ -428,7 +428,7 @@ function embed_optimizer_render_generator(): void { /** * Gets the path to a script or stylesheet. * - * @since n.e.x.t + * @since 0.4.0 * * @param string $src_path Source path, relative to plugin root. * @param string|null $min_path Minified path. If not supplied, then '.min' is injected before the file extension in the source path. diff --git a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php index c6570d4d2c..0661bdfb98 100644 --- a/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php +++ b/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php @@ -29,7 +29,7 @@ final class Image_Prioritizer_Background_Image_Styled_Tag_Visitor extends Image_ /** * Class name used to indicate a background image which is lazy-loaded. * - * @since n.e.x.t + * @since 0.3.0 * @var string */ const LAZY_BG_IMAGE_CLASS_NAME = 'od-lazy-bg-image'; @@ -37,7 +37,7 @@ final class Image_Prioritizer_Background_Image_Styled_Tag_Visitor extends Image_ /** * Whether the lazy-loading script and stylesheet have been added. * - * @since n.e.x.t + * @since 0.3.0 * @var bool */ private $added_lazy_assets = false; @@ -45,7 +45,7 @@ final class Image_Prioritizer_Background_Image_Styled_Tag_Visitor extends Image_ /** * Tuples of URL Metric group and the common LCP element external background image. * - * @since n.e.x.t + * @since 0.3.0 * @var array */ private $group_common_lcp_element_external_background_images; @@ -99,7 +99,7 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { /** * Gets the common LCP element external background image for a URL Metric group. * - * @since n.e.x.t + * @since 0.3.0 * * @param OD_URL_Metric_Group $group Group. * @return LcpElementExternalBackgroundImage|null @@ -135,7 +135,7 @@ private function get_common_lcp_element_external_background_image( OD_URL_Metric /** * Maybe preloads external background image. * - * @since n.e.x.t + * @since 0.3.0 * * @param OD_Tag_Visitor_Context $context Context. */ @@ -182,7 +182,7 @@ private function maybe_preload_external_lcp_background_image( OD_Tag_Visitor_Con /** * Adds an image preload link for the group. * - * @since n.e.x.t + * @since 0.3.0 * * @param OD_Link_Collection $link_collection Link collection. * @param OD_URL_Metric_Group $group URL Metric group. @@ -205,7 +205,7 @@ private function add_image_preload_link( OD_Link_Collection $link_collection, OD /** * Optimizes an element with a background image based on whether it is displayed in any initial viewport. * - * @since n.e.x.t + * @since 0.3.0 * * @param OD_Tag_Visitor_Context $context Tag visitor context, with the cursor currently at block with a background image. */ diff --git a/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php b/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php index ba14edf57e..15a3008ce4 100644 --- a/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php +++ b/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php @@ -25,7 +25,7 @@ final class Image_Prioritizer_Img_Tag_Visitor extends Image_Prioritizer_Tag_Visi * Visits a tag. * * @since 0.1.0 - * @since n.e.x.t Separate the processing of IMG and PICTURE elements. + * @since 0.3.0 Separate the processing of IMG and PICTURE elements. * * @param OD_Tag_Visitor_Context $context Tag visitor context. * @return bool Whether the tag should be tracked in URL Metrics. @@ -46,7 +46,7 @@ public function __invoke( OD_Tag_Visitor_Context $context ): bool { /** * Process an IMG element. * - * @since n.e.x.t + * @since 0.3.0 * * @param OD_HTML_Tag_Processor $processor HTML tag processor. * @param OD_Tag_Visitor_Context $context Tag visitor context. @@ -183,7 +183,7 @@ private function process_img( OD_HTML_Tag_Processor $processor, OD_Tag_Visitor_C /** * Process a PICTURE element. * - * @since n.e.x.t + * @since 0.3.0 * * @param OD_HTML_Tag_Processor $processor HTML tag processor. * @param OD_Tag_Visitor_Context $context Tag visitor context. @@ -283,7 +283,7 @@ private function process_picture( OD_HTML_Tag_Processor $processor, OD_Tag_Visit * Returns null if the src attribute is not a string (i.e. src was used as a boolean attribute was used), if it * it has an empty string value after trimming, or if it is a data: URL. * - * @since n.e.x.t + * @since 0.3.0 * * @param OD_HTML_Tag_Processor $processor Processor. * @param 'src'|'srcset' $attribute_name Attribute name. @@ -304,7 +304,7 @@ private function get_valid_src( OD_HTML_Tag_Processor $processor, string $attrib /** * Adds a LINK with the supplied attributes for each viewport group when the provided XPath is the LCP element. * - * @since n.e.x.t + * @since 0.3.0 * * @param OD_Tag_Visitor_Context $context Tag visitor context. * @param string $xpath XPath of the element. @@ -351,7 +351,7 @@ static function ( $attribute_value ) { /** * Gets the parent tag name. * - * @since n.e.x.t + * @since 0.3.0 * * @param OD_Tag_Visitor_Context $context Tag visitor context. * @return string|null The parent tag name or null if not found. diff --git a/plugins/image-prioritizer/detect.js b/plugins/image-prioritizer/detect.js index 6a73074c24..54fe64d09b 100644 --- a/plugins/image-prioritizer/detect.js +++ b/plugins/image-prioritizer/detect.js @@ -34,7 +34,7 @@ const externalBackgroundImages = []; /** * Logs a message. * - * @since n.e.x.t + * @since 0.3.0 * * @param {...*} message */ @@ -46,7 +46,7 @@ function log( ...message ) { /** * Logs a warning. * - * @since n.e.x.t + * @since 0.3.0 * * @param {...*} message */ @@ -58,7 +58,7 @@ function warn( ...message ) { /** * Initializes extension. * - * @since n.e.x.t + * @since 0.3.0 * * @type {InitializeCallback} * @param {InitializeArgs} args Args. @@ -80,7 +80,7 @@ export async function initialize( { isDebug, onLCP } ) { /** * Gets the performance resource entry for a given URL. * - * @since n.e.x.t + * @since 0.3.0 * * @param {string} url - Resource URL. * @return {PerformanceResourceTiming|null} Resource entry or null. @@ -101,7 +101,7 @@ function getPerformanceResourceByURL( url ) { /** * Handles a new LCP metric being reported. * - * @since n.e.x.t + * @since 0.3.0 * * @param {LCPMetric} metric - LCP Metric. * @param {boolean} isDebug - Whether in debug mode. @@ -203,7 +203,7 @@ function handleLCPMetric( metric, isDebug ) { /** * Finalizes extension. * - * @since n.e.x.t + * @since 0.3.0 * * @type {FinalizeCallback} * @param {FinalizeArgs} args Args. diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 75c3e18d43..3e8dd49cf2 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -83,7 +83,7 @@ function image_prioritizer_register_tag_visitors( OD_Tag_Visitor_Registry $regis /** * Filters the list of Optimization Detective extension module URLs to include the extension for Image Prioritizer. * - * @since n.e.x.t + * @since 0.3.0 * @access private * * @param string[]|mixed $extension_module_urls Extension module URLs. @@ -100,7 +100,7 @@ function image_prioritizer_filter_extension_module_urls( $extension_module_urls /** * Filters additional properties for the element item schema for Optimization Detective. * - * @since n.e.x.t + * @since 0.3.0 * @access private * * @param array $additional_properties Additional properties. @@ -145,7 +145,7 @@ function image_prioritizer_add_element_item_schema_properties( array $additional /** * Validates URL for a background image. * - * @since n.e.x.t + * @since 0.3.0 * @access private * * @param string $url Background image URL. @@ -271,7 +271,7 @@ static function ( $host ) { * potentially no URL Metrics would ever be collected if, for example, the background image URL is pointing to a * disallowed origin. Then none of the other optimizations would be able to be applied. * - * @since n.e.x.t + * @since 0.3.0 * @access private * * @phpstan-param WP_REST_Request> $request @@ -322,7 +322,7 @@ function image_prioritizer_filter_rest_request_before_callbacks( $response, arra /** * Gets the path to a script or stylesheet. * - * @since n.e.x.t + * @since 0.3.0 * @access private * * @param string $src_path Source path, relative to plugin root. @@ -384,7 +384,7 @@ function image_prioritizer_get_video_lazy_load_script(): string { * * Load the background image when it approaches the viewport using an IntersectionObserver. * - * @since n.e.x.t + * @since 0.3.0 * @access private * * @return string Lazy load script. @@ -397,7 +397,7 @@ function image_prioritizer_get_lazy_load_bg_image_script(): string { /** * Gets the stylesheet to lazy-load background images. * - * @since n.e.x.t + * @since 0.3.0 * @access private * * @return string Lazy load stylesheet. diff --git a/plugins/optimization-detective/class-od-html-tag-processor.php b/plugins/optimization-detective/class-od-html-tag-processor.php index 16952a23fd..48ad11d126 100644 --- a/plugins/optimization-detective/class-od-html-tag-processor.php +++ b/plugins/optimization-detective/class-od-html-tag-processor.php @@ -497,7 +497,7 @@ public function release_bookmark( $name ): bool { * A breadcrumb consists of a tag name and its sibling index. * * @since 0.4.0 - * @since n.e.x.t Renamed from get_breadcrumbs() to get_indexed_breadcrumbs(). + * @since 0.9.0 Renamed from get_breadcrumbs() to get_indexed_breadcrumbs(). * * @return Generator Breadcrumb. */ @@ -513,7 +513,7 @@ private function get_indexed_breadcrumbs(): Generator { * Breadcrumbs start at the outermost parent and descend toward the matched element. * They always include the entire path from the root HTML node to the matched element. * - * @since n.e.x.t + * @since 0.9.0 * @see WP_HTML_Processor::get_breadcrumbs() * * @return string[] Array of tag names representing path to matched node. diff --git a/plugins/optimization-detective/class-od-url-metric-group-collection.php b/plugins/optimization-detective/class-od-url-metric-group-collection.php index c87ac93e33..56d257402e 100644 --- a/plugins/optimization-detective/class-od-url-metric-group-collection.php +++ b/plugins/optimization-detective/class-od-url-metric-group-collection.php @@ -38,7 +38,7 @@ final class OD_URL_Metric_Group_Collection implements Countable, IteratorAggrega /** * The current ETag. * - * @since n.e.x.t + * @since 0.9.0 * @var non-empty-string */ private $current_etag; @@ -186,7 +186,7 @@ public function __construct( array $url_metrics, string $current_etag, array $br /** * Gets the current ETag. * - * @since n.e.x.t + * @since 0.9.0 * * @return non-empty-string Current ETag. */ @@ -427,7 +427,7 @@ public function get_groups_by_lcp_element( string $xpath ): array { * Gets common LCP element. * * @since 0.3.0 - * @since n.e.x.t An LCP element is also considered common if it is the same in the narrowest and widest viewport groups, and all intermediate groups are empty. + * @since 0.9.0 An LCP element is also considered common if it is the same in the narrowest and widest viewport groups, and all intermediate groups are empty. * * @return OD_Element|null Common LCP element if it exists. */ diff --git a/plugins/optimization-detective/class-od-url-metric-group.php b/plugins/optimization-detective/class-od-url-metric-group.php index f8772eca75..1e81641fdc 100644 --- a/plugins/optimization-detective/class-od-url-metric-group.php +++ b/plugins/optimization-detective/class-od-url-metric-group.php @@ -183,7 +183,7 @@ public function get_maximum_viewport_width(): int { /** * Gets the sample size for URL Metrics for a given breakpoint. * - * @since n.e.x.t + * @since 0.9.0 * * @todo Eliminate in favor of readonly public property. * @phpstan-return positive-int @@ -196,7 +196,7 @@ public function get_sample_size(): int { /** * Gets the freshness age (TTL) for a given URL Metric. * - * @since n.e.x.t + * @since 0.9.0 * * @todo Eliminate in favor of readonly public property. * @phpstan-return 0|positive-int @@ -265,7 +265,7 @@ static function ( OD_URL_Metric $a, OD_URL_Metric $b ): int { * and all of these URL Metrics are fresh. * * @since 0.1.0 - * @since n.e.x.t If the current environment's generated ETag does not match the URL Metric's ETag, the URL Metric is considered stale. + * @since 0.9.0 If the current environment's generated ETag does not match the URL Metric's ETag, the URL Metric is considered stale. * * @return bool Whether complete. */ @@ -385,7 +385,7 @@ public function get_lcp_element(): ?OD_Element { /** * Gets all elements from all URL Metrics in the viewport group keyed by the elements' XPaths. * - * @since n.e.x.t + * @since 0.9.0 * * @return array> Keys are XPaths and values are the element instances. */ @@ -411,7 +411,7 @@ public function get_xpath_elements_map(): array { /** * Gets the max intersection ratios of all elements in the viewport group and its captured URL Metrics. * - * @since n.e.x.t + * @since 0.9.0 * * @return array Keys are XPaths and values are the intersection ratios. */ @@ -439,7 +439,7 @@ public function get_all_element_max_intersection_ratios(): array { /** * Gets the max intersection ratio of an element in the viewport group and its captured URL Metrics. * - * @since n.e.x.t + * @since 0.9.0 * * @param string $xpath XPath for the element. * @return float|null Max intersection ratio of null if tag is unknown (not captured). @@ -473,7 +473,7 @@ public function count(): int { /** * Clears result cache. * - * @since n.e.x.t + * @since 0.9.0 */ public function clear_cache(): void { $this->result_cache = array(); diff --git a/plugins/optimization-detective/class-od-url-metric.php b/plugins/optimization-detective/class-od-url-metric.php index f4a4e25cc9..0a164edc9e 100644 --- a/plugins/optimization-detective/class-od-url-metric.php +++ b/plugins/optimization-detective/class-od-url-metric.php @@ -156,7 +156,7 @@ public function set_group( OD_URL_Metric_Group $group ): void { * Gets JSON schema for URL Metric. * * @since 0.1.0 - * @since n.e.x.t Added the 'etag' property to the schema. + * @since 0.9.0 Added the 'etag' property to the schema. * * @todo Cache the return value? * @@ -431,7 +431,7 @@ public function get_uuid(): string { /** * Gets ETag. * - * @since n.e.x.t + * @since 0.9.0 * * @return non-empty-string|null ETag. */ diff --git a/plugins/optimization-detective/helper.php b/plugins/optimization-detective/helper.php index bc9bbfb7fe..27073205d1 100644 --- a/plugins/optimization-detective/helper.php +++ b/plugins/optimization-detective/helper.php @@ -68,7 +68,7 @@ function od_render_generator_meta_tag(): void { /** * Gets the path to a script or stylesheet. * - * @since n.e.x.t + * @since 0.9.0 * * @param string $src_path Source path, relative to plugin root. * @param string|null $min_path Minified path. If not supplied, then '.min' is injected before the file extension in the source path. diff --git a/plugins/optimization-detective/optimization.php b/plugins/optimization-detective/optimization.php index 74548eef12..fa72a6194e 100644 --- a/plugins/optimization-detective/optimization.php +++ b/plugins/optimization-detective/optimization.php @@ -98,7 +98,7 @@ function_exists( 'perflab_server_timing_use_output_buffer' ) * Determines whether the current response can be optimized. * * @since 0.1.0 - * @since n.e.x.t Response is optimized for admin users as well when in 'plugin' development mode. + * @since 0.9.0 Response is optimized for admin users as well when in 'plugin' development mode. * * @access private * diff --git a/plugins/optimization-detective/storage/data.php b/plugins/optimization-detective/storage/data.php index 4c51c49ab9..367637f00d 100644 --- a/plugins/optimization-detective/storage/data.php +++ b/plugins/optimization-detective/storage/data.php @@ -143,7 +143,7 @@ function od_get_url_metrics_slug( array $query_vars ): string { /** * Gets the current template for a block theme or a classic theme. * - * @since n.e.x.t + * @since 0.9.0 * @access private * * @global string|null $_wp_current_template_id Current template ID. @@ -173,7 +173,7 @@ function od_get_current_theme_template() { * posts in The Loop, and theme information in the current environment. This ETag * is used to assess if the URL Metrics are stale when its value changes. * - * @since n.e.x.t + * @since 0.9.0 * @access private * * @param OD_Tag_Visitor_Registry $tag_visitor_registry Tag visitor registry. @@ -238,7 +238,7 @@ static function ( $post ): ?array { /** * Filters the data that goes into computing the current ETag for URL Metrics. * - * @since n.e.x.t + * @since 0.9.0 * * @param array $data Data. */ @@ -253,7 +253,7 @@ static function ( $post ): ?array { * This is used in the REST API to authenticate the storage of new URL Metrics from a given URL. * * @since 0.8.0 - * @since n.e.x.t Introduced the `$current_etag` parameter. + * @since 0.9.0 Introduced the `$current_etag` parameter. * @access private * * @see od_verify_url_metrics_storage_hmac() @@ -274,7 +274,7 @@ function od_get_url_metrics_storage_hmac( string $slug, string $current_etag, st * Verifies HMAC for storing URL Metrics for a specific slug. * * @since 0.8.0 - * @since n.e.x.t Introduced the `$current_etag` parameter. + * @since 0.9.0 Introduced the `$current_etag` parameter. * @access private * * @see od_get_url_metrics_storage_hmac() diff --git a/plugins/performance-lab/includes/admin/load.php b/plugins/performance-lab/includes/admin/load.php index 8416f3e74a..aecac103d5 100644 --- a/plugins/performance-lab/includes/admin/load.php +++ b/plugins/performance-lab/includes/admin/load.php @@ -216,7 +216,7 @@ function perflab_dismiss_wp_pointer_wrapper(): void { /** * Gets the path to a script or stylesheet. * - * @since n.e.x.t + * @since 3.7.0 * * @param string $src_path Source path. * @param string|null $min_path Minified path. If not supplied, then '.min' is injected before the file extension in the source path. diff --git a/plugins/web-worker-offloading/third-party/google-site-kit.php b/plugins/web-worker-offloading/third-party/google-site-kit.php index 535c9cd923..cf334056e8 100644 --- a/plugins/web-worker-offloading/third-party/google-site-kit.php +++ b/plugins/web-worker-offloading/third-party/google-site-kit.php @@ -2,7 +2,7 @@ /** * Web Worker Offloading integration with Site Kit by Google. * - * @since n.e.x.t + * @since 0.2.0 * @package web-worker-offloading */ @@ -13,7 +13,7 @@ /** * Configures WWO for Site Kit and Google Analytics. * - * @since n.e.x.t + * @since 0.2.0 * @access private * @link https://partytown.builder.io/google-tag-manager#forward-events * @@ -53,7 +53,7 @@ function plwwo_google_site_kit_configure( $configuration ): array { /** * Filters inline script attributes to offload Google Site Kit's GTag script tag to Partytown. * - * @since n.e.x.t + * @since 0.2.0 * @access private * @link https://github.com/google/site-kit-wp/blob/abbb74ff21f98a8779fbab0eeb9a16279a122bc4/includes/Core/Consent_Mode/Consent_Mode.php#L244-L259 * diff --git a/plugins/web-worker-offloading/third-party/seo-by-rank-math.php b/plugins/web-worker-offloading/third-party/seo-by-rank-math.php index 7436e924bb..302d218b50 100644 --- a/plugins/web-worker-offloading/third-party/seo-by-rank-math.php +++ b/plugins/web-worker-offloading/third-party/seo-by-rank-math.php @@ -2,7 +2,7 @@ /** * Web Worker Offloading integration with Rank Math SEO. * - * @since n.e.x.t + * @since 0.2.0 * @package web-worker-offloading */ @@ -13,7 +13,7 @@ /** * Configures WWO for Rank Math SEO and Google Analytics. * - * @since n.e.x.t + * @since 0.2.0 * @access private * @link https://partytown.builder.io/google-tag-manager#forward-events * @@ -43,7 +43,7 @@ function plwwo_rank_math_configure( $configuration ): array { /** * Filters script attributes to offload Rank Math's GTag script tag to Partytown. * - * @since n.e.x.t + * @since 0.2.0 * @access private * @link https://github.com/rankmath/seo-by-rank-math/blob/c78adba6f78079f27ff1430fabb75c6ac3916240/includes/modules/analytics/class-gtag.php#L161-L167 * @@ -63,7 +63,7 @@ function plwwo_rank_math_filter_script_attributes( $attributes ) { /** * Filters inline script attributes to offload Rank Math's GTag script tag to Partytown. * - * @since n.e.x.t + * @since 0.2.0 * @access private * @link https://github.com/rankmath/seo-by-rank-math/blob/c78adba6f78079f27ff1430fabb75c6ac3916240/includes/modules/analytics/class-gtag.php#L169-L174 * diff --git a/plugins/webp-uploads/helper.php b/plugins/webp-uploads/helper.php index b89e442ba9..2ca917012e 100644 --- a/plugins/webp-uploads/helper.php +++ b/plugins/webp-uploads/helper.php @@ -414,7 +414,7 @@ function webp_uploads_is_fallback_enabled(): bool { /** * Checks if the `perflab_generate_all_fallback_sizes` option is enabled. * - * @since n.e.x.t + * @since 2.4.0 * * @return bool Whether the option is enabled. Default is false. */ diff --git a/plugins/webp-uploads/hooks.php b/plugins/webp-uploads/hooks.php index 0e36bbca5a..3f5e5014d2 100644 --- a/plugins/webp-uploads/hooks.php +++ b/plugins/webp-uploads/hooks.php @@ -784,7 +784,7 @@ function webp_uploads_init(): void { /** * Automatically opt into extra image sizes when generating fallback images. * - * @since n.e.x.t + * @since 2.4.0 * * @global array $_wp_additional_image_sizes Associative array of additional image sizes. */ @@ -810,7 +810,7 @@ function webp_uploads_opt_in_extra_image_sizes(): void { /** * Enables additional MIME type support for all image sizes based on the generate all fallback sizes settings. * - * @since n.e.x.t + * @since 2.4.0 * * @param array $allowed_sizes A map of image size names and whether they are allowed to have additional MIME types. * @return array Modified map of image sizes with additional MIME type support. diff --git a/plugins/webp-uploads/settings.php b/plugins/webp-uploads/settings.php index cad0352e11..fa27269c47 100644 --- a/plugins/webp-uploads/settings.php +++ b/plugins/webp-uploads/settings.php @@ -204,7 +204,7 @@ function webp_uploads_generate_webp_jpeg_setting_callback(): void { /** * Renders the settings field for generating all fallback image sizes. * - * @since n.e.x.t + * @since 2.4.0 */ function webp_uploads_generate_all_fallback_sizes_callback(): void { $all_fallback_sizes_enabled = webp_uploads_should_generate_all_fallback_sizes(); From 2d45c30f90784ef37cb5c6611f1dc61687681bbf Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 14:03:12 -0800 Subject: [PATCH 227/237] Add changelogs to readmes via npm run readme --- plugins/auto-sizes/readme.txt | 15 +++++++++++++++ plugins/dominant-color-images/readme.txt | 4 ++++ plugins/embed-optimizer/readme.txt | 4 ++++ plugins/image-prioritizer/readme.txt | 7 +++++++ plugins/optimization-detective/readme.txt | 12 ++++++++++++ plugins/performance-lab/readme.txt | 11 +++++++++++ plugins/web-worker-offloading/readme.txt | 9 +++++++++ plugins/webp-uploads/readme.txt | 6 ++++++ 8 files changed, 68 insertions(+) diff --git a/plugins/auto-sizes/readme.txt b/plugins/auto-sizes/readme.txt index 6116afa37b..73a53923f5 100644 --- a/plugins/auto-sizes/readme.txt +++ b/plugins/auto-sizes/readme.txt @@ -54,6 +54,21 @@ Contributions are always welcome! Learn more about how to get involved in the [C = 1.4.0 = +**Features** + +* Accurate Sizes: Incorporate layout constraints in image sizes calculations. ([1738](https://github.com/WordPress/performance/pull/1738)) + +**Enhancements** + +* Accurate sizes: Pass parent alignment context to images. ([1701](https://github.com/WordPress/performance/pull/1701)) +* Accurate sizes: Reorganize file structure by feature. ([1699](https://github.com/WordPress/performance/pull/1699)) +* Accurate sizes: Support relative alignment widths. ([1737](https://github.com/WordPress/performance/pull/1737)) +* Remove `auto_sizes_get_layout_settings()`. ([1743](https://github.com/WordPress/performance/pull/1743)) + +**Bug Fixes** + +* Accurate sizes: Disable layout calculations for classic themes. ([1744](https://github.com/WordPress/performance/pull/1744)) + = 1.3.0 = **Enhancements** diff --git a/plugins/dominant-color-images/readme.txt b/plugins/dominant-color-images/readme.txt index 63f1c6f46a..1aa6df61f6 100644 --- a/plugins/dominant-color-images/readme.txt +++ b/plugins/dominant-color-images/readme.txt @@ -49,6 +49,10 @@ Contributions are always welcome! Learn more about how to get involved in the [C = 1.2.0 = +**Enhancements** + +* Enhance admin media UI with dominant color support. ([1719](https://github.com/WordPress/performance/pull/1719)) + = 1.1.2 = **Enhancements** diff --git a/plugins/embed-optimizer/readme.txt b/plugins/embed-optimizer/readme.txt index 8cc2bab624..0ee1fa1d70 100644 --- a/plugins/embed-optimizer/readme.txt +++ b/plugins/embed-optimizer/readme.txt @@ -69,6 +69,10 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu = 0.4.0 = +**Enhancements** + +* Incorporate breakpoints into preconnect links added by Embed Optimizer. ([1654](https://github.com/WordPress/performance/pull/1654)) + = 0.3.0 = **Enhancements** diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt index 8c6e28df84..5e14d18a9f 100644 --- a/plugins/image-prioritizer/readme.txt +++ b/plugins/image-prioritizer/readme.txt @@ -72,6 +72,13 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu = 0.3.0 = +**Enhancements** + +* Add preload links LCP picture elements. ([1707](https://github.com/WordPress/performance/pull/1707)) +* Harden validation of user-submitted LCP background image URL. ([1713](https://github.com/WordPress/performance/pull/1713)) +* Lazy load background images added via inline style attributes. ([1708](https://github.com/WordPress/performance/pull/1708)) +* Preload image URLs for LCP elements with external background images. ([1697](https://github.com/WordPress/performance/pull/1697)) + = 0.2.0 = **Enhancements** diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index 453a8719ce..b41503248a 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -321,6 +321,18 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu = 0.9.0 = +**Enhancements** + +* Add `fetchpriority=high` to `IMG` when it is the LCP element on desktop and mobile with other viewport groups empty. ([1723](https://github.com/WordPress/performance/pull/1723)) +* Improve debugging stored URL Metrics in Optimization Detective. ([1656](https://github.com/WordPress/performance/pull/1656)) +* Incorporate page state into ETag computation. ([1722](https://github.com/WordPress/performance/pull/1722)) +* Mark existing URL Metrics as stale when a new tag visitor is registered. ([1705](https://github.com/WordPress/performance/pull/1705)) +* Set development mode to 'plugin' in the dev environment and allow pages to be optimized when admin is logged-in (when in plugin dev mode). ([1700](https://github.com/WordPress/performance/pull/1700)) + +**Bug Fixes** + +* Prevent submitting URL Metric if viewport size changed. ([1712](https://github.com/WordPress/performance/pull/1712)) + = 0.8.0 = **Enhancements** diff --git a/plugins/performance-lab/readme.txt b/plugins/performance-lab/readme.txt index 003c3cbab4..6202decfa0 100644 --- a/plugins/performance-lab/readme.txt +++ b/plugins/performance-lab/readme.txt @@ -73,6 +73,17 @@ Contributions are always welcome! Learn more about how to get involved in the [C = 3.7.0 = +**Enhancements** + +* Add guidance for managing Performance feature plugins. ([1734](https://github.com/WordPress/performance/pull/1734)) +* Automatically discover plugin dependencies when obtaining Performance feature plugins from WordPress.org. ([1680](https://github.com/WordPress/performance/pull/1680)) +* Disregard transient cache in perflab_query_plugin_info() when a plugin is absent. ([1694](https://github.com/WordPress/performance/pull/1694)) +* Minify script used for ajax activation of features; warn if absent and serve original file when SCRIPT_DEBUG is enabled. ([1658](https://github.com/WordPress/performance/pull/1658)) + +**Bug Fixes** + +* Fix latest plugin version not being downloaded consistently. ([1693](https://github.com/WordPress/performance/pull/1693)) + = 3.6.1 = **Bug Fixes** diff --git a/plugins/web-worker-offloading/readme.txt b/plugins/web-worker-offloading/readme.txt index d2eda12c89..e9a68eea7d 100644 --- a/plugins/web-worker-offloading/readme.txt +++ b/plugins/web-worker-offloading/readme.txt @@ -96,6 +96,15 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu = 0.2.0 = +**Enhancements** + +* Integrate Web Worker Offloading with Google Site Kit. ([1686](https://github.com/WordPress/performance/pull/1686)) +* Integrate Web Worker Offloading with Rank Math SEO. ([1685](https://github.com/WordPress/performance/pull/1685)) + +**Bug Fixes** + +* Fix tracking events like add_to_cart in WooCommerce integration. ([1740](https://github.com/WordPress/performance/pull/1740)) + = 0.1.1 = **Enhancements** diff --git a/plugins/webp-uploads/readme.txt b/plugins/webp-uploads/readme.txt index 8695e754d9..559b3c23a7 100644 --- a/plugins/webp-uploads/readme.txt +++ b/plugins/webp-uploads/readme.txt @@ -62,6 +62,12 @@ By default, the Modern Image Formats plugin will only generate WebP versions of = 2.4.0 = +**Enhancements** + +* Automatically opt into 1536x1536 and 2048x2048 sizes when generating fallback images. ([1679](https://github.com/WordPress/performance/pull/1679)) +* Convert WebP to AVIF on upload. ([1724](https://github.com/WordPress/performance/pull/1724)) +* Enable end user opt-in to generate all sizes in fallback format. ([1689](https://github.com/WordPress/performance/pull/1689)) + = 2.3.0 = **Enhancements** From 57aa92727b0adeda95f084ab59d40a06ca3fc63a Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 14:04:56 -0800 Subject: [PATCH 228/237] Update WWO readme with current integrations --- plugins/web-worker-offloading/readme.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/web-worker-offloading/readme.txt b/plugins/web-worker-offloading/readme.txt index e9a68eea7d..08fc2cfe20 100644 --- a/plugins/web-worker-offloading/readme.txt +++ b/plugins/web-worker-offloading/readme.txt @@ -26,10 +26,10 @@ Unlike with the script loading strategies (async/defer), any inline before/after Otherwise, the plugin currently ships with built-in integrations to offload Google Analytics to a web worker for the following plugin: +* [Rank Math SEO](https://wordpress.org/plugins/seo-by-rank-math/) +* [Site Kit by Google](https://wordpress.org/plugins/google-site-kit/) * [WooCommerce](https://wordpress.org/plugins/woocommerce/) -Support for [Site Kit by Google](https://wordpress.org/plugins/google-site-kit/) and [Rank Math SEO](https://wordpress.org/plugins/seo-by-rank-math/) are [planned](https://github.com/WordPress/performance/issues/1455). - Please monitor your analytics once activating to ensure all the expected events are being logged. At the same time, monitor your INP scores to check for improvement. This plugin relies on the [Partytown 🎉](https://partytown.builder.io/) library by Builder.io, released under the MIT license. This library is in beta and there are quite a few [open bugs](https://github.com/BuilderIO/partytown/issues?q=is%3Aopen+is%3Aissue+label%3Abug). From 011fbce53419ef523aafc4e275d5e9c4c1194de3 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 14:31:54 -0800 Subject: [PATCH 229/237] Update code references in Optimization Detective readme --- plugins/image-prioritizer/readme.txt | 4 +-- plugins/optimization-detective/readme.txt | 30 +++++++++++------------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt index 5e14d18a9f..bd52f9e1fe 100644 --- a/plugins/image-prioritizer/readme.txt +++ b/plugins/image-prioritizer/readme.txt @@ -11,7 +11,7 @@ Prioritizes the loading of images and videos based on how visible they are to ac == Description == -This plugin optimizes the loading of images (and videos) with prioritization, lazy loading, and more accurate image size selection. +This plugin optimizes the loading of images (and videos) with prioritization to improve [Largest Contentful Paint](https://web.dev/articles/lcp) (LCP), lazy loading, and more accurate image size selection. The current optimizations include: @@ -21,7 +21,7 @@ The current optimizations include: 3. An element with a CSS `background-image` inline `style` attribute. 4. An element with a CSS `background-image` applied with a stylesheet (when the image is from an allowed origin). 5. A `VIDEO` element's `poster` image. -2. Ensure `fetchpriority=high` is only added to an `IMG` when it is the Largest Contentful Paint (LCP) element across all responsive breakpoints. +2. Ensure `fetchpriority=high` is only added to an `IMG` when it is the LCP element across all responsive breakpoints. 3. Add `fetchpriority=low` to `IMG` tags which appear in the initial viewport but are not visible, such as when they are subsequent carousel slides. 4. Lazy loading: 1. Apply lazy loading to `IMG` tags based on whether they appear in any breakpoint’s initial viewport. diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index b41503248a..f76c36ddf6 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -47,25 +47,25 @@ As mentioned above, this plugin is a dependency that doesn't provide features on **[Image Prioritizer](https://wordpress.org/plugins/image-prioritizer/) ([GitHub](https://github.com/WordPress/performance/tree/trunk/plugins/image-prioritizer)):** 1. Add breakpoint-specific `fetchpriority=high` preload links (`LINK[rel=preload]`) for image URLs of LCP elements: - 1. An `IMG` element, including the `srcset`/`sizes` attributes supplied as `imagesrcset`/`imagesizes` on the `LINK`. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L167-L177), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L304-L349)) - 2. The first `SOURCE` element with a `type` attribute in a `PICTURE` element. (Art-directed `PICTURE` elements using media queries are not supported.) ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L192-L275), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L304-L349)) - 3. An element with a CSS `background-image` inline `style` attribute. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L62-L92), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L182-L203)) - 4. An element with a CSS `background-image` applied with a stylesheet (when the image is from an allowed origin). ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L82-L83), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L135-L203), [3](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/helper.php#L83-L264), [4](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/detect.js)) - 5. A `VIDEO` element's `poster` image. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php#L127-L161)) -2. Ensure `fetchpriority=high` is only added to an `IMG` when it is the Largest Contentful Paint (LCP) element across all responsive breakpoints. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L65-L91), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L137-L146)) -3. Add `fetchpriority=low` to `IMG` tags which appear in the initial viewport but are not visible, such as when they are subsequent carousel slides. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L105-L123), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L137-L146)) + 1. An `IMG` element, including the `srcset`/`sizes` attributes supplied as `imagesrcset`/`imagesizes` on the `LINK`. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L167-L177), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L304-L349)) + 2. The first `SOURCE` element with a `type` attribute in a `PICTURE` element. (Art-directed `PICTURE` elements using media queries are not supported.) ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L192-L275), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L304-L349)) + 3. An element with a CSS `background-image` inline `style` attribute. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L62-L92), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L182-L203)) + 4. An element with a CSS `background-image` applied with a stylesheet (when the image is from an allowed origin). ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/hooks.php#L14-L16), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L82-L83), [3](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L135-L203), [4](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/helper.php#L83-L320), [5](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/detect.js)) + 5. A `VIDEO` element's `poster` image. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php#L127-L161)) +2. Ensure `fetchpriority=high` is only added to an `IMG` when it is the LCP element across all responsive breakpoints. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L65-L91), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L137-L146)) +3. Add `fetchpriority=low` to `IMG` tags which appear in the initial viewport but are not visible, such as when they are subsequent carousel slides. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L105-L123), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L137-L146)) 4. Lazy loading: - 1. Apply lazy loading to `IMG` tags based on whether they appear in any breakpoint’s initial viewport. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L124-L133)) - 2. Implement lazy loading of CSS background images added via inline `style` attributes. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L205-L238), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/helper.php#L369-L382), [3](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/lazy-load-bg-image.js)) - 3. Lazy-load `VIDEO` tags by setting the appropriate attributes based on whether they appear in the initial viewport. If a `VIDEO` is the LCP element, it gets `preload=auto`; if it is in an initial viewport, the `preload=metadata` default is left; if it is not in an initial viewport, it gets `preload=none`. Lazy-loaded videos also get initial `preload`, `autoplay`, and `poster` attributes restored when the `VIDEO` is going to enter the viewport. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php#L163-L246), [2](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/helper.php#L352-L367), [3](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/lazy-load-video.js)) -5. Ensure that [`sizes=auto`](https://make.wordpress.org/core/2024/10/18/auto-sizes-for-lazy-loaded-images-in-wordpress-6-7/) is added to all lazy-loaded `IMG` elements. ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L148-L163)) -6. Reduce the size of the `poster` image of a `VIDEO` from full size to the size appropriate for the maximum width of the video (on desktop). ([1](https://github.com/WordPress/performance/blob/e1d0ac9dd935634b782d711c7e1ae85d296f44cf/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php#L84-L125)) + 1. Apply lazy loading to `IMG` tags based on whether they appear in any breakpoint’s initial viewport. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L124-L133)) + 2. Implement lazy loading of CSS background images added via inline `style` attributes. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-background-image-styled-tag-visitor.php#L205-L238), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/helper.php#L365-L380), [3](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/lazy-load-bg-image.js)) + 3. Lazy-load `VIDEO` tags by setting the appropriate attributes based on whether they appear in the initial viewport. If a `VIDEO` is the LCP element, it gets `preload=auto`; if it is in an initial viewport, the `preload=metadata` default is left; if it is not in an initial viewport, it gets `preload=none`. Lazy-loaded videos also get initial `preload`, `autoplay`, and `poster` attributes restored when the `VIDEO` is going to enter the viewport. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php#L163-L246), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/helper.php#L365-L380), [3](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/lazy-load-video.js)) +5. Ensure that [`sizes=auto`](https://make.wordpress.org/core/2024/10/18/auto-sizes-for-lazy-loaded-images-in-wordpress-6-7/) is added to all lazy-loaded `IMG` elements. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-img-tag-visitor.php#L148-L163)) +6. Reduce the size of the `poster` image of a `VIDEO` from full size to the size appropriate for the maximum width of the video (on desktop). ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/image-prioritizer/class-image-prioritizer-video-tag-visitor.php#L84-L125)) **[Embed Optimizer](https://wordpress.org/plugins/embed-optimizer/) ([GitHub](https://github.com/WordPress/performance/tree/trunk/plugins/embed-optimizer)):** -1. Lazy loading embeds just before they come into view. ([1](https://github.com/WordPress/performance/blob/ce76a6a77c15248126b5dab895bc11d0adda0baa/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php#L191-L194), [2](https://github.com/WordPress/performance/blob/ce76a6a77c15248126b5dab895bc11d0adda0baa/plugins/embed-optimizer/hooks.php#L168-L336)) -2. Adding preconnect links for embeds in the initial viewport. ([1](https://github.com/WordPress/performance/blob/ce76a6a77c15248126b5dab895bc11d0adda0baa/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php#L114-L190)) -3. Reserving space for embeds that resize to reduce layout shifting. ([1](https://github.com/WordPress/performance/blob/ce76a6a77c15248126b5dab895bc11d0adda0baa/plugins/embed-optimizer/hooks.php#L81-L144), [2](https://github.com/WordPress/performance/blob/ce76a6a77c15248126b5dab895bc11d0adda0baa/plugins/embed-optimizer/detect.js), [3](https://github.com/WordPress/performance/blob/ce76a6a77c15248126b5dab895bc11d0adda0baa/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php#L218-L285)) +1. Lazy loading embeds just before they come into view. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php#L191-L194), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/embed-optimizer/hooks.php#L168-L336)) +2. Adding preconnect links for embeds in the initial viewport. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php#L114-L190)) +3. Reserving space for embeds that resize to reduce layout shifting. ([1](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/embed-optimizer/hooks.php#L64-L65), [2](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/embed-optimizer/hooks.php#L81-L144), [3](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/embed-optimizer/detect.js), [4](https://github.com/WordPress/performance/blob/f5f50f9179c26deadeef966734367d199ba6de6f/plugins/embed-optimizer/class-embed-optimizer-tag-visitor.php#L218-L285)) = Hooks = From 4c13b74aaf5e6d2b6f36ac656b77f4e1d6e157e1 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 14:53:45 -0800 Subject: [PATCH 230/237] Amend changelogs --- plugins/embed-optimizer/readme.txt | 2 +- plugins/optimization-detective/readme.txt | 5 +++++ plugins/performance-lab/readme.txt | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/embed-optimizer/readme.txt b/plugins/embed-optimizer/readme.txt index 0ee1fa1d70..0aa3b3453d 100644 --- a/plugins/embed-optimizer/readme.txt +++ b/plugins/embed-optimizer/readme.txt @@ -71,7 +71,7 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu **Enhancements** -* Incorporate breakpoints into preconnect links added by Embed Optimizer. ([1654](https://github.com/WordPress/performance/pull/1654)) +* Incorporate media queries into preconnect links to account for whether embeds are in viewport. ([1654](https://github.com/WordPress/performance/pull/1654)) = 0.3.0 = diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index f76c36ddf6..62dac5293a 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -328,10 +328,15 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu * Incorporate page state into ETag computation. ([1722](https://github.com/WordPress/performance/pull/1722)) * Mark existing URL Metrics as stale when a new tag visitor is registered. ([1705](https://github.com/WordPress/performance/pull/1705)) * Set development mode to 'plugin' in the dev environment and allow pages to be optimized when admin is logged-in (when in plugin dev mode). ([1700](https://github.com/WordPress/performance/pull/1700)) +* Add `get_xpath_elements_map()` helper methods to `OD_URL_Metric_Group_Collection` and `OD_URL_Metric_Group`, and add `get_all_element_max_intersection_ratios`/`get_element_max_intersection_ratio` methods to `OD_URL_Metric_Group`. ([1654](https://github.com/WordPress/performance/pull/1654)) +* Add `get_breadcrumbs()` method to `OD_HTML_Tag_Processor`. ([1707](https://github.com/WordPress/performance/pull/1707)) +* Add `get_sample_size()` and `get_freshness_ttl()` methods to `OD_URL_Metric_Group`. ([1697](https://github.com/WordPress/performance/pull/1697)) +* Expose `onTTFB`, `onFCP`, `onLCP`, `onINP`, and `onCLS` from web-vitals.js to extension JS modules via args their `initialize` functions. ([1697](https://github.com/WordPress/performance/pull/1697)) **Bug Fixes** * Prevent submitting URL Metric if viewport size changed. ([1712](https://github.com/WordPress/performance/pull/1712)) +* Fix construction of XPath expressions for implicitly closed paragraphs. ([1707](https://github.com/WordPress/performance/pull/1707)) = 0.8.0 = diff --git a/plugins/performance-lab/readme.txt b/plugins/performance-lab/readme.txt index 6202decfa0..98276fc6e6 100644 --- a/plugins/performance-lab/readme.txt +++ b/plugins/performance-lab/readme.txt @@ -77,7 +77,7 @@ Contributions are always welcome! Learn more about how to get involved in the [C * Add guidance for managing Performance feature plugins. ([1734](https://github.com/WordPress/performance/pull/1734)) * Automatically discover plugin dependencies when obtaining Performance feature plugins from WordPress.org. ([1680](https://github.com/WordPress/performance/pull/1680)) -* Disregard transient cache in perflab_query_plugin_info() when a plugin is absent. ([1694](https://github.com/WordPress/performance/pull/1694)) +* Disregard transient cache in `perflab_query_plugin_info()` when a plugin is absent. ([1694](https://github.com/WordPress/performance/pull/1694)) * Minify script used for ajax activation of features; warn if absent and serve original file when SCRIPT_DEBUG is enabled. ([1658](https://github.com/WordPress/performance/pull/1658)) **Bug Fixes** From c4cc9e8a00ceaffc6c5ccfbadc21d38ff23eb5fe Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 15:22:39 -0800 Subject: [PATCH 231/237] Add missing changelog entries for unminified scripts when WP_DEBUG is enabled --- plugins/embed-optimizer/readme.txt | 1 + plugins/image-prioritizer/readme.txt | 1 + plugins/web-worker-offloading/readme.txt | 1 + 3 files changed, 3 insertions(+) diff --git a/plugins/embed-optimizer/readme.txt b/plugins/embed-optimizer/readme.txt index 0aa3b3453d..65c30a2a15 100644 --- a/plugins/embed-optimizer/readme.txt +++ b/plugins/embed-optimizer/readme.txt @@ -72,6 +72,7 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu **Enhancements** * Incorporate media queries into preconnect links to account for whether embeds are in viewport. ([1654](https://github.com/WordPress/performance/pull/1654)) +* Serve unminified scripts when `SCRIPT_DEBUG` is enabled. ([1643](https://github.com/WordPress/performance/pull/1643)) = 0.3.0 = diff --git a/plugins/image-prioritizer/readme.txt b/plugins/image-prioritizer/readme.txt index bd52f9e1fe..36a942c819 100644 --- a/plugins/image-prioritizer/readme.txt +++ b/plugins/image-prioritizer/readme.txt @@ -78,6 +78,7 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu * Harden validation of user-submitted LCP background image URL. ([1713](https://github.com/WordPress/performance/pull/1713)) * Lazy load background images added via inline style attributes. ([1708](https://github.com/WordPress/performance/pull/1708)) * Preload image URLs for LCP elements with external background images. ([1697](https://github.com/WordPress/performance/pull/1697)) +* Serve unminified scripts when `SCRIPT_DEBUG` is enabled. ([1643](https://github.com/WordPress/performance/pull/1643)) = 0.2.0 = diff --git a/plugins/web-worker-offloading/readme.txt b/plugins/web-worker-offloading/readme.txt index 08fc2cfe20..e2d6623c09 100644 --- a/plugins/web-worker-offloading/readme.txt +++ b/plugins/web-worker-offloading/readme.txt @@ -100,6 +100,7 @@ The [plugin source code](https://github.com/WordPress/performance/tree/trunk/plu * Integrate Web Worker Offloading with Google Site Kit. ([1686](https://github.com/WordPress/performance/pull/1686)) * Integrate Web Worker Offloading with Rank Math SEO. ([1685](https://github.com/WordPress/performance/pull/1685)) +* Serve unminified scripts when `SCRIPT_DEBUG` is enabled. ([1643](https://github.com/WordPress/performance/pull/1643)) **Bug Fixes** From c568871226ee029487b87c7bd0b80134e9305d2f Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Tue, 17 Dec 2024 22:30:36 -0800 Subject: [PATCH 232/237] Account for $meta arg in wp_prepare_attachment_for_js not being an array --- plugins/dominant-color-images/hooks.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/dominant-color-images/hooks.php b/plugins/dominant-color-images/hooks.php index a841a6f1f0..c60f1d7a49 100644 --- a/plugins/dominant-color-images/hooks.php +++ b/plugins/dominant-color-images/hooks.php @@ -251,15 +251,18 @@ function dominant_color_admin_script(): void { * * @since n.e.x.t * - * @param array|mixed $response The current response array for the attachment. - * @param WP_Post $attachment The attachment post object. - * @param array $meta The attachment metadata. - * @return array The modified response array with added dominant color and transparency information. + * @param array|mixed $response The current response array for the attachment. + * @param WP_Post $attachment The attachment post object. + * @param array|false $meta The attachment metadata. + * @return array The modified response array with added dominant color and transparency information. */ -function dominant_color_prepare_attachment_for_js( $response, WP_Post $attachment, array $meta ): array { +function dominant_color_prepare_attachment_for_js( $response, WP_Post $attachment, $meta ): array { if ( ! is_array( $response ) ) { $response = array(); } + if ( ! is_array( $meta ) ) { + return $response; + } $response['dominantColor'] = ''; if ( From 2033d448b25012ce9134b6fc329e851ca852e1fc Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 18 Dec 2024 16:27:38 -0800 Subject: [PATCH 233/237] Remove erroneous check for resource initiatorType when considering whether to submit external background image --- plugins/image-prioritizer/detect.js | 36 ----------------------------- 1 file changed, 36 deletions(-) diff --git a/plugins/image-prioritizer/detect.js b/plugins/image-prioritizer/detect.js index 54fe64d09b..4f5f5b158c 100644 --- a/plugins/image-prioritizer/detect.js +++ b/plugins/image-prioritizer/detect.js @@ -77,27 +77,6 @@ export async function initialize( { isDebug, onLCP } ) { ); } -/** - * Gets the performance resource entry for a given URL. - * - * @since 0.3.0 - * - * @param {string} url - Resource URL. - * @return {PerformanceResourceTiming|null} Resource entry or null. - */ -function getPerformanceResourceByURL( url ) { - const entries = - /** @type PerformanceResourceTiming[] */ performance.getEntriesByType( - 'resource' - ); - for ( const entry of entries ) { - if ( entry.name === url ) { - return entry; - } - } - return null; -} - /** * Handles a new LCP metric being reported. * @@ -129,21 +108,6 @@ function handleLCPMetric( metric, isDebug ) { continue; } - // Now only consider proceeding with the URL if its loading was initiated with stylesheet or preload link. - const resourceEntry = getPerformanceResourceByURL( entry.url ); - if ( - ! resourceEntry || - ! [ 'css', 'link' ].includes( resourceEntry.initiatorType ) - ) { - if ( isDebug ) { - warn( - `Skipped considering URL (${ entry.url }) due to unexpected performance resource timing entry:`, - resourceEntry - ); - } - return; - } - // Skip URLs that are excessively long. This is the maxLength defined in image_prioritizer_add_element_item_schema_properties(). if ( entry.url.length > 500 ) { if ( isDebug ) { From eee6bf2b53dc0f05d8981f30b03d02fbccec8ae3 Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Wed, 18 Dec 2024 17:14:19 -0800 Subject: [PATCH 234/237] Remove unused warn() --- plugins/image-prioritizer/detect.js | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/plugins/image-prioritizer/detect.js b/plugins/image-prioritizer/detect.js index 4f5f5b158c..63c1c27705 100644 --- a/plugins/image-prioritizer/detect.js +++ b/plugins/image-prioritizer/detect.js @@ -43,18 +43,6 @@ function log( ...message ) { console.log( consoleLogPrefix, ...message ); } -/** - * Logs a warning. - * - * @since 0.3.0 - * - * @param {...*} message - */ -function warn( ...message ) { - // eslint-disable-next-line no-console - console.warn( consoleLogPrefix, ...message ); -} - /** * Initializes extension. * From fe7e81eb7cee86d61366e790ce148f9a62225d28 Mon Sep 17 00:00:00 2001 From: SohamPatel46 Date: Thu, 19 Dec 2024 16:33:38 +0530 Subject: [PATCH 235/237] Update all instance of plugin_dir_url() to plugins_url() --- plugins/embed-optimizer/hooks.php | 2 +- plugins/image-prioritizer/helper.php | 2 +- plugins/optimization-detective/detection.php | 4 ++-- plugins/optimization-detective/readme.txt | 2 +- plugins/performance-lab/includes/admin/load.php | 2 +- plugins/web-worker-offloading/helper.php | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/embed-optimizer/hooks.php b/plugins/embed-optimizer/hooks.php index 988a5a2dfd..33e8aff954 100644 --- a/plugins/embed-optimizer/hooks.php +++ b/plugins/embed-optimizer/hooks.php @@ -121,7 +121,7 @@ function embed_optimizer_filter_extension_module_urls( $extension_module_urls ): if ( ! is_array( $extension_module_urls ) ) { $extension_module_urls = array(); } - $extension_module_urls[] = add_query_arg( 'ver', EMBED_OPTIMIZER_VERSION, plugin_dir_url( __FILE__ ) . embed_optimizer_get_asset_path( 'detect.js' ) ); + $extension_module_urls[] = plugins_url( add_query_arg( 'ver', EMBED_OPTIMIZER_VERSION, embed_optimizer_get_asset_path( 'detect.js' ) ), __FILE__ ); return $extension_module_urls; } diff --git a/plugins/image-prioritizer/helper.php b/plugins/image-prioritizer/helper.php index 75c3e18d43..e333529790 100644 --- a/plugins/image-prioritizer/helper.php +++ b/plugins/image-prioritizer/helper.php @@ -93,7 +93,7 @@ function image_prioritizer_filter_extension_module_urls( $extension_module_urls if ( ! is_array( $extension_module_urls ) ) { $extension_module_urls = array(); } - $extension_module_urls[] = add_query_arg( 'ver', IMAGE_PRIORITIZER_VERSION, plugin_dir_url( __FILE__ ) . image_prioritizer_get_asset_path( 'detect.js' ) ); + $extension_module_urls[] = plugins_url( add_query_arg( 'ver', IMAGE_PRIORITIZER_VERSION, image_prioritizer_get_asset_path( 'detect.js' ) ), __FILE__ ); return $extension_module_urls; } diff --git a/plugins/optimization-detective/detection.php b/plugins/optimization-detective/detection.php index 2fa2a6dee7..116249704d 100644 --- a/plugins/optimization-detective/detection.php +++ b/plugins/optimization-detective/detection.php @@ -71,7 +71,7 @@ function od_get_cache_purge_post_id(): ?int { */ function od_get_detection_script( string $slug, OD_URL_Metric_Group_Collection $group_collection ): string { $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 = plugins_url( add_query_arg( 'ver', $web_vitals_lib_data['version'], 'build/web-vitals.js' ), __FILE__ ); /** * Filters the list of extension script module URLs to import when performing detection. @@ -118,7 +118,7 @@ static function ( OD_URL_Metric_Group $group ): array { return wp_get_inline_script_tag( sprintf( 'import detect from %s; detect( %s );', - wp_json_encode( add_query_arg( 'ver', OPTIMIZATION_DETECTIVE_VERSION, plugin_dir_url( __FILE__ ) . od_get_asset_path( 'detect.js' ) ) ), + wp_json_encode( plugins_url( add_query_arg( 'ver', OPTIMIZATION_DETECTIVE_VERSION, od_get_asset_path( 'detect.js' ) ), __FILE__ ) ), wp_json_encode( $detect_args ) ), array( 'type' => 'module' ) diff --git a/plugins/optimization-detective/readme.txt b/plugins/optimization-detective/readme.txt index 1e783ef823..6b21e03fdc 100644 --- a/plugins/optimization-detective/readme.txt +++ b/plugins/optimization-detective/readme.txt @@ -257,7 +257,7 @@ For example: add_filter( 'od_extension_module_urls', static function ( array $extension_module_urls ): array { - $extension_module_urls[] = add_query_arg( 'ver', '1.0', plugin_dir_url( __FILE__ ) . 'detect.js' ); + $extension_module_urls[] = plugins_url( add_query_arg( 'ver', '1.0', 'detect.js' ), __FILE__ ); return $extension_module_urls; } ); diff --git a/plugins/performance-lab/includes/admin/load.php b/plugins/performance-lab/includes/admin/load.php index 8416f3e74a..634c53795a 100644 --- a/plugins/performance-lab/includes/admin/load.php +++ b/plugins/performance-lab/includes/admin/load.php @@ -264,7 +264,7 @@ function perflab_enqueue_features_page_scripts(): void { // Enqueue plugin activate AJAX script and localize script data. wp_enqueue_script( 'perflab-plugin-activate-ajax', - plugin_dir_url( PERFLAB_MAIN_FILE ) . perflab_get_asset_path( 'includes/admin/plugin-activate-ajax.js' ), + plugins_url( perflab_get_asset_path( 'includes/admin/plugin-activate-ajax.js' ), PERFLAB_MAIN_FILE ), array( 'wp-i18n', 'wp-a11y', 'wp-api-fetch' ), PERFLAB_VERSION, true diff --git a/plugins/web-worker-offloading/helper.php b/plugins/web-worker-offloading/helper.php index a3b15219a0..e9cc956d1c 100644 --- a/plugins/web-worker-offloading/helper.php +++ b/plugins/web-worker-offloading/helper.php @@ -23,7 +23,7 @@ function plwwo_get_configuration(): array { $config = array( // The source code in the build directory is compiled from . // See webpack config in the WordPress/performance repo: . - 'lib' => wp_parse_url( plugin_dir_url( __FILE__ ), PHP_URL_PATH ) . 'build/', + 'lib' => wp_parse_url( plugins_url( 'build/', __FILE__ ), PHP_URL_PATH ), ); if ( WP_DEBUG && SCRIPT_DEBUG ) { From 739bc1576db699a76f0713e2780ecbf710242a3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2024 23:36:15 +0000 Subject: [PATCH 236/237] Bump lint-staged from 15.2.11 to 15.3.0 Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 15.2.11 to 15.3.0. - [Release notes](https://github.com/lint-staged/lint-staged/releases) - [Changelog](https://github.com/lint-staged/lint-staged/blob/master/CHANGELOG.md) - [Commits](https://github.com/lint-staged/lint-staged/compare/v15.2.11...v15.3.0) --- updated-dependencies: - dependency-name: lint-staged dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 19be816bc8..6a0aa7a909 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,7 @@ "fast-glob": "^3.3.2", "fs-extra": "^11.2.0", "husky": "^9.1.7", - "lint-staged": "^15.2.11", + "lint-staged": "^15.3.0", "lodash": "4.17.21", "micromatch": "^4.0.8", "npm-run-all": "^4.1.5", @@ -14144,12 +14144,12 @@ } }, "node_modules/lint-staged": { - "version": "15.2.11", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.11.tgz", - "integrity": "sha512-Ev6ivCTYRTGs9ychvpVw35m/bcNDuBN+mnTeObCL5h+boS5WzBEC6LHI4I9F/++sZm1m+J2LEiy0gxL/R9TBqQ==", + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.3.0.tgz", + "integrity": "sha512-vHFahytLoF2enJklgtOtCtIjZrKD/LoxlaUusd5nh7dWv/dkKQJY74ndFSzxCdv7g0ueGg1ORgTSt4Y9LPZn9A==", "dev": true, "dependencies": { - "chalk": "~5.3.0", + "chalk": "~5.4.1", "commander": "~12.1.0", "debug": "~4.4.0", "execa": "~8.0.1", @@ -14171,9 +14171,9 @@ } }, "node_modules/lint-staged/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" diff --git a/package.json b/package.json index 624ceae946..fab4088dae 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "fast-glob": "^3.3.2", "fs-extra": "^11.2.0", "husky": "^9.1.7", - "lint-staged": "^15.2.11", + "lint-staged": "^15.3.0", "lodash": "4.17.21", "micromatch": "^4.0.8", "npm-run-all": "^4.1.5", From 4ebdb42e8d4882f23adee9b0820f23c2503904bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Jan 2025 03:38:32 +0000 Subject: [PATCH 237/237] Bump commander from 12.1.0 to 13.0.0 Bumps [commander](https://github.com/tj/commander.js) from 12.1.0 to 13.0.0. - [Release notes](https://github.com/tj/commander.js/releases) - [Changelog](https://github.com/tj/commander.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/tj/commander.js/compare/v12.1.0...v13.0.0) --- updated-dependencies: - dependency-name: commander dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- package-lock.json | 17 +++++++++++++---- package.json | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6a0aa7a909..558323f571 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@wordpress/env": "^10.14.0", "@wordpress/prettier-config": "^4.14.0", "@wordpress/scripts": "^30.7.0", - "commander": "12.1.0", + "commander": "13.0.0", "copy-webpack-plugin": "^12.0.2", "css-minimizer-webpack-plugin": "^7.0.0", "fast-glob": "^3.3.2", @@ -7279,9 +7279,9 @@ } }, "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.0.0.tgz", + "integrity": "sha512-oPYleIY8wmTVzkvQq10AEok6YcTC4sRUBl8F9gVuwchGVUCTbl/vhLTaQqutuuySYOsu8YTgV+OxKc/8Yvx+mQ==", "dev": true, "engines": { "node": ">=18" @@ -14182,6 +14182,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/lint-staged/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/lint-staged/node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", diff --git a/package.json b/package.json index fab4088dae..f8ec87cd84 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@wordpress/env": "^10.14.0", "@wordpress/prettier-config": "^4.14.0", "@wordpress/scripts": "^30.7.0", - "commander": "12.1.0", + "commander": "13.0.0", "copy-webpack-plugin": "^12.0.2", "css-minimizer-webpack-plugin": "^7.0.0", "fast-glob": "^3.3.2",