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.
Built from https://develop.svn.wordpress.org/trunk@61587
git-svn-id: http://core.svn.wordpress.org/trunk@60898 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Improve dependency warning messages so that list separators are localized according to the current locale when multiple dependencies are listed.
Follow-up to [61323], [60999], [61357].
Props mukeshpanchal27, jorbin, westonruter, wildworks.
See #64229.
Built from https://develop.svn.wordpress.org/trunk@61542
git-svn-id: http://core.svn.wordpress.org/trunk@60853 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This brings API parity with `WP_Scripts` by implementing opt-in support for printing in the footer via an `in_footer` argument. This argument can be supplied via the `$args` array passed to `wp_enqueue_script_module()` or `wp_register_script_module()`, alongside the existing `fetchpriority` key introduced in #61734. It can also be set for previously-registered script modules via `WP_Script_Modules::set_in_footer()`. This is not applicable to classic themes since modules are enqueued while blocks are rendered after `wp_head` has completed, so all script modules are printed in the footer anyway; the `importmap` script must be printed after all script modules have been enqueued.
Script modules used for interactive blocks (with the Interactivity API) are automatically printed in the footer. Such script modules should be deprioritized because they are not in the critical rendering path due to interactive blocks using server-side rendering. Script modules remain printed at `wp_head` by default, although this default should be revisited since they have deferred execution (and they are printed in the footer for classic themes already, as previously noted). Moving a script module to the footer ensures that its loading does not contend with the loading of critical resources, such as the LCP element's image resource, and LCP is improved as a result.
This also improves specificity of some PHP types, it ensures that script modules can't be registered with an empty ID, and it prevents printing script modules with empty `src` URLs.
Developed in https://github.com/WordPress/wordpress-develop/pull/9867
Follow-up to [60704].
Props b1ink0, westonruter, jonsurrell, peterwilsoncc, vipulpatil, mindctrl.
See #61734.
Fixes#63486.
Built from https://develop.svn.wordpress.org/trunk@60999
git-svn-id: http://core.svn.wordpress.org/trunk@60335 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This eliminates constant emptying out of the queues for styles, scripts, and script modules before rendering each block. This ensures that `wp_script_is()`/`wp_style_is()` will return true for assets that are actually enqueued. The `WP_Script_Modules::$queue` member which was made public in [60930] is now made private in favor of a `WP_Script_Modules::get_queue()` method, since there is no need to clear out the queue before rendering each block and restore after the rendering is complete.
Finally, as a very special case for unusual blocks which contain `wp_head()`, a check is done to see if the `wp_enqueue_scripts` action occurred during the rendering of a block; if so, then no assets will be dequeued even if no markup is rendered in the block, since it may be that a script was enqueued for the footer and not the head.
Developed in https://github.com/WordPress/wordpress-develop/pull/10252
Follow-up to [60930].
Props westonruter, dd32, peterwilsoncc, nikunj8866, krupajnanda.
Fixes#63676.
Built from https://develop.svn.wordpress.org/trunk@60951
git-svn-id: http://core.svn.wordpress.org/trunk@60287 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This introduces a "fetchpriority bumping" mechanism for both classic scripts (`WP_Scripts`) and script modules (`WP_Script_Modules`). When a script with a higher `fetchpriority` is enqueued, any of its dependencies will have their `fetchpriority` elevated to match that of the highest-priority dependent. This ensures that all assets in a critical dependency chain are loaded with the appropriate priority, preventing a high-priority script from being blocked by a low-priority dependency. This is similar to logic used in script loading strategies to ensure that a blocking dependent causes delayed (`async`/`defer`) dependencies to also become blocking. See #12009.
When a script's `fetchpriority` is escalated, its original, registered priority is added to the tag via a `data-wp-fetchpriority` attribute. This matches the addition of the `data-wp-strategy` parameter added when the resulting loading strategy does not match the original.
Developed in https://github.com/WordPress/wordpress-develop/pull/9770.
Follow-up to [60704].
Props westonruter, jonsurrell.
Fixes#61734.
Built from https://develop.svn.wordpress.org/trunk@60931
git-svn-id: http://core.svn.wordpress.org/trunk@60267 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This change prevents scripts, styles, and script modules from being enqueued for blocks that do not render any HTML content. This is common for hidden blocks or blocks like the Featured Image block when no image is present. This change reduces the amount of unused CSS and JavaScript on a page, improving performance.
A new filter, `enqueue_empty_block_content_assets`, is introduced to allow developers to override this behavior and enqueue assets for empty blocks if needed.
The implementation involves capturing the asset queues before and after a block is rendered. The newly enqueued assets are only merged if the block's rendered content is not empty. This is done recursively for nested blocks to ensure that assets for inner blocks are also not enqueued if a parent block is hidden.
Developed in https://github.com/WordPress/wordpress-develop/pull/9213.
Props westonruter, aristath, peterwilsoncc, gziolo, krupajnanda, dd32, jorbin.
See #50328.
Fixes#63676.
Built from https://develop.svn.wordpress.org/trunk@60930
git-svn-id: http://core.svn.wordpress.org/trunk@60266 1a063a9b-81f0-0310-95a4-ce76da25c4cd
* Allow scripts and script modules to be registered with a `fetchpriority` of `auto` (default), `high`, `low`:
* When registering a script, add a `fetchpriority` arg to go alongside the `strategy` arg which was added for loading scripts with the `defer` and `async` loading strategies. See #12009.
* For script modules, introduce an `$args` array parameter with a `fetchpriority` key to the `wp_register_script_module()`, and `wp_enqueue_script_module()` functions (and their respective underlying `WP_Script_Modules::register()` and `WP_Script_Modules::enqueue()` methods). This `$args` parameter corresponds with the same parameter used when registering non-module scripts.
* Also for script modules, introduce `WP_Script_Modules::set_fetchpriority()` to override the `fetchpriority` for what was previously registered.
* Emit a `_doing_it_wrong()` warning when an invalid `fetchpriority` value is used, and when `fetchpriority` is added to a script alias.
* Include `fetchpriority` as an attribute on printed `SCRIPT` tags as well as on preload `LINK` tags for static script module dependencies.
* Use a `fetchpriority` of `low` by default for:
* Script modules used with the Interactivity API. For overriding this default in blocks, see [https://github.com/WordPress/gutenberg/issues/71366 Gutenberg#71366].
* The `comment-reply` script.
* Improve type checks and type hints.
Developed in [https://github.com/WordPress/wordpress-develop/pull/8815 GitHub PR], with [https://github.com/WordPress/gutenberg/pull/70173 companion for Gutenberg].
Props westonruter, jonsurrell, swissspidy, luisherranz, kraftbj, audrasjb, dennysdionigi.
Fixes#61734.
Built from https://develop.svn.wordpress.org/trunk@60704
git-svn-id: http://core.svn.wordpress.org/trunk@60040 1a063a9b-81f0-0310-95a4-ce76da25c4cd
The Script Module has the same API as the `wp-a11y` WP Script.
Key changes:
- Add `@wordpress/a11y` to the list of Script and Module dual packages.
- Update `script-modules-packages.min.php` to include the a11y module.
- Modify `WP_Script_Modules` class to track and handle a11y module availability.
- Add method to print required HTML markup for a11y `speak()` functionality.
See #60647.
Props jonsurrell, gziolo, czapla.
Built from https://develop.svn.wordpress.org/trunk@59089
git-svn-id: http://core.svn.wordpress.org/trunk@58485 1a063a9b-81f0-0310-95a4-ce76da25c4cd
The polyfill was added in [57492], but all browsers supported by WordPress already support import maps.
This not only disables the polyfill, but completely removes it as it was only added recently and there is no usage outside of core.
Props swissspidy, desrosj, luisherranz, gziolo.
Fixes#60970.
Built from https://develop.svn.wordpress.org/trunk@58952
git-svn-id: http://core.svn.wordpress.org/trunk@58348 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Add a new filter `script_module_data_{$module_id}` to associate data
with a Script Module. For example:
{{{#!php
add_filter(
'script_module_data_MyScriptModuleID',
function ( array $data ): array {
$data['script-needs-this-data'] = 'ok';
return $data;
}
);
}}}
If the Script Module is included in the page, enqueued or as a
dependency, the associated data will be JSON-encoded and embedded in the
HTML in a `<script type="application/json">` tag with an ID of the form
`wp-script-module-data-{$module_id}` allowing the Script Module to
access the data on the client.
See the original proposal: https://make.wordpress.org/core/2024/05/06/proposal-server-to-client-data-sharing-for-script-modules/
Developed in https://github.com/WordPress/wordpress-develop/pull/6682.
Props jonsurrell, cbravobernal, westonruter, gziolo, bernhard-reiter, youknowriad, sergiomdgomes, czapla.
Fixes#61510. See #60647.
Built from https://develop.svn.wordpress.org/trunk@58579
git-svn-id: http://core.svn.wordpress.org/trunk@58026 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Script Modules cannot be used in wp-admin. The necessary hooks are registered on wp_head or wp_footer, but should also be registered for the admin variants so that modules can be used from wp-admin.
Fixes#61086.
Props jonsurrell, cbravobernal, gziolo.
Built from https://develop.svn.wordpress.org/trunk@58126
git-svn-id: http://core.svn.wordpress.org/trunk@57591 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Ensures parity with the `script_loader_src` filter for regular scripts, allowing the URL to be filtered, for example to load them from a CDN or alter query parameters.
Props dd32, peterwilsoncc, westonruter.
Fixes#60742.
Built from https://develop.svn.wordpress.org/trunk@57840
git-svn-id: http://core.svn.wordpress.org/trunk@57341 1a063a9b-81f0-0310-95a4-ce76da25c4cd
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.
Built from https://develop.svn.wordpress.org/trunk@57345
git-svn-id: http://core.svn.wordpress.org/trunk@56851 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Renames all mentions to "module" with "script module", including function names, comments, and tests.
Follow up to [57269]
The list of functions renamed are:
- `wp_module()` -> `wp_script_module()`.
- `wp_register_module()` -> `wp_register_script_module()`.
- `wp_enqueue_module()` -> `wp_enqueue_script_module()`.
- `wp_dequeue_module()` -> `wp_dequeue_script_module()`.
- `WP_Script_Modules::print_enqueued_modules()` -> `WP_Script_Modules::print_enqueued_script_modules()`.
- `WP_Script_Modules::print_module_preloads()` -> `WP_Script_Modules::print_script_module_preloads()`.
It also adds PHP 7 typing to all the functions and improves the types of the `$deps` argument of `wp_register_script_module()` and `wp_enqueue_script_module()` using `@type`.
Props luisherranz, idad5, costdev, nefff, joemcgill, jorbin, swisspidy, jonsurrel, flixos90, gziolo, westonruter, bernhard-reiter, kamranzafar4343
See #56313
Built from https://develop.svn.wordpress.org/trunk@57327
git-svn-id: http://core.svn.wordpress.org/trunk@56833 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This changeset adds a new API for WordPress, designed to work with native ES Modules and Import Maps. It introduces functions such as `wp_register_module`, and `wp_enqueue_module`.
The API aims to provide a familiar experience to the existing `WP_Scripts` class, offering similar functionality. However, **it's not intended to duplicate the exact functionality of `WP_Scripts`**; rather, it is carefully tailored to address the specific needs and capabilities of ES modules.
For this initial version, **the current proposal is intentionally simplistic**, covering only the essential features needed to work with ES modules. Other enhancements and optimizations can be added later as the community identifies additional requirements and use cases.
== Differences Between WP_Script_Modules and WP_Scripts
=== Dependency Specification
With `WP_Script_Modules`, the array of dependencies supports not only strings but also arrays that include the dependency import type (`static` or `dynamic`). This design choice allows for future extensions of dependency properties, such as adding a `version` property to support "scopes" within import maps.
=== Module Identifier
Instead of a handle, `WP_Script_Modules` utilizes the module identifier, aligning with the module identifiers used in JavaScript files and import maps.
=== Deregistration
There is no equivalent of `wp_deregister_script` at this stage.
== API
=== `wp_register_module( $module_identifier, $src, $deps, $version )`
Registers a module.
{{{
// Registers a module with dependencies and versioning.
wp_register_module(
'my-module',
'/path/to/my-module.js',
array( 'static-dependency-1', 'static-dependency-2' ),
'1.2.3'
);
}}}
{{{
// my-module.js
import { ... } from 'static-dependency-1';
import { ... } from 'static-dependency-2';
// ...
}}}
{{{
// Registers a module with a dynamic dependency.
wp_register_module(
'my-module',
'/path/to/my-module.js',
array(
'static-dependency',
array(
'id' => 'dynamic-dependency',
'import' => 'dynamic'
),
)
);
}}}
{{{
// my-module.js
import { ... } from 'static-dependency';
// ...
const dynamicModule = await import('dynamic-dependency');
}}}
=== `wp_enqueue_module( $module_identifier, $src, $deps, $version )`
Enqueues a module. If a source is provided, it will also register the module.
{{{
wp_enqueue_module( 'my-module' );
}}}
=== `wp_dequeue_module( $module_identifier )`
Dequeues a module.
{{{
wp_dequeue_module( 'my-module' );
}}}
== Output
- When modules are enqueued, they are printed within script tags containing `type="module"` attributes.
- Additionally, static dependencies of enqueued modules utilize `link` tags with `rel="modulepreload"` attributes.
- Lastly, an import map is generated and inserted using a `<script type="importmap">` tag.
{{{
<script type="module" src="/path/to/my-module.js" id="my-module"></script>
<link rel="modulepreload" href="/path/to/static-dependency.js" id="static-dependency" />
<script type="importmap">
{
"imports": {
"static-dependency": "/path/to/static-dependency.js",
"dynamic-dependency": "/path/to/dynamic-dependency.js"
}
}
</script>
}}}
== Import Map Polyfill Requirement
Even though all major browsers already support import maps, an import map polyfill is required until the percentage of users using old browser versions without import map support drops significantly.
This work is ongoing and will be added once it's ready. Progress is tracked in #60232.
Props luisherranz, idad5, costdev, neffff, joemcgill, jorbin, swissspidy, jonsurrell, flixos90, gziolo, westonruter.
Fixes#56313.
Built from https://develop.svn.wordpress.org/trunk@57269
git-svn-id: http://core.svn.wordpress.org/trunk@56775 1a063a9b-81f0-0310-95a4-ce76da25c4cd