Make WordPress Core


Ignore:
Timestamp:
02/04/2026 01:41:25 AM (8 months ago)
Author:
westonruter
Message:

Script Loader: Allow classic scripts to depend on script modules.

This allows classic scripts to declare dependencies on script modules by passing module_dependencies in the $args param for wp_register_script() or wp_enqueue_script(). The WP_Script_Modules::get_import_map() method is updated to traverse the dependency tree of all enqueued classic scripts to find any associated script module dependencies and include them in the importmap, enabling dynamic imports of modules within classic scripts.

A _wp_scripts_add_args_data() helper function is introduced to consolidate argument validation and processing for wp_register_script() and wp_enqueue_script(), reducing code duplication. This function validates that the $args array only contains recognized keys (strategy, in_footer, fetchpriority, module_dependencies) and triggers a _doing_it_wrong() notice for any unrecognized keys. Similarly, WP_Scripts::add_data() is updated to do early type checking for the data passed to $args. The script modules in module_dependencies may be referenced by a module ID string or by an array that has an id key, following the same pattern as dependencies in WP_Script_Modules.

When a script module is added to the module_dependencies for a classic script, but it does not exist at the time the importmap is printed, a _doing_it_wrong() notice is emitted.

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

Follow-up to [61323].

Props sirreal, westonruter.
See #64229.
Fixes #61500.

File:
1 edited

