@tanstack/router-core 1.121.0-alpha.27 → 1.121.0-alpha.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +31 -1
- package/dist/cjs/RouterProvider.d.cts +2 -1
- package/dist/cjs/defer.cjs +1 -1
- package/dist/cjs/defer.cjs.map +1 -1
- package/dist/cjs/global.d.cts +7 -0
- package/dist/cjs/index.cjs +1 -2
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +6 -6
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/link.d.cts +12 -0
- package/dist/cjs/lru-cache.cjs +62 -0
- package/dist/cjs/lru-cache.cjs.map +1 -0
- package/dist/cjs/lru-cache.d.cts +5 -0
- package/dist/cjs/not-found.cjs +1 -1
- package/dist/cjs/not-found.cjs.map +1 -1
- package/dist/cjs/path.cjs +316 -148
- package/dist/cjs/path.cjs.map +1 -1
- package/dist/cjs/path.d.cts +18 -24
- package/dist/cjs/qss.cjs.map +1 -1
- package/dist/cjs/redirect.cjs +3 -0
- package/dist/cjs/redirect.cjs.map +1 -1
- package/dist/cjs/route.cjs +6 -12
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +29 -9
- package/dist/cjs/router.cjs +453 -272
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +55 -85
- package/dist/cjs/scroll-restoration.cjs +20 -13
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.d.cts +9 -1
- package/dist/cjs/searchMiddleware.cjs.map +1 -1
- package/dist/cjs/searchParams.cjs.map +1 -1
- package/dist/cjs/ssr/client.cjs +10 -0
- package/dist/cjs/ssr/client.cjs.map +1 -0
- package/dist/cjs/ssr/client.d.cts +5 -0
- package/dist/cjs/ssr/createRequestHandler.cjs +50 -0
- package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -0
- package/dist/cjs/ssr/createRequestHandler.d.cts +9 -0
- package/dist/cjs/ssr/handlerCallback.cjs +7 -0
- package/dist/cjs/ssr/handlerCallback.cjs.map +1 -0
- package/dist/cjs/ssr/handlerCallback.d.cts +9 -0
- package/dist/cjs/ssr/headers.cjs +39 -0
- package/dist/cjs/ssr/headers.cjs.map +1 -0
- package/dist/cjs/ssr/headers.d.cts +5 -0
- package/dist/cjs/ssr/json.cjs +14 -0
- package/dist/cjs/ssr/json.cjs.map +1 -0
- package/dist/cjs/ssr/json.d.cts +4 -0
- package/dist/cjs/ssr/seroval-plugins.cjs +34 -0
- package/dist/cjs/ssr/seroval-plugins.cjs.map +1 -0
- package/dist/cjs/ssr/seroval-plugins.d.cts +10 -0
- package/dist/cjs/ssr/server.cjs +13 -0
- package/dist/cjs/ssr/server.cjs.map +1 -0
- package/dist/cjs/ssr/server.d.cts +6 -0
- package/dist/cjs/ssr/ssr-client.cjs +159 -0
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -0
- package/dist/cjs/ssr/ssr-client.d.cts +29 -0
- package/dist/cjs/ssr/ssr-server.cjs +107 -0
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -0
- package/dist/cjs/ssr/ssr-server.d.cts +18 -0
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +183 -0
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -0
- package/dist/cjs/ssr/transformStreamWithRouter.d.cts +6 -0
- package/dist/cjs/ssr/tsrScript.cjs +4 -0
- package/dist/cjs/ssr/tsrScript.cjs.map +1 -0
- package/dist/cjs/ssr/tsrScript.d.cts +0 -0
- package/dist/cjs/utils.cjs +7 -25
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +1 -6
- package/dist/esm/Matches.d.ts +31 -1
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/RouterProvider.d.ts +2 -1
- package/dist/esm/defer.js +1 -1
- package/dist/esm/defer.js.map +1 -1
- package/dist/esm/global.d.ts +7 -0
- package/dist/esm/index.d.ts +6 -6
- package/dist/esm/index.js +2 -3
- package/dist/esm/link.d.ts +12 -0
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/lru-cache.d.ts +5 -0
- package/dist/esm/lru-cache.js +62 -0
- package/dist/esm/lru-cache.js.map +1 -0
- package/dist/esm/not-found.js +1 -1
- package/dist/esm/not-found.js.map +1 -1
- package/dist/esm/path.d.ts +18 -24
- package/dist/esm/path.js +316 -148
- package/dist/esm/path.js.map +1 -1
- package/dist/esm/qss.js.map +1 -1
- package/dist/esm/redirect.js +3 -0
- package/dist/esm/redirect.js.map +1 -1
- package/dist/esm/route.d.ts +29 -9
- package/dist/esm/route.js +6 -12
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +55 -85
- package/dist/esm/router.js +462 -281
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration.d.ts +9 -1
- package/dist/esm/scroll-restoration.js +20 -13
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/searchMiddleware.js.map +1 -1
- package/dist/esm/searchParams.js.map +1 -1
- package/dist/esm/ssr/client.d.ts +5 -0
- package/dist/esm/ssr/client.js +10 -0
- package/dist/esm/ssr/client.js.map +1 -0
- package/dist/esm/ssr/createRequestHandler.d.ts +9 -0
- package/dist/esm/ssr/createRequestHandler.js +50 -0
- package/dist/esm/ssr/createRequestHandler.js.map +1 -0
- package/dist/esm/ssr/handlerCallback.d.ts +9 -0
- package/dist/esm/ssr/handlerCallback.js +7 -0
- package/dist/esm/ssr/handlerCallback.js.map +1 -0
- package/dist/esm/ssr/headers.d.ts +5 -0
- package/dist/esm/ssr/headers.js +39 -0
- package/dist/esm/ssr/headers.js.map +1 -0
- package/dist/esm/ssr/json.d.ts +4 -0
- package/dist/esm/ssr/json.js +14 -0
- package/dist/esm/ssr/json.js.map +1 -0
- package/dist/esm/ssr/seroval-plugins.d.ts +10 -0
- package/dist/esm/ssr/seroval-plugins.js +34 -0
- package/dist/esm/ssr/seroval-plugins.js.map +1 -0
- package/dist/esm/ssr/server.d.ts +6 -0
- package/dist/esm/ssr/server.js +13 -0
- package/dist/esm/ssr/server.js.map +1 -0
- package/dist/esm/ssr/ssr-client.d.ts +29 -0
- package/dist/esm/ssr/ssr-client.js +159 -0
- package/dist/esm/ssr/ssr-client.js.map +1 -0
- package/dist/esm/ssr/ssr-server.d.ts +18 -0
- package/dist/esm/ssr/ssr-server.js +107 -0
- package/dist/esm/ssr/ssr-server.js.map +1 -0
- package/dist/esm/ssr/transformStreamWithRouter.d.ts +6 -0
- package/dist/esm/ssr/transformStreamWithRouter.js +183 -0
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -0
- package/dist/esm/ssr/tsrScript.d.ts +0 -0
- package/dist/esm/ssr/tsrScript.js +5 -0
- package/dist/esm/ssr/tsrScript.js.map +1 -0
- package/dist/esm/utils.d.ts +1 -6
- package/dist/esm/utils.js +8 -26
- package/dist/esm/utils.js.map +1 -1
- package/package.json +29 -2
- package/src/Matches.ts +40 -1
- package/src/RouterProvider.ts +2 -1
- package/src/global.ts +9 -0
- package/src/index.ts +12 -20
- package/src/link.ts +12 -0
- package/src/lru-cache.ts +68 -0
- package/src/path.ts +424 -174
- package/src/redirect.ts +3 -0
- package/src/route.ts +44 -13
- package/src/router.ts +580 -312
- package/src/scroll-restoration.ts +30 -18
- package/src/ssr/client.ts +5 -0
- package/src/ssr/createRequestHandler.ts +74 -0
- package/src/ssr/handlerCallback.ts +15 -0
- package/src/ssr/headers.ts +51 -0
- package/src/ssr/json.ts +18 -0
- package/src/ssr/seroval-plugins.ts +43 -0
- package/src/ssr/server.ts +10 -0
- package/src/ssr/ssr-client.ts +242 -0
- package/src/ssr/ssr-server.ts +132 -0
- package/src/ssr/transformStreamWithRouter.ts +259 -0
- package/src/ssr/tsrScript.ts +7 -0
- package/src/utils.ts +10 -39
- package/src/vite-env.d.ts +4 -0
- package/dist/cjs/serializer.d.cts +0 -22
- package/dist/esm/serializer.d.ts +0 -22
- package/src/serializer.ts +0 -32
package/src/path.ts
CHANGED
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import { last } from './utils'
|
|
2
|
+
import type { LRUCache } from './lru-cache'
|
|
2
3
|
import type { MatchLocation } from './RouterProvider'
|
|
3
4
|
import type { AnyPathParams } from './route'
|
|
4
5
|
|
|
6
|
+
export const SEGMENT_TYPE_PATHNAME = 0
|
|
7
|
+
export const SEGMENT_TYPE_PARAM = 1
|
|
8
|
+
export const SEGMENT_TYPE_WILDCARD = 2
|
|
9
|
+
export const SEGMENT_TYPE_OPTIONAL_PARAM = 3
|
|
10
|
+
|
|
5
11
|
export interface Segment {
|
|
6
|
-
type:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
readonly type:
|
|
13
|
+
| typeof SEGMENT_TYPE_PATHNAME
|
|
14
|
+
| typeof SEGMENT_TYPE_PARAM
|
|
15
|
+
| typeof SEGMENT_TYPE_WILDCARD
|
|
16
|
+
| typeof SEGMENT_TYPE_OPTIONAL_PARAM
|
|
17
|
+
readonly value: string
|
|
18
|
+
readonly prefixSegment?: string
|
|
19
|
+
readonly suffixSegment?: string
|
|
20
|
+
// Indicates if there is a static segment after this required/optional param
|
|
21
|
+
readonly hasStaticAfter?: boolean
|
|
11
22
|
}
|
|
12
23
|
|
|
13
24
|
export function joinPaths(paths: Array<string | undefined>) {
|
|
@@ -91,6 +102,52 @@ interface ResolvePathOptions {
|
|
|
91
102
|
to: string
|
|
92
103
|
trailingSlash?: 'always' | 'never' | 'preserve'
|
|
93
104
|
caseSensitive?: boolean
|
|
105
|
+
parseCache?: ParsePathnameCache
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function segmentToString(segment: Segment): string {
|
|
109
|
+
const { type, value } = segment
|
|
110
|
+
if (type === SEGMENT_TYPE_PATHNAME) {
|
|
111
|
+
return value
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const { prefixSegment, suffixSegment } = segment
|
|
115
|
+
|
|
116
|
+
if (type === SEGMENT_TYPE_PARAM) {
|
|
117
|
+
const param = value.substring(1)
|
|
118
|
+
if (prefixSegment && suffixSegment) {
|
|
119
|
+
return `${prefixSegment}{$${param}}${suffixSegment}`
|
|
120
|
+
} else if (prefixSegment) {
|
|
121
|
+
return `${prefixSegment}{$${param}}`
|
|
122
|
+
} else if (suffixSegment) {
|
|
123
|
+
return `{$${param}}${suffixSegment}`
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (type === SEGMENT_TYPE_OPTIONAL_PARAM) {
|
|
128
|
+
const param = value.substring(1)
|
|
129
|
+
if (prefixSegment && suffixSegment) {
|
|
130
|
+
return `${prefixSegment}{-$${param}}${suffixSegment}`
|
|
131
|
+
} else if (prefixSegment) {
|
|
132
|
+
return `${prefixSegment}{-$${param}}`
|
|
133
|
+
} else if (suffixSegment) {
|
|
134
|
+
return `{-$${param}}${suffixSegment}`
|
|
135
|
+
}
|
|
136
|
+
return `{-$${param}}`
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (type === SEGMENT_TYPE_WILDCARD) {
|
|
140
|
+
if (prefixSegment && suffixSegment) {
|
|
141
|
+
return `${prefixSegment}{$}${suffixSegment}`
|
|
142
|
+
} else if (prefixSegment) {
|
|
143
|
+
return `${prefixSegment}{$}`
|
|
144
|
+
} else if (suffixSegment) {
|
|
145
|
+
return `{$}${suffixSegment}`
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// This case should never happen, should we throw instead?
|
|
150
|
+
return value
|
|
94
151
|
}
|
|
95
152
|
|
|
96
153
|
export function resolvePath({
|
|
@@ -99,75 +156,73 @@ export function resolvePath({
|
|
|
99
156
|
to,
|
|
100
157
|
trailingSlash = 'never',
|
|
101
158
|
caseSensitive,
|
|
159
|
+
parseCache,
|
|
102
160
|
}: ResolvePathOptions) {
|
|
103
161
|
base = removeBasepath(basepath, base, caseSensitive)
|
|
104
162
|
to = removeBasepath(basepath, to, caseSensitive)
|
|
105
163
|
|
|
106
|
-
let baseSegments = parsePathname(base)
|
|
107
|
-
const toSegments = parsePathname(to)
|
|
164
|
+
let baseSegments = parsePathname(base, parseCache).slice()
|
|
165
|
+
const toSegments = parsePathname(to, parseCache)
|
|
108
166
|
|
|
109
167
|
if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
|
|
110
168
|
baseSegments.pop()
|
|
111
169
|
}
|
|
112
170
|
|
|
113
|
-
|
|
114
|
-
|
|
171
|
+
for (let index = 0, length = toSegments.length; index < length; index++) {
|
|
172
|
+
const toSegment = toSegments[index]!
|
|
173
|
+
const value = toSegment.value
|
|
174
|
+
if (value === '/') {
|
|
115
175
|
if (!index) {
|
|
116
176
|
// Leading slash
|
|
117
177
|
baseSegments = [toSegment]
|
|
118
|
-
} else if (index ===
|
|
178
|
+
} else if (index === length - 1) {
|
|
119
179
|
// Trailing Slash
|
|
120
180
|
baseSegments.push(toSegment)
|
|
121
181
|
} else {
|
|
122
182
|
// ignore inter-slashes
|
|
123
183
|
}
|
|
124
|
-
} else if (
|
|
184
|
+
} else if (value === '..') {
|
|
125
185
|
baseSegments.pop()
|
|
126
|
-
} else if (
|
|
186
|
+
} else if (value === '.') {
|
|
127
187
|
// ignore
|
|
128
188
|
} else {
|
|
129
189
|
baseSegments.push(toSegment)
|
|
130
190
|
}
|
|
131
|
-
}
|
|
191
|
+
}
|
|
132
192
|
|
|
133
193
|
if (baseSegments.length > 1) {
|
|
134
|
-
if (last(baseSegments)
|
|
194
|
+
if (last(baseSegments)!.value === '/') {
|
|
135
195
|
if (trailingSlash === 'never') {
|
|
136
196
|
baseSegments.pop()
|
|
137
197
|
}
|
|
138
198
|
} else if (trailingSlash === 'always') {
|
|
139
|
-
baseSegments.push({ type:
|
|
199
|
+
baseSegments.push({ type: SEGMENT_TYPE_PATHNAME, value: '/' })
|
|
140
200
|
}
|
|
141
201
|
}
|
|
142
202
|
|
|
143
|
-
const segmentValues = baseSegments.map(
|
|
144
|
-
if (segment.type === 'param') {
|
|
145
|
-
const param = segment.value.substring(1)
|
|
146
|
-
if (segment.prefixSegment && segment.suffixSegment) {
|
|
147
|
-
return `${segment.prefixSegment}{$${param}}${segment.suffixSegment}`
|
|
148
|
-
} else if (segment.prefixSegment) {
|
|
149
|
-
return `${segment.prefixSegment}{$${param}}`
|
|
150
|
-
} else if (segment.suffixSegment) {
|
|
151
|
-
return `{$${param}}${segment.suffixSegment}`
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (segment.type === 'wildcard') {
|
|
155
|
-
if (segment.prefixSegment && segment.suffixSegment) {
|
|
156
|
-
return `${segment.prefixSegment}{$}${segment.suffixSegment}`
|
|
157
|
-
} else if (segment.prefixSegment) {
|
|
158
|
-
return `${segment.prefixSegment}{$}`
|
|
159
|
-
} else if (segment.suffixSegment) {
|
|
160
|
-
return `{$}${segment.suffixSegment}`
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
return segment.value
|
|
164
|
-
})
|
|
203
|
+
const segmentValues = baseSegments.map(segmentToString)
|
|
165
204
|
const joined = joinPaths([basepath, ...segmentValues])
|
|
166
|
-
return
|
|
205
|
+
return joined
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export type ParsePathnameCache = LRUCache<string, ReadonlyArray<Segment>>
|
|
209
|
+
export const parsePathname = (
|
|
210
|
+
pathname?: string,
|
|
211
|
+
cache?: ParsePathnameCache,
|
|
212
|
+
): ReadonlyArray<Segment> => {
|
|
213
|
+
if (!pathname) return []
|
|
214
|
+
const cached = cache?.get(pathname)
|
|
215
|
+
if (cached) return cached
|
|
216
|
+
const parsed = baseParsePathname(pathname)
|
|
217
|
+
cache?.set(pathname, parsed)
|
|
218
|
+
return parsed
|
|
167
219
|
}
|
|
168
220
|
|
|
169
221
|
const PARAM_RE = /^\$.{1,}$/ // $paramName
|
|
170
222
|
const PARAM_W_CURLY_BRACES_RE = /^(.*?)\{(\$[a-zA-Z_$][a-zA-Z0-9_$]*)\}(.*)$/ // prefix{$paramName}suffix
|
|
223
|
+
const OPTIONAL_PARAM_W_CURLY_BRACES_RE =
|
|
224
|
+
/^(.*?)\{-(\$[a-zA-Z_$][a-zA-Z0-9_$]*)\}(.*)$/ // prefix{-$paramName}suffix
|
|
225
|
+
|
|
171
226
|
const WILDCARD_RE = /^\$$/ // $
|
|
172
227
|
const WILDCARD_W_CURLY_BRACES_RE = /^(.*?)\{\$\}(.*)$/ // prefix{$}suffix
|
|
173
228
|
|
|
@@ -177,8 +232,10 @@ const WILDCARD_W_CURLY_BRACES_RE = /^(.*?)\{\$\}(.*)$/ // prefix{$}suffix
|
|
|
177
232
|
* Wildcard: `/foo/$` ✅
|
|
178
233
|
* Wildcard with Prefix and Suffix: `/foo/prefix{$}suffix` ✅
|
|
179
234
|
*
|
|
235
|
+
* Optional param: `/foo/{-$bar}`
|
|
236
|
+
* Optional param with Prefix and Suffix: `/foo/prefix{-$bar}suffix`
|
|
237
|
+
|
|
180
238
|
* Future:
|
|
181
|
-
* Optional: `/foo/{-bar}`
|
|
182
239
|
* Optional named segment: `/foo/{bar}`
|
|
183
240
|
* Optional named segment with Prefix and Suffix: `/foo/prefix{-bar}suffix`
|
|
184
241
|
* Escape special characters:
|
|
@@ -186,11 +243,7 @@ const WILDCARD_W_CURLY_BRACES_RE = /^(.*?)\{\$\}(.*)$/ // prefix{$}suffix
|
|
|
186
243
|
* - `/foo/[$]{$foo} - Dynamic route with a static prefix of `$`
|
|
187
244
|
* - `/foo/{$foo}[$]` - Dynamic route with a static suffix of `$`
|
|
188
245
|
*/
|
|
189
|
-
|
|
190
|
-
if (!pathname) {
|
|
191
|
-
return []
|
|
192
|
-
}
|
|
193
|
-
|
|
246
|
+
function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
|
|
194
247
|
pathname = cleanPath(pathname)
|
|
195
248
|
|
|
196
249
|
const segments: Array<Segment> = []
|
|
@@ -198,7 +251,7 @@ export function parsePathname(pathname?: string): Array<Segment> {
|
|
|
198
251
|
if (pathname.slice(0, 1) === '/') {
|
|
199
252
|
pathname = pathname.substring(1)
|
|
200
253
|
segments.push({
|
|
201
|
-
type:
|
|
254
|
+
type: SEGMENT_TYPE_PATHNAME,
|
|
202
255
|
value: '/',
|
|
203
256
|
})
|
|
204
257
|
}
|
|
@@ -218,13 +271,29 @@ export function parsePathname(pathname?: string): Array<Segment> {
|
|
|
218
271
|
const prefix = wildcardBracesMatch[1]
|
|
219
272
|
const suffix = wildcardBracesMatch[2]
|
|
220
273
|
return {
|
|
221
|
-
type:
|
|
274
|
+
type: SEGMENT_TYPE_WILDCARD,
|
|
222
275
|
value: '$',
|
|
223
276
|
prefixSegment: prefix || undefined,
|
|
224
277
|
suffixSegment: suffix || undefined,
|
|
225
278
|
}
|
|
226
279
|
}
|
|
227
280
|
|
|
281
|
+
// Check for optional parameter format: prefix{-$paramName}suffix
|
|
282
|
+
const optionalParamBracesMatch = part.match(
|
|
283
|
+
OPTIONAL_PARAM_W_CURLY_BRACES_RE,
|
|
284
|
+
)
|
|
285
|
+
if (optionalParamBracesMatch) {
|
|
286
|
+
const prefix = optionalParamBracesMatch[1]
|
|
287
|
+
const paramName = optionalParamBracesMatch[2]!
|
|
288
|
+
const suffix = optionalParamBracesMatch[3]
|
|
289
|
+
return {
|
|
290
|
+
type: SEGMENT_TYPE_OPTIONAL_PARAM,
|
|
291
|
+
value: paramName, // Now just $paramName (no prefix)
|
|
292
|
+
prefixSegment: prefix || undefined,
|
|
293
|
+
suffixSegment: suffix || undefined,
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
228
297
|
// Check for the new parameter format: prefix{$paramName}suffix
|
|
229
298
|
const paramBracesMatch = part.match(PARAM_W_CURLY_BRACES_RE)
|
|
230
299
|
if (paramBracesMatch) {
|
|
@@ -232,7 +301,7 @@ export function parsePathname(pathname?: string): Array<Segment> {
|
|
|
232
301
|
const paramName = paramBracesMatch[2]
|
|
233
302
|
const suffix = paramBracesMatch[3]
|
|
234
303
|
return {
|
|
235
|
-
type:
|
|
304
|
+
type: SEGMENT_TYPE_PARAM,
|
|
236
305
|
value: '' + paramName,
|
|
237
306
|
prefixSegment: prefix || undefined,
|
|
238
307
|
suffixSegment: suffix || undefined,
|
|
@@ -243,7 +312,7 @@ export function parsePathname(pathname?: string): Array<Segment> {
|
|
|
243
312
|
if (PARAM_RE.test(part)) {
|
|
244
313
|
const paramName = part.substring(1)
|
|
245
314
|
return {
|
|
246
|
-
type:
|
|
315
|
+
type: SEGMENT_TYPE_PARAM,
|
|
247
316
|
value: '$' + paramName,
|
|
248
317
|
prefixSegment: undefined,
|
|
249
318
|
suffixSegment: undefined,
|
|
@@ -253,7 +322,7 @@ export function parsePathname(pathname?: string): Array<Segment> {
|
|
|
253
322
|
// Check for bare wildcard: $ (without curly braces)
|
|
254
323
|
if (WILDCARD_RE.test(part)) {
|
|
255
324
|
return {
|
|
256
|
-
type:
|
|
325
|
+
type: SEGMENT_TYPE_WILDCARD,
|
|
257
326
|
value: '$',
|
|
258
327
|
prefixSegment: undefined,
|
|
259
328
|
suffixSegment: undefined,
|
|
@@ -262,7 +331,7 @@ export function parsePathname(pathname?: string): Array<Segment> {
|
|
|
262
331
|
|
|
263
332
|
// Handle regular pathname segment
|
|
264
333
|
return {
|
|
265
|
-
type:
|
|
334
|
+
type: SEGMENT_TYPE_PATHNAME,
|
|
266
335
|
value: part.includes('%25')
|
|
267
336
|
? part
|
|
268
337
|
.split('%25')
|
|
@@ -276,7 +345,7 @@ export function parsePathname(pathname?: string): Array<Segment> {
|
|
|
276
345
|
if (pathname.slice(-1) === '/') {
|
|
277
346
|
pathname = pathname.substring(1)
|
|
278
347
|
segments.push({
|
|
279
|
-
type:
|
|
348
|
+
type: SEGMENT_TYPE_PATHNAME,
|
|
280
349
|
value: '/',
|
|
281
350
|
})
|
|
282
351
|
}
|
|
@@ -291,6 +360,7 @@ interface InterpolatePathOptions {
|
|
|
291
360
|
leaveParams?: boolean
|
|
292
361
|
// Map of encoded chars to decoded chars (e.g. '%40' -> '@') that should remain decoded in path params
|
|
293
362
|
decodeCharMap?: Map<string, string>
|
|
363
|
+
parseCache?: ParsePathnameCache
|
|
294
364
|
}
|
|
295
365
|
|
|
296
366
|
type InterPolatePathResult = {
|
|
@@ -304,14 +374,15 @@ export function interpolatePath({
|
|
|
304
374
|
leaveWildcards,
|
|
305
375
|
leaveParams,
|
|
306
376
|
decodeCharMap,
|
|
377
|
+
parseCache,
|
|
307
378
|
}: InterpolatePathOptions): InterPolatePathResult {
|
|
308
|
-
const interpolatedPathSegments = parsePathname(path)
|
|
379
|
+
const interpolatedPathSegments = parsePathname(path, parseCache)
|
|
309
380
|
|
|
310
381
|
function encodeParam(key: string): any {
|
|
311
382
|
const value = params[key]
|
|
312
383
|
const isValueString = typeof value === 'string'
|
|
313
384
|
|
|
314
|
-
if (
|
|
385
|
+
if (key === '*' || key === '_splat') {
|
|
315
386
|
// the splat/catch-all routes shouldn't have the '/' encoded out
|
|
316
387
|
return isValueString ? encodeURI(value) : value
|
|
317
388
|
} else {
|
|
@@ -326,10 +397,29 @@ export function interpolatePath({
|
|
|
326
397
|
const usedParams: Record<string, unknown> = {}
|
|
327
398
|
const interpolatedPath = joinPaths(
|
|
328
399
|
interpolatedPathSegments.map((segment) => {
|
|
329
|
-
if (segment.type ===
|
|
400
|
+
if (segment.type === SEGMENT_TYPE_PATHNAME) {
|
|
401
|
+
return segment.value
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (segment.type === SEGMENT_TYPE_WILDCARD) {
|
|
330
405
|
usedParams._splat = params._splat
|
|
331
406
|
const segmentPrefix = segment.prefixSegment || ''
|
|
332
407
|
const segmentSuffix = segment.suffixSegment || ''
|
|
408
|
+
|
|
409
|
+
// Check if _splat parameter is missing
|
|
410
|
+
if (!('_splat' in params)) {
|
|
411
|
+
isMissingParams = true
|
|
412
|
+
// For missing splat parameters, just return the prefix and suffix without the wildcard
|
|
413
|
+
if (leaveWildcards) {
|
|
414
|
+
return `${segmentPrefix}${segment.value}${segmentSuffix}`
|
|
415
|
+
}
|
|
416
|
+
// If there is a prefix or suffix, return them joined, otherwise omit the segment
|
|
417
|
+
if (segmentPrefix || segmentSuffix) {
|
|
418
|
+
return `${segmentPrefix}${segmentSuffix}`
|
|
419
|
+
}
|
|
420
|
+
return undefined
|
|
421
|
+
}
|
|
422
|
+
|
|
333
423
|
const value = encodeParam('_splat')
|
|
334
424
|
if (leaveWildcards) {
|
|
335
425
|
return `${segmentPrefix}${segment.value}${value ?? ''}${segmentSuffix}`
|
|
@@ -337,7 +427,7 @@ export function interpolatePath({
|
|
|
337
427
|
return `${segmentPrefix}${value}${segmentSuffix}`
|
|
338
428
|
}
|
|
339
429
|
|
|
340
|
-
if (segment.type ===
|
|
430
|
+
if (segment.type === SEGMENT_TYPE_PARAM) {
|
|
341
431
|
const key = segment.value.substring(1)
|
|
342
432
|
if (!isMissingParams && !(key in params)) {
|
|
343
433
|
isMissingParams = true
|
|
@@ -353,6 +443,37 @@ export function interpolatePath({
|
|
|
353
443
|
return `${segmentPrefix}${encodeParam(key) ?? 'undefined'}${segmentSuffix}`
|
|
354
444
|
}
|
|
355
445
|
|
|
446
|
+
if (segment.type === SEGMENT_TYPE_OPTIONAL_PARAM) {
|
|
447
|
+
const key = segment.value.substring(1)
|
|
448
|
+
|
|
449
|
+
const segmentPrefix = segment.prefixSegment || ''
|
|
450
|
+
const segmentSuffix = segment.suffixSegment || ''
|
|
451
|
+
|
|
452
|
+
// Check if optional parameter is missing or undefined
|
|
453
|
+
if (!(key in params) || params[key] == null) {
|
|
454
|
+
if (leaveWildcards) {
|
|
455
|
+
return `${segmentPrefix}${key}${segmentSuffix}`
|
|
456
|
+
}
|
|
457
|
+
// For optional params with prefix/suffix, keep the prefix/suffix but omit the param
|
|
458
|
+
if (segmentPrefix || segmentSuffix) {
|
|
459
|
+
return `${segmentPrefix}${segmentSuffix}`
|
|
460
|
+
}
|
|
461
|
+
// If no prefix/suffix, omit the entire segment
|
|
462
|
+
return undefined
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
usedParams[key] = params[key]
|
|
466
|
+
|
|
467
|
+
if (leaveParams) {
|
|
468
|
+
const value = encodeParam(segment.value)
|
|
469
|
+
return `${segmentPrefix}${segment.value}${value ?? ''}${segmentSuffix}`
|
|
470
|
+
}
|
|
471
|
+
if (leaveWildcards) {
|
|
472
|
+
return `${segmentPrefix}${key}${encodeParam(key) ?? ''}${segmentSuffix}`
|
|
473
|
+
}
|
|
474
|
+
return `${segmentPrefix}${encodeParam(key) ?? ''}${segmentSuffix}`
|
|
475
|
+
}
|
|
476
|
+
|
|
356
477
|
return segment.value
|
|
357
478
|
}),
|
|
358
479
|
)
|
|
@@ -373,8 +494,14 @@ export function matchPathname(
|
|
|
373
494
|
basepath: string,
|
|
374
495
|
currentPathname: string,
|
|
375
496
|
matchLocation: Pick<MatchLocation, 'to' | 'fuzzy' | 'caseSensitive'>,
|
|
497
|
+
parseCache?: ParsePathnameCache,
|
|
376
498
|
): AnyPathParams | undefined {
|
|
377
|
-
const pathParams = matchByPath(
|
|
499
|
+
const pathParams = matchByPath(
|
|
500
|
+
basepath,
|
|
501
|
+
currentPathname,
|
|
502
|
+
matchLocation,
|
|
503
|
+
parseCache,
|
|
504
|
+
)
|
|
378
505
|
// const searchMatched = matchBySearch(location.search, matchLocation)
|
|
379
506
|
|
|
380
507
|
if (matchLocation.to && !pathParams) {
|
|
@@ -428,156 +555,221 @@ export function removeBasepath(
|
|
|
428
555
|
export function matchByPath(
|
|
429
556
|
basepath: string,
|
|
430
557
|
from: string,
|
|
431
|
-
|
|
558
|
+
{
|
|
559
|
+
to,
|
|
560
|
+
fuzzy,
|
|
561
|
+
caseSensitive,
|
|
562
|
+
}: Pick<MatchLocation, 'to' | 'caseSensitive' | 'fuzzy'>,
|
|
563
|
+
parseCache?: ParsePathnameCache,
|
|
432
564
|
): Record<string, string> | undefined {
|
|
433
565
|
// check basepath first
|
|
434
566
|
if (basepath !== '/' && !from.startsWith(basepath)) {
|
|
435
567
|
return undefined
|
|
436
568
|
}
|
|
437
569
|
// Remove the base path from the pathname
|
|
438
|
-
from = removeBasepath(basepath, from,
|
|
570
|
+
from = removeBasepath(basepath, from, caseSensitive)
|
|
439
571
|
// Default to to $ (wildcard)
|
|
440
|
-
|
|
441
|
-
basepath,
|
|
442
|
-
`${matchLocation.to ?? '$'}`,
|
|
443
|
-
matchLocation.caseSensitive,
|
|
444
|
-
)
|
|
572
|
+
to = removeBasepath(basepath, `${to ?? '$'}`, caseSensitive)
|
|
445
573
|
|
|
446
574
|
// Parse the from and to
|
|
447
|
-
const baseSegments = parsePathname(
|
|
448
|
-
|
|
575
|
+
const baseSegments = parsePathname(
|
|
576
|
+
from.startsWith('/') ? from : `/${from}`,
|
|
577
|
+
parseCache,
|
|
578
|
+
)
|
|
579
|
+
const routeSegments = parsePathname(
|
|
580
|
+
to.startsWith('/') ? to : `/${to}`,
|
|
581
|
+
parseCache,
|
|
582
|
+
)
|
|
449
583
|
|
|
450
|
-
|
|
451
|
-
baseSegments.unshift({
|
|
452
|
-
type: 'pathname',
|
|
453
|
-
value: '/',
|
|
454
|
-
})
|
|
455
|
-
}
|
|
584
|
+
const params: Record<string, string> = {}
|
|
456
585
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
586
|
+
const result = isMatch(
|
|
587
|
+
baseSegments,
|
|
588
|
+
routeSegments,
|
|
589
|
+
params,
|
|
590
|
+
fuzzy,
|
|
591
|
+
caseSensitive,
|
|
592
|
+
)
|
|
463
593
|
|
|
464
|
-
|
|
594
|
+
return result ? params : undefined
|
|
595
|
+
}
|
|
465
596
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
const isLastBaseSegment = i >= baseSegments.length - 1
|
|
476
|
-
const isLastRouteSegment = i >= routeSegments.length - 1
|
|
477
|
-
|
|
478
|
-
if (routeSegment) {
|
|
479
|
-
if (routeSegment.type === 'wildcard') {
|
|
480
|
-
// Capture all remaining segments for a wildcard
|
|
481
|
-
const remainingBaseSegments = baseSegments.slice(i)
|
|
482
|
-
|
|
483
|
-
let _splat: string
|
|
484
|
-
|
|
485
|
-
// If this is a wildcard with prefix/suffix, we need to handle the first segment specially
|
|
486
|
-
if (routeSegment.prefixSegment || routeSegment.suffixSegment) {
|
|
487
|
-
if (!baseSegment) return false
|
|
488
|
-
|
|
489
|
-
const prefix = routeSegment.prefixSegment || ''
|
|
490
|
-
const suffix = routeSegment.suffixSegment || ''
|
|
491
|
-
|
|
492
|
-
// Check if the base segment starts with prefix and ends with suffix
|
|
493
|
-
const baseValue = baseSegment.value
|
|
494
|
-
if ('prefixSegment' in routeSegment) {
|
|
495
|
-
if (!baseValue.startsWith(prefix)) {
|
|
496
|
-
return false
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
if ('suffixSegment' in routeSegment) {
|
|
500
|
-
if (
|
|
501
|
-
!baseSegments[baseSegments.length - 1]?.value.endsWith(suffix)
|
|
502
|
-
) {
|
|
503
|
-
return false
|
|
504
|
-
}
|
|
505
|
-
}
|
|
597
|
+
function isMatch(
|
|
598
|
+
baseSegments: ReadonlyArray<Segment>,
|
|
599
|
+
routeSegments: ReadonlyArray<Segment>,
|
|
600
|
+
params: Record<string, string>,
|
|
601
|
+
fuzzy?: boolean,
|
|
602
|
+
caseSensitive?: boolean,
|
|
603
|
+
): boolean {
|
|
604
|
+
let baseIndex = 0
|
|
605
|
+
let routeIndex = 0
|
|
506
606
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
607
|
+
while (baseIndex < baseSegments.length || routeIndex < routeSegments.length) {
|
|
608
|
+
const baseSegment = baseSegments[baseIndex]
|
|
609
|
+
const routeSegment = routeSegments[routeIndex]
|
|
510
610
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
611
|
+
if (routeSegment) {
|
|
612
|
+
if (routeSegment.type === SEGMENT_TYPE_WILDCARD) {
|
|
613
|
+
// Capture all remaining segments for a wildcard
|
|
614
|
+
const remainingBaseSegments = baseSegments.slice(baseIndex)
|
|
515
615
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
616
|
+
let _splat: string
|
|
617
|
+
|
|
618
|
+
// If this is a wildcard with prefix/suffix, we need to handle the first segment specially
|
|
619
|
+
if (routeSegment.prefixSegment || routeSegment.suffixSegment) {
|
|
620
|
+
if (!baseSegment) return false
|
|
621
|
+
|
|
622
|
+
const prefix = routeSegment.prefixSegment || ''
|
|
623
|
+
const suffix = routeSegment.suffixSegment || ''
|
|
624
|
+
|
|
625
|
+
// Check if the base segment starts with prefix and ends with suffix
|
|
626
|
+
const baseValue = baseSegment.value
|
|
627
|
+
if ('prefixSegment' in routeSegment) {
|
|
628
|
+
if (!baseValue.startsWith(prefix)) {
|
|
629
|
+
return false
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
if ('suffixSegment' in routeSegment) {
|
|
633
|
+
if (
|
|
634
|
+
!baseSegments[baseSegments.length - 1]?.value.endsWith(suffix)
|
|
635
|
+
) {
|
|
636
|
+
return false
|
|
521
637
|
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
let rejoinedSplat = decodeURI(
|
|
641
|
+
joinPaths(remainingBaseSegments.map((d) => d.value)),
|
|
642
|
+
)
|
|
522
643
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
644
|
+
// Remove the prefix and suffix from the rejoined splat
|
|
645
|
+
if (prefix && rejoinedSplat.startsWith(prefix)) {
|
|
646
|
+
rejoinedSplat = rejoinedSplat.slice(prefix.length)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (suffix && rejoinedSplat.endsWith(suffix)) {
|
|
650
|
+
rejoinedSplat = rejoinedSplat.slice(
|
|
651
|
+
0,
|
|
652
|
+
rejoinedSplat.length - suffix.length,
|
|
528
653
|
)
|
|
529
654
|
}
|
|
530
655
|
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
656
|
+
_splat = rejoinedSplat
|
|
657
|
+
} else {
|
|
658
|
+
// If no prefix/suffix, just rejoin the remaining segments
|
|
659
|
+
_splat = decodeURI(
|
|
660
|
+
joinPaths(remainingBaseSegments.map((d) => d.value)),
|
|
661
|
+
)
|
|
535
662
|
}
|
|
536
663
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
664
|
+
// TODO: Deprecate *
|
|
665
|
+
params['*'] = _splat
|
|
666
|
+
params['_splat'] = _splat
|
|
667
|
+
return true
|
|
668
|
+
}
|
|
541
669
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
) {
|
|
670
|
+
if (routeSegment.type === SEGMENT_TYPE_PATHNAME) {
|
|
671
|
+
if (routeSegment.value === '/' && !baseSegment?.value) {
|
|
672
|
+
routeIndex++
|
|
673
|
+
continue
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
if (baseSegment) {
|
|
677
|
+
if (caseSensitive) {
|
|
678
|
+
if (routeSegment.value !== baseSegment.value) {
|
|
551
679
|
return false
|
|
552
680
|
}
|
|
681
|
+
} else if (
|
|
682
|
+
routeSegment.value.toLowerCase() !== baseSegment.value.toLowerCase()
|
|
683
|
+
) {
|
|
684
|
+
return false
|
|
553
685
|
}
|
|
686
|
+
baseIndex++
|
|
687
|
+
routeIndex++
|
|
688
|
+
continue
|
|
689
|
+
} else {
|
|
690
|
+
return false
|
|
554
691
|
}
|
|
692
|
+
}
|
|
555
693
|
|
|
694
|
+
if (routeSegment.type === SEGMENT_TYPE_PARAM) {
|
|
556
695
|
if (!baseSegment) {
|
|
557
696
|
return false
|
|
558
697
|
}
|
|
559
698
|
|
|
560
|
-
if (
|
|
561
|
-
|
|
699
|
+
if (baseSegment.value === '/') {
|
|
700
|
+
return false
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
let _paramValue = ''
|
|
704
|
+
let matched = false
|
|
705
|
+
|
|
706
|
+
// If this param has prefix/suffix, we need to extract the actual parameter value
|
|
707
|
+
if (routeSegment.prefixSegment || routeSegment.suffixSegment) {
|
|
708
|
+
const prefix = routeSegment.prefixSegment || ''
|
|
709
|
+
const suffix = routeSegment.suffixSegment || ''
|
|
710
|
+
|
|
711
|
+
// Check if the base segment starts with prefix and ends with suffix
|
|
712
|
+
const baseValue = baseSegment.value
|
|
713
|
+
if (prefix && !baseValue.startsWith(prefix)) {
|
|
714
|
+
return false
|
|
715
|
+
}
|
|
716
|
+
if (suffix && !baseValue.endsWith(suffix)) {
|
|
562
717
|
return false
|
|
563
718
|
}
|
|
564
719
|
|
|
565
|
-
let
|
|
720
|
+
let paramValue = baseValue
|
|
721
|
+
if (prefix && paramValue.startsWith(prefix)) {
|
|
722
|
+
paramValue = paramValue.slice(prefix.length)
|
|
723
|
+
}
|
|
724
|
+
if (suffix && paramValue.endsWith(suffix)) {
|
|
725
|
+
paramValue = paramValue.slice(0, paramValue.length - suffix.length)
|
|
726
|
+
}
|
|
566
727
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
728
|
+
_paramValue = decodeURIComponent(paramValue)
|
|
729
|
+
matched = true
|
|
730
|
+
} else {
|
|
731
|
+
// If no prefix/suffix, just decode the base segment value
|
|
732
|
+
_paramValue = decodeURIComponent(baseSegment.value)
|
|
733
|
+
matched = true
|
|
734
|
+
}
|
|
571
735
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
736
|
+
if (matched) {
|
|
737
|
+
params[routeSegment.value.substring(1)] = _paramValue
|
|
738
|
+
baseIndex++
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
routeIndex++
|
|
742
|
+
continue
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
if (routeSegment.type === SEGMENT_TYPE_OPTIONAL_PARAM) {
|
|
746
|
+
// Optional parameters can be missing - don't fail the match
|
|
747
|
+
if (!baseSegment) {
|
|
748
|
+
// No base segment for optional param - skip this route segment
|
|
749
|
+
routeIndex++
|
|
750
|
+
continue
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
if (baseSegment.value === '/') {
|
|
754
|
+
// Skip slash segments for optional params
|
|
755
|
+
routeIndex++
|
|
756
|
+
continue
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
let _paramValue = ''
|
|
760
|
+
let matched = false
|
|
580
761
|
|
|
762
|
+
// If this optional param has prefix/suffix, we need to extract the actual parameter value
|
|
763
|
+
if (routeSegment.prefixSegment || routeSegment.suffixSegment) {
|
|
764
|
+
const prefix = routeSegment.prefixSegment || ''
|
|
765
|
+
const suffix = routeSegment.suffixSegment || ''
|
|
766
|
+
|
|
767
|
+
// Check if the base segment starts with prefix and ends with suffix
|
|
768
|
+
const baseValue = baseSegment.value
|
|
769
|
+
if (
|
|
770
|
+
(!prefix || baseValue.startsWith(prefix)) &&
|
|
771
|
+
(!suffix || baseValue.endsWith(suffix))
|
|
772
|
+
) {
|
|
581
773
|
let paramValue = baseValue
|
|
582
774
|
if (prefix && paramValue.startsWith(prefix)) {
|
|
583
775
|
paramValue = paramValue.slice(prefix.length)
|
|
@@ -590,23 +782,81 @@ export function matchByPath(
|
|
|
590
782
|
}
|
|
591
783
|
|
|
592
784
|
_paramValue = decodeURIComponent(paramValue)
|
|
593
|
-
|
|
785
|
+
matched = true
|
|
786
|
+
}
|
|
787
|
+
} else {
|
|
788
|
+
// For optional params without prefix/suffix, we need to check if the current
|
|
789
|
+
// base segment should match this optional param or a later route segment
|
|
790
|
+
|
|
791
|
+
// Look ahead to see if there's a later route segment that matches the current base segment
|
|
792
|
+
let shouldMatchOptional = true
|
|
793
|
+
for (
|
|
794
|
+
let lookAhead = routeIndex + 1;
|
|
795
|
+
lookAhead < routeSegments.length;
|
|
796
|
+
lookAhead++
|
|
797
|
+
) {
|
|
798
|
+
const futureRouteSegment = routeSegments[lookAhead]
|
|
799
|
+
if (
|
|
800
|
+
futureRouteSegment?.type === SEGMENT_TYPE_PATHNAME &&
|
|
801
|
+
futureRouteSegment.value === baseSegment.value
|
|
802
|
+
) {
|
|
803
|
+
// The current base segment matches a future pathname segment,
|
|
804
|
+
// so we should skip this optional parameter
|
|
805
|
+
shouldMatchOptional = false
|
|
806
|
+
break
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
// If we encounter a required param or wildcard, stop looking ahead
|
|
810
|
+
if (
|
|
811
|
+
futureRouteSegment?.type === SEGMENT_TYPE_PARAM ||
|
|
812
|
+
futureRouteSegment?.type === SEGMENT_TYPE_WILDCARD
|
|
813
|
+
) {
|
|
814
|
+
if (baseSegments.length < routeSegments.length) {
|
|
815
|
+
shouldMatchOptional = false
|
|
816
|
+
}
|
|
817
|
+
break
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (shouldMatchOptional) {
|
|
594
822
|
// If no prefix/suffix, just decode the base segment value
|
|
595
823
|
_paramValue = decodeURIComponent(baseSegment.value)
|
|
824
|
+
matched = true
|
|
596
825
|
}
|
|
826
|
+
}
|
|
597
827
|
|
|
828
|
+
if (matched) {
|
|
598
829
|
params[routeSegment.value.substring(1)] = _paramValue
|
|
830
|
+
baseIndex++
|
|
599
831
|
}
|
|
832
|
+
|
|
833
|
+
routeIndex++
|
|
834
|
+
continue
|
|
600
835
|
}
|
|
836
|
+
}
|
|
601
837
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
838
|
+
// If we have base segments left but no route segments, it's a fuzzy match
|
|
839
|
+
if (baseIndex < baseSegments.length && routeIndex >= routeSegments.length) {
|
|
840
|
+
params['**'] = joinPaths(
|
|
841
|
+
baseSegments.slice(baseIndex).map((d) => d.value),
|
|
842
|
+
)
|
|
843
|
+
return !!fuzzy && routeSegments[routeSegments.length - 1]?.value !== '/'
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// If we have route segments left but no base segments, check if remaining are optional
|
|
847
|
+
if (routeIndex < routeSegments.length && baseIndex >= baseSegments.length) {
|
|
848
|
+
// Check if all remaining route segments are optional
|
|
849
|
+
for (let i = routeIndex; i < routeSegments.length; i++) {
|
|
850
|
+
if (routeSegments[i]?.type !== SEGMENT_TYPE_OPTIONAL_PARAM) {
|
|
851
|
+
return false
|
|
852
|
+
}
|
|
605
853
|
}
|
|
854
|
+
// All remaining are optional, so we can finish
|
|
855
|
+
break
|
|
606
856
|
}
|
|
607
857
|
|
|
608
|
-
|
|
609
|
-
}
|
|
858
|
+
break
|
|
859
|
+
}
|
|
610
860
|
|
|
611
|
-
return
|
|
861
|
+
return true
|
|
612
862
|
}
|