Skip to content

Commit 67babd4

Browse files
G100myposva
andauthored
fix(unplugin): generate param types from override paths and stop inheritance on absolute overrides (#2646)
* test(unplugin): add path override params coverage * fix(unplugin): extract params from path overrides * refactor: tests and renames --------- Co-authored-by: Eduardo San Martin Morote <posva13@gmail.com>
1 parent 48566ba commit 67babd4

7 files changed

Lines changed: 592 additions & 45 deletions

File tree

packages/router/src/unplugin/codegen/generateRouteMap.spec.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,55 @@ describe('generateRouteNamedMap', () => {
463463
`)
464464
})
465465

466+
it('types params added by a path override', () => {
467+
const tree = new PrefixTree(DEFAULT_OPTIONS)
468+
const node = tree.insert('users/profile', 'users/profile.vue')
469+
node.value.setOverride('users/profile', { path: '/users/:id' })
470+
expect(
471+
formatExports(generateRouteNamedMap(tree, DEFAULT_OPTIONS, new Map()))
472+
).toMatchInlineSnapshot(`
473+
"export interface RouteNamedMap {
474+
'/users/profile': RouteRecordInfo<
475+
'/users/profile',
476+
'/users/:id',
477+
{ id: ParamValue<true> },
478+
{ id: ParamValue<false> },
479+
| never
480+
>,
481+
}"
482+
`)
483+
})
484+
485+
it('does not inherit parent params on an absolute path override', () => {
486+
const tree = new PrefixTree(DEFAULT_OPTIONS)
487+
tree.insert('org/[orgId]', 'org/[orgId].vue')
488+
const child = tree.insert(
489+
'org/[orgId]/dashboard',
490+
'org/[orgId]/dashboard.vue'
491+
)
492+
child.value.setOverride('org/[orgId]/dashboard', { path: '/dash/:id' })
493+
expect(
494+
formatExports(generateRouteNamedMap(tree, DEFAULT_OPTIONS, new Map()))
495+
).toMatchInlineSnapshot(`
496+
"export interface RouteNamedMap {
497+
'/org/[orgId]': RouteRecordInfo<
498+
'/org/[orgId]',
499+
'/org/:orgId',
500+
{ orgId: ParamValue<true> },
501+
{ orgId: ParamValue<false> },
502+
| '/org/[orgId]/dashboard'
503+
>,
504+
'/org/[orgId]/dashboard': RouteRecordInfo<
505+
'/org/[orgId]/dashboard',
506+
'/dash/:id',
507+
{ id: ParamValue<true> },
508+
{ id: ParamValue<false> },
509+
| never
510+
>,
511+
}"
512+
`)
513+
})
514+
466515
it('adds children route names', () => {
467516
const tree = new PrefixTree(DEFAULT_OPTIONS)
468517
tree.insert('parent', 'parent.vue')

packages/router/src/unplugin/codegen/generateRouteResolver.spec.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,90 @@ describe('generateRouteResolver', () => {
14191419
expect(resolver).toMatchSnapshot()
14201420
})
14211421

1422+
describe('path overrides', () => {
1423+
// FIXME: `node.regexp` and `node.matcherPatternPathDynamicParts` are built
1424+
// from the file segments and ignore `overrides.path`, while
1425+
// `node.pathParams` follows the override. `MatcherPatternPathDynamic`
1426+
// consumes them positionally, so they disagree today. The snapshots below
1427+
// are the wanted output and are marked as failing until the matcher honors
1428+
// overrides.
1429+
it.todo('builds the matcher regexp from an absolute path override', () => {
1430+
const tree = new PrefixTree(DEFAULT_OPTIONS)
1431+
const node = tree.insert('shop/[id]', 'shop/[id].vue')
1432+
node.value.setOverride('shop/[id]', { path: '/store/:slug' })
1433+
1434+
// the regexp still matches `/shop/:id` while the param is named `slug`
1435+
expect(
1436+
generateRouteResolver(
1437+
tree,
1438+
DEFAULT_OPTIONS,
1439+
new ImportsMap(),
1440+
new Map()
1441+
)
1442+
// FIXME: should the name change and be similar to the path?
1443+
).toMatchInlineSnapshot(`
1444+
"
1445+
const __route_0 = normalizeRouteRecord({
1446+
name: '/shop/[id]',
1447+
path: new MatcherPatternPathDynamic(
1448+
/^\\/store\\/([^/]+?)$/i,
1449+
{
1450+
slug: [/* no parser */],
1451+
},
1452+
["store",1],
1453+
/* trailingSlash */
1454+
),
1455+
components: {
1456+
'default': () => import('shop/[id].vue')
1457+
},
1458+
})
1459+
1460+
export const resolver = createFixedResolver([
1461+
__route_0, // /store/:slug
1462+
])
1463+
"
1464+
`)
1465+
})
1466+
1467+
it.todo('builds one capture group per param of the override path', () => {
1468+
const tree = new PrefixTree(DEFAULT_OPTIONS)
1469+
const node = tree.insert('shop/[id]', 'shop/[id].vue')
1470+
node.value.setOverride('shop/[id]', { path: '/shop/:a/:b' })
1471+
1472+
// two params for a single capture group
1473+
expect(
1474+
generateRouteResolver(
1475+
tree,
1476+
DEFAULT_OPTIONS,
1477+
new ImportsMap(),
1478+
new Map()
1479+
)
1480+
).toMatchInlineSnapshot(`
1481+
"
1482+
const __route_0 = normalizeRouteRecord({
1483+
name: '/shop/[id]',
1484+
path: new MatcherPatternPathDynamic(
1485+
/^\\/shop\\/([^/]+?)\\/([^/]+?)$/i,
1486+
{
1487+
a: [/* no parser */],
1488+
b: [/* no parser */],
1489+
},
1490+
["shop",1,1],
1491+
/* trailingSlash */
1492+
),
1493+
components: {
1494+
'default': () => import('shop/[id].vue')
1495+
},
1496+
})
1497+
1498+
export const resolver = createFixedResolver([
1499+
__route_0, // /shop/:a/:b
1500+
])
1501+
"
1502+
`)
1503+
})
1504+
})
1505+
14221506
describe('aliases', () => {
14231507
it('generates alias records for static alias paths', () => {
14241508
const tree = new PrefixTree(DEFAULT_OPTIONS)

packages/router/src/unplugin/codegen/generateRouteResolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ function generatePathCode(
325325
${node.regexp},
326326
${generatePathParamsOptions(params, importsMap, paramParsersMap)},
327327
${JSON.stringify(node.matcherPatternPathDynamicParts)},
328-
${node.isSplat ? 'null,' : '/* trailingSlash */'}
328+
${node.endsWithSplat ? 'null,' : '/* trailingSlash */'}
329329
),`
330330
} else {
331331
return `path: new MatcherPatternPathStatic(${toStringLiteral(node.fullPath)}),`

0 commit comments

Comments
 (0)