Skip to content

Commit d0fc9f3

Browse files
committed
fix: stable order in children
Fix #664
1 parent 08235ad commit d0fc9f3

File tree

3 files changed

+45
-4
lines changed

3 files changed

+45
-4
lines changed

src/codegen/generateRouteMap.spec.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ describe('generateRouteNamedMap', () => {
156156
tree.insert('a/[id]/index', 'a/[id]/index.vue')
157157
expect(formatExports(generateRouteNamedMap(tree))).toMatchInlineSnapshot(`
158158
"export interface RouteNamedMap {
159-
'/a': RouteRecordInfo<'/a', '/a', Record<never, never>, Record<never, never>, '/a/' | '/a/[id]/' | '/a/[id]'>,
159+
'/a': RouteRecordInfo<'/a', '/a', Record<never, never>, Record<never, never>, '/a/' | '/a/[id]' | '/a/[id]/'>,
160160
'/a/': RouteRecordInfo<'/a/', '/a', Record<never, never>, Record<never, never>>,
161161
'/a/[id]': RouteRecordInfo<'/a/[id]', '/a/:id', { id: ParamValue<true> }, { id: ParamValue<false> }, '/a/[id]/'>,
162162
'/a/[id]/': RouteRecordInfo<'/a/[id]/', '/a/:id', { id: ParamValue<true> }, { id: ParamValue<false> }>,
@@ -190,8 +190,8 @@ describe('generateRouteNamedMap', () => {
190190
tree.insert('parent/other-child', 'parent/other-child.vue')
191191
expect(formatExports(generateRouteNamedMap(tree))).toMatchInlineSnapshot(`
192192
"export interface RouteNamedMap {
193-
'/parent': RouteRecordInfo<'/parent', '/parent', Record<never, never>, Record<never, never>, '/parent/child' | '/parent/child/subchild/grandchild' | '/parent/other-child' | '/parent/child/subchild'>,
194-
'/parent/child': RouteRecordInfo<'/parent/child', '/parent/child', Record<never, never>, Record<never, never>, '/parent/child/subchild/grandchild' | '/parent/child/subchild'>,
193+
'/parent': RouteRecordInfo<'/parent', '/parent', Record<never, never>, Record<never, never>, '/parent/child' | '/parent/child/subchild' | '/parent/child/subchild/grandchild' | '/parent/other-child'>,
194+
'/parent/child': RouteRecordInfo<'/parent/child', '/parent/child', Record<never, never>, Record<never, never>, '/parent/child/subchild' | '/parent/child/subchild/grandchild'>,
195195
'/parent/child/subchild': RouteRecordInfo<'/parent/child/subchild', '/parent/child/subchild', Record<never, never>, Record<never, never>, '/parent/child/subchild/grandchild'>,
196196
'/parent/child/subchild/grandchild': RouteRecordInfo<'/parent/child/subchild/grandchild', '/parent/child/subchild/grandchild', Record<never, never>, Record<never, never>>,
197197
'/parent/other-child': RouteRecordInfo<'/parent/other-child', '/parent/other-child', Record<never, never>, Record<never, never>>,
@@ -296,6 +296,39 @@ describe('generateRouteNamedMap', () => {
296296
}"
297297
`)
298298
})
299+
300+
it('generates stable union types regardless of insertion order', () => {
301+
// Test that same routes inserted in different orders produce identical union types
302+
const createTree = (insertionOrder: string[]) => {
303+
const tree = new PrefixTree(DEFAULT_OPTIONS)
304+
tree.insert('parent', 'parent.vue')
305+
306+
// Insert children in the specified order
307+
insertionOrder.forEach((route) => {
308+
tree.insert(route, `${route}.vue`)
309+
})
310+
311+
return formatExports(generateRouteNamedMap(tree))
312+
}
313+
314+
// Same routes, different insertion orders
315+
const order1 = ['parent/zebra', 'parent/alpha', 'parent/beta']
316+
const order2 = ['parent/alpha', 'parent/zebra', 'parent/beta']
317+
const order3 = ['parent/beta', 'parent/alpha', 'parent/zebra']
318+
319+
const result1 = createTree(order1)
320+
const result2 = createTree(order2)
321+
const result3 = createTree(order3)
322+
323+
// All should be identical due to stable sorting
324+
expect(result1).toBe(result2)
325+
expect(result2).toBe(result3)
326+
327+
// Verify the union type is alphabetically sorted
328+
expect(result1).toContain(
329+
"'/parent/alpha' | '/parent/beta' | '/parent/zebra'"
330+
)
331+
})
299332
})
300333

301334
/**

src/codegen/generateRouteMap.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,13 @@ export function generateRouteRecordInfo(node: TreeNode) {
2929

3030
if (node.children.size > 0) {
3131
const deepNamedChildren = node
32-
.getSortedChildrenDeep()
32+
.getChildrenDeep()
3333
// skip routes that are not added to the types
3434
.filter(
3535
(childRoute) => childRoute.value.components.size > 0 && childRoute.name
3636
)
3737
.map((childRoute) => `'${childRoute.name}'`)
38+
.sort()
3839

3940
if (deepNamedChildren.length > 0) {
4041
typeParams.push(deepNamedChildren.join(' | '))

src/core/tree.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ export class TreeNode {
142142
.sort((a, b) => a.path.localeCompare(b.path))
143143
}
144144

145+
getChildrenDeep(): TreeNode[] {
146+
return Array.from(this.children.values()).flatMap((child) => [
147+
child,
148+
...child.getChildrenDeep(),
149+
])
150+
}
151+
145152
/**
146153
* Delete and detach itself from the tree.
147154
*/

0 commit comments

Comments
 (0)