react-router 6.0.0-beta.5 → 6.0.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.
@@ -1,27 +1,19 @@
1
+ /**
2
+ * React Router v6.0.0
3
+ *
4
+ * Copyright (c) Remix Software Inc.
5
+ *
6
+ * This source code is licensed under the MIT license found in the
7
+ * LICENSE.md file in the root directory of this source tree.
8
+ *
9
+ * @license MIT
10
+ */
1
11
  (function (global, factory) {
2
12
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('history')) :
3
13
  typeof define === 'function' && define.amd ? define(['exports', 'react', 'history'], factory) :
4
14
  (global = global || self, factory(global.ReactRouter = {}, global.React, global.HistoryLibrary));
5
15
  }(this, (function (exports, React, history) { 'use strict';
6
16
 
7
- function _extends() {
8
- _extends = Object.assign || function (target) {
9
- for (var i = 1; i < arguments.length; i++) {
10
- var source = arguments[i];
11
-
12
- for (var key in source) {
13
- if (Object.prototype.hasOwnProperty.call(source, key)) {
14
- target[key] = source[key];
15
- }
16
- }
17
- }
18
-
19
- return target;
20
- };
21
-
22
- return _extends.apply(this, arguments);
23
- }
24
-
25
17
  function invariant(cond, message) {
26
18
  if (!cond) throw new Error(message);
27
19
  }
@@ -49,7 +41,20 @@
49
41
  alreadyWarned[key] = true;
50
42
  warning(false, message) ;
51
43
  }
52
- }
44
+ } ///////////////////////////////////////////////////////////////////////////////
45
+ // CONTEXT
46
+ ///////////////////////////////////////////////////////////////////////////////
47
+
48
+ /**
49
+ * A Navigator is a "location changer"; it's how you get to different locations.
50
+ *
51
+ * Every history instance conforms to the Navigator interface, but the
52
+ * distinction is useful primarily when it comes to the low-level <Router> API
53
+ * where both the location and a navigator must be provided separately in order
54
+ * to avoid "tearing" that may occur in a suspense-enabled app if the action
55
+ * and/or location were to be read directly from the history instance.
56
+ */
57
+
53
58
 
54
59
  const NavigationContext = /*#__PURE__*/React.createContext(null);
55
60
 
@@ -65,21 +70,21 @@
65
70
 
66
71
  const RouteContext = /*#__PURE__*/React.createContext({
67
72
  outlet: null,
68
- params: {},
69
- pathname: "/",
70
- route: null
73
+ matches: []
71
74
  });
72
75
 
73
76
  {
74
77
  RouteContext.displayName = "Route";
75
- }
78
+ } ///////////////////////////////////////////////////////////////////////////////
79
+ // COMPONENTS
80
+ ///////////////////////////////////////////////////////////////////////////////
81
+
82
+
76
83
  /**
77
84
  * A <Router> that stores all entries in memory.
78
85
  *
79
86
  * @see https://reactrouter.com/api/MemoryRouter
80
87
  */
81
-
82
-
83
88
  function MemoryRouter(_ref) {
84
89
  let {
85
90
  basename,
@@ -105,11 +110,12 @@
105
110
  return /*#__PURE__*/React.createElement(Router, {
106
111
  basename: basename,
107
112
  children: children,
108
- action: state.action,
109
113
  location: state.location,
114
+ navigationType: state.action,
110
115
  navigator: history$1
111
116
  });
112
117
  }
118
+
113
119
  /**
114
120
  * Changes the current location.
115
121
  *
@@ -119,7 +125,6 @@
119
125
  *
120
126
  * @see https://reactrouter.com/api/Navigate
121
127
  */
122
-
123
128
  function Navigate(_ref2) {
124
129
  let {
125
130
  to,
@@ -139,24 +144,25 @@
139
144
  });
140
145
  return null;
141
146
  }
