fix(html): resolve modulepreload href through the module pipeline (fix #22845) - #23320
fix(html): resolve modulepreload href through the module pipeline (fix #22845)#23320santhiprakash wants to merge 1 commit into
Conversation
…vitejs#22845) Plugin-resolved (virtual) ids referenced via <link rel="modulepreload"> were left unresolved in the build HTML and 404ed in production, while the matching <script type="module" src> for the same id was bundled correctly. Route rel=modulepreload hrefs through the module resolution pipeline (same as the script-src branch) by emitting an import up-front, then in a post-pass: - if the href resolves: strip the original <link> (build-import-analysis will re-emit the modulepreload for the resolved chunk URL), mark moduleSideEffects on the resolved module so treeshake doesn't drop it; - if it doesn't resolve: drop the transient import and leave the link intact (preserves runtime-handled URLs, matching the existing stylesheet fallback). Public-file and excluded URLs are untouched. Adds a playground test (playground/html/modulepreloadResolved.html) that exercises a plugin-resolved virtual module id and asserts the build output no longer contains an unresolved /@plugin-script href while the matching script's src gets bundled to a hashed asset URL.
ba72c21 to
9fdd4b4
Compare
|
This PR has been automatically flagged as likely to be created by a bot, LLM, or agent, and will be automatically closed. These contributions harm the maintenance of the project. Please read our AI policy for more information. |
|
Hi, the close/reopen above was a manual CI retrigger after the Windows |
|
Force-pushed a cleaned branch. The unrelated |
What is this PR solving?
Closes #22845.
When an HTML entry contains
<link rel="modulepreload" href="/@plugin-script">(a plugin-resolved virtual id) the href is left untouched in the build output, while the matching<script type="module" src="/@plugin-script">is correctly resolved and bundled. The result: a 404 in production for the modulepreload while the script loads fine.The root cause is in
packages/vite/src/node/plugins/html.ts(thebuild-htmlplugin).<link>nodes only get CSS-import or asset-URL treatment;rel="modulepreload"falls into the generic asset path which callsprocessAssetUrland does not run the id throughthis.resolve(). Real files happen to already live at that same href, so the bug stays hidden until you use a virtual module.Fix
Route
rel="modulepreload"hrefs through the same module pipeline the script-src branch already uses:<link rel="modulepreload">whose href is not a public file and not excluded, push it ontomodulePreloadLinkUrlsand emit a transientimport "<url>"so the build-import-analysis plugin runsthis.resolve()on it;this.resolve()succeeds → strip the original<link>from the HTML (build-import-analysis will re-emit the modulepreload for the resolved chunk URL), and markmoduleSideEffects = trueon the resolved module so treeshake cannot drop it;This mirrors the established pattern for
<link rel="stylesheet">resolution in the same plugin.Verification
playground/html/modulepreloadResolved.htmlexercises a plugin-resolved virtual module id (/@plugin-script) declared by both amodulepreloadlink and ascript type="module"src.playground/html/__tests__/html.spec.ts:/@plugin-scripthref/src does NOT survive in the build output, and every remaining<link rel="modulepreload">points at a hashed bundled asset..outputtext becomes'loaded') and the unresolved/@plugin-scriptlink is stripped in dev too.pnpm typecheck -F @vitejs/vitepasses cleanly.Alternatives considered
this.load()to register the modulepreload href as an alias of the script src: rejected — only fixes the specific co-emitted case fromprerenderToNodeStream.Reviewer attention
import "<url>"is dropped exactly once per failed resolution viajs.replace(importExpression, ''). If two modulepreloads share the same URL and both fail, only the first match is removed. The existing stylesheet branch has the same characteristic; I left it consistent rather than introducing a new escape strategy. Worth a reviewer call if you want stricter behavior.Checklist
modulepreloadfor plugin-resolved IDs #22845 (only closed fix(html): resolve modulepreload href for plugin-resolved module ids #22879 by codewithsupra, closed not merged)/@plugin-script)pnpm typecheck -F @vitejs/viteclean