@react-navigation/core 8.0.0-alpha.33 → 8.0.0-alpha.35

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.
Files changed (136) hide show
  1. package/README.md +17 -14
  2. package/lib/module/BaseNavigationContainer.js +14 -2
  3. package/lib/module/BaseNavigationContainer.js.map +1 -1
  4. package/lib/module/NavigationBuilderContext.js +8 -13
  5. package/lib/module/NavigationBuilderContext.js.map +1 -1
  6. package/lib/module/NavigationIndependentTree.js +45 -15
  7. package/lib/module/NavigationIndependentTree.js.map +1 -1
  8. package/lib/module/NavigationProvider.js +9 -4
  9. package/lib/module/NavigationProvider.js.map +1 -1
  10. package/lib/module/PreventRemoveProvider.js +2 -2
  11. package/lib/module/PreventRemoveProvider.js.map +1 -1
  12. package/lib/module/SceneView.js +2 -1
  13. package/lib/module/SceneView.js.map +1 -1
  14. package/lib/module/getPathFromState.js +41 -16
  15. package/lib/module/getPathFromState.js.map +1 -1
  16. package/lib/module/getPatternParts.js +43 -12
  17. package/lib/module/getPatternParts.js.map +1 -1
  18. package/lib/module/getStateFromPath.js +175 -109
  19. package/lib/module/getStateFromPath.js.map +1 -1
  20. package/lib/module/index.js +1 -0
  21. package/lib/module/index.js.map +1 -1
  22. package/lib/module/useDescriptors.js +9 -6
  23. package/lib/module/useDescriptors.js.map +1 -1
  24. package/lib/module/useFocusEffect.js +4 -2
  25. package/lib/module/useFocusEffect.js.map +1 -1
  26. package/lib/module/useFocusEvents.js +4 -2
  27. package/lib/module/useFocusEvents.js.map +1 -1
  28. package/lib/module/useFocusedListenersChildrenAdapter.js +2 -2
  29. package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
  30. package/lib/module/useIsFocused.js +1 -0
  31. package/lib/module/useIsFocused.js.map +1 -1
  32. package/lib/module/useNavigationBuilder.js +42 -39
  33. package/lib/module/useNavigationBuilder.js.map +1 -1
  34. package/lib/module/useNavigationCache.js +85 -81
  35. package/lib/module/useNavigationCache.js.map +1 -1
  36. package/lib/module/useNavigationHelpers.js +2 -2
  37. package/lib/module/useNavigationHelpers.js.map +1 -1
  38. package/lib/module/useNavigationState.js +48 -23
  39. package/lib/module/useNavigationState.js.map +1 -1
  40. package/lib/module/useOnAction.js +6 -4
  41. package/lib/module/useOnAction.js.map +1 -1
  42. package/lib/module/useOnGetNavigation.js +2 -2
  43. package/lib/module/useOnGetNavigation.js.map +1 -1
  44. package/lib/module/useOnGetState.js +2 -2
  45. package/lib/module/useOnGetState.js.map +1 -1
  46. package/lib/module/useOnPreventRemove.js +2 -2
  47. package/lib/module/useOnPreventRemove.js.map +1 -1
  48. package/lib/module/useOnRouteFocus.js +2 -2
  49. package/lib/module/useOnRouteFocus.js.map +1 -1
  50. package/lib/module/useOptionsGetters.js +19 -20
  51. package/lib/module/useOptionsGetters.js.map +1 -1
  52. package/lib/module/usePreventRemoveContext.js +1 -1
  53. package/lib/module/usePreventRemoveContext.js.map +1 -1
  54. package/lib/module/useRouteCache.js +43 -33
  55. package/lib/module/useRouteCache.js.map +1 -1
  56. package/lib/module/useScheduleUpdate.js +5 -3
  57. package/lib/module/useScheduleUpdate.js.map +1 -1
  58. package/lib/module/useSyncState.js +9 -10
  59. package/lib/module/useSyncState.js.map +1 -1
  60. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  61. package/lib/typescript/src/NavigationBuilderContext.d.ts +9 -6
  62. package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
  63. package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
  64. package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
  65. package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
  66. package/lib/typescript/src/SceneView.d.ts +2 -1
  67. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  68. package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
  69. package/lib/typescript/src/getPatternParts.d.ts +4 -2
  70. package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
  71. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  72. package/lib/typescript/src/index.d.ts +1 -0
  73. package/lib/typescript/src/index.d.ts.map +1 -1
  74. package/lib/typescript/src/types.d.ts +1 -1
  75. package/lib/typescript/src/types.d.ts.map +1 -1
  76. package/lib/typescript/src/useDescriptors.d.ts +2 -2
  77. package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
  78. package/lib/typescript/src/useFocusEffect.d.ts.map +1 -1
  79. package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
  80. package/lib/typescript/src/useIsFocused.d.ts +1 -0
  81. package/lib/typescript/src/useIsFocused.d.ts.map +1 -1
  82. package/lib/typescript/src/useNavigationBuilder.d.ts +1 -3
  83. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  84. package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
  85. package/lib/typescript/src/useNavigationState.d.ts +1 -0
  86. package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
  87. package/lib/typescript/src/useOnAction.d.ts.map +1 -1
  88. package/lib/typescript/src/useOnGetNavigation.d.ts.map +1 -1
  89. package/lib/typescript/src/useOnGetState.d.ts.map +1 -1
  90. package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
  91. package/lib/typescript/src/useOnRouteFocus.d.ts.map +1 -1
  92. package/lib/typescript/src/useOptionsGetters.d.ts +2 -4
  93. package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
  94. package/lib/typescript/src/useRouteCache.d.ts +7 -12
  95. package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
  96. package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
  97. package/lib/typescript/src/useSyncState.d.ts.map +1 -1
  98. package/lib/typescript/src/utilities.d.ts +3 -0
  99. package/lib/typescript/src/utilities.d.ts.map +1 -1
  100. package/package.json +2 -3
  101. package/src/BaseNavigationContainer.tsx +19 -2
  102. package/src/NavigationBuilderContext.tsx +22 -18
  103. package/src/NavigationIndependentTree.tsx +56 -22
  104. package/src/NavigationProvider.tsx +13 -4
  105. package/src/PreventRemoveProvider.tsx +2 -8
  106. package/src/SceneView.tsx +3 -1
  107. package/src/getPathFromState.tsx +63 -15
  108. package/src/getPatternParts.tsx +75 -14
  109. package/src/getStateFromPath.tsx +250 -113
  110. package/src/index.tsx +1 -0
  111. package/src/types.tsx +1 -1
  112. package/src/useDescriptors.tsx +20 -6
  113. package/src/useFocusEffect.tsx +5 -2
  114. package/src/useFocusEvents.tsx +5 -2
  115. package/src/useFocusedListenersChildrenAdapter.tsx +2 -2
  116. package/src/useIsFocused.tsx +4 -0
  117. package/src/useNavigationBuilder.tsx +54 -48
  118. package/src/useNavigationCache.tsx +130 -108
  119. package/src/useNavigationHelpers.tsx +2 -2
  120. package/src/useNavigationState.tsx +60 -29
  121. package/src/useOnAction.tsx +6 -5
  122. package/src/useOnGetNavigation.tsx +3 -2
  123. package/src/useOnGetState.tsx +4 -2
  124. package/src/useOnPreventRemove.tsx +4 -2
  125. package/src/useOnRouteFocus.tsx +2 -4
  126. package/src/useOptionsGetters.tsx +26 -25
  127. package/src/usePreventRemoveContext.tsx +1 -1
  128. package/src/useRouteCache.tsx +49 -33
  129. package/src/useScheduleUpdate.tsx +6 -3
  130. package/src/useSyncState.tsx +9 -11
  131. package/src/utilities.tsx +5 -2
  132. package/lib/module/useComponent.js +0 -34
  133. package/lib/module/useComponent.js.map +0 -1
  134. package/lib/typescript/src/useComponent.d.ts +0 -7
  135. package/lib/typescript/src/useComponent.d.ts.map +0 -1
  136. package/src/useComponent.tsx +0 -37
