Skip to content

Conversation

@sirreal
Copy link
Member

@sirreal sirreal commented Dec 19, 2024

This change allows classic scripts to declare dependencies on Script Modules.

Classic scripts can declare module dependencies by passing a module_dependencies key in the $args array of wp_register_script() or wp_enqueue_script().

In order for a classic script to have access to a script module, the script module must appear in the import map so that it can be accessed by its module identifier from the classic script in a dynamic import (e.g. import( '@example/module' )).

For a classic script to depend on a module, it must declare that dependency via the new module_dependencies argument. When the script modules import map is printed, it inspects all enqueued classic scripts (and their dependencies recursively) to search for these declared module dependencies and ensures those modules are included in the import map.

The implementation introduces a new _wp_scripts_add_args_data() helper function 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. It also validates that module_dependencies is an array of strings.

When WP_Script_Modules::get_import_map() is called, it now efficiently traverses the dependency tree of all enqueued classic scripts to find any associated module dependencies. These modules are then added to the import map, making them available for dynamic imports within the classic scripts.

The usage looks like this:

wp_register_script_module( '@example/module', /*…*/ );

wp_enqueue_script(
	'example-classic',
	'/classic.js',
	array( 'classic-script-dependency' ), // Classic script dependencies.
	null,
	array(
		// Script module dependencies are declared here.
		'module_dependencies' => array( '@example/module' ),
	)
);
Old Description

This change proposes allowing classic scripts to add script module dependencies.

Classic scripts can add module dependencies like array( 'type' => 'module', 'id' => 'example-module-id' ), where classic script dependencies are simple strings, e.g. 'wp-i18n'.

In order for a classic script to have access to a script module, the script module must appear in the importmap so that it can be accessed by its module identifier from the classic script in a dynamic import.

For a classic script to depend on a module, it must declare that dependency and must be enqueued before the importmap is printed. When the script modules import map is printed, it inspects enqueued scripts to search for module dependencies and includes those dependencies in the import map. Import maps must be printed before any modulepreloads or any script module tags, and only one can be printed (although the specification has changed to allow multiple import maps).

The implementation overrides the WP_Dependencies::add method in WP_Scripts. add is is the main method that handles script registration. The subclassed implementation is used to partition the provided dependencies array into classic script dependencies and script module dependencies. The classic script dependencies are passed on to WP_Dependencies::add along with the rest of the arguments. The module dependencies are attached to the script as extra data module_deps.

When the script modules importmap is printed, it scans through the registered classic scripts for enqueued scripts with module_deps and adds those modules to the import map.

Script modules in the import map will be available for classic scripts to use via dynamic import().

The dependencies look like this:

wp_register_script_module( '@example/module', /*…*/ );
wp_enqueue_script(
	'example-classic',
	'/classic.js',

	// Dependencies array:
	array(
		// Classic script dependency, no changes.
		'classic-script-dependency',

		// Script module dependency, this form is new.
		array(
			'type' => 'module',
			'id' => '@example/module',
		),
	),
);

Builds on #8009. The implementation here is now independent.

Trac ticket: https://core.trac.wordpress.org/ticket/61500


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

@github-actions
Copy link

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

@sirreal sirreal changed the title Add module dependency to classic scripts Allow classic scripts to depend on modules Dec 19, 2024
@sirreal sirreal marked this pull request as ready for review December 19, 2024 17:24
@github-actions
Copy link

github-actions bot commented Dec 19, 2024

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props jonsurrell, westonruter.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@sirreal
Copy link
Member Author

sirreal commented Dec 19, 2024

@luisherranz @youknowriad @swissspidy @gziolo I'd love to have your review on this approach to allowing classic scripts to depend on script modules.

This allows for script modules to be exposed in the import map
regardless of being a dependency or enqueued.

This will enable scripts to depend on script modules by requesting they
be exposed.
The + operator merges arrays with key overwriting.
array_merge renumbers numeric arrays, the desired behavior in this case.
This works, but it only works as long as scripts are printed before the importmap is printed.
That is undesireable sometimes. An alternate approach may be to invert this scheme and have
script modules inspect enqueued and printed scripts before printing the importmap.
@westonruter
Copy link
Member

Updated Gemini review (with nitpicks not needing actioning):

Review of changes in scripts/allow-script-module-dependency

The changes introduce functionality to allow classic scripts to declare dependencies on script modules, including validation, processing, and handling within the import map generation. The implementation also refactors script registration logic to reduce duplication.

