UNPKG

34.3 kBJavaScriptView Raw
1/**
2 * React Router v6.2.1
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 */
11import { createContext, useRef, useState, useLayoutEffect, createElement, useContext, useEffect, useMemo, useCallback, Children, isValidElement, Fragment } from 'react';
12import { createMemoryHistory, Action, parsePath } from 'history';
13
14function invariant(cond, message) {
15 if (!cond) throw new Error(message);
16}
17
18function warning(cond, message) {
19 if (!cond) {
20 // eslint-disable-next-line no-console
21 if (typeof console !== "undefined") console.warn(message);
22
23 try {
24 // Welcome to debugging React Router!
25 //
26 // This error is thrown as a convenience so you can more easily
27 // find the source for a warning that appears in the console by
28 // enabling "pause on exceptions" in your JavaScript debugger.
29 throw new Error(message); // eslint-disable-next-line no-empty
30 } catch (e) {}
31 }
32}
33
34const alreadyWarned = {};
35
36function warningOnce(key, cond, message) {
37 if (!cond && !alreadyWarned[key]) {
38 alreadyWarned[key] = true;
39 process.env.NODE_ENV !== "production" ? warning(false, message) : void 0;
40 }
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
55
56const NavigationContext = /*#__PURE__*/createContext(null);
57
58if (process.env.NODE_ENV !== "production") {
59 NavigationContext.displayName = "Navigation";
60}
61
62const LocationContext = /*#__PURE__*/createContext(null);
63
64if (process.env.NODE_ENV !== "production") {
65 LocationContext.displayName = "Location";
66}
67
68const RouteContext = /*#__PURE__*/createContext({
69 outlet: null,
70 matches: []
71});
72
73if (process.env.NODE_ENV !== "production") {
74 RouteContext.displayName = "Route";
75} ///////////////////////////////////////////////////////////////////////////////
76// COMPONENTS
77///////////////////////////////////////////////////////////////////////////////
78
79
80/**
81 * A <Router> that stores all entries in memory.
82 *
83 * @see https://reactrouter.com/docs/en/v6/api#memoryrouter
84 */
85function MemoryRouter(_ref) {
86 let {
87 basename,
88 children,
89 initialEntries,
90 initialIndex
91 } = _ref;
92 let historyRef = useRef();
93
94 if (historyRef.current == null) {
95 historyRef.current = createMemoryHistory({
96 initialEntries,
97 initialIndex
98 });
99 }
100
101 let history = historyRef.current;
102 let [state, setState] = useState({
103 action: history.action,
104 location: history.location
105 });
106 useLayoutEffect(() => history.listen(setState), [history]);
107 return /*#__PURE__*/createElement(Router, {
108 basename: basename,
109 children: children,
110 location: state.location,
111 navigationType: state.action,
112 navigator: history
113 });
114}
115
116/**
117 * Changes the current location.
118 *
119 * Note: This API is mostly useful in React.Component subclasses that are not
120 * able to use hooks. In functional components, we recommend you use the
121 * `useNavigate` hook instead.
122 *
123 * @see https://reactrouter.com/docs/en/v6/api#navigate
124 */
125function Navigate(_ref2) {
126 let {
127 to,
128 replace,
129 state
130 } = _ref2;
131 !useInRouterContext() ? process.env.NODE_ENV !== "production" ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of
132 // the router loaded. We can help them understand how to avoid that.
133 "<Navigate> may be used only in the context of a <Router> component.") : invariant(false) : void 0;
134 process.env.NODE_ENV !== "production" ? warning(!useContext(NavigationContext).static, "<Navigate> must not be used on the initial render in a <StaticRouter>. " + "This is a no-op, but you should modify your code so the <Navigate> is " + "only ever rendered in response to some user interaction or state change.") : void 0;
135 let navigate = useNavigate();
136 useEffect(() => {
137 navigate(to, {
138 replace,
139 state
140 });
141 });
142 return null;
143}
144
145/**
146 * Renders the child route's element, if there is one.
147 *
148 * @see https://reactrouter.com/docs/en/v6/api#outlet
149 */
150function Outlet(props) {
151 return useOutlet(props.context);
152}
153
154/**
155 * Declares an element that should be rendered at a certain URL path.
156 *
157 * @see https://reactrouter.com/docs/en/v6/api#route
158 */
159function Route(_props) {
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) ;
161}
162
163/**
164 * Provides location context for the rest of the app.
165 *
166 * Note: You usually won't render a <Router> directly. Instead, you'll render a
167 * router that is more specific to your environment such as a <BrowserRouter>
168 * in web browsers or a <StaticRouter> for server rendering.
169 *
170 * @see https://reactrouter.com/docs/en/v6/api#router
171 */
172function Router(_ref3) {
173 let {
174 basename: basenameProp = "/",
175 children = null,
176 location: locationProp,
177 navigationType = Action.Pop,
178 navigator,
179 static: staticProp = false
180 } = _ref3;
181 !!useInRouterContext() ? process.env.NODE_ENV !== "production" ? invariant(false, "You cannot render a <Router> inside another <Router>." + " You should never have more than one in your app.") : invariant(false) : void 0;
182 let basename = normalizePathname(basenameProp);
183 let navigationContext = useMemo(() => ({
184 basename,
185 navigator,
186 static: staticProp
187 }), [basename, navigator, staticProp]);
188
189 if (typeof locationProp === "string") {
190 locationProp = parsePath(locationProp);
191 }
192
193 let {
194 pathname = "/",
195 search = "",
196 hash = "",
197 state = null,
198 key = "default"
199 } = locationProp;
200 let location = useMemo(() => {
201 let trailingPathname = stripBasename(pathname, basename);
202
203 if (trailingPathname == null) {
204 return null;
205 }
206
207 return {
208 pathname: trailingPathname,
209 search,
210 hash,
211 state,
212 key
213 };
214 }, [basename, pathname, search, hash, state, key]);
215 process.env.NODE_ENV !== "production" ? warning(location != null, "<Router basename=\"" + basename + "\"> is not able to match the URL " + ("\"" + pathname + search + hash + "\" because it does not start with the ") + "basename, so the <Router> won't render anything.") : void 0;
216
217 if (location == null) {
218 return null;
219 }
220
221 return /*#__PURE__*/createElement(NavigationContext.Provider, {
222 value: navigationContext
223 }, /*#__PURE__*/createElement(LocationContext.Provider, {
224 children: children,
225 value: {
226 location,
227 navigationType
228 }
229 }));
230}
231
232/**
233 * A container for a nested tree of <Route> elements that renders the branch
234 * that best matches the current location.
235 *
236 * @see https://reactrouter.com/docs/en/v6/api#routes
237 */
238function Routes(_ref4) {
239 let {
240 children,
241 location
242 } = _ref4;
243 return useRoutes(createRoutesFromChildren(children), location);
244} ///////////////////////////////////////////////////////////////////////////////
245// HOOKS
246///////////////////////////////////////////////////////////////////////////////
247
248/**
249 * Returns the full href for the given "to" value. This is useful for building
250 * custom links that are also accessible and preserve right-click behavior.
251 *
252 * @see https://reactrouter.com/docs/en/v6/api#usehref
253 */
254
255function useHref(to) {
256 !useInRouterContext() ? process.env.NODE_ENV !== "production" ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of the
257 // router loaded. We can help them understand how to avoid that.
258 "useHref() may be used only in the context of a <Router> component.") : invariant(false) : void 0;
259 let {
260 basename,
261 navigator
262 } = useContext(NavigationContext);
263 let {
264 hash,
265 pathname,
266 search
267 } = useResolvedPath(to);
268 let joinedPathname = pathname;
269
270 if (basename !== "/") {
271 let toPathname = getToPathname(to);
272 let endsWithSlash = toPathname != null && toPathname.endsWith("/");
273 joinedPathname = pathname === "/" ? basename + (endsWithSlash ? "/" : "") : joinPaths([basename, pathname]);
274 }
275
276 return navigator.createHref({
277 pathname: joinedPathname,
278 search,
279 hash
280 });
281}
282/**
283 * Returns true if this component is a descendant of a <Router>.
284 *
285 * @see https://reactrouter.com/docs/en/v6/api#useinroutercontext
286 */
287
288function useInRouterContext() {
289 return useContext(LocationContext) != null;
290}
291/**
292 * Returns the current location object, which represents the current URL in web
293 * browsers.
294 *
295 * Note: If you're using this it may mean you're doing some of your own
296 * "routing" in your app, and we'd like to know what your use case is. We may
297 * be able to provide something higher-level to better suit your needs.
298 *
299 * @see https://reactrouter.com/docs/en/v6/api#uselocation
300 */
301
302function useLocation() {
303 !useInRouterContext() ? process.env.NODE_ENV !== "production" ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of the
304 // router loaded. We can help them understand how to avoid that.
305 "useLocation() may be used only in the context of a <Router> component.") : invariant(false) : void 0;
306 return useContext(LocationContext).location;
307}
308
309/**
310 * Returns the current navigation action which describes how the router came to
311 * the current location, either by a pop, push, or replace on the history stack.
312 *
313 * @see https://reactrouter.com/docs/en/v6/api#usenavigationtype
314 */
315function useNavigationType() {
316 return useContext(LocationContext).navigationType;
317}
318/**
319 * Returns true if the URL for the given "to" value matches the current URL.
320 * This is useful for components that need to know "active" state, e.g.
321 * <NavLink>.
322 *
323 * @see https://reactrouter.com/docs/en/v6/api#usematch
324 */
325
326function useMatch(pattern) {
327 !useInRouterContext() ? process.env.NODE_ENV !== "production" ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of the
328 // router loaded. We can help them understand how to avoid that.
329 "useMatch() may be used only in the context of a <Router> component.") : invariant(false) : void 0;
330 let {
331 pathname
332 } = useLocation();
333 return useMemo(() => matchPath(pattern, pathname), [pathname, pattern]);
334}
335/**
336 * The interface for the navigate() function returned from useNavigate().
337 */
338
339/**
340 * Returns an imperative method for changing the location. Used by <Link>s, but
341 * may also be used by other elements to change the location.
342 *
343 * @see https://reactrouter.com/docs/en/v6/api#usenavigate
344 */
345function useNavigate() {
346 !useInRouterContext() ? process.env.NODE_ENV !== "production" ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of the
347 // router loaded. We can help them understand how to avoid that.
348 "useNavigate() may be used only in the context of a <Router> component.") : invariant(false) : void 0;
349 let {
350 basename,
351 navigator
352 } = useContext(NavigationContext);
353 let {
354 matches
355 } = useContext(RouteContext);
356 let {
357 pathname: locationPathname
358 } = useLocation();
359 let routePathnamesJson = JSON.stringify(matches.map(match => match.pathnameBase));
360 let activeRef = useRef(false);
361 useEffect(() => {
362 activeRef.current = true;
363 });
364 let navigate = useCallback(function (to, options) {
365 if (options === void 0) {
366 options = {};
367 }
368
369 process.env.NODE_ENV !== "production" ? warning(activeRef.current, "You should call navigate() in a React.useEffect(), not when " + "your component is first rendered.") : void 0;
370 if (!activeRef.current) return;
371
372 if (typeof to === "number") {
373 navigator.go(to);
374 return;
375 }
376
377 let path = resolveTo(to, JSON.parse(routePathnamesJson), locationPathname);
378
379 if (basename !== "/") {
380 path.pathname = joinPaths([basename, path.pathname]);
381 }
382
383 (!!options.replace ? navigator.replace : navigator.push)(path, options.state);
384 }, [basename, navigator, routePathnamesJson, locationPathname]);
385 return navigate;
386}
387const OutletContext = /*#__PURE__*/createContext(null);
388/**
389 * Returns the context (if provided) for the child route at this level of the route
390 * hierarchy.
391 * @see https://reactrouter.com/docs/en/v6/api#useoutletcontext
392 */
393
394function useOutletContext() {
395 return useContext(OutletContext);
396}
397/**
398 * Returns the element for the child route at this level of the route
399 * hierarchy. Used internally by <Outlet> to render child routes.
400 *
401 * @see https://reactrouter.com/docs/en/v6/api#useoutlet
402 */
403
404function useOutlet(context) {
405 let outlet = useContext(RouteContext).outlet;
406
407 if (outlet) {
408 return /*#__PURE__*/createElement(OutletContext.Provider, {
409 value: context
410 }, outlet);
411 }
412
413 return outlet;
414}
415/**
416 * Returns an object of key/value pairs of the dynamic params from the current
417 * URL that were matched by the route path.
418 *
419 * @see https://reactrouter.com/docs/en/v6/api#useparams
420 */
421
422function useParams() {
423 let {
424 matches
425 } = useContext(RouteContext);
426 let routeMatch = matches[matches.length - 1];
427 return routeMatch ? routeMatch.params : {};
428}
429/**
430 * Resolves the pathname of the given `to` value against the current location.
431 *
432 * @see https://reactrouter.com/docs/en/v6/api#useresolvedpath
433 */
434
435function useResolvedPath(to) {
436 let {
437 matches
438 } = useContext(RouteContext);
439 let {
440 pathname: locationPathname
441 } = useLocation();
442 let routePathnamesJson = JSON.stringify(matches.map(match => match.pathnameBase));
443 return useMemo(() => resolveTo(to, JSON.parse(routePathnamesJson), locationPathname), [to, routePathnamesJson, locationPathname]);
444}
445/**
446 * Returns the element of the route that matched the current location, prepared
447 * with the correct context to render the remainder of the route tree. Route
448 * elements in the tree must render an <Outlet> to render their child route's
449 * element.
450 *
451 * @see https://reactrouter.com/docs/en/v6/api#useroutes
452 */
453
454function useRoutes(routes, locationArg) {
455 !useInRouterContext() ? process.env.NODE_ENV !== "production" ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of the
456 // router loaded. We can help them understand how to avoid that.
457 "useRoutes() may be used only in the context of a <Router> component.") : invariant(false) : void 0;
458 let {
459 matches: parentMatches
460 } = useContext(RouteContext);
461 let routeMatch = parentMatches[parentMatches.length - 1];
462 let parentParams = routeMatch ? routeMatch.params : {};
463 let parentPathname = routeMatch ? routeMatch.pathname : "/";
464 let parentPathnameBase = routeMatch ? routeMatch.pathnameBase : "/";
465 let parentRoute = routeMatch && routeMatch.route;
466
467 if (process.env.NODE_ENV !== "production") {
468 // You won't get a warning about 2 different <Routes> under a <Route>
469 // without a trailing *, but this is a best-effort warning anyway since we
470 // cannot even give the warning unless they land at the parent route.
471 //
472 // Example:
473 //
474 // <Routes>
475 // {/* This route path MUST end with /* because otherwise
476 // it will never match /blog/post/123 */}
477 // <Route path="blog" element={<Blog />} />
478 // <Route path="blog/feed" element={<BlogFeed />} />
479 // </Routes>
480 //
481 // function Blog() {
482 // return (
483 // <Routes>
484 // <Route path="post/:id" element={<Post />} />
485 // </Routes>
486 // );
487 // }
488 let parentPath = parentRoute && parentRoute.path || "";
489 warningOnce(parentPathname, !parentRoute || parentPath.endsWith("*"), "You rendered descendant <Routes> (or called `useRoutes()`) at " + ("\"" + parentPathname + "\" (under <Route path=\"" + parentPath + "\">) but the ") + "parent route path has no trailing \"*\". This means if you navigate " + "deeper, the parent won't match anymore and therefore the child " + "routes will never render.\n\n" + ("Please change the parent <Route path=\"" + parentPath + "\"> to <Route ") + ("path=\"" + (parentPath === "/" ? "*" : parentPath + "/*") + "\">."));
490 }
491
492 let locationFromContext = useLocation();
493 let location;
494
495 if (locationArg) {
496 var _parsedLocationArg$pa;
497
498 let parsedLocationArg = typeof locationArg === "string" ? parsePath(locationArg) : locationArg;
499 !(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;
500 location = parsedLocationArg;
501 } else {
502 location = locationFromContext;
503 }
504
505 let pathname = location.pathname || "/";
506 let remainingPathname = parentPathnameBase === "/" ? pathname : pathname.slice(parentPathnameBase.length) || "/";
507 let matches = matchRoutes(routes, {
508 pathname: remainingPathname
509 });
510
511 if (process.env.NODE_ENV !== "production") {
512 process.env.NODE_ENV !== "production" ? warning(parentRoute || matches != null, "No routes matched location \"" + location.pathname + location.search + location.hash + "\" ") : void 0;
513 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;
514 }
515
516 return _renderMatches(matches && matches.map(match => Object.assign({}, match, {
517 params: Object.assign({}, parentParams, match.params),
518 pathname: joinPaths([parentPathnameBase, match.pathname]),
519 pathnameBase: match.pathnameBase === "/" ? parentPathnameBase : joinPaths([parentPathnameBase, match.pathnameBase])
520 })), parentMatches);
521} ///////////////////////////////////////////////////////////////////////////////
522// UTILS
523///////////////////////////////////////////////////////////////////////////////
524
525/**
526 * Creates a route config from a React "children" object, which is usually
527 * either a `<Route>` element or an array of them. Used internally by
528 * `<Routes>` to create a route config from its children.
529 *
530 * @see https://reactrouter.com/docs/en/v6/api#createroutesfromchildren
531 */
532
533function createRoutesFromChildren(children) {
534 let routes = [];
535 Children.forEach(children, element => {
536 if (! /*#__PURE__*/isValidElement(element)) {
537 // Ignore non-elements. This allows people to more easily inline
538 // conditionals in their route config.
539 return;
540 }
541
542 if (element.type === Fragment) {
543 // Transparently support React.Fragment and its children.
544 routes.push.apply(routes, createRoutesFromChildren(element.props.children));
545 return;
546 }
547
548 !(element.type === Route) ? process.env.NODE_ENV !== "production" ? invariant(false, "[" + (typeof element.type === "string" ? element.type : element.type.name) + "] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>") : invariant(false) : void 0;
549 let route = {
550 caseSensitive: element.props.caseSensitive,
551 element: element.props.element,
552 index: element.props.index,
553 path: element.props.path
554 };
555
556 if (element.props.children) {
557 route.children = createRoutesFromChildren(element.props.children);
558 }
559
560 routes.push(route);
561 });
562 return routes;
563}
564/**
565 * The parameters that were parsed from the URL path.
566 */
567
568/**
569 * Returns a path with params interpolated.
570 *
571 * @see https://reactrouter.com/docs/en/v6/api#generatepath
572 */
573function generatePath(path, params) {
574 if (params === void 0) {
575 params = {};
576 }
577
578 return path.replace(/:(\w+)/g, (_, key) => {
579 !(params[key] != null) ? process.env.NODE_ENV !== "production" ? invariant(false, "Missing \":" + key + "\" param") : invariant(false) : void 0;
580 return params[key];
581 }).replace(/\/*\*$/, _ => params["*"] == null ? "" : params["*"].replace(/^\/*/, "/"));
582}
583/**
584 * A RouteMatch contains info about how a route matched a URL.
585 */
586
587/**
588 * Matches the given routes to a location and returns the match data.
589 *
590 * @see https://reactrouter.com/docs/en/v6/api#matchroutes
591 */
592function matchRoutes(routes, locationArg, basename) {
593 if (basename === void 0) {
594 basename = "/";
595 }
596
597 let location = typeof locationArg === "string" ? parsePath(locationArg) : locationArg;
598 let pathname = stripBasename(location.pathname || "/", basename);
599
600 if (pathname == null) {
601 return null;
602 }
603
604 let branches = flattenRoutes(routes);
605 rankRouteBranches(branches);
606 let matches = null;
607
608 for (let i = 0; matches == null && i < branches.length; ++i) {
609 matches = matchRouteBranch(branches[i], pathname);
610 }
611
612 return matches;
613}
614
615function flattenRoutes(routes, branches, parentsMeta, parentPath) {
616 if (branches === void 0) {
617 branches = [];
618 }
619
620 if (parentsMeta === void 0) {
621 parentsMeta = [];
622 }
623
624 if (parentPath === void 0) {
625 parentPath = "";
626 }
627
628 routes.forEach((route, index) => {
629 let meta = {
630 relativePath: route.path || "",
631 caseSensitive: route.caseSensitive === true,
632 childrenIndex: index,
633 route
634 };
635
636 if (meta.relativePath.startsWith("/")) {
637 !meta.relativePath.startsWith(parentPath) ? process.env.NODE_ENV !== "production" ? invariant(false, "Absolute route path \"" + meta.relativePath + "\" nested under path " + ("\"" + parentPath + "\" is not valid. An absolute child route path ") + "must start with the combined path of all its parent routes.") : invariant(false) : void 0;
638 meta.relativePath = meta.relativePath.slice(parentPath.length);
639 }
640
641 let path = joinPaths([parentPath, meta.relativePath]);
642 let routesMeta = parentsMeta.concat(meta); // Add the children before adding this route to the array so we traverse the
643 // route tree depth-first and child routes appear before their parents in
644 // the "flattened" version.
645
646 if (route.children && route.children.length > 0) {
647 !(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;
648 flattenRoutes(route.children, branches, routesMeta, path);
649 } // Routes without a path shouldn't ever match by themselves unless they are
650 // index routes, so don't add them to the list of possible branches.
651
652
653 if (route.path == null && !route.index) {
654 return;
655 }
656
657 branches.push({
658 path,
659 score: computeScore(path, route.index),
660 routesMeta
661 });
662 });
663 return branches;
664}
665
666function rankRouteBranches(branches) {
667 branches.sort((a, b) => a.score !== b.score ? b.score - a.score // Higher score first
668 : compareIndexes(a.routesMeta.map(meta => meta.childrenIndex), b.routesMeta.map(meta => meta.childrenIndex)));
669}
670
671const paramRe = /^:\w+$/;
672const dynamicSegmentValue = 3;
673const indexRouteValue = 2;
674const emptySegmentValue = 1;
675const staticSegmentValue = 10;
676const splatPenalty = -2;
677
678const isSplat = s => s === "*";
679
680function computeScore(path, index) {
681 let segments = path.split("/");
682 let initialScore = segments.length;
683
684 if (segments.some(isSplat)) {
685 initialScore += splatPenalty;
686 }
687
688 if (index) {
689 initialScore += indexRouteValue;
690 }
691
692 return segments.filter(s => !isSplat(s)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue), initialScore);
693}
694
695function compareIndexes(a, b) {
696 let siblings = a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]);
697 return siblings ? // If two routes are siblings, we should try to match the earlier sibling
698 // first. This allows people to have fine-grained control over the matching
699 // behavior by simply putting routes with identical paths in the order they
700 // want them tried.
701 a[a.length - 1] - b[b.length - 1] : // Otherwise, it doesn't really make sense to rank non-siblings by index,
702 // so they sort equally.
703 0;
704}
705
706function matchRouteBranch(branch, pathname) {
707 let {
708 routesMeta
709 } = branch;
710 let matchedParams = {};
711 let matchedPathname = "/";
712 let matches = [];
713
714 for (let i = 0; i < routesMeta.length; ++i) {
715 let meta = routesMeta[i];
716 let end = i === routesMeta.length - 1;
717 let remainingPathname = matchedPathname === "/" ? pathname : pathname.slice(matchedPathname.length) || "/";
718 let match = matchPath({
719 path: meta.relativePath,
720 caseSensitive: meta.caseSensitive,
721 end
722 }, remainingPathname);
723 if (!match) return null;
724 Object.assign(matchedParams, match.params);
725 let route = meta.route;
726 matches.push({
727 params: matchedParams,
728 pathname: joinPaths([matchedPathname, match.pathname]),
729 pathnameBase: joinPaths([matchedPathname, match.pathnameBase]),
730 route
731 });
732
733 if (match.pathnameBase !== "/") {
734 matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);
735 }
736 }
737
738 return matches;
739}
740/**
741 * Renders the result of `matchRoutes()` into a React element.
742 */
743
744
745function renderMatches(matches) {
746 return _renderMatches(matches);
747}
748
749function _renderMatches(matches, parentMatches) {
750 if (parentMatches === void 0) {
751 parentMatches = [];
752 }
753
754 if (matches == null) return null;
755 return matches.reduceRight((outlet, match, index) => {
756 return /*#__PURE__*/createElement(RouteContext.Provider, {
757 children: match.route.element !== undefined ? match.route.element : /*#__PURE__*/createElement(Outlet, null),
758 value: {
759 outlet,
760 matches: parentMatches.concat(matches.slice(0, index + 1))
761 }
762 });
763 }, null);
764}
765/**
766 * A PathPattern is used to match on some portion of a URL pathname.
767 */
768
769
770/**
771 * Performs pattern matching on a URL pathname and returns information about
772 * the match.
773 *
774 * @see https://reactrouter.com/docs/en/v6/api#matchpath
775 */
776function matchPath(pattern, pathname) {
777 if (typeof pattern === "string") {
778 pattern = {
779 path: pattern,
780 caseSensitive: false,
781 end: true
782 };
783 }
784
785 let [matcher, paramNames] = compilePath(pattern.path, pattern.caseSensitive, pattern.end);
786 let match = pathname.match(matcher);
787 if (!match) return null;
788 let matchedPathname = match[0];
789 let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
790 let captureGroups = match.slice(1);
791 let params = paramNames.reduce((memo, paramName, index) => {
792 // We need to compute the pathnameBase here using the raw splat value
793 // instead of using params["*"] later because it will be decoded then
794 if (paramName === "*") {
795 let splatValue = captureGroups[index] || "";
796 pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
797 }
798
799 memo[paramName] = safelyDecodeURIComponent(captureGroups[index] || "", paramName);
800 return memo;
801 }, {});
802 return {
803 params,
804 pathname: matchedPathname,
805 pathnameBase,
806 pattern
807 };
808}
809
810function compilePath(path, caseSensitive, end) {
811 if (caseSensitive === void 0) {
812 caseSensitive = false;
813 }
814
815 if (end === void 0) {
816 end = true;
817 }
818
819 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;
820 let paramNames = [];
821 let regexpSource = "^" + path.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below
822 .replace(/^\/*/, "/") // Make sure it has a leading /
823 .replace(/[\\.*+^$?{}|()[\]]/g, "\\$&") // Escape special regex chars
824 .replace(/:(\w+)/g, (_, paramName) => {
825 paramNames.push(paramName);
826 return "([^\\/]+)";
827 });
828
829 if (path.endsWith("*")) {
830 paramNames.push("*");
831 regexpSource += path === "*" || path === "/*" ? "(.*)$" // Already matched the initial /, just match the rest
832 : "(?:\\/(.+)|\\/*)$"; // Don't include the / in params["*"]
833 } else {
834 regexpSource += end ? "\\/*$" // When matching to the end, ignore trailing slashes
835 : // Otherwise, match a word boundary or a proceeding /. The word boundary restricts
836 // parent routes to matching only their own words and nothing more, e.g. parent
837 // route "/home" should not match "/home2".
838 "(?:\\b|\\/|$)";
839 }
840
841 let matcher = new RegExp(regexpSource, caseSensitive ? undefined : "i");
842 return [matcher, paramNames];
843}
844
845function safelyDecodeURIComponent(value, paramName) {
846 try {
847 return decodeURIComponent(value);
848 } catch (error) {
849 process.env.NODE_ENV !== "production" ? warning(false, "The value for the URL param \"" + paramName + "\" will not be decoded because" + (" the string \"" + value + "\" is a malformed URL segment. This is probably") + (" due to a bad percent encoding (" + error + ").")) : void 0;
850 return value;
851 }
852}
853/**
854 * Returns a resolved path object relative to the given pathname.
855 *
856 * @see https://reactrouter.com/docs/en/v6/api#resolvepath
857 */
858
859
860function resolvePath(to, fromPathname) {
861 if (fromPathname === void 0) {
862 fromPathname = "/";
863 }
864
865 let {
866 pathname: toPathname,
867 search = "",
868 hash = ""
869 } = typeof to === "string" ? parsePath(to) : to;
870 let pathname = toPathname ? toPathname.startsWith("/") ? toPathname : resolvePathname(toPathname, fromPathname) : fromPathname;
871 return {
872 pathname,
873 search: normalizeSearch(search),
874 hash: normalizeHash(hash)
875 };
876}
877
878function resolvePathname(relativePath, fromPathname) {
879 let segments = fromPathname.replace(/\/+$/, "").split("/");
880 let relativeSegments = relativePath.split("/");
881 relativeSegments.forEach(segment => {
882 if (segment === "..") {
883 // Keep the root "" segment so the pathname starts at /
884 if (segments.length > 1) segments.pop();
885 } else if (segment !== ".") {
886 segments.push(segment);
887 }
888 });
889 return segments.length > 1 ? segments.join("/") : "/";
890}
891
892function resolveTo(toArg, routePathnames, locationPathname) {
893 let to = typeof toArg === "string" ? parsePath(toArg) : toArg;
894 let toPathname = toArg === "" || to.pathname === "" ? "/" : to.pathname; // If a pathname is explicitly provided in `to`, it should be relative to the
895 // route context. This is explained in `Note on `<Link to>` values` in our
896 // migration guide from v5 as a means of disambiguation between `to` values
897 // that begin with `/` and those that do not. However, this is problematic for
898 // `to` values that do not provide a pathname. `to` can simply be a search or
899 // hash string, in which case we should assume that the navigation is relative
900 // to the current location's pathname and *not* the route pathname.
901
902 let from;
903
904 if (toPathname == null) {
905 from = locationPathname;
906 } else {
907 let routePathnameIndex = routePathnames.length - 1;
908
909 if (toPathname.startsWith("..")) {
910 let toSegments = toPathname.split("/"); // Each leading .. segment means "go up one route" instead of "go up one
911 // URL segment". This is a key difference from how <a href> works and a
912 // major reason we call this a "to" value instead of a "href".
913
914 while (toSegments[0] === "..") {
915 toSegments.shift();
916 routePathnameIndex -= 1;
917 }
918
919 to.pathname = toSegments.join("/");
920 } // If there are more ".." segments than parent routes, resolve relative to
921 // the root / URL.
922
923
924 from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : "/";
925 }
926
927 let path = resolvePath(to, from); // Ensure the pathname has a trailing slash if the original to value had one.
928
929 if (toPathname && toPathname !== "/" && toPathname.endsWith("/") && !path.pathname.endsWith("/")) {
930 path.pathname += "/";
931 }
932
933 return path;
934}
935
936function getToPathname(to) {
937 // Empty strings should be treated the same as / paths
938 return to === "" || to.pathname === "" ? "/" : typeof to === "string" ? parsePath(to).pathname : to.pathname;
939}
940
941function stripBasename(pathname, basename) {
942 if (basename === "/") return pathname;
943
944 if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) {
945 return null;
946 }
947
948 let nextChar = pathname.charAt(basename.length);
949
950 if (nextChar && nextChar !== "/") {
951 // pathname does not start with basename/
952 return null;
953 }
954
955 return pathname.slice(basename.length) || "/";
956}
957
958const joinPaths = paths => paths.join("/").replace(/\/\/+/g, "/");
959
960const normalizePathname = pathname => pathname.replace(/\/+$/, "").replace(/^\/*/, "/");
961
962const normalizeSearch = search => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
963
964const normalizeHash = hash => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash; ///////////////////////////////////////////////////////////////////////////////
965
966export { 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, useOutletContext, useParams, useResolvedPath, useRoutes };
967//# sourceMappingURL=index.js.map