Skip to content

Commit 1db9467

Browse files
committed
fix: stable route ordering for group folders with same path
Group folders like (user) and (user-home) both resolve to an empty path segment, making TreeNode.compare return 0 and fall back to non-deterministic insertion order. Use rawSegment as tiebreaker for stable sorting.
1 parent 2a2e68a commit 1db9467

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,30 @@ describe('generateRouteFileInfoMap', () => {
226226
`)
227227
})
228228

229+
it('produces stable output for sibling group folders with same path', () => {
230+
const createTree = (insertionOrder: string[]) => {
231+
const tree = new PrefixTree(DEFAULT_OPTIONS)
232+
insertionOrder.forEach(route => {
233+
tree.insert(route, `${route}.vue`)
234+
})
235+
return formatExports(generateRouteFileInfoMap(tree, { root: '' }))
236+
}
237+
238+
const order1 = [
239+
'[username]/(user)/profile',
240+
'[username]/(user-home)/(user-home)',
241+
]
242+
const order2 = [
243+
'[username]/(user-home)/(user-home)',
244+
'[username]/(user)/profile',
245+
]
246+
247+
const result1 = createTree(order1)
248+
const result2 = createTree(order2)
249+
250+
expect(result1).toBe(result2)
251+
})
252+
229253
it('works with children', () => {
230254
const tree = new PrefixTree(DEFAULT_OPTIONS)
231255
tree.insert('parent', 'src/pages/parent.vue')

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,34 @@ describe('generateRouteNamedMap', () => {
684684
`)
685685
})
686686

687+
it('produces stable output for sibling group folders with same path', () => {
688+
const createTree = (insertionOrder: string[]) => {
689+
const tree = new PrefixTree(DEFAULT_OPTIONS)
690+
insertionOrder.forEach(route => {
691+
tree.insert(route, `${route}.vue`)
692+
})
693+
return formatExports(
694+
generateRouteNamedMap(tree, DEFAULT_OPTIONS, new Map())
695+
)
696+
}
697+
698+
// Group folders (user) and (user-home) both resolve to empty path segments
699+
// but should produce stable output regardless of insertion order
700+
const order1 = [
701+
'[username]/(user)/profile',
702+
'[username]/(user-home)/(user-home)',
703+
]
704+
const order2 = [
705+
'[username]/(user-home)/(user-home)',
706+
'[username]/(user)/profile',
707+
]
708+
709+
const result1 = createTree(order1)
710+
const result2 = createTree(order2)
711+
712+
expect(result1).toBe(result2)
713+
})
714+
687715
it('treats files named with parentheses as index inside static folder', () => {
688716
const tree = new PrefixTree(DEFAULT_OPTIONS)
689717

packages/router/src/unplugin/core/tree.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,10 @@ export class TreeNode {
182182
static compare(a: TreeNode, b: TreeNode): number {
183183
// for this case, ASCII, short list, it's better than Internation Collator
184184
// https://stackoverflow.com/questions/77246375/why-localecompare-can-be-faster-than-collator-compare
185-
return a.path.localeCompare(b.path, 'en')
185+
return (
186+
a.path.localeCompare(b.path, 'en') ||
187+
a.value.rawSegment.localeCompare(b.value.rawSegment, 'en')
188+
)
186189
}
187190

188191
/**

0 commit comments

Comments
 (0)