diff --git a/src/wp-includes/class-wp-script-modules.php b/src/wp-includes/class-wp-script-modules.php
index dbfa038f8cbe2..e7b9ad2482280 100644
--- a/src/wp-includes/class-wp-script-modules.php
+++ b/src/wp-includes/class-wp-script-modules.php
@@ -208,10 +208,15 @@ public function add_hooks() {
*/
public function print_enqueued_script_modules() {
foreach ( $this->get_marked_for_enqueue() as $id => $script_module ) {
+ $src = $this->get_src( $id );
+ if ( null === $src ) {
+ continue;
+ }
+
wp_print_script_tag(
array(
'type' => 'module',
- 'src' => $this->get_src( $id ),
+ 'src' => $src,
'id' => $id . '-js-module',
)
);
@@ -228,11 +233,16 @@ public function print_enqueued_script_modules() {
*/
public function print_script_module_preloads() {
foreach ( $this->get_dependencies( array_keys( $this->get_marked_for_enqueue() ), array( 'static' ) ) as $id => $script_module ) {
+ $src = $this->get_src( $id );
+ if ( null === $src ) {
+ continue;
+ }
+
// Don't preload if it's marked for enqueue.
if ( true !== $script_module['enqueue'] ) {
echo sprintf(
'',
- esc_url( $this->get_src( $id ) ),
+ esc_url( $src ),
esc_attr( $id . '-js-modulepreload' )
);
}
@@ -262,14 +272,53 @@ public function print_import_map() {
*
* @since 6.5.0
*
- * @return array Array with an `imports` key mapping to an array of script module identifiers and their respective
- * URLs, including the version query.
+ * @global WP_Dependencies $wp_scripts
+ *
+ * @return array Array with an `imports` key mapping to an array of script
+ * module identifiers and their respective URLs, including
+ * the version query.
*/
private function get_import_map(): array {
+ global $wp_scripts;
+
$imports = array();
- foreach ( $this->get_dependencies( array_keys( $this->get_marked_for_enqueue() ) ) as $id => $script_module ) {
- $imports[ $id ] = $this->get_src( $id );
+
+ $classic_script_dependencies = array();
+ if ( $wp_scripts instanceof WP_Scripts ) {
+ foreach ( $wp_scripts->registered as $dependency ) {
+ $handle = $dependency->handle;
+
+ if (
+ ! $wp_scripts->query( $handle, 'done' ) &&
+ ! $wp_scripts->query( $handle, 'to_do' ) &&
+ ! $wp_scripts->query( $handle, 'enqueued' )
+ ) {
+ continue;
+ }
+
+ $module_deps = $wp_scripts->get_data( $handle, 'module_deps' );
+ if ( ! $module_deps ) {
+ continue;
+ }
+ foreach ( $module_deps as $id ) {
+ $src = $this->get_src( $id );
+ if ( null === $src ) {
+ continue;
+ }
+ $imports[ $id ] = $src;
+ $classic_script_dependencies[] = $id;
+ }
+ }
}
+
+ foreach ( $this->get_dependencies( array_merge( $classic_script_dependencies, array_keys( $this->get_marked_for_enqueue() ) ) ) as $id => $script_module ) {
+ $src = $this->get_src( $id );
+ if ( null === $src ) {
+ continue;
+ }
+ $imports[ $id ] = $src;
+ }
+
return array( 'imports' => $imports );
}
@@ -335,11 +384,11 @@ function ( $dependency_script_modules, $id ) use ( $import_types ) {
* @since 6.5.0
*
* @param string $id The script module identifier.
- * @return string The script module src with a version if relevant.
+ * @return string|null The script module src with a version if relevant.
*/
- private function get_src( string $id ): string {
+ private function get_src( string $id ): ?string {
if ( ! isset( $this->registered[ $id ] ) ) {
- return '';
+ return null;
}
$script_module = $this->registered[ $id ];
diff --git a/src/wp-includes/class-wp-scripts.php b/src/wp-includes/class-wp-scripts.php
index 77dff94c0497a..29ae293350974 100644
--- a/src/wp-includes/class-wp-scripts.php
+++ b/src/wp-includes/class-wp-scripts.php
@@ -150,6 +150,58 @@ public function __construct() {
add_action( 'init', array( $this, 'init' ), 0 );
}
+ /**
+ * Register an item.
+ *
+ * Registers the item if no item of that name already exists.
+ *
+ * This method is subclassed here in order to add special handling for script module
+ * dependencies.
+ *
+ * @since 6.8.0
+ *
+ * @see WP_Dependencies::add()
+ *
+ * @param string $handle Name of the item. Should be unique.
+ * @param string|false $src Full URL of the item, or path of the item relative
+ * to the WordPress root directory. If source is set to false,
+ * the item is an alias of other items it depends on.
+ * @param (string|array{type: string, id: string})[] $deps Optional. An array of registered item handles this item depends on.
+ * Default empty array.
+ * @param string|bool|null $ver Optional. String specifying item version number, if it has one,
+ * which is added to the URL as a query string for cache busting purposes.
+ * If version is set to false, a version number is automatically added
+ * equal to current installed WordPress version.
+ * If set to null, no version is added.
+ * @param mixed $args Optional. Custom property of the item. NOT the class property $args.
+ * Examples: $media, $in_footer.
+ * @return bool Whether the item has been registered. True on success, false on failure.
+ */
+ public function add( $handle, $src, $deps = array(), $ver = false, $args = null ) {
+ $module_deps = array();
+ $script_deps = array();
+ if ( array() !== $deps ) {
+ foreach ( $deps as $dep ) {
+ if ( is_string( $dep ) ) {
+ $script_deps[] = $dep;
+ } elseif (
+ isset( $dep['type'], $dep['id'] ) &&
+ 'module' === $dep['type'] &&
+ is_string( $dep['id'] )
+ ) {
+ $module_deps[] = $dep['id'];
+ }
+ }
+ }
+ if ( ! parent::add( $handle, $src, $script_deps, $ver, $args ) ) {
+ return false;
+ }
+ if ( array() !== $module_deps ) {
+ $this->add_data( $handle, 'module_deps', $module_deps );
+ }
+ return true;
+ }
+
/**
* Initialize the class.
*
diff --git a/tests/phpunit/tests/script-modules/wpScriptModules.php b/tests/phpunit/tests/script-modules/wpScriptModules.php
index 85f9599f0dac3..a44317d598ecc 100644
--- a/tests/phpunit/tests/script-modules/wpScriptModules.php
+++ b/tests/phpunit/tests/script-modules/wpScriptModules.php
@@ -27,14 +27,44 @@ public function set_up() {
parent::set_up();
// Set up the WP_Script_Modules instance.
$this->script_modules = new WP_Script_Modules();
+ unset( $GLOBALS['wp_scripts'] );
}
+ private static $wp_scripts;
+ private static $wp_scripts_was_set = false;
+
+ public static function set_up_before_class() {
+ parent::set_up_before_class();
+
+ // If the global is set, store it for restoring when done testing.
+ static::$wp_scripts_was_set = array_key_exists( 'wp_scripts', $GLOBALS );
+ if ( static::$wp_scripts_was_set ) {
+ static::$wp_scripts = $GLOBALS['wp_scripts'];
+ unset( $GLOBALS['wp_scripts'] );
+ }
+ }
+
+ public static function tear_down_after_class() {
+ // Restore the global if it was set before running this set of tests.
+ if ( static::$wp_scripts_was_set ) {
+ $GLOBALS['wp_scripts'] = static::$wp_scripts;
+ }
+
+ parent::tear_down_after_class();
+ }
+
+ public function clean_up_global_scope() {
+ unset( $GLOBALS['wp_scripts'] );
+ parent::clean_up_global_scope();
+ }
+
+
/**
* Gets a list of the enqueued script modules.
*
* @return array Enqueued script module URLs, keyed by script module identifier.
*/
- public function get_enqueued_script_modules() {
+ private function get_enqueued_script_modules() {
$script_modules_markup = get_echo( array( $this->script_modules, 'print_enqueued_script_modules' ) );
$p = new WP_HTML_Tag_Processor( $script_modules_markup );
$enqueued_script_modules = array();
@@ -54,10 +84,12 @@ public function get_enqueued_script_modules() {
*
* @return array Import map entry URLs, keyed by script module identifier.
*/
- public function get_import_map() {
+ private function get_import_map() {
$import_map_markup = get_echo( array( $this->script_modules, 'print_import_map' ) );
preg_match( '/