@react-navigation/core 6.0.2

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 (327) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +38 -0
  3. package/lib/commonjs/BaseNavigationContainer.js +393 -0
  4. package/lib/commonjs/BaseNavigationContainer.js.map +1 -0
  5. package/lib/commonjs/CurrentRenderContext.js +21 -0
  6. package/lib/commonjs/CurrentRenderContext.js.map +1 -0
  7. package/lib/commonjs/EnsureSingleNavigator.js +53 -0
  8. package/lib/commonjs/EnsureSingleNavigator.js.map +1 -0
  9. package/lib/commonjs/Group.js +15 -0
  10. package/lib/commonjs/Group.js.map +1 -0
  11. package/lib/commonjs/NavigationBuilderContext.js +23 -0
  12. package/lib/commonjs/NavigationBuilderContext.js.map +1 -0
  13. package/lib/commonjs/NavigationContainerRefContext.js +20 -0
  14. package/lib/commonjs/NavigationContainerRefContext.js.map +1 -0
  15. package/lib/commonjs/NavigationContext.js +20 -0
  16. package/lib/commonjs/NavigationContext.js.map +1 -0
  17. package/lib/commonjs/NavigationHelpersContext.js +21 -0
  18. package/lib/commonjs/NavigationHelpersContext.js.map +1 -0
  19. package/lib/commonjs/NavigationRouteContext.js +20 -0
  20. package/lib/commonjs/NavigationRouteContext.js.map +1 -0
  21. package/lib/commonjs/NavigationStateContext.js +42 -0
  22. package/lib/commonjs/NavigationStateContext.js.map +1 -0
  23. package/lib/commonjs/SceneView.js +97 -0
  24. package/lib/commonjs/SceneView.js.map +1 -0
  25. package/lib/commonjs/Screen.js +15 -0
  26. package/lib/commonjs/Screen.js.map +1 -0
  27. package/lib/commonjs/StaticContainer.js +43 -0
  28. package/lib/commonjs/StaticContainer.js.map +1 -0
  29. package/lib/commonjs/UnhandledActionContext.js +17 -0
  30. package/lib/commonjs/UnhandledActionContext.js.map +1 -0
  31. package/lib/commonjs/checkDuplicateRouteNames.js +31 -0
  32. package/lib/commonjs/checkDuplicateRouteNames.js.map +1 -0
  33. package/lib/commonjs/checkSerializable.js +59 -0
  34. package/lib/commonjs/checkSerializable.js.map +1 -0
  35. package/lib/commonjs/createNavigationContainerRef.js +82 -0
  36. package/lib/commonjs/createNavigationContainerRef.js.map +1 -0
  37. package/lib/commonjs/createNavigatorFactory.js +34 -0
  38. package/lib/commonjs/createNavigatorFactory.js.map +1 -0
  39. package/lib/commonjs/findFocusedRoute.js +22 -0
  40. package/lib/commonjs/findFocusedRoute.js.map +1 -0
  41. package/lib/commonjs/fromEntries.js +19 -0
  42. package/lib/commonjs/fromEntries.js.map +1 -0
  43. package/lib/commonjs/getActionFromState.js +99 -0
  44. package/lib/commonjs/getActionFromState.js.map +1 -0
  45. package/lib/commonjs/getFocusedRouteNameFromRoute.js +23 -0
  46. package/lib/commonjs/getFocusedRouteNameFromRoute.js.map +1 -0
  47. package/lib/commonjs/getPathFromState.js +238 -0
  48. package/lib/commonjs/getPathFromState.js.map +1 -0
  49. package/lib/commonjs/getStateFromPath.js +450 -0
  50. package/lib/commonjs/getStateFromPath.js.map +1 -0
  51. package/lib/commonjs/index.js +227 -0
  52. package/lib/commonjs/index.js.map +1 -0
  53. package/lib/commonjs/isArrayEqual.js +15 -0
  54. package/lib/commonjs/isArrayEqual.js.map +1 -0
  55. package/lib/commonjs/types.js +18 -0
  56. package/lib/commonjs/types.js.map +1 -0
  57. package/lib/commonjs/useChildListeners.js +38 -0
  58. package/lib/commonjs/useChildListeners.js.map +1 -0
  59. package/lib/commonjs/useComponent.js +36 -0
  60. package/lib/commonjs/useComponent.js.map +1 -0
  61. package/lib/commonjs/useCurrentRender.js +33 -0
  62. package/lib/commonjs/useCurrentRender.js.map +1 -0
  63. package/lib/commonjs/useDescriptors.js +141 -0
  64. package/lib/commonjs/useDescriptors.js.map +1 -0
  65. package/lib/commonjs/useEventEmitter.js +118 -0
  66. package/lib/commonjs/useEventEmitter.js.map +1 -0
  67. package/lib/commonjs/useFocusEffect.js +97 -0
  68. package/lib/commonjs/useFocusEffect.js.map +1 -0
  69. package/lib/commonjs/useFocusEvents.js +77 -0
  70. package/lib/commonjs/useFocusEvents.js.map +1 -0
  71. package/lib/commonjs/useFocusedListenersChildrenAdapter.js +57 -0
  72. package/lib/commonjs/useFocusedListenersChildrenAdapter.js.map +1 -0
  73. package/lib/commonjs/useIsFocused.js +47 -0
  74. package/lib/commonjs/useIsFocused.js.map +1 -0
  75. package/lib/commonjs/useKeyedChildListeners.js +35 -0
  76. package/lib/commonjs/useKeyedChildListeners.js.map +1 -0
  77. package/lib/commonjs/useNavigation.js +36 -0
  78. package/lib/commonjs/useNavigation.js.map +1 -0
  79. package/lib/commonjs/useNavigationBuilder.js +480 -0
  80. package/lib/commonjs/useNavigationBuilder.js.map +1 -0
  81. package/lib/commonjs/useNavigationCache.js +122 -0
  82. package/lib/commonjs/useNavigationCache.js.map +1 -0
  83. package/lib/commonjs/useNavigationContainerRef.js +27 -0
  84. package/lib/commonjs/useNavigationContainerRef.js.map +1 -0
  85. package/lib/commonjs/useNavigationHelpers.js +77 -0
  86. package/lib/commonjs/useNavigationHelpers.js.map +1 -0
  87. package/lib/commonjs/useNavigationState.js +41 -0
  88. package/lib/commonjs/useNavigationState.js.map +1 -0
  89. package/lib/commonjs/useOnAction.js +118 -0
  90. package/lib/commonjs/useOnAction.js.map +1 -0
  91. package/lib/commonjs/useOnGetState.js +60 -0
  92. package/lib/commonjs/useOnGetState.js.map +1 -0
  93. package/lib/commonjs/useOnPreventRemove.js +89 -0
  94. package/lib/commonjs/useOnPreventRemove.js.map +1 -0
  95. package/lib/commonjs/useOnRouteFocus.js +45 -0
  96. package/lib/commonjs/useOnRouteFocus.js.map +1 -0
  97. package/lib/commonjs/useOptionsGetters.js +99 -0
  98. package/lib/commonjs/useOptionsGetters.js.map +1 -0
  99. package/lib/commonjs/useRegisterNavigator.js +40 -0
  100. package/lib/commonjs/useRegisterNavigator.js.map +1 -0
  101. package/lib/commonjs/useRoute.js +32 -0
  102. package/lib/commonjs/useRoute.js.map +1 -0
  103. package/lib/commonjs/useRouteCache.js +61 -0
  104. package/lib/commonjs/useRouteCache.js.map +1 -0
  105. package/lib/commonjs/useScheduleUpdate.js +44 -0
  106. package/lib/commonjs/useScheduleUpdate.js.map +1 -0
  107. package/lib/commonjs/useSyncState.js +76 -0
  108. package/lib/commonjs/useSyncState.js.map +1 -0
  109. package/lib/commonjs/validatePathConfig.js +31 -0
  110. package/lib/commonjs/validatePathConfig.js.map +1 -0
  111. package/lib/module/BaseNavigationContainer.js +360 -0
  112. package/lib/module/BaseNavigationContainer.js.map +1 -0
  113. package/lib/module/CurrentRenderContext.js +9 -0
  114. package/lib/module/CurrentRenderContext.js.map +1 -0
  115. package/lib/module/EnsureSingleNavigator.js +38 -0
  116. package/lib/module/EnsureSingleNavigator.js.map +1 -0
  117. package/lib/module/Group.js +8 -0
  118. package/lib/module/Group.js.map +1 -0
  119. package/lib/module/NavigationBuilderContext.js +11 -0
  120. package/lib/module/NavigationBuilderContext.js.map +1 -0
  121. package/lib/module/NavigationContainerRefContext.js +8 -0
  122. package/lib/module/NavigationContainerRefContext.js.map +1 -0
  123. package/lib/module/NavigationContext.js +8 -0
  124. package/lib/module/NavigationContext.js.map +1 -0
  125. package/lib/module/NavigationHelpersContext.js +9 -0
  126. package/lib/module/NavigationHelpersContext.js.map +1 -0
  127. package/lib/module/NavigationRouteContext.js +8 -0
  128. package/lib/module/NavigationRouteContext.js.map +1 -0
  129. package/lib/module/NavigationStateContext.js +27 -0
  130. package/lib/module/NavigationStateContext.js.map +1 -0
  131. package/lib/module/SceneView.js +80 -0
  132. package/lib/module/SceneView.js.map +1 -0
  133. package/lib/module/Screen.js +8 -0
  134. package/lib/module/Screen.js.map +1 -0
  135. package/lib/module/StaticContainer.js +30 -0
  136. package/lib/module/StaticContainer.js.map +1 -0
  137. package/lib/module/UnhandledActionContext.js +4 -0
  138. package/lib/module/UnhandledActionContext.js.map +1 -0
  139. package/lib/module/checkDuplicateRouteNames.js +24 -0
  140. package/lib/module/checkDuplicateRouteNames.js.map +1 -0
  141. package/lib/module/checkSerializable.js +52 -0
  142. package/lib/module/checkSerializable.js.map +1 -0
  143. package/lib/module/createNavigationContainerRef.js +71 -0
  144. package/lib/module/createNavigationContainerRef.js.map +1 -0
  145. package/lib/module/createNavigatorFactory.js +24 -0
  146. package/lib/module/createNavigatorFactory.js.map +1 -0
  147. package/lib/module/findFocusedRoute.js +15 -0
  148. package/lib/module/findFocusedRoute.js.map +1 -0
  149. package/lib/module/fromEntries.js +12 -0
  150. package/lib/module/fromEntries.js.map +1 -0
  151. package/lib/module/getActionFromState.js +92 -0
  152. package/lib/module/getActionFromState.js.map +1 -0
  153. package/lib/module/getFocusedRouteNameFromRoute.js +15 -0
  154. package/lib/module/getFocusedRouteNameFromRoute.js.map +1 -0
  155. package/lib/module/getPathFromState.js +223 -0
  156. package/lib/module/getPathFromState.js.map +1 -0
  157. package/lib/module/getStateFromPath.js +434 -0
  158. package/lib/module/getStateFromPath.js.map +1 -0
  159. package/lib/module/index.js +24 -0
  160. package/lib/module/index.js.map +1 -0
  161. package/lib/module/isArrayEqual.js +8 -0
  162. package/lib/module/isArrayEqual.js.map +1 -0
  163. package/lib/module/types.js +9 -0
  164. package/lib/module/types.js.map +1 -0
  165. package/lib/module/useChildListeners.js +27 -0
  166. package/lib/module/useChildListeners.js.map +1 -0
  167. package/lib/module/useComponent.js +24 -0
  168. package/lib/module/useComponent.js.map +1 -0
  169. package/lib/module/useCurrentRender.js +19 -0
  170. package/lib/module/useCurrentRender.js.map +1 -0
  171. package/lib/module/useDescriptors.js +122 -0
  172. package/lib/module/useDescriptors.js.map +1 -0
  173. package/lib/module/useEventEmitter.js +107 -0
  174. package/lib/module/useEventEmitter.js.map +1 -0
  175. package/lib/module/useFocusEffect.js +83 -0
  176. package/lib/module/useFocusEffect.js.map +1 -0
  177. package/lib/module/useFocusEvents.js +63 -0
  178. package/lib/module/useFocusEvents.js.map +1 -0
  179. package/lib/module/useFocusedListenersChildrenAdapter.js +43 -0
  180. package/lib/module/useFocusedListenersChildrenAdapter.js.map +1 -0
  181. package/lib/module/useIsFocused.js +34 -0
  182. package/lib/module/useIsFocused.js.map +1 -0
  183. package/lib/module/useKeyedChildListeners.js +24 -0
  184. package/lib/module/useKeyedChildListeners.js.map +1 -0
  185. package/lib/module/useNavigation.js +21 -0
  186. package/lib/module/useNavigation.js.map +1 -0
  187. package/lib/module/useNavigationBuilder.js +443 -0
  188. package/lib/module/useNavigationBuilder.js.map +1 -0
  189. package/lib/module/useNavigationCache.js +107 -0
  190. package/lib/module/useNavigationCache.js.map +1 -0
  191. package/lib/module/useNavigationContainerRef.js +12 -0
  192. package/lib/module/useNavigationContainerRef.js.map +1 -0
  193. package/lib/module/useNavigationHelpers.js +59 -0
  194. package/lib/module/useNavigationHelpers.js.map +1 -0
  195. package/lib/module/useNavigationState.js +27 -0
  196. package/lib/module/useNavigationState.js.map +1 -0
  197. package/lib/module/useOnAction.js +103 -0
  198. package/lib/module/useOnAction.js.map +1 -0
  199. package/lib/module/useOnGetState.js +43 -0
  200. package/lib/module/useOnGetState.js.map +1 -0
  201. package/lib/module/useOnPreventRemove.js +68 -0
  202. package/lib/module/useOnPreventRemove.js.map +1 -0
  203. package/lib/module/useOnRouteFocus.js +31 -0
  204. package/lib/module/useOnRouteFocus.js.map +1 -0
  205. package/lib/module/useOptionsGetters.js +83 -0
  206. package/lib/module/useOptionsGetters.js.map +1 -0
  207. package/lib/module/useRegisterNavigator.js +27 -0
  208. package/lib/module/useRegisterNavigator.js.map +1 -0
  209. package/lib/module/useRoute.js +18 -0
  210. package/lib/module/useRoute.js.map +1 -0
  211. package/lib/module/useRouteCache.js +47 -0
  212. package/lib/module/useRouteCache.js.map +1 -0
  213. package/lib/module/useScheduleUpdate.js +29 -0
  214. package/lib/module/useScheduleUpdate.js.map +1 -0
  215. package/lib/module/useSyncState.js +64 -0
  216. package/lib/module/useSyncState.js.map +1 -0
  217. package/lib/module/validatePathConfig.js +24 -0
  218. package/lib/module/validatePathConfig.js.map +1 -0
  219. package/lib/typescript/src/BaseNavigationContainer.d.ts +14 -0
  220. package/lib/typescript/src/CurrentRenderContext.d.ts +9 -0
  221. package/lib/typescript/src/EnsureSingleNavigator.d.ts +13 -0
  222. package/lib/typescript/src/Group.d.ts +6 -0
  223. package/lib/typescript/src/NavigationBuilderContext.d.ts +34 -0
  224. package/lib/typescript/src/NavigationContainerRefContext.d.ts +8 -0
  225. package/lib/typescript/src/NavigationContext.d.ts +24 -0
  226. package/lib/typescript/src/NavigationHelpersContext.d.ts +9 -0
  227. package/lib/typescript/src/NavigationRouteContext.d.ts +7 -0
  228. package/lib/typescript/src/NavigationStateContext.d.ts +45 -0
  229. package/lib/typescript/src/SceneView.d.ts +21 -0
  230. package/lib/typescript/src/Screen.d.ts +6 -0
  231. package/lib/typescript/src/StaticContainer.d.ts +7 -0
  232. package/lib/typescript/src/UnhandledActionContext.d.ts +4 -0
  233. package/lib/typescript/src/checkDuplicateRouteNames.d.ts +2 -0
  234. package/lib/typescript/src/checkSerializable.d.ts +9 -0
  235. package/lib/typescript/src/createNavigationContainerRef.d.ts +3 -0
  236. package/lib/typescript/src/createNavigatorFactory.d.ts +11 -0
  237. package/lib/typescript/src/findFocusedRoute.d.ts +22 -0
  238. package/lib/typescript/src/fromEntries.d.ts +1 -0
  239. package/lib/typescript/src/getActionFromState.d.ts +16 -0
  240. package/lib/typescript/src/getFocusedRouteNameFromRoute.d.ts +2 -0
  241. package/lib/typescript/src/getPathFromState.d.ts +38 -0
  242. package/lib/typescript/src/getStateFromPath.d.ts +32 -0
  243. package/lib/typescript/src/index.d.ts +23 -0
  244. package/lib/typescript/src/isArrayEqual.d.ts +5 -0
  245. package/lib/typescript/src/types.d.ts +485 -0
  246. package/lib/typescript/src/useChildListeners.d.ts +11 -0
  247. package/lib/typescript/src/useComponent.d.ts +2 -0
  248. package/lib/typescript/src/useCurrentRender.d.ts +13 -0
  249. package/lib/typescript/src/useDescriptors.d.ts +89 -0
  250. package/lib/typescript/src/useEventEmitter.d.ts +8 -0
  251. package/lib/typescript/src/useFocusEffect.d.ts +10 -0
  252. package/lib/typescript/src/useFocusEvents.d.ts +12 -0
  253. package/lib/typescript/src/useFocusedListenersChildrenAdapter.d.ts +12 -0
  254. package/lib/typescript/src/useIsFocused.d.ts +5 -0
  255. package/lib/typescript/src/useKeyedChildListeners.d.ts +11 -0
  256. package/lib/typescript/src/useNavigation.d.ts +7 -0
  257. package/lib/typescript/src/useNavigationBuilder.d.ts +223 -0
  258. package/lib/typescript/src/useNavigationCache.d.ts +19 -0
  259. package/lib/typescript/src/useNavigationContainerRef.d.ts +2 -0
  260. package/lib/typescript/src/useNavigationHelpers.d.ts +174 -0
  261. package/lib/typescript/src/useNavigationState.d.ts +9 -0
  262. package/lib/typescript/src/useOnAction.d.ts +25 -0
  263. package/lib/typescript/src/useOnGetState.d.ts +8 -0
  264. package/lib/typescript/src/useOnPreventRemove.d.ts +16 -0
  265. package/lib/typescript/src/useOnRouteFocus.d.ts +14 -0
  266. package/lib/typescript/src/useOptionsGetters.d.ts +12 -0
  267. package/lib/typescript/src/useRegisterNavigator.d.ts +5 -0
  268. package/lib/typescript/src/useRoute.d.ts +8 -0
  269. package/lib/typescript/src/useRouteCache.d.ts +13 -0
  270. package/lib/typescript/src/useScheduleUpdate.d.ts +13 -0
  271. package/lib/typescript/src/useSyncState.d.ts +4 -0
  272. package/lib/typescript/src/validatePathConfig.d.ts +1 -0
  273. package/package.json +73 -0
  274. package/src/BaseNavigationContainer.tsx +453 -0
  275. package/src/CurrentRenderContext.tsx +10 -0
  276. package/src/EnsureSingleNavigator.tsx +53 -0
  277. package/src/Group.tsx +14 -0
  278. package/src/NavigationBuilderContext.tsx +70 -0
  279. package/src/NavigationContainerRefContext.tsx +14 -0
  280. package/src/NavigationContext.tsx +12 -0
  281. package/src/NavigationHelpersContext.tsx +13 -0
  282. package/src/NavigationRouteContext.tsx +10 -0
  283. package/src/NavigationStateContext.tsx +39 -0
  284. package/src/SceneView.tsx +134 -0
  285. package/src/Screen.tsx +17 -0
  286. package/src/StaticContainer.tsx +29 -0
  287. package/src/UnhandledActionContext.tsx +9 -0
  288. package/src/checkDuplicateRouteNames.tsx +33 -0
  289. package/src/checkSerializable.tsx +74 -0
  290. package/src/createNavigationContainerRef.tsx +97 -0
  291. package/src/createNavigatorFactory.tsx +40 -0
  292. package/src/findFocusedRoute.tsx +13 -0
  293. package/src/fromEntries.tsx +13 -0
  294. package/src/getActionFromState.tsx +154 -0
  295. package/src/getFocusedRouteNameFromRoute.tsx +28 -0
  296. package/src/getPathFromState.tsx +297 -0
  297. package/src/getStateFromPath.tsx +575 -0
  298. package/src/index.tsx +23 -0
  299. package/src/isArrayEqual.tsx +7 -0
  300. package/src/types.tsx +645 -0
  301. package/src/useChildListeners.tsx +37 -0
  302. package/src/useComponent.tsx +30 -0
  303. package/src/useCurrentRender.tsx +35 -0
  304. package/src/useDescriptors.tsx +229 -0
  305. package/src/useEventEmitter.tsx +130 -0
  306. package/src/useFocusEffect.tsx +112 -0
  307. package/src/useFocusEvents.tsx +73 -0
  308. package/src/useFocusedListenersChildrenAdapter.tsx +47 -0
  309. package/src/useIsFocused.tsx +43 -0
  310. package/src/useKeyedChildListeners.tsx +40 -0
  311. package/src/useNavigation.tsx +26 -0
  312. package/src/useNavigationBuilder.tsx +640 -0
  313. package/src/useNavigationCache.tsx +159 -0
  314. package/src/useNavigationContainerRef.tsx +17 -0
  315. package/src/useNavigationHelpers.tsx +98 -0
  316. package/src/useNavigationState.tsx +41 -0
  317. package/src/useOnAction.tsx +166 -0
  318. package/src/useOnGetState.tsx +47 -0
  319. package/src/useOnPreventRemove.tsx +99 -0
  320. package/src/useOnRouteFocus.tsx +47 -0
  321. package/src/useOptionsGetters.tsx +98 -0
  322. package/src/useRegisterNavigator.tsx +29 -0
  323. package/src/useRoute.tsx +22 -0
  324. package/src/useRouteCache.tsx +55 -0
  325. package/src/useScheduleUpdate.tsx +32 -0
  326. package/src/useSyncState.tsx +74 -0
  327. package/src/validatePathConfig.tsx +32 -0
