@ionic/react-router 8.7.13-dev.11765569652.136c6b13 → 8.7.13-dev.11765907916.16a61ecf
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/dist/index.js
CHANGED
|
@@ -13,10 +13,13 @@ const IonRouteInner = ({ path, element }) => {
|
|
|
13
13
|
* @see https://reactrouter.com/v6/utils/match-path
|
|
14
14
|
*/
|
|
15
15
|
const matchPath = ({ pathname, componentProps }) => {
|
|
16
|
-
var _a, _b;
|
|
17
16
|
const { path, index } = componentProps, restProps = __rest(componentProps, ["path", "index"]);
|
|
18
|
-
// Handle index routes
|
|
17
|
+
// Handle index routes
|
|
19
18
|
if (index && !path) {
|
|
19
|
+
// Index routes match when there's no additional path after the parent route
|
|
20
|
+
// For example, in a nested outlet at /routing/*, the index route matches
|
|
21
|
+
// when the relative path is empty (i.e., we're exactly at /routing)
|
|
22
|
+
// If pathname is empty or just "/", it should match the index route
|
|
20
23
|
if (pathname === '' || pathname === '/') {
|
|
21
24
|
return {
|
|
22
25
|
params: {},
|
|
@@ -29,25 +32,14 @@ const matchPath = ({ pathname, componentProps }) => {
|
|
|
29
32
|
},
|
|
30
33
|
};
|
|
31
34
|
}
|
|
35
|
+
// Otherwise, index routes don't match when there's additional path
|
|
32
36
|
return null;
|
|
33
37
|
}
|
|
34
|
-
|
|
35
|
-
if (path === '' || path === undefined) {
|
|
36
|
-
if (pathname === '' || pathname === '/') {
|
|
37
|
-
return {
|
|
38
|
-
params: {},
|
|
39
|
-
pathname: pathname,
|
|
40
|
-
pathnameBase: pathname || '/',
|
|
41
|
-
pattern: {
|
|
42
|
-
path: '',
|
|
43
|
-
caseSensitive: (_a = restProps.caseSensitive) !== null && _a !== void 0 ? _a : false,
|
|
44
|
-
end: (_b = restProps.end) !== null && _b !== void 0 ? _b : true,
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
}
|
|
38
|
+
if (!path) {
|
|
48
39
|
return null;
|
|
49
40
|
}
|
|
50
|
-
// For relative paths (don't start with '/'),
|
|
41
|
+
// For relative paths in nested routes (those that don't start with '/'),
|
|
42
|
+
// use React Router's matcher against a normalized path.
|
|
51
43
|
if (!path.startsWith('/')) {
|
|
52
44
|
const matchOptions = Object.assign({ path: `/${path}` }, restProps);
|
|
53
45
|
if ((matchOptions === null || matchOptions === void 0 ? void 0 : matchOptions.end) === undefined) {
|
|
@@ -59,6 +51,7 @@ const matchPath = ({ pathname, componentProps }) => {
|
|
|
59
51
|
// Adjust the match to remove the leading '/' we added
|
|
60
52
|
return Object.assign(Object.assign({}, match), { pathname: pathname, pathnameBase: match.pathnameBase === '/' ? '' : match.pathnameBase.slice(1), pattern: Object.assign(Object.assign({}, match.pattern), { path: path }) });
|
|
61
53
|
}
|
|
54
|
+
// No match found
|
|
62
55
|
return null;
|
|
63
56
|
}
|
|
64
57
|
// For absolute paths, use React Router's matcher directly.
|
|
@@ -78,16 +71,12 @@ const matchPath = ({ pathname, componentProps }) => {
|
|
|
78
71
|
*/
|
|
79
72
|
const derivePathnameToMatch = (fullPathname, routePath) => {
|
|
80
73
|
var _a;
|
|
81
|
-
// For absolute or empty routes, use the full pathname as-is
|
|
82
74
|
if (!routePath || routePath === '' || routePath.startsWith('/')) {
|
|
83
75
|
return fullPathname;
|
|
84
76
|
}
|
|
85
77
|
const trimmedPath = fullPathname.startsWith('/') ? fullPathname.slice(1) : fullPathname;
|
|
86
78
|
if (!trimmedPath) {
|
|
87
|
-
|
|
88
|
-
// return the full pathname so matchPath can normalize both.
|
|
89
|
-
// This allows routes like <Route path="foo/*" .../> at root level to work correctly.
|
|
90
|
-
return fullPathname;
|
|
79
|
+
return '';
|
|
91
80
|
}
|
|
92
81
|
const fullSegments = trimmedPath.split('/').filter(Boolean);
|
|
93
82
|
if (fullSegments.length === 0) {
|
|
@@ -163,36 +152,24 @@ const computeCommonPrefix = (paths) => {
|
|
|
163
152
|
return commonSegments.length > 0 ? '/' + commonSegments.join('/') : '';
|
|
164
153
|
};
|
|
165
154
|
/**
|
|
166
|
-
* Checks if a route
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Checks if a route has an embedded wildcard (e.g., "tab1/*" but not "*" or "/*").
|
|
173
|
-
*/
|
|
174
|
-
const hasEmbeddedWildcard = (routePath) => {
|
|
175
|
-
return !!routePath && routePath.includes('*') && !isSplatOnlyRoute(routePath);
|
|
176
|
-
};
|
|
177
|
-
/**
|
|
178
|
-
* Checks if a route with an embedded wildcard matches a pathname.
|
|
179
|
-
*/
|
|
180
|
-
const matchesEmbeddedWildcardRoute = (route, pathname) => {
|
|
181
|
-
const routePath = route.props.path;
|
|
182
|
-
if (!hasEmbeddedWildcard(routePath)) {
|
|
183
|
-
return false;
|
|
184
|
-
}
|
|
185
|
-
return !!matchPath({ pathname, componentProps: route.props });
|
|
186
|
-
};
|
|
187
|
-
/**
|
|
188
|
-
* Checks if a route is a specific match (not wildcard-only or index).
|
|
155
|
+
* Checks if a route is a specific match (not wildcard or index).
|
|
156
|
+
*
|
|
157
|
+
* @param route The route element to check.
|
|
158
|
+
* @param remainingPath The remaining path to match against.
|
|
159
|
+
* @returns True if the route specifically matches the remaining path.
|
|
189
160
|
*/
|
|
190
161
|
const isSpecificRouteMatch = (route, remainingPath) => {
|
|
191
162
|
const routePath = route.props.path;
|
|
192
|
-
|
|
163
|
+
const isWildcardOnly = routePath === '*' || routePath === '/*';
|
|
164
|
+
const isIndex = route.props.index;
|
|
165
|
+
// Skip wildcards and index routes
|
|
166
|
+
if (isIndex || isWildcardOnly) {
|
|
193
167
|
return false;
|
|
194
168
|
}
|
|
195
|
-
return !!matchPath({
|
|
169
|
+
return !!matchPath({
|
|
170
|
+
pathname: remainingPath,
|
|
171
|
+
componentProps: route.props,
|
|
172
|
+
});
|
|
196
173
|
};
|
|
197
174
|
/**
|
|
198
175
|
* Analyzes route children to determine their characteristics.
|
|
@@ -237,13 +214,11 @@ const computeParentPath = (options) => {
|
|
|
237
214
|
let firstSpecificMatch = undefined;
|
|
238
215
|
let firstWildcardMatch = undefined;
|
|
239
216
|
let indexMatchAtMount = undefined;
|
|
240
|
-
// Start at i = 1 (normal case: strip at least one segment for parent path)
|
|
241
217
|
for (let i = 1; i <= segments.length; i++) {
|
|
242
218
|
const parentPath = '/' + segments.slice(0, i).join('/');
|
|
243
219
|
const remainingPath = segments.slice(i).join('/');
|
|
244
|
-
// Check for specific
|
|
245
|
-
|
|
246
|
-
const hasSpecificMatch = routeChildren.some((route) => isSpecificRouteMatch(route, remainingPath) || matchesEmbeddedWildcardRoute(route, remainingPath));
|
|
220
|
+
// Check for specific (non-wildcard, non-index) route matches
|
|
221
|
+
const hasSpecificMatch = routeChildren.some((route) => isSpecificRouteMatch(route, remainingPath));
|
|
247
222
|
if (hasSpecificMatch && !firstSpecificMatch) {
|
|
248
223
|
firstSpecificMatch = parentPath;
|
|
249
224
|
// Found a specific match - this is our answer for non-index routes
|
|
@@ -290,16 +265,6 @@ const computeParentPath = (options) => {
|
|
|
290
265
|
}
|
|
291
266
|
}
|
|
292
267
|
}
|
|
293
|
-
// Fallback: check at root level (i = 0) for embedded wildcard routes.
|
|
294
|
-
// This handles outlets inside root-level splat routes where routes like
|
|
295
|
-
// "tab1/*" need to match the full pathname.
|
|
296
|
-
if (!firstSpecificMatch) {
|
|
297
|
-
const fullRemainingPath = segments.join('/');
|
|
298
|
-
const hasRootLevelMatch = routeChildren.some((route) => matchesEmbeddedWildcardRoute(route, fullRemainingPath));
|
|
299
|
-
if (hasRootLevelMatch) {
|
|
300
|
-
firstSpecificMatch = '/';
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
268
|
// Determine the best parent path:
|
|
304
269
|
// 1. Specific match (routes like tabs/*, favorites) - highest priority
|
|
305
270
|
// 2. Wildcard match (route path="*") - catches unmatched segments
|
|
@@ -775,36 +740,27 @@ class ReactRouterViewStack extends ViewStacks {
|
|
|
775
740
|
const combinedParams = Object.assign(Object.assign({}, accumulatedParentParams), ((_c = routeMatch === null || routeMatch === void 0 ? void 0 : routeMatch.params) !== null && _c !== void 0 ? _c : {}));
|
|
776
741
|
// For relative route paths, we need to compute an absolute pathnameBase
|
|
777
742
|
// by combining the parent's pathnameBase with the matched portion
|
|
743
|
+
let absolutePathnameBase = (routeMatch === null || routeMatch === void 0 ? void 0 : routeMatch.pathnameBase) || routeInfo.pathname;
|
|
778
744
|
const routePath = routeElement.props.path;
|
|
779
745
|
const isRelativePath = routePath && !routePath.startsWith('/');
|
|
780
746
|
const isIndexRoute = !!routeElement.props.index;
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
absolutePathnameBase =
|
|
799
|
-
parentPathnameBase === '/' ? `/${relativeBase}` : `${parentPathnameBase}/${relativeBase}`;
|
|
800
|
-
}
|
|
801
|
-
else if (isIndexRoute) {
|
|
802
|
-
// Index routes should use the parent's base as their base
|
|
803
|
-
absolutePathnameBase = parentPathnameBase;
|
|
804
|
-
}
|
|
805
|
-
else {
|
|
806
|
-
// Default: use the match's pathnameBase or the current pathname
|
|
807
|
-
absolutePathnameBase = (routeMatch === null || routeMatch === void 0 ? void 0 : routeMatch.pathnameBase) || routeInfo.pathname;
|
|
747
|
+
if (isRelativePath || isIndexRoute) {
|
|
748
|
+
// Get the parent's pathnameBase to build the absolute path
|
|
749
|
+
const parentPathnameBase = parentMatches.length > 0 ? parentMatches[parentMatches.length - 1].pathnameBase : '/';
|
|
750
|
+
// For relative paths, the matchPath returns a relative pathnameBase
|
|
751
|
+
// We need to make it absolute by prepending the parent's base
|
|
752
|
+
if ((routeMatch === null || routeMatch === void 0 ? void 0 : routeMatch.pathnameBase) && isRelativePath) {
|
|
753
|
+
// Strip leading slash if present in the relative match
|
|
754
|
+
const relativeBase = routeMatch.pathnameBase.startsWith('/')
|
|
755
|
+
? routeMatch.pathnameBase.slice(1)
|
|
756
|
+
: routeMatch.pathnameBase;
|
|
757
|
+
absolutePathnameBase =
|
|
758
|
+
parentPathnameBase === '/' ? `/${relativeBase}` : `${parentPathnameBase}/${relativeBase}`;
|
|
759
|
+
}
|
|
760
|
+
else if (isIndexRoute) {
|
|
761
|
+
// Index routes should use the parent's base as their base
|
|
762
|
+
absolutePathnameBase = parentPathnameBase;
|
|
763
|
+
}
|
|
808
764
|
}
|
|
809
765
|
const contextMatches = [
|
|
810
766
|
...parentMatches,
|
|
@@ -848,9 +804,7 @@ class ReactRouterViewStack extends ViewStacks {
|
|
|
848
804
|
let parentPath = undefined;
|
|
849
805
|
try {
|
|
850
806
|
// Only attempt parent path computation for non-root outlets
|
|
851
|
-
|
|
852
|
-
const isRootOutlet = outletId.startsWith('routerOutlet');
|
|
853
|
-
if (!isRootOutlet) {
|
|
807
|
+
if (outletId !== 'routerOutlet') {
|
|
854
808
|
const routeChildren = extractRouteChildren(ionRouterOutlet.props.children);
|
|
855
809
|
const { hasRelativeRoutes, hasIndexRoute, hasWildcardRoute } = analyzeRouteChildren(routeChildren);
|
|
856
810
|
if (hasRelativeRoutes || hasIndexRoute) {
|
|
@@ -1092,7 +1046,7 @@ class ReactRouterViewStack extends ViewStacks {
|
|
|
1092
1046
|
* Matches a view with no path prop (default fallback route) or index route.
|
|
1093
1047
|
*/
|
|
1094
1048
|
function matchDefaultRoute(v) {
|
|
1095
|
-
var _a
|
|
1049
|
+
var _a;
|
|
1096
1050
|
const childProps = v.routeData.childProps;
|
|
1097
1051
|
const isDefaultRoute = childProps.path === undefined || childProps.path === '';
|
|
1098
1052
|
const isIndexRoute = !!childProps.index;
|
|
@@ -1105,22 +1059,14 @@ class ReactRouterViewStack extends ViewStacks {
|
|
|
1105
1059
|
}
|
|
1106
1060
|
return false;
|
|
1107
1061
|
}
|
|
1108
|
-
// For empty path routes, only match if we're at the same level as when the view was created.
|
|
1109
|
-
// This prevents an empty path view item from being reused for different routes.
|
|
1110
1062
|
if (isDefaultRoute) {
|
|
1111
|
-
const previousPathnameBase = ((_b = (_a = v.routeData) === null || _a === void 0 ? void 0 : _a.match) === null || _b === void 0 ? void 0 : _b.pathnameBase) || '';
|
|
1112
|
-
const normalizedBase = normalizePathnameForComparison(previousPathnameBase);
|
|
1113
|
-
const normalizedPathname = normalizePathnameForComparison(pathname);
|
|
1114
|
-
if (normalizedPathname !== normalizedBase) {
|
|
1115
|
-
return false;
|
|
1116
|
-
}
|
|
1117
1063
|
match = {
|
|
1118
1064
|
params: {},
|
|
1119
1065
|
pathname,
|
|
1120
1066
|
pathnameBase: pathname === '' ? '/' : pathname,
|
|
1121
1067
|
pattern: {
|
|
1122
1068
|
path: '',
|
|
1123
|
-
caseSensitive: (
|
|
1069
|
+
caseSensitive: (_a = childProps.caseSensitive) !== null && _a !== void 0 ? _a : false,
|
|
1124
1070
|
end: true,
|
|
1125
1071
|
},
|
|
1126
1072
|
};
|
|
@@ -1245,30 +1191,24 @@ class StackManager extends React.PureComponent {
|
|
|
1245
1191
|
if (this.outletMountPath && !currentPathname.startsWith(this.outletMountPath)) {
|
|
1246
1192
|
return undefined;
|
|
1247
1193
|
}
|
|
1248
|
-
//
|
|
1249
|
-
|
|
1194
|
+
// If this is a nested outlet (has an explicit ID like "main"),
|
|
1195
|
+
// we need to figure out what part of the path was already matched
|
|
1196
|
+
if (this.id !== 'routerOutlet' && this.ionRouterOutlet) {
|
|
1250
1197
|
const routeChildren = extractRouteChildren(this.ionRouterOutlet.props.children);
|
|
1251
1198
|
const { hasRelativeRoutes, hasIndexRoute, hasWildcardRoute } = analyzeRouteChildren(routeChildren);
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
hasIndexRoute,
|
|
1264
|
-
hasWildcardRoute,
|
|
1265
|
-
});
|
|
1266
|
-
// Update the outlet mount path if it was set
|
|
1267
|
-
if (result.outletMountPath && !this.outletMountPath) {
|
|
1268
|
-
this.outletMountPath = result.outletMountPath;
|
|
1269
|
-
}
|
|
1270
|
-
return result.parentPath;
|
|
1199
|
+
const result = computeParentPath({
|
|
1200
|
+
currentPathname,
|
|
1201
|
+
outletMountPath: this.outletMountPath,
|
|
1202
|
+
routeChildren,
|
|
1203
|
+
hasRelativeRoutes,
|
|
1204
|
+
hasIndexRoute,
|
|
1205
|
+
hasWildcardRoute,
|
|
1206
|
+
});
|
|
1207
|
+
// Update the outlet mount path if it was set
|
|
1208
|
+
if (result.outletMountPath && !this.outletMountPath) {
|
|
1209
|
+
this.outletMountPath = result.outletMountPath;
|
|
1271
1210
|
}
|
|
1211
|
+
return result.parentPath;
|
|
1272
1212
|
}
|
|
1273
1213
|
return this.outletMountPath;
|
|
1274
1214
|
}
|
|
@@ -1357,9 +1297,7 @@ class StackManager extends React.PureComponent {
|
|
|
1357
1297
|
*/
|
|
1358
1298
|
handleOutOfContextNestedOutlet(parentPath, leavingViewItem) {
|
|
1359
1299
|
var _a;
|
|
1360
|
-
|
|
1361
|
-
const isRootOutlet = this.id.startsWith('routerOutlet');
|
|
1362
|
-
if (isRootOutlet || parentPath !== undefined || !this.ionRouterOutlet) {
|
|
1300
|
+
if (this.id === 'routerOutlet' || parentPath !== undefined || !this.ionRouterOutlet) {
|
|
1363
1301
|
return false;
|
|
1364
1302
|
}
|
|
1365
1303
|
const routesChildren = (_a = getRoutesChildren(this.ionRouterOutlet.props.children)) !== null && _a !== void 0 ? _a : this.ionRouterOutlet.props.children;
|
|
@@ -1384,9 +1322,7 @@ class StackManager extends React.PureComponent {
|
|
|
1384
1322
|
* Returns true if the transition should be aborted.
|
|
1385
1323
|
*/
|
|
1386
1324
|
handleNoMatchingRoute(enteringRoute, enteringViewItem, leavingViewItem) {
|
|
1387
|
-
|
|
1388
|
-
const isRootOutlet = this.id.startsWith('routerOutlet');
|
|
1389
|
-
if (isRootOutlet || enteringRoute || enteringViewItem) {
|
|
1325
|
+
if (this.id === 'routerOutlet' || enteringRoute || enteringViewItem) {
|
|
1390
1326
|
return false;
|
|
1391
1327
|
}
|
|
1392
1328
|
// Hide any visible views in this outlet since it has no matching route
|
|
@@ -1690,19 +1626,6 @@ class StackManager extends React.PureComponent {
|
|
|
1690
1626
|
const foundView = this.context.findViewItemByRouteInfo(routeInfo, this.id);
|
|
1691
1627
|
if (foundView) {
|
|
1692
1628
|
const oldPageElement = foundView.ionPageElement;
|
|
1693
|
-
/**
|
|
1694
|
-
* FIX for issue #28878: Reject orphaned IonPage registrations.
|
|
1695
|
-
*
|
|
1696
|
-
* When a component conditionally renders different IonPages (e.g., list vs empty state)
|
|
1697
|
-
* using React keys, and state changes simultaneously with navigation, the new IonPage
|
|
1698
|
-
* tries to register for a route we're navigating away from. This creates a stale view.
|
|
1699
|
-
*
|
|
1700
|
-
* Only reject if both pageIds exist and differ, to allow nested outlet registrations.
|
|
1701
|
-
*/
|
|
1702
|
-
if (this.shouldRejectOrphanedPage(page, oldPageElement, routeInfo)) {
|
|
1703
|
-
this.hideAndRemoveOrphanedPage(page);
|
|
1704
|
-
return;
|
|
1705
|
-
}
|
|
1706
1629
|
foundView.ionPageElement = page;
|
|
1707
1630
|
foundView.ionRoute = true;
|
|
1708
1631
|
/**
|
|
@@ -1716,35 +1639,6 @@ class StackManager extends React.PureComponent {
|
|
|
1716
1639
|
}
|
|
1717
1640
|
this.handlePageTransition(routeInfo);
|
|
1718
1641
|
}
|
|
1719
|
-
/**
|
|
1720
|
-
* Determines if a new IonPage registration should be rejected as orphaned.
|
|
1721
|
-
* This happens when a component re-renders with a different IonPage while navigating away.
|
|
1722
|
-
*/
|
|
1723
|
-
shouldRejectOrphanedPage(newPage, oldPageElement, routeInfo) {
|
|
1724
|
-
if (!oldPageElement || oldPageElement === newPage) {
|
|
1725
|
-
return false;
|
|
1726
|
-
}
|
|
1727
|
-
const newPageId = newPage.getAttribute('data-pageid');
|
|
1728
|
-
const oldPageId = oldPageElement.getAttribute('data-pageid');
|
|
1729
|
-
// Only reject if both pageIds exist and are different
|
|
1730
|
-
if (!newPageId || !oldPageId || newPageId === oldPageId) {
|
|
1731
|
-
return false;
|
|
1732
|
-
}
|
|
1733
|
-
// Reject only if we're navigating away from this route
|
|
1734
|
-
return this.props.routeInfo.pathname !== routeInfo.pathname;
|
|
1735
|
-
}
|
|
1736
|
-
/**
|
|
1737
|
-
* Hides an orphaned IonPage and schedules its removal from the DOM.
|
|
1738
|
-
*/
|
|
1739
|
-
hideAndRemoveOrphanedPage(page) {
|
|
1740
|
-
page.classList.add('ion-page-hidden');
|
|
1741
|
-
page.setAttribute('aria-hidden', 'true');
|
|
1742
|
-
setTimeout(() => {
|
|
1743
|
-
if (page.parentElement) {
|
|
1744
|
-
page.remove();
|
|
1745
|
-
}
|
|
1746
|
-
}, VIEW_UNMOUNT_DELAY_MS);
|
|
1747
|
-
}
|
|
1748
1642
|
/**
|
|
1749
1643
|
* Configures the router outlet for the swipe-to-go-back gesture.
|
|
1750
1644
|
*
|
|
@@ -1975,59 +1869,27 @@ function findRouteByRouteInfo(node, routeInfo, parentPath) {
|
|
|
1975
1869
|
});
|
|
1976
1870
|
// For nested routes in React Router 6, we need to extract the relative path
|
|
1977
1871
|
// that this outlet should be responsible for matching
|
|
1978
|
-
|
|
1979
|
-
let relativePathnameToMatch = routeInfo.pathname;
|
|
1872
|
+
let pathnameToMatch = routeInfo.pathname;
|
|
1980
1873
|
// Check if we have relative routes (routes that don't start with '/')
|
|
1981
1874
|
const hasRelativeRoutes = sortedRoutes.some((r) => r.props.path && !r.props.path.startsWith('/'));
|
|
1982
1875
|
const hasIndexRoute = sortedRoutes.some((r) => r.props.index);
|
|
1983
1876
|
// SIMPLIFIED: Trust React Router 6's matching more, compute relative path when parent is known
|
|
1984
1877
|
if ((hasRelativeRoutes || hasIndexRoute) && parentPath) {
|
|
1985
1878
|
const parentPrefix = parentPath.replace('/*', '');
|
|
1986
|
-
|
|
1987
|
-
const normalizedParent = stripTrailingSlash(parentPrefix.startsWith('/') ? parentPrefix : `/${parentPrefix}`);
|
|
1879
|
+
const normalizedParent = stripTrailingSlash(parentPrefix);
|
|
1988
1880
|
const normalizedPathname = stripTrailingSlash(routeInfo.pathname);
|
|
1989
1881
|
// Only compute relative path if pathname is within parent scope
|
|
1990
1882
|
if (normalizedPathname.startsWith(normalizedParent + '/') || normalizedPathname === normalizedParent) {
|
|
1991
1883
|
const pathSegments = routeInfo.pathname.split('/').filter(Boolean);
|
|
1992
1884
|
const parentSegments = normalizedParent.split('/').filter(Boolean);
|
|
1993
1885
|
const relativeSegments = pathSegments.slice(parentSegments.length);
|
|
1994
|
-
|
|
1886
|
+
pathnameToMatch = relativeSegments.join('/'); // Empty string is valid for index routes
|
|
1995
1887
|
}
|
|
1996
1888
|
}
|
|
1997
1889
|
// Find the first matching route
|
|
1998
1890
|
for (const child of sortedRoutes) {
|
|
1999
|
-
const childPath = child.props.path;
|
|
2000
|
-
const isAbsoluteRoute = childPath && childPath.startsWith('/');
|
|
2001
|
-
// Determine which pathname to match against:
|
|
2002
|
-
// - For absolute routes: use the original full pathname
|
|
2003
|
-
// - For relative routes with a parent: use the computed relative pathname
|
|
2004
|
-
// - For relative routes at root level (no parent): use the original pathname
|
|
2005
|
-
// (matchPath will handle the relative-to-absolute normalization)
|
|
2006
|
-
const pathnameToMatch = isAbsoluteRoute ? originalPathname : relativePathnameToMatch;
|
|
2007
|
-
// Determine the path portion to match:
|
|
2008
|
-
// - For absolute routes: use derivePathnameToMatch
|
|
2009
|
-
// - For relative routes at root level (no parent): use original pathname
|
|
2010
|
-
// directly since matchPath normalizes both path and pathname
|
|
2011
|
-
// - For relative routes with parent: use derivePathnameToMatch for wildcards,
|
|
2012
|
-
// or the computed relative pathname for non-wildcards
|
|
2013
|
-
let pathForMatch;
|
|
2014
|
-
if (isAbsoluteRoute) {
|
|
2015
|
-
pathForMatch = derivePathnameToMatch(pathnameToMatch, childPath);
|
|
2016
|
-
}
|
|
2017
|
-
else if (!parentPath && childPath) {
|
|
2018
|
-
// Root-level relative route: use the full pathname and let matchPath
|
|
2019
|
-
// handle the normalization (it adds '/' to both path and pathname)
|
|
2020
|
-
pathForMatch = originalPathname;
|
|
2021
|
-
}
|
|
2022
|
-
else if (childPath && childPath.includes('*')) {
|
|
2023
|
-
// Relative wildcard route with parent path: use derivePathnameToMatch
|
|
2024
|
-
pathForMatch = derivePathnameToMatch(pathnameToMatch, childPath);
|
|
2025
|
-
}
|
|
2026
|
-
else {
|
|
2027
|
-
pathForMatch = pathnameToMatch;
|
|
2028
|
-
}
|
|
2029
1891
|
const match = matchPath({
|
|
2030
|
-
pathname:
|
|
1892
|
+
pathname: pathnameToMatch,
|
|
2031
1893
|
componentProps: child.props,
|
|
2032
1894
|
});
|
|
2033
1895
|
if (match) {
|