Make WordPress Core

Opened 12 months ago

Closed 2 months ago

#62435 closed defect (bug) (invalid)

Preload script modules and enqueue script modules in wrong order

Reported by: yogeshbhutkar's profile yogeshbhutkar Owned by:
Milestone: Priority: normal
Severity: normal Version: 6.5
Component: Script Loader Keywords: has-patch reporter-feedback close
Focuses: Cc:

Description

Issue:

In WP_Script_Modules, there is this order of output:

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' ) );

This is adding a preload after printing script modules. This is not always a problem, but can cause a race condition if assets are stored in the browser or served quickly. Dependencies may not load before dependent scripts.

Steps to reproduce the issue:

  1. Add a script module with dependencies marked as "static"
<?php
// instead, use a static loader that injects the script at runtime.
$static_assets = include trailingslashit( plugin_dir_path( __FILE__ ) ) . 'build/scripts/test/index.asset.php';
wp_register_script_module(
	'@my-plugin/test',
	trailingslashit( plugin_dir_url( __FILE__ ) ) . 'build/scripts/test/index.js',
	array(
		array(
			'id'     => '@wordpress/interactivity',
			'import' => 'static',
		),
	),
	$static_assets['version']
);
  1. View the markup on the page source:
<script type="module" src="https://mysite.com/wp-content/plugins/my-plugin/build/scripts/test/index.js?ver=0d40f7f88c7660867299" id="@my-plugin/test-js-module"></script>
<link rel="modulepreload" href="https://mysite.com/wp-includes/js/dist/script-modules/interactivity/debug.js?ver=d1c1c7faff86314c361a" id="@wordpress/interactivity-js-modulepreload">

The expected output should be the opposite, since the module requires @wordpress/interactivity.

<link rel="modulepreload" href="https://mysite.com/wp-includes/js/dist/script-modules/interactivity/debug.js?ver=d1c1c7faff86314c361a" id="@wordpress/interactivity-js-modulepreload">
<script type="module" src="https://mysite.com/wp-content/plugins/my-plugin/build/scripts/test/index.js?ver=0d40f7f88c7660867299" id="@my-plugin/test-js-module"></script>

@ajgagnon reported this issue earlier in the Gutenberg repo. We'd like to give him credit for sharing it with us.

Original Issue Link: https://github.com/WordPress/gutenberg/issues/66982

Thank You @ajgagnon.

Change History (9)

#1 @yogeshbhutkar
12 months ago

As per the issue, I was able to replicate the bug. The modulepreload is enlisted after loading module scripts. Whereas, I believe, the preload should happen before. Source: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/modulepreload

This ticket was mentioned in PR #7808 on WordPress/wordpress-develop by @yogeshbhutkar.


12 months ago
#2

  • Keywords has-patch added

…printed after printing preload scripts

This PR fixes the bug where the preload scripts are included in the head after the module imports which ideally should be reversed.

Trac ticket: https://core.trac.wordpress.org/ticket/62435#comment:1

This ticket was mentioned in Slack in #core by desrosj. View the logs.


12 months ago

#4 @desrosj
12 months ago

  • Version changed from trunk to 6.5

#5 @jonsurrell
12 months ago

Hello, and thanks for the report!

First, I'd like to clarify. Is there a bug that's been observed or is this describing a hypothetical race condition that could lead to a bug?

I don't believe there's an issue with the ordering, but if there's a reproducible bug I'd like to investigate further. Modules work very differently from "classic" scripts.

The modulepreload that are mentioned are a included for performance reasons, they are not strictly necessary. If they are removed the JavaScript modules should continue to work without issue. Module preloads do not cause JavaScript to be executed, they fetch the referenced modules and prepare them for use.

If the preloads are removed, the modules will still fetch their dependencies, but it may happen later because the browser will need to discover, fetch, and process the dependency modules inside the JavaScript modules themselves.

I believe ordering as they are (enqueued modules first, preloads second) are because the enqueued modules are higher priority. They're the main modules that are expected to be executed when the page is loaded. The preloads hint at the dependencies that will be statically required so that the browser can also fetch them and get them ready (before discovering them in JavaScript source).

Please let me know if I misunderstood this ticket or there are more details!

#6 @desrosj
12 months ago

  • Keywords reporter-feedback added

#7 @yogeshbhutkar
12 months ago

Thank you, @jonsurrell, for your detailed explanation.

The final points you mentioned were particularly helpful in addressing the query. Since this issue was originally raised by another contributor on the Gutenberg GitHub repository, I will incorporate my understanding of the problem from their perspective while presenting my response.

As you pointed out, the primary rationale for prioritizing module imports was their higher importance in the overall execution flow. This clarification significantly resolves the query.

The confusion likely stemmed from the general convention of importing preloads before modules, which is a widely recognized standard practice.

I'll be tagging the original author on GitHub and request him to provide his views for further clarity in case I missed something.

Thanks.

#8 @jonsurrell
12 months ago

  • Keywords close added

#9 @jonsurrell
2 months ago

  • Milestone Awaiting Review deleted
  • Resolution set to invalid
  • Status changed from new to closed
Note: See TracTickets for help on using tickets.