147
+
142
148
  /**
143
149
  * Renders the child route's element, if there is one.
144
150
  *
145
151
  * @see https://reactrouter.com/api/Outlet
146
152
  */
147
-
148
153
  function Outlet(_props) {
149
154
  return useOutlet();
150
155
  }
156
+
151
157
  /**
152
158
  * Declares an element that should be rendered at a certain URL path.
153
159
  *
154
160
  * @see https://reactrouter.com/api/Route
155
161
  */
156
-
157
162
  function Route(_props) {
158
163
  invariant(false, "A <Route> is only ever to be used as the child of <Routes> element, " + "never rendered directly. Please wrap your <Route> in a <Routes>.") ;
159
164
  }
165
+
160
166
  /**
161
167
  * Provides location context for the rest of the app.
162
168
  *
@@ -166,13 +172,12 @@
166
172
  *
167
173
  * @see https://reactrouter.com/api/Router
168
174
  */
169
-
170
175
  function Router(_ref3) {
171
176
  let {
172
- action = history.Action.Pop,
173
177
  basename: basenameProp = "/",
174
178
  children = null,
175
179
  location: locationProp,
180
+ navigationType = history.Action.Pop,
176
181
  navigator,
177
182
  static: staticProp = false
178
183
  } = _ref3;
@@ -221,18 +226,18 @@
221
226
  }, /*#__PURE__*/React.createElement(LocationContext.Provider, {
222
227
  children: children,
223
228
  value: {
224
- action,
225
- location
229
+ location,
230
+ navigationType
226
231
  }
227
232
  }));
228
233
  }
234
+
229
235
  /**
230
236
  * A container for a nested tree of <Route> elements that renders the branch
231
237
  * that best matches the current location.
232
238
  *
233
239
  * @see https://reactrouter.com/api/Routes
234
240
  */
