From 34a725dcf4726675ae153d6d574b3292b6a1a69d Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Tue, 20 Jun 2023 16:01:12 -0500 Subject: [PATCH 1/7] Update unit tests for removal of inline script support --- tests/phpunit/tests/dependencies/scripts.php | 386 +++---------------- 1 file changed, 63 insertions(+), 323 deletions(-) diff --git a/tests/phpunit/tests/dependencies/scripts.php b/tests/phpunit/tests/dependencies/scripts.php index beeb812200cd6..2e47446ecea7c 100644 --- a/tests/phpunit/tests/dependencies/scripts.php +++ b/tests/phpunit/tests/dependencies/scripts.php @@ -95,227 +95,6 @@ public function data_provider_delayed_strategies() { ); } - /** - * Data provider for test_print_delayed_inline_script_loader_timing. - * - * @return array[] - */ - public function data_to_test_print_delayed_inline_script_loader_timing() { - /** - * Enqueue script with defer strategy. - * - * @param string $handle Handle. - * @param bool $in_footer In footer. - */ - $enqueue_script = static function ( $handle, $in_footer = false, $deps = array() ) { - wp_enqueue_script( - $handle, - sprintf( 'https://example.com/%s.js', $handle ), - $deps, - null, - array( - 'in_footer' => $in_footer, - 'strategy' => 'defer', - ) - ); - }; - - /** - * Add inline after script. - * - * @param string $handle Handle. - * @param string $position Position. - */ - $add_inline_script = static function ( $handle, $position = 'after' ) { - wp_add_inline_script( - $handle, - "/*{$handle}-{$position}*/", - $position - ); - }; - - return array( - 'no_delayed_inline_scripts' => array( - 'set_up' => static function () use ( $enqueue_script ) { - $enqueue_script( 'foo-head', false ); - $enqueue_script( 'foo-footer', true ); - }, - 'expected_head' => << -HTML - , - 'expected_torso' => '', - 'expected_footer' => << -HTML - , - ), - 'delayed_inline_script_in_head_only' => array( - 'set_up' => static function () use ( $enqueue_script, $add_inline_script ) { - $enqueue_script( 'foo-head', false ); - $add_inline_script( 'foo-head' ); - }, - 'expected_head' => $this->get_delayed_inline_script_loader_script_tag() . << - -HTML - , - 'expected_torso' => '', - 'expected_footer' => '', - ), - 'delayed_inline_script_in_footer_only' => array( - 'set_up' => static function () use ( $enqueue_script, $add_inline_script ) { - $enqueue_script( 'foo-footer', true ); - $add_inline_script( 'foo-footer' ); - }, - /* - * TODO: This script is getting output even though it isn't needed yet. It could be printed in the footer instead. - * In order to handle this case, the logic to determine whether the script needs to be printed would have to - * take into account the current group being printed. If we're printing the head scripts, but none of the - * head scripts are delayed, then the script wouldn't have to be printed. - */ - 'expected_head' => $this->get_delayed_inline_script_loader_script_tag(), - 'expected_torso' => '', - 'expected_footer' => << - -HTML - , - ), - 'delayed_inline_script_in_both_head_and_footer' => array( - 'set_up' => static function () use ( $enqueue_script, $add_inline_script ) { - foreach ( array( false, true ) as $in_footer ) { - $handle = $in_footer ? 'foo-footer' : 'foo-head'; - $enqueue_script( $handle, $in_footer ); - $add_inline_script( $handle ); - } - }, - 'expected_head' => $this->get_delayed_inline_script_loader_script_tag() . << - -HTML - , - 'expected_torso' => '', - 'expected_footer' => << - -HTML - , - ), - 'delayed_inline_script_enqueued_in_torso_for_footer' => array( - 'set_up' => static function () use ( $enqueue_script, $add_inline_script ) { - add_action( - 'test_torso', - static function () use ( $enqueue_script, $add_inline_script ) { - $enqueue_script( 'foo-footer', true ); - $add_inline_script( 'foo-footer' ); - } - ); - }, - 'expected_head' => '', - 'expected_torso' => '', - 'expected_footer' => $this->get_delayed_inline_script_loader_script_tag() . << - -HTML - , - ), - 'delayed_inline_printed_in_torso' => array( - 'set_up' => static function () use ( $enqueue_script, $add_inline_script ) { - add_action( - 'test_torso', - static function () use ( $enqueue_script, $add_inline_script ) { - wp_register_script( 'foo-torso', 'https://example.com/foo-torso.js', array(), null, array( 'strategy' => 'defer' ) ); - $add_inline_script( 'foo-torso' ); - wp_print_scripts( array( 'foo-torso' ) ); - } - ); - }, - 'expected_head' => '', - 'expected_torso' => $this->get_delayed_inline_script_loader_script_tag() . << - -HTML - , - 'expected_footer' => '', - ), - 'delayed_script_registered_but_not_enqueued' => array( - 'set_up' => static function () use ( $enqueue_script, $add_inline_script ) { - wp_register_script( 'not-enqueued', 'https://example.com/not-enqueued.js', array(), null, array( 'strategy' => 'defer' ) ); - $add_inline_script( 'not-enqueued', 'after' ); - - wp_enqueue_script( 'enqueued', 'https://example.com/enqueued.js', array(), null ); - $add_inline_script( 'enqueued', 'after' ); - }, - 'expected_head' => << - -HTML - , - 'expected_torso' => '', - 'expected_footer' => '', - ), - ); - } - - /** - * Tests that wp_print_delayed_inline_script_loader() is output before the first delayed inline script and not - * duplicated in header and footer. - * - * @covers WP_Scripts::print_delayed_inline_script_loader - * - * @dataProvider data_to_test_print_delayed_inline_script_loader_timing - * @param callable $set_up Set up. - * @param string $expected_head Expected head. - * @param string $expected_torso Expected torso. - * @param string $expected_footer Expected footer. - */ - public function test_print_delayed_inline_script_loader_timing( $set_up, $expected_head, $expected_torso, $expected_footer ) { - $set_up(); - - // Note that test_head, test_enqueue_scripts, and test_footer are used instead of their wp_* actions to avoid triggering core actions. - add_action( - 'test_head', - static function () { - do_action( 'test_enqueue_scripts' ); - }, - 1 // Priority corresponds to wp_head in default-filters.php. - ); - add_action( 'test_head', 'wp_print_head_scripts', 9 ); // Priority corresponds to wp_head in default-filters.php. - add_action( 'test_footer', 'wp_print_footer_scripts', 20 ); // Priority corresponds to wp_footer in default-filters.php. - - $actual_head = get_echo( 'do_action', array( 'test_head' ) ); - $actual_torso = get_echo( 'do_action', array( 'test_torso' ) ); - $actual_footer = get_echo( 'do_action', array( 'test_footer' ) ); - - $delayed_script_count = substr_count( $actual_head . $actual_torso . $actual_footer, $this->get_delayed_inline_script_loader_script_tag() ); - if ( wp_scripts()->has_delayed_inline_script( wp_scripts()->done ) ) { - $this->assertSame( 1, $delayed_script_count, 'Expected delayed inline script to occur exactly once.' ); - } else { - $this->assertSame( 0, $delayed_script_count, 'Expected delayed inline script to not occur since no delayed inline scripts.' ); - } - - $this->assertLessThanOrEqual( 1, $delayed_script_count, 'Expected delayed-inline-script-loader to occur at most once.' ); - - $this->assertEqualMarkup( $actual_head, $expected_head, 'Expected head to match.' ); - $this->assertEqualMarkup( $actual_torso, $expected_torso, 'Expected torso to match.' ); - $this->assertEqualMarkup( $actual_footer, $expected_footer, 'Expected footer to match.' ); - } - /** * Tests that inline scripts in the `after` position, attached to delayed main scripts, remain unaffected. * @@ -326,7 +105,6 @@ static function () { * * @covers WP_Scripts::do_item * @covers WP_Scripts::get_inline_script_tag - * @covers WP_Scripts::print_delayed_inline_script_loader * @covers ::wp_add_inline_script * @covers ::wp_enqueue_script * @@ -338,13 +116,11 @@ public function test_after_inline_script_with_delayed_main_script( $strategy ) { wp_enqueue_script( 'ms-isa-1', 'http://example.org/ms-isa-1.js', array(), null, compact( 'strategy' ) ); wp_add_inline_script( 'ms-isa-1', 'console.log("after one");', 'after' ); $output = get_echo( 'wp_print_scripts' ); - $expected = $this->get_delayed_inline_script_loader_script_tag(); - $expected .= "\n"; + $expected = "\n"; $expected .= wp_get_inline_script_tag( "console.log(\"after one\");\n", array( - 'id' => 'ms-isa-1-js-after', - 'type' => 'text/plain', + 'id' => 'ms-isa-1-js-after', ) ); $this->assertSame( $expected, $output, 'Inline scripts in the "after" position, that are attached to a deferred main script, are failing to print/execute.' ); @@ -403,22 +179,20 @@ public function test_before_inline_scripts_with_delayed_main_script( $strategy ) wp_add_inline_script( 'ms-i1-1', 'console.log("before last");', 'before' ); $output = get_echo( 'wp_print_scripts' ); - $expected = $this->get_delayed_inline_script_loader_script_tag(); - $expected .= wp_get_inline_script_tag( + $expected = wp_get_inline_script_tag( "console.log(\"before first\");\n", array( 'id' => 'ds-i1-1-js-before', ) ); - $expected .= "\n"; - $expected .= "\n"; - $expected .= "\n"; + $expected .= "\n"; + $expected .= "\n"; + $expected .= "\n"; $expected .= wp_get_inline_script_tag( "console.log(\"before last\");\n", array( 'id' => 'ms-i1-1-js-before', - 'type' => 'text/plain', - 'data-wp-deps' => 'ds-i1-1,ds-i1-2,ds-i1-3', + 'type' => 'text/javascript', ) ); $expected .= "\n"; @@ -697,7 +471,7 @@ public function data_provider_to_test_various_strategy_dependency_chains() { $this->add_test_inline_script( $handle, 'after' ); } }, - 'expected_markup' => $this->get_delayed_inline_script_loader_script_tag() . << << scriptEventLog.push( "blocking-not-async-without-dependency: before inline" ) @@ -708,8 +482,8 @@ public function data_provider_to_test_various_strategy_dependency_chains() { - - + HTML @@ -734,26 +508,26 @@ public function data_provider_to_test_various_strategy_dependency_chains() { $this->add_test_inline_script( $handle, 'after' ); } }, - 'expected_markup' => $this->get_delayed_inline_script_loader_script_tag() . << << scriptEventLog.push( "async-no-dependency: before inline" ) - - + - - - + - - - + HTML @@ -799,19 +573,19 @@ public function data_provider_to_test_various_strategy_dependency_chains() { $this->add_test_inline_script( $handle, 'after' ); } }, - 'expected_markup' => $this->get_delayed_inline_script_loader_script_tag() . << << scriptEventLog.push( "async-with-defer-dependent: before inline" ) - - + - - - + HTML @@ -831,7 +605,7 @@ public function data_provider_to_test_various_strategy_dependency_chains() { $this->add_test_inline_script( $handle2, 'before' ); $this->add_test_inline_script( $handle2, 'after' ); }, - 'expected_markup' => $this->get_delayed_inline_script_loader_script_tag() . << << scriptEventLog.push( "blocking-bundle-of-none: before inline" ) @@ -841,8 +615,8 @@ public function data_provider_to_test_various_strategy_dependency_chains() { - - + HTML @@ -865,7 +639,7 @@ public function data_provider_to_test_various_strategy_dependency_chains() { $this->add_test_inline_script( $handle, 'after' ); } }, - 'expected_markup' => $this->get_delayed_inline_script_loader_script_tag() . << << scriptEventLog.push( "blocking-bundle-member-one: before inline" ) @@ -883,8 +657,8 @@ public function data_provider_to_test_various_strategy_dependency_chains() { - - + HTML @@ -906,7 +680,7 @@ public function data_provider_to_test_various_strategy_dependency_chains() { $this->add_test_inline_script( $handle2, 'before' ); $this->add_test_inline_script( $handle2, 'after' ); }, - 'expected_markup' => $this->get_delayed_inline_script_loader_script_tag() . << << scriptEventLog.push( "defer-bundle-of-none: before inline" ) @@ -916,8 +690,8 @@ public function data_provider_to_test_various_strategy_dependency_chains() { - - + HTML @@ -937,7 +711,7 @@ public function data_provider_to_test_various_strategy_dependency_chains() { $this->add_test_inline_script( $dep, 'after' ); } }, - 'expected_markup' => $this->get_delayed_inline_script_loader_script_tag() . << << scriptEventLog.push( "blocking-dependency-with-defer-following-dependency: before inline" ) @@ -948,15 +722,15 @@ public function data_provider_to_test_various_strategy_dependency_chains() { - - + - - - + HTML @@ -976,12 +750,12 @@ public function data_provider_to_test_various_strategy_dependency_chains() { $this->add_test_inline_script( $dep, 'after' ); } }, - 'expected_markup' => $this->get_delayed_inline_script_loader_script_tag() . << << scriptEventLog.push( "defer-dependency-with-blocking-following-dependency: before inline" ) - - + - - - + HTML @@ -1012,19 +786,19 @@ public function data_provider_to_test_various_strategy_dependency_chains() { $this->add_test_inline_script( $handle, 'after' ); } }, - 'expected_markup' => $this->get_delayed_inline_script_loader_script_tag() . << << scriptEventLog.push( "defer-with-async-dependent: before inline" ) - - + - - - + HTML @@ -1052,9 +826,9 @@ public function data_provider_to_test_various_strategy_dependency_chains() { $this->enqueue_test_script( $handle, 'defer', array() ); $this->add_test_inline_script( $handle, 'after' ); }, - 'expected_markup' => $this->get_delayed_inline_script_loader_script_tag() . << - + HTML @@ -1095,15 +869,15 @@ public function data_provider_to_test_various_strategy_dependency_chains() { $this->add_test_inline_script( 'defer-dependent-of-nested-aliases', 'before' ); $this->add_test_inline_script( 'defer-dependent-of-nested-aliases', 'after' ); }, - 'expected_markup' => $this->get_delayed_inline_script_loader_script_tag() . << - + 'expected_markup' => << + - - - + HTML @@ -1138,9 +912,7 @@ public function data_provider_to_test_various_strategy_dependency_chains() { * @covers ::wp_enqueue_script() * @covers ::wp_add_inline_script() * @covers ::wp_print_scripts() - * @covers WP_Scripts::should_delay_inline_script * @covers WP_Scripts::get_inline_script_tag - * @covers WP_Scripts::has_delayed_inline_script * * @dataProvider data_provider_to_test_various_strategy_dependency_chains * @@ -2407,7 +2179,7 @@ public function data_provider_to_test_get_inline_script() { ), 'delayed' => true, 'expected_data' => '/*before foo 1*/', - 'expected_tag' => "\n", + 'expected_tag' => "\n", ), 'after-delayed' => array( 'position' => 'after', @@ -2417,7 +2189,7 @@ public function data_provider_to_test_get_inline_script() { ), 'delayed' => true, 'expected_data' => "/*after foo 1*/\n/*after foo 2*/", - 'expected_tag' => "\n", + 'expected_tag' => "\n", ), ); } @@ -3130,38 +2902,6 @@ static function() { $this->assertSame( $expected, $print_scripts ); } - /** - * Gets the script tag for the delayed inline script loader. - * - * @return string Script tag. - */ - protected function get_delayed_inline_script_loader_script_tag() { - /* - * Ensure the built delayed inline script loader file exists - * when the test suite is run from the 'src' directory. - * - * Note this should no longer be needed as of https://core.trac.wordpress.org/ticket/57844. - */ - $build_path = ABSPATH . WPINC . '/js/wp-delayed-inline-script-loader' . wp_scripts_get_suffix() . '.js'; - - if ( ! file_exists( $build_path ) ) { - $src_path = ABSPATH . 'js/_enqueues/lib/delayed-inline-script-loader.js'; - - $file_contents = file_get_contents( $src_path ); - - self::touch( $build_path ); - file_put_contents( - $build_path, - $file_contents - ); - } - - return wp_get_inline_script_tag( - file_get_contents( $build_path ), - array( 'id' => 'wp-delayed-inline-script-loader' ) - ); - } - /** * Parse an HTML markup fragment. * From 1c91094bd60a2a468b340772973c9f42a27a8caa Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Tue, 20 Jun 2023 17:21:07 -0500 Subject: [PATCH 2/7] Remove support for delayed inline scripts --- src/wp-includes/class-wp-dependencies.php | 12 -- src/wp-includes/class-wp-scripts.php | 177 ++-------------------- 2 files changed, 14 insertions(+), 175 deletions(-) diff --git a/src/wp-includes/class-wp-dependencies.php b/src/wp-includes/class-wp-dependencies.php index 1977a06917357..0bc42bb282b77 100644 --- a/src/wp-includes/class-wp-dependencies.php +++ b/src/wp-includes/class-wp-dependencies.php @@ -126,18 +126,6 @@ public function do_items( $handles = false, $group = false ) { $handles = false === $handles ? $this->queue : (array) $handles; $this->all_deps( $handles ); - return $this->process_to_do_items( $group ); - } - - /** - * Processes the items marked for to do, and their dependencies. - * - * @since 6.3.0 - * - * @param int|false $group Group level: level (int), no group (false). - * @return string[] Array of handles of items that have been processed. - */ - protected function process_to_do_items( $group ) { foreach ( $this->to_do as $key => $handle ) { if ( ! in_array( $handle, $this->done, true ) && isset( $this->registered[ $handle ] ) ) { /* diff --git a/src/wp-includes/class-wp-scripts.php b/src/wp-includes/class-wp-scripts.php index eaede952cdd01..57dbcf95f12a7 100644 --- a/src/wp-includes/class-wp-scripts.php +++ b/src/wp-includes/class-wp-scripts.php @@ -75,15 +75,6 @@ class WP_Scripts extends WP_Dependencies { */ public $do_concat = false; - /** - * Whether the delayed inline script loader has been printed. - * - * @since 6.3.0 - * @see WP_Scripts::print_delayed_inline_script_loader() - * @var bool - */ - public $printed_delayed_inline_script_loader = false; - /** * Holds HTML markup of scripts and additional data if concatenation * is enabled. @@ -272,33 +263,6 @@ public function print_extra_script( $handle, $display = true ) { return true; } - /** - * Processes the items and dependencies. - * - * Processes the items passed to it or the queue, and their dependencies. - * - * @since 6.3.0 - * - * @param string|string[]|false $handles Optional. Items to be processed: queue (false), - * single item (string), or multiple items (array of strings). - * Default false. - * @param int|false $group Optional. Group level: level (int), no group (false). - * @return string[] Array of handles of items that have been processed. - */ - public function do_items( $handles = false, $group = false ) { - $handles = false === $handles ? $this->queue : (array) $handles; - $this->all_deps( $handles ); - - // This statement is the only difference from parent::do_items(). - if ( ! $this->printed_delayed_inline_script_loader - && $this->has_delayed_inline_script( $this->to_do ) - ) { - $this->print_delayed_inline_script_loader(); - } - - return $this->process_to_do_items( $group ); - } - /** * Processes a script dependency. * @@ -596,108 +560,8 @@ public function get_inline_script_tag( $handle, $position = 'after' ) { } $id = "{$handle}-js-{$position}"; - if ( $this->should_delay_inline_script( $handle, $position ) ) { - $attributes = array( - 'id' => $id, - 'type' => 'text/plain', - ); - - /* - * Note that any dependency aliases need to be flattened because an alias is a bundle of dependencies - * and their handles won't appear on any specific scripts. Since no script will appear in the DOM for - * an alias, there won't be any way to keep track of when it has loaded. Therefore, we only keep track of - * the aliased dependencies (the leaf nodes of the alias dependency tree as it were). - */ - $deps = $this->get_unaliased_deps( $this->registered[ $handle ]->deps ); - if ( $deps ) { - $attributes['data-wp-deps'] = implode( ',', $deps ); - } - return wp_get_inline_script_tag( $js, $attributes ); - } else { - return wp_get_inline_script_tag( $js, compact( 'id' ) ); - } - } - - /** - * Prints a script to load delayed inline scripts. - * - * When a script dependency has attached inline scripts, the execution - * of the inline scripts needs to be delayed in order to preserve the - * execution order with the script along with any dependency/dependent - * scripts. When there are delayed inline scripts needing to be printed - * this function will print the loader script once. - * - * @since 6.3.0 - */ - public function print_delayed_inline_script_loader() { - wp_print_inline_script_tag( - file_get_contents( ABSPATH . WPINC . '/js/wp-delayed-inline-script-loader' . wp_scripts_get_suffix() . '.js' ), - array( 'id' => 'wp-delayed-inline-script-loader' ) - ); - $this->printed_delayed_inline_script_loader = true; - } - - /** - * Determines whether an inline script should be delayed. - * - * @since 6.3.0 - * - * @param string $handle Name of the script for which the check should be run against. - * Must be lowercase. - * @param string $position Position, either 'before' or 'after'. - * - * @return bool Whether to delay. - */ - private function should_delay_inline_script( $handle, $position ) { - // Never delay the inline script if the script is blocking. - if ( ! $this->is_delayed_strategy( $this->get_eligible_loading_strategy( $handle ) ) ) { - return false; - } - - // After inline scripts must always be delayed for non-blocking scripts. - if ( 'after' === $position ) { - return true; - } - - // From now on, we're only considering before inline scripts. - $deps = $this->registered[ $handle ]->deps; - - /* - * If there are no dependencies, the before script must not delay since there will not be a load event on a - * preceding script for which an event handler can run the before inline script. - */ - if ( empty( $deps ) ) { - return false; - } - - /* - * Only delay a before inline script if there is a delayed dependency. When this dependency loads, the load - * event will fire and the logic in delayed-inline-script-loader.js will run any inline before scripts for which - * all dependencies have been run. Importantly, if there are no dependencies, the before inline script must not - * be delayed because there will be no such preceding load event at which the before inline scripts can be run. - * Additionally, if there are only blocking dependencies then the before inline script cannot be delayed because - * a blocking dependency may have an after script which must execute after the load event, meaning the load - * event cannot be used to run the before scripts of delayed dependents since it would be running before the - * dependency's after inline script. - */ - foreach ( $deps as $dep ) { - if ( ! isset( $this->registered[ $dep ] ) ) { - continue; - } - - // If the dependency is an alias, look at its members. - if ( ! $this->registered[ $dep ]->src ) { - foreach ( $this->get_unaliased_deps( $this->registered[ $dep ]->deps ) as $alias_dep ) { - if ( $this->is_delayed_strategy( $this->get_eligible_loading_strategy( $alias_dep ) ) ) { - return true; - } - } - } elseif ( $this->is_delayed_strategy( $this->get_eligible_loading_strategy( $dep ) ) ) { - return true; - } - } - return false; + return wp_get_inline_script_tag( $js, compact( 'id' ) ); } /** @@ -989,29 +853,6 @@ public function add_data( $handle, $key, $value ) { return parent::add_data( $handle, $key, $value ); } - /** - * Checks all handles for any delayed inline scripts. - * - * @since 6.3.0 - * @see WP_Scripts::should_delay_inline_script() - * - * @param string[] $handles Handles to check. - * @return bool True if the inline script present, otherwise false. - */ - public function has_delayed_inline_script( array $handles ) { - foreach ( $handles as $handle ) { - foreach ( array( 'before', 'after' ) as $position ) { - if ( - $this->get_data( $handle, $position ) && - $this->should_delay_inline_script( $handle, $position ) - ) { - return true; - } - } - } - return false; - } - /** * Gets all dependents of a script. * @@ -1148,6 +989,11 @@ private function get_eligible_loading_strategy( $handle ) { return ''; } + // Handles with inline scripts attached in the 'after' position cannot be delayed. + if ( $this->has_inline_script( $handle, 'after' ) ) { + return ''; + } + // Handle async strategy scenario. if ( 'async' === $intended_strategy && $this->has_only_delayed_dependents( $handle, true ) ) { return 'async'; @@ -1166,11 +1012,16 @@ private function get_eligible_loading_strategy( $handle ) { * * @since 6.3.0 * - * @param string $handle Name of the script to get data for. - * Must be lowercase. + * @param string $handle Name of the script to get data for. Must be lowercase. + * @param string $position The position of the inline script. + * * @return bool Whether the handle has an inline script (either before or after). */ - private function has_inline_script( $handle ) { + private function has_inline_script( $handle, $position = null ) { + if ( $position && in_array( $position, array( 'before', 'after' ), true ) ) { + return $this->get_data( $handle, $position ); + } + return $this->get_data( $handle, 'before' ) || $this->get_data( $handle, 'after' ); } From 967a704debe68c839b86f0c7bb5b4b9dc9300be8 Mon Sep 17 00:00:00 2001 From: Simon Dowdles Date: Wed, 21 Jun 2023 15:29:22 +0200 Subject: [PATCH 3/7] Update has_only_delayed_dependents to return false if inline after scripts associated with handle or its dependents. --- src/wp-includes/class-wp-scripts.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/class-wp-scripts.php b/src/wp-includes/class-wp-scripts.php index 57dbcf95f12a7..1e932d8d51422 100644 --- a/src/wp-includes/class-wp-scripts.php +++ b/src/wp-includes/class-wp-scripts.php @@ -917,13 +917,19 @@ private function has_only_delayed_dependents( $handle, $async_only = false, $che $checked[ $handle ] = true; $dependents = $this->get_dependents( $handle ); - // If there are no dependents remaining to consider, the script can be delayed. + // If there are no dependents remaining to consider, and if the handle does not have `after` inline scripts + // associated with it, the script can be delayed. if ( empty( $dependents ) ) { - return true; + return ! $this->has_inline_script( $handle, 'after' ); } // Consider each dependent and check if it is delayed. foreach ( $dependents as $dependent ) { + // Do not defer if there are any `after` inline scripts associated with the dependent. + if ( $this->has_inline_script( $handle, 'after' ) ) { + return false; + } + if ( ! isset( $this->registered[ $dependent ] ) ) { continue; } From c6fc771029aaebf3ea22a8e1bef84f1d0abf7d50 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Wed, 21 Jun 2023 09:34:35 -0500 Subject: [PATCH 4/7] Remove delayed-inline-script-loader.js and references --- Gruntfile.js | 2 - .../lib/delayed-inline-script-loader.js | 107 ------------------ 2 files changed, 109 deletions(-) delete mode 100644 src/js/_enqueues/lib/delayed-inline-script-loader.js diff --git a/Gruntfile.js b/Gruntfile.js index bbcdcd776e225..2a6d2b9fbaa52 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -371,7 +371,6 @@ module.exports = function(grunt) { [ WORKING_DIR + 'wp-includes/js/wp-auth-check.js' ]: [ './src/js/_enqueues/lib/auth-check.js' ], [ WORKING_DIR + 'wp-includes/js/wp-backbone.js' ]: [ './src/js/_enqueues/wp/backbone.js' ], [ WORKING_DIR + 'wp-includes/js/wp-custom-header.js' ]: [ './src/js/_enqueues/wp/custom-header.js' ], - [ WORKING_DIR + 'wp-includes/js/wp-delayed-inline-script-loader.js' ]: [ './src/js/_enqueues/lib/delayed-inline-script-loader.js' ], [ WORKING_DIR + 'wp-includes/js/wp-embed-template.js' ]: [ './src/js/_enqueues/lib/embed-template.js' ], [ WORKING_DIR + 'wp-includes/js/wp-embed.js' ]: [ './src/js/_enqueues/wp/embed.js' ], [ WORKING_DIR + 'wp-includes/js/wp-emoji-loader.js' ]: [ './src/js/_enqueues/lib/emoji-loader.js' ], @@ -924,7 +923,6 @@ module.exports = function(grunt) { 'src/wp-includes/js/wp-auth-check.js': 'src/js/_enqueues/lib/auth-check.js', 'src/wp-includes/js/wp-backbone.js': 'src/js/_enqueues/wp/backbone.js', 'src/wp-includes/js/wp-custom-header.js': 'src/js/_enqueues/wp/custom-header.js', - 'src/wp-includes/js/wp-delayed-inline-script-loader.js': 'src/js/_enqueues/lib/delayed-inline-script-loader.js', 'src/wp-includes/js/wp-embed-template.js': 'src/js/_enqueues/lib/embed-template.js', 'src/wp-includes/js/wp-embed.js': 'src/js/_enqueues/wp/embed.js', 'src/wp-includes/js/wp-emoji-loader.js': 'src/js/_enqueues/lib/emoji-loader.js', diff --git a/src/js/_enqueues/lib/delayed-inline-script-loader.js b/src/js/_enqueues/lib/delayed-inline-script-loader.js deleted file mode 100644 index 98a257331bc2d..0000000000000 --- a/src/js/_enqueues/lib/delayed-inline-script-loader.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Handles delayed execution of before/after inline scripts for defer/async scripts. - * - * @output wp-includes/js/wp-delayed-inline-script-loader.js - */ - -(function (window, document) { - var nonce = document.currentScript.nonce, - doneDependencies = new Set(); - - /** - * Determines whether a script was loaded. - * - * @param {string} dep Dependency handle. - * @returns {boolean} Whether dependency was done. - */ - function isDependencyDone(dep) { - return doneDependencies.has(dep); - } - - /** - * Runs an inline script. - * - * @param {HTMLScriptElement} script Script to run. - */ - function runInlineScript(script) { - var newScript; - script.dataset.wpDone = '1'; - if (nonce && nonce !== script.nonce) { - console.error( - 'CSP nonce check failed for after inline script. Execution aborted.', - script - ); - return; - } - newScript = script.cloneNode(true); - newScript.type = 'text/javascript'; - script.parentNode.replaceChild(newScript, script); - } - - /** - * Runs the supplied inline scripts if all of their dependencies have been done. - * - * @param {NodeList} scripts Scripts to run if ready. - */ - function runReadyInlineScripts(scripts) { - var i, len, deps; - for (i = 0, len = scripts.length; i < len; i++) { - deps = scripts[i].dataset.wpDeps.split(/,/); - if (deps.every(isDependencyDone)) { - runInlineScript(scripts[i]); - } - } - } - - /** - * Runs whenever a load event happens. - * - * @param {Event} event Event. - */ - function onScriptLoad(event) { - var matches, handle, script; - if (!(event.target instanceof HTMLScriptElement || event.target.id)) { - return; - } - - matches = event.target.id.match(/^(.+)-js$/); - if (!matches) { - return; - } - handle = matches[1]; - doneDependencies.add(handle); - - /* - * Return now if blocking because we cannot run delayed inline scripts because if we do, we could accidentally - * run the before inline script for a dependent _before_ the after script of this blocking script is evaluated. - */ - if (!(event.target.async || event.target.defer)) { - return; - } - - // First, run all inline after scripts which are associated with this handle. - script = document.querySelector( - 'script:not([src])[type="text/plain"][id="' + handle + '-js-after"]' - ); - if (script instanceof HTMLScriptElement) { - runInlineScript(script); - } - - // Next, run all pending inline before scripts for all dependents for which all dependencies have loaded. - runReadyInlineScripts( - document.querySelectorAll( - 'script:not([src])[type="text/plain"][data-wp-deps][id$="-js-before"]:not([data-wp-done])' - ) - ); - } - - document.addEventListener('load', onScriptLoad, true); - - window.addEventListener( - 'load', - function () { - document.removeEventListener('load', onScriptLoad, true); - }, - { once: true } - ); -})(window, document); From a4eeb985fbecbefa5a7744f3e4b1bc9e859048fe Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Wed, 21 Jun 2023 15:44:09 -0500 Subject: [PATCH 5/7] Consolidate logic for determining eligible loading strategies This adds a new private method, `WP_Scripts::filter_eligible_strategies` that is responsible for recursively eliminating possible loading strategies. This removes duplication of logic that previously was in place for checking the eligible strategy of a handle with checking for whether dependents could be deferred. This also removes `WP_Scripts::has_only_deferrable_dependents`, which is no longer needed. Unit tests for `has_only_deferrable_dependents` have been updated to test `filter_eligible_strategies` instead. --- src/wp-includes/class-wp-scripts.php | 137 ++++++++----------- tests/phpunit/tests/dependencies/scripts.php | 61 ++++----- 2 files changed, 79 insertions(+), 119 deletions(-) diff --git a/src/wp-includes/class-wp-scripts.php b/src/wp-includes/class-wp-scripts.php index 1e932d8d51422..ad77817dc713c 100644 --- a/src/wp-includes/class-wp-scripts.php +++ b/src/wp-includes/class-wp-scripts.php @@ -899,118 +899,89 @@ private function is_delayed_strategy( $strategy ) { } /** - * Checks if all of a script's dependents are delayed, which is required to maintain execution order. + * Gets the best eligible loading strategy for a script. * * @since 6.3.0 * - * @param string $handle The script handle. - * @param bool $async_only Whether to limit to async strategy only. - * @param array $checked Optional. An array of already checked script handles, used to avoid recursive loops. - * @return bool True if all dependents are delayed, false otherwise. + * @param string $handle The script handle. + * @return string The best eligible loading strategy. */ - private function has_only_delayed_dependents( $handle, $async_only = false, $checked = array() ) { - // If this node was already checked, this script can be delayed and the branch ends. - if ( isset( $checked[ $handle ] ) ) { - return true; - } - - $checked[ $handle ] = true; - $dependents = $this->get_dependents( $handle ); - - // If there are no dependents remaining to consider, and if the handle does not have `after` inline scripts - // associated with it, the script can be delayed. - if ( empty( $dependents ) ) { - return ! $this->has_inline_script( $handle, 'after' ); - } - - // Consider each dependent and check if it is delayed. - foreach ( $dependents as $dependent ) { - // Do not defer if there are any `after` inline scripts associated with the dependent. - if ( $this->has_inline_script( $handle, 'after' ) ) { - return false; - } - - if ( ! isset( $this->registered[ $dependent ] ) ) { - continue; - } - - // If the dependency is not enqueued, exclude it from consideration. - if ( ! $this->query( $dependent, 'enqueued' ) ) { - continue; - } - - // Handle script alias case (where it has no src). Here, the strategy doesn't matter, but only whether there are inline scripts. - if ( ! $this->registered[ $dependent ]->src ) { - // A script alias cannot be delayed if it has inline scripts since there is no load event. - if ( $this->has_inline_script( $dependent ) ) { - return false; - } - } else { - // If the dependent script is not using the defer or async strategy, no script in the chain is delayed. - $strategy = $this->get_data( $dependent, 'strategy' ); - if ( $async_only ? - 'async' !== $strategy : - ! $this->is_delayed_strategy( $strategy ) - ) { - return false; - } - } + private function get_eligible_loading_strategy( $handle ) { + $eligible = $this->filter_eligible_strategies( $handle ); - // Recursively check all dependents. - if ( ! $this->has_only_delayed_dependents( $dependent, $async_only, $checked ) ) { - return false; - } + // Bail early once we know the eligible strategy is blocking. + if ( empty( $eligible ) ) { + return ''; } - return true; + return in_array( 'async', $eligible, true ) ? 'async' : 'defer' ; } /** - * Gets the best eligible loading strategy for a script. + * Filter the list of eligible loading strategies for a script. * * @since 6.3.0 * - * @param string $handle The script handle. - * @return string $strategy The best eligible loading strategy. + * @param string $handle The script handle. + * @param string[]|null $eligible Optional. The list of strategies to filter. Default null. + * @param array $checked Optional. An array of already checked script handles, used to avoid recursive loops. + * @return string[] A list of eligible loading strategies that could be used. */ - private function get_eligible_loading_strategy( $handle ) { - if ( ! isset( $this->registered[ $handle ] ) ) { - return ''; + private function filter_eligible_strategies( $handle, $eligible = null, $checked = array() ) { + // If no strategies are being passed, all strategies are eligible. + if ( null === $eligible ) { + $eligible = $this->delayed_strategies; } - $intended_strategy = $this->get_data( $handle, 'strategy' ); + // If this handle was already checked, return early. + if ( isset( $checked[ $handle ] ) ) { + return $eligible; + } - /* - * Handle known blocking strategy scenarios. - * - An empty strategy is synonymous with blocking. - * - A script alias (where $src is false) must always be blocking since the after inline script cannot be - * delayed as there is no external script tag and thus no load event at which the inline script can be run. - */ - if ( empty( $intended_strategy ) ) { - return ''; + // Mark this handle as checked. + $checked[ $handle ] = true; + + // If this handle isn't registered, don't filter anything and return. + if ( ! isset( $this->registered[ $handle ] ) ) { + return $eligible; } - // Aliases that have before/after inline scripts can never be delayed since there is no load event. - if ( ! $this->registered[ $handle ]->src && $this->has_inline_script( $handle ) ) { - return ''; + // If the handle is not enqueued, don't filter anything and return. + if ( ! $this->query( $handle, 'enqueued' ) ) { + return $eligible; + } + + $is_alias = (bool) ! $this->registered[ $handle ]->src; + $intended_strategy = $this->get_data( $handle, 'strategy' ); + + // For non-alias handles, an empty intended strategy filters all strategies. + if ( ! $is_alias && empty( $intended_strategy ) ) { + return array(); } // Handles with inline scripts attached in the 'after' position cannot be delayed. if ( $this->has_inline_script( $handle, 'after' ) ) { - return ''; + return array(); } - // Handle async strategy scenario. - if ( 'async' === $intended_strategy && $this->has_only_delayed_dependents( $handle, true ) ) { - return 'async'; + // If the intended strategy is 'defer', filter out 'async'. + if ( $intended_strategy === 'defer' ) { + $eligible = array( 'defer' ); } - // Handling defer strategy scenarios. (The intended strategy could actually be 'async' here.) - if ( $this->has_only_delayed_dependents( $handle ) ) { - return 'defer'; + $dependents = $this->get_dependents( $handle ); + + // Recursively filter eligible strategies for dependents. + foreach ( $dependents as $dependent ) { + // Bail early once we know the eligible strategy is blocking. + if ( empty( $eligible ) ) { + return array(); + } + + $eligible = $this->filter_eligible_strategies( $dependent, $eligible, $checked ); } - return ''; + return $eligible; } /** diff --git a/tests/phpunit/tests/dependencies/scripts.php b/tests/phpunit/tests/dependencies/scripts.php index 2e47446ecea7c..83ef982e40234 100644 --- a/tests/phpunit/tests/dependencies/scripts.php +++ b/tests/phpunit/tests/dependencies/scripts.php @@ -207,7 +207,7 @@ public function test_before_inline_scripts_with_delayed_main_script( $strategy ) * * @covers WP_Scripts::do_item * @covers WP_Scripts::get_eligible_loading_strategy - * @covers WP_Scripts::has_only_delayed_dependents + * @covers WP_Scripts::filter_eligible_strategies * @covers ::wp_enqueue_script */ public function test_loading_strategy_with_valid_async_registration() { @@ -225,7 +225,7 @@ public function test_loading_strategy_with_valid_async_registration() { * * @covers WP_Scripts::do_item * @covers WP_Scripts::get_eligible_loading_strategy - * @covers WP_Scripts::has_only_delayed_dependents + * @covers WP_Scripts::filter_eligible_strategies * @covers ::wp_enqueue_script * * @dataProvider data_provider_delayed_strategies @@ -247,7 +247,7 @@ public function test_delayed_dependent_with_blocking_dependency( $strategy ) { * * @covers WP_Scripts::do_item * @covers WP_Scripts::get_eligible_loading_strategy - * @covers WP_Scripts::has_only_delayed_dependents + * @covers WP_Scripts::filter_eligible_strategies * @covers ::wp_enqueue_script * * @dataProvider data_provider_delayed_strategies @@ -268,7 +268,7 @@ public function test_blocking_dependent_with_delayed_dependency( $strategy ) { * * @covers WP_Scripts::do_item * @covers WP_Scripts::get_eligible_loading_strategy - * @covers WP_Scripts::has_only_delayed_dependents + * @covers WP_Scripts::filter_eligible_strategies * @covers ::wp_enqueue_script * * @dataProvider data_provider_delayed_strategies @@ -284,19 +284,18 @@ public function test_delayed_dependent_with_blocking_dependency_not_enqueued( $s } /** - * Data provider for test_has_only_delayed_dependents. + * Data provider for test_filter_eligible_strategies. * * @return array */ - public function get_data_to_test_has_only_delayed_dependents() { + public function get_data_to_filter_eligible_strategies() { return array( 'no_dependents' => array( 'set_up' => static function () { wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) ); return 'foo'; }, - 'async_only' => false, - 'expected' => true, + 'expected' => array( 'defer' ), ), 'one_delayed_dependent' => array( 'set_up' => static function () { @@ -304,8 +303,7 @@ public function get_data_to_test_has_only_delayed_dependents() { wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) ); return 'foo'; }, - 'async_only' => false, - 'expected' => true, + 'expected' => array( 'defer' ), ), 'one_blocking_dependent' => array( 'set_up' => static function () { @@ -313,8 +311,7 @@ public function get_data_to_test_has_only_delayed_dependents() { wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null ); return 'foo'; }, - 'async_only' => false, - 'expected' => false, + 'expected' => array(), ), 'one_blocking_dependent_not_enqueued' => array( 'set_up' => static function () { @@ -322,8 +319,7 @@ public function get_data_to_test_has_only_delayed_dependents() { wp_register_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null ); return 'foo'; }, - 'async_only' => false, - 'expected' => true, // Because bar was not enqueued, only foo was. + 'expected' => array( 'defer' ), // Because bar was not enqueued, only foo was. ), 'two_delayed_dependents' => array( 'set_up' => static function () { @@ -332,24 +328,21 @@ public function get_data_to_test_has_only_delayed_dependents() { wp_enqueue_script( 'baz', 'https://example.com/baz.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) ); return 'foo'; }, - 'async_only' => false, - 'expected' => true, + 'expected' => array( 'defer' ), ), 'recursion_not_delayed' => array( 'set_up' => static function () { wp_enqueue_script( 'foo', 'https://example.com/foo.js', array( 'foo' ), null ); return 'foo'; }, - 'async_only' => false, - 'expected' => false, + 'expected' => array(), ), 'recursion_yes_delayed' => array( 'set_up' => static function () { wp_enqueue_script( 'foo', 'https://example.com/foo.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) ); return 'foo'; }, - 'async_only' => false, - 'expected' => true, + 'expected' => array( 'defer' ), ), 'recursion_triple_level' => array( 'set_up' => static function () { @@ -358,8 +351,7 @@ public function get_data_to_test_has_only_delayed_dependents() { wp_enqueue_script( 'baz', 'https://example.com/bar.js', array( 'bar' ), null, array( 'strategy' => 'defer' ) ); return 'foo'; }, - 'async_only' => false, - 'expected' => true, + 'expected' => array( 'defer' ), ), 'async_only_with_defer_dependency' => array( 'set_up' => static function () { @@ -367,8 +359,7 @@ public function get_data_to_test_has_only_delayed_dependents() { wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) ); return 'foo'; }, - 'async_only' => true, - 'expected' => false, + 'expected' => array( 'defer' ), ), 'not_async_only_with_defer_dependency' => array( 'set_up' => static function () { @@ -376,33 +367,31 @@ public function get_data_to_test_has_only_delayed_dependents() { wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) ); return 'foo'; }, - 'async_only' => false, - 'expected' => true, + 'expected' => array( 'defer' ), ), ); } /** - * Tests that the has_only_delayed_dependents method works as expected and returns the correct value. + * Tests that the filter_eligible_strategies method works as expected and returns the correct value. * * @ticket 12009 * - * @covers WP_Scripts::has_only_delayed_dependents - * @covers WP_Scripts::get_dependents + * @covers WP_Scripts::filter_eligible_strategies * - * @dataProvider get_data_to_test_has_only_delayed_dependents + * @dataProvider get_data_to_filter_eligible_strategies * * @param callable $set_up Set up. * @param bool $async_only Async only. * @param bool $expected Expected return value. */ - public function test_has_only_delayed_dependents( $set_up, $async_only, $expected ) { + public function test_filter_eligible_strategies( $set_up, $expected ) { $handle = $set_up(); $wp_scripts_reflection = new ReflectionClass( WP_Scripts::class ); - $has_only_delayed_dependents = $wp_scripts_reflection->getMethod( 'has_only_delayed_dependents' ); - $has_only_delayed_dependents->setAccessible( true ); - $this->assertSame( $expected, $has_only_delayed_dependents->invokeArgs( wp_scripts(), array( $handle, $async_only ) ), 'Expected return value of WP_Scripts::has_only_delayed_dependents to match.' ); + $filter_eligible_strategies = $wp_scripts_reflection->getMethod( 'filter_eligible_strategies' ); + $filter_eligible_strategies->setAccessible( true ); + $this->assertSame( $expected, $filter_eligible_strategies->invokeArgs( wp_scripts(), array( $handle ) ), 'Expected return value of WP_Scripts::filter_eligible_strategies to match.' ); } /** @@ -1010,7 +999,7 @@ public function test_defer_with_async_dependent() { * * @covers WP_Scripts::do_item * @covers WP_Scripts::get_eligible_loading_strategy - * @covers WP_Scripts::has_only_delayed_dependents + * @covers WP_Scripts::filter_eligible_strategies * @covers ::wp_enqueue_script */ public function test_loading_strategy_with_invalid_defer_registration() { @@ -1031,7 +1020,7 @@ public function test_loading_strategy_with_invalid_defer_registration() { * * @covers WP_Scripts::do_item * @covers WP_Scripts::get_eligible_loading_strategy - * @covers WP_Scripts::has_only_delayed_dependents + * @covers WP_Scripts::filter_eligible_strategies * @covers ::wp_enqueue_script */ public function test_loading_strategy_with_valid_blocking_registration() { From 12969ead675c93e2f1aa83bddf038e5f20ab96f0 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Thu, 22 Jun 2023 09:09:31 -0500 Subject: [PATCH 6/7] Cast return values for has_inline_script to bool --- src/wp-includes/class-wp-scripts.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/class-wp-scripts.php b/src/wp-includes/class-wp-scripts.php index ad77817dc713c..0f223a96ceb5f 100644 --- a/src/wp-includes/class-wp-scripts.php +++ b/src/wp-includes/class-wp-scripts.php @@ -996,10 +996,10 @@ private function filter_eligible_strategies( $handle, $eligible = null, $checked */ private function has_inline_script( $handle, $position = null ) { if ( $position && in_array( $position, array( 'before', 'after' ), true ) ) { - return $this->get_data( $handle, $position ); + return (bool) $this->get_data( $handle, $position ); } - return $this->get_data( $handle, 'before' ) || $this->get_data( $handle, 'after' ); + return (bool) ( $this->get_data( $handle, 'before' ) || $this->get_data( $handle, 'after' ) ); } /** From 16ffb03a23de1f99e87c456f1364b60c8d09b8b0 Mon Sep 17 00:00:00 2001 From: Joe McGill Date: Thu, 22 Jun 2023 09:24:05 -0500 Subject: [PATCH 7/7] Add additional unit test cases --- tests/phpunit/tests/dependencies/scripts.php | 44 +++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/dependencies/scripts.php b/tests/phpunit/tests/dependencies/scripts.php index 83ef982e40234..f1688ed3b04ad 100644 --- a/tests/phpunit/tests/dependencies/scripts.php +++ b/tests/phpunit/tests/dependencies/scripts.php @@ -353,6 +353,14 @@ public function get_data_to_filter_eligible_strategies() { }, 'expected' => array( 'defer' ), ), + 'async_only_with_async_dependency' => array( + 'set_up' => static function () { + wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) ); + wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'async' ) ); + return 'foo'; + }, + 'expected' => array( 'defer', 'async' ), + ), 'async_only_with_defer_dependency' => array( 'set_up' => static function () { wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) ); @@ -361,14 +369,46 @@ public function get_data_to_filter_eligible_strategies() { }, 'expected' => array( 'defer' ), ), - 'not_async_only_with_defer_dependency' => array( + 'async_only_with_blocking_dependency' => array( 'set_up' => static function () { wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) ); - wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null, array( 'strategy' => 'defer' ) ); + wp_enqueue_script( 'bar', 'https://example.com/bar.js', array( 'foo' ), null ); + return 'foo'; + }, + 'expected' => array(), + ), + 'defer_with_inline_after_script' => array( + 'set_up' => static function () { + wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) ); + wp_add_inline_script( 'foo', 'console.log("foo")', 'after' ); + return 'foo'; + }, + 'expected' => array(), + ), + 'defer_with_inline_before_script' => array( + 'set_up' => static function () { + wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'defer' ) ); + wp_add_inline_script( 'foo', 'console.log("foo")', 'before' ); return 'foo'; }, 'expected' => array( 'defer' ), ), + 'async_with_inline_after_script' => array( + 'set_up' => static function () { + wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) ); + wp_add_inline_script( 'foo', 'console.log("foo")', 'after' ); + return 'foo'; + }, + 'expected' => array(), + ), + 'async_with_inline_before_script' => array( + 'set_up' => static function () { + wp_enqueue_script( 'foo', 'https://example.com/foo.js', array(), null, array( 'strategy' => 'async' ) ); + wp_add_inline_script( 'foo', 'console.log("foo")', 'before' ); + return 'foo'; + }, + 'expected' => array( 'defer', 'async' ), + ), ); }