Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Script Modules API: Move the script modules to the footer in classic themes #5931

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
62 changes: 17 additions & 45 deletions src/wp-includes/class-wp-script-modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,12 @@ public function register( string $id, string $src, array $deps = array(), $versi
'version' => $version,
'enqueue' => isset( $this->enqueued_before_registered[ $id ] ),
'dependencies' => $dependencies,
'enqueued' => false,
'preloaded' => false,
);
}
}

/**
* Marks the script module to be enqueued in the page the next time
* `print_enqueued_script_modules` is called.
* Marks the script module to be enqueued in the page.
*
* If a src is provided and the script module has not been registered yet, it
* will be registered.
Expand Down Expand Up @@ -158,74 +155,49 @@ public function dequeue( string $id ) {
* Adds the hooks to print the import map, enqueued script modules and script
* module preloads.
*
* It adds the actions to print the enqueued script modules and script module
* preloads to both `wp_head` and `wp_footer` because in classic themes, the
* script modules used by the theme and plugins will likely be able to be
* printed in the `head`, but the ones used by the blocks will need to be
* enqueued in the `footer`.
*
* As all script modules are deferred and dependencies are handled by the
* browser, the order of the script modules is not important, but it's still
* better to print the ones that are available when the `wp_head` is rendered,
* so the browser starts downloading those as soon as possible.
*
* The import map is also printed in the footer to be able to include the
* dependencies of all the script modules, including the ones printed in the
* In classic themes, the script modules used by the blocks are not yet known
* when the `wp_head` actions is fired, so it needs to print everything in the
* footer.
*
* @since 6.5.0
*/
public function add_hooks() {
add_action( 'wp_head', array( $this, 'print_enqueued_script_modules' ) );
add_action( 'wp_head', array( $this, 'print_script_module_preloads' ) );
add_action( 'wp_footer', array( $this, 'print_enqueued_script_modules' ) );
add_action( 'wp_footer', array( $this, 'print_script_module_preloads' ) );
add_action( 'wp_footer', array( $this, 'print_import_map' ) );
$position = wp_is_block_theme() ? 'wp_head' : 'wp_footer';
add_action( $position, array( $this, 'print_import_map' ) );
add_action( $position, array( $this, 'print_enqueued_script_modules' ) );
add_action( $position, array( $this, 'print_script_module_preloads' ) );
}

/**
* Prints the enqueued script modules using script tags with type="module"
* attributes.
*
* If a enqueued script module has already been printed, it will not be
* printed again on subsequent calls to this function.
*
* @since 6.5.0
*/
public function print_enqueued_script_modules() {
foreach ( $this->get_marked_for_enqueue() as $id => $script_module ) {
if ( false === $script_module['enqueued'] ) {
// Mark it as enqueued so it doesn't get enqueued again.
$this->registered[ $id ]['enqueued'] = true;

wp_print_script_tag(
array(
'type' => 'module',
'src' => $this->get_versioned_src( $script_module ),
'id' => $id . '-js-module',
)
);
}
wp_print_script_tag(
array(
'type' => 'module',
'src' => $this->get_versioned_src( $script_module ),
'id' => $id . '-js-module',
)
);
}
}

/**
* Prints the the static dependencies of the enqueued script modules using
* link tags with rel="modulepreload" attributes.
*
* If a script module is marked for enqueue, it will not be preloaded. If a
* preloaded script module has already been printed, it will not be printed
* again on subsequent calls to this function.
* If a script module is marked for enqueue, it will not be preloaded.
*
* @since 6.5.0
*/
public function print_script_module_preloads() {
foreach ( $this->get_dependencies( array_keys( $this->get_marked_for_enqueue() ), array( 'static' ) ) as $id => $script_module ) {
// Don't preload if it's marked for enqueue or has already been preloaded.
if ( true !== $script_module['enqueue'] && false === $script_module['preloaded'] ) {
// Mark it as preloaded so it doesn't get preloaded again.
$this->registered[ $id ]['preloaded'] = true;

// Don't preload if it's marked for enqueue.
if ( true !== $script_module['enqueue'] ) {
echo sprintf(
'<link rel="modulepreload" href="%s" id="%s">',
esc_url( $this->get_versioned_src( $script_module ) ),
Expand Down
62 changes: 0 additions & 62 deletions tests/phpunit/tests/script-modules/wpScriptModules.php
Original file line number Diff line number Diff line change
Expand Up @@ -516,68 +516,6 @@ public function test_version_is_propagated_correctly() {
$this->assertEquals( '/dep.js?ver=2.0', $preloaded_script_modules['dep'] );
}

/**
* Tests that it can print the enqueued script modules multiple times, and it
* will only print the script modules that have not been printed before.
*
* @ticket 56313
*
* @covers ::register()
* @covers ::enqueue()
* @covers ::print_enqueued_script_modules()
*/
public function test_print_enqueued_script_modules_can_be_called_multiple_times() {
$this->script_modules->register( 'foo', '/foo.js' );
$this->script_modules->register( 'bar', '/bar.js' );
$this->script_modules->enqueue( 'foo' );

$enqueued_script_modules = $this->get_enqueued_script_modules();
$this->assertCount( 1, $enqueued_script_modules );
$this->assertTrue( isset( $enqueued_script_modules['foo'] ) );

$this->script_modules->enqueue( 'bar' );

$enqueued_script_modules = $this->get_enqueued_script_modules();
$this->assertCount( 1, $enqueued_script_modules );
$this->assertTrue( isset( $enqueued_script_modules['bar'] ) );

$enqueued_script_modules = $this->get_enqueued_script_modules();
$this->assertCount( 0, $enqueued_script_modules );
}

/**
* Tests that it can print the preloaded script modules multiple times, and it
* will only print the script modules that have not been printed before.
*
* @ticket 56313
*
* @covers ::register()
* @covers ::enqueue()
* @covers ::print_script_module_preloads()
*/
public function test_print_preloaded_script_modules_can_be_called_multiple_times() {
$this->script_modules->register( 'foo', '/foo.js', array( 'static-dep-1', 'static-dep-2' ) );
$this->script_modules->register( 'bar', '/bar.js', array( 'static-dep-3' ) );
$this->script_modules->register( 'static-dep-1', '/static-dep-1.js' );
$this->script_modules->register( 'static-dep-3', '/static-dep-3.js' );
$this->script_modules->enqueue( 'foo' );

$preloaded_script_modules = $this->get_preloaded_script_modules();
$this->assertCount( 1, $preloaded_script_modules );
$this->assertTrue( isset( $preloaded_script_modules['static-dep-1'] ) );

$this->script_modules->register( 'static-dep-2', '/static-dep-2.js' );
$this->script_modules->enqueue( 'bar' );

$preloaded_script_modules = $this->get_preloaded_script_modules();
$this->assertCount( 2, $preloaded_script_modules );
$this->assertTrue( isset( $preloaded_script_modules['static-dep-2'] ) );
$this->assertTrue( isset( $preloaded_script_modules['static-dep-3'] ) );

$preloaded_script_modules = $this->get_preloaded_script_modules();
$this->assertCount( 0, $preloaded_script_modules );
}

/**
* Tests that a script module is not registered when calling enqueue without a
* valid src.
Expand Down
Loading