235
-
236
241
  function Routes(_ref4) {
237
242
  let {
238
243
  children,
@@ -243,43 +248,6 @@
243
248
  // HOOKS
244
249
  ///////////////////////////////////////////////////////////////////////////////
245
250
 
246
- /**
247
- * Blocks all navigation attempts. This is useful for preventing the page from
248
- * changing until some condition is met, like saving form data.
249
- *
250
- * @see https://reactrouter.com/api/useBlocker
251
- */
252
-
253
- function useBlocker(blocker, when) {
254
- if (when === void 0) {
255
- when = true;
256
- }
257
-
258
- !useInRouterContext() ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of the
259
- // router loaded. We can help them understand how to avoid that.
260
- "useBlocker() may be used only in the context of a <Router> component.") : void 0;
261
- let {
262
- navigator
263
- } = React.useContext(NavigationContext);
264
- React.useEffect(() => {
265
- if (!when) return;
266
- let unblock = navigator.block(tx => {
267
- let autoUnblockingTx = _extends({}, tx, {
268
- retry() {
269
- // Automatically unblock the transition so it can play all the way
270
- // through before retrying it. TODO: Figure out how to re-enable
271
- // this block if the transition is cancelled for some reason.
272
- unblock();
273
- tx.retry();
274
- }
275
-
276
- });
277
-
278
- blocker(autoUnblockingTx);
279
- });
280
- return unblock;
281
- }, [navigator, blocker, when]);
282
- }
283
251
  /**
284
252
  * Returns the full href for the given "to" value. This is useful for building
285
253
  * custom links that are also accessible and preserve right-click behavior.
@@ -295,15 +263,24 @@
295
263
  basename,
296
264
  navigator
297
265
  } = React.useContext(NavigationContext);
298
- let path = useResolvedPath(to);
266
+ let {
267
+ hash,
268
+ pathname,
269
+ search
270
+ } = useResolvedPath(to);
271
+ let joinedPathname = pathname;
299
272
 
300
273
  if (basename !== "/") {
301
274
  let toPathname = getToPathname(to);
302
275
  let endsWithSlash = toPathname != null && toPathname.endsWith("/");
303
- path.pathname = path.pathname === "/" ? basename + (endsWithSlash ? "/" : "") : joinPaths([basename, path.pathname]);
276
+ joinedPathname = pathname === "/" ? basename + (endsWithSlash ? "/" : "") : joinPaths([basename, pathname]);
304
277
  }
305
278
 
306
- return navigator.createHref(path);
279
+ return navigator.createHref({
280
+ pathname: joinedPathname,
281
+ search,
282
+ hash
283
+ });
307
284
  }
308
285
  /**
309
286
  * Returns true if this component is a descendant of a <Router>.
@@ -331,6 +308,16 @@
331
308
  "useLocation() may be used only in the context of a <Router> component.") : void 0;
332
309
  return React.useContext(LocationContext).location;
333
310
  }
311
+ /**
312
+ * Returns the current navigation action which describes how the router came to
313
+ * the current location, either by a pop, push, or replace on the history stack.
314
+ *
315
+ * @see https://reactrouter.com/api/useNavigationType
316
+ */
317
+
318
+ function useNavigationType() {
319
+ return React.useContext(LocationContext).navigationType;
320
+ }
334
321
  /**
335
322
  * Returns true if the URL for the given "to" value matches the current URL.
336
323
  * This is useful for components that need to know "active" state, e.g.
@@ -345,13 +332,16 @@
345
332
  "useMatch() may be used only in the context of a <Router> component.") : void 0;
346
333
  return matchPath(pattern, useLocation().pathname);
347
334
  }
335
+ /**
336
+ * The interface for the navigate() function returned from useNavigate().
337
+ */
338
+
348
339
  /**
349
340
  * Returns an imperative method for changing the location. Used by <Link>s, but
350
341
  * may also be used by other elements to change the location.
351
342
  *
352
343
  * @see https://reactrouter.com/api/useNavigate
353
344
  */
354
-
355
345
  function useNavigate() {
356
346
  !useInRouterContext() ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of the
357
347
  // router loaded. We can help them understand how to avoid that.
@@ -361,11 +351,12 @@
361
351
  navigator
362
352
  } = React.useContext(NavigationContext);
363
353
  let {
364
- pathname: routePathname
354
+ matches
365
355
  } = React.useContext(RouteContext);
366
356
  let {
367
357
  pathname: locationPathname
368
358
  } = useLocation();
359
+ let routePathnamesJson = JSON.stringify(matches.map(match => match.pathnameBase));
369
360
  let activeRef = React.useRef(false);
370
361
  React.useEffect(() => {
371
362
  activeRef.current = true;
@@ -383,14 +374,14 @@
383
374
  return;
384
375
  }
385
376
 
386
- let path = resolveTo(to, routePathname, locationPathname);
377
+ let path = resolveTo(to, JSON.parse(routePathnamesJson), locationPathname);
387
378
 
388
379
  if (basename !== "/") {
389
380
  path.pathname = joinPaths([basename, path.pathname]);
390
381
  }
391
382
 
392
383
  (!!options.replace ? navigator.replace : navigator.push)(path, options.state);
393
- }, [basename, navigator, routePathname, locationPathname]);
384
+ }, [basename, navigator, routePathnamesJson, locationPathname]);
394
385
  return navigate;
395
386
  }
396
387
  /**
@@ -411,7 +402,11 @@
411
402
  */
412
403
 
413
404
  function useParams() {
414
- return React.useContext(RouteContext).params;
405
+ let {
406
+ matches
407
+ } = React.useContext(RouteContext);
408
+ let routeMatch = matches[matches.length - 1];
409
+ return routeMatch ? routeMatch.params : {};
415
410
  }
416
411
  /**
417
412
  * Resolves the pathname of the given `to` value against the current location.
@@ -420,13 +415,14 @@
420
415
  */
421
416
 