Legend:

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

    r61542 r61587  
    8989         * @since 6.9.0 Added the $args parameter.
    9090         *
    91          * @param string                     $id       The identifier of the script module. Should be unique. It will be used in the
    92          *                                             final import map.
    93          * @param string                     $src      Optional. Full URL of the script module, or path of the script module relative
    94          *                                             to the WordPress root directory. If it is provided and the script module has
    95          *                                             not been registered yet, it will be registered.
    96          * @param array<string|array>        $deps     {
    97          *                                                 Optional. List of dependencies.
    98          *
    99          *                                                 @type string|array ...$0 {
    100          *                                                     An array of script module identifiers of the dependencies of this script
    101          *                                                     module. The dependencies can be strings or arrays. If they are arrays,
    102          *                                                     they need an `id` key with the script module identifier, and can contain
    103          *                                                     an `import` key with either `static` or `dynamic`. By default,
    104          *                                                     dependencies that don't contain an `import` key are considered static.
    105          *
    106          *                                                     @type string $id     The script module identifier.
    107          *                                                     @type string $import Optional. Import type. May be either `static` or
    108          *                                                                          `dynamic`. Defaults to `static`.
    109          *                                                 }
    110          *                                             }
    111          * @param string|false|null          $version Optional. String specifying the script module version number. Defaults to false.
    112          *                                             It is added to the URL as a query string for cache busting purposes. If $version
    113          *                                             is set to false, the version number is the currently installed WordPress version.
    114          *                                             If $version is set to null, no version is added.
    115          * @param array<string, string|bool> $args     {
     91         * @param string                              $id      The identifier of the script module. Should be unique. It will be used in the
     92         *                                                     final import map.
     93         * @param string                              $src     Optional. Full URL of the script module, or path of the script module relative
     94         *                                                     to the WordPress root directory. If it is provided and the script module has
     95         *                                                     not been registered yet, it will be registered.
     96         * @param array<string|array<string, string>> $deps    {
     97         *                                                         Optional. List of dependencies.
     98         *
     99         *                                                         @type string|array<string, string> ...$0 {
     100         *                                                             An array of script module identifiers of the dependencies of this script
     101         *                                                             module. The dependencies can be strings or arrays. If they are arrays,
     102         *                                                             they need an `id` key with the script module identifier, and can contain
     103         *                                                             an `import` key with either `static` or `dynamic`. By default,
     104         *                                                             dependencies that don't contain an `import` key are considered static.
     105         *
     106         *                                                             @type string $id     The script module identifier.
     107         *                                                             @type string $import Optional. Import type. May be either `static` or
     108         *                                                                                  `dynamic`. Defaults to `static`.
     109         *                                                         }
     110         *                                                     }
     111         * @param string|false|null                   $version Optional. String specifying the script module version number. Defaults to false.
     112         *                                                     It is added to the URL as a query string for cache busting purposes. If $version
     113         *                                                     is set to false, the version number is the currently installed WordPress version.
     114         *                                                     If $version is set to null, no version is added.
     115         * @param array<string, string|bool>          $args    {
    116116         *     Optional. An array of additional args. Default empty array.
    117117         *
    … …  
    261261         * @since 6.9.0 Added the $args parameter.
    262262         *
    263          * @param string                     $id       The identifier of the script module. Should be unique. It will be used in the
    264          *                                             final import map.
    265          * @param string                     $src      Optional. Full URL of the script module, or path of the script module relative
    266          *                                             to the WordPress root directory. If it is provided and the script module has
    267          *                                             not been registered yet, it will be registered.
    268          * @param array<string|array>        $deps     {
    269          *                                                 Optional. List of dependencies.
    270          *
    271          *                                                 @type string|array ...$0 {
    272          *                                                     An array of script module identifiers of the dependencies of this script
    273          *                                                     module. The dependencies can be strings or arrays. If they are arrays,
    274          *                                                     they need an `id` key with the script module identifier, and can contain
    275          *                                                     an `import` key with either `static` or `dynamic`. By default,
    276          *                                                     dependencies that don't contain an `import` key are considered static.
    277          *
    278          *                                                     @type string $id     The script module identifier.
    279          *                                                     @type string $import Optional. Import type. May be either `static` or
    280          *                                                                          `dynamic`. Defaults to `static`.
    281          *                                                 }
    282          *                                             }
    283          * @param string|false|null          $version Optional. String specifying the script module version number. Defaults to false.
    284          *                                             It is added to the URL as a query string for cache busting purposes. If $version
    285          *                                             is set to false, the version number is the currently installed WordPress version.
    286          *                                             If $version is set to null, no version is added.
    287          * @param array<string, string|bool> $args     {
     263         * @param string                              $id      The identifier of the script module. Should be unique. It will be used in the
     264         *                                                     final import map.
     265         * @param string                              $src     Optional. Full URL of the script module, or path of the script module relative
     266         *                                                     to the WordPress root directory. If it is provided and the script module has
     267         *                                                     not been registered yet, it will be registered.
     268         * @param array<string|array<string, string>> $deps    {
     269         *                                                         Optional. List of dependencies.
     270         *
     271         *                                                         @type string|array<string, string> ...$0 {
     272         *                                                             An array of script module identifiers of the dependencies of this script
     273         *                                                             module. The dependencies can be strings or arrays. If they are arrays,
     274         *                                                             they need an `id` key with the script module identifier, and can contain
     275         *                                                             an `import` key with either `static` or `dynamic`. By default,
     276         *                                                             dependencies that don't contain an `import` key are considered static.
     277         *
     278         *                                                             @type string $id     The script module identifier.
     279         *                                                             @type string $import Optional. Import type. May be either `static` or
     280         *                                                                                  `dynamic`. Defaults to `static`.
     281         *                                                         }
     282         *                                                     }
     283         * @param string|false|null                   $version Optional. String specifying the script module version number. Defaults to false.
     284         *                                                     It is added to the URL as a query string for cache busting purposes. If $version
     285         *                                                     is set to false, the version number is the currently installed WordPress version.
     286         *                                                     If $version is set to null, no version is added.
     287         * @param array<string, string|bool>          $args    {
    288288         *     Optional. An array of additional args. Default empty array.
    289289         *
    … …  
    534534         *
    535535         * @since 6.5.0
     536         * @since 7.0.0 Script module dependencies ('module_dependencies') of classic scripts are now included.
     537         *
     538         * @global WP_Scripts $wp_scripts
    536539         *
    537540         * @return array<string, array<string, string>> Array with an `imports` key mapping to an array of script module
    … …  
    539542         */
    540543        private function get_import_map(): array {
     544                global $wp_scripts;
     545
    541546                $imports = array();
    542                 foreach ( array_keys( $this->get_dependencies( $this->queue ) ) as $id ) {
     547
     548                // Identify script modules that are dependencies of classic scripts.
     549                $classic_script_module_dependencies = array();
     550                if ( $wp_scripts instanceof WP_Scripts ) {
     551                        $handles = array_merge(
     552                                $wp_scripts->queue,
     553                                $wp_scripts->to_do,
     554                                $wp_scripts->done
     555                        );
     556
     557                        $processed = array();
     558                        while ( ! empty( $handles ) ) {
     559                                $handle = array_pop( $handles );
     560                                if ( isset( $processed[ $handle ] ) || ! isset( $wp_scripts->registered[ $handle ] ) ) {
     561                                        continue;
     562                                }
     563                                $processed[ $handle ] = true;
     564
     565                                $module_dependencies = $wp_scripts->get_data( $handle, 'module_dependencies' );
     566                                if ( is_array( $module_dependencies ) ) {
     567                                        $missing_module_dependencies = array();
     568                                        foreach ( $module_dependencies as $module ) {
     569                                                if ( is_string( $module ) ) {
     570                                                        $id = $module;
     571                                                } elseif ( is_array( $module ) && isset( $module['id'] ) && is_string( $module['id'] ) ) {
     572                                                        $id = $module['id'];
     573                                                } else {
     574                                                        // Invalid module dependency was supplied by direct manipulation of the extra data.
     575                                                        // Normally, this error scenario would be caught when WP_Scripts::add_data() is called.
     576                                                        continue;
     577                                                }
     578
     579                                                if ( ! isset( $this->registered[ $id ] ) ) {
     580                                                        $missing_module_dependencies[] = $id;
     581                                                } else {
     582                                                        $classic_script_module_dependencies[] = $id;
     583                                                }
     584                                        }
     585
     586                                        if ( count( $missing_module_dependencies ) > 0 ) {
     587                                                _doing_it_wrong(
     588                                                        'WP_Scripts::add_data',
     589                                                        sprintf(
     590                                                                /* translators: 1: Script handle, 2: 'module_dependencies', 3: List of missing dependency IDs. */
     591                                                                __( 'The script with the handle "%1$s" was enqueued with script module dependencies ("%2$s") that are not registered: %3$s.' ),
     592                                                                $handle,
     593                                                                'module_dependencies',
     594                                                                implode( wp_get_list_item_separator(), $missing_module_dependencies )
     595                                                        ),
     596                                                        '7.0.0'
     597                                                );
     598                                        }
     599                                }
     600
     601                                foreach ( $wp_scripts->registered[ $handle ]->deps as $dep ) {
     602                                        if ( ! isset( $processed[ $dep ] ) ) {
     603                                                $handles[] = $dep;
     604                                        }
     605                                }
     606                        }
     607                }
     608
     609                // Note: the script modules in $this->queue are not included in the importmap because they get printed as scripts.
     610                $ids = array_unique(
     611                        array_merge(
     612                                $classic_script_module_dependencies,
     613                                array_keys( $this->get_dependencies( array_merge( $this->queue, $classic_script_module_dependencies ) ) )
     614                        )
     615                );
     616                foreach ( $ids as $id ) {
    543617                        $src = $this->get_src( $id );
    544618                        if ( '' !== $src ) {
Note: See TracChangeset for help on using the changeset viewer.