@tanstack/router-core 0.0.1-alpha.1 → 0.0.1-alpha.11

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.
Files changed (43) hide show
  1. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js +30 -0
  2. package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +1 -0
  3. package/build/cjs/packages/router-core/src/index.js +35 -1421
  4. package/build/cjs/packages/router-core/src/index.js.map +1 -1
  5. package/build/cjs/packages/router-core/src/path.js +222 -0
  6. package/build/cjs/packages/router-core/src/path.js.map +1 -0
  7. package/build/cjs/packages/router-core/src/qss.js +1 -1
  8. package/build/cjs/packages/router-core/src/qss.js.map +1 -1
  9. package/build/cjs/packages/router-core/src/route.js +161 -0
  10. package/build/cjs/packages/router-core/src/route.js.map +1 -0
  11. package/build/cjs/packages/router-core/src/routeConfig.js +69 -0
  12. package/build/cjs/packages/router-core/src/routeConfig.js.map +1 -0
  13. package/build/cjs/packages/router-core/src/routeMatch.js +266 -0
  14. package/build/cjs/packages/router-core/src/routeMatch.js.map +1 -0
  15. package/build/cjs/packages/router-core/src/router.js +789 -0
  16. package/build/cjs/packages/router-core/src/router.js.map +1 -0
  17. package/build/cjs/packages/router-core/src/searchParams.js +70 -0
  18. package/build/cjs/packages/router-core/src/searchParams.js.map +1 -0
  19. package/build/cjs/packages/router-core/src/utils.js +118 -0
  20. package/build/cjs/packages/router-core/src/utils.js.map +1 -0
  21. package/build/esm/index.js +1385 -1232
  22. package/build/esm/index.js.map +1 -1
  23. package/build/stats-html.html +1 -1
  24. package/build/stats-react.json +388 -46
  25. package/build/types/index.d.ts +433 -338
  26. package/build/umd/index.development.js +1226 -1065
  27. package/build/umd/index.development.js.map +1 -1
  28. package/build/umd/index.production.js +1 -1
  29. package/build/umd/index.production.js.map +1 -1
  30. package/package.json +2 -2
  31. package/src/frameworks.ts +12 -0
  32. package/src/index.ts +15 -2969
  33. package/src/link.ts +319 -0
  34. package/src/path.ts +236 -0
  35. package/src/qss.ts +1 -1
  36. package/src/route.ts +243 -0
  37. package/src/routeConfig.ts +495 -0
  38. package/src/routeInfo.ts +228 -0
  39. package/src/routeMatch.ts +374 -0
  40. package/src/router.ts +1230 -0
  41. package/src/searchParams.ts +54 -0
  42. package/src/utils.ts +157 -0
  43. package/src/createRoutes.test.ts +0 -328
