Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/vite/src/node/server/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
createExplicitDepsOptimizer,
} from '../optimizer/optimizer'
import { ERR_OUTDATED_OPTIMIZED_DEP } from '../../shared/constants'
import { cleanUrl, promiseWithResolvers } from '../../shared/utils'
import { cleanUrl, promiseWithResolvers, unwrapId } from '../../shared/utils'
import type { ViteDevServer } from '../server'
import { EnvironmentModuleGraph } from './moduleGraph'
import type { EnvironmentModuleNode } from './moduleGraph'
Expand Down Expand Up @@ -176,7 +176,7 @@ export class DevEnvironment extends BaseEnvironment {
({ path, message, firstInvalidatedBy }, client) => {
this.invalidateModule(
{
path,
path: unwrapId(path),
message,
firstInvalidatedBy,
},
Expand Down
14 changes: 14 additions & 0 deletions playground/hmr/__tests__/hmr.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,20 @@ if (!isBuild) {
}
})

test('invalidate virtual module propagates to importers', async () => {
const el = await page.$('.virtual-invalidation-parent')
await expect.poll(() => el.textContent()).toBe('initial')

// Edit the real dep file — Vite detects the change and sends
// js-update to the virtual module. The virtual module accepts
// then invalidates, which should propagate to parent.js.
editFile('virtual-invalidation/dep.js', (code) =>
code.replace('initial', 'updated2'),
)

await expect.poll(() => el.textContent()).toBe('updated2')
})

test('invalidate on root triggers page reload', async () => {
editFile('invalidation/root.js', (code) => code.replace('Init', 'Updated'))
await page.waitForEvent('load')
Expand Down
2 changes: 2 additions & 0 deletions playground/hmr/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
<div class="virtual-dep"></div>
<div class="soft-invalidation"></div>
<div class="invalidation-parent"></div>
<div class="virtual-invalidation-parent"></div>
<script type="module" src="./virtual-invalidation/parent.js"></script>
<div class="invalidation-root"></div>
<div class="invalidation-circular-deps"></div>
<div class="invalidation-circular-deps-handled"></div>
Expand Down
1 change: 1 addition & 0 deletions playground/hmr/virtual-invalidation/dep.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const depValue = 'initial'
7 changes: 7 additions & 0 deletions playground/hmr/virtual-invalidation/parent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { value } from 'virtual:invalidation-file'

if (import.meta.hot) {
import.meta.hot.accept()
}

document.querySelector('.virtual-invalidation-parent').innerHTML = value
28 changes: 28 additions & 0 deletions playground/hmr/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,41 @@ export default defineConfig(({ command }) => ({
},
},
virtualPlugin(),
virtualInvalidationPlugin(),
transformCountPlugin(),
watchCssDepsPlugin(),
TestCssLinkPlugin(),
hotEventsPlugin(),
],
}))

// Virtual module that calls invalidate()
function virtualInvalidationPlugin(): Plugin {
return {
name: 'virtual-invalidation-file',
resolveId(id) {
if (id === 'virtual:invalidation-file') {
return '\0virtual:invalidation-file'
}
},
load(id) {
if (id === '\0virtual:invalidation-file') {
// Import a real file so editing it triggers Vite's HMR pipeline.
// The virtual module accepts and immediately invalidates,
// which should propagate to its importers.
return `\
import { depValue } from '/virtual-invalidation/dep.js';
export const value = depValue;
if (import.meta.hot) {
import.meta.hot.accept(() => {
import.meta.hot.invalidate()
})
}`
}
},
}
}

function virtualPlugin(): Plugin {
let num = 0
return {
Expand Down