Make WordPress Core

Changeset 57345


Ignore:
Timestamp:
01/24/2024 10:37:54 AM (3 months ago)
Author:
youknowriad
Message:

Script Loader: Load the modules to the footer in classic themes

Incremental import maps fail if the import map is printed after the module scripts.
This means, we should always render import maps first. This means that for classic themes, we need to move the import map and modules to the footer because we can't know before that which modules are needed.

Props luisherranz, cbravobernal.
Fixes #60240.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-script-modules.php

    r57327 r57345  
    9090                'enqueue'      => isset( $this->enqueued_before_registered[ $id ] ),
    9191                'dependencies' => $dependencies,
    92                 'enqueued'     => false,
    93                 'preloaded'    => false,
    9492            );
    9593        }
     
    9795
    9896    /**
    99      * Marks the script module to be enqueued in the page the next time
    100      * `print_enqueued_script_modules` is called.
     97     * Marks the script module to be enqueued in the page.
    10198     *
    10299     * If a src is provided and the script module has not been registered yet, it
     
    159156     * module preloads.
    160157     *
    161      * It adds the actions to print the enqueued script modules and script module
    162      * preloads to both `wp_head` and `wp_footer` because in classic themes, the
    163      * script modules used by the theme and plugins will likely be able to be
    164      * printed in the `head`, but the ones used by the blocks will need to be
    165      * enqueued in the `footer`.
    166      *
    167      * As all script modules are deferred and dependencies are handled by the
    168      * browser, the order of the script modules is not important, but it's still
    169      * better to print the ones that are available when the `wp_head` is rendered,
    170      * so the browser starts downloading those as soon as possible.
    171      *
    172      * The import map is also printed in the footer to be able to include the
    173      * dependencies of all the script modules, including the ones printed in the
     158     * In classic themes, the script modules used by the blocks are not yet known
     159     * when the `wp_head` actions is fired, so it needs to print everything in the
    174160     * footer.
    175161     *
     
    177163     */
    178164    public function add_hooks() {
    179         add_action( 'wp_head', array( $this, 'print_enqueued_script_modules' ) );
    180         add_action( 'wp_head', array( $this, 'print_script_module_preloads' ) );
    181         add_action( 'wp_footer', array( $this, 'print_enqueued_script_modules' ) );
    182         add_action( 'wp_footer', array( $this, 'print_script_module_preloads' ) );
    183         add_action( 'wp_footer', array( $this, 'print_import_map' ) );
     165        $position = wp_is_block_theme() ? 'wp_head' : 'wp_footer';
     166        add_action( $position, array( $this, 'print_import_map' ) );
     167        add_action( $position, array( $this, 'print_enqueued_script_modules' ) );
     168        add_action( $position, array( $this, 'print_script_module_preloads' ) );
    184169    }
    185170
     
    188173     * attributes.
    189174     *
    190      * If a enqueued script module has already been printed, it will not be
    191      * printed again on subsequent calls to this function.
    192      *
    193175     * @since 6.5.0
    194176     */
    195177    public function print_enqueued_script_modules() {
    196178        foreach ( $this->get_marked_for_enqueue() as $id => $script_module ) {
    197             if ( false === $script_module['enqueued'] ) {
    198                 // Mark it as enqueued so it doesn't get enqueued again.
    199                 $this->registered[ $id ]['enqueued'] = true;
    200 
    201                 wp_print_script_tag(
    202                     array(
    203                         'type' => 'module',
    204                         'src'  => $this->get_versioned_src( $script_module ),
    205                         'id'   => $id . '-js-module',
    206                     )
    207                 );
    208             }
     179            wp_print_script_tag(
     180                array(
     181                    'type' => 'module',
     182                    'src'  => $this->get_versioned_src( $script_module ),
     183                    'id'   => $id . '-js-module',
     184                )
     185            );
    209186        }
    210187    }
     
    214191     * link tags with rel="modulepreload" attributes.
    215192     *
    216      * If a script module is marked for enqueue, it will not be preloaded. If a
    217      * preloaded script module has already been printed, it will not be printed
    218      * again on subsequent calls to this function.
     193     * If a script module is marked for enqueue, it will not be preloaded.
    219194     *
    220195     * @since 6.5.0
     
    222197    public function print_script_module_preloads() {
    223198        foreach ( $this->get_dependencies( array_keys( $this->get_marked_for_enqueue() ), array( 'static' ) ) as $id => $script_module ) {
    224             // Don't preload if it's marked for enqueue or has already been preloaded.
    225             if ( true !== $script_module['enqueue'] && false === $script_module['preloaded'] ) {
    226                 // Mark it as preloaded so it doesn't get preloaded again.
    227                 $this->registered[ $id ]['preloaded'] = true;
    228 
     199            // Don't preload if it's marked for enqueue.
     200            if ( true !== $script_module['enqueue'] ) {
    229201                echo sprintf(
    230202                    '<link rel="modulepreload" href="%s" id="%s">',
  • trunk/tests/phpunit/tests/script-modules/wpScriptModules.php

    r57327 r57345  
    518518
    519519    /**
    520      * Tests that it can print the enqueued script modules multiple times, and it
    521      * will only print the script modules that have not been printed before.
    522      *
    523      * @ticket 56313
    524      *
    525      * @covers ::register()
    526      * @covers ::enqueue()
    527      * @covers ::print_enqueued_script_modules()
    528      */
    529     public function test_print_enqueued_script_modules_can_be_called_multiple_times() {
    530         $this->script_modules->register( 'foo', '/foo.js' );
    531         $this->script_modules->register( 'bar', '/bar.js' );
    532         $this->script_modules->enqueue( 'foo' );
    533 
    534         $enqueued_script_modules = $this->get_enqueued_script_modules();
    535         $this->assertCount( 1, $enqueued_script_modules );
    536         $this->assertTrue( isset( $enqueued_script_modules['foo'] ) );
    537 
    538         $this->script_modules->enqueue( 'bar' );
    539 
    540         $enqueued_script_modules = $this->get_enqueued_script_modules();
    541         $this->assertCount( 1, $enqueued_script_modules );
    542         $this->assertTrue( isset( $enqueued_script_modules['bar'] ) );
    543 
    544         $enqueued_script_modules = $this->get_enqueued_script_modules();
    545         $this->assertCount( 0, $enqueued_script_modules );
    546     }
    547 
    548     /**
    549      * Tests that it can print the preloaded script modules multiple times, and it
    550      * will only print the script modules that have not been printed before.
    551      *
    552      * @ticket 56313
    553      *
    554      * @covers ::register()
    555      * @covers ::enqueue()
    556      * @covers ::print_script_module_preloads()
    557      */
    558     public function test_print_preloaded_script_modules_can_be_called_multiple_times() {
    559         $this->script_modules->register( 'foo', '/foo.js', array( 'static-dep-1', 'static-dep-2' ) );
    560         $this->script_modules->register( 'bar', '/bar.js', array( 'static-dep-3' ) );
    561         $this->script_modules->register( 'static-dep-1', '/static-dep-1.js' );
    562         $this->script_modules->register( 'static-dep-3', '/static-dep-3.js' );
    563         $this->script_modules->enqueue( 'foo' );
    564 
    565         $preloaded_script_modules = $this->get_preloaded_script_modules();
    566         $this->assertCount( 1, $preloaded_script_modules );
    567         $this->assertTrue( isset( $preloaded_script_modules['static-dep-1'] ) );
    568 
    569         $this->script_modules->register( 'static-dep-2', '/static-dep-2.js' );
    570         $this->script_modules->enqueue( 'bar' );
    571 
    572         $preloaded_script_modules = $this->get_preloaded_script_modules();
    573         $this->assertCount( 2, $preloaded_script_modules );
    574         $this->assertTrue( isset( $preloaded_script_modules['static-dep-2'] ) );
    575         $this->assertTrue( isset( $preloaded_script_modules['static-dep-3'] ) );
    576 
    577         $preloaded_script_modules = $this->get_preloaded_script_modules();
    578         $this->assertCount( 0, $preloaded_script_modules );
    579     }
    580 
    581     /**
    582520     * Tests that a script module is not registered when calling enqueue without a
    583521     * valid src.
Note: See TracChangeset for help on using the changeset viewer.