Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 15 additions & 27 deletions packages/router-core/src/new-process-route-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,35 +220,23 @@ function parseSegments<TRouteLike extends RouteLike>(
switch (kind) {
case SEGMENT_TYPE_PATHNAME: {
const value = path.substring(segment[2], segment[3])
let name = value
let staticChildren: Map<string, StaticSegmentNode<TRouteLike>>
if (caseSensitive) {
const existingNode = node.static?.get(value)
if (existingNode) {
nextNode = existingNode
} else {
node.static ??= new Map()
const next = createStaticNode<TRouteLike>(
route.fullPath ?? route.from,
)
next.parent = node
next.depth = depth
nextNode = next
node.static.set(value, next)
}
staticChildren = node.static ??= new Map()
} else {
const name = value.toLowerCase()
const existingNode = node.staticInsensitive?.get(name)
if (existingNode) {
nextNode = existingNode
} else {
node.staticInsensitive ??= new Map()
const next = createStaticNode<TRouteLike>(
route.fullPath ?? route.from,
)
next.parent = node
next.depth = depth
nextNode = next
node.staticInsensitive.set(name, next)
}
name = value.toLowerCase()
staticChildren = node.staticInsensitive ??= new Map()
}
const existingNode = staticChildren.get(name)
if (existingNode) {
nextNode = existingNode
} else {
const next = createStaticNode<TRouteLike>(path)
next.parent = node
next.depth = depth
nextNode = next
staticChildren.set(name, next)
}
break
}
Expand Down
62 changes: 62 additions & 0 deletions packages/router-core/tests/new-process-route-tree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,63 @@ describe('findRouteMatch', () => {
})

describe('case sensitivity competition', () => {
it('reuses an insensitive static node for differently cased siblings', () => {
const tree = {
id: '__root__',
isRoot: true,
fullPath: '/',
path: '/',
children: [
{
id: '/Docs/API',
fullPath: '/Docs/API',
path: 'Docs/API',
},
{
id: '/docs/guide',
fullPath: '/docs/guide',
path: 'docs/guide',
},
],
}
const { processedTree } = processRouteTree(tree)
const docs = processedTree.segmentTree.staticInsensitive?.get('docs')

expect(processedTree.segmentTree.staticInsensitive?.size).toBe(1)
expect(docs?.staticInsensitive?.size).toBe(2)
expect(findRouteMatch('/DOCS/api', processedTree)?.route.id).toBe(
'/Docs/API',
)
expect(findRouteMatch('/Docs/GUIDE', processedTree)?.route.id).toBe(
'/docs/guide',
)
})
it('allows a route to override a sensitive tree default', () => {
const tree = {
id: '__root__',
isRoot: true,
fullPath: '/',
path: '/',
children: [
{
id: '/Strict',
fullPath: '/Strict',
path: 'Strict',
},
{
id: '/loose',
fullPath: '/loose',
path: 'loose',
options: { caseSensitive: false },
},
],
}
const { processedTree } = processRouteTree(tree, true)

expect(findRouteMatch('/Strict', processedTree)?.route.id).toBe('/Strict')
expect(findRouteMatch('/strict', processedTree)).toBeNull()
expect(findRouteMatch('/LOOSE', processedTree)?.route.id).toBe('/loose')
})
it('a case sensitive segment early on should not prevent a case insensitive match', () => {
const tree = {
id: '__root__',
Expand Down Expand Up @@ -1645,6 +1702,7 @@ describe('processRouteMasks', { sequential: true }, () => {
{ from: '/a/$param/d', routeTree },
{ from: '/a/{-$optional}/d', routeTree },
{ from: '/a/b/{$}.txt', routeTree },
{ from: '/Admin/Panel', routeTree },
]
processRouteMasks(routeMasks, processedTree)
const aBranch = processedTree.masksTree?.staticInsensitive?.get('a')
Expand All @@ -1657,6 +1715,10 @@ describe('processRouteMasks', { sequential: true }, () => {
const res = findFlatMatch('/a/b/c', processedTree)
expect(res?.route.from).toBe('/a/b/c')
})
it('matches uppercase static route masks case-insensitively', () => {
const res = findFlatMatch('/admin/panel', processedTree)
expect(res?.route.from).toBe('/Admin/Panel')
})
it('can match dynamic route masks w/ `findFlatMatch`', () => {
const res = findFlatMatch('/a/123/d', processedTree)
expect(res?.route.from).toBe('/a/$param/d')
Expand Down
Loading