From fe83d2b0e25e6e05f6ae49407031f7fd2b969f8c Mon Sep 17 00:00:00 2001 From: btea <2356281422@qq.com> Date: Fri, 7 Aug 2026 16:20:25 +0800 Subject: [PATCH 1/3] fix(bundled-dev): refresh after editing inline scripts in html entries --- packages/vite/src/node/plugins/html.ts | 10 +++-- packages/vite/src/node/server/bundledDev.ts | 16 +++++++ playground/html/__tests__/html.spec.ts | 46 +++++++++++++++++++++ playground/html/inline-classic-script.html | 5 +++ playground/html/vite.config.js | 1 + 5 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 playground/html/inline-classic-script.html diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 36dfafd4fca36b..21427c97ab069a 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -58,7 +58,7 @@ interface ScriptAssetsUrl { } const htmlProxyRE = - /[?&]html-proxy=?(?:&inline-css)?(?:&style-attr)?&index=(\d+)\.(?:js|css)$/ + /[?&]html-proxy=?(?:&inline-css)?(?:&style-attr)?&index=(\d+)(?:&h=[a-z0-9]+)?\.(?:js|css)$/ const isHtmlProxyRE = /[?&]html-proxy\b/ const inlineCSSRE = /__VITE_INLINE_CSS__([a-z\d]{8}_\d+)__/g @@ -592,7 +592,9 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { addToHTMLProxyCache(config, filePath, inlineModuleIndex, { code: contents, }) - js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}.js"` + // include a content hash in the proxy id so that editing the + // inline script is detected as a change by the bundler + js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}&h=${getHash(contents)}.js"` shouldRemove = true } @@ -713,7 +715,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { const filePath = id.replace(normalizePath(config.root), '') addToHTMLProxyCache(config, filePath, inlineModuleIndex, { code }) // will transform with css plugin and cache result with css-post plugin - js += `\nimport "${id}?html-proxy&inline-css&style-attr&index=${inlineModuleIndex}.css"` + js += `\nimport "${id}?html-proxy&inline-css&style-attr&index=${inlineModuleIndex}&h=${getHash(code)}.css"` const hash = getHash(cleanUrl(id)) // will transform in `applyHtmlTransforms` overwriteAttrValue( @@ -732,7 +734,7 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { addToHTMLProxyCache(config, filePath, inlineModuleIndex, { code: styleNode.value, }) - js += `\nimport "${id}?html-proxy&inline-css&index=${inlineModuleIndex}.css"` + js += `\nimport "${id}?html-proxy&inline-css&index=${inlineModuleIndex}&h=${getHash(styleNode.value)}.css"` const hash = getHash(cleanUrl(id)) // will transform in `applyHtmlTransforms` s.update( diff --git a/packages/vite/src/node/server/bundledDev.ts b/packages/vite/src/node/server/bundledDev.ts index f5b6ca0c4c06dc..8c3452bc95f7cc 100644 --- a/packages/vite/src/node/server/bundledDev.ts +++ b/packages/vite/src/node/server/bundledDev.ts @@ -10,6 +10,7 @@ import getEtag from 'etag' import { ChunkMetadataMap, resolveRolldownOptions } from '../build' import { BUNDLED_DEV_CLIENT_FILENAME } from '../constants' import { getHmrImplementation } from '../plugins/clientInjections' +import { isHTMLRequest } from '../plugins/html' import { createDebugger, formatAndTruncateFileList } from '../utils' import type { DevEnvironment } from './environment' import { type NormalizedHotChannelClient, debugHmr, getShortName } from './hmr' @@ -195,6 +196,21 @@ export class BundledDev { if (changedFiles.length === 0) { return } + // Edits to an HTML entry may leave its transformed module code + // unchanged (classic inline scripts and markup stay in the html + // output; only module scripts/styles turn into imports), in which + // case rolldown reports Noop updates and nothing would happen. + // Always rebuild and reload on html changes, like the unbundled + // dev server does. + if (changedFiles.some((file) => isHTMLRequest(file))) { + debug?.(`TRIGGER: html entry changed, forcing full rebuild`) + this.devEngine.triggerFullBuild() + this.devEngine.ensureLatestBuildOutput().then( + () => this.debouncedFullReload(), + () => {}, + ) + return + } if (updates.every((update) => update.update.type === 'Noop')) { debug?.(`ignored file change for ${changedFiles.join(', ')}`) return diff --git a/playground/html/__tests__/html.spec.ts b/playground/html/__tests__/html.spec.ts index 08c453ff9354c6..9045f84ff3c412 100644 --- a/playground/html/__tests__/html.spec.ts +++ b/playground/html/__tests__/html.spec.ts @@ -529,6 +529,52 @@ test('invalidate inline proxy module on reload', async () => { expect(await page.textContent('.test')).toContain('ok') }) +test.runIf(isServe)( + 'editing inline script in html reloads with updated content', + async () => { + await page.goto(viteTestUrl + '/a á.html') + try { + await untilBrowserLogAfter( + () => + editFile('a á.html', (code) => + code.replace('special character', 'special character edited'), + ), + 'special character edited', + ) + } finally { + editFile('a á.html', (code) => + code.replace('special character edited', 'special character'), + ) + } + }, +) + +// TODO: enable for bundledDev once rolldown includes +// https://github.com/rolldown/rolldown/pull/10637 — classic inline scripts stay +// in the html output, and rolldown <1.2.4 drops re-emitted assets with changed +// content from rebuild output, so bundledDev would serve the stale html. +test.skipIf(isBundled)( + 'editing classic inline script in html reloads with updated content', + async () => { + await page.goto(`${viteTestUrl}/inline-classic-script.html`) + expect(await page.textContent('.classic-script-content')).toContain( + 'classic before', + ) + editFile('inline-classic-script.html', (code) => + code.replace('classic before', 'classic after edit'), + ) + try { + await expect + .poll(() => page.textContent('.classic-script-content')) + .toContain('classic after edit') + } finally { + editFile('inline-classic-script.html', (code) => + code.replace('classic after edit', 'classic before'), + ) + } + }, +) + test.runIf(isServe)( 'malformed URLs in src attributes should show errors', async () => { diff --git a/playground/html/inline-classic-script.html b/playground/html/inline-classic-script.html new file mode 100644 index 00000000000000..98eb499da634b9 --- /dev/null +++ b/playground/html/inline-classic-script.html @@ -0,0 +1,5 @@ +