Build Tools: Scope wp-build's deregister-before-register to @wordpress/* modules#77465
Build Tools: Scope wp-build's deregister-before-register to @wordpress/* modules#77465retrofox wants to merge 2 commits into
Conversation
The deregister-before-register pattern in wp-build templates was introduced in #75909 so that Gutenberg-as-plugin can override Core's bundled @wordpress/* module versions. Core's wp_register_script_module() is idempotent (first-wins) with no override API, so deregister-then- register is the only path. Applied universally, the pattern creates silent last-plugin-wins collisions for any non-@wordpress/* module shared across plugins (plugin-local IDs, vendor packages) — with no upside, since these entries are never overriding Core defaults. Scope the pattern to @wordpress/* IDs — the only namespace Core registers by default. Plugin-local and shared-package IDs now rely on Core's idempotent first-wins semantics, matching the behavior the Script Modules API was designed for.
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Why this matters beyond Core's own modules: The blanket Core's For any shared non-core ID, the same deregister just means the last plugin to register it clobbers whatever was there (last-registration-wins), with nothing from Core being overridden.
if ( ! isset( $this->registered[ $id ] ) ) {
// ...register...
}Scoping the generated deregister to This pairs with #78822: once packages outside |
What?
Scopes wp-build's deregister-before-register pattern to IDs in the
@wordpress/*namespace, leaving every other module ID to rely on Core's idempotentwp_register_script_module()first-wins semantics.Affects the two generated PHP templates:
packages/wp-build/templates/module-registration.php.templatepackages/wp-build/templates/routes-registration.php.templateWhy?
The deregister-before-register pattern was introduced in #75909 for a specific scenario: Gutenberg-as-plugin needs to override Core's bundled
@wordpress/*module versions, andWP_Script_Modules::register()is idempotent with no override API. Deregister-then-register is the only Core-sanctioned path.The fix worked, but was applied universally to every generated registration — including plugin-local modules and shared packages that were never overriding anything Core registers. For an ID Core doesn't own there's no upside: nothing to override, and each deregister also dequeues the module (see #76170, which added a static guard to work around that).
This becomes load-bearing once more than one wp-build plugin can register the same shared, non-
@wordpress/*module ID. The universal deregister then turns into a silent last-plugin-wins race decided by plugin load order; scoping it keeps those IDs on Core's deterministic first-wins instead.How?
Wrap each
wp_deregister_script_module()call in astr_starts_with( $id, '@wordpress/' )check.@wordpress/*is the only namespace Core registers by default (seewp-includes/assets/script-modules-packages.php), so the Gutenberg-as-plugin override scenario is preserved. All other IDs fall through to Core's idempotent first-wins semantics.str_starts_withis safe here — WordPress Core polyfills it since 5.9 and Gutenberg already uses it unconditionally inlib/client-assets.php.Testing
build/modules.phpand route registration contain the conditional.@wordpress/*ID → first-plugin-wins, no cross-plugin dequeue. Load Gutenberg-as-plugin →@wordpress/*overrides still take precedence over Core.Related
name, which is what makes a shared non-@wordpress/*ID registrable by more than one plugin. This PR is the semantics half of that pair and can land independently.