@react-navigation/core 8.0.0-alpha.2 → 8.0.0-alpha.20

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 (251) hide show
  1. package/lib/module/BaseNavigationContainer.js +20 -9
  2. package/lib/module/BaseNavigationContainer.js.map +1 -1
  3. package/lib/module/ConsumedParamsContext.js +5 -0
  4. package/lib/module/ConsumedParamsContext.js.map +1 -0
  5. package/lib/module/NavigationBuilderContext.js +1 -0
  6. package/lib/module/NavigationBuilderContext.js.map +1 -1
  7. package/lib/module/NavigationIndependentTree.js +8 -4
  8. package/lib/module/NavigationIndependentTree.js.map +1 -1
  9. package/lib/module/NavigationProvider.js +14 -3
  10. package/lib/module/NavigationProvider.js.map +1 -1
  11. package/lib/module/NavigationStateContext.js.map +1 -1
  12. package/lib/module/PreventRemoveProvider.js +3 -3
  13. package/lib/module/PreventRemoveProvider.js.map +1 -1
  14. package/lib/module/SceneView.js +46 -56
  15. package/lib/module/SceneView.js.map +1 -1
  16. package/lib/module/StaticNavigation.js +166 -46
  17. package/lib/module/StaticNavigation.js.map +1 -1
  18. package/lib/module/checkSerializable.js +11 -4
  19. package/lib/module/checkSerializable.js.map +1 -1
  20. package/lib/module/createNavigatorFactory.js +27 -5
  21. package/lib/module/createNavigatorFactory.js.map +1 -1
  22. package/lib/module/findFocusedRoute.js +7 -4
  23. package/lib/module/findFocusedRoute.js.map +1 -1
  24. package/lib/module/getActionFromState.js +40 -14
  25. package/lib/module/getActionFromState.js.map +1 -1
  26. package/lib/module/getPathFromState.js +48 -11
  27. package/lib/module/getPathFromState.js.map +1 -1
  28. package/lib/module/getPatternParts.js +3 -0
  29. package/lib/module/getPatternParts.js.map +1 -1
  30. package/lib/module/getStateFromPath.js +353 -166
  31. package/lib/module/getStateFromPath.js.map +1 -1
  32. package/lib/module/getStateFromRouteParams.js +24 -0
  33. package/lib/module/getStateFromRouteParams.js.map +1 -0
  34. package/lib/module/index.js +3 -4
  35. package/lib/module/index.js.map +1 -1
  36. package/lib/module/theming/useTheme.js +1 -1
  37. package/lib/module/theming/useTheme.js.map +1 -1
  38. package/lib/module/types.js +15 -0
  39. package/lib/module/types.js.map +1 -1
  40. package/lib/module/useDescriptors.js +18 -9
  41. package/lib/module/useDescriptors.js.map +1 -1
  42. package/lib/module/useEventEmitter.js +43 -31
  43. package/lib/module/useEventEmitter.js.map +1 -1
  44. package/lib/module/useFocusEvents.js +6 -2
  45. package/lib/module/useFocusEvents.js.map +1 -1
  46. package/lib/module/useFocusedListenersChildrenAdapter.js +2 -2
  47. package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -1
  48. package/lib/module/useIsFocused.js +7 -12
  49. package/lib/module/useIsFocused.js.map +1 -1
  50. package/lib/module/useNavigation.js.map +1 -1
  51. package/lib/module/useNavigationBuilder.js +144 -86
  52. package/lib/module/useNavigationBuilder.js.map +1 -1
  53. package/lib/module/useNavigationCache.js +3 -3
  54. package/lib/module/useNavigationCache.js.map +1 -1
  55. package/lib/module/useNavigationHelpers.js +1 -1
  56. package/lib/module/useNavigationHelpers.js.map +1 -1
  57. package/lib/module/useNavigationIndependentTree.js +1 -1
  58. package/lib/module/useNavigationIndependentTree.js.map +1 -1
  59. package/lib/module/useNavigationState.js +6 -4
  60. package/lib/module/useNavigationState.js.map +1 -1
  61. package/lib/module/useOnAction.js +3 -3
  62. package/lib/module/useOnAction.js.map +1 -1
  63. package/lib/module/useOnGetState.js +6 -3
  64. package/lib/module/useOnGetState.js.map +1 -1
  65. package/lib/module/useOnPreventRemove.js +3 -3
  66. package/lib/module/useOnPreventRemove.js.map +1 -1
  67. package/lib/module/useOnRouteFocus.js +1 -1
  68. package/lib/module/useOnRouteFocus.js.map +1 -1
  69. package/lib/module/useOptionsGetters.js +7 -4
  70. package/lib/module/useOptionsGetters.js.map +1 -1
  71. package/lib/module/usePreventRemove.js +1 -1
  72. package/lib/module/usePreventRemove.js.map +1 -1
  73. package/lib/module/usePreventRemoveContext.js +1 -1
  74. package/lib/module/usePreventRemoveContext.js.map +1 -1
  75. package/lib/module/useRegisterNavigator.js +2 -2
  76. package/lib/module/useRegisterNavigator.js.map +1 -1
  77. package/lib/module/useRoute.js.map +1 -1
  78. package/lib/module/useRouteCache.js +2 -1
  79. package/lib/module/useRouteCache.js.map +1 -1
  80. package/lib/module/useScheduleUpdate.js +1 -1
  81. package/lib/module/useScheduleUpdate.js.map +1 -1
  82. package/lib/module/useStateForPath.js +1 -1
  83. package/lib/module/useStateForPath.js.map +1 -1
  84. package/lib/module/validatePathConfig.js +2 -1
  85. package/lib/module/validatePathConfig.js.map +1 -1
  86. package/lib/typescript/src/BaseNavigationContainer.d.ts +6 -3
  87. package/lib/typescript/src/BaseNavigationContainer.d.ts.map +1 -1
  88. package/lib/typescript/src/ConsumedParamsContext.d.ts +8 -0
  89. package/lib/typescript/src/ConsumedParamsContext.d.ts.map +1 -0
  90. package/lib/typescript/src/EnsureSingleNavigator.d.ts +1 -1
  91. package/lib/typescript/src/EnsureSingleNavigator.d.ts.map +1 -1
  92. package/lib/typescript/src/Group.d.ts +1 -1
  93. package/lib/typescript/src/Group.d.ts.map +1 -1
  94. package/lib/typescript/src/NavigationBuilderContext.d.ts +12 -6
  95. package/lib/typescript/src/NavigationBuilderContext.d.ts.map +1 -1
  96. package/lib/typescript/src/NavigationContainerRefContext.d.ts +1 -1
  97. package/lib/typescript/src/NavigationContainerRefContext.d.ts.map +1 -1
  98. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts +4 -4
  99. package/lib/typescript/src/NavigationFocusedRouteStateContext.d.ts.map +1 -1
  100. package/lib/typescript/src/NavigationHelpersContext.d.ts +1 -1
  101. package/lib/typescript/src/NavigationHelpersContext.d.ts.map +1 -1
  102. package/lib/typescript/src/NavigationIndependentTree.d.ts +1 -1
  103. package/lib/typescript/src/NavigationIndependentTree.d.ts.map +1 -1
  104. package/lib/typescript/src/NavigationProvider.d.ts +10 -10
  105. package/lib/typescript/src/NavigationProvider.d.ts.map +1 -1
  106. package/lib/typescript/src/NavigationStateContext.d.ts +3 -3
  107. package/lib/typescript/src/NavigationStateContext.d.ts.map +1 -1
  108. package/lib/typescript/src/PreventRemoveProvider.d.ts +1 -1
  109. package/lib/typescript/src/PreventRemoveProvider.d.ts.map +1 -1
  110. package/lib/typescript/src/SceneView.d.ts +3 -2
  111. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  112. package/lib/typescript/src/Screen.d.ts +1 -1
  113. package/lib/typescript/src/Screen.d.ts.map +1 -1
  114. package/lib/typescript/src/StaticContainer.d.ts +1 -1
  115. package/lib/typescript/src/StaticContainer.d.ts.map +1 -1
  116. package/lib/typescript/src/StaticNavigation.d.ts +123 -92
  117. package/lib/typescript/src/StaticNavigation.d.ts.map +1 -1
  118. package/lib/typescript/src/checkSerializable.d.ts +5 -3
  119. package/lib/typescript/src/checkSerializable.d.ts.map +1 -1
  120. package/lib/typescript/src/createNavigationContainerRef.d.ts +1 -1
  121. package/lib/typescript/src/createNavigationContainerRef.d.ts.map +1 -1
  122. package/lib/typescript/src/createNavigatorFactory.d.ts +17 -5
  123. package/lib/typescript/src/createNavigatorFactory.d.ts.map +1 -1
  124. package/lib/typescript/src/findFocusedRoute.d.ts +3 -3
  125. package/lib/typescript/src/findFocusedRoute.d.ts.map +1 -1
  126. package/lib/typescript/src/getActionFromState.d.ts +4 -4
  127. package/lib/typescript/src/getActionFromState.d.ts.map +1 -1
  128. package/lib/typescript/src/getPathFromState.d.ts +3 -3
  129. package/lib/typescript/src/getPathFromState.d.ts.map +1 -1
  130. package/lib/typescript/src/getPatternParts.d.ts +1 -0
  131. package/lib/typescript/src/getPatternParts.d.ts.map +1 -1
  132. package/lib/typescript/src/getStateFromPath.d.ts +5 -5
  133. package/lib/typescript/src/getStateFromPath.d.ts.map +1 -1
  134. package/lib/typescript/src/getStateFromRouteParams.d.ts +3 -0
  135. package/lib/typescript/src/getStateFromRouteParams.d.ts.map +1 -0
  136. package/lib/typescript/src/index.d.ts +34 -33
  137. package/lib/typescript/src/index.d.ts.map +1 -1
  138. package/lib/typescript/src/theming/ThemeContext.d.ts +1 -1
  139. package/lib/typescript/src/theming/ThemeContext.d.ts.map +1 -1
  140. package/lib/typescript/src/theming/ThemeProvider.d.ts +2 -2
  141. package/lib/typescript/src/theming/ThemeProvider.d.ts.map +1 -1
  142. package/lib/typescript/src/theming/useTheme.d.ts +1 -1
  143. package/lib/typescript/src/theming/useTheme.d.ts.map +1 -1
  144. package/lib/typescript/src/types.d.ts +215 -131
  145. package/lib/typescript/src/types.d.ts.map +1 -1
  146. package/lib/typescript/src/useChildListeners.d.ts +3 -3
  147. package/lib/typescript/src/useChildListeners.d.ts.map +1 -1
  148. package/lib/typescript/src/useComponent.d.ts +1 -1
  149. package/lib/typescript/src/useComponent.d.ts.map +1 -1
  150. package/lib/typescript/src/useDescriptors.d.ts +7 -7
  151. package/lib/typescript/src/useDescriptors.d.ts.map +1 -1
  152. package/lib/typescript/src/useEventEmitter.d.ts +1 -1
  153. package/lib/typescript/src/useEventEmitter.d.ts.map +1 -1
  154. package/lib/typescript/src/useFocusEvents.d.ts +2 -2
  155. package/lib/typescript/src/useFocusEvents.d.ts.map +1 -1
  156. package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts +2 -2
  157. package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts.map +1 -1
  158. package/lib/typescript/src/useIsFocused.d.ts +3 -0
  159. package/lib/typescript/src/useIsFocused.d.ts.map +1 -1
  160. package/lib/typescript/src/useKeyedChildListeners.d.ts +3 -3
  161. package/lib/typescript/src/useKeyedChildListeners.d.ts.map +1 -1
  162. package/lib/typescript/src/useNavigation.d.ts +5 -3
  163. package/lib/typescript/src/useNavigation.d.ts.map +1 -1
  164. package/lib/typescript/src/useNavigationBuilder.d.ts +24 -24
  165. package/lib/typescript/src/useNavigationBuilder.d.ts.map +1 -1
  166. package/lib/typescript/src/useNavigationCache.d.ts +3 -3
  167. package/lib/typescript/src/useNavigationCache.d.ts.map +1 -1
  168. package/lib/typescript/src/useNavigationContainerRef.d.ts +1 -1
  169. package/lib/typescript/src/useNavigationContainerRef.d.ts.map +1 -1
  170. package/lib/typescript/src/useNavigationHelpers.d.ts +19 -19
  171. package/lib/typescript/src/useNavigationHelpers.d.ts.map +1 -1
  172. package/lib/typescript/src/useNavigationState.d.ts +2 -2
  173. package/lib/typescript/src/useNavigationState.d.ts.map +1 -1
  174. package/lib/typescript/src/useOnAction.d.ts +9 -9
  175. package/lib/typescript/src/useOnAction.d.ts.map +1 -1
  176. package/lib/typescript/src/useOnGetState.d.ts +1 -1
  177. package/lib/typescript/src/useOnGetState.d.ts.map +1 -1
  178. package/lib/typescript/src/useOnPreventRemove.d.ts +3 -3
  179. package/lib/typescript/src/useOnPreventRemove.d.ts.map +1 -1
  180. package/lib/typescript/src/useOnRouteFocus.d.ts +6 -6
  181. package/lib/typescript/src/useOnRouteFocus.d.ts.map +1 -1
  182. package/lib/typescript/src/useOptionsGetters.d.ts +1 -1
  183. package/lib/typescript/src/useOptionsGetters.d.ts.map +1 -1
  184. package/lib/typescript/src/usePreventRemoveContext.d.ts +1 -1
  185. package/lib/typescript/src/useRoute.d.ts +2 -2
  186. package/lib/typescript/src/useRoute.d.ts.map +1 -1
  187. package/lib/typescript/src/useRouteCache.d.ts +4 -4
  188. package/lib/typescript/src/useRouteCache.d.ts.map +1 -1
  189. package/lib/typescript/src/useScheduleUpdate.d.ts.map +1 -1
  190. package/lib/typescript/src/useStateForPath.d.ts +1 -1
  191. package/lib/typescript/src/useStateForPath.d.ts.map +1 -1
  192. package/lib/typescript/src/utilities.d.ts +45 -5
  193. package/lib/typescript/src/utilities.d.ts.map +1 -1
  194. package/lib/typescript/src/validatePathConfig.d.ts.map +1 -1
  195. package/package.json +23 -20
  196. package/src/BaseNavigationContainer.tsx +337 -326
  197. package/src/ConsumedParamsContext.tsx +10 -0
  198. package/src/NavigationBuilderContext.tsx +14 -8
  199. package/src/NavigationFocusedRouteStateContext.tsx +4 -4
  200. package/src/NavigationIndependentTree.tsx +8 -5
  201. package/src/NavigationProvider.tsx +17 -3
  202. package/src/NavigationStateContext.tsx +5 -6
  203. package/src/PreventRemoveProvider.tsx +3 -3
  204. package/src/SceneView.tsx +58 -56
  205. package/src/StaticNavigation.tsx +488 -250
  206. package/src/checkSerializable.tsx +22 -12
  207. package/src/createNavigatorFactory.tsx +83 -8
  208. package/src/findFocusedRoute.tsx +13 -8
  209. package/src/getActionFromState.tsx +68 -39
  210. package/src/getPathFromState.tsx +91 -27
  211. package/src/getPatternParts.tsx +8 -0
  212. package/src/getStateFromPath.tsx +555 -180
  213. package/src/getStateFromRouteParams.tsx +60 -0
  214. package/src/index.tsx +47 -5
  215. package/src/theming/useTheme.tsx +1 -1
  216. package/src/types.tsx +541 -338
  217. package/src/useDescriptors.tsx +27 -12
  218. package/src/useEventEmitter.tsx +45 -36
  219. package/src/useFocusEvents.tsx +8 -2
  220. package/src/useFocusedListenersChildrenAdapter.tsx +2 -2
  221. package/src/useIsFocused.tsx +14 -21
  222. package/src/useNavigation.tsx +8 -4
  223. package/src/useNavigationBuilder.tsx +220 -132
  224. package/src/useNavigationCache.tsx +6 -6
  225. package/src/useNavigationHelpers.tsx +1 -1
  226. package/src/useNavigationIndependentTree.tsx +1 -1
  227. package/src/useNavigationState.tsx +11 -7
  228. package/src/useOnAction.tsx +10 -10
  229. package/src/useOnGetState.tsx +5 -3
  230. package/src/useOnPreventRemove.tsx +3 -3
  231. package/src/useOnRouteFocus.tsx +10 -12
  232. package/src/useOptionsGetters.tsx +7 -4
  233. package/src/usePreventRemove.tsx +1 -1
  234. package/src/usePreventRemoveContext.tsx +1 -1
  235. package/src/useRegisterNavigator.tsx +2 -2
  236. package/src/useRoute.tsx +4 -2
  237. package/src/useRouteCache.tsx +2 -1
  238. package/src/useScheduleUpdate.tsx +1 -3
  239. package/src/useStateForPath.tsx +1 -1
  240. package/src/utilities.tsx +101 -5
  241. package/src/validatePathConfig.tsx +1 -0
  242. package/lib/module/CurrentRenderContext.js +0 -10
  243. package/lib/module/CurrentRenderContext.js.map +0 -1
  244. package/lib/module/useCurrentRender.js +0 -19
  245. package/lib/module/useCurrentRender.js.map +0 -1
  246. package/lib/typescript/src/CurrentRenderContext.d.ts +0 -9
  247. package/lib/typescript/src/CurrentRenderContext.d.ts.map +0 -1
  248. package/lib/typescript/src/useCurrentRender.d.ts +0 -14
  249. package/lib/typescript/src/useCurrentRender.d.ts.map +0 -1
  250. package/src/CurrentRenderContext.tsx +0 -9
  251. package/src/useCurrentRender.tsx +0 -31
