Skip to content

Commit d1e8ac1

Browse files
committed
fix(experimental): keep last hash param instead of throwing
1 parent e7f4f79 commit d1e8ac1

4 files changed

Lines changed: 53 additions & 26 deletions

File tree

‎packages/router/src/unplugin/codegen/generateRouteParams.spec.ts‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -277,15 +277,17 @@ describe('EXPERIMENTAL_generateRouteParams', () => {
277277
expect(isTreeParamOptional(node.hashParams[0]!)).toBe(false)
278278
})
279279

280-
it('rejects multiple named hash params before generating types', () => {
280+
it('keeps only the last hash param when several are declared', () => {
281281
const node = createTreeWithParam('page')
282282
node.setCustomRouteBlock('page.vue', {
283-
params: { hash: { section: 'string', tab: 'string' } },
283+
params: { hash: { section: 'string', other: 'int', tab: 'bool' } },
284284
})
285-
expect(() => node.params).toThrow(
286-
expect.objectContaining({ name: 'VUE_ROUTER_B0022' })
287-
)
288-
expect('Only one hash param can be declared per route').toHaveBeenWarned()
285+
expect(node.hashParams).toEqual([
286+
{ paramName: 'tab', parser: 'bool', hash: true },
287+
])
288+
// the getter runs again but warns once
289+
void node.params
290+
expect('using "tab" and ignoring: section, other').toHaveBeenWarnedTimes(1)
289291
})
290292

291293
it('preserves hash declarations when merging overrides', () => {

‎packages/router/src/unplugin/core/definePage.spec.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,16 +316,18 @@ definePage({ params: { hash: {
316316
expect(isTreeParamOptional(node.hashParams[0]!)).toBe(optional)
317317
})
318318

319-
it('rejects multiple named hash parameters with options', () => {
319+
it('keeps the last of multiple named hash parameters with options', () => {
320320
const code = `definePage({ params: { hash: {
321321
section: { required: true }, tab: 'int'
322322
} } })`
323323
const node = new TreeNodeValueStatic('test', undefined)
324324
node.setOverride('test.ts', {
325325
params: extractDefinePageInfo(code, 'test.ts')!.params,
326326
})
327-
expect(() => node.hashParams).toThrow()
328-
expect('Only one hash param can be declared per route.').toHaveBeenWarned()
327+
expect(node.hashParams).toEqual([
328+
{ paramName: 'tab', parser: 'int', hash: true },
329+
])
330+
expect('using "tab" and ignoring: section').toHaveBeenWarned()
329331
})
330332

331333
it('extracts all types of params', () => {

‎packages/router/src/unplugin/core/treeNodeValue.ts‎

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ class _TreeNodeValueBase {
6161
*/
6262
private _warnedParsers = new Set<string>()
6363

64+
/**
65+
* Ignored hash params last warned about, for the same reason.
66+
*/
67+
private _warnedHashParams = ''
68+
6469
/**
6570
* View name (Vue Router feature) mapped to their corresponding file. By default, the view name is `default` unless
6671
* specified with a `@` e.g. `index@aux.vue` will have a view name of `aux`.
@@ -148,22 +153,36 @@ class _TreeNodeValueBase {
148153
* Gets hash params declared on this node.
149154
*/
150155
get hashParams(): TreeHashParam[] {
151-
const entries = Object.entries(this.overrides.params?.hash ?? {})
152-
if (entries.length > 1) {
153-
throw diagnostics.VUE_ROUTER_B0022({
154-
paramNames: entries.map(([name]) => name).join(', '),
155-
})
156-
}
157-
return entries.map(([paramName, config]) => {
158-
if (typeof config === 'string') config = { parser: config }
159-
return {
160-
paramName,
161-
parser: config.parser || null,
162-
defaultValue: config.default,
163-
required: config.required,
164-
hash: true,
156+
const hash = this.overrides.params?.hash ?? {}
157+
const paramNames = Object.keys(hash)
158+
// the last one wins, like other overrides
159+
const paramName = paramNames.at(-1)
160+
if (paramNames.length > 1) {
161+
const ignoredParamNames = paramNames.slice(0, -1).join(', ')
162+
if (this._warnedHashParams !== ignoredParamNames) {
163+
this._warnedHashParams = ignoredParamNames
164+
diagnostics.VUE_ROUTER_B0022({
165+
segment: this.rawSegment,
166+
usedParamName: paramName!,
167+
ignoredParamNames,
168+
})
165169
}
166-
})
170+
}
171+
172+
let config = paramName ? hash[paramName] : undefined
173+
if (typeof config === 'string') config = { parser: config }
174+
175+
return paramName && config
176+
? [
177+
{
178+
paramName,
179+
parser: config.parser || null,
180+
defaultValue: config.default,
181+
required: config.required,
182+
hash: true,
183+
},
184+
]
185+
: []
167186
}
168187

169188
/**

‎packages/router/src/unplugin/diagnostics.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ export const diagnostics = /*#__PURE__*/ defineDiagnostics({
1515
reporters: [/*#__PURE__*/ createConsoleReporter()],
1616
codes: {
1717
VUE_ROUTER_B0022: {
18-
why: (p: { paramNames: string }) =>
19-
`Only one hash param can be declared per route. Found: ${p.paramNames}.`,
18+
why: (p: {
19+
segment: string
20+
usedParamName: string
21+
ignoredParamNames: string
22+
}) =>
23+
`Only one hash param can be declared per route. In "${p.segment}", using "${p.usedParamName}" and ignoring: ${p.ignoredParamNames}.`,
2024
fix: 'Use one hash parser that returns an object with all extracted values.',
2125
},
2226
// --- core/definePage.ts ---

0 commit comments

Comments
 (0)