diff --git a/packages/vite/src/client/client.ts b/packages/vite/src/client/client.ts index fdd28e6be42278..df05a4eccb276c 100644 --- a/packages/vite/src/client/client.ts +++ b/packages/vite/src/client/client.ts @@ -388,7 +388,12 @@ export function removeStyle(id: string): void { } } -async function fetchUpdate({ path, acceptedPath, timestamp }: Update) { +async function fetchUpdate({ + path, + acceptedPath, + timestamp, + explicitImportRequired +}: Update) { const mod = hotModulesMap.get(path) if (!mod) { // In a code-splitting project, @@ -415,7 +420,9 @@ async function fetchUpdate({ path, acceptedPath, timestamp }: Update) { /* @vite-ignore */ base + path.slice(1) + - `?import&t=${timestamp}${query ? `&${query}` : ''}` + `?${explicitImportRequired ? 'import&' : ''}t=${timestamp}${ + query ? `&${query}` : '' + }` ) moduleMap.set(dep, newMod) } catch (e) { diff --git a/packages/vite/src/node/plugins/importAnalysis.ts b/packages/vite/src/node/plugins/importAnalysis.ts index 77b24c2a35da68..f1356d5c7f879e 100644 --- a/packages/vite/src/node/plugins/importAnalysis.ts +++ b/packages/vite/src/node/plugins/importAnalysis.ts @@ -76,7 +76,7 @@ export const canSkipImportAnalysis = (id: string): boolean => const optimizedDepChunkRE = /\/chunk-[A-Z0-9]{8}\.js/ const optimizedDepDynamicRE = /-[A-Z0-9]{8}\.js/ -function isExplicitImportRequired(url: string) { +export function isExplicitImportRequired(url: string): boolean { return !isJSRequest(cleanUrl(url)) && !isCSSRequest(url) } diff --git a/packages/vite/src/node/server/hmr.ts b/packages/vite/src/node/server/hmr.ts index f3298944a5e0b0..3493d370ab0b65 100644 --- a/packages/vite/src/node/server/hmr.ts +++ b/packages/vite/src/node/server/hmr.ts @@ -9,6 +9,7 @@ import { createDebugger, normalizePath, unique } from '../utils' import type { ViteDevServer } from '..' import { isCSSRequest } from '../plugins/css' import { getAffectedGlobModules } from '../plugins/importMetaGlob' +import { isExplicitImportRequired } from '../plugins/importAnalysis' import type { ModuleNode } from './moduleGraph' export const debugHmr = createDebugger('vite:hmr') @@ -151,9 +152,13 @@ export function updateModules( updates.push( ...[...boundaries].map(({ boundary, acceptedVia }) => ({ - type: `${boundary.type}-update` as Update['type'], + type: `${boundary.type}-update` as const, timestamp, path: boundary.url, + explicitImportRequired: + boundary.type === 'js' + ? isExplicitImportRequired(acceptedVia.url) + : undefined, acceptedPath: acceptedVia.url })) ) diff --git a/packages/vite/types/hmrPayload.d.ts b/packages/vite/types/hmrPayload.d.ts index 2fbed3a821466f..9ddb1a48c78072 100644 --- a/packages/vite/types/hmrPayload.d.ts +++ b/packages/vite/types/hmrPayload.d.ts @@ -20,6 +20,10 @@ export interface Update { path: string acceptedPath: string timestamp: number + /** + * @internal + */ + explicitImportRequired: boolean | undefined } export interface PrunePayload { diff --git a/playground/hmr/__tests__/hmr.spec.ts b/playground/hmr/__tests__/hmr.spec.ts index d0635fc04db9ee..3858719b772a37 100644 --- a/playground/hmr/__tests__/hmr.spec.ts +++ b/playground/hmr/__tests__/hmr.spec.ts @@ -218,6 +218,32 @@ if (!isBuild) { expect(await btn.textContent()).toBe('Counter 1') }) + // #2255 + test('importing reloaded', async () => { + await page.goto(viteTestUrl) + const outputEle = await page.$('.importing-reloaded') + const getOutput = () => { + return outputEle.innerHTML() + } + + await untilUpdated(getOutput, ['a.js: a0', 'b.js: b0,a0'].join('
')) + + editFile('importing-updated/a.js', (code) => code.replace("'a0'", "'a1'")) + await untilUpdated( + getOutput, + ['a.js: a0', 'b.js: b0,a0', 'a.js: a1'].join('
') + ) + + editFile('importing-updated/b.js', (code) => + code.replace('`b0,${a}`', '`b1,${a}`') + ) + // note that "a.js: a1" should not happen twice after "b.js: b0,a0'" + await untilUpdated( + getOutput, + ['a.js: a0', 'b.js: b0,a0', 'a.js: a1', 'b.js: b1,a1'].join('
') + ) + }) + describe('acceptExports', () => { const HOT_UPDATED = /hot updated/ const CONNECTED = /connected/ diff --git a/playground/hmr/hmr.ts b/playground/hmr/hmr.ts index f2d21b9bc78884..97330f05f29f64 100644 --- a/playground/hmr/hmr.ts +++ b/playground/hmr/hmr.ts @@ -1,4 +1,5 @@ import { foo as depFoo, nestedFoo } from './hmrDep' +import './importing-updated' export const foo = 1 text('.app', foo) diff --git a/playground/hmr/importing-updated/a.js b/playground/hmr/importing-updated/a.js new file mode 100644 index 00000000000000..4b08229417e4c3 --- /dev/null +++ b/playground/hmr/importing-updated/a.js @@ -0,0 +1,8 @@ +const val = 'a0' +document.querySelector('.importing-reloaded').innerHTML += `a.js: ${val}
` + +export default val + +if (import.meta.hot) { + import.meta.hot.accept() +} diff --git a/playground/hmr/importing-updated/b.js b/playground/hmr/importing-updated/b.js new file mode 100644 index 00000000000000..87c4a065061fea --- /dev/null +++ b/playground/hmr/importing-updated/b.js @@ -0,0 +1,8 @@ +import a from './a.js' + +const val = `b0,${a}` +document.querySelector('.importing-reloaded').innerHTML += `b.js: ${val}
` + +if (import.meta.hot) { + import.meta.hot.accept() +} diff --git a/playground/hmr/importing-updated/index.js b/playground/hmr/importing-updated/index.js new file mode 100644 index 00000000000000..0cc74268d385de --- /dev/null +++ b/playground/hmr/importing-updated/index.js @@ -0,0 +1,2 @@ +import './a' +import './b' diff --git a/playground/hmr/index.html b/playground/hmr/index.html index 7857ef818a911c..aafeaea5b565d4 100644 --- a/playground/hmr/index.html +++ b/playground/hmr/index.html @@ -25,3 +25,4 @@
+