fix: enqueue Vite entries as script modules, after WordPress's import map - #357
Merged
Merged
Conversation
… map
A Vite entry is an ES module, but it was enqueued as a classic script and
printed in the head, before WordPress's import map. Firefox and Safari then
ignore the map, so every WordPress module on the page failed on
@wordpress/interactivity. In a classic theme the map is always in the
footer, so any theme was exposed as soon as a navigation block was used.
On wp_enqueue_scripts and admin_enqueue_scripts, a Vite entry (and the dev
server's client) is now a script module: WordPress places it as it places
its own. Dependencies, localized data and inline script go on a classic
companion, {handle}-data. No version is appended: a module is identified by
its URL.
This was referenced Sep 29, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
A Vite entry is an ES module, but
AssetEnqueuerenqueued it as a classic script (wp_enqueue_script+ a rewritten<script type="module">tag), so it followed the classic default: the<head>. WordPress prints its import map later:<head>, after classic scripts<head>, before it<head>, before itFirefox and Safari ignore an import map once a module has started, so every WordPress module on the page then fails with
@wordpress/interactivity was a bare specifier— the navigation block's menu no longer opens. Chromium tolerates the order, which hid it. A classic theme (theme-default, apiary) was exposed as soon as an author inserted a navigation, search or lightbox block. Found by Buzz's Firefox tests (Pollora/theme-buzz#1).Change
On
wp_enqueue_scriptsandadmin_enqueue_scripts— the pages where WordPress prints modules — a Vite entry is enqueued withwp_enqueue_script_module, so WordPress places it exactly as it places its own modules:<head>after the import map, or footer withloadInFooter()(passed asin_footer);Details:
dependencies(),localize(),inline(): a module takes none of them, so they go on a classic companion{handle}-data(registered with no source), printed in the head, which runs before the deferred module. No first-party theme or plugin uses them with Vite.null, notfalse): a module is identified by its URL, and?ver=would let a chunk importing the entry load a second copy.crossoriginkept, viawp_script_attributes.@vite/clientis enqueued as a module too (newViteManagerInterface::clientUrl()); it had the same problem.Tests
AssetEnqueuerTest(front/admin module,in_footer, no version, editor stays classic,crossoriginon this tag only, companion, Vite client). Full suite 1312 passed; Pint, PHPStan, Rector clean.script-modules.spec.ts: a post with a navigation block; the import map precedes every theme Vite entry (read from the HTML, so it fails in Chromium too), and the menu opens with no page error.loadInFooter()/ classic theme footer, each after the import map.CHANGELOG updated. Docs: Pollora/documentation PR to follow. Will conflict with #356 on CHANGELOG only.