From 1ca78dd9161c345cb3abfcdec27a81f4096d85d8 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Tue, 8 Jul 2025 21:13:22 +0900 Subject: [PATCH] feat(html): process HTML with plugin container in dev --- packages/vite/src/node/plugins/html.ts | 17 +++++++- packages/vite/src/node/plugins/index.ts | 4 +- .../src/node/server/middlewares/indexHtml.ts | 43 +++++++++++-------- .../assets-relative-base.spec.ts | 2 +- .../runtime-base/assets-runtime-base.spec.ts | 2 +- .../url-base/assets-url-base.spec.ts | 2 +- 6 files changed, 46 insertions(+), 24 deletions(-) diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 56e328974cd708..b10a2e0cd23a01 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -328,7 +328,7 @@ function handleParseError( /** * Compiles index.html into an entry js module */ -export function buildHtmlPlugin(config: ResolvedConfig): Plugin { +export function htmlPlugin(config: ResolvedConfig): Plugin { const [preHooks, normalHooks, postHooks] = resolveHtmlTransforms( config.plugins, ) @@ -345,9 +345,15 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { // Same reason with `htmlInlineProxyPlugin` isAsyncScriptMap.set(config, new Map()) + let server: ViteDevServer | undefined + return { name: 'vite:build-html', + configureServer(s) { + server = s + }, + transform: { async handler(html, id) { if (id.endsWith('.html')) { @@ -356,6 +362,15 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { const publicPath = `/${relativeUrlPath}` const publicBase = getBaseInHTML(relativeUrlPath, config) + if (server) { + const result = await server.transformIndexHtml(publicPath, html) + return { + code: '', + map: { mappings: '' }, + meta: { 'vite:build-html': result }, + } + } + const publicToRelative = (filename: string) => publicBase + filename const toOutputPublicFilePath = (url: string) => toOutputFilePathInHtml( diff --git a/packages/vite/src/node/plugins/index.ts b/packages/vite/src/node/plugins/index.ts index 82d87ab1f621b7..7e7eeb093933ae 100644 --- a/packages/vite/src/node/plugins/index.ts +++ b/packages/vite/src/node/plugins/index.ts @@ -11,7 +11,7 @@ import { importAnalysisPlugin } from './importAnalysis' import { cssAnalysisPlugin, cssPlugin, cssPostPlugin } from './css' import { assetPlugin } from './asset' import { clientInjectionsPlugin } from './clientInjections' -import { buildHtmlPlugin, htmlInlineProxyPlugin } from './html' +import { htmlInlineProxyPlugin, htmlPlugin } from './html' import { wasmFallbackPlugin, wasmHelperPlugin } from './wasm' import { modulePreloadPolyfillPlugin } from './modulePreloadPolyfill' import { webWorkerPlugin } from './worker' @@ -79,7 +79,7 @@ export async function resolvePlugins( wasmFallbackPlugin(), definePlugin(config), cssPostPlugin(config), - isBuild && buildHtmlPlugin(config), + htmlPlugin(config), workerImportMetaUrlPlugin(config), assetImportMetaUrlPlugin(config), ...buildPlugins.pre, diff --git a/packages/vite/src/node/server/middlewares/indexHtml.ts b/packages/vite/src/node/server/middlewares/indexHtml.ts index 22b122bee9ff09..78743092c31fb0 100644 --- a/packages/vite/src/node/server/middlewares/indexHtml.ts +++ b/packages/vite/src/node/server/middlewares/indexHtml.ts @@ -129,7 +129,7 @@ const processNodeUrl = ( useSrcSetReplacer: boolean, config: ResolvedConfig, htmlPath: string, - originalUrl?: string, + _originalUrl?: string, server?: ViteDevServer, isClassicScriptLink?: boolean, ): string => { @@ -145,26 +145,15 @@ const processNodeUrl = ( // rewrite `./index.js` -> `localhost:5173/a/index.js`. // rewrite `../index.js` -> `localhost:5173/index.js`. // rewrite `relative/index.js` -> `localhost:5173/a/relative/index.js`. - ((url[0] === '.' || isBareRelative(url)) && - originalUrl && - originalUrl !== '/' && - htmlPath === '/index.html') + url[0] === '.' || + isBareRelative(url) ) { - url = path.posix.join(config.base, url) + url = path.posix.join(config.base, path.posix.dirname(htmlPath), url) } let preTransformUrl: string | undefined - if (!isClassicScriptLink && shouldPreTransform(url, config)) { - if (url[0] === '/' && url[1] !== '/') { - preTransformUrl = url - } else if (url[0] === '.' || isBareRelative(url)) { - preTransformUrl = path.posix.join( - config.base, - path.posix.dirname(htmlPath), - url, - ) - } + preTransformUrl = url } if (server) { @@ -369,6 +358,11 @@ const devHtmlHook: IndexHtmlTransformHook = async ( } }), ) + + const module = await clientModuelGraph.getModuleById(filename) + if (module) { + clientModuelGraph.invalidateModule(module) + } } await Promise.all([ @@ -463,9 +457,22 @@ export function indexHtmlMiddleware( : server.config.preview.headers try { - let html = await fsp.readFile(filePath, 'utf-8') + let html: string if (isDev) { - html = await server.transformIndexHtml(url, html, req.originalUrl) + const clientEnv = server.environments.client + const resolvedId = + await clientEnv.pluginContainer.resolveId(filePath) + if (!resolvedId) return next() + + const result = await clientEnv.transformRequest(filePath) + if (!result) return next() + const moduleInfo = clientEnv.pluginContainer.getModuleInfo( + resolvedId.id, + ) + if (!moduleInfo) return next() + html = moduleInfo.meta['vite:build-html'] ?? '' + } else { + html = await fsp.readFile(filePath, 'utf-8') } return send(req, res, html, 'html', { headers }) } catch (e) { diff --git a/playground/assets/__tests__/relative-base/assets-relative-base.spec.ts b/playground/assets/__tests__/relative-base/assets-relative-base.spec.ts index 576e65bae7cb89..cfd2a9c91b8ccc 100644 --- a/playground/assets/__tests__/relative-base/assets-relative-base.spec.ts +++ b/playground/assets/__tests__/relative-base/assets-relative-base.spec.ts @@ -162,7 +162,7 @@ describe('image', () => { expect(s).toMatch( isBuild ? /other-assets\/asset-[-\w]{8}\.png \dx/ - : /\.\/nested\/asset\.png \dx/, + : /\/nested\/asset\.png \dx/, ) }) }) diff --git a/playground/assets/__tests__/runtime-base/assets-runtime-base.spec.ts b/playground/assets/__tests__/runtime-base/assets-runtime-base.spec.ts index 48e982b60e37ac..2a826bc7b6337c 100644 --- a/playground/assets/__tests__/runtime-base/assets-runtime-base.spec.ts +++ b/playground/assets/__tests__/runtime-base/assets-runtime-base.spec.ts @@ -157,7 +157,7 @@ describe('image', () => { expect(s).toMatch( isBuild ? /other-assets\/asset-[-\w]{8}\.png \dx/ - : /\.\/nested\/asset\.png \dx/, + : /\/nested\/asset\.png \dx/, ) }) }) diff --git a/playground/assets/__tests__/url-base/assets-url-base.spec.ts b/playground/assets/__tests__/url-base/assets-url-base.spec.ts index 1e143c9b772fba..9587fd66bc96d4 100644 --- a/playground/assets/__tests__/url-base/assets-url-base.spec.ts +++ b/playground/assets/__tests__/url-base/assets-url-base.spec.ts @@ -151,7 +151,7 @@ describe('image', () => { expect(s).toMatch( isBuild ? /other-assets\/asset-[-\w]{8}\.png \dx/ - : /\.\/nested\/asset\.png \dx/, + : /\/nested\/asset\.png \dx/, ) }) })