@@ -1,14 +1,16 @@
1
- const checkSerializableWithoutCircularReference = (
2
- o: { [key: string]: any },
3
- seen: Set<any>,
4
- location: (string | number)[]
5
- ):
1
+ type Result =
6
2
  | { serializable: true }
7
3
  | {
8
4
  serializable: false;
9
5
  location: (string | number)[];
10
6
  reason: string;
11
- } => {
7
+ };
8
+
9
+ const checkSerializableWithoutCircularReference = (
10
+ o: { [key: string]: any },
11
+ seen: Set<any>,
12
+ location: (string | number)[]
13
+ ): Result => {
12
14
  if (
13
15
  o === undefined ||
14
16
  o === null ||
@@ -25,7 +27,7 @@ const checkSerializableWithoutCircularReference = (
25
27
  ) {
26
28
  return {
27
29
  serializable: false,
28
- location,
30
+ location: location.slice(),
29
31
  reason: typeof o === 'function' ? 'Function' : String(o),
30
32
  };
31
33
  }
@@ -34,7 +36,7 @@ const checkSerializableWithoutCircularReference = (
34
36
  return {
35
37
  serializable: false,
36
38
  reason: 'Circular reference',
37
- location,
39
+ location: location.slice(),
38
40
  };
39
41
  }
40
42
 
@@ -42,30 +44,38 @@ const checkSerializableWithoutCircularReference = (
42
44
 
43
45
  if (Array.isArray(o)) {
44
46
  for (let i = 0; i < o.length; i++) {
47
+ location.push(i);
45
48
  const childResult = checkSerializableWithoutCircularReference(
46
49
  o[i],
47
- new Set<any>(seen),
48
- [...location, i]
50
+ seen,
51
+ location
49
52
  );
53
+ location.pop();
50
54
 
51
55
  if (!childResult.serializable) {
56
+ seen.delete(o);
52
57
  return childResult;
53
58
  }
54
59
  }
55
60
  } else {
56
61
  for (const key in o) {
62
+ location.push(key);
57
63
  const childResult = checkSerializableWithoutCircularReference(
58
64
  o[key],
59
- new Set<any>(seen),
60
- [...location, key]
65
+ seen,
66
+ location
61
67
  );
68
+ location.pop();
62
69
 
63
70
  if (!childResult.serializable) {
71
+ seen.delete(o);
64
72
  return childResult;
65
73
  }
66
74
  }
67
75
  }
68
76
 
77
+ seen.delete(o);
78
+
69
79
  return { serializable: true };
70
80
  };
71
81
 
@@ -1,23 +1,98 @@
1
- import type * as React from 'react';
1
+ import type { ParamListBase } from '@react-navigation/routers';
2
+ import * as React from 'react';
2
3
 
3
4
  import { Group } from './Group';
4
5
  import { Screen } from './Screen';
6
+ import {
7
+ createComponentForStaticConfig,
8
+ type StaticConfig,
9
+ type StaticParamList,
10
+ } from './StaticNavigation';
11
+ import type {
12
+ NavigatorTypeBagBase,
13
+ NavigatorTypeBagFor,
14
+ TypedNavigator,
15
+ } from './types';
16
+ import type { KeysOf } from './utilities';
17
+
18
+ export type TypedNavigatorFactory<in out TypeBag extends NavigatorTypeBagBase> =
19
+ {
20
+ <const ParamList extends ParamListBase>(): TypedNavigator<
21
+ NavigatorTypeBagFor<TypeBag, ParamList>,
22
+ undefined
23
+ >;
24
+ <
25
+ const Config extends StaticConfig<
26
+ NavigatorTypeBagFor<TypeBag, ParamListBase>
27
+ > &
28
+ // TS only checks for excess properties for non-generic object literal types.
29
+ // Since `Config` is generic, extra keys satisfy the constraint and slip through.
30
+ // So we force any key in `Config` that isn't a known key to have the type `never`,
31
+ // Thus causing an error for any extra key.
32
+ Record<
33
+ Exclude<
34
+ keyof Config,
35
+ KeysOf<StaticConfig<NavigatorTypeBagFor<TypeBag, ParamListBase>>>
36
+ >,
37
+ never
38
+ >,
39
+ >(
40
+ config: Config
41
+ ): TypedNavigator<
42
+ NavigatorTypeBagFor<TypeBag, StaticParamList<{ config: Config }>>,
43
+ Config
44
+ >;
45
+ };
5
46
 
6
47
  /**
7
- * Higher order component to create a `Navigator` and `Screen` pair.
8
- * Custom navigators should wrap the navigator component in `createNavigator` before exporting.
48
+ * Higher order function to create a navigator factory
49
+ * A navigator factory creates a navigator:
50
+ * - `Navigator` and `Screen` pairs for dynamic configuration
51
+ * - Static navigator object for static configuration
9
52
  *
10
53
  * @param Navigator The navigator component to wrap.
11
- * @returns Factory method to create a `Navigator` and `Screen` pair.
54
+ * @returns Factory method to create a navigator
12
55
  */
13
- export function createNavigatorFactory(Navigator: React.ComponentType<any>) {
56
+ export function createNavigatorFactory<TypeBag extends NavigatorTypeBagBase>(
57
+ Navigator: React.ComponentType<any>
58
+ ): TypedNavigatorFactory<TypeBag> {
59
+ const displayName = Navigator.displayName ?? Navigator.name ?? 'Navigator';
60
+
14
61
  function createNavigator(config?: any): any {
15
62
  if (config != null) {
63
+ const NavigatorComponent = createComponentForStaticConfig(
64
+ {
65
+ Navigator,
66
+ Screen,
67
+ Group,
68
+ config,
69
+ },
70
+ displayName
71
+ );
72
+
16
73
  return {
17
- Navigator,
18
- Screen,
19
- Group,
20
74
  config,
75
+ with(
76
+ DecoratorComponent: React.ComponentType<{
77
+ Navigator: React.ComponentType<any>;
78
+ }>
79
+ ) {
80
+ const WithComponent = () => {
81
+ return React.createElement(DecoratorComponent, {
82
+ Navigator: NavigatorComponent,
83
+ });
84
+ };
85
+
86
+ WithComponent.displayName = `${displayName}With`;
87
+
88
+ return {
89
+ config,
90
+ getComponent: () => WithComponent,
91
+ };
92
+ },
93
+ getComponent() {
94
+ return NavigatorComponent;
95
+ },
21
96
  };
22
97
  }
23
98
 
@@ -2,21 +2,26 @@ import type { InitialState } from '@react-navigation/routers';
2
2
 
3
3
  type Result =
4
4
  | {
5
- key?: string;
5
+ key?: string | undefined;
6
6
  name: string;
7
- params?: object;
8
- path?: string;
7
+ params?: object | undefined;
8
+ path?: string | undefined;
9
9
  }
10
10
  | undefined;
11
11
 
12
12
  export function findFocusedRoute(state: InitialState): Result {
13
13
  let current: InitialState | undefined = state;
14
14
 
15
- while (current?.routes[current.index ?? 0].state != null) {
16
- current = current.routes[current.index ?? 0].state;
17
- }
15
+ while (current != null) {
16
+ const route: InitialState['routes'][number] | undefined =
17
+ current.routes[current.index ?? 0];
18
+
19
+ if (route?.state == null) {
20
+ return route;
21
+ }
18
22
 
19
- const route = current?.routes[current?.index ?? 0];
23
+ current = route.state;
24
+ }
20
25
 
21
- return route;
26
+ return undefined;
22
27
  }
@@ -2,20 +2,18 @@ import type {
2
2
  CommonActions,
3
3
  NavigationState,
4
4
  ParamListBase,
5
- PartialRoute,
6
5
  PartialState,
7
- Route,
8
6
  } from '@react-navigation/routers';
9
7
 
10
8
  import type { NavigatorScreenParams, PathConfig, PathConfigMap } from './types';
11
9
 
12
10
  type ConfigItem = {
13
- initialRouteName?: string;
14
- screens?: Record<string, ConfigItem>;
11
+ initialRouteName?: string | undefined;
12
+ screens?: Record<string, ConfigItem> | undefined;
15
13
  };
16
14
 
17
15
  type Options = {
18
- initialRouteName?: string;
16
+ initialRouteName?: string | undefined;
19
17
  screens: PathConfigMap<object>;
20
18
  };
21
19
 
@@ -23,19 +21,35 @@ type NavigateAction<State extends NavigationState> = {
23
21
  type: 'NAVIGATE';
24
22
  payload: {
25
23
  name: string;
26
- params?: NavigatorScreenParams<State>;
27
- path?: string;
24
+ params?: NavigatorScreenParams<State> | undefined;
25
+ path?: string | undefined;
28
26
  };
29
27
  };
30
28
 
29
+ // Cache the normalized config across calls for the same `options` reference,
30
+ // so we don't re-walk the config tree on every action conversion.
31
+ const cachedNormalizedConfig = new WeakMap<Options, ConfigItem>();
32
+
31
33
  export function getActionFromState(
32
34
  state: PartialState<NavigationState>,
33
35
  options?: Options
34
36
  ): NavigateAction<NavigationState> | CommonActions.Action | undefined {
35
- // Create a normalized configs object which will be easier to use
36
- const normalizedConfig = options
37
- ? createNormalizedConfigItem(options as PathConfig<object> | string)
38
- : {};
37
+ let normalizedConfig;
38
+
39
+ if (options) {
40
+ const cached = cachedNormalizedConfig.get(options);
41
+
42
+ if (cached) {
43
+ normalizedConfig = cached;
44
+ } else {
45
+ normalizedConfig = createNormalizedConfigItem(
46
+ options as PathConfig<object> | string
47
+ );
48
+ cachedNormalizedConfig.set(options, normalizedConfig as ConfigItem);
49
+ }
50
+ } else {
51
+ normalizedConfig = {};
52
+ }
39
53
 
40
54
  const routes =
41
55
  state.index != null ? state.routes.slice(0, state.index + 1) : state.routes;
@@ -44,13 +58,21 @@ export function getActionFromState(
44
58
  return undefined;
45
59
  }
46
60
 
61
+ const firstRoute = routes[0];
62
+
63
+ if (firstRoute == null) {
64
+ return undefined;
65
+ }
66
+
67
+ const secondRoute = routes[1];
68
+
47
69
  if (
48
70
  !(
49
- (routes.length === 1 && routes[0].key === undefined) ||
71
+ (routes.length === 1 && firstRoute.key == null) ||
50
72
  (routes.length === 2 &&
51
- routes[0].key === undefined &&
52
- routes[0].name === normalizedConfig?.initialRouteName &&
53
- routes[1].key === undefined)
73
+ firstRoute.key == null &&
74
+ firstRoute.name === normalizedConfig?.initialRouteName &&
75
+ secondRoute?.key == null)
54
76
  )
55
77
  ) {
56
78
  return {
@@ -61,20 +83,20 @@ export function getActionFromState(
61
83
 
62
84
  const route = state.routes[state.index ?? state.routes.length - 1];
63
85
 
64
- let current: PartialState<NavigationState> | undefined = route?.state;
65
- let config: ConfigItem | undefined = normalizedConfig?.screens?.[route?.name];
86
+ if (route == null) {
87
+ return undefined;
88
+ }
89
+
90
+ let current: PartialState<NavigationState> | undefined = route.state;
91
+ let config: ConfigItem | undefined = normalizedConfig?.screens?.[route.name];
66
92
  let params = { ...route.params } as NavigatorScreenParams<ParamListBase>;
67
93
 
68
- const payload:
69
- | {
70
- name: string;
71
- params: NavigatorScreenParams<ParamListBase>;
72
- path?: string;
73
- pop?: boolean;
74
- }
75
- | undefined = route
76
- ? { name: route.name, path: route.path, params }
77
- : undefined;
94
+ const payload: {
95
+ name: string;
96
+ params: NavigatorScreenParams<ParamListBase>;
97
+ path?: string | undefined;
98
+ pop?: boolean | undefined;
99
+ } = { name: route.name, path: route.path, params };
78
100
 
79
101
  // If the screen contains a navigator, pop other screens to navigate to it
80
102
  // This avoid pushing multiple instances of navigators onto a stack
@@ -90,7 +112,7 @@ export function getActionFromState(
90
112
  // There are 2 ways we can detect if a screen contains a navigator:
91
113
  // - The route contains nested state in `route.state`
92
114
  // - Nested screens are defined in the config
93
- if (payload && config?.screens && Object.keys(config.screens).length) {
115
+ if (config?.screens && Object.keys(config.screens).length) {
94
116
  payload.pop = true;
95
117
  }
96
118
 
@@ -104,8 +126,19 @@ export function getActionFromState(
104
126
  ? current.routes.slice(0, current.index + 1)
105
127
  : current.routes;
106
128
 
107
- const route: Route<string> | PartialRoute<Route<string>> =
108
- routes[routes.length - 1];
129
+ const route = routes[routes.length - 1];
130
+
131
+ if (route == null) {
132
+ return undefined;
133
+ }
134
+
135
+ const firstRoute = routes[0];
136
+
137
+ if (firstRoute == null) {
138
+ return undefined;
139
+ }
140
+
141
+ const secondRoute = routes[1];
109
142
 
110
143
  // Explicitly set to override existing value when merging params
111
144
  Object.assign(params, {
@@ -115,14 +148,14 @@ export function getActionFromState(
115
148
  state: undefined,
116
149
  });
117
150
 
118
- if (routes.length === 1 && routes[0].key === undefined) {
151
+ if (routes.length === 1 && firstRoute.key == null) {
119
152
  params.initial = true;
120
153
  params.screen = route.name;
121
154
  } else if (
122
155
  routes.length === 2 &&
123
- routes[0].key === undefined &&
124
- routes[0].name === config?.initialRouteName &&
125
- routes[1].key === undefined
156
+ firstRoute.key == null &&
157
+ firstRoute.name === config?.initialRouteName &&
158
+ secondRoute?.key == null
126
159
  ) {
127
160
  params.initial = false;
128
161
  params.screen = route.name;
@@ -148,14 +181,10 @@ export function getActionFromState(
148
181
  }
149
182
  }
150
183
 
151
- if (payload?.params.screen || payload?.params.state) {
184
+ if (payload.params.screen || payload.params.state) {
152
185
  payload.pop = true;
153
186
  }
154
187
 
155
- if (!payload) {
156
- return;
157
- }
158
-
159
188
  // Try to construct payload for a `NAVIGATE` action from the state
160
189
  // This lets us preserve the navigation state and not lose it
161
190
  return {
@@ -1,17 +1,14 @@
1
- import type {
2
- NavigationState,
3
- PartialState,
4
- Route,
5
- } from '@react-navigation/routers';
6
- import * as queryString from 'query-string';
1
+ import type { NavigationState, PartialState } from '@react-navigation/routers';
2
+ import queryString from 'query-string';
7
3
 
8
4
  import { getPatternParts, type PatternPart } from './getPatternParts';
5
+ import { getStateFromRouteParams } from './getStateFromRouteParams';
9
6
  import type { PathConfig, PathConfigMap } from './types';
10
7
  import { validatePathConfig } from './validatePathConfig';
11
8
 
12
9
  type Options<ParamList extends {}> = {
13
- path?: string;
14
- initialRouteName?: string;
10
+ path?: string | undefined;
11
+ initialRouteName?: string | undefined;
15
12
  screens: PathConfigMap<ParamList>;
16
13
  };
17
14
 
@@ -20,17 +17,23 @@ type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
20
17
  type StringifyConfig = Record<string, ((value: unknown) => string) | undefined>;
21
18
 
22
19
  type ConfigItem = {
23
- parts?: PatternPart[];
24
- stringify?: StringifyConfig;
25
- screens?: Record<string, ConfigItem>;
20
+ parts?: PatternPart[] | undefined;
21
+ stringify?: StringifyConfig | undefined;
22
+ screens?: Record<string, ConfigItem> | undefined;
26
23
  };
27
24
 
28
- const getActiveRoute = (state: State): { name: string; params?: object } => {
25
+ const getActiveRoute = (
26
+ state: State
27
+ ): { name: string; params?: object | undefined } => {
29
28
  const route =
30
29
  typeof state.index === 'number'
31
30
  ? state.routes[state.index]
32
31
  : state.routes[state.routes.length - 1];
33
32
 
33
+ if (route == null) {
34
+ throw new Error(`Couldn't find the active route.`);
35
+ }
36
+
34
37
  if (route.state) {
35
38
  return getActiveRoute(route.state);
36
39
  }
@@ -57,6 +60,48 @@ const getNormalizedConfigs = (options?: Options<{}>) => {
57
60
  return normalizedConfigs;
58
61
  };
59
62
 
63
+ const getTransformedState = (
64
+ state: State,
65
+ configs: Record<string, ConfigItem> | undefined
66
+ ): Omit<PartialState<NavigationState>, 'stale'> => {
67
+ const routes = state.routes.map(
68
+ (route): Omit<PartialState<NavigationState>, 'stale'>['routes'][number] => {
69
+ const config = configs?.[route.name];
70
+
71
+ if (
72
+ route.state ||
73
+ (config?.screens &&
74
+ route.params &&
75
+ (('screen' in route.params &&
76
+ typeof route.params.screen === 'string' &&
77
+ config.screens[route.params.screen]) ||
78
+ 'state' in route.params))
79
+ ) {
80
+ const nestedState: State | undefined =
81
+ route.state ?? getStateFromRouteParams(route.params);
82
+
83
+ if (nestedState) {
84
+ return {
85
+ ...route,
86
+ state: getTransformedState(
87
+ nestedState,
88
+ configs?.[route.name]?.screens
89
+ ),
90
+ };
91
+ }
92
+ }
93
+
94
+ // @ts-expect-error route.state is handled in previous condition
95
+ return route;
96
+ }
97
+ );
98
+
99
+ return {
100
+ ...state,
101
+ routes,
102
+ };
103
+ };
104
+
60
105
  /**
61
106
  * Utility to serialize a navigation state object to a path string.
62
107
  *
@@ -101,37 +146,50 @@ export function getPathFromState<ParamList extends {}>(
101
146
  }
102
147
 
103
148
  const configs = getNormalizedConfigs(options);
149
+ const transformedState = getTransformedState(state, configs);
104
150
 
105
151
  let path = '/';
106
- let current: State | undefined = state;
152
+ let current: State | undefined = transformedState;
107
153
 
108
154
  const allParams: Record<string, string> = {};
109
155
 
110
156
  while (current) {
111
- let index = typeof current.index === 'number' ? current.index : 0;
112
- let route = current.routes[index] as Route<string> & {
113
- state?: State;
114
- };
157
+ let index: number = typeof current.index === 'number' ? current.index : 0;
158
+
159
+ const initialRoute: State['routes'][number] | undefined =
160
+ current.routes[index];
161
+
162
+ if (initialRoute == null) {
163
+ throw new Error(`Couldn't find a route at index ${index}.`);
164
+ }
165
+
166
+ let route: State['routes'][number] = initialRoute;
115
167
 
116
168
  let parts: PatternPart[] | undefined;
117
169
 
118
170
  let focusedParams: Record<string, string> | undefined;
119
171
  let currentOptions = configs;
120
172
 
121
- const focusedRoute = getActiveRoute(state);
173
+ const focusedRoute = getActiveRoute(transformedState);
122
174
 
123
175
  // Keep all the route names that appeared during going deeper in config in case the pattern is resolved to undefined
124
176
  const nestedRouteNames = [];
125
177
 
126
178
  let hasNext = true;
127
179
 
128
- while (route.name in currentOptions && hasNext) {
129
- parts = currentOptions[route.name].parts;
180
+ while (hasNext) {
181
+ const config = currentOptions[route.name];
182
+
183
+ if (config == null) {
184
+ break;
185
+ }
186
+
187
+ parts = config.parts;
130
188
 
131
189
  nestedRouteNames.push(route.name);
132
190
 
133
191
  if (route.params) {
134
- const options = currentOptions[route.name];
192
+ const options = config;
135
193
 
136
194
  const currentParams = Object.fromEntries(
137
195
  Object.entries(route.params)
@@ -181,7 +239,7 @@ export function getPathFromState<ParamList extends {}>(
181
239
  }
182
240
 
183
241
  // If there is no `screens` property or no nested state, we return pattern
184
- if (!currentOptions[route.name].screens || route.state === undefined) {
242
+ if (!config.screens || route.state == null) {
185
243
  hasNext = false;
186
244
  } else {
187
245
  index =
@@ -189,12 +247,18 @@ export function getPathFromState<ParamList extends {}>(
189
247
  ? route.state.index
190
248
  : route.state.routes.length - 1;
191
249
 
192
- const nextRoute = route.state.routes[index];
193
- const nestedConfig = currentOptions[route.name].screens;
250
+ const nextRoute: State['routes'][number] | undefined =
251
+ route.state.routes[index];
252
+
253
+ if (nextRoute == null) {
254
+ throw new Error(`Couldn't find a route at index ${index}.`);
255
+ }
256
+
257
+ const nestedConfig = config.screens;
194
258
 
195
259
  // if there is config for next route name, we go deeper
196
- if (nestedConfig && nextRoute.name in nestedConfig) {
197
- route = nextRoute as Route<string> & { state?: State };
260
+ if (nextRoute.name in nestedConfig) {
261
+ route = nextRoute;
198
262
  currentOptions = nestedConfig;
199
263
  } else {
200
264
  // If not, there is no sense in going deeper in config
@@ -203,7 +267,7 @@ export function getPathFromState<ParamList extends {}>(
203
267
  }
204
268
  }
205
269
 
206
- if (currentOptions[route.name] !== undefined) {
270
+ if (currentOptions[route.name] != null) {
207
271
  path += parts
208
272
  ?.map(({ segment, param, optional }) => {
209
273
  // We don't know what to show for wildcard patterns
@@ -124,3 +124,11 @@ export function getPatternParts(path: string): PatternPart[] {
124
124
 
125
125
  return parts;
126
126
  }
127
+
128
+ export function combinePatternParts<T extends PatternPart>(
129
+ parentParts: T[],
130
+ parts: T[],
131
+ exact = false
132
+ ): T[] {
133
+ return exact ? parts : [...parentParts, ...parts];
134
+ }