Skip to content

Commit c6a9f26

Browse files
committed
Editor: Opt to dequeue assets enqueued in hidden blocks, rather than to enqueue assets for non-hidden blocks.
This eliminates constant emptying out of the queues for styles, scripts, and script modules before rendering each block. This ensures that `wp_script_is()`/`wp_style_is()` will return true for assets that are actually enqueued. The `WP_Script_Modules::$queue` member which was made public in [60930] is now made private in favor of a `WP_Script_Modules::get_queue()` method, since there is no need to clear out the queue before rendering each block and restore after the rendering is complete. Finally, as a very special case for unusual blocks which contain `wp_head()`, a check is done to see if the `wp_enqueue_scripts` action occurred during the rendering of a block; if so, then no assets will be dequeued even if no markup is rendered in the block, since it may be that a script was enqueued for the footer and not the head. Developed in #10252 Follow-up to [60930]. Props westonruter, dd32, peterwilsoncc, nikunj8866, krupajnanda. Fixes #63676. git-svn-id: https://develop.svn.wordpress.org/trunk@60951 602fd350-edb4-49c9-b593-d223f7449a82
1 parent cd61711 commit c6a9f26

File tree

4 files changed

+121
-65
lines changed

4 files changed

+121
-65
lines changed

src/wp-includes/class-wp-block.php

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -492,13 +492,12 @@ public function replace_rich_text( $rich_text ) {
492492
public function render( $options = array() ) {
493493
global $post;
494494

495-
// Capture the current assets queues and then clear out to capture the diff of what was introduced by rendering.
495+
$before_wp_enqueue_scripts_count = did_action( 'wp_enqueue_scripts' );
496+
497+
// Capture the current assets queues.
496498
$before_styles_queue = wp_styles()->queue;
497499
$before_scripts_queue = wp_scripts()->queue;
498-
$before_script_modules_queue = wp_script_modules()->queue;
499-
wp_styles()->queue = array();
500-
wp_scripts()->queue = array();
501-
wp_script_modules()->queue = array();
500+
$before_script_modules_queue = wp_script_modules()->get_queue();
502501

503502
/*
504503
* There can be only one root interactive block at a time because the rendered HTML of that block contains
@@ -670,21 +669,27 @@ public function render( $options = array() ) {
670669
}
671670

672671
// Capture the new assets enqueued during rendering, and restore the queues the state prior to rendering.
673-
$new_styles_queue = wp_styles()->queue;
674-
$new_scripts_queue = wp_scripts()->queue;
675-
$new_script_modules_queue = wp_script_modules()->queue;
676-
wp_styles()->queue = $before_styles_queue;
677-
wp_scripts()->queue = $before_scripts_queue;
678-
wp_script_modules()->queue = $before_script_modules_queue;
679-
$has_new_styles = count( $new_styles_queue ) > 0;
680-
$has_new_scripts = count( $new_scripts_queue ) > 0;
681-
$has_new_script_modules = count( $new_script_modules_queue ) > 0;
682-
683-
// Merge the newly enqueued assets with the existing assets if the rendered block is not empty.
672+
$after_styles_queue = wp_styles()->queue;
673+
$after_scripts_queue = wp_scripts()->queue;
674+
$after_script_modules_queue = wp_script_modules()->get_queue();
675+
676+
/*
677+
* As a very special case, a dynamic block may in fact include a call to wp_head() (and thus wp_enqueue_scripts()),
678+
* in which all of its enqueued assets are targeting wp_footer. In this case, nothing would be printed, but this
679+
* shouldn't indicate that the just-enqueued assets should be dequeued due to it being an empty block.
680+
*/
681+
$just_did_wp_enqueue_scripts = ( did_action( 'wp_enqueue_scripts' ) !== $before_wp_enqueue_scripts_count );
682+
683+
$has_new_styles = ( $before_styles_queue !== $after_styles_queue );
684+
$has_new_scripts = ( $before_scripts_queue !== $after_scripts_queue );
685+
$has_new_script_modules = ( $before_script_modules_queue !== $after_script_modules_queue );
686+
687+
// Dequeue the newly enqueued assets with the existing assets if the rendered block was empty & wp_enqueue_scripts did not fire.
684688
if (
689+
! $just_did_wp_enqueue_scripts &&
685690
( $has_new_styles || $has_new_scripts || $has_new_script_modules ) &&
686691
(
687-
trim( $block_content ) !== '' ||
692+
trim( $block_content ) === '' &&
688693
/**
689694
* Filters whether to enqueue assets for a block which has no rendered content.
690695
*
@@ -693,17 +698,17 @@ public function render( $options = array() ) {
693698
* @param bool $enqueue Whether to enqueue assets.
694699
* @param string $block_name Block name.
695700
*/
696-
(bool) apply_filters( 'enqueue_empty_block_content_assets', false, $this->name )
701+
! (bool) apply_filters( 'enqueue_empty_block_content_assets', false, $this->name )
697702
)
698703
) {
699-
if ( $has_new_styles ) {
700-
wp_styles()->queue = array_unique( array_merge( wp_styles()->queue, $new_styles_queue ) );
704+
foreach ( array_diff( $after_styles_queue, $before_styles_queue ) as $handle ) {
705+
wp_dequeue_style( $handle );
701706
}
702-
if ( $has_new_scripts ) {
703-
wp_scripts()->queue = array_unique( array_merge( wp_scripts()->queue, $new_scripts_queue ) );
707+
foreach ( array_diff( $after_scripts_queue, $before_scripts_queue ) as $handle ) {
708+
wp_dequeue_script( $handle );
704709
}
705-
if ( $has_new_script_modules ) {
706-
wp_script_modules()->queue = array_unique( array_merge( wp_script_modules()->queue, $new_script_modules_queue ) );
710+
foreach ( array_diff( $after_script_modules_queue, $before_script_modules_queue ) as $handle ) {
711+
wp_dequeue_script_module( $handle );
707712
}
708713
}
709714

src/wp-includes/class-wp-script-modules.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class WP_Script_Modules {
2828
* @since 6.9.0
2929
* @var string[]
3030
*/
31-
public $queue = array();
31+
private $queue = array();
3232

3333
/**
3434
* Tracks whether the @wordpress/a11y script module is available.
@@ -137,6 +137,17 @@ public function register( string $id, string $src, array $deps = array(), $versi
137137
}
138138
}
139139

140+
/**
141+
* Gets IDs for queued script modules.
142+
*
143+
* @since 6.9.0
144+
*
145+
* @return string[] Script module IDs.
146+
*/
147+
public function get_queue(): array {
148+
return $this->queue;
149+
}
150+
140151
/**
141152
* Checks if the provided fetchpriority is valid.
142153
*
@@ -237,7 +248,7 @@ public function enqueue( string $id, string $src = '', array $deps = array(), $v
237248
* @param string $id The identifier of the script module.
238249
*/
239250
public function dequeue( string $id ) {
240-
$this->queue = array_diff( $this->queue, array( $id ) );
251+
$this->queue = array_values( array_diff( $this->queue, array( $id ) ) );
241252
}
242253

243254
/**

tests/phpunit/tests/blocks/wpBlock.php

Lines changed: 70 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,62 @@ static function ( $content ) {
543543
'expected_scripts' => array( 'static-view-script', 'static-child-view-script', 'dynamic-view-script' ),
544544
'expected_script_modules' => array( 'static-view-script-module', 'static-child-view-script-module', 'dynamic-view-script-module' ),
545545
),
546+
'admin_bar_assets_enqueued_in_block' => array(
547+
'set_up' => static function () {
548+
wp_enqueue_script( 'admin-bar' );
549+
wp_enqueue_style( 'admin-bar' );
550+
551+
add_filter(
552+
'render_block_core/static',
553+
static function ( $content ) {
554+
$processor = new WP_HTML_Tag_Processor( $content );
555+
$processor->next_tag();
556+
$processor->add_class( wp_script_is( 'admin-bar', 'enqueued' ) ? 'yes-admin-bar-script-enqueued' : 'not-admin-bar-script-enqueued' );
557+
$processor->add_class( wp_style_is( 'admin-bar', 'enqueued' ) ? 'yes-admin-bar-style-enqueued' : 'not-admin-bar-style-enqueued' );
558+
return $processor->get_updated_html();
559+
},
560+
10,
561+
3
562+
);
563+
},
564+
'block_markup' => '<!-- wp:static --><div class="static"></div><!-- /wp:static -->',
565+
'expected_rendered_block' => '
566+
<div class="static yes-admin-bar-script-enqueued yes-admin-bar-style-enqueued"></div>
567+
',
568+
'expected_styles' => array( 'static-view-style', 'admin-bar' ),
569+
'expected_scripts' => array( 'static-view-script', 'admin-bar' ),
570+
'expected_script_modules' => array( 'static-view-script-module' ),
571+
),
572+
'enqueues_in_wp_head_block' => array(
573+
'set_up' => static function () {
574+
remove_all_actions( 'wp_head' );
575+
remove_all_actions( 'wp_enqueue_scripts' );
576+
577+
add_action( 'wp_head', 'wp_enqueue_scripts', 1 );
578+
add_action( 'wp_head', 'wp_print_styles', 8 );
579+
add_action( 'wp_head', 'wp_print_head_scripts', 9 );
580+
remove_action( 'wp_print_styles', 'print_emoji_styles' );
581+
582+
add_action(
583+
'wp_enqueue_scripts',
584+
static function () {
585+
wp_enqueue_script( 'for-footer', '/footer.js', array(), null, array( 'in_footer' => true ) );
586+
}
587+
);
588+
add_action(
589+
'wp_head',
590+
static function () {
591+
wp_enqueue_style( 'for-footer', '/footer.css', array(), null );
592+
},
593+
10000
594+
);
595+
},
596+
'block_markup' => '<!-- wp:wp-head /-->',
597+
'expected_rendered_block' => '',
598+
'expected_styles' => array( 'for-footer' ),
599+
'expected_scripts' => array( 'for-footer' ),
600+
'expected_script_modules' => array(),
601+
),
546602
);
547603
}
548604

@@ -562,6 +618,16 @@ public function test_render_enqueues_scripts_and_styles( ?Closure $set_up, strin
562618
if ( $set_up instanceof Closure ) {
563619
$set_up();
564620
}
621+
622+
$this->registry->register(
623+
'core/wp-head',
624+
array(
625+
'render_callback' => static function () {
626+
return get_echo( 'wp_head' );
627+
},
628+
)
629+
);
630+
565631
wp_register_style( 'static-view-style', home_url( '/static-view-style.css' ) );
566632
wp_register_script( 'static-view-script', home_url( '/static-view-script.js' ) );
567633
wp_register_script_module( 'static-view-script-module', home_url( '/static-view-script-module.js' ) );
@@ -608,44 +674,16 @@ public function test_render_enqueues_scripts_and_styles( ?Closure $set_up, strin
608674
$block = new WP_Block( $parsed_block, $context, $this->registry );
609675
$rendered_block = $block->render();
610676

677+
$this->assertSameSets( $expected_styles, wp_styles()->queue, 'Enqueued styles do not meet expectations' );
678+
$this->assertSameSets( $expected_scripts, wp_scripts()->queue, 'Enqueued scripts do not meet expectations' );
679+
$this->assertSameSets( $expected_script_modules, wp_script_modules()->get_queue(), 'Enqueued script modules do not meet expectations' );
680+
611681
$this->assertEqualHTML(
612682
$expected_rendered_block,
613683
$rendered_block,
614684
'<body>',
615685
"Rendered block does not contain expected HTML:\n$rendered_block"
616686
);
617-
618-
remove_action( 'wp_print_styles', 'print_emoji_styles' );
619-
620-
$actual_styles = array();
621-
$printed_styles = get_echo( 'wp_print_styles' );
622-
$processor = new WP_HTML_Tag_Processor( $printed_styles );
623-
while ( $processor->next_tag( array( 'tag_name' => 'LINK' ) ) ) {
624-
if ( 1 === preg_match( '/^(.+)-css$/', $processor->get_attribute( 'id' ), $matches ) ) {
625-
$actual_styles[] = $matches[1];
626-
}
627-
}
628-
$this->assertSameSets( $expected_styles, $actual_styles, 'Enqueued styles do not meet expectations' );
629-
630-
$actual_scripts = array();
631-
$printed_scripts = get_echo( 'wp_print_scripts' );
632-
$processor = new WP_HTML_Tag_Processor( $printed_scripts );
633-
while ( $processor->next_tag( array( 'tag_name' => 'SCRIPT' ) ) ) {
634-
if ( 1 === preg_match( '/^(.+)-js$/', $processor->get_attribute( 'id' ), $matches ) ) {
635-
$actual_scripts[] = $matches[1];
636-
}
637-
}
638-
$this->assertSameSets( $expected_scripts, $actual_scripts, 'Enqueued scripts do not meet expectations' );
639-
640-
$actual_script_modules = array();
641-
$printed_script_modules = get_echo( array( wp_script_modules(), 'print_enqueued_script_modules' ) );
642-
$processor = new WP_HTML_Tag_Processor( $printed_script_modules );
643-
while ( $processor->next_tag( array( 'tag_name' => 'SCRIPT' ) ) ) {
644-
if ( 1 === preg_match( '/^(.+)-js-module$/', $processor->get_attribute( 'id' ), $matches ) ) {
645-
$actual_script_modules[] = $matches[1];
646-
}
647-
}
648-
$this->assertSameSets( $expected_script_modules, $actual_script_modules, 'Enqueued script modules do not meet expectations' );
649687
}
650688

651689
/**

tests/phpunit/tests/script-modules/wpScriptModules.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,28 +1676,30 @@ private function normalize_markup_for_snapshot( string $markup ): string {
16761676
}
16771677

16781678
/**
1679-
* Tests that directly manipulating the queue works as expected.
1679+
* Tests that manipulating the queue works as expected.
16801680
*
16811681
* @ticket 63676
16821682
*
1683+
* @covers WP_Script_Modules::get_queue
16831684
* @covers WP_Script_Modules::queue
16841685
* @covers WP_Script_Modules::dequeue
16851686
*/
16861687
public function test_direct_queue_manipulation() {
16871688
$this->script_modules->register( 'foo', '/foo.js' );
16881689
$this->script_modules->register( 'bar', '/bar.js' );
16891690
$this->script_modules->register( 'baz', '/baz.js' );
1690-
$this->assertSame( array(), $this->script_modules->queue, 'Expected queue to be empty.' );
1691+
$this->assertSame( array(), $this->script_modules->get_queue(), 'Expected queue to be empty.' );
16911692
$this->script_modules->enqueue( 'foo' );
16921693
$this->script_modules->enqueue( 'foo' );
16931694
$this->script_modules->enqueue( 'bar' );
1694-
$this->assertSame( array( 'foo', 'bar' ), $this->script_modules->queue, 'Expected two deduplicated queued items.' );
1695-
$this->script_modules->queue = array( 'baz' );
1696-
$this->script_modules->enqueue( 'bar' );
1697-
$this->assertSame( array( 'baz', 'bar' ), $this->script_modules->queue, 'Expected queue updated via setter and enqueue method to have two items.' );
1695+
$this->assertSame( array( 'foo', 'bar' ), $this->script_modules->get_queue(), 'Expected two deduplicated queued items.' );
1696+
$this->script_modules->dequeue( 'foo' );
1697+
$this->script_modules->dequeue( 'foo' );
1698+
$this->script_modules->enqueue( 'baz' );
1699+
$this->assertSame( array( 'bar', 'baz' ), $this->script_modules->get_queue(), 'Expected items tup be updated after dequeue and enqueue.' );
16981700
$this->script_modules->dequeue( 'baz' );
16991701
$this->script_modules->dequeue( 'bar' );
1700-
$this->assertSame( array(), $this->script_modules->queue, 'Expected queue to be empty after dequeueing both items.' );
1702+
$this->assertSame( array(), $this->script_modules->get_queue(), 'Expected queue to be empty after dequeueing both items.' );
17011703
}
17021704

17031705
/**

0 commit comments

Comments
 (0)