Make WordPress Core


Ignore:
Timestamp:
10/14/2025 12:10:31 AM (12 months ago)
Author:
westonruter
Message:

Editor: Avoid enqueueing assets for blocks which do not render content.

This change prevents scripts, styles, and script modules from being enqueued for blocks that do not render any HTML content. This is common for hidden blocks or blocks like the Featured Image block when no image is present. This change reduces the amount of unused CSS and JavaScript on a page, improving performance.

A new filter, enqueue_empty_block_content_assets, is introduced to allow developers to override this behavior and enqueue assets for empty blocks if needed.

The implementation involves capturing the asset queues before and after a block is rendered. The newly enqueued assets are only merged if the block's rendered content is not empty. This is done recursively for nested blocks to ensure that assets for inner blocks are also not enqueued if a parent block is hidden.

Developed in ​https://github.com/WordPress/wordpress-develop/pull/9213.

Props westonruter, aristath, peterwilsoncc, gziolo, krupajnanda, dd32, jorbin.
See #50328.
Fixes #63676.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/blocks/wpBlock.php

    r59866 r60930  
    1414         * Fake block type registry.
    1515         *
    16          * @var WP_Block_Type_Registry
     16         * @var WP_Block_Type_Registry|null
    1717         */
    1818        private $registry = null;
    … …  
    2424                parent::set_up();
    2525
     26                global $wp_styles, $wp_scripts, $wp_script_modules;
     27                $wp_styles         = null;
     28                $wp_scripts        = null;
     29                $wp_script_modules = null;
     30
    2631                $this->registry = new WP_Block_Type_Registry();
    2732        }
    … …  
    3237        public function tear_down() {
    3338                $this->registry = null;
     39
     40                global $wp_styles, $wp_scripts, $wp_script_modules;
     41                $wp_styles         = null;
     42                $wp_scripts        = null;
     43                $wp_script_modules = null;
    3444
    3545                parent::tear_down();
    … …  
    351361
    352362                $this->assertSame( 'Original: "StaticOriginal: "Inner", from block "core/example"", from block "core/example"', $rendered_content );
     363        }
     364
     365        /**
     366         * Data provider for test_render_enqueues_scripts_and_styles.
     367         *
     368         * @return array
     369         */
     370        public function data_provider_test_render_enqueues_scripts_and_styles(): array {
     371                $block_markup = '
     372                        <!-- wp:static -->
     373                        <div class="static">
     374                                <!-- wp:static-child -->
     375                                <div class="static-child">First child</div>
     376                                <!-- /wp:static-child -->
     377                                <!-- wp:dynamic /-->
     378                                <!-- wp:static-child -->
     379                                <div class="static-child">Last child</div>
     380                                <!-- /wp:static-child -->
     381                        </div>
     382                        <!-- /wp:static -->
     383                ';
     384
     385                // TODO: Add case where a dynamic block renders other blocks?
     386                return array(
     387                        'all_printed'                             => array(
     388                                'set_up'                  => null,
     389                                'block_markup'            => $block_markup,
     390                                'expected_rendered_block' => '
     391                                        <div class="static">
     392                                                <div class="static-child">First child</div>
     393                                                <p class="dynamic">Hello World!</p>
     394                                                <div class="static-child">Last child</div>
     395                                        </div>
     396                                ',
     397                                'expected_styles'         => array( 'static-view-style', 'static-child-view-style', 'dynamic-view-style' ),
     398                                'expected_scripts'        => array( 'static-view-script', 'static-child-view-script', 'dynamic-view-script' ),
     399                                'expected_script_modules' => array( 'static-view-script-module', 'static-child-view-script-module', 'dynamic-view-script-module' ),
     400                        ),
     401                        'all_printed_with_extra_asset_via_filter' => array(
     402                                'set_up'                  => static function () {
     403                                        add_filter(
     404                                                'render_block_core/dynamic',
     405                                                static function ( $content ) {
     406                                                        wp_enqueue_style( 'dynamic-extra', home_url( '/dynamic-extra.css' ), array(), null );
     407                                                        $processor = new WP_HTML_Tag_Processor( $content );
     408                                                        if ( $processor->next_tag() ) {
     409                                                                $processor->add_class( 'filtered' );
     410                                                                $content = $processor->get_updated_html();
     411                                                        }
     412                                                        return $content;
     413                                                }
     414                                        );
     415                                },
     416                                'block_markup'            => $block_markup,
     417                                'expected_rendered_block' => '
     418                                        <div class="static">
     419                                                <div class="static-child">First child</div>
     420                                                <p class="dynamic filtered">Hello World!</p>
     421                                                <div class="static-child">Last child</div>
     422                                        </div>
     423                                ',
     424                                'expected_styles'         => array( 'static-view-style', 'dynamic-extra', 'static-child-view-style', 'dynamic-view-style' ),
     425                                'expected_scripts'        => array( 'static-view-script', 'static-child-view-script', 'dynamic-view-script' ),
     426                                'expected_script_modules' => array( 'static-view-script-module', 'static-child-view-script-module', 'dynamic-view-script-module' ),
     427                        ),
     428                        'dynamic_hidden_assets_omitted'           => array(
     429                                'set_up'                  => static function () {
     430                                        add_filter( 'render_block_core/dynamic', '__return_empty_string' );
     431                                },
     432                                'block_markup'            => $block_markup,
     433                                'expected_rendered_block' => '
     434                                        <div class="static">
     435                                                <div class="static-child">First child</div>
     436                                                <div class="static-child">Last child</div>
     437                                        </div>
     438                                ',
     439                                'expected_styles'         => array( 'static-view-style', 'static-child-view-style' ),
     440                                'expected_scripts'        => array( 'static-view-script', 'static-child-view-script' ),
     441                                'expected_script_modules' => array( 'static-view-script-module', 'static-child-view-script-module' ),
     442                        ),
     443                        'dynamic_hidden_assets_included'          => array(
     444                                'set_up'                  => static function () {
     445                                        add_filter( 'render_block_core/dynamic', '__return_empty_string' );
     446                                        add_filter(
     447                                                'enqueue_empty_block_content_assets',
     448                                                static function ( $enqueue, $block_name ) {
     449                                                        if ( 'core/dynamic' === $block_name ) {
     450                                                                $enqueue = true;
     451                                                        }
     452                                                        return $enqueue;
     453                                                },
     454                                                10,
     455                                                2
     456                                        );
     457                                },
     458                                'block_markup'            => $block_markup,
     459                                'expected_rendered_block' => '
     460                                        <div class="static">
     461                                                <div class="static-child">First child</div>
     462                                                <div class="static-child">Last child</div>
     463                                        </div>
     464                                ',
     465                                'expected_styles'         => array( 'static-view-style', 'static-child-view-style', 'dynamic-view-style' ),
     466                                'expected_scripts'        => array( 'static-view-script', 'static-child-view-script', 'dynamic-view-script' ),
     467                                'expected_script_modules' => array( 'static-view-script-module', 'static-child-view-script-module', 'dynamic-view-script-module' ),
     468                        ),
     469                        'static_hidden_assets_omitted'            => array(
     470                                'set_up'                  => static function () {
     471                                        add_filter( 'render_block_core/static', '__return_empty_string' );
     472                                        add_filter(
     473                                                'render_block_core/dynamic',
     474                                                static function ( $content ) {
     475                                                        wp_enqueue_style( 'dynamic-extra', home_url( '/dynamic-extra.css' ), array(), null );
     476                                                        return $content;
     477                                                }
     478                                        );
     479                                },
     480                                'block_markup'            => $block_markup,
     481                                'expected_rendered_block' => '',
     482                                'expected_styles'         => array(),
     483                                'expected_scripts'        => array(),
     484                                'expected_script_modules' => array(),
     485                        ),
     486                        'static_child_hidden_assets_omitted'      => array(
     487                                'set_up'                  => static function () {
     488                                        add_filter( 'render_block_core/static-child', '__return_empty_string' );
     489                                },
     490                                'block_markup'            => $block_markup,
     491                                'expected_rendered_block' => '
     492                                        <div class="static">
     493                                                <p class="dynamic">Hello World!</p>
     494                                        </div>
     495                                ',
     496                                'expected_styles'         => array( 'static-view-style', 'dynamic-view-style' ),
     497                                'expected_scripts'        => array( 'static-view-script', 'dynamic-view-script' ),
     498                                'expected_script_modules' => array( 'static-view-script-module', 'dynamic-view-script-module' ),
     499                        ),
     500                        'last_static_child_hidden_assets_omitted' => array(
     501                                'set_up'                  => static function () {
     502                                        add_filter(
     503                                                'render_block_core/static-child',
     504                                                static function ( $content ) {
     505                                                        if ( str_contains( $content, 'Last child' ) ) {
     506                                                                $content = '';
     507                                                        }
     508                                                        return $content;
     509                                                },
     510                                                10,
     511                                                3
     512                                        );
     513                                },
     514                                'block_markup'            => $block_markup,
     515                                'expected_rendered_block' => '
     516                                        <div class="static">
     517                                                <div class="static-child">First child</div>
     518                                                <p class="dynamic">Hello World!</p>
     519                                        </div>
     520                                ',
     521                                'expected_styles'         => array( 'static-view-style', 'static-child-view-style', 'dynamic-view-style' ),
     522                                'expected_scripts'        => array( 'static-view-script', 'static-child-view-script', 'dynamic-view-script' ),
     523                                'expected_script_modules' => array( 'static-view-script-module', 'static-child-view-script-module', 'dynamic-view-script-module' ),
     524                        ),
     525                        'all_hidden_assets_omitted'               => array(
     526                                'set_up'                  => static function () {
     527                                        add_filter( 'render_block', '__return_empty_string' );
     528                                },
     529                                'block_markup'            => $block_markup,
     530                                'expected_rendered_block' => '',
     531                                'expected_styles'         => array(),
     532                                'expected_scripts'        => array(),
     533                                'expected_script_modules' => array(),
     534                        ),
     535                        'all_hidden_assets_included'              => array(
     536                                'set_up'                  => static function () {
     537                                        add_filter( 'render_block', '__return_empty_string' );
     538                                        add_filter( 'enqueue_empty_block_content_assets', '__return_true' );
     539                                },
     540                                'block_markup'            => $block_markup,
     541                                'expected_rendered_block' => '',
     542                                'expected_styles'         => array( 'static-view-style', 'static-child-view-style', 'dynamic-view-style' ),
     543                                'expected_scripts'        => array( 'static-view-script', 'static-child-view-script', 'dynamic-view-script' ),
     544                                'expected_script_modules' => array( 'static-view-script-module', 'static-child-view-script-module', 'dynamic-view-script-module' ),
     545                        ),
     546                );
     547        }
     548
     549        /**
     550         * @ticket 63676
     551         * @covers WP_Block::render()
     552         *
     553         * @dataProvider data_provider_test_render_enqueues_scripts_and_styles
     554         *
     555         * @param Closure|null $set_up
     556         * @param string       $block_markup
     557         * @param string[]     $expected_styles
     558         * @param string[]     $expected_scripts
     559         * @param string[]     $expected_script_modules
     560         */
     561        public function test_render_enqueues_scripts_and_styles( ?Closure $set_up, string $block_markup, string $expected_rendered_block, array $expected_styles, array $expected_scripts, array $expected_script_modules ) {
     562                if ( $set_up instanceof Closure ) {
     563                        $set_up();
     564                }
     565                wp_register_style( 'static-view-style', home_url( '/static-view-style.css' ) );
     566                wp_register_script( 'static-view-script', home_url( '/static-view-script.js' ) );
     567                wp_register_script_module( 'static-view-script-module', home_url( '/static-view-script-module.js' ) );
     568                $this->registry->register(
     569                        'core/static',
     570                        array(
     571                                'view_style_handles'     => array( 'static-view-style' ),
     572                                'view_script_handles'    => array( 'static-view-script' ),
     573                                'view_script_module_ids' => array( 'static-view-script-module' ),
     574                        )
     575                );
     576
     577                wp_register_style( 'static-child-view-style', home_url( '/static-child-view-style.css' ) );
     578                wp_register_script( 'static-child-view-script', home_url( '/static-child-view-script.js' ) );
     579                wp_register_script_module( 'static-child-view-script-module', home_url( '/static-child-view-script-module.js' ) );
     580                $this->registry->register(
     581                        'core/static-child',
     582                        array(
     583                                'view_style_handles'     => array( 'static-child-view-style' ),
     584                                'view_script_handles'    => array( 'static-child-view-script' ),
     585                                'view_script_module_ids' => array( 'static-child-view-script-module' ),
     586                        )
     587                );
     588
     589                wp_register_style( 'dynamic-view-style', home_url( '/dynamic-view-style.css' ) );
     590                wp_register_script( 'dynamic-view-script', home_url( '/dynamic-view-script.js' ) );
     591                wp_register_script_module( 'dynamic-view-script-module', home_url( '/dynamic-view-script-module.js' ) );
     592                $this->registry->register(
     593                        'core/dynamic',
     594                        array(
     595                                'render_callback'        => static function () {
     596                                        return '<p class="dynamic">Hello World!</p>';
     597                                },
     598                                'view_style_handles'     => array( 'dynamic-view-style' ),
     599                                'view_script_handles'    => array( 'dynamic-view-script' ),
     600                                'view_script_module_ids' => array( 'dynamic-view-script-module' ),
     601                        )
     602                );
     603
     604                // TODO: Why not use do_blocks() instead?
     605                $parsed_blocks  = parse_blocks( trim( $block_markup ) );
     606                $parsed_block   = $parsed_blocks[0];
     607                $context        = array();
     608                $block          = new WP_Block( $parsed_block, $context, $this->registry );
     609                $rendered_block = $block->render();
     610
     611                $this->assertEqualHTML(
     612                        $expected_rendered_block,
     613                        $rendered_block,
     614                        '<body>',
     615                        "Rendered block does not contain expected HTML:\n$rendered_block"
     616                );
     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' );
    353649        }
    354650
Note: See TracChangeset for help on using the changeset viewer.