422
417
  function useResolvedPath(to) {
418
+ let {
419
+ matches
420
+ } = React.useContext(RouteContext);
423
421
  let {
424
422
  pathname: locationPathname
425
423
  } = useLocation();
426
- let {
427
- pathname: routePathname
428
- } = React.useContext(RouteContext);
429
- return React.useMemo(() => resolveTo(to, routePathname, locationPathname), [to, routePathname, locationPathname]);
424
+ let routePathnamesJson = JSON.stringify(matches.map(match => match.pathnameBase));
425
+ return React.useMemo(() => resolveTo(to, JSON.parse(routePathnamesJson), locationPathname), [to, routePathnamesJson, locationPathname]);
430
426
  }
431
427
  /**
432
428
  * Returns the element of the route that matched the current location, prepared
@@ -442,10 +438,13 @@
442
438
  // router loaded. We can help them understand how to avoid that.
443
439
  "useRoutes() may be used only in the context of a <Router> component.") : void 0;
444
440
  let {
445
- params: parentParams,
446
- pathname: parentPathname,
447
- route: parentRoute
441
+ matches: parentMatches
448
442
  } = React.useContext(RouteContext);
443
+ let routeMatch = parentMatches[parentMatches.length - 1];
444
+ let parentParams = routeMatch ? routeMatch.params : {};
445
+ let parentPathname = routeMatch ? routeMatch.pathname : "/";
446
+ let parentPathnameBase = routeMatch ? routeMatch.pathnameBase : "/";
447
+ let parentRoute = routeMatch && routeMatch.route;
449
448
 
450
449
  {
451
450
  // You won't get a warning about 2 different <Routes> under a <Route>
@@ -473,21 +472,34 @@
473
472
  }
474
473
 
475
474
  let locationFromContext = useLocation();
476
- let location = locationArg ? typeof locationArg === "string" ? history.parsePath(locationArg) : locationArg : locationFromContext;
475
+ let location;
476
+
477
+ if (locationArg) {
478
+ var _parsedLocationArg$pa;
479
+
480
+ let parsedLocationArg = typeof locationArg === "string" ? history.parsePath(locationArg) : locationArg;
481
+ !(parentPathnameBase === "/" || ((_parsedLocationArg$pa = parsedLocationArg.pathname) == null ? void 0 : _parsedLocationArg$pa.startsWith(parentPathnameBase))) ? invariant(false, "When overriding the location using `<Routes location>` or `useRoutes(routes, location)`, " + "the location pathname must begin with the portion of the URL pathname that was " + ("matched by all parent routes. The current pathname base is \"" + parentPathnameBase + "\" ") + ("but pathname \"" + parsedLocationArg.pathname + "\" was given in the `location` prop.")) : void 0;
482
+ location = parsedLocationArg;
483
+ } else {
484
+ location = locationFromContext;
485
+ }
486
+
477
487
  let pathname = location.pathname || "/";
478
- let parentPathnameStart = getPathnameStart(parentPathname, parentParams);
479
- let remainingPathname = parentPathnameStart === "/" ? pathname : pathname.slice(parentPathnameStart.length);
488
+ let remainingPathname = parentPathnameBase === "/" ? pathname : pathname.slice(parentPathnameBase.length) || "/";
480
489
  let matches = matchRoutes(routes, {
481
490
  pathname: remainingPathname
482
491
  });
483
492
 
484
493
  {
485
494
  warning(parentRoute || matches != null, "No routes matched location \"" + location.pathname + location.search + location.hash + "\" ") ;
495
+ warning(matches == null || matches[matches.length - 1].route.element !== undefined, "Matched leaf route at location \"" + location.pathname + location.search + location.hash + "\" does not have an element. " + "This means it will render an <Outlet /> with a null value by default resulting in an \"empty\" page.") ;
486
496
  }
487
497
 
488
- return renderMatches(matches && matches.map(match => Object.assign({}, match, {
489
- pathname: joinPaths([parentPathnameStart, match.pathname])
490
- })));
498
+ return _renderMatches(matches && matches.map(match => Object.assign({}, match, {
499
+ params: Object.assign({}, parentParams, match.params),
500
+ pathname: joinPaths([parentPathnameBase, match.pathname]),
501
+ pathnameBase: match.pathnameBase === "/" ? parentPathnameBase : joinPaths([parentPathnameBase, match.pathnameBase])
502
+ })), parentMatches);
491
503
  } ///////////////////////////////////////////////////////////////////////////////
492
504
  // UTILS
493
505
  ///////////////////////////////////////////////////////////////////////////////
@@ -516,10 +528,10 @@
516
528
  }
517
529
 
518
530
  let route = {
519
- path: element.props.path,
520
531
  caseSensitive: element.props.caseSensitive,
532
+ element: element.props.element,
521
533
  index: element.props.index,
522
- element: element.props.element
534
+ path: element.props.path
523
535
  };
524
536
 
525
537
  if (element.props.children) {
@@ -530,12 +542,15 @@
530
542
  });
531
543
  return routes;
532
544
  }
545
+ /**
546
+ * The parameters that were parsed from the URL path.
547
+ */
548
+
533
549
  /**
534
550
  * Returns a path with params interpolated.
535
551
  *
536
552
  * @see https://reactrouter.com/api/generatePath
537
553
  */
