Skip to content

Commit

Permalink
feat(loaders): internal mods in route records
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Sep 10, 2024
1 parent 4861467 commit 5ff2cd0
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 4 deletions.
26 changes: 26 additions & 0 deletions packages/router/__tests__/guards/extractComponentsGuards.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,30 @@ describe('extractComponentsGuards', () => {
'custom'
)
})

it('preserves resolved modules in mods', async () => {
const mod = {
default: components.Home,
__esModule: true,
custom: true,
}
const mod2 = {
default: components.Bar,
__esModule: true,
custom: true,
}
const record = normalizeRouteRecord({
path: '/',
components: { default: async () => mod, other: async () => mod2 },
})
expect(record.mods).toEqual({})
const guards = extractComponentsGuards(
[record],
'beforeRouteEnter',
to,
from
)
await Promise.all(guards.map(guard => guard()))
expect(record.mods).toEqual({ default: mod, other: mod2 })
})
})
55 changes: 55 additions & 0 deletions packages/router/__tests__/guards/loadRouteLocation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,61 @@ describe('loadRouteLocation', () => {
])
})

describe('mods', () => {
const mod = {
default: components.Home,
__esModule: true,
custom: true,
}
const mod2 = {
default: FunctionalHome,
__esModule: true,
custom: true,
}

it('preserves resolved modules', async () => {
const router = createRouter({
history: createMemoryHistory(),
routes: [
{
path: '/',
component: async () => mod,
},
],
})

const loaded = await loadRouteLocation(router.resolve('/'))
// mods follow the same structure as components
expect(loaded.matched[0]?.mods).toEqual({
default: expect.anything(),
})
expect(loaded.matched[0]?.mods?.default).toBe(mod)
})

it('preserves resolved modules for named components', async () => {
const router = createRouter({
history: createMemoryHistory(),
routes: [
{
path: '/',
components: {
default: async () => mod2,
name: async () => mod,
},
},
],
})

const loaded = await loadRouteLocation(router.resolve('/'))
expect(loaded.matched[0]?.mods).toEqual({
default: expect.anything(),
name: expect.anything(),
})
expect(loaded.matched[0]?.mods?.name).toBe(mod)
expect(loaded.matched[0]?.mods?.default).toBe(mod2)
})
})

it('throws with non loadable routes', async () => {
expect.assertions(1)
await expect(
Expand Down
1 change: 1 addition & 0 deletions packages/router/src/matcher/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,7 @@ export function normalizeRouteRecord(
leaveGuards: new Set(),
updateGuards: new Set(),
enterCallbacks: {},
mods: {},
components:
'components' in record
? record.components || null
Expand Down
7 changes: 7 additions & 0 deletions packages/router/src/matcher/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ export interface RouteRecordNormalized {
* {@inheritDoc RouteRecordMultipleViews.components}
*/
components: RouteRecordMultipleViews['components'] | null | undefined

/**
* Contains the original modules for lazy loaded components.
* @internal
*/
mods: Record<string, unknown>

/**
* Nested route records.
*/
Expand Down
12 changes: 8 additions & 4 deletions packages/router/src/navigationGuards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,21 +316,22 @@ export function extractComponentsGuards(
guards.push(() =>
componentPromise.then(resolved => {
if (!resolved)
return Promise.reject(
new Error(
`Couldn't resolve component "${name}" at "${record.path}"`
)
throw new Error(
`Couldn't resolve component "${name}" at "${record.path}"`
)
const resolvedComponent = isESModule(resolved)
? resolved.default
: resolved
// keep the resolved module for plugins like data loaders
record.mods[name] = resolved
// replace the function with the resolved component
// cannot be null or undefined because we went into the for loop
record.components![name] = resolvedComponent
// __vccOpts is added by vue-class-component and contain the regular options
const options: ComponentOptions =
(resolvedComponent as any).__vccOpts || resolvedComponent
const guard = options[guardType]

return (
guard &&
guardToPromiseFn(guard, to, from, record, name, runWithContext)()
Expand Down Expand Up @@ -373,9 +374,12 @@ export function loadRouteLocation(
`Couldn't resolve component "${name}" at "${record.path}". Ensure you passed a function that returns a promise.`
)
)

const resolvedComponent = isESModule(resolved)
? resolved.default
: resolved
// keep the resolved module for plugins like data loaders
record.mods[name] = resolved
// replace the function with the resolved component
// cannot be null or undefined because we went into the for loop
record.components![name] = resolvedComponent
Expand Down

0 comments on commit 5ff2cd0

Please sign in to comment.