Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ function RouteComponent() {
to="./$foo/$bar"
params={{ foo: 'foo', bar: 'bar' }}
>
/params-ps/non-nested/$foo/$bar
/params-ps/non-nested/foo/bar
</Link>
<Link
from={Route.fullPath}
data-testid="l-to-non-nested-foo2-bar2"
to="./$foo/$bar"
params={{ foo: 'foo2', bar: 'bar2' }}
>
/params-ps/non-nested/foo2/bar2
</Link>
</li>
</ul>
Expand Down
24 changes: 23 additions & 1 deletion e2e/react-router/basic-file-based/tests/params.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ test.describe('params operations + non-nested routes', () => {
'href',
'/params-ps/non-nested/foo/bar',
)

await fooBarLink.click()
await page.waitForLoadState('networkidle')
await page.waitForURL('/params-ps/non-nested/foo/bar')
const pagePathname = new URL(page.url()).pathname
expect(pagePathname).toBe('/params-ps/non-nested/foo/bar')

Expand All @@ -83,6 +84,27 @@ test.describe('params operations + non-nested routes', () => {
const paramsText = await paramsValue.innerText()
const paramsObj = JSON.parse(paramsText)
expect(paramsObj).toEqual({ foo: 'foo', bar: 'bar' })

const foo2Bar2Link = page.getByTestId('l-to-non-nested-foo2-bar2')

await expect(foo2Bar2Link).toHaveAttribute(
'href',
'/params-ps/non-nested/foo2/bar2',
)
await foo2Bar2Link.click()
await page.waitForURL('/params-ps/non-nested/foo2/bar2')
const pagePathname2 = new URL(page.url()).pathname
expect(pagePathname2).toBe('/params-ps/non-nested/foo2/bar2')

const foo2ParamsValue = page.getByTestId('foo-params-value')
const foo2ParamsText = await foo2ParamsValue.innerText()
const foo2ParamsObj = JSON.parse(foo2ParamsText)
expect(foo2ParamsObj).toEqual({ foo: 'foo2' })

const params2Value = page.getByTestId('foo-bar-params-value')
const params2Text = await params2Value.innerText()
const params2Obj = JSON.parse(params2Text)
expect(params2Obj).toEqual({ foo: 'foo2', bar: 'bar2' })
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,15 @@ function RouteComponent() {
to="./$foo/$bar"
params={{ foo: 'foo', bar: 'bar' }}
>
/params-ps/non-nested/$foo/$bar
/params-ps/non-nested/foo/bar
</Link>
<Link
from={Route.fullPath}
data-testid="l-to-non-nested-foo2-bar2"
to="./$foo/$bar"
params={{ foo: 'foo2', bar: 'bar2' }}
>
/params-ps/non-nested/foo2/bar2
</Link>
</li>
</ul>
Expand Down
24 changes: 23 additions & 1 deletion e2e/solid-router/basic-file-based/tests/params.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ test.describe('params operations + non-nested routes', () => {
'/params-ps/non-nested/foo/bar',
)
await fooBarLink.click()
await page.waitForLoadState('networkidle')
await page.waitForURL('/params-ps/non-nested/foo/bar')

const pagePathname = new URL(page.url()).pathname
expect(pagePathname).toBe('/params-ps/non-nested/foo/bar')

Expand All @@ -158,5 +159,26 @@ test.describe('params operations + non-nested routes', () => {
const paramsText = await paramsValue.innerText()
const paramsObj = JSON.parse(paramsText)
expect(paramsObj).toEqual({ foo: 'foo', bar: 'bar' })

const foo2Bar2Link = page.getByTestId('l-to-non-nested-foo2-bar2')

await expect(foo2Bar2Link).toHaveAttribute(
'href',
'/params-ps/non-nested/foo2/bar2',
)
await foo2Bar2Link.click()
await page.waitForURL('/params-ps/non-nested/foo2/bar2')
const pagePathname2 = new URL(page.url()).pathname
expect(pagePathname2).toBe('/params-ps/non-nested/foo2/bar2')

const foo2ParamsValue = page.getByTestId('foo-params-value')
const foo2ParamsText = await foo2ParamsValue.innerText()
const foo2ParamsObj = JSON.parse(foo2ParamsText)
expect(foo2ParamsObj).toEqual({ foo: 'foo2' })

const params2Value = page.getByTestId('foo-bar-params-value')
const params2Text = await params2Value.innerText()
const params2Obj = JSON.parse(params2Text)
expect(params2Obj).toEqual({ foo: 'foo2', bar: 'bar2' })
})
})
20 changes: 12 additions & 8 deletions packages/router-core/src/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,11 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {

segments.push(
...split.map((part): Segment => {
// strip tailing underscore for non-nested paths
const partToMatch = part.slice(-1) === '_' ? part.slice(0, -1) : part

// Check for wildcard with curly braces: prefix{$}suffix
const wildcardBracesMatch = part.match(WILDCARD_W_CURLY_BRACES_RE)
const wildcardBracesMatch = partToMatch.match(WILDCARD_W_CURLY_BRACES_RE)
if (wildcardBracesMatch) {
const prefix = wildcardBracesMatch[1]
const suffix = wildcardBracesMatch[2]
Expand All @@ -279,7 +282,7 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
}

// Check for optional parameter format: prefix{-$paramName}suffix
const optionalParamBracesMatch = part.match(
const optionalParamBracesMatch = partToMatch.match(
OPTIONAL_PARAM_W_CURLY_BRACES_RE,
)
if (optionalParamBracesMatch) {
Expand All @@ -295,7 +298,7 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
}

// Check for the new parameter format: prefix{$paramName}suffix
const paramBracesMatch = part.match(PARAM_W_CURLY_BRACES_RE)
const paramBracesMatch = partToMatch.match(PARAM_W_CURLY_BRACES_RE)
if (paramBracesMatch) {
const prefix = paramBracesMatch[1]
const paramName = paramBracesMatch[2]
Expand All @@ -309,8 +312,9 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
}

// Check for bare parameter format: $paramName (without curly braces)
if (PARAM_RE.test(part)) {
const paramName = part.substring(1)
if (PARAM_RE.test(partToMatch)) {
const paramName = partToMatch.substring(1)

return {
type: SEGMENT_TYPE_PARAM,
value: '$' + paramName,
Expand All @@ -320,7 +324,7 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
}

// Check for bare wildcard: $ (without curly braces)
if (WILDCARD_RE.test(part)) {
if (WILDCARD_RE.test(partToMatch)) {
return {
type: SEGMENT_TYPE_WILDCARD,
value: '$',
Expand All @@ -332,8 +336,8 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
// Handle regular pathname segment
return {
type: SEGMENT_TYPE_PATHNAME,
value: part.includes('%25')
? part
value: partToMatch.includes('%25')
? partToMatch
.split('%25')
.map((segment) => decodeURI(segment))
.join('%25')
Expand Down
23 changes: 23 additions & 0 deletions packages/router-core/tests/path.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,12 @@ describe('interpolatePath', () => {
params: { _splat: 'sean/cassiere' },
result: '/users/sean/cassiere',
},
{
name: 'should interpolate the non-nested path',
path: '/users/$id_',
params: { id: '123' },
result: '/users/123',
},
])('$name', ({ path, params, decodeCharMap, result }) => {
expect(
interpolatePath({
Expand Down Expand Up @@ -654,6 +660,14 @@ describe('matchPathname', () => {
},
expectedMatchedParams: { id: '123' },
},
{
name: 'should match and return the non-nested named path params',
input: '/users/123',
matchingOptions: {
to: '/users/$id_',
},
expectedMatchedParams: { id: '123' },
},
{
name: 'should match and return the the splat param',
input: '/users/123',
Expand Down Expand Up @@ -893,6 +907,15 @@ describe('parsePathname', () => {
{ type: SEGMENT_TYPE_PARAM, value: '$bar' },
],
},
{
name: 'should handle non-nested named params',
to: '/foo/$bar_',
expected: [
{ type: SEGMENT_TYPE_PATHNAME, value: '/' },
{ type: SEGMENT_TYPE_PATHNAME, value: 'foo' },
{ type: SEGMENT_TYPE_PARAM, value: '$bar' },
],
},
{
name: 'should handle named params at the root',
to: '/$bar',
Expand Down
Loading