538
-
539
554
  function generatePath(path, params) {
540
555
  if (params === void 0) {
541
556
  params = {};
@@ -546,12 +561,15 @@
546
561
  return params[key];
547
562
  }).replace(/\/*\*$/, _ => params["*"] == null ? "" : params["*"].replace(/^\/*/, "/"));
548
563
  }
564
+ /**
565
+ * A RouteMatch contains info about how a route matched a URL.
566
+ */
567
+
549
568
  /**
550
569
  * Matches the given routes to a location and returns the match data.
551
570
  *
552
571
  * @see https://reactrouter.com/api/matchRoutes
553
572
  */
554
-
555
573
  function matchRoutes(routes, locationArg, basename) {
556
574
  if (basename === void 0) {
557
575
  basename = "/";
@@ -608,11 +626,17 @@
608
626
  if (route.children && route.children.length > 0) {
609
627
  !(route.index !== true) ? invariant(false, "Index routes must not have child routes. Please remove " + ("all child routes from route path \"" + path + "\".")) : void 0;
610
628
  flattenRoutes(route.children, branches, routesMeta, path);
629
+ } // Routes without a path shouldn't ever match by themselves unless they are
630
+ // index routes, so don't add them to the list of possible branches.
631
+
632
+
633
+ if (route.path == null && !route.index) {
634
+ return;
611
635
  }
612
636
 
613
637
  branches.push({
614
638
  path,
615
- score: computeScore(path),
639
+ score: computeScore(path, route.index),
616
640
  routesMeta
617
641
  });
618
642
  });
@@ -625,14 +649,15 @@
625
649
  }
626
650
 
627
651
  const paramRe = /^:\w+$/;
628
- const dynamicSegmentValue = 2;
652
+ const dynamicSegmentValue = 3;
653
+ const indexRouteValue = 2;
629
654
  const emptySegmentValue = 1;
630
655
  const staticSegmentValue = 10;
631
656
  const splatPenalty = -2;
632
657
 
633
658
  const isSplat = s => s === "*";
634
659
 
635
- function computeScore(path) {
660
+ function computeScore(path, index) {
636
661
  let segments = path.split("/");
637
662
  let initialScore = segments.length;
638
663
 
@@ -640,6 +665,10 @@
640
665
  initialScore += splatPenalty;
641
666
  }
642
667
 
668
+ if (index) {
669
+ initialScore += indexRouteValue;
670
+ }
671
+
643
672
  return segments.filter(s => !isSplat(s)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue), initialScore);
644
673
  }
645
674
 
@@ -654,7 +683,8 @@
654
683
  0;
655
684
  }
656
685
 
