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