@tanstack/router-core 1.132.0-alpha.21 → 1.132.0-alpha.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/router-core",
3
- "version": "1.132.0-alpha.21",
3
+ "version": "1.132.0-alpha.25",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/path.ts CHANGED
@@ -154,8 +154,8 @@ export function resolvePath({
154
154
  trailingSlash = 'never',
155
155
  parseCache,
156
156
  }: ResolvePathOptions) {
157
- let baseSegments = parsePathname(base, parseCache).slice()
158
- const toSegments = parsePathname(to, parseCache)
157
+ let baseSegments = parseBasePathSegments(base, parseCache).slice()
158
+ const toSegments = parseRoutePathSegments(to, parseCache)
159
159
 
160
160
  if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
161
161
  baseSegments.pop()
@@ -200,14 +200,26 @@ export function resolvePath({
200
200
  }
201
201
 
202
202
  export type ParsePathnameCache = LRUCache<string, ReadonlyArray<Segment>>
203
+
204
+ export const parseBasePathSegments = (
205
+ pathname?: string,
206
+ cache?: ParsePathnameCache,
207
+ ): ReadonlyArray<Segment> => parsePathname(pathname, cache, true)
208
+
209
+ export const parseRoutePathSegments = (
210
+ pathname?: string,
211
+ cache?: ParsePathnameCache,
212
+ ): ReadonlyArray<Segment> => parsePathname(pathname, cache, false)
213
+
203
214
  export const parsePathname = (
204
215
  pathname?: string,
205
216
  cache?: ParsePathnameCache,
217
+ basePathValues?: boolean,
206
218
  ): ReadonlyArray<Segment> => {
207
219
  if (!pathname) return []
208
220
  const cached = cache?.get(pathname)
209
221
  if (cached) return cached
210
- const parsed = baseParsePathname(pathname)
222
+ const parsed = baseParsePathname(pathname, basePathValues)
211
223
  cache?.set(pathname, parsed)
212
224
  return parsed
213
225
  }
@@ -237,7 +249,10 @@ const WILDCARD_W_CURLY_BRACES_RE = /^(.*?)\{\$\}(.*)$/ // prefix{$}suffix
237
249
  * - `/foo/[$]{$foo} - Dynamic route with a static prefix of `$`
238
250
  * - `/foo/{$foo}[$]` - Dynamic route with a static suffix of `$`
239
251
  */
240
- function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
252
+ function baseParsePathname(
253
+ pathname: string,
254
+ basePathValues?: boolean,
255
+ ): ReadonlyArray<Segment> {
241
256
  pathname = cleanPath(pathname)
242
257
 
243
258
  const segments: Array<Segment> = []
@@ -259,8 +274,12 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
259
274
 
260
275
  segments.push(
261
276
  ...split.map((part): Segment => {
277
+ // strip tailing underscore for non-nested paths
278
+ const partToMatch =
279
+ !basePathValues && part.slice(-1) === '_' ? part.slice(0, -1) : part
280
+
262
281
  // Check for wildcard with curly braces: prefix{$}suffix
263
- const wildcardBracesMatch = part.match(WILDCARD_W_CURLY_BRACES_RE)
282
+ const wildcardBracesMatch = partToMatch.match(WILDCARD_W_CURLY_BRACES_RE)
264
283
  if (wildcardBracesMatch) {
265
284
  const prefix = wildcardBracesMatch[1]
266
285
  const suffix = wildcardBracesMatch[2]
@@ -273,7 +292,7 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
273
292
  }
274
293
 
275
294
  // Check for optional parameter format: prefix{-$paramName}suffix
276
- const optionalParamBracesMatch = part.match(
295
+ const optionalParamBracesMatch = partToMatch.match(
277
296
  OPTIONAL_PARAM_W_CURLY_BRACES_RE,
278
297
  )
279
298
  if (optionalParamBracesMatch) {
@@ -289,7 +308,7 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
289
308
  }
290
309
 
291
310
  // Check for the new parameter format: prefix{$paramName}suffix
292
- const paramBracesMatch = part.match(PARAM_W_CURLY_BRACES_RE)
311
+ const paramBracesMatch = partToMatch.match(PARAM_W_CURLY_BRACES_RE)
293
312
  if (paramBracesMatch) {
294
313
  const prefix = paramBracesMatch[1]
295
314
  const paramName = paramBracesMatch[2]
@@ -303,8 +322,8 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
303
322
  }
304
323
 
305
324
  // Check for bare parameter format: $paramName (without curly braces)
306
- if (PARAM_RE.test(part)) {
307
- const paramName = part.substring(1)
325
+ if (PARAM_RE.test(partToMatch)) {
326
+ const paramName = partToMatch.substring(1)
308
327
  return {
309
328
  type: SEGMENT_TYPE_PARAM,
310
329
  value: '$' + paramName,
@@ -314,7 +333,7 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
314
333
  }
315
334
 
316
335
  // Check for bare wildcard: $ (without curly braces)
317
- if (WILDCARD_RE.test(part)) {
336
+ if (WILDCARD_RE.test(partToMatch)) {
318
337
  return {
319
338
  type: SEGMENT_TYPE_WILDCARD,
320
339
  value: '$',
@@ -326,12 +345,12 @@ function baseParsePathname(pathname: string): ReadonlyArray<Segment> {
326
345
  // Handle regular pathname segment
327
346
  return {
328
347
  type: SEGMENT_TYPE_PATHNAME,
329
- value: part.includes('%25')
330
- ? part
348
+ value: partToMatch.includes('%25')
349
+ ? partToMatch
331
350
  .split('%25')
332
351
  .map((segment) => decodeURI(segment))
333
352
  .join('%25')
334
- : decodeURI(part),
353
+ : decodeURI(partToMatch),
335
354
  }
336
355
  }),
337
356
  )
@@ -370,7 +389,7 @@ export function interpolatePath({
370
389
  decodeCharMap,
371
390
  parseCache,
372
391
  }: InterpolatePathOptions): InterPolatePathResult {
373
- const interpolatedPathSegments = parsePathname(path, parseCache)
392
+ const interpolatedPathSegments = parseRoutePathSegments(path, parseCache)
374
393
 
375
394
  function encodeParam(key: string): any {
376
395
  const value = params[key]
@@ -515,11 +534,11 @@ export function matchByPath(
515
534
  const stringTo = to as string
516
535
 
517
536
  // Parse the from and to
518
- const baseSegments = parsePathname(
537
+ const baseSegments = parseBasePathSegments(
519
538
  from.startsWith('/') ? from : `/${from}`,
520
539
  parseCache,
521
540
  )
522
- const routeSegments = parsePathname(
541
+ const routeSegments = parseRoutePathSegments(
523
542
  stringTo.startsWith('/') ? stringTo : `/${stringTo}`,
524
543
  parseCache,
525
544
  )
package/src/router.ts CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  cleanPath,
18
18
  interpolatePath,
19
19
  matchPathname,
20
- parsePathname,
20
+ parseRoutePathSegments,
21
21
  resolvePath,
22
22
  trimPath,
23
23
  trimPathLeft,
@@ -2601,7 +2601,7 @@ export function processRouteTree<TRouteLike extends RouteLike>({
2601
2601
  }
2602
2602
 
2603
2603
  const trimmed = trimPathLeft(d.fullPath)
2604
- let parsed = parsePathname(trimmed)
2604
+ let parsed = parseRoutePathSegments(trimmed)
2605
2605
 
2606
2606
  // Removes the leading slash if it is not the only remaining segment
2607
2607
  let skip = 0