657
- function matchRouteBranch(branch, routesArg, pathname) {
686
+ function matchRouteBranch(branch, // TODO: attach original route object inside routesMeta so we don't need this arg
687
+ routesArg, pathname) {
658
688
  let routes = routesArg;
659
689
  let {
660
690
  routesMeta
@@ -665,27 +695,25 @@
665
695
 
666
696
  for (let i = 0; i < routesMeta.length; ++i) {
667
697
  let meta = routesMeta[i];
668
- let trailingPathname = matchedPathname === "/" ? pathname : pathname.slice(matchedPathname.length) || "/";
698
+ let end = i === routesMeta.length - 1;
699
+ let remainingPathname = matchedPathname === "/" ? pathname : pathname.slice(matchedPathname.length) || "/";
669
700
  let match = matchPath({
670
701
  path: meta.relativePath,
671
702
  caseSensitive: meta.caseSensitive,
672
- end: i === routesMeta.length - 1
673
- }, trailingPathname);
703
+ end
704
+ }, remainingPathname);
674
705
  if (!match) return null;
675
706
  Object.assign(matchedParams, match.params);
676
707
  let route = routes[meta.childrenIndex];
677
708
  matches.push({
678
709
  params: matchedParams,
679
- pathname: match.pathname === "/" ? matchedPathname : joinPaths([matchedPathname, match.pathname]),
710
+ pathname: joinPaths([matchedPathname, match.pathname]),
711
+ pathnameBase: joinPaths([matchedPathname, match.pathnameBase]),
680
712
  route
681
713
  });
682
- let pathnameStart = getPathnameStart(match.pathname, match.params);
683
714
 
684
- if (pathnameStart !== "/") {
685
- // Add only the portion of the match.pathname that comes before the * to
686
- // the matchedPathname. This allows child routes to match against the
687
- // portion of the pathname that was matched by the *.
688
- matchedPathname = joinPaths([matchedPathname, pathnameStart]);
715
+ if (match.pathnameBase !== "/") {
716
+ matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);
689
717
  }
690
718
 
691
719
  routes = route.children;
@@ -699,26 +727,36 @@
699
727
 
700
728
 
701
729
  function renderMatches(matches) {
730
+ return _renderMatches(matches);
731
+ }
732
+
733
+ function _renderMatches(matches, parentMatches) {
734
+ if (parentMatches === void 0) {
735
+ parentMatches = [];
736
+ }
737
+
702
738
  if (matches == null) return null;
703
- return matches.reduceRight((outlet, match) => {
739
+ return matches.reduceRight((outlet, match, index) => {
704
740
  return /*#__PURE__*/React.createElement(RouteContext.Provider, {
705
- children: match.route.element || /*#__PURE__*/React.createElement(Outlet, null),
741
+ children: match.route.element !== undefined ? match.route.element : /*#__PURE__*/React.createElement(Outlet, null),
706
742
  value: {
707
743
  outlet,
708
- params: match.params,
709
- pathname: match.pathname,
710
- route: match.route
744
+ matches: parentMatches.concat(matches.slice(0, index + 1))
711
745
  }
712
746
  });
713
747
  }, null);
714
748
  }
749
+ /**
750
+ * A PathPattern is used to match on some portion of a URL pathname.
751
+ */
752
+
753
+
715
754
  /**
716
755
  * Performs pattern matching on a URL pathname and returns information about
717
756
  * the match.
718
757
  *
719
758
  * @see https://reactrouter.com/api/matchPath
720
759
  */
721
-
722
760
  function matchPath(pattern, pathname) {
723
761
  if (typeof pattern === "string") {
724
762
  pattern = {
@@ -732,14 +770,23 @@
732
770
  let match = pathname.match(matcher);
733
771
  if (!match) return null;
734
772
  let matchedPathname = match[0];
735
- let values = match.slice(1);
773
+ let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
774
+ let captureGroups = match.slice(1);
736
775
  let params = paramNames.reduce((memo, paramName, index) => {
737
- memo[paramName] = safelyDecodeURIComponent(values[index] || "", paramName);
776
+ // We need to compute the pathnameBase here using the raw splat value
777
+ // instead of using params["*"] later because it will be decoded then
778
+ if (paramName === "*") {
779
+ let splatValue = captureGroups[index] || "";
780
+ pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
781
+ }
782
+
783
+ memo[paramName] = safelyDecodeURIComponent(captureGroups[index] || "", paramName);
738
784
  return memo;
739
785
  }, {});
740
786
  return {
741
787
  params,
742
788
  pathname: matchedPathname,
789
+ pathnameBase,
743
790
  pattern
744
791
  };
745
792
  }
@@ -753,35 +800,30 @@
753
800
  end = true;
754
801
  }
755
802
 
756
- let keys = [];
757
- let source = "^" + path.replace(/\/?\*?$/, "") // Ignore trailing / and /*, we'll handle it below
803
+ warning(path === "*" || !path.endsWith("*") || path.endsWith("/*"), "Route path \"" + path + "\" will be treated as if it were " + ("\"" + path.replace(/\*$/, "/*") + "\" because the `*` character must ") + "always follow a `/` in the pattern. To get rid of this warning, " + ("please change the route path to \"" + path.replace(/\*$/, "/*") + "\".")) ;
804
+ let paramNames = [];
805
+ let regexpSource = "^" + path.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below
758
806
  .replace(/^\/*/, "/") // Make sure it has a leading /
759
807
  .replace(/[\\.*+^$?{}|()[\]]/g, "\\$&") // Escape special regex chars
760
- .replace(/:(\w+)/g, (_, key) => {
761
- keys.push(key);
808
+ .replace(/:(\w+)/g, (_, paramName) => {
809
+ paramNames.push(paramName);
762
810
  return "([^\\/]+)";
763
811
  });
764
812
 
765
813
  if (path.endsWith("*")) {
766
- if (path.endsWith("/*")) {
767
- source += "(?:\\/(.+)|\\/?)$"; // Don't include the / in params['*']
768
- } else {
769
- source += "(.*)$";
770
- }
771
-
772
- keys.push("*");
773
- } else if (end) {
774
- // When matching to the end, ignore trailing slashes.
775
- source += "\\/?$";
814
+ paramNames.push("*");
815
+ regexpSource += path === "*" || path === "/*" ? "(.*)$" // Already matched the initial /, just match the rest
816
+ : "(?:\\/(.+)|\\/*)$"; // Don't include the / in params["*"]
776
817
  } else {
777
- // If not matching to the end (as parent routes do), at least match a word
778
- // boundary. This restricts a parent route to matching only its own words
779
- // and nothing more, e.g. parent route "/home" should not match "/home2".
780
- source += "(?:\\b|$)";
818
+ regexpSource += end ? "\\/*$" // When matching to the end, ignore trailing slashes
819
+ : // Otherwise, at least match a word boundary. This restricts parent
820
+ // routes to matching only their own words and nothing more, e.g. parent
821
+ // route "/home" should not match "/home2".
822
+ "(?:\\b|$)";
781
823
  }
782
824
 
783
- let matcher = new RegExp(source, caseSensitive ? undefined : "i");
784
- return [matcher, keys];
825
+ let matcher = new RegExp(regexpSource, caseSensitive ? undefined : "i");
826
+ return [matcher, paramNames];
785
827
  }
786
828
 
787
829
  function safelyDecodeURIComponent(value, paramName) {
@@ -831,17 +873,48 @@
831
873
  return segments.length > 1 ? segments.join("/") : "/";
832
874
  }
833
875
 
834
- function resolveTo(to, routePathname, locationPathname) {
835
- return resolvePath(to, // If a pathname is explicitly provided in `to`, it should be
836
- // relative to the route context. This is explained in `Note on
837
- // `<Link to>` values` in our migration guide from v5 as a means of
838
- // disambiguation between `to` values that begin with `/` and those
839
- // that do not. However, this is problematic for `to` values that do
840
- // not provide a pathname. `to` can simply be a search or hash
841
- // string, in which case we should assume that the navigation is
842
- // relative to the current location's pathname and *not* the
843
- // route pathname.
844
- getToPathname(to) == null ? locationPathname : routePathname);
876
+ function resolveTo(toArg, routePathnames, locationPathname) {
877
+ let to = typeof toArg === "string" ? history.parsePath(toArg) : toArg;
878
+ let toPathname = toArg === "" || to.pathname === "" ? "/" : to.pathname; // If a pathname is explicitly provided in `to`, it should be relative to the
879
+ // route context. This is explained in `Note on `<Link to>` values` in our
880
+ // migration guide from v5 as a means of disambiguation between `to` values
881
+ // that begin with `/` and those that do not. However, this is problematic for
882
+ // `to` values that do not provide a pathname. `to` can simply be a search or
883
+ // hash string, in which case we should assume that the navigation is relative
884
+ // to the current location's pathname and *not* the route pathname.
885
+
886
+ let from;
887
+
888
+ if (toPathname == null) {
889
+ from = locationPathname;
890
+ } else {
891
+ let routePathnameIndex = routePathnames.length - 1;
892
+
893
+ if (toPathname.startsWith("..")) {
894
+ let toSegments = toPathname.split("/"); // Each leading .. segment means "go up one route" instead of "go up one
895
+ // URL segment". This is a key difference from how <a href> works and a
896
+ // major reason we call this a "to" value instead of a "href".
897
+
898
+ while (toSegments[0] === "..") {
899
+ toSegments.shift();
900
+ routePathnameIndex -= 1;
901
+ }
902
+
903
+ to.pathname = toSegments.join("/");
904
+ } // If there are more ".." segments than parent routes, resolve relative to
905
+ // the root / URL.
906
+
907
+
908
+ from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : "/";
909
+ }
910
+
911
+ let path = resolvePath(to, from); // Ensure the pathname has a trailing slash if the original to value had one.
912
+
913
+ if (toPathname && toPathname !== "/" && toPathname.endsWith("/") && !path.pathname.endsWith("/")) {
914
+ path.pathname += "/";
915
+ }
916
+
917
+ return path;
845
918
  }
846
919
 
847
920
  function getToPathname(to) {
@@ -849,16 +922,6 @@
849
922
  return to === "" || to.pathname === "" ? "/" : typeof to === "string" ? history.parsePath(to).pathname : to.pathname;
850
923
  }
851
924
 
852
- function getPathnameStart(pathname, params) {
853
- let splat = params["*"];
854
- if (!splat) return pathname;
855
- let pathnameStart = pathname.slice(0, -splat.length);
856
- if (splat.startsWith("/")) return pathnameStart;
857
- let index = pathnameStart.lastIndexOf("/");
858
- if (index > 0) return pathnameStart.slice(0, index);
859
- return "/";
860
- }
861
-
862
925
  function stripBasename(pathname, basename) {
863
926
  if (basename === "/") return pathname;
864
927
 
@@ -899,12 +962,12 @@
899
962
  exports.matchRoutes = matchRoutes;
900
963
  exports.renderMatches = renderMatches;
901
964
  exports.resolvePath = resolvePath;
902
- exports.useBlocker = useBlocker;
903
965
  exports.useHref = useHref;
904
966
  exports.useInRouterContext = useInRouterContext;
905
967
  exports.useLocation = useLocation;
906
968
  exports.useMatch = useMatch;
907
969
  exports.useNavigate = useNavigate;
970
+ exports.useNavigationType = useNavigationType;
908
971
  exports.useOutlet = useOutlet;
909
972
  exports.useParams = useParams;
910
973
  exports.useResolvedPath = useResolvedPath;