package/src/link.ts ADDED
@@ -0,0 +1,319 @@
1
+ import { AnyPathParams } from './routeConfig'
2
+ import {
3
+ AnyAllRouteInfo,
4
+ DefaultAllRouteInfo,
5
+ RouteInfoByPath,
6
+ } from './routeInfo'
7
+ import { Location } from './router'
8
+ import { Expand, NoInfer, PickAsRequired, PickRequired, Updater } from './utils'
9
+
10
+ export type LinkInfo =
11
+ | {
12
+ type: 'external'
13
+ href: string
14
+ }
15
+ | {
16
+ type: 'internal'
17
+ next: Location
18
+ handleFocus: (e: any) => void
19
+ handleClick: (e: any) => void
20
+ handleEnter: (e: any) => void
21
+ handleLeave: (e: any) => void
22
+ isActive: boolean
23
+ disabled?: boolean
24
+ }
25
+
26
+ type StartsWith<A, B> = A extends `${B extends string ? B : never}${infer _}`
27
+ ? true
28
+ : false
29
+
30
+ type CleanPath<T extends string> = T extends `${infer L}//${infer R}`
31
+ ? CleanPath<`${CleanPath<L>}/${CleanPath<R>}`>
32
+ : T extends `${infer L}//`
33
+ ? `${CleanPath<L>}/`
34
+ : T extends `//${infer L}`
35
+ ? `/${CleanPath<L>}`
36
+ : T
37
+
38
+ export type Split<S, TIncludeTrailingSlash = true> = S extends unknown
39
+ ? string extends S
40
+ ? string[]
41
+ : S extends string
42
+ ? CleanPath<S> extends ''
43
+ ? []
44
+ : TIncludeTrailingSlash extends true
45
+ ? CleanPath<S> extends `${infer T}/`
46
+ ? [...Split<T>, '/']
47
+ : CleanPath<S> extends `/${infer U}`
48
+ ? Split<U>
49
+ : CleanPath<S> extends `${infer T}/${infer U}`
50
+ ? [...Split<T>, ...Split<U>]
51
+ : [S]
52
+ : CleanPath<S> extends `${infer T}/${infer U}`
53
+ ? [...Split<T>, ...Split<U>]
54
+ : S extends string
55
+ ? [S]
56
+ : never
57
+ : never
58
+ : never
59
+
60
+ export type ParsePathParams<T extends string> = Split<T>[number] extends infer U
61
+ ? U extends `:${infer V}`
62
+ ? V
63
+ : never
64
+ : never
65
+
66
+ type Join<T> = T extends []
67
+ ? ''
68
+ : T extends [infer L extends string]
69
+ ? L
70
+ : T extends [infer L extends string, ...infer Tail extends [...string[]]]
71
+ ? CleanPath<`${L}/${Join<Tail>}`>
72
+ : never
73
+
74
+ export type RelativeToPathAutoComplete<
75
+ AllPaths extends string,
76
+ TFrom extends string,
77
+ TTo extends string,
78
+ SplitPaths extends string[] = Split<AllPaths, false>,
79
+ > = TTo extends `..${infer _}`
80
+ ? SplitPaths extends [
81
+ ...Split<ResolveRelativePath<TFrom, TTo>, false>,
82
+ ...infer TToRest,
83
+ ]
84
+ ? `${CleanPath<
85
+ Join<
86
+ [
87
+ ...Split<TTo, false>,
88
+ ...(
89
+ | TToRest
90
+ | (Split<
91
+ ResolveRelativePath<TFrom, TTo>,
92
+ false
93
+ >['length'] extends 1
94
+ ? never
95
+ : ['../'])
96
+ ),
97
+ ]
98
+ >
99
+ >}`
100
+ : never
101
+ : TTo extends `./${infer RestTTo}`
102
+ ? SplitPaths extends [
103
+ ...Split<TFrom, false>,
104
+ ...Split<RestTTo, false>,
105
+ ...infer RestPath,
106
+ ]
107
+ ? `${TTo}${Join<RestPath>}`
108
+ : never
109
+ : './' | '../' | AllPaths
110
+
111
+ export type NavigateOptionsAbsolute<
112
+ TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
113
+ TFrom extends ValidFromPath<TAllRouteInfo> = '/',
114
+ TTo extends string = '.',
115
+ > = ToOptions<TAllRouteInfo, TFrom, TTo> & {
116
+ // Whether to replace the current history stack instead of pushing a new one
117
+ replace?: boolean
118
+ }
119
+
120
+ export type ToOptions<
121
+ TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
122
+ TFrom extends ValidFromPath<TAllRouteInfo> = '/',
123
+ TTo extends string = '.',
124
+ TResolvedTo = ResolveRelativePath<TFrom, NoInfer<TTo>>,
125
+ > = {
126
+ to?: ToPathOption<TAllRouteInfo, TFrom, TTo>
127
+ // The new has string or a function to update it
128
+ hash?: Updater<string>
129
+ // The source route path. This is automatically set when using route-level APIs, but for type-safe relative routing on the router itself, this is required
130
+ from?: TFrom
131
+ // // When using relative route paths, this option forces resolution from the current path, instead of the route API's path or `from` path
132
+ // fromCurrent?: boolean
133
+ } & CheckPath<TAllRouteInfo, NoInfer<TResolvedTo>, {}> &
134
+ SearchParamOptions<TAllRouteInfo, TFrom, TResolvedTo> &
135
+ PathParamOptions<TAllRouteInfo, TFrom, TResolvedTo>
136
+
137
+ type SearchParamOptions<
138
+ TAllRouteInfo extends AnyAllRouteInfo,
139
+ TFrom,
140
+ TTo,
141
+ TFromSchema = RouteInfoByPath<TAllRouteInfo, TFrom>['fullSearchSchema'],
142
+ TToSchema = RouteInfoByPath<TAllRouteInfo, TTo>['fullSearchSchema'],
143
+ > = StartsWith<TFrom, TTo> extends true // If the next route search extend or cover the from route, params will be optional
144
+ ? {
145
+ search?: SearchReducer<TFromSchema, TToSchema>
146
+ }
147
+ : // Optional search params? Allow it
148
+ keyof PickRequired<TToSchema> extends never
149
+ ? {
150
+ search?: SearchReducer<TFromSchema, TToSchema>
151
+ }
152
+ : {
153
+ // Must have required search params, enforce it
154
+ search: SearchReducer<TFromSchema, TToSchema>
155
+ }
156
+
157
+ type SearchReducer<TFrom, TTo> =
158
+ | { [TKey in keyof TTo]: TTo[TKey] }
159
+ | ((current: TFrom) => TTo)
160
+
161
+ type PathParamOptions<
162
+ TAllRouteInfo extends AnyAllRouteInfo,
163
+ TFrom,
164
+ TTo,
165
+ TFromParams = RouteInfoByPath<TAllRouteInfo, TFrom>['allParams'],
166
+ TToParams = RouteInfoByPath<TAllRouteInfo, TTo>['allParams'],
167
+ > =
168
+ // If the next routes params extend or cover the from route, params will be optional
169
+ StartsWith<TFrom, TTo> extends true
170
+ ? {
171
+ params?: ParamsReducer<TFromParams, TToParams>
172
+ }
173
+ : // If the next route doesn't have params, warn if any have been passed
174
+ AnyPathParams extends TToParams
175
+ ? {
176
+ params?: ParamsReducer<TFromParams, Record<string, never>>
177
+ }
178
+ : // If the next route has params, enforce them
179
+ {
180
+ params: ParamsReducer<TFromParams, TToParams>
181
+ }
182
+
183
+ type ParamsReducer<TFrom, TTo> = TTo | ((current: TFrom) => TTo)
184
+
185
+ export type ToPathOption<
186
+ TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
187
+ TFrom extends ValidFromPath<TAllRouteInfo> = '/',
188
+ TTo extends string = '.',
189
+ > =
190
+ | TTo
191
+ | RelativeToPathAutoComplete<
192
+ TAllRouteInfo['routePaths'],
193
+ NoInfer<TFrom> extends string ? NoInfer<TFrom> : '',
194
+ NoInfer<TTo> & string
195
+ >
196
+
197
+ export type ToIdOption<
198
+ TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
199
+ TFrom extends ValidFromPath<TAllRouteInfo> = '/',
200
+ TTo extends string = '.',
201
+ > =
202
+ | TTo
203
+ | RelativeToPathAutoComplete<
204
+ TAllRouteInfo['routeIds'],
205
+ NoInfer<TFrom> extends string ? NoInfer<TFrom> : '',
206
+ NoInfer<TTo> & string
207
+ >
208
+
209
+ interface ActiveOptions {
210
+ exact?: boolean
211
+ includeHash?: boolean
212
+ }
213
+
214
+ export type LinkOptions<
215
+ TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
216
+ TFrom extends ValidFromPath<TAllRouteInfo> = '/',
217
+ TTo extends string = '.',
218
+ > = NavigateOptionsAbsolute<TAllRouteInfo, TFrom, TTo> & {
219
+ // The standard anchor tag target attribute
220
+ target?: HTMLAnchorElement['target']
221
+ // Defaults to `{ exact: false, includeHash: false }`
222
+ activeOptions?: ActiveOptions
223
+ // If set, will preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there.
224
+ preload?: false | 'intent'
225
+ // When preloaded, the preloaded result will be considered "fresh" for this duration in milliseconds
226
+ preloadMaxAge?: number
227
+ // When preloaded and subsequently inactive, the preloaded result will remain in memory for this duration in milliseconds
228
+ preloadGcMaxAge?: number
229
+ // Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled.
230
+ preloadDelay?: number
231
+ // If true, will render the link without the href attribute
232
+ disabled?: boolean
233
+ }
234
+
235
+ export type CheckRelativePath<
236
+ TAllRouteInfo extends AnyAllRouteInfo,
237
+ TFrom,
238
+ TTo,
239
+ > = TTo extends string
240
+ ? TFrom extends string
241
+ ? ResolveRelativePath<TFrom, TTo> extends TAllRouteInfo['routePaths']
242
+ ? {}
243
+ : {
244
+ Error: `${TFrom} + ${TTo} resolves to ${ResolveRelativePath<
245
+ TFrom,
246
+ TTo
247
+ >}, which is not a valid route path.`
248
+ 'Valid Route Paths': TAllRouteInfo['routePaths']
249
+ }
250
+ : {}
251
+ : {}
252
+
253
+ export type CheckPath<
254
+ TAllRouteInfo extends AnyAllRouteInfo,
255
+ TPath,
256
+ TPass,
257
+ > = Exclude<TPath, TAllRouteInfo['routePaths']> extends never
258
+ ? TPass
259
+ : CheckPathError<TAllRouteInfo, Exclude<TPath, TAllRouteInfo['routePaths']>>
260
+
261
+ export type CheckPathError<
262
+ TAllRouteInfo extends AnyAllRouteInfo,
263
+ TInvalids,
264
+ > = Expand<{
265
+ Error: `${TInvalids extends string
266
+ ? TInvalids
267
+ : never} is not a valid route path.`
268
+ 'Valid Route Paths': TAllRouteInfo['routePaths']
269
+ }>
270
+
271
+ export type CheckId<
272
+ TAllRouteInfo extends AnyAllRouteInfo,
273
+ TPath,
274
+ TPass,
275
+ > = Exclude<TPath, TAllRouteInfo['routeIds']> extends never
276
+ ? TPass
277
+ : CheckIdError<TAllRouteInfo, Exclude<TPath, TAllRouteInfo['routeIds']>>
278
+
279
+ export type CheckIdError<
280
+ TAllRouteInfo extends AnyAllRouteInfo,
281
+ TInvalids,
282
+ > = Expand<{
283
+ Error: `${TInvalids extends string
284
+ ? TInvalids
285
+ : never} is not a valid route ID.`
286
+ 'Valid Route IDs': TAllRouteInfo['routeIds']
287
+ }>
288
+
289
+ export type ResolveRelativePath<TFrom, TTo = '.'> = TFrom extends string
290
+ ? TTo extends string
291
+ ? TTo extends '.'
292
+ ? TFrom
293
+ : TTo extends `./`
294
+ ? Join<[TFrom, '/']>
295
+ : TTo extends `./${infer TRest}`
296
+ ? ResolveRelativePath<TFrom, TRest>
297
+ : TTo extends `/${infer TRest}`
298
+ ? TTo
299
+ : Split<TTo> extends ['..', ...infer ToRest]
300
+ ? Split<TFrom> extends [...infer FromRest, infer FromTail]
301
+ ? ToRest extends ['/']
302
+ ? Join<[...FromRest, '/']>
303
+ : ResolveRelativePath<Join<FromRest>, Join<ToRest>>
304
+ : never
305
+ : Split<TTo> extends ['.', ...infer ToRest]
306
+ ? ToRest extends ['/']
307
+ ? Join<[TFrom, '/']>
308
+ : ResolveRelativePath<TFrom, Join<ToRest>>
309
+ : CleanPath<Join<['/', ...Split<TFrom>, ...Split<TTo>]>>
310
+ : never
311
+ : never
312
+
313
+ export type ValidFromPath<
314
+ TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
315
+ > =
316
+ | undefined
317
+ | (string extends TAllRouteInfo['routePaths']
318
+ ? string
319
+ : TAllRouteInfo['routePaths'])
package/src/path.ts ADDED
@@ -0,0 +1,236 @@
1
+ import { AnyPathParams } from './routeConfig'
2
+ import { MatchLocation } from './router'
3
+ import { last } from './utils'
4
+
5
+ export interface Segment {
6
+ type: 'pathname' | 'param' | 'wildcard'
7
+ value: string
8
+ }
9
+
10
+ export function joinPaths(paths: (string | undefined)[]) {
11
+ return cleanPath(paths.filter(Boolean).join('/'))
12
+ }
13
+
14
+ export function cleanPath(path: string) {
15
+ // remove double slashes
16
+ return path.replace(/\/{2,}/g, '/')
17
+ }
18
+
19
+ export function trimPathLeft(path: string) {
20
+ return path === '/' ? path : path.replace(/^\/{1,}/, '')
21
+ }
22
+
23
+ export function trimPathRight(path: string) {
24
+ return path === '/' ? path : path.replace(/\/{1,}$/, '')
25
+ }
26
+
27
+ export function trimPath(path: string) {
28
+ return trimPathRight(trimPathLeft(path))
29
+ }
30
+
31
+ export function resolvePath(basepath: string, base: string, to: string) {
32
+ base = base.replace(new RegExp(`^${basepath}`), '/')
33
+ to = to.replace(new RegExp(`^${basepath}`), '/')
34
+
35
+ let baseSegments = parsePathname(base)
36
+ const toSegments = parsePathname(to)
37
+
38
+ toSegments.forEach((toSegment, index) => {
39
+ if (toSegment.value === '/') {
40
+ if (!index) {
41
+ // Leading slash
42
+ baseSegments = [toSegment]
43
+ } else if (index === toSegments.length - 1) {
44
+ // Trailing Slash
45
+ baseSegments.push(toSegment)
46
+ } else {
47
+ // ignore inter-slashes
48
+ }
49
+ } else if (toSegment.value === '..') {
50
+ // Extra trailing slash? pop it off
51
+ if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
52
+ baseSegments.pop()
53
+ }
54
+ baseSegments.pop()
55
+ } else if (toSegment.value === '.') {
56
+ return
57
+ } else {
58
+ baseSegments.push(toSegment)
59
+ }
60
+ })
61
+
62
+ const joined = joinPaths([basepath, ...baseSegments.map((d) => d.value)])
63
+
64
+ return cleanPath(joined)
65
+ }
66
+
67
+ export function parsePathname(pathname?: string): Segment[] {
68
+ if (!pathname) {
69
+ return []
70
+ }
71
+
72
+ pathname = cleanPath(pathname)
73
+
74
+ const segments: Segment[] = []
75
+
76
+ if (pathname.slice(0, 1) === '/') {
77
+ pathname = pathname.substring(1)
78
+ segments.push({
79
+ type: 'pathname',
80
+ value: '/',
81
+ })
82
+ }
83
+
84
+ if (!pathname) {
85
+ return segments
86
+ }
87
+
88
+ // Remove empty segments and '.' segments
89
+ const split = pathname.split('/').filter(Boolean)
90
+
91
+ segments.push(
92
+ ...split.map((part): Segment => {
93
+ if (part.startsWith('*')) {
94
+ return {
95
+ type: 'wildcard',
96
+ value: part,
97
+ }
98
+ }
99
+
100
+ if (part.charAt(0) === ':') {
101
+ return {
102
+ type: 'param',
103
+ value: part,
104
+ }
105
+ }
106
+
107
+ return {
108
+ type: 'pathname',
109
+ value: part,
110
+ }
111
+ }),
112
+ )
113
+
114
+ if (pathname.slice(-1) === '/') {
115
+ pathname = pathname.substring(1)
116
+ segments.push({
117
+ type: 'pathname',
118
+ value: '/',
119
+ })
120
+ }
121
+
122
+ return segments
123
+ }
124
+
125
+ export function interpolatePath(
126
+ path: string | undefined,
127
+ params: any,
128
+ leaveWildcard?: boolean,
129
+ ) {
130
+ const interpolatedPathSegments = parsePathname(path)
131
+
132
+ return joinPaths(
133
+ interpolatedPathSegments.map((segment) => {
134
+ if (segment.value === '*' && !leaveWildcard) {
135
+ return ''
136
+ }
137
+
138
+ if (segment.type === 'param') {
139
+ return params![segment.value.substring(1)] ?? ''
140
+ }
141
+
142
+ return segment.value
143
+ }),
144
+ )
145
+ }
146
+
147
+ export function matchPathname(
148
+ currentPathname: string,
149
+ matchLocation: Pick<MatchLocation, 'to' | 'fuzzy' | 'caseSensitive'>,
150
+ ): AnyPathParams | undefined {
151
+ const pathParams = matchByPath(currentPathname, matchLocation)
152
+ // const searchMatched = matchBySearch(currentLocation.search, matchLocation)
153
+
154
+ if (matchLocation.to && !pathParams) {
155
+ return
156
+ }
157
+
158
+ // if (matchLocation.search && !searchMatched) {
159
+ // return
160
+ // }
161
+
162
+ return pathParams ?? {}
163
+ }
164
+
165
+ export function matchByPath(
166
+ from: string,
167
+ matchLocation: Pick<MatchLocation, 'to' | 'caseSensitive' | 'fuzzy'>,
168
+ ): Record<string, string> | undefined {
169
+ const baseSegments = parsePathname(from)
170
+ const routeSegments = parsePathname(`${matchLocation.to ?? '*'}`)
171
+
172
+ const params: Record<string, string> = {}
173
+
174
+ let isMatch = (() => {
175
+ for (
176
+ let i = 0;
177
+ i < Math.max(baseSegments.length, routeSegments.length);
178
+ i++
179
+ ) {
180
+ const baseSegment = baseSegments[i]
181
+ const routeSegment = routeSegments[i]
182
+
183
+ const isLastRouteSegment = i === routeSegments.length - 1
184
+ const isLastBaseSegment = i === baseSegments.length - 1
185
+
186
+ if (routeSegment) {
187
+ if (routeSegment.type === 'wildcard') {
188
+ if (baseSegment?.value) {
189
+ params['*'] = joinPaths(baseSegments.slice(i).map((d) => d.value))
190
+ return true
191
+ }
192
+ return false
193
+ }
194
+
195
+ if (routeSegment.type === 'pathname') {
196
+ if (routeSegment.value === '/' && !baseSegment?.value) {
197
+ return true
198
+ }
199
+
200
+ if (baseSegment) {
201
+ if (matchLocation.caseSensitive) {
202
+ if (routeSegment.value !== baseSegment.value) {
203
+ return false
204
+ }
205
+ } else if (
206
+ routeSegment.value.toLowerCase() !==
207
+ baseSegment.value.toLowerCase()
208
+ ) {
209
+ return false
210
+ }
211
+ }
212
+ }
213
+
214
+ if (!baseSegment) {
215
+ return false
216
+ }
217
+
218
+ if (routeSegment.type === 'param') {
219
+ if (baseSegment?.value === '/') {
220
+ return false
221
+ }
222
+ if (!baseSegment.value.startsWith(':')) {
223
+ params[routeSegment.value.substring(1)] = baseSegment.value
224
+ }
225
+ }
226
+ }
227
+
228
+ if (isLastRouteSegment && !isLastBaseSegment) {
229
+ return !!matchLocation.fuzzy
230
+ }
231
+ }
232
+ return true
233
+ })()
234
+
235
+ return isMatch ? (params as Record<string, string>) : undefined
236
+ }
package/src/qss.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
 
3
- // We're inlining qss here for compression's sake, but we've included it as a hard dependency for the MIT license it requires.
3
+ // qss has been slightly modified and inlined here for our use cases (and compression's sake). We've included it as a hard dependency for MIT license attribution.
4
4
 
5
5
  export function encode(obj, pfx?: string) {
6
6
  var k,