Skip to content

Commit d45c26c

Browse files
committed
fix: pass null to splat params
1 parent 415aa01 commit d45c26c

File tree

14 files changed

+175
-60
lines changed

14 files changed

+175
-60
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
]
140140
},
141141
"dependencies": {
142+
"@babel/generator": "^7.28.3",
142143
"@vue-macros/common": "3.0.0-beta.23",
143144
"@vue/language-core": "^3.0.7",
144145
"ast-walker-scope": "^0.8.2",
@@ -171,6 +172,7 @@
171172
"@posva/prompts": "^2.4.4",
172173
"@shikijs/vitepress-twoslash": "3.12.2",
173174
"@tanstack/vue-query": "^5.87.4",
175+
"@types/babel__generator": "^7.27.0",
174176
"@types/node": "^24.3.0",
175177
"@types/picomatch": "^4.0.2",
176178
"@vitest/coverage-v8": "^3.2.4",
@@ -203,7 +205,7 @@
203205
"vitepress-plugin-llms": "^1.7.5",
204206
"vitest": "^3.2.4",
205207
"vue": "^3.5.21",
206-
"vue-router": "https://pkg.pr.new/vue-router@be1679c",
208+
"vue-router": "https://pkg.pr.new/vue-router@ecd1303",
207209
"vue-router-mock": "^2.0.0",
208210
"vue-tsc": "^3.0.7",
209211
"vuefire": "^3.2.2",

playground-experimental/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@
1818
"mande": "^2.0.9",
1919
"pinia": "^3.0.3",
2020
"vue": "^3.5.18",
21-
"vue-router": "https://pkg.pr.new/vue-router@be1679c"
21+
"vue-router": "https://pkg.pr.new/vue-router@ecd1303"
2222
}
2323
}