Observations

  1. Refactoring (functions.wp-scripts.php):

    • The introduction of _wp_scripts_add_args_data() is a solid improvement, centralizing validation and data addition.
    • Validation logic correctly checks for unknown keys and ensures module_dependencies is well-formed.
    • The placeholder usage for $args in translation strings is correct.
  2. Script Module Integration (WP_Script_Modules::get_import_map):

    • The logic to traverse classic script dependencies ($wp_scripts->registered[$handle]->deps) to find associated module dependencies is implemented iteratively with a stack (array_pop). This avoids recursion limits and is efficient.
    • The _doing_it_wrong call correctly identifies missing module dependencies.
    • Usage of WP_Scripts::class . '::add' (or specifically WP_Scripts::add_data in the diff) as the function name in _doing_it_wrong is acceptable, though slightly unusual to see class constants used this way in legacy-style warnings.
  3. Tests (Tests_Script_Modules_WpScriptModules):

    • The tests are comprehensive, covering direct module dependencies, transitive dependencies, and error conditions.
    • The typo fix "dependnecy" -> "dependency" in test_wp_scripts_doing_it_wrong_for_missing_script_module_dependencies is correct.
    • Assertions are specific and verify the expected behavior (import map contents, error notices).

Nitpicks

  1. _doing_it_wrong context in WP_Script_Modules::get_import_map:
    In the diff:

    _doing_it_wrong(
        'WP_Scripts::add_data', // <--- This string
        // ...
    );

    While WP_Scripts::add_data is technically where the data was added, the error is being detected during import map generation (enqueue time). A user might find it confusing to see WP_Scripts::add_data if they didn't explicitly call it (e.g. they called wp_enqueue_script). However, since wp_enqueue_script calls add_data internally via the new helper, this is technically accurate enough.

  2. _wp_scripts_add_args_data Documentation:
    The docblock says:
    * Adds the data for the recognized args and warns for unrecognized args.
    This follows the third-person singular verb standard requested.

Conclusion

The code is robust, follows WordPress coding standards, and the test coverage is thorough. The refactoring improves maintainability.

Plan:
No changes are required. The current state is ready.

Copy link
Member

@westonruter westonruter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sirreal I'm excited about getting this capability into core! If you approve the changes, let's get this committed. You can do the honors if you'd like!

westonruter added a commit to westonruter/wordpress-develop that referenced this pull request Jan 31, 2026
… at 9acd7f0

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
westonruter added a commit to westonruter/wordpress-develop that referenced this pull request Jan 31, 2026
… at 9acd7f0

Co-authored-by: Jon Surrell <jonsurrell@git.wordpress.org>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@westonruter
Copy link
Member

I squash-merged this PR into #10806 to kick the tires. I added 34f5e4c to make use of it to add espree as a dependency of wp-codemirror. It works!

@westonruter westonruter requested a review from Copilot February 2, 2026 22:17
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +2010 to +2012
array(
'id' => 'example2',
),
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test case includes an array format array('id' => 'example2') for module dependencies, but there is no test coverage for validating that this array format is handled correctly. Consider adding a test case that specifically validates the array format with an 'id' key is properly processed.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not factual. There is a test case for it being handled properly, but it is in scripts.php. The bottom of this same test method in fact shows that the format is handled properly, as example2 appears in the array returned by get_import_map.

westonruter and others added 3 commits February 2, 2026 14:20
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copy link
Member Author

@sirreal sirreal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have one final thing I'd like to consider, otherwise this seems ready to me. Thanks again for all your work on it @westonruter 🙂

…gs_data().

Automatically determine the calling function name using debug_backtrace() for context in _doing_it_wrong() calls.

Co-authored-by: Jon Surrell <jonsurrell@git.wordpress.org>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Copy link
Member Author

@sirreal sirreal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ This is ready!

@westonruter feel free to merge if you're ready. Otherwise, I'll get it landed this week. I don't mind either way 🙂

pento pushed a commit that referenced this pull request Feb 4, 2026
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 #8024

Follow-up to [61323].

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


git-svn-id: https://develop.svn.wordpress.org/trunk@61587 602fd350-edb4-49c9-b593-d223f7449a82
@github-actions
Copy link

github-actions bot commented Feb 4, 2026

A commit was made that fixes the Trac ticket referenced in the description of this pull request.

SVN changeset: 61587
GitHub commit: 6357346

This PR will be closed, but please confirm the accuracy of this and reopen if there is more work to be done.

@github-actions github-actions bot closed this Feb 4, 2026
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Feb 4, 2026
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 WordPress/wordpress-develop#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
westonruter added a commit to westonruter/wordpress-develop that referenced this pull request Feb 4, 2026
… at 9acd7f0...3a67800

Co-authored-by: Jon Surrell <jonsurrell@git.wordpress.org>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@sirreal sirreal deleted the scripts/allow-script-module-dependency branch February 4, 2026 08:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants