@tanstack/router-core 0.0.1-alpha.9 → 0.0.1-beta.1

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/src/router.ts CHANGED
@@ -25,8 +25,11 @@ import {
25
25
  } from './path'
26
26
  import { AnyRoute, cascadeLoaderData, createRoute, Route } from './route'
27
27
  import {
28
+ AnyLoaderData,
29
+ AnyPathParams,
28
30
  AnyRouteConfig,
29
31
  AnySearchSchema,
32
+ LoaderContext,
30
33
  RouteConfig,
31
34
  SearchFilter,
32
35
  } from './routeConfig'
@@ -43,6 +46,7 @@ import {
43
46
  functionalUpdate,
44
47
  last,
45
48
  PickAsRequired,
49
+ PickRequired,
46
50
  replaceEqualDeep,
47
51
  Timeout,
48
52
  Updater,
@@ -99,6 +103,11 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
99
103
  basepath?: string
100
104
  createRouter?: (router: Router<any, any>) => void
101
105
  createRoute?: (opts: { route: AnyRoute; router: Router<any, any> }) => void
106
+ createElement?: (
107
+ element:
108
+ | GetFrameworkGeneric<'Element'>
109
+ | (() => Promise<GetFrameworkGeneric<'Element'>>),
110
+ ) => Promise<GetFrameworkGeneric<'Element'>>
102
111
  }
103
112
 
104
113
  export interface Action<
@@ -124,6 +133,42 @@ export interface ActionState<
124
133
  error?: unknown
125
134
  }
126
135
 
136
+ export interface Loader<
137
+ TFullSearchSchema extends AnySearchSchema = {},
138
+ TAllParams extends AnyPathParams = {},
139
+ TRouteLoaderData = AnyLoaderData,
140
+ > {
141
+ fetch: keyof PickRequired<TFullSearchSchema> extends never
142
+ ? keyof TAllParams extends never
143
+ ? (loaderContext: { signal?: AbortSignal }) => Promise<TRouteLoaderData>
144
+ : (loaderContext: {
145
+ params: TAllParams
146
+ search?: TFullSearchSchema
147
+ signal?: AbortSignal
148
+ }) => Promise<TRouteLoaderData>
149
+ : keyof TAllParams extends never
150
+ ? (loaderContext: {
151
+ search: TFullSearchSchema
152
+ params: TAllParams
153
+ signal?: AbortSignal
154
+ }) => Promise<TRouteLoaderData>
155
+ : (loaderContext: {
156
+ search: TFullSearchSchema
157
+ signal?: AbortSignal
158
+ }) => Promise<TRouteLoaderData>
159
+ current?: LoaderState<TFullSearchSchema, TAllParams>
160
+ latest?: LoaderState<TFullSearchSchema, TAllParams>
161
+ pending: LoaderState<TFullSearchSchema, TAllParams>[]
162
+ }
163
+
164
+ export interface LoaderState<
165
+ TFullSearchSchema = unknown,
166
+ TAllParams = unknown,
167
+ > {
168
+ loadedAt: number
169
+ loaderContext: LoaderContext<TFullSearchSchema, TAllParams>
170
+ }
171
+
127
172
  export interface RouterState {
128
173
  status: 'idle' | 'loading'
129
174
  location: Location
@@ -133,6 +178,7 @@ export interface RouterState {
133
178
  currentAction?: ActionState
134
179
  latestAction?: ActionState
135
180
  actions: Record<string, Action>
181
+ loaders: Record<string, Loader>
136
182
  pending?: PendingState
137
183
  isFetching: boolean
138
184
  isPreloading: boolean
@@ -185,6 +231,7 @@ export interface Router<
185
231
  TRouteConfig extends AnyRouteConfig = RouteConfig,
186
232
  TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
187
233
  > {
234
+ history: BrowserHistory | MemoryHistory | HashHistory
188
235
  options: PickAsRequired<
189
236
  RouterOptions<TRouteConfig>,
190
237
  'stringifySearch' | 'parseSearch'
@@ -211,15 +258,7 @@ export interface Router<
211
258
  update: <TRouteConfig extends RouteConfig = RouteConfig>(
212
259
  opts?: RouterOptions<TRouteConfig>,
213
260
  ) => Router<TRouteConfig>
214
- buildRouteTree: (
215
- routeConfig: RouteConfig,
216
- ) => Route<TAllRouteInfo, AnyRouteInfo>
217
- parseLocation: (
218
- location: History['location'],
219
- previousLocation?: Location,
220
- ) => Location
221
- buildLocation: (dest: BuildNextOptions) => Location
222
- commitLocation: (next: Location, replace?: boolean) => Promise<void>
261
+
223
262
  buildNext: (opts: BuildNextOptions) => Location
224
263
  cancelMatches: () => void
225
264
  loadLocation: (next?: Location) => Promise<void>
@@ -247,9 +286,6 @@ export interface Router<
247
286
  invalidateRoute: (opts: MatchLocation) => void
248
287
  reload: () => Promise<void>
249
288
  resolvePath: (from: string, path: string) => string
250
- _navigate: (
251
- location: BuildNextOptions & { replace?: boolean },
252
- ) => Promise<void>
253
289
  navigate: <
254
290
  TFrom extends ValidFromPath<TAllRouteInfo> = '/',
255
291
  TTo extends string = '.',
@@ -269,6 +305,20 @@ export interface Router<
269
305
  >(
270
306
  opts: LinkOptions<TAllRouteInfo, TFrom, TTo>,
271
307
  ) => LinkInfo
308
+ __: {
309
+ buildRouteTree: (
310
+ routeConfig: RouteConfig,
311
+ ) => Route<TAllRouteInfo, AnyRouteInfo>
312
+ parseLocation: (
313
+ location: History['location'],
314
+ previousLocation?: Location,
315
+ ) => Location
316
+ buildLocation: (dest: BuildNextOptions) => Location
317
+ commitLocation: (next: Location, replace?: boolean) => Promise<void>
318
+ navigate: (
319
+ location: BuildNextOptions & { replace?: boolean },
320
+ ) => Promise<void>
321
+ }
272
322
  }
273
323
 
274
324
  // Detect if we're in the DOM
@@ -299,6 +349,7 @@ export function createRouter<
299
349
  }
300
350
 
301
351
  let router: Router<TRouteConfig, TAllRouteInfo> = {
352
+ history,
302
353
  options: originalOptions,
303
354
  listeners: [],
304
355
  removeActionQueue: [],
@@ -317,6 +368,7 @@ export function createRouter<
317
368
  location: null!,
318
369
  matches: [],
319
370
  actions: {},
371
+ loaders: {},
320
372
  loaderData: {} as any,
321
373
  lastUpdated: Date.now(),
322
374
  isFetching: false,
@@ -350,7 +402,7 @@ export function createRouter<
350
402
  },
351
403
 
352
404
  mount: () => {
353
- const next = router.buildLocation({
405
+ const next = router.__.buildLocation({
354
406
  to: '.',
355
407
  search: true,
356
408
  hash: true,
@@ -359,14 +411,14 @@ export function createRouter<
359
411
  // If the current location isn't updated, trigger a navigation
360
412
  // to the current location. Otherwise, load the current location.
361
413
  if (next.href !== router.location.href) {
362
- router.commitLocation(next, true)
414
+ router.__.commitLocation(next, true)
363
415
  } else {
364
416
  router.loadLocation()
365
417
  }
366
418
 
367
419
  const unsub = history.listen((event) => {
368
420
  router.loadLocation(
369
- router.parseLocation(event.location, router.location),
421
+ router.__.parseLocation(event.location, router.location),
370
422
  )
371
423
  })
372
424
 
@@ -399,235 +451,12 @@ export function createRouter<
399
451
 
400
452
  if (routeConfig) {
401
453
  router.routesById = {} as any
402
- router.routeTree = router.buildRouteTree(routeConfig)
454
+ router.routeTree = router.__.buildRouteTree(routeConfig)
403
455
  }
404
456
 
405
457
  return router as any
406
458
  },
407
459
 
408
- buildRouteTree: (rootRouteConfig: RouteConfig) => {
409
- const recurseRoutes = (
410
- routeConfigs: RouteConfig[],
411
- parent?: Route<TAllRouteInfo, any>,
412
- ): Route<TAllRouteInfo, any>[] => {
413
- return routeConfigs.map((routeConfig) => {
414
- const routeOptions = routeConfig.options
415
- const route = createRoute(routeConfig, routeOptions, parent, router)
416
-
417
- // {
418
- // pendingMs: routeOptions.pendingMs ?? router.defaultPendingMs,
419
- // pendingMinMs: routeOptions.pendingMinMs ?? router.defaultPendingMinMs,
420
- // }
421
-
422
- const existingRoute = (router.routesById as any)[route.routeId]
423
-
424
- if (existingRoute) {
425
- if (process.env.NODE_ENV !== 'production') {
426
- console.warn(
427
- `Duplicate routes found with id: ${String(route.routeId)}`,
428
- router.routesById,
429
- route,
430
- )
431
- }
432
- throw new Error()
433
- }
434
-
435
- ;(router.routesById as any)[route.routeId] = route
436
-
437
- const children = routeConfig.children as RouteConfig[]
438
-
439
- route.childRoutes = children?.length
440
- ? recurseRoutes(children, route)
441
- : undefined
442
-
443
- return route
444
- })
445
- }
446
-
447
- const routes = recurseRoutes([rootRouteConfig])
448
-
449
- return routes[0]!
450
- },
451
-
452
- parseLocation: (
453
- location: History['location'],
454
- previousLocation?: Location,
455
- ): Location => {
456
- const parsedSearch = router.options.parseSearch(location.search)
457
-
458
- return {
459
- pathname: location.pathname,
460
- searchStr: location.search,
461
- search: replaceEqualDeep(previousLocation?.search, parsedSearch),
462
- hash: location.hash.split('#').reverse()[0] ?? '',
463
- href: `${location.pathname}${location.search}${location.hash}`,
464
- state: location.state as LocationState,
465
- key: location.key,
466
- }
467
- },
468
-
469
- buildLocation: (dest: BuildNextOptions = {}): Location => {
470
- // const resolvedFrom: Location = {
471
- // ...router.location,
472
- const fromPathname = dest.fromCurrent
473
- ? router.location.pathname
474
- : dest.from ?? router.location.pathname
475
-
476
- let pathname = resolvePath(
477
- router.basepath ?? '/',
478
- fromPathname,
479
- `${dest.to ?? '.'}`,
480
- )
481
-
482
- const fromMatches = router.matchRoutes(router.location.pathname, {
483
- strictParseParams: true,
484
- })
485
-
486
- const toMatches = router.matchRoutes(pathname)
487
-
488
- const prevParams = { ...last(fromMatches)?.params }
489
-
490
- let nextParams =
491
- (dest.params ?? true) === true
492
- ? prevParams
493
- : functionalUpdate(dest.params!, prevParams)
494
-
495
- if (nextParams) {
496
- toMatches
497
- .map((d) => d.options.stringifyParams)
498
- .filter(Boolean)
499
- .forEach((fn) => {
500
- Object.assign({}, nextParams!, fn!(nextParams!))
501
- })
502
- }
503
-
504
- pathname = interpolatePath(pathname, nextParams ?? {})
505
-
506
- // Pre filters first
507
- const preFilteredSearch = dest.__preSearchFilters?.length
508
- ? dest.__preSearchFilters.reduce(
509
- (prev, next) => next(prev),
510
- router.location.search,
511
- )
512
- : router.location.search
513
-
514
- // Then the link/navigate function
515
- const destSearch =
516
- dest.search === true
517
- ? preFilteredSearch // Preserve resolvedFrom true
518
- : dest.search
519
- ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
520
- : dest.__preSearchFilters?.length
521
- ? preFilteredSearch // Preserve resolvedFrom filters
522
- : {}
523
-
524
- // Then post filters
525
- const postFilteredSearch = dest.__postSearchFilters?.length
526
- ? dest.__postSearchFilters.reduce(
527
- (prev, next) => next(prev),
528
- destSearch,
529
- )
530
- : destSearch
531
-
532
- const search = replaceEqualDeep(
533
- router.location.search,
534
- postFilteredSearch,
535
- )
536
-
537
- const searchStr = router.options.stringifySearch(search)
538
- let hash =
539
- dest.hash === true
540
- ? router.location.hash
541
- : functionalUpdate(dest.hash!, router.location.hash)
542
- hash = hash ? `#${hash}` : ''
543
-
544
- return {
545
- pathname,
546
- search,
547
- searchStr,
548
- state: router.location.state,
549
- hash,
550
- href: `${pathname}${searchStr}${hash}`,
551
- key: dest.key,
552
- }
553
- },
554
-
555
- commitLocation: (next: Location, replace?: boolean): Promise<void> => {
556
- const id = '' + Date.now() + Math.random()
557
-
558
- if (router.navigateTimeout) clearTimeout(router.navigateTimeout)
559
-
560
- let nextAction: 'push' | 'replace' = 'replace'
561
-
562
- if (!replace) {
563
- nextAction = 'push'
564
- }
565
-
566
- const isSameUrl =
567
- router.parseLocation(history.location).href === next.href
568
-
569
- if (isSameUrl && !next.key) {
570
- nextAction = 'replace'
571
- }
572
-
573
- if (nextAction === 'replace') {
574
- history.replace(
575
- {
576
- pathname: next.pathname,
577
- hash: next.hash,
578
- search: next.searchStr,
579
- },
580
- {
581
- id,
582
- },
583
- )
584
- } else {
585
- history.push(
586
- {
587
- pathname: next.pathname,
588
- hash: next.hash,
589
- search: next.searchStr,
590
- },
591
- {
592
- id,
593
- },
594
- )
595
- }
596
-
597
- router.navigationPromise = new Promise((resolve) => {
598
- const previousNavigationResolve = router.resolveNavigation
599
-
600
- router.resolveNavigation = () => {
601
- previousNavigationResolve()
602
- resolve()
603
- }
604
- })
605
-
606
- return router.navigationPromise
607
- },
608
-
609
- buildNext: (opts: BuildNextOptions) => {
610
- const next = router.buildLocation(opts)
611
-
612
- const matches = router.matchRoutes(next.pathname)
613
-
614
- const __preSearchFilters = matches
615
- .map((match) => match.options.preSearchFilters ?? [])
616
- .flat()
617
- .filter(Boolean)
618
-
619
- const __postSearchFilters = matches
620
- .map((match) => match.options.postSearchFilters ?? [])
621
- .flat()
622
- .filter(Boolean)
623
-
624
- return router.buildLocation({
625
- ...opts,
626
- __preSearchFilters,
627
- __postSearchFilters,
628
- })
629
- },
630
-
631
460
  cancelMatches: () => {
632
461
  ;[
633
462
  ...router.state.matches,
@@ -739,6 +568,7 @@ export function createRouter<
739
568
  params: d.params,
740
569
  search: d.search,
741
570
  })
571
+ delete router.matchCache[d.matchId]
742
572
  })
743
573
 
744
574
  if (matches.some((d) => d.status === 'loading')) {
@@ -922,38 +752,10 @@ export function createRouter<
922
752
  },
923
753
 
924
754
  loadMatches: async (resolvedMatches, loaderOpts) => {
925
- const now = Date.now()
926
- const minMaxAge = loaderOpts?.preload
927
- ? Math.max(loaderOpts?.maxAge, loaderOpts?.gcMaxAge)
928
- : 0
929
-
930
755
  const matchPromises = resolvedMatches.map(async (match) => {
931
756
  // Validate the match (loads search params etc)
932
757
  match.__.validate()
933
-
934
- // If this is a preload, add it to the preload cache
935
- if (loaderOpts?.preload && minMaxAge > 0) {
936
- // If the match is currently active, don't preload it
937
- if (router.state.matches.find((d) => d.matchId === match.matchId)) {
938
- return
939
- }
940
-
941
- router.matchCache[match.matchId] = {
942
- gc: now + loaderOpts.gcMaxAge,
943
- match,
944
- }
945
- }
946
-
947
- // If the match is invalid, errored or idle, trigger it to load
948
- if (
949
- (match.status === 'success' && match.getIsInvalid()) ||
950
- match.status === 'error' ||
951
- match.status === 'idle'
952
- ) {
953
- const maxAge = loaderOpts?.preload ? loaderOpts?.maxAge : undefined
954
-
955
- match.load({ maxAge })
956
- }
758
+ match.load(loaderOpts)
957
759
 
958
760
  if (match.status === 'loading') {
959
761
  // If requested, start the pending timers
@@ -986,7 +788,7 @@ export function createRouter<
986
788
  },
987
789
 
988
790
  reload: () =>
989
- router._navigate({
791
+ router.__.navigate({
990
792
  fromCurrent: true,
991
793
  replace: true,
992
794
  search: true,
@@ -1024,11 +826,6 @@ export function createRouter<
1024
826
  })
1025
827
  },
1026
828
 
1027
- _navigate: (location: BuildNextOptions & { replace?: boolean }) => {
1028
- const next = router.buildNext(location)
1029
- return router.commitLocation(next, location.replace)
1030
- },
1031
-
1032
829
  navigate: async ({ from, to = '.', search, hash, replace, params }) => {
1033
830
  // If this link simply reloads the current route,
1034
831
  // make sure it has a new key so it will trigger a data refresh
@@ -1050,7 +847,7 @@ export function createRouter<
1050
847
  'Attempting to navigate to external url with router.navigate!',
1051
848
  )
1052
849
 
1053
- return router._navigate({
850
+ return router.__.navigate({
1054
851
  from: fromString,
1055
852
  to: toString,
1056
853
  search,
@@ -1134,7 +931,7 @@ export function createRouter<
1134
931
  }
1135
932
 
1136
933
  // All is well? Navigate!)
1137
- router._navigate(nextOpts)
934
+ router.__.navigate(nextOpts)
1138
935
  }
1139
936
  }
1140
937
 
@@ -1186,9 +983,238 @@ export function createRouter<
1186
983
  disabled,
1187
984
  }
1188
985
  },
986
+ buildNext: (opts: BuildNextOptions) => {
987
+ const next = router.__.buildLocation(opts)
988
+
989
+ const matches = router.matchRoutes(next.pathname)
990
+
991
+ const __preSearchFilters = matches
992
+ .map((match) => match.options.preSearchFilters ?? [])
993
+ .flat()
994
+ .filter(Boolean)
995
+
996
+ const __postSearchFilters = matches
997
+ .map((match) => match.options.postSearchFilters ?? [])
998
+ .flat()
999
+ .filter(Boolean)
1000
+
1001
+ return router.__.buildLocation({
1002
+ ...opts,
1003
+ __preSearchFilters,
1004
+ __postSearchFilters,
1005
+ })
1006
+ },
1007
+
1008
+ __: {
1009
+ buildRouteTree: (rootRouteConfig: RouteConfig) => {
1010
+ const recurseRoutes = (
1011
+ routeConfigs: RouteConfig[],
1012
+ parent?: Route<TAllRouteInfo, any>,
1013
+ ): Route<TAllRouteInfo, any>[] => {
1014
+ return routeConfigs.map((routeConfig) => {
1015
+ const routeOptions = routeConfig.options
1016
+ const route = createRoute(routeConfig, routeOptions, parent, router)
1017
+
1018
+ // {
1019
+ // pendingMs: routeOptions.pendingMs ?? router.defaultPendingMs,
1020
+ // pendingMinMs: routeOptions.pendingMinMs ?? router.defaultPendingMinMs,
1021
+ // }
1022
+
1023
+ const existingRoute = (router.routesById as any)[route.routeId]
1024
+
1025
+ if (existingRoute) {
1026
+ if (process.env.NODE_ENV !== 'production') {
1027
+ console.warn(
1028
+ `Duplicate routes found with id: ${String(route.routeId)}`,
1029
+ router.routesById,
1030
+ route,
1031
+ )
1032
+ }
1033
+ throw new Error()
1034
+ }
1035
+
1036
+ ;(router.routesById as any)[route.routeId] = route
1037
+
1038
+ const children = routeConfig.children as RouteConfig[]
1039
+
1040
+ route.childRoutes = children?.length
1041
+ ? recurseRoutes(children, route)
1042
+ : undefined
1043
+
1044
+ return route
1045
+ })
1046
+ }
1047
+
1048
+ const routes = recurseRoutes([rootRouteConfig])
1049
+
1050
+ return routes[0]!
1051
+ },
1052
+
1053
+ parseLocation: (
1054
+ location: History['location'],
1055
+ previousLocation?: Location,
1056
+ ): Location => {
1057
+ const parsedSearch = router.options.parseSearch(location.search)
1058
+
1059
+ return {
1060
+ pathname: location.pathname,
1061
+ searchStr: location.search,
1062
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1063
+ hash: location.hash.split('#').reverse()[0] ?? '',
1064
+ href: `${location.pathname}${location.search}${location.hash}`,
1065
+ state: location.state as LocationState,
1066
+ key: location.key,
1067
+ }
1068
+ },
1069
+
1070
+ navigate: (location: BuildNextOptions & { replace?: boolean }) => {
1071
+ const next = router.buildNext(location)
1072
+ return router.__.commitLocation(next, location.replace)
1073
+ },
1074
+
1075
+ buildLocation: (dest: BuildNextOptions = {}): Location => {
1076
+ // const resolvedFrom: Location = {
1077
+ // ...router.location,
1078
+ const fromPathname = dest.fromCurrent
1079
+ ? router.location.pathname
1080
+ : dest.from ?? router.location.pathname
1081
+
1082
+ let pathname = resolvePath(
1083
+ router.basepath ?? '/',
1084
+ fromPathname,
1085
+ `${dest.to ?? '.'}`,
1086
+ )
1087
+
1088
+ const fromMatches = router.matchRoutes(router.location.pathname, {
1089
+ strictParseParams: true,
1090
+ })
1091
+
1092
+ const toMatches = router.matchRoutes(pathname)
1093
+
1094
+ const prevParams = { ...last(fromMatches)?.params }
1095
+
1096
+ let nextParams =
1097
+ (dest.params ?? true) === true
1098
+ ? prevParams
1099
+ : functionalUpdate(dest.params!, prevParams)
1100
+
1101
+ if (nextParams) {
1102
+ toMatches
1103
+ .map((d) => d.options.stringifyParams)
1104
+ .filter(Boolean)
1105
+ .forEach((fn) => {
1106
+ Object.assign({}, nextParams!, fn!(nextParams!))
1107
+ })
1108
+ }
1109
+
1110
+ pathname = interpolatePath(pathname, nextParams ?? {})
1111
+
1112
+ // Pre filters first
1113
+ const preFilteredSearch = dest.__preSearchFilters?.length
1114
+ ? dest.__preSearchFilters.reduce(
1115
+ (prev, next) => next(prev),
1116
+ router.location.search,
1117
+ )
1118
+ : router.location.search
1119
+
1120
+ // Then the link/navigate function
1121
+ const destSearch =
1122
+ dest.search === true
1123
+ ? preFilteredSearch // Preserve resolvedFrom true
1124
+ : dest.search
1125
+ ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
1126
+ : dest.__preSearchFilters?.length
1127
+ ? preFilteredSearch // Preserve resolvedFrom filters
1128
+ : {}
1129
+
1130
+ // Then post filters
1131
+ const postFilteredSearch = dest.__postSearchFilters?.length
1132
+ ? dest.__postSearchFilters.reduce(
1133
+ (prev, next) => next(prev),
1134
+ destSearch,
1135
+ )
1136
+ : destSearch
1137
+
1138
+ const search = replaceEqualDeep(
1139
+ router.location.search,
1140
+ postFilteredSearch,
1141
+ )
1142
+
1143
+ const searchStr = router.options.stringifySearch(search)
1144
+ let hash =
1145
+ dest.hash === true
1146
+ ? router.location.hash
1147
+ : functionalUpdate(dest.hash!, router.location.hash)
1148
+ hash = hash ? `#${hash}` : ''
1149
+
1150
+ return {
1151
+ pathname,
1152
+ search,
1153
+ searchStr,
1154
+ state: router.location.state,
1155
+ hash,
1156
+ href: `${pathname}${searchStr}${hash}`,
1157
+ key: dest.key,
1158
+ }
1159
+ },
1160
+
1161
+ commitLocation: (next: Location, replace?: boolean): Promise<void> => {
1162
+ const id = '' + Date.now() + Math.random()
1163
+
1164
+ if (router.navigateTimeout) clearTimeout(router.navigateTimeout)
1165
+
1166
+ let nextAction: 'push' | 'replace' = 'replace'
1167
+
1168
+ if (!replace) {
1169
+ nextAction = 'push'
1170
+ }
1171
+
1172
+ const isSameUrl =
1173
+ router.__.parseLocation(history.location).href === next.href
1174
+
1175
+ if (isSameUrl && !next.key) {
1176
+ nextAction = 'replace'
1177
+ }
1178
+
1179
+ if (nextAction === 'replace') {
1180
+ history.replace(
1181
+ {
1182
+ pathname: next.pathname,
1183
+ hash: next.hash,
1184
+ search: next.searchStr,
1185
+ },
1186
+ {
1187
+ id,
1188
+ },
1189
+ )
1190
+ } else {
1191
+ history.push(
1192
+ {
1193
+ pathname: next.pathname,
1194
+ hash: next.hash,
1195
+ search: next.searchStr,
1196
+ },
1197
+ {
1198
+ id,
1199
+ },
1200
+ )
1201
+ }
1202
+
1203
+ router.navigationPromise = new Promise((resolve) => {
1204
+ const previousNavigationResolve = router.resolveNavigation
1205
+
1206
+ router.resolveNavigation = () => {
1207
+ previousNavigationResolve()
1208
+ resolve()
1209
+ }
1210
+ })
1211
+
1212
+ return router.navigationPromise
1213
+ },
1214
+ },
1189
1215
  }
1190
1216
 
1191
- router.location = router.parseLocation(history.location)
1217
+ router.location = router.__.parseLocation(history.location)
1192
1218
  router.state.location = router.location
1193
1219
 
1194
1220
  router.update(userOptions)