playground-experimental/src/pages/[...path].vue

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import doc from '../main.ts?raw'
33
44
const route = useRoute()
5+
const router = useRouter()
56
route.params.path
67
78
console.log('typeof', typeof route.params.active)
@@ -22,13 +23,30 @@ definePage({
2223
page: {
2324
parser: 'int',
2425
default: 1,
25-
format: 'array',
26+
format: 'value',
2627
},
27-
other: 'bool',
28-
active: {
28+
other: {
2929
parser: 'bool',
3030
default: false,
3131
},
32+
active: {
33+
parser: 'bool',
34+
default: true,
35+
},
36+
37+
multi: {
38+
format: 'array',
39+
},
40+
41+
req: {
42+
parser: 'int',
43+
default: -1,
44+
},
45+
46+
when: {
47+
parser: 'date',
48+
default: () => new Date(),
49+
},
3250
},
3351
},
3452
})
@@ -39,7 +57,7 @@ definePage({
3957
<h1>Not Found</h1>
4058

4159
<pre>{{ $route.params }}</pre>
42-
<pre>{{ $route.params }}</pre>
60+
<pre>{{ $route.query }}</pre>
4361
<pre>{{ $route.meta }}</pre>
4462
</main>
4563
</template>
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script lang="ts" setup>
2+
const route = useRoute()
3+
console.log(route.params.when)
4+
</script>
5+
6+
<template>
7+
<h1>Date</h1>
8+
<pre>{{ typeof route.params.when }} - {{ route.params.when }}</pre>
9+
</template>

playground-experimental/src/pages/users/[userId=int].vue

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ definePage({
99
// path: '/users/:userId',
1010
params: {
1111
query: {
12-
anyParam: {},
12+
anyParam: {
13+
default: '',
14+
},
1315
page: {
1416
parser: 'int',
1517
default: 1,
Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,31 @@
1-
import { type ParamParser, miss } from 'vue-router/experimental'
1+
import { defineParamParser, miss } from 'vue-router/experimental'
22

3-
export const parser: ParamParser<Date, string> = {
4-
get: (value: string): Date => {
5-
const asDate = new Date(value)
6-
if (Number.isNaN(asDate.getTime())) {
7-
throw miss(`Invalid date: "${value}"`)
8-
}
3+
function toDate(value: string): Date {
4+
const asDate = new Date(value)
5+
if (Number.isNaN(asDate.getTime())) {
6+
throw miss(`Invalid date: "${value}"`)
7+
}
98

10-
return asDate
11-
},
12-
set: (value: Date): string =>
9+
return asDate
10+
}
11+
12+
function toString(value: Date): string {
13+
return (
1314
value
1415
.toISOString()
1516
// allows keeping simple dates like 2023-10-01 without time
1617
// while still being able to parse full dates like 2023-10-01T12:00:00.000Z
17-
.replace('T00:00:00.000Z', ''),
18+
.replace('T00:00:00.000Z', '')
19+
)
1820
}
21+
22+
export const parser = defineParamParser({
23+
get: (value: string | string[] | null) => {
24+
if (!value) {
25+
throw miss()
26+
}
27+
return Array.isArray(value) ? value.map(toDate) : toDate(value)
28+
},
29+
set: (value: Date | Date[]) =>
30+
Array.isArray(value) ? value.map(toString) : toString(value),
31+
})

playground-experimental/typed-router.d.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,21 @@ declare module 'vue-router/auto-routes' {
2222
*/
2323
export interface RouteNamedMap {
2424
'/(home)': RouteRecordInfo<'/(home)', '/', Record<never, never>, Record<never, never>>,
25-
'not-found': RouteRecordInfo<'not-found', '/:path(.*)', { path: string, page?: number, other: boolean, active?: boolean }, { path: string, page: number, other: boolean, active: boolean }>,
25+
'not-found': RouteRecordInfo<'not-found', '/:path(.*)', { path: string, page?: number, other?: boolean, active?: boolean, multi: string[], req?: number, when?: Exclude<Param_date, unknown[]> }, { path: string, page: number, other: boolean, active: boolean, multi: string[], req: number, when: Exclude<Param_date, unknown[]> }>,
2626
'/a.[b].c.[d]': RouteRecordInfo<'/a.[b].c.[d]', '/a/:b/c/:d', { b: string, d: string }, { b: string, d: string }>,
2727
'/b': RouteRecordInfo<'/b', '/b', Record<never, never>, Record<never, never>>,
28-
'/blog/[slug]+': RouteRecordInfo<'/blog/[slug]+', '/blog/:slug+', { slug: string[] }, { slug: [string, ...string[]] }>,
28+
'/blog/[slug]+': RouteRecordInfo<'/blog/[slug]+', '/blog/:slug+', { slug: string[] }, { slug: string[] }>,
2929
'/blog/[[slugOptional]]+': RouteRecordInfo<'/blog/[[slugOptional]]+', '/blog/:slugOptional*', { slugOptional?: string[] }, { slugOptional: string[] }>,
3030
'/blog/info/(info)': RouteRecordInfo<'/blog/info/(info)', '/blog/info', Record<never, never>, Record<never, never>>,
3131
'/blog/info/[[section]]': RouteRecordInfo<'/blog/info/[[section]]', '/blog/info/:section?', { section?: string | null }, { section: string | null }>,
32-
'/events/[when=date]': RouteRecordInfo<'/events/[when=date]', '/events/:when', { when: string }, { when: string }>,
32+
'/events/[when=date]': RouteRecordInfo<'/events/[when=date]', '/events/:when', { when: Exclude<Param_date, unknown[]> }, { when: Exclude<Param_date, unknown[]> }>,
33+
'/events/repeat/[when=date]+': RouteRecordInfo<'/events/repeat/[when=date]+', '/events/repeat/:when+', { when: Extract<Param_date, unknown[]> }, { when: Extract<Param_date, unknown[]> }>,
3334
'/manually-added': RouteRecordInfo<'/manually-added', '/manually-added', Record<never, never>, Record<never, never>>,
3435
'/tests/[[optional]]/end': RouteRecordInfo<'/tests/[[optional]]/end', '/tests/:optional?/end', { optional?: string | null }, { optional: string | null }>,
3536
'/u[name]': RouteRecordInfo<'/u[name]', '/u:name', { name: string }, { name: string }, '/u[name]/24' | '/u[name]/[userId=int]'>,
36-
'/u[name]/[userId=int]': RouteRecordInfo<'/u[name]/[userId=int]', '/u:name/:userId', { name: string, userId: string }, { name: string, userId: string }>,
37+
'/u[name]/[userId=int]': RouteRecordInfo<'/u[name]/[userId=int]', '/u:name/:userId', { name: string, userId: number }, { name: string, userId: number }>,
3738
'/u[name]/24': RouteRecordInfo<'/u[name]/24', '/u:name/24', { name: string }, { name: string }>,
38-
'/users/[userId=int]': RouteRecordInfo<'/users/[userId=int]', '/users/:userId', { userId: string, anyParam: null, page?: number }, { userId: string, anyParam: null, page: number }>,
39+
'/users/[userId=int]': RouteRecordInfo<'/users/[userId=int]', '/users/:userId', { userId: number, anyParam?: string, page?: number }, { userId: number, anyParam: string, page: number }>,
3940
'/users/sub-[first]-[second]': RouteRecordInfo<'/users/sub-[first]-[second]', '/users/sub-:first-:second', { first: string, second: string }, { first: string, second: string }>,
4041
}
4142

@@ -86,6 +87,10 @@ declare module 'vue-router/auto-routes' {
8687
routes: '/events/[when=date]'
8788
views: never
8889
}
90+
'src/pages/events/repeat/[when=date]+.vue': {
91+
routes: '/events/repeat/[when=date]+'
92+
views: never
93+
}
8994
'src/page-outside.vue': {
9095
routes: '/manually-added'
9196
views: never

pnpm-lock.yaml

Lines changed: 24 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/codegen/generateRouteParams.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { TreeNode } from '../core/tree'
2-
import { isTreeParamOptional } from '../core/treeNodeValue'
2+
import {
3+
isTreeParamOptional,
4+
isTreeParamRepeatable,
5+
isTreePathParam,
6+
} from '../core/treeNodeValue'
37

48
export function generateRouteParams(node: TreeNode, isRaw: boolean): string {
59
// node.pathParams is a getter so we compute it once
@@ -33,19 +37,26 @@ export function EXPERIMENTAL_generateRouteParams(
3337
return nodeParams.length > 0
3438
? `{ ${nodeParams
3539
.map((param, i) => {
40+
const isOptional = isTreeParamOptional(param)
41+
const isRepeatable = isTreeParamRepeatable(param)
42+
3643
const type = types[i]
37-
return `${param.paramName}${
38-
isRaw && isTreeParamOptional(param) ? '?' : ''
39-
}: ${
40-
'modifier' in param
41-
? param.repeatable
42-
? param.optional || isRaw // in raw mode, the tuple version is annoying to pass
43-
? 'string[]'
44-
: '[string, ...string[]]'
45-
: param.optional
46-
? 'string | null'
47-
: 'string'
48-
: type
44+
45+
let extractedType: string
46+
47+
if (type?.startsWith('Param_')) {
48+
extractedType = `${isRepeatable ? 'Extract' : 'Exclude'}<${type}, unknown[]>`
49+
} else {
50+
extractedType = `${type ?? 'string'}${isRepeatable ? '[]' : ''}`
51+
}
52+
53+
extractedType +=
54+
isTreePathParam(param) && isOptional && !isRepeatable
55+
? ' | null'
56+
: ''
57+
58+
return `${param.paramName}${isRaw && isOptional ? '?' : ''}: ${
59+
extractedType
4960
}`
5061
})
5162
.join(', ')} }`

0 commit comments

Comments
 (0)