@tanstack/router-generator 1.141.9 → 1.142.0
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/filesystem/physical/getRouteNodes.cjs +67 -33
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -1
- package/dist/cjs/filesystem/physical/getRouteNodes.d.cts +3 -2
- package/dist/cjs/generator.cjs +25 -3
- package/dist/cjs/generator.cjs.map +1 -1
- package/dist/cjs/utils.cjs +69 -16
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +41 -0
- package/dist/esm/filesystem/physical/getRouteNodes.d.ts +3 -2
- package/dist/esm/filesystem/physical/getRouteNodes.js +68 -34
- package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -1
- package/dist/esm/generator.js +26 -4
- package/dist/esm/generator.js.map +1 -1
- package/dist/esm/utils.d.ts +41 -0
- package/dist/esm/utils.js +69 -16
- package/dist/esm/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/filesystem/physical/getRouteNodes.ts +119 -38
- package/src/generator.ts +33 -5
- package/src/utils.ts +153 -17
package/src/utils.ts
CHANGED
|
@@ -140,23 +140,26 @@ export function removeTrailingSlash(s: string) {
|
|
|
140
140
|
const BRACKET_CONTENT_RE = /\[(.*?)\]/g
|
|
141
141
|
const SPLIT_REGEX = /(?<!\[)\.(?!\])/g
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Characters that cannot be escaped in square brackets.
|
|
145
|
+
* These are characters that would cause issues in URLs or file systems.
|
|
146
|
+
*/
|
|
147
|
+
const DISALLOWED_ESCAPE_CHARS = new Set([
|
|
148
|
+
'/',
|
|
149
|
+
'\\',
|
|
150
|
+
'?',
|
|
151
|
+
'#',
|
|
152
|
+
':',
|
|
153
|
+
'*',
|
|
154
|
+
'<',
|
|
155
|
+
'>',
|
|
156
|
+
'|',
|
|
157
|
+
'!',
|
|
158
|
+
'$',
|
|
159
|
+
'%',
|
|
160
|
+
])
|
|
159
161
|
|
|
162
|
+
export function determineInitialRoutePath(routePath: string) {
|
|
160
163
|
const originalRoutePath =
|
|
161
164
|
cleanPath(
|
|
162
165
|
`/${(cleanPath(routePath) || '').split(SPLIT_REGEX).join('/')}`,
|
|
@@ -201,6 +204,47 @@ export function determineInitialRoutePath(routePath: string) {
|
|
|
201
204
|
}
|
|
202
205
|
}
|
|
203
206
|
|
|
207
|
+
/**
|
|
208
|
+
* Checks if a segment is fully escaped (entirely wrapped in brackets with no nested brackets).
|
|
209
|
+
* E.g., "[index]" -> true, "[_layout]" -> true, "foo[.]bar" -> false, "index" -> false
|
|
210
|
+
*/
|
|
211
|
+
function isFullyEscapedSegment(originalSegment: string): boolean {
|
|
212
|
+
return (
|
|
213
|
+
originalSegment.startsWith('[') &&
|
|
214
|
+
originalSegment.endsWith(']') &&
|
|
215
|
+
!originalSegment.slice(1, -1).includes('[') &&
|
|
216
|
+
!originalSegment.slice(1, -1).includes(']')
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Checks if the leading underscore in a segment is escaped.
|
|
222
|
+
* Returns true if:
|
|
223
|
+
* - Segment starts with [_] pattern: "[_]layout" -> "_layout"
|
|
224
|
+
* - Segment is fully escaped and content starts with _: "[_1nd3x]" -> "_1nd3x"
|
|
225
|
+
*/
|
|
226
|
+
export function hasEscapedLeadingUnderscore(originalSegment: string): boolean {
|
|
227
|
+
// Pattern: [_]something or [_something]
|
|
228
|
+
return (
|
|
229
|
+
originalSegment.startsWith('[_]') ||
|
|
230
|
+
(originalSegment.startsWith('[_') && isFullyEscapedSegment(originalSegment))
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Checks if the trailing underscore in a segment is escaped.
|
|
236
|
+
* Returns true if:
|
|
237
|
+
* - Segment ends with [_] pattern: "blog[_]" -> "blog_"
|
|
238
|
+
* - Segment is fully escaped and content ends with _: "[_r0ut3_]" -> "_r0ut3_"
|
|
239
|
+
*/
|
|
240
|
+
export function hasEscapedTrailingUnderscore(originalSegment: string): boolean {
|
|
241
|
+
// Pattern: something[_] or [something_]
|
|
242
|
+
return (
|
|
243
|
+
originalSegment.endsWith('[_]') ||
|
|
244
|
+
(originalSegment.endsWith('_]') && isFullyEscapedSegment(originalSegment))
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
|
|
204
248
|
const backslashRegex = /\\/g
|
|
205
249
|
|
|
206
250
|
export function replaceBackslash(s: string) {
|
|
@@ -271,6 +315,92 @@ export function removeUnderscores(s?: string) {
|
|
|
271
315
|
.replace(underscoreSlashRegex, '/')
|
|
272
316
|
}
|
|
273
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Removes underscores from a path, but preserves underscores that were escaped
|
|
320
|
+
* in the original path (indicated by [_] syntax).
|
|
321
|
+
*
|
|
322
|
+
* @param routePath - The path with brackets removed
|
|
323
|
+
* @param originalPath - The original path that may contain [_] escape sequences
|
|
324
|
+
* @returns The path with non-escaped underscores removed
|
|
325
|
+
*/
|
|
326
|
+
export function removeUnderscoresWithEscape(
|
|
327
|
+
routePath?: string,
|
|
328
|
+
originalPath?: string,
|
|
329
|
+
): string {
|
|
330
|
+
if (!routePath) return ''
|
|
331
|
+
if (!originalPath) return removeUnderscores(routePath) ?? ''
|
|
332
|
+
|
|
333
|
+
const routeSegments = routePath.split('/')
|
|
334
|
+
const originalSegments = originalPath.split('/')
|
|
335
|
+
|
|
336
|
+
const newSegments = routeSegments.map((segment, i) => {
|
|
337
|
+
const originalSegment = originalSegments[i] || ''
|
|
338
|
+
|
|
339
|
+
// Check if leading underscore is escaped
|
|
340
|
+
const leadingEscaped = hasEscapedLeadingUnderscore(originalSegment)
|
|
341
|
+
// Check if trailing underscore is escaped
|
|
342
|
+
const trailingEscaped = hasEscapedTrailingUnderscore(originalSegment)
|
|
343
|
+
|
|
344
|
+
let result = segment
|
|
345
|
+
|
|
346
|
+
// Remove leading underscore only if not escaped
|
|
347
|
+
if (result.startsWith('_') && !leadingEscaped) {
|
|
348
|
+
result = result.slice(1)
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Remove trailing underscore only if not escaped
|
|
352
|
+
if (result.endsWith('_') && !trailingEscaped) {
|
|
353
|
+
result = result.slice(0, -1)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return result
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
return newSegments.join('/')
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Removes layout segments (segments starting with underscore) from a path,
|
|
364
|
+
* but preserves segments where the underscore was escaped.
|
|
365
|
+
*
|
|
366
|
+
* @param routePath - The path with brackets removed
|
|
367
|
+
* @param originalPath - The original path that may contain [_] escape sequences
|
|
368
|
+
* @returns The path with non-escaped layout segments removed
|
|
369
|
+
*/
|
|
370
|
+
export function removeLayoutSegmentsWithEscape(
|
|
371
|
+
routePath: string = '/',
|
|
372
|
+
originalPath?: string,
|
|
373
|
+
): string {
|
|
374
|
+
if (!originalPath) return removeLayoutSegments(routePath)
|
|
375
|
+
|
|
376
|
+
const routeSegments = routePath.split('/')
|
|
377
|
+
const originalSegments = originalPath.split('/')
|
|
378
|
+
|
|
379
|
+
// Keep segments that are NOT pathless (i.e., don't start with unescaped underscore)
|
|
380
|
+
const newSegments = routeSegments.filter((segment, i) => {
|
|
381
|
+
const originalSegment = originalSegments[i] || ''
|
|
382
|
+
return !isSegmentPathless(segment, originalSegment)
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
return newSegments.join('/')
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Checks if a segment should be treated as a pathless/layout segment.
|
|
390
|
+
* A segment is pathless if it starts with underscore and the underscore is not escaped.
|
|
391
|
+
*
|
|
392
|
+
* @param segment - The segment from routePath (brackets removed)
|
|
393
|
+
* @param originalSegment - The segment from originalRoutePath (may contain brackets)
|
|
394
|
+
* @returns true if the segment is pathless (has non-escaped leading underscore)
|
|
395
|
+
*/
|
|
396
|
+
export function isSegmentPathless(
|
|
397
|
+
segment: string,
|
|
398
|
+
originalSegment: string,
|
|
399
|
+
): boolean {
|
|
400
|
+
if (!segment.startsWith('_')) return false
|
|
401
|
+
return !hasEscapedLeadingUnderscore(originalSegment)
|
|
402
|
+
}
|
|
403
|
+
|
|
274
404
|
function escapeRegExp(s: string): string {
|
|
275
405
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
276
406
|
}
|
|
@@ -495,7 +625,13 @@ export const inferPath = (routeNode: RouteNode): string => {
|
|
|
495
625
|
*/
|
|
496
626
|
export const inferFullPath = (routeNode: RouteNode): string => {
|
|
497
627
|
const fullPath = removeGroups(
|
|
498
|
-
|
|
628
|
+
removeUnderscoresWithEscape(
|
|
629
|
+
removeLayoutSegmentsWithEscape(
|
|
630
|
+
routeNode.routePath,
|
|
631
|
+
routeNode.originalRoutePath,
|
|
632
|
+
),
|
|
633
|
+
routeNode.originalRoutePath,
|
|
634
|
+
),
|
|
499
635
|
)
|
|
500
636
|
|
|
501
637
|
return routeNode.cleanedPath === '/' ? fullPath : fullPath.replace(/\/$/, '')
|