@@ -0,0 +1,575 @@
1
+ import type {
2
+ InitialState,
3
+ NavigationState,
4
+ PartialState,
5
+ } from '@react-navigation/routers';
6
+ import escape from 'escape-string-regexp';
7
+ import * as queryString from 'query-string';
8
+
9
+ import findFocusedRoute from './findFocusedRoute';
10
+ import type { PathConfigMap } from './types';
11
+ import validatePathConfig from './validatePathConfig';
12
+
13
+ type Options<ParamList extends {}> = {
14
+ initialRouteName?: string;
15
+ screens: PathConfigMap<ParamList>;
16
+ };
17
+
18
+ type ParseConfig = Record<string, (value: string) => any>;
19
+
20
+ type RouteConfig = {
21
+ screen: string;
22
+ regex?: RegExp;
23
+ path: string;
24
+ pattern: string;
25
+ routeNames: string[];
26
+ parse?: ParseConfig;
27
+ };
28
+
29
+ type InitialRouteConfig = {
30
+ initialRouteName: string;
31
+ parentScreens: string[];
32
+ };
33
+
34
+ type ResultState = PartialState<NavigationState> & {
35
+ state?: ResultState;
36
+ };
37
+
38
+ type ParsedRoute = {
39
+ name: string;
40
+ path?: string;
41
+ params?: Record<string, any> | undefined;
42
+ };
43
+
44
+ /**
45
+ * Utility to parse a path string to initial state object accepted by the container.
46
+ * This is useful for deep linking when we need to handle the incoming URL.
47
+ *
48
+ * @example
49
+ * ```js
50
+ * getStateFromPath(
51
+ * '/chat/jane/42',
52
+ * {
53
+ * screens: {
54
+ * Chat: {
55
+ * path: 'chat/:author/:id',
56
+ * parse: { id: Number }
57
+ * }
58
+ * }
59
+ * }
60
+ * )
61
+ * ```
62
+ * @param path Path string to parse and convert, e.g. /foo/bar?count=42.
63
+ * @param options Extra options to fine-tune how to parse the path.
64
+ */
65
+ export default function getStateFromPath<ParamList extends {}>(
66
+ path: string,
67
+ options?: Options<ParamList>
68
+ ): ResultState | undefined {
69
+ if (options) {
70
+ validatePathConfig(options);
71
+ }
72
+
73
+ let initialRoutes: InitialRouteConfig[] = [];
74
+
75
+ if (options?.initialRouteName) {
76
+ initialRoutes.push({
77
+ initialRouteName: options.initialRouteName,
78
+ parentScreens: [],
79
+ });
80
+ }
81
+
82
+ const screens = options?.screens;
83
+
84
+ let remaining = path
85
+ .replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
86
+ .replace(/^\//, '') // Remove extra leading slash
87
+ .replace(/\?.*$/, ''); // Remove query params which we will handle later
88
+
89
+ // Make sure there is a trailing slash
90
+ remaining = remaining.endsWith('/') ? remaining : `${remaining}/`;
91
+
92
+ if (screens === undefined) {
93
+ // When no config is specified, use the path segments as route names
94
+ const routes = remaining
95
+ .split('/')
96
+ .filter(Boolean)
97
+ .map((segment) => {
98
+ const name = decodeURIComponent(segment);
99
+ return { name };
100
+ });
101
+
102
+ if (routes.length) {
103
+ return createNestedStateObject(path, routes, initialRoutes);
104
+ }
105
+
106
+ return undefined;
107
+ }
108
+
109
+ // Create a normalized configs array which will be easier to use
110
+ const configs = ([] as RouteConfig[])
111
+ .concat(
112
+ ...Object.keys(screens).map((key) =>
113
+ createNormalizedConfigs(
114
+ key,
115
+ screens as PathConfigMap<object>,
116
+ [],
117
+ initialRoutes,
118
+ []
119
+ )
120
+ )
121
+ )
122
+ .sort((a, b) => {
123
+ // Sort config so that:
124
+ // - the most exhaustive ones are always at the beginning
125
+ // - patterns with wildcard are always at the end
126
+
127
+ // If 2 patterns are same, move the one with less route names up
128
+ // This is an error state, so it's only useful for consistent error messages
129
+ if (a.pattern === b.pattern) {
130
+ return b.routeNames.join('>').localeCompare(a.routeNames.join('>'));
131
+ }
132
+
133
+ // If one of the patterns starts with the other, it's more exhaustive
134
+ // So move it up
135
+ if (a.pattern.startsWith(b.pattern)) {
136
+ return -1;
137
+ }
138
+
139
+ if (b.pattern.startsWith(a.pattern)) {
140
+ return 1;
141
+ }
142
+
143
+ const aParts = a.pattern.split('/');
144
+ const bParts = b.pattern.split('/');
145
+
146
+ for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
147
+ // if b is longer, b get higher priority
148
+ if (aParts[i] == null) {
149
+ return 1;
150
+ }
151
+ // if a is longer, a get higher priority
152
+ if (bParts[i] == null) {
153
+ return -1;
154
+ }
155
+ const aWildCard = aParts[i] === '*' || aParts[i].startsWith(':');
156
+ const bWildCard = bParts[i] === '*' || bParts[i].startsWith(':');
157
+ // if both are wildcard we compare next component
158
+ if (aWildCard && bWildCard) {
159
+ continue;
160
+ }
161
+ // if only a is wild card, b get higher priority
162
+ if (aWildCard) {
163
+ return 1;
164
+ }
165
+ // if only b is wild card, a get higher priority
166
+ if (bWildCard) {
167
+ return -1;
168
+ }
169
+ }
170
+ return bParts.length - aParts.length;
171
+ });
172
+
173
+ // Check for duplicate patterns in the config
174
+ configs.reduce<Record<string, RouteConfig>>((acc, config) => {
175
+ if (acc[config.pattern]) {
176
+ const a = acc[config.pattern].routeNames;
177
+ const b = config.routeNames;
178
+
179
+ // It's not a problem if the path string omitted from a inner most screen
180
+ // For example, it's ok if a path resolves to `A > B > C` or `A > B`
181
+ const intersects =
182
+ a.length > b.length
183
+ ? b.every((it, i) => a[i] === it)
184
+ : a.every((it, i) => b[i] === it);
185
+
186
+ if (!intersects) {
187
+ throw new Error(
188
+ `Found conflicting screens with the same pattern. The pattern '${
189
+ config.pattern
190
+ }' resolves to both '${a.join(' > ')}' and '${b.join(
191
+ ' > '
192
+ )}'. Patterns must be unique and cannot resolve to more than one screen.`
193
+ );
194
+ }
195
+ }
196
+
197
+ return Object.assign(acc, {
198
+ [config.pattern]: config,
199
+ });
200
+ }, {});
201
+
202
+ if (remaining === '/') {
203
+ // We need to add special handling of empty path so navigation to empty path also works
204
+ // When handling empty path, we should only look at the root level config
205
+ const match = configs.find(
206
+ (config) =>
207
+ config.path === '' &&
208
+ config.routeNames.every(
209
+ // Make sure that none of the parent configs have a non-empty path defined
210
+ (name) => !configs.find((c) => c.screen === name)?.path
211
+ )
212
+ );
213
+
214
+ if (match) {
215
+ return createNestedStateObject(
216
+ path,
217
+ match.routeNames.map((name) => ({ name })),
218
+ initialRoutes,
219
+ configs
220
+ );
221
+ }
222
+
223
+ return undefined;
224
+ }
225
+
226
+ let result: PartialState<NavigationState> | undefined;
227
+ let current: PartialState<NavigationState> | undefined;
228
+
229
+ // We match the whole path against the regex instead of segments
230
+ // This makes sure matches such as wildcard will catch any unmatched routes, even if nested
231
+ const { routes, remainingPath } = matchAgainstConfigs(
232
+ remaining,
233
+ configs.map((c) => ({
234
+ ...c,
235
+ // Add `$` to the regex to make sure it matches till end of the path and not just beginning
236
+ regex: c.regex ? new RegExp(c.regex.source + '$') : undefined,
237
+ }))
238
+ );
239
+
240
+ if (routes !== undefined) {
241
+ // This will always be empty if full path matched
242
+ current = createNestedStateObject(path, routes, initialRoutes, configs);
243
+ remaining = remainingPath;
244
+ result = current;
245
+ }
246
+
247
+ if (current == null || result == null) {
248
+ return undefined;
249
+ }
250
+
251
+ return result;
252
+ }
253
+
254
+ const joinPaths = (...paths: string[]): string =>
255
+ ([] as string[])
256
+ .concat(...paths.map((p) => p.split('/')))
257
+ .filter(Boolean)
258
+ .join('/');
259
+
260
+ const matchAgainstConfigs = (remaining: string, configs: RouteConfig[]) => {
261
+ let routes: ParsedRoute[] | undefined;
262
+ let remainingPath = remaining;
263
+
264
+ // Go through all configs, and see if the next path segment matches our regex
265
+ for (const config of configs) {
266
+ if (!config.regex) {
267
+ continue;
268
+ }
269
+
270
+ const match = remainingPath.match(config.regex);
271
+
272
+ // If our regex matches, we need to extract params from the path
273
+ if (match) {
274
+ const matchedParams = config.pattern
275
+ ?.split('/')
276
+ .filter((p) => p.startsWith(':'))
277
+ .reduce<Record<string, any>>(
278
+ (acc, p, i) =>
279
+ Object.assign(acc, {
280
+ // The param segments appear every second item starting from 2 in the regex match result
281
+ [p]: match![(i + 1) * 2].replace(/\//, ''),
282
+ }),
283
+ {}
284
+ );
285
+
286
+ routes = config.routeNames.map((name) => {
287
+ const config = configs.find((c) => c.screen === name);
288
+ const params = config?.path
289
+ ?.split('/')
290
+ .filter((p) => p.startsWith(':'))
291
+ .reduce<Record<string, any>>((acc, p) => {
292
+ const value = matchedParams[p];
293
+
294
+ if (value) {
295
+ const key = p.replace(/^:/, '').replace(/\?$/, '');
296
+ acc[key] = config.parse?.[key] ? config.parse[key](value) : value;
297
+ }
298
+
299
+ return acc;
300
+ }, {});
301
+
302
+ if (params && Object.keys(params).length) {
303
+ return { name, params };
304
+ }
305
+
306
+ return { name };
307
+ });
308
+
309
+ remainingPath = remainingPath.replace(match[1], '');
310
+
311
+ break;
312
+ }
313
+ }
314
+
315
+ return { routes, remainingPath };
316
+ };
317
+
318
+ const createNormalizedConfigs = (
319
+ screen: string,
320
+ routeConfig: PathConfigMap<object>,
321
+ routeNames: string[] = [],
322
+ initials: InitialRouteConfig[],
323
+ parentScreens: string[],
324
+ parentPattern?: string
325
+ ): RouteConfig[] => {
326
+ const configs: RouteConfig[] = [];
327
+
328
+ routeNames.push(screen);
329
+
330
+ parentScreens.push(screen);
331
+
332
+ // @ts-expect-error: we can't strongly typecheck this for now
333
+ const config = routeConfig[screen];
334
+
335
+ if (typeof config === 'string') {
336
+ // If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
337
+ const pattern = parentPattern ? joinPaths(parentPattern, config) : config;
338
+
339
+ configs.push(createConfigItem(screen, routeNames, pattern, config));
340
+ } else if (typeof config === 'object') {
341
+ let pattern: string | undefined;
342
+
343
+ // if an object is specified as the value (e.g. Foo: { ... }),
344
+ // it can have `path` property and
345
+ // it could have `screens` prop which has nested configs
346
+ if (typeof config.path === 'string') {
347
+ if (config.exact && config.path === undefined) {
348
+ throw new Error(
349
+ "A 'path' needs to be specified when specifying 'exact: true'. If you don't want this screen in the URL, specify it as empty string, e.g. `path: ''`."
350
+ );
351
+ }
352
+
353
+ pattern =
354
+ config.exact !== true
355
+ ? joinPaths(parentPattern || '', config.path || '')
356
+ : config.path || '';
357
+
358
+ configs.push(
359
+ createConfigItem(
360
+ screen,
361
+ routeNames,
362
+ pattern!,
363
+ config.path,
364
+ config.parse
365
+ )
366
+ );
367
+ }
368
+
369
+ if (config.screens) {
370
+ // property `initialRouteName` without `screens` has no purpose
371
+ if (config.initialRouteName) {
372
+ initials.push({
373
+ initialRouteName: config.initialRouteName,
374
+ parentScreens,
375
+ });
376
+ }
377
+
378
+ Object.keys(config.screens).forEach((nestedConfig) => {
379
+ const result = createNormalizedConfigs(
380
+ nestedConfig,
381
+ config.screens as PathConfigMap<object>,
382
+ routeNames,
383
+ initials,
384
+ [...parentScreens],
385
+ pattern ?? parentPattern
386
+ );
387
+
388
+ configs.push(...result);
389
+ });
390
+ }
391
+ }
392
+
393
+ routeNames.pop();
394
+
395
+ return configs;
396
+ };
397
+
398
+ const createConfigItem = (
399
+ screen: string,
400
+ routeNames: string[],
401
+ pattern: string,
402
+ path: string,
403
+ parse?: ParseConfig
404
+ ): RouteConfig => {
405
+ // Normalize pattern to remove any leading, trailing slashes, duplicate slashes etc.
406
+ pattern = pattern.split('/').filter(Boolean).join('/');
407
+
408
+ const regex = pattern
409
+ ? new RegExp(
410
+ `^(${pattern
411
+ .split('/')
412
+ .map((it) => {
413
+ if (it.startsWith(':')) {
414
+ return `(([^/]+\\/)${it.endsWith('?') ? '?' : ''})`;
415
+ }
416
+
417
+ return `${it === '*' ? '.*' : escape(it)}\\/`;
418
+ })
419
+ .join('')})`
420
+ )
421
+ : undefined;
422
+
423
+ return {
424
+ screen,
425
+ regex,
426
+ pattern,
427
+ path,
428
+ // The routeNames array is mutated, so copy it to keep the current state
429
+ routeNames: [...routeNames],
430
+ parse,
431
+ };
432
+ };
433
+
434
+ const findParseConfigForRoute = (
435
+ routeName: string,
436
+ flatConfig: RouteConfig[]
437
+ ): ParseConfig | undefined => {
438
+ for (const config of flatConfig) {
439
+ if (routeName === config.routeNames[config.routeNames.length - 1]) {
440
+ return config.parse;
441
+ }
442
+ }
443
+
444
+ return undefined;
445
+ };
446
+
447
+ // Try to find an initial route connected with the one passed
448
+ const findInitialRoute = (
449
+ routeName: string,
450
+ parentScreens: string[],
451
+ initialRoutes: InitialRouteConfig[]
452
+ ): string | undefined => {
453
+ for (const config of initialRoutes) {
454
+ if (parentScreens.length === config.parentScreens.length) {
455
+ let sameParents = true;
456
+ for (let i = 0; i < parentScreens.length; i++) {
457
+ if (parentScreens[i].localeCompare(config.parentScreens[i]) !== 0) {
458
+ sameParents = false;
459
+ break;
460
+ }
461
+ }
462
+ if (sameParents) {
463
+ return routeName !== config.initialRouteName
464
+ ? config.initialRouteName
465
+ : undefined;
466
+ }
467
+ }
468
+ }
469
+ return undefined;
470
+ };
471
+
472
+ // returns state object with values depending on whether
473
+ // it is the end of state and if there is initialRoute for this level
474
+ const createStateObject = (
475
+ initialRoute: string | undefined,
476
+ route: ParsedRoute,
477
+ isEmpty: boolean
478
+ ): InitialState => {
479
+ if (isEmpty) {
480
+ if (initialRoute) {
481
+ return {
482
+ index: 1,
483
+ routes: [{ name: initialRoute }, route],
484
+ };
485
+ } else {
486
+ return {
487
+ routes: [route],
488
+ };
489
+ }
490
+ } else {
491
+ if (initialRoute) {
492
+ return {
493
+ index: 1,
494
+ routes: [{ name: initialRoute }, { ...route, state: { routes: [] } }],
495
+ };
496
+ } else {
497
+ return {
498
+ routes: [{ ...route, state: { routes: [] } }],
499
+ };
500
+ }
501
+ }
502
+ };
503
+
504
+ const createNestedStateObject = (
505
+ path: string,
506
+ routes: ParsedRoute[],
507
+ initialRoutes: InitialRouteConfig[],
508
+ flatConfig?: RouteConfig[]
509
+ ) => {
510
+ let state: InitialState;
511
+ let route = routes.shift() as ParsedRoute;
512
+ const parentScreens: string[] = [];
513
+
514
+ let initialRoute = findInitialRoute(route.name, parentScreens, initialRoutes);
515
+
516
+ parentScreens.push(route.name);
517
+
518
+ state = createStateObject(initialRoute, route, routes.length === 0);
519
+
520
+ if (routes.length > 0) {
521
+ let nestedState = state;
522
+
523
+ while ((route = routes.shift() as ParsedRoute)) {
524
+ initialRoute = findInitialRoute(route.name, parentScreens, initialRoutes);
525
+
526
+ const nestedStateIndex =
527
+ nestedState.index || nestedState.routes.length - 1;
528
+
529
+ nestedState.routes[nestedStateIndex].state = createStateObject(
530
+ initialRoute,
531
+ route,
532
+ routes.length === 0
533
+ );
534
+
535
+ if (routes.length > 0) {
536
+ nestedState = nestedState.routes[nestedStateIndex]
537
+ .state as InitialState;
538
+ }
539
+
540
+ parentScreens.push(route.name);
541
+ }
542
+ }
543
+
544
+ route = findFocusedRoute(state) as ParsedRoute;
545
+ route.path = path;
546
+
547
+ const params = parseQueryParams(
548
+ path,
549
+ flatConfig ? findParseConfigForRoute(route.name, flatConfig) : undefined
550
+ );
551
+
552
+ if (params) {
553
+ route.params = { ...route.params, ...params };
554
+ }
555
+
556
+ return state;
557
+ };
558
+
559
+ const parseQueryParams = (
560
+ path: string,
561
+ parseConfig?: Record<string, (value: string) => any>
562
+ ) => {
563
+ const query = path.split('?')[1];
564
+ const params = queryString.parse(query);
565
+
566
+ if (parseConfig) {
567
+ Object.keys(params).forEach((name) => {
568
+ if (parseConfig[name] && typeof params[name] === 'string') {
569
+ params[name] = parseConfig[name](params[name] as string);
570
+ }
571
+ });
572
+ }
573
+
574
+ return Object.keys(params).length ? params : undefined;
575
+ };
package/src/index.tsx ADDED
@@ -0,0 +1,23 @@
1
+ export { default as BaseNavigationContainer } from './BaseNavigationContainer';
2
+ export { default as createNavigationContainerRef } from './createNavigationContainerRef';
3
+ export { default as createNavigatorFactory } from './createNavigatorFactory';
4
+ export { default as CurrentRenderContext } from './CurrentRenderContext';
5
+ export { default as findFocusedRoute } from './findFocusedRoute';
6
+ export { default as getActionFromState } from './getActionFromState';
7
+ export { default as getFocusedRouteNameFromRoute } from './getFocusedRouteNameFromRoute';
8
+ export { default as getPathFromState } from './getPathFromState';
9
+ export { default as getStateFromPath } from './getStateFromPath';
10
+ export { default as NavigationContainerRefContext } from './NavigationContainerRefContext';
11
+ export { default as NavigationContext } from './NavigationContext';
12
+ export { default as NavigationHelpersContext } from './NavigationHelpersContext';
13
+ export { default as NavigationRouteContext } from './NavigationRouteContext';
14
+ export * from './types';
15
+ export { default as useFocusEffect } from './useFocusEffect';
16
+ export { default as useIsFocused } from './useIsFocused';
17
+ export { default as useNavigation } from './useNavigation';
18
+ export { default as useNavigationBuilder } from './useNavigationBuilder';
19
+ export { default as useNavigationContainerRef } from './useNavigationContainerRef';
20
+ export { default as useNavigationState } from './useNavigationState';
21
+ export { default as useRoute } from './useRoute';
22
+ export { default as validatePathConfig } from './validatePathConfig';
23
+ export * from '@react-navigation/routers';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Compare two arrays with primitive values as the content.
3
+ * We need to make sure that both values and order match.
4
+ */
5
+ export default function isArrayEqual(a: any[], b: any[]) {
6
+ return a.length === b.length && a.every((it, index) => it === b[index]);
7
+ }