From e520c1d461d43d4c51bf809df387bfdc285734fa Mon Sep 17 00:00:00 2001 From: Oleg Shibaev Date: Sat, 15 Nov 2025 19:46:22 +0400 Subject: [PATCH 1/3] fix: fix hmr of dynamically loaded vue sfc modules in apps with tailwind --- packages/plugin-vue/src/handleHotUpdate.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/plugin-vue/src/handleHotUpdate.ts b/packages/plugin-vue/src/handleHotUpdate.ts index 8e133229..46d1c0b7 100644 --- a/packages/plugin-vue/src/handleHotUpdate.ts +++ b/packages/plugin-vue/src/handleHotUpdate.ts @@ -336,7 +336,17 @@ function getMainModule(modules: ModuleNode[]) { // #9341 // We pick the module with the shortest URL in order to pick the module // with the lowest number of query parameters. + // https://github.com/vitejs/vite-plugin-vue/issues/701 + // Prefer type: 'js' modules, since tailwind can add type: 'asset' modules + // which can have the same url length and come first in the array + // in certain cases .sort((m1, m2) => { + if (m1.type !== m2.type) { + if (m1.type === 'js' || m2.type === 'js') { + return m1.type === 'js' ? -1 : 1 + } + } + return m1.url.length - m2.url.length })[0] ) From 971c15c492b94d2c2d95596c456db93ede0d1a6c Mon Sep 17 00:00:00 2001 From: Oleg Shibaev Date: Mon, 17 Nov 2025 17:37:48 +0400 Subject: [PATCH 2/3] fix: filter out non-js modules instead of sorting --- packages/plugin-vue/src/handleHotUpdate.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/plugin-vue/src/handleHotUpdate.ts b/packages/plugin-vue/src/handleHotUpdate.ts index 46d1c0b7..3f9fd1b3 100644 --- a/packages/plugin-vue/src/handleHotUpdate.ts +++ b/packages/plugin-vue/src/handleHotUpdate.ts @@ -332,28 +332,24 @@ function hasScriptChanged(prev: SFCDescriptor, next: SFCDescriptor): boolean { function getMainModule(modules: ModuleNode[]) { return ( modules - .filter((m) => !/type=/.test(m.url) || /type=script/.test(m.url)) + .filter( + (m) => + m.type === 'js' && + (!/type=/.test(m.url) || /type=script/.test(m.url)), + ) // #9341 // We pick the module with the shortest URL in order to pick the module // with the lowest number of query parameters. - // https://github.com/vitejs/vite-plugin-vue/issues/701 - // Prefer type: 'js' modules, since tailwind can add type: 'asset' modules - // which can have the same url length and come first in the array - // in certain cases .sort((m1, m2) => { - if (m1.type !== m2.type) { - if (m1.type === 'js' || m2.type === 'js') { - return m1.type === 'js' ? -1 : 1 - } - } - return m1.url.length - m2.url.length })[0] ) } function getScriptModule(modules: ModuleNode[]) { - return modules.find((m) => /type=script.*&lang\.\w+$/.test(m.url)) + return modules.find( + (m) => m.type === 'js' && /type=script.*&lang\.\w+$/.test(m.url), + ) } export function handleTypeDepChange( From 07b6cf6c8422c57a8bdb4abe6a02305744c947a1 Mon Sep 17 00:00:00 2001 From: Oleg Shibaev Date: Tue, 18 Nov 2025 15:36:58 +0400 Subject: [PATCH 3/3] Preemptively filter out non-js modules --- packages/plugin-vue/src/handleHotUpdate.ts | 35 +++++++++------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/packages/plugin-vue/src/handleHotUpdate.ts b/packages/plugin-vue/src/handleHotUpdate.ts index 3f9fd1b3..aee56d25 100644 --- a/packages/plugin-vue/src/handleHotUpdate.ts +++ b/packages/plugin-vue/src/handleHotUpdate.ts @@ -22,8 +22,6 @@ import type { ResolvedOptions } from './index' const debug = _debug('vite:hmr') -const directRequestRE = /(?:\?|&)direct\b/ - /** * Vite-specific HMR handling */ @@ -43,17 +41,19 @@ export async function handleHotUpdate( const { descriptor } = createDescriptor(file, content, options, true) let needRerender = false + const nonJsModules = modules.filter((m) => m.type !== 'js') + const jsModules = modules.filter((m) => m.type === 'js') const affectedModules = new Set( - modules.filter((mod) => mod.type !== 'js'), // this plugin does not handle non-js modules + nonJsModules, // this plugin does not handle non-js modules ) - const mainModule = getMainModule(modules) - const templateModule = modules.find((m) => /type=template/.test(m.url)) + const mainModule = getMainModule(jsModules) + const templateModule = jsModules.find((m) => /type=template/.test(m.url)) // trigger resolveScript for descriptor so that we'll have the AST ready resolveScript(descriptor, options, false, customElement) const scriptChanged = hasScriptChanged(prevDescriptor, descriptor) if (scriptChanged) { - affectedModules.add(getScriptModule(modules) || mainModule) + affectedModules.add(getScriptModule(jsModules) || mainModule) } if (!isEqualBlock(descriptor.template, prevDescriptor.template)) { @@ -95,11 +95,10 @@ export async function handleHotUpdate( const next = nextStyles[i] if (!prev || !isEqualBlock(prev, next)) { didUpdateStyle = true - const mod = modules.find( + const mod = jsModules.find( (m) => m.url.includes(`type=style&index=${i}`) && - m.url.endsWith(`.${next.lang || 'css'}`) && - !directRequestRE.test(m.url), + m.url.endsWith(`.${next.lang || 'css'}`), ) if (mod) { affectedModules.add(mod) @@ -130,7 +129,7 @@ export async function handleHotUpdate( const prev = prevCustoms[i] const next = nextCustoms[i] if (!prev || !isEqualBlock(prev, next)) { - const mod = modules.find((m) => + const mod = jsModules.find((m) => m.url.includes(`type=${prev.type}&index=${i}`), ) if (mod) { @@ -329,14 +328,10 @@ function hasScriptChanged(prev: SFCDescriptor, next: SFCDescriptor): boolean { return false } -function getMainModule(modules: ModuleNode[]) { +function getMainModule(jsModules: ModuleNode[]) { return ( - modules - .filter( - (m) => - m.type === 'js' && - (!/type=/.test(m.url) || /type=script/.test(m.url)), - ) + jsModules + .filter((m) => !/type=/.test(m.url) || /type=script/.test(m.url)) // #9341 // We pick the module with the shortest URL in order to pick the module // with the lowest number of query parameters. @@ -346,10 +341,8 @@ function getMainModule(modules: ModuleNode[]) { ) } -function getScriptModule(modules: ModuleNode[]) { - return modules.find( - (m) => m.type === 'js' && /type=script.*&lang\.\w+$/.test(m.url), - ) +function getScriptModule(jsModules: ModuleNode[]) { + return jsModules.find((m) => /type=script.*&lang\.\w+$/.test(m.url)) } export function handleTypeDepChange(