Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 29 additions & 24 deletions src/wp-includes/class-wp-block.php
Original file line number Diff line number Diff line change
Expand Up @@ -492,13 +492,12 @@ public function replace_rich_text( $rich_text ) {
public function render( $options = array() ) {
global $post;

// Capture the current assets queues and then clear out to capture the diff of what was introduced by rendering.
$before_wp_enqueue_scripts_count = did_action( 'wp_enqueue_scripts' );

// Capture the current assets queues.
$before_styles_queue = wp_styles()->queue;
$before_scripts_queue = wp_scripts()->queue;
$before_script_modules_queue = wp_script_modules()->queue;
wp_styles()->queue = array();
wp_scripts()->queue = array();
wp_script_modules()->queue = array();
$before_script_modules_queue = wp_script_modules()->get_queue();

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

// Capture the new assets enqueued during rendering, and restore the queues the state prior to rendering.
$new_styles_queue = wp_styles()->queue;
$new_scripts_queue = wp_scripts()->queue;
$new_script_modules_queue = wp_script_modules()->queue;
wp_styles()->queue = $before_styles_queue;
wp_scripts()->queue = $before_scripts_queue;
wp_script_modules()->queue = $before_script_modules_queue;
$has_new_styles = count( $new_styles_queue ) > 0;
$has_new_scripts = count( $new_scripts_queue ) > 0;
$has_new_script_modules = count( $new_script_modules_queue ) > 0;

// Merge the newly enqueued assets with the existing assets if the rendered block is not empty.
$after_styles_queue = wp_styles()->queue;
$after_scripts_queue = wp_scripts()->queue;
$after_script_modules_queue = wp_script_modules()->get_queue();

/*
* As a very special case, a dynamic block may in fact include a call to wp_head() (and thus wp_enqueue_scripts()),
* in which all of its enqueued assets are targeting wp_footer. In this case, nothing would be printed, but this
* shouldn't indicate that the just-enqueued assets should be dequeued due to it being an empty block.
*/
$just_did_wp_enqueue_scripts = ( did_action( 'wp_enqueue_scripts' ) !== $before_wp_enqueue_scripts_count );

$has_new_styles = ( $before_styles_queue !== $after_styles_queue );
$has_new_scripts = ( $before_scripts_queue !== $after_scripts_queue );
$has_new_script_modules = ( $before_script_modules_queue !== $after_script_modules_queue );

// Dequeue the newly enqueued assets with the existing assets if the rendered block was empty & wp_enqueue_scripts did not fire.
if (
! $just_did_wp_enqueue_scripts &&
( $has_new_styles || $has_new_scripts || $has_new_script_modules ) &&
(
trim( $block_content ) !== '' ||
trim( $block_content ) === '' &&
/**
* Filters whether to enqueue assets for a block which has no rendered content.
*
Expand All @@ -693,17 +698,17 @@ public function render( $options = array() ) {
* @param bool $enqueue Whether to enqueue assets.
* @param string $block_name Block name.
*/
(bool) apply_filters( 'enqueue_empty_block_content_assets', false, $this->name )
! (bool) apply_filters( 'enqueue_empty_block_content_assets', false, $this->name )
)
) {
if ( $has_new_styles ) {
wp_styles()->queue = array_unique( array_merge( wp_styles()->queue, $new_styles_queue ) );
foreach ( array_diff( $after_styles_queue, $before_styles_queue ) as $handle ) {
wp_dequeue_style( $handle );
}
if ( $has_new_scripts ) {
wp_scripts()->queue = array_unique( array_merge( wp_scripts()->queue, $new_scripts_queue ) );
foreach ( array_diff( $after_scripts_queue, $before_scripts_queue ) as $handle ) {
wp_dequeue_script( $handle );
}
if ( $has_new_script_modules ) {
wp_script_modules()->queue = array_unique( array_merge( wp_script_modules()->queue, $new_script_modules_queue ) );
foreach ( array_diff( $after_script_modules_queue, $before_script_modules_queue ) as $handle ) {
wp_dequeue_script_module( $handle );
}
}

Expand Down
15 changes: 13 additions & 2 deletions src/wp-includes/class-wp-script-modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class WP_Script_Modules {
* @since 6.9.0
* @var string[]
*/
public $queue = array();
private $queue = array();

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

/**
* Gets IDs for queued script modules.
*
* @since 6.9.0
*
* @return string[] Script module IDs.
*/
public function get_queue(): array {
return $this->queue;
}

/**
* Checks if the provided fetchpriority is valid.
*
Expand Down Expand Up @@ -237,7 +248,7 @@ public function enqueue( string $id, string $src = '', array $deps = array(), $v
* @param string $id The identifier of the script module.
*/
public function dequeue( string $id ) {
$this->queue = array_diff( $this->queue, array( $id ) );
$this->queue = array_values( array_diff( $this->queue, array( $id ) ) );
}

/**
Expand Down
102 changes: 70 additions & 32 deletions tests/phpunit/tests/blocks/wpBlock.php
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,62 @@ static function ( $content ) {
'expected_scripts' => array( 'static-view-script', 'static-child-view-script', 'dynamic-view-script' ),
'expected_script_modules' => array( 'static-view-script-module', 'static-child-view-script-module', 'dynamic-view-script-module' ),
),
'admin_bar_assets_enqueued_in_block' => array(
'set_up' => static function () {
wp_enqueue_script( 'admin-bar' );
wp_enqueue_style( 'admin-bar' );

add_filter(
'render_block_core/static',
static function ( $content ) {
$processor = new WP_HTML_Tag_Processor( $content );
$processor->next_tag();
$processor->add_class( wp_script_is( 'admin-bar', 'enqueued' ) ? 'yes-admin-bar-script-enqueued' : 'not-admin-bar-script-enqueued' );
$processor->add_class( wp_style_is( 'admin-bar', 'enqueued' ) ? 'yes-admin-bar-style-enqueued' : 'not-admin-bar-style-enqueued' );
return $processor->get_updated_html();
},
10,
3
);
},
'block_markup' => '<!-- wp:static --><div class="static"></div><!-- /wp:static -->',
'expected_rendered_block' => '
<div class="static yes-admin-bar-script-enqueued yes-admin-bar-style-enqueued"></div>
',
'expected_styles' => array( 'static-view-style', 'admin-bar' ),
'expected_scripts' => array( 'static-view-script', 'admin-bar' ),
'expected_script_modules' => array( 'static-view-script-module' ),
),
'enqueues_in_wp_head_block' => array(
'set_up' => static function () {
remove_all_actions( 'wp_head' );
remove_all_actions( 'wp_enqueue_scripts' );

add_action( 'wp_head', 'wp_enqueue_scripts', 1 );
add_action( 'wp_head', 'wp_print_styles', 8 );
add_action( 'wp_head', 'wp_print_head_scripts', 9 );
remove_action( 'wp_print_styles', 'print_emoji_styles' );

add_action(
'wp_enqueue_scripts',
static function () {
wp_enqueue_script( 'for-footer', '/footer.js', array(), null, array( 'in_footer' => true ) );
}
);
add_action(
'wp_head',
static function () {
wp_enqueue_style( 'for-footer', '/footer.css', array(), null );
},
10000
);
},
'block_markup' => '<!-- wp:wp-head /-->',
'expected_rendered_block' => '',
'expected_styles' => array( 'for-footer' ),
'expected_scripts' => array( 'for-footer' ),
'expected_script_modules' => array(),
),
);
}

Expand All @@ -562,6 +618,16 @@ public function test_render_enqueues_scripts_and_styles( ?Closure $set_up, strin
if ( $set_up instanceof Closure ) {
$set_up();
}

$this->registry->register(
'core/wp-head',
array(
'render_callback' => static function () {
return get_echo( 'wp_head' );
},
)
);

wp_register_style( 'static-view-style', home_url( '/static-view-style.css' ) );
wp_register_script( 'static-view-script', home_url( '/static-view-script.js' ) );
wp_register_script_module( 'static-view-script-module', home_url( '/static-view-script-module.js' ) );
Expand Down Expand Up @@ -608,44 +674,16 @@ public function test_render_enqueues_scripts_and_styles( ?Closure $set_up, strin
$block = new WP_Block( $parsed_block, $context, $this->registry );
$rendered_block = $block->render();

$this->assertSameSets( $expected_styles, wp_styles()->queue, 'Enqueued styles do not meet expectations' );
$this->assertSameSets( $expected_scripts, wp_scripts()->queue, 'Enqueued scripts do not meet expectations' );
$this->assertSameSets( $expected_script_modules, wp_script_modules()->get_queue(), 'Enqueued script modules do not meet expectations' );

$this->assertEqualHTML(
$expected_rendered_block,
$rendered_block,
'<body>',
"Rendered block does not contain expected HTML:\n$rendered_block"
);

remove_action( 'wp_print_styles', 'print_emoji_styles' );

$actual_styles = array();
$printed_styles = get_echo( 'wp_print_styles' );
$processor = new WP_HTML_Tag_Processor( $printed_styles );
while ( $processor->next_tag( array( 'tag_name' => 'LINK' ) ) ) {
if ( 1 === preg_match( '/^(.+)-css$/', $processor->get_attribute( 'id' ), $matches ) ) {
$actual_styles[] = $matches[1];
}
}
$this->assertSameSets( $expected_styles, $actual_styles, 'Enqueued styles do not meet expectations' );

$actual_scripts = array();
$printed_scripts = get_echo( 'wp_print_scripts' );
$processor = new WP_HTML_Tag_Processor( $printed_scripts );
while ( $processor->next_tag( array( 'tag_name' => 'SCRIPT' ) ) ) {
if ( 1 === preg_match( '/^(.+)-js$/', $processor->get_attribute( 'id' ), $matches ) ) {
$actual_scripts[] = $matches[1];
}
}
$this->assertSameSets( $expected_scripts, $actual_scripts, 'Enqueued scripts do not meet expectations' );

$actual_script_modules = array();
$printed_script_modules = get_echo( array( wp_script_modules(), 'print_enqueued_script_modules' ) );
$processor = new WP_HTML_Tag_Processor( $printed_script_modules );
while ( $processor->next_tag( array( 'tag_name' => 'SCRIPT' ) ) ) {
if ( 1 === preg_match( '/^(.+)-js-module$/', $processor->get_attribute( 'id' ), $matches ) ) {
$actual_script_modules[] = $matches[1];
}
}
$this->assertSameSets( $expected_script_modules, $actual_script_modules, 'Enqueued script modules do not meet expectations' );
}

/**
Expand Down
16 changes: 9 additions & 7 deletions tests/phpunit/tests/script-modules/wpScriptModules.php
Original file line number Diff line number Diff line change
Expand Up @@ -1676,28 +1676,30 @@ private function normalize_markup_for_snapshot( string $markup ): string {
}

/**
* Tests that directly manipulating the queue works as expected.
* Tests that manipulating the queue works as expected.
*
* @ticket 63676
*
* @covers WP_Script_Modules::get_queue
* @covers WP_Script_Modules::queue
* @covers WP_Script_Modules::dequeue
*/
public function test_direct_queue_manipulation() {
$this->script_modules->register( 'foo', '/foo.js' );
$this->script_modules->register( 'bar', '/bar.js' );
$this->script_modules->register( 'baz', '/baz.js' );
$this->assertSame( array(), $this->script_modules->queue, 'Expected queue to be empty.' );
$this->assertSame( array(), $this->script_modules->get_queue(), 'Expected queue to be empty.' );
$this->script_modules->enqueue( 'foo' );
$this->script_modules->enqueue( 'foo' );
$this->script_modules->enqueue( 'bar' );
$this->assertSame( array( 'foo', 'bar' ), $this->script_modules->queue, 'Expected two deduplicated queued items.' );
$this->script_modules->queue = array( 'baz' );
$this->script_modules->enqueue( 'bar' );
$this->assertSame( array( 'baz', 'bar' ), $this->script_modules->queue, 'Expected queue updated via setter and enqueue method to have two items.' );
$this->assertSame( array( 'foo', 'bar' ), $this->script_modules->get_queue(), 'Expected two deduplicated queued items.' );
$this->script_modules->dequeue( 'foo' );
$this->script_modules->dequeue( 'foo' );
$this->script_modules->enqueue( 'baz' );
$this->assertSame( array( 'bar', 'baz' ), $this->script_modules->get_queue(), 'Expected items tup be updated after dequeue and enqueue.' );
$this->script_modules->dequeue( 'baz' );
$this->script_modules->dequeue( 'bar' );
$this->assertSame( array(), $this->script_modules->queue, 'Expected queue to be empty after dequeueing both items.' );
$this->assertSame( array(), $this->script_modules->get_queue(), 'Expected queue to be empty after dequeueing both items.' );
}

/**
Expand Down
Loading