Skip to content

Commit 4e90e60

Browse files
committed
handle error while collecting loaders
improve tests handle error while collecting loaders
1 parent 1fe9c8a commit 4e90e60

File tree

2 files changed

+73
-15
lines changed

2 files changed

+73
-15
lines changed

src/data-loaders/navigation-guard.spec.ts

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,17 +182,65 @@ describe('navigation-guard', () => {
182182
it('collects all loaders from lazy loaded pages with repeated navigation', async () => {
183183
setupApp({ isSSR: false })
184184
const router = getRouter()
185+
186+
const { promise, resolve } = Promise.withResolvers<void>()
187+
185188
router.addRoute({
186189
name: '_test',
187190
path: '/fetch',
188-
component: () =>
189-
import('../../tests/data-loaders/ComponentWithLoader.vue'),
191+
component: async () => {
192+
await promise
193+
194+
return import('../../tests/data-loaders/ComponentWithLoader.vue')
195+
},
190196
})
191197

192198
void router.push('/fetch')
193199

194-
// simulate repeated navigation while the async component is loading
200+
// wait a tick to ensure first navigation is started
195201
await Promise.resolve()
202+
203+
const secondNavPromise = router.push('/fetch')
204+
resolve()
205+
await secondNavPromise
206+
207+
const set = router.currentRoute.value.meta[LOADER_SET_KEY]
208+
expect([...set!]).toEqual([useDataOne, useDataTwo])
209+
210+
for (const record of router.currentRoute.value.matched) {
211+
expect(record.meta[LOADER_SET_PROMISES_KEY]).toBeUndefined()
212+
}
213+
})
214+
215+
it('collects all loaders from lazy loaded pages when first navigation fails', async () => {
216+
setupApp({ isSSR: false })
217+
const router = getRouter()
218+
219+
let first = true
220+
router.addRoute({
221+
name: '_test',
222+
path: '/fetch',
223+
component: async () => {
224+
if (first) {
225+
first = false
226+
227+
throw new Error('Failed to load component')
228+
}
229+
230+
return import('../../tests/data-loaders/ComponentWithLoader.vue')
231+
},
232+
})
233+
234+
const firstNavPromise = router.push('/fetch')
235+
await expect(firstNavPromise).rejects.toThrow(Error)
236+
237+
// Verify loaders collection cleanup after failure
238+
const rec = getRouter()
239+
.getRoutes()
240+
.find((r) => r.name === '_test')!
241+
expect(rec.meta[LOADER_SET_KEY]).toBeUndefined()
242+
expect(rec.meta[LOADER_SET_PROMISES_KEY]).toBeUndefined()
243+
196244
await router.push('/fetch')
197245

198246
const set = router.currentRoute.value.meta[LOADER_SET_KEY]

src/data-loaders/navigation-guard.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -151,25 +151,35 @@ export function setupLoaderGuard({
151151
lazyLoadingPromises.push(promise)
152152
}
153153
} else if (record.meta[LOADER_SET_PROMISES_KEY]) {
154-
console.log('REUSE', record.path)
155154
// When repeated navigation happen on the same route, loaders might still be
156155
// loading from async components, so we need to wait for them to resolve.
157156
lazyLoadingPromises.push(...record.meta[LOADER_SET_PROMISES_KEY])
158157
}
159158
}
160159

161-
return Promise.all(lazyLoadingPromises).then(() => {
162-
// group all the loaders in a single set
163-
for (const record of to.matched) {
164-
// merge the whole set of loaders
165-
for (const loader of record.meta[LOADER_SET_KEY]!) {
166-
to.meta[LOADER_SET_KEY]!.add(loader)
160+
return Promise.all(lazyLoadingPromises)
161+
.then(() => {
162+
// group all the loaders in a single set
163+
for (const record of to.matched) {
164+
// merge the whole set of loaders
165+
for (const loader of record.meta[LOADER_SET_KEY]!) {
166+
to.meta[LOADER_SET_KEY]!.add(loader)
167+
}
168+
record.meta[LOADER_SET_PROMISES_KEY] = undefined
167169
}
168-
record.meta[LOADER_SET_PROMISES_KEY] = undefined
169-
}
170-
// we return nothing to remove the value to allow the navigation
171-
// same as return true
172-
})
170+
// we return nothing to remove the value to allow the navigation
171+
// same as return true
172+
})
173+
.catch((error) => {
174+
// If error happens while collecting loaders, reset them
175+
// so on next navigation we can try again
176+
for (const record of to.matched) {
177+
record.meta[LOADER_SET_KEY] = undefined
178+
record.meta[LOADER_SET_PROMISES_KEY] = undefined
179+
}
180+
181+
throw error
182+
})
173183
})
174184

175185
const removeDataLoaderGuard = router.beforeResolve((to, from) => {

0 commit comments

Comments
 (0)