@@ -10,6 +10,7 @@ export function getPatternParts(path) {
10
10
  };
11
11
  let isRegex = false;
12
12
  let isParam = false;
13
+ let hasRepeat = false;
13
14
  let isEscaped = false;
14
15
  let isInCharClass = false;
15
16
  let regexInnerParens = 0;
@@ -44,7 +45,7 @@ export function getPatternParts(path) {
44
45
  // The ')' is part of the regex if we're already inside one
45
46
  regexInnerParens--;
46
47
  } else {
47
- current.regex += char;
48
+ current.pattern += char;
48
49
  isRegex = false;
49
50
  isParam = false;
50
51
  }
@@ -52,12 +53,19 @@ export function getPatternParts(path) {
52
53
  throw new Error(`Encountered ')' without preceding '(' in path: ${path}`);
53
54
  }
54
55
  } else if (char === '?' && !isRegex) {
55
- if (current.param) {
56
+ if (current.name) {
56
57
  isParam = false;
57
58
  current.optional = true;
58
59
  } else {
59
60
  throw new Error(`Encountered '?' without preceding ':' in path: ${path}`);
60
61
  }
62
+ } else if (current.name && !isRegex && (char === '*' || char === '+') && (path[i + 1] == null || path[i + 1] === '/' || path[i + 1] === '?' || path[i + 1] === '*' || path[i + 1] === '+')) {
63
+ if (current.optional || path[i + 1] !== undefined && path[i + 1] !== '/') {
64
+ throw new Error(`Cannot combine path param modifiers in path: ${path}`);
65
+ }
66
+ isParam = false;
67
+ current.repeat = char === '*' ? 'zero-or-more' : 'one-or-more';
68
+ hasRepeat = true;
61
69
  } else if (char == null || char === '/' && !isRegex) {
62
70
  isParam = false;
63
71
 
@@ -66,11 +74,11 @@ export function getPatternParts(path) {
66
74
  if (current.segment === '') {
67
75
  continue;
68
76
  }
69
- if (current.param) {
70
- current.param = current.param.replace(/^:/, '');
77
+ if (current.name) {
78
+ current.name = current.name.replace(/^:/, '');
71
79
  }
72
- if (current.regex) {
73
- current.regex = current.regex.replace(/^\(/, '').replace(/\)$/, '');
80
+ if (current.pattern) {
81
+ current.pattern = current.pattern.replace(/^\(/, '').replace(/\)$/, '');
74
82
  }
75
83
  parts.push(current);
76
84
  if (char == null) {
@@ -81,8 +89,8 @@ export function getPatternParts(path) {
81
89
  };
82
90
  }
83
91
  if (isRegex) {
84
- current.regex = current.regex || '';
85
- current.regex += char;
92
+ current.pattern = current.pattern || '';
93
+ current.pattern += char;
86
94
 
87
95
  // Track escapes and character classes so parens inside them are not special
88
96
  if (isEscaped) {
@@ -96,22 +104,45 @@ export function getPatternParts(path) {
96
104
  }
97
105
  }
98
106
  if (isParam && !isRegex) {
99
- current.param = current.param || '';
100
- current.param += char;
107
+ current.name = current.name || '';
108
+ current.name += char;
101
109
  }
102
110
  }
103
111
  if (isRegex) {
104
112
  throw new Error(`Could not find closing ')' in path: ${path}`);
105
113
  }
106
- const params = parts.map(part => part.param).filter(Boolean);
114
+ const params = parts.map(part => part.name).filter(Boolean);
107
115
  for (const [index, param] of params.entries()) {
108
116
  if (params.indexOf(param) !== index) {
109
117
  throw new Error(`Duplicate param name '${param}' found in path: ${path}`);
110
118
  }
111
119
  }
120
+ if (hasRepeat) {
121
+ validateRepeatedParts(parts, path);
122
+ }
112
123
  return parts;
113
124
  }
125
+ function validateRepeatedParts(parts, path) {
126
+ let variablePart;
127
+ for (const part of parts) {
128
+ if (!part.name && part.segment !== '*') {
129
+ variablePart = undefined;
130
+ continue;
131
+ }
132
+ if (!part.repeat && !part.optional && part.segment !== '*') {
133
+ continue;
134
+ }
135
+ if (variablePart && (variablePart.repeat || part.repeat)) {
136
+ throw new Error(`A repeated param must be separated from optional, repeated, or wildcard segments by a static segment: ${path}`);
137
+ }
138
+ variablePart = part;
139
+ }
140
+ }
114
141
  export function combinePatternParts(parentParts, parts, exact = false) {
115
- return exact ? parts : [...parentParts, ...parts];
142
+ const combined = exact ? parts : [...parentParts, ...parts];
143
+ if (combined.some(part => part.repeat)) {
144
+ validateRepeatedParts(combined, combined.map(part => part.segment).join('/'));
145
+ }
146
+ return combined;
116
147
  }
117
148
  //# sourceMappingURL=getPatternParts.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["getPatternParts","path","parts","current","segment","isRegex","isParam","isEscaped","isInCharClass","regexInnerParens","i","length","char","Error","regex","param","optional","replace","push","params","map","part","filter","Boolean","index","entries","indexOf","combinePatternParts","parentParts","exact"],"sourceRoot":"../../src","sources":["getPatternParts.tsx"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["getPatternParts","path","parts","current","segment","isRegex","isParam","hasRepeat","isEscaped","isInCharClass","regexInnerParens","i","length","char","Error","pattern","name","optional","undefined","repeat","replace","push","params","map","part","filter","Boolean","index","param","entries","indexOf","validateRepeatedParts","variablePart","combinePatternParts","parentParts","exact","combined","some","join"],"sourceRoot":"../../src","sources":["getPatternParts.tsx"],"mappings":"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","ignoreList":[]}
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
 
3
- import escape from 'escape-string-regexp';
4
3
  import queryString from 'query-string';
5
4
  import { arrayStartsWith } from "./arrayStartsWith.js";
6
5
  import { combinePatternParts, getPatternParts } from "./getPatternParts.js";
@@ -10,11 +9,34 @@ const INVALID_SCHEMA_RESULT_ERROR = 'Invalid validation result from schema. It s
10
9
  const INVALID_PARSER_ERROR = 'Invalid parser. Expected a function or a Standard Schema V1 object.';
11
10
  const PARAM_GROUP_PREFIX = 'param_';
12
11
  const NESTED_SCREEN_PARAM_NAMES = ['screen', 'params', 'initial', 'path', 'merge', 'pop'];
13
- const getStaticSegmentPattern = segment => Array.from(segment, char => {
14
- const encoded = encodeURIComponent(char);
15
- const percentEncoded = encoded === char ? `%${char.charCodeAt(0).toString(16).padStart(2, '0').toUpperCase()}` : encoded;
16
- return `(?:${escape(char)}|${escape(percentEncoded)})`;
17
- }).join('');
12
+ const REGEX_SYNTAX_CHAR = /[|\\{}()[\]^$+*?.]/;
13
+ const HEX_DIGITS = '0123456789ABCDEF';
14
+ const getStaticSegmentPattern = segment => {
15
+ let pattern = '';
16
+ for (const char of segment) {
17
+ const code = char.charCodeAt(0);
18
+ let escaped = char;
19
+
20
+ // A hyphen uses a hexadecimal escape because a regular backslash escape
21
+ // isn't valid outside character classes when a regex uses Unicode mode.
22
+ if (char === '-') {
23
+ escaped = '\\x2d';
24
+ } else {
25
+ // Other characters with special meaning in a regex can use a regular
26
+ // backslash escape so they are matched as literal characters.
27
+ if (REGEX_SYNTAX_CHAR.test(char)) {
28
+ escaped = `\\${char}`;
29
+ }
30
+ }
31
+
32
+ // ASCII characters have a single-byte percent-encoded form that can be
33
+ // built directly. Other characters need encodeURIComponent to encode all
34
+ // bytes in their UTF-8 representation.
35
+ const percentEncoded = char.length === 1 && code < 128 ? `%${HEX_DIGITS.charAt(code >> 4)}${HEX_DIGITS.charAt(code & 15)}` : encodeURIComponent(char);
36
+ pattern += `(?:${escaped}|${percentEncoded})`;
37
+ }
38
+ return pattern;
39
+ };
18
40
  const getExplicitParamNames = parse => {
19
41
  const names = [];
20
42
  if (parse) {
@@ -65,6 +87,7 @@ export function getStateFromPath(path, options, previous) {
65
87
  const {
66
88
  initialRoutes,
67
89
  configs,
90
+ configsWithZeroOrMore,
68
91
  configsByScreen,
69
92
  configsByPattern,
70
93
  prefixRegex
@@ -122,9 +145,31 @@ export function getStateFromPath(path, options, previous) {
122
145
  const match = configs.find(config => config.pattern === '');
123
146
  if (match) {
124
147
  const config = resolveSharedConfig(match);
125
- return createNestedStateObject(path, config.routeNames.map(name => ({
148
+ const state = createNestedStateObject(path, config.routeNames.map(name => ({
126
149
  name
127
150
  })), initialRoutes, config);
151
+ if (state) {
152
+ return state;
153
+ }
154
+ }
155
+ for (const config of configsWithZeroOrMore) {
156
+ const routes = matchAgainstConfig('', config, configsByScreen);
157
+ if (routes) {
158
+ let selectedConfig = resolveSharedConfig(config);
159
+ let selectedRoutes = routes;
160
+ if (selectedConfig !== config) {
161
+ const rematchedRoutes = matchAgainstConfig('', selectedConfig, configsByScreen);
162
+ if (rematchedRoutes === undefined) {
163
+ selectedConfig = config;
164
+ } else {
165
+ selectedRoutes = rematchedRoutes;
166
+ }
167
+ }
168
+ const state = createNestedStateObject(path, selectedRoutes, initialRoutes, selectedConfig);
169
+ if (state) {
170
+ return state;
171
+ }
172
+ }
128
173
  }
129
174
  return undefined;
130
175
  }
@@ -206,7 +251,11 @@ function prepareConfigResources(options) {
206
251
  }
207
252
  const configsByScreen = {};
208
253
  const configsByPattern = {};
254
+ const configsWithZeroOrMore = [];
209
255
  for (const c of configs) {
256
+ if (c.params.some(param => param.repeat === 'zero-or-more')) {
257
+ configsWithZeroOrMore.push(c);
258
+ }
210
259
  (configsByScreen[c.screen] ??= []).push(c);
211
260
  const existing = configsByPattern[c.pattern];
212
261
  if (existing) {
@@ -219,6 +268,7 @@ function prepareConfigResources(options) {
219
268
  return {
220
269
  initialRoutes,
221
270
  configs,
271
+ configsWithZeroOrMore,
222
272
  configsByScreen,
223
273
  configsByPattern,
224
274
  prefixRegex
@@ -238,7 +288,7 @@ function getSortedNormalizedConfigs(initialRoutes, screens = {}) {
238
288
  // Create a normalized configs array which will be easier to use
239
289
  const configs = [];
240
290
  for (const key in screens) {
241
- configs.push(...createNormalizedConfigs(key, screens, initialRoutes, [], [], []));
291
+ setupNormalizedConfigs(key, screens, initialRoutes, configs, [], [], []);
242
292
  }
243
293
  for (let order = 0; order < configs.length; order++) {
244
294
  const config = configs[order];
@@ -248,15 +298,17 @@ function getSortedNormalizedConfigs(initialRoutes, screens = {}) {
248
298
  }
249
299
  return configs.sort((a, b) => {
250
300
  // Sort config from most specific to least specific:
251
- // - more segments
252
301
  // - static segments
253
302
  // - params with regex
254
303
  // - regular params
304
+ // - one-or-more params
305
+ // - the end of the pattern
306
+ // - zero-or-more params
255
307
  // - wildcard
256
308
 
257
309
  // If 2 patterns are same, move the one with less route names up
258
310
  // This is an error state, so it's only useful for consistent error messages
259
- if (isArrayEqual(a.segments, b.segments)) {
311
+ if (a.pattern === b.pattern && isArrayEqual(a.segments, b.segments)) {
260
312
  if (a.routeNames.length > b.routeNames.length && arrayStartsWith(a.routeNames, b.routeNames)) {
261
313
  return -1;
262
314
  }
@@ -265,73 +317,34 @@ function getSortedNormalizedConfigs(initialRoutes, screens = {}) {
265
317
  }
266
318
  return a.routeNames.length - b.routeNames.length || a.order - b.order;
267
319
  }
268
-
269
- // If one of the patterns starts with the other, it's more exhaustive
270
- // So move it up
271
- if (arrayStartsWith(a.segments, b.segments)) {
272
- return -1;
273
- }
274
- if (arrayStartsWith(b.segments, a.segments)) {
275
- return 1;
276
- }
277
320
  for (let i = 0; i < Math.max(a.segments.length, b.segments.length); i++) {
278
- const aSegment = a.segments[i];
279
- const bSegment = b.segments[i];
280
-
281
- // if b is longer, b gets higher priority
282
- if (aSegment == null) {
283
- return 1;
284
- }
285
-
286
- // if a is longer, a gets higher priority
287
- if (bSegment == null) {
288
- return -1;
289
- }
290
- const aWildCard = aSegment === '*';
291
- const bWildCard = bSegment === '*';
292
- const aParam = aSegment.startsWith(':');
293
- const bParam = bSegment.startsWith(':');
294
- const aRegex = aParam && aSegment.includes('(');
295
- const bRegex = bParam && bSegment.includes('(');
296
-
297
- // if both are wildcard or regex, we compare next component
298
- if (aWildCard && bWildCard || aRegex && bRegex) {
299
- continue;
300
- }
301
-
302
- // if only a is wildcard, b gets higher priority
303
- if (aWildCard && !bWildCard) {
304
- return 1;
305
- }
306
-
307
- // if only b is wildcard, a gets higher priority
308
- if (bWildCard && !aWildCard) {
309
- return -1;
310
- }
311
-
312
- // If only a has a param, b gets higher priority
313
- if (aParam && !bParam) {
314
- return 1;
315
- }
316
-
317
- // If only b has a param, a gets higher priority
318
- if (bParam && !aParam) {
319
- return -1;
320
- }
321
-
322
- // if only a has regex, a gets higher priority
323
- if (aRegex && !bRegex) {
324
- return -1;
325
- }
326
-
327
- // if only b has regex, b gets higher priority
328
- if (bRegex && !aRegex) {
329
- return 1;
321
+ const rank = (a.segmentRanks[i] ?? 5) - (b.segmentRanks[i] ?? 5);
322
+ if (rank !== 0) {
323
+ return rank;
330
324
  }
331
325
  }
332
- return a.segments.length - b.segments.length;
326
+ return a.order - b.order;
333
327
  });
334
328
  }
329
+ const getSegmentRank = segment => {
330
+ if (segment === undefined) {
331
+ return 5;
332
+ }
333
+ if (segment === '*') {
334
+ return 8;
335
+ }
336
+ if (!segment.startsWith(':')) {
337
+ return 0;
338
+ }
339
+ const regex = segment.includes('(');
340
+ if (segment.endsWith('+')) {
341
+ return regex ? 3 : 4;
342
+ }
343
+ if (segment.endsWith('*')) {
344
+ return regex ? 6 : 7;
345
+ }
346
+ return regex ? 1 : 2;
347
+ };
335
348
 
336
349
  // Throw if two configs resolve to the same pattern but conflicting screens
337
350
  function checkForDuplicatedConfigs(existing, config) {
@@ -370,23 +383,41 @@ const matchAgainstConfig = (remaining, config, configsByScreen) => {
370
383
  if (param.screen !== routeName) {
371
384
  continue;
372
385
  }
373
- const value = match.groups[`${PARAM_GROUP_PREFIX}${param.index}`];
374
- if (value == null) {
375
- params[param.name] = undefined;
376
- hasParams = true;
377
- continue;
386
+ let value = match.groups[`${PARAM_GROUP_PREFIX}${param.index}`];
387
+ if (param.repeat && value != null) {
388
+ value = value.replace(/\/$/, '');
378
389
  }
379
390
  let decoded;
391
+ let decodedSegments;
380
392
  try {
381
- decoded = decodeURIComponent(value);
393
+ if (param.repeat) {
394
+ if (value == null) {
395
+ decodedSegments = [];
396
+ } else {
397
+ decodedSegments = value.split('/').map(decodeURIComponent);
398
+ }
399
+ decoded = decodedSegments.join('/');
400
+ } else {
401
+ if (value == null) {
402
+ params[param.name] = undefined;
403
+ hasParams = true;
404
+ continue;
405
+ }
406
+ decoded = decodeURIComponent(value);
407
+ }
382
408
  } catch {
383
409
  return undefined;
384
410
  }
385
411
 
386
412
  // Percent-encoded values are matched permissively
387
413
  // So validate the decoded value against the custom regex
388
- if (param.regex && value !== decoded && !param.regex.test(decoded)) {
389
- return undefined;
414
+ if (param.regex) {
415
+ const regex = param.regex;
416
+ const values = decodedSegments ?? [decoded];
417
+ const rawValues = param.repeat ? value?.split('/') ?? [] : [value];
418
+ if (values.some((item, index) => item !== rawValues[index] && !regex.test(item))) {
419
+ return undefined;
420
+ }
390
421
  }
391
422
  const parser = routeConfig.parse?.[param.name];
392
423
  if (!parser) {
@@ -433,16 +464,47 @@ const matchAgainstConfig = (remaining, config, configsByScreen) => {
433
464
  }
434
465
  return matchedRoutes;
435
466
  };
436
- const createNormalizedConfigs = (screen, routeConfig, initials, parts, parentScreens, routeNames) => {
437
- const configs = [];
467
+ const getRoutePatternParts = (path, screen) => getPatternParts(path).map(part => {
468
+ const rank = getSegmentRank(part.segment);
469
+ if (part.name) {
470
+ return {
471
+ type: 'param',
472
+ screen,
473
+ segment: part.segment,
474
+ rank,
475
+ name: part.name,
476
+ ...(part.pattern ? {
477
+ pattern: part.pattern,
478
+ regex: new RegExp(`^(?:${part.pattern})$`)
479
+ } : null),
480
+ ...(part.optional ? {
481
+ optional: true
482
+ } : null),
483
+ ...(part.repeat ? {
484
+ repeat: part.repeat
485
+ } : null)
486
+ };
487
+ }
488
+ if (part.segment === '*') {
489
+ return {
490
+ type: 'wildcard',
491
+ segment: part.segment,
492
+ rank
493
+ };
494
+ }
495
+ return {
496
+ type: 'static',
497
+ segment: part.segment,
498
+ rank,
499
+ pattern: getStaticSegmentPattern(part.segment)
500
+ };
501
+ });
502
+ const setupNormalizedConfigs = (screen, routeConfig, initials, configs, parts, parentScreens, routeNames) => {
438
503
  routeNames.push(screen);
439
504
  parentScreens.push(screen);
440
505
  const config = routeConfig[screen];
441
506
  if (typeof config === 'string') {
442
- const configParts = combinePatternParts(parts, getPatternParts(config).map(part => ({
443
- ...part,
444
- screen
445
- })));
507
+ const configParts = combinePatternParts(parts, getRoutePatternParts(config, screen));
446
508
  configs.push(createConfigItem(screen, [...routeNames], configParts));
447
509
  } else if (typeof config === 'object') {
448
510
  // if an object is specified as the value (e.g. Foo: { ... }),
@@ -450,10 +512,7 @@ const createNormalizedConfigs = (screen, routeConfig, initials, parts, parentScr
450
512
  // it could have `screens` prop which has nested configs
451
513
  const nestedScreens = 'screens' in config ? config.screens : undefined;
452
514
  const hasNestedScreens = !!nestedScreens;
453
- const currentParts = typeof config.path === 'string' ? combinePatternParts(parts, getPatternParts(config.path).map(part => ({
454
- ...part,
455
- screen
456
- })), config.exact) : parts;
515
+ const currentParts = typeof config.path === 'string' ? combinePatternParts(parts, getRoutePatternParts(config.path, screen), config.exact) : parts;
457
516
  if (typeof config.path === 'string') {
458
517
  // We should add alias configs after the main config
459
518
  // So unless they are more specific, main config will be matched first
@@ -461,15 +520,9 @@ const createNormalizedConfigs = (screen, routeConfig, initials, parts, parentScr
461
520
  if (config.alias) {
462
521
  for (const alias of config.alias) {
463
522
  if (typeof alias === 'string') {
464
- aliasConfigs.push(createConfigItem(screen, [...routeNames], combinePatternParts(parts, getPatternParts(alias).map(part => ({
465
- ...part,
466
- screen
467
- }))), config.parse, hasNestedScreens, false));
523
+ aliasConfigs.push(createConfigItem(screen, [...routeNames], combinePatternParts(parts, getRoutePatternParts(alias, screen)), config.parse, hasNestedScreens, false));
468
524
  } else if (typeof alias === 'object') {
469
- aliasConfigs.push(createConfigItem(screen, [...routeNames], combinePatternParts(parts, getPatternParts(alias.path).map(part => ({
470
- ...part,
471
- screen
472
- })), alias.exact), alias.parse, hasNestedScreens, alias.shared === true));
525
+ aliasConfigs.push(createConfigItem(screen, [...routeNames], combinePatternParts(parts, getRoutePatternParts(alias.path, screen), alias.exact), alias.parse, hasNestedScreens, alias.shared === true));
473
526
  }
474
527
  }
475
528
  }
@@ -488,17 +541,16 @@ const createNormalizedConfigs = (screen, routeConfig, initials, parts, parentScr
488
541
  });
489
542
  }
490
543
  for (const nestedConfig in nestedScreens) {
491
- const result = createNormalizedConfigs(nestedConfig, nestedScreens, initials, currentParts, [...parentScreens], routeNames);
492
- configs.push(...result);
544
+ setupNormalizedConfigs(nestedConfig, nestedScreens, initials, configs, currentParts, [...parentScreens], routeNames);
493
545
  }
494
546
  }
495
547
  }
496
548
  routeNames.pop();
497
- return configs;
498
549
  };
499
550
  const createConfigItem = (screen, routeNames, parts, parse, hasNestedScreens = false, shared = false) => {
500
551
  let regex;
501
552
  const segments = [];
553
+ const segmentRanks = [];
502
554
  const params = [];
503
555
  const pathParamNames = new Set();
504
556
  if (parts.length) {
@@ -506,24 +558,37 @@ const createConfigItem = (screen, routeNames, parts, parse, hasNestedScreens = f
506
558
  let index = 0;
507
559
  for (const part of parts) {
508
560
  segments.push(part.segment);
509
- if (part.param) {
561
+ segmentRanks.push(part.rank);
562
+ if (part.type === 'param') {
510
563
  // A custom regex may not match the percent-encoded form of the value
511
564
  // So also accept segments containing an encoded character and validate them after decoding
512
- const reg = part.regex ? `(?:${part.regex})|(?=[^/]*%[0-9A-F]{2})[^/]+` : '[^/]+';
513
- regexString += `(((?<${PARAM_GROUP_PREFIX}${index}>${reg})\\/)${part.optional ? '?' : ''})`;
565
+ let reg = '[^/]+';
566
+ if (part.pattern) {
567
+ const encodedReg = `(?=[^/]*%[0-9A-F]{2})[^/]+`;
568
+ reg = part.repeat ? `(?:${encodedReg})|(?![^/]*%[0-9A-F]{2})(?:${part.pattern})` : `(?:${part.pattern})|${encodedReg}`;
569
+ }
570
+ if (part.repeat) {
571
+ const repeatedReg = `(?:(?:${reg})\\/)+`;
572
+ regexString += `(?<${PARAM_GROUP_PREFIX}${index}>${repeatedReg})${part.repeat === 'zero-or-more' ? '?' : ''}`;
573
+ } else {
574
+ regexString += `(((?<${PARAM_GROUP_PREFIX}${index}>${reg})\\/)${part.optional ? '?' : ''})`;
575
+ }
514
576
  params.push({
515
577
  index,
516
578
  screen: part.screen,
517
- name: part.param,
518
- regex: part.regex ? new RegExp(`^(?:${part.regex})$`) : undefined
579
+ name: part.name,
580
+ regex: part.regex,
581
+ ...(part.repeat ? {
582
+ repeat: part.repeat
583
+ } : {})
519
584
  });
520
585
  if (part.screen === screen) {
521
- pathParamNames.add(part.param);
586
+ pathParamNames.add(part.name);
522
587
  }
523
- } else if (part.segment === '*') {
588
+ } else if (part.type === 'wildcard') {
524
589
  regexString += `.*\\/`;
525
590
  } else {
526
- regexString += `${getStaticSegmentPattern(part.segment)}\\/`;
591
+ regexString += `${part.pattern}\\/`;
527
592
  }
528
593
  index++;
529
594
  }
@@ -537,6 +602,7 @@ const createConfigItem = (screen, routeNames, parts, parse, hasNestedScreens = f
537
602
  regex,
538
603
  pattern,
539
604
  segments,
605
+ segmentRanks,
540
606
  params,
541
607
  routeNames,
542
608
  parse,