Skip to content

Commit f146397

Browse files
committed
fix(ci): invalidate worker importers on route refresh
1 parent 6b37329 commit f146397

2 files changed

Lines changed: 78 additions & 1 deletion

File tree

packages/mokup/src/vite/plugin/refresh.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,29 @@ import { resolveDirs, toPosix } from '../../shared/utils'
99
import { buildRouteSignature } from './routes'
1010
import { isViteDevServer } from './server'
1111

12+
interface InvalidatableModuleNode {
13+
importers?: Set<InvalidatableModuleNode>
14+
}
15+
16+
function invalidateModuleChain(server: ViteDevServer, node: InvalidatableModuleNode) {
17+
const visited = new Set<InvalidatableModuleNode>()
18+
const stack = [node]
19+
20+
while (stack.length > 0) {
21+
const current = stack.pop()
22+
if (!current || visited.has(current)) {
23+
continue
24+
}
25+
visited.add(current)
26+
server.moduleGraph.invalidateModule(current as Parameters<typeof server.moduleGraph.invalidateModule>[0])
27+
if (current.importers) {
28+
for (const importer of current.importers) {
29+
stack.push(importer)
30+
}
31+
}
32+
}
33+
}
34+
1235
function createRouteRefresher(params: {
1336
state: PluginState
1437
optionList: VitePluginOptions[]
@@ -157,7 +180,7 @@ function createRouteRefresher(params: {
157180
for (const id of virtualModuleIds) {
158181
const moduleNode = server.moduleGraph.getModuleById(id)
159182
if (moduleNode) {
160-
server.moduleGraph.invalidateModule(moduleNode)
183+
invalidateModuleChain(server, moduleNode as InvalidatableModuleNode)
161184
}
162185
}
163186
}

packages/mokup/test/vite-plugin-refresh.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,60 @@ describe('vite plugin route refresh', () => {
160160
expect(server.moduleGraph.invalidateModule).toHaveBeenCalledWith(moduleNode)
161161
})
162162

163+
it('invalidates importer chains for virtual modules when routes change', async () => {
164+
const parsed = parseRouteTemplate('/ping')
165+
mocks.scanRoutes.mockResolvedValueOnce([
166+
{
167+
file: '/root/mock/ping.get.json',
168+
template: parsed.template,
169+
method: 'GET',
170+
tokens: parsed.tokens,
171+
score: parsed.score,
172+
handler: { ok: true },
173+
},
174+
])
175+
176+
const state = {
177+
routes: [],
178+
serverRoutes: [],
179+
swRoutes: [],
180+
disabledRoutes: [],
181+
ignoredRoutes: [],
182+
configFiles: [],
183+
disabledConfigFiles: [],
184+
app: null,
185+
lastSignature: 'old',
186+
swModuleVersion: 0,
187+
}
188+
189+
const workerImporter = { id: '/worker/index.ts', importers: new Set() }
190+
const moduleNode = {
191+
id: '\0virtual:mokup-bundle',
192+
importers: new Set([workerImporter]),
193+
}
194+
const server = {
195+
ws: { send: vi.fn() },
196+
moduleGraph: {
197+
getModuleById: vi.fn().mockReturnValue(moduleNode),
198+
invalidateModule: vi.fn(),
199+
},
200+
}
201+
202+
const refresher = createRouteRefresher({
203+
state: state as never,
204+
optionList: [{ dir: '/root/mock', prefix: '/api' }],
205+
root: () => '/root',
206+
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
207+
enableViteMiddleware: false,
208+
virtualModuleIds: ['\0virtual:mokup-bundle'],
209+
})
210+
211+
await refresher(server as never)
212+
213+
expect(server.moduleGraph.invalidateModule).toHaveBeenCalledWith(moduleNode)
214+
expect(server.moduleGraph.invalidateModule).toHaveBeenCalledWith(workerImporter)
215+
})
216+
163217
it('forces virtual module invalidation when signatures are unchanged', async () => {
164218
const parsed = parseRouteTemplate('/about')
165219
const route = {

0 commit comments

Comments
 (0)