one 1.1.390 → 1.1.392

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 (294) hide show
  1. package/dist/cjs/Root.cjs +6 -17
  2. package/dist/cjs/Root.js +9 -13
  3. package/dist/cjs/Root.js.map +1 -1
  4. package/dist/cjs/Root.native.js +4 -12
  5. package/dist/cjs/Root.native.js.map +1 -1
  6. package/dist/cjs/cli/build.cjs +2 -3
  7. package/dist/cjs/cli/build.js +2 -3
  8. package/dist/cjs/cli/build.js.map +1 -1
  9. package/dist/cjs/cli/build.native.js +2 -3
  10. package/dist/cjs/cli/build.native.js.map +2 -2
  11. package/dist/cjs/constants.cjs +9 -1
  12. package/dist/cjs/constants.js +7 -1
  13. package/dist/cjs/constants.js.map +1 -1
  14. package/dist/cjs/constants.native.js +9 -1
  15. package/dist/cjs/constants.native.js.map +2 -2
  16. package/dist/cjs/fork/NavigationContainer.cjs +49 -31
  17. package/dist/cjs/fork/NavigationContainer.js +40 -21
  18. package/dist/cjs/fork/NavigationContainer.js.map +2 -2
  19. package/dist/cjs/fork/NavigationContainer.native.js +51 -29
  20. package/dist/cjs/fork/NavigationContainer.native.js.map +2 -2
  21. package/dist/cjs/fork/createMemoryHistory.cjs +4 -3
  22. package/dist/cjs/fork/createMemoryHistory.js +3 -2
  23. package/dist/cjs/fork/createMemoryHistory.js.map +1 -1
  24. package/dist/cjs/fork/createMemoryHistory.native.js +8 -4
  25. package/dist/cjs/fork/createMemoryHistory.native.js.map +2 -2
  26. package/dist/cjs/fork/extractPathFromURL.cjs +48 -28
  27. package/dist/cjs/fork/extractPathFromURL.js +44 -21
  28. package/dist/cjs/fork/extractPathFromURL.js.map +2 -2
  29. package/dist/cjs/fork/extractPathFromURL.native.js +58 -26
  30. package/dist/cjs/fork/extractPathFromURL.native.js.map +2 -2
  31. package/dist/cjs/fork/findFocusedRoute.js.map +1 -1
  32. package/dist/cjs/fork/findFocusedRoute.native.js.map +1 -1
  33. package/dist/cjs/fork/getPathFromState-mods.cjs +65 -0
  34. package/dist/cjs/fork/getPathFromState-mods.js +56 -0
  35. package/dist/cjs/fork/getPathFromState-mods.js.map +6 -0
  36. package/dist/cjs/fork/getPathFromState-mods.native.js +70 -0
  37. package/dist/cjs/fork/getPathFromState-mods.native.js.map +6 -0
  38. package/dist/cjs/fork/getPathFromState.cjs +75 -224
  39. package/dist/cjs/fork/getPathFromState.js +68 -223
  40. package/dist/cjs/fork/getPathFromState.js.map +2 -2
  41. package/dist/cjs/fork/getPathFromState.native.js +79 -261
  42. package/dist/cjs/fork/getPathFromState.native.js.map +2 -2
  43. package/dist/cjs/fork/getStateFromPath-mods.cjs +187 -0
  44. package/dist/cjs/fork/getStateFromPath-mods.js +199 -0
  45. package/dist/cjs/fork/getStateFromPath-mods.js.map +6 -0
  46. package/dist/cjs/fork/getStateFromPath-mods.native.js +284 -0
  47. package/dist/cjs/fork/getStateFromPath-mods.native.js.map +6 -0
  48. package/dist/cjs/fork/getStateFromPath.cjs +181 -263
  49. package/dist/cjs/fork/getStateFromPath.js +148 -264
  50. package/dist/cjs/fork/getStateFromPath.js.map +2 -2
  51. package/dist/cjs/fork/getStateFromPath.native.js +164 -304
  52. package/dist/cjs/fork/getStateFromPath.native.js.map +2 -2
  53. package/dist/cjs/fork/useBackButton.js.map +1 -1
  54. package/dist/cjs/fork/useBackButton.native.js.map +1 -1
  55. package/dist/cjs/fork/useDocumentTitle.js +4 -1
  56. package/dist/cjs/fork/useDocumentTitle.js.map +1 -1
  57. package/dist/cjs/fork/useDocumentTitle.native.js +1 -1
  58. package/dist/cjs/fork/useDocumentTitle.native.js.map +2 -2
  59. package/dist/cjs/fork/useLinking.cjs +21 -24
  60. package/dist/cjs/fork/useLinking.js +21 -25
  61. package/dist/cjs/fork/useLinking.js.map +2 -2
  62. package/dist/cjs/fork/useLinking.native.js +68 -66
  63. package/dist/cjs/fork/useLinking.native.js.map +2 -2
  64. package/dist/cjs/fork/useThenable.js.map +1 -1
  65. package/dist/cjs/fork/useThenable.native.js.map +1 -1
  66. package/dist/cjs/fork/validatePathConfig.cjs +32 -11
  67. package/dist/cjs/fork/validatePathConfig.js +41 -11
  68. package/dist/cjs/fork/validatePathConfig.js.map +1 -1
  69. package/dist/cjs/fork/validatePathConfig.native.js +47 -18
  70. package/dist/cjs/fork/validatePathConfig.native.js.map +2 -2
  71. package/dist/cjs/layouts/withLayoutContext.cjs +1 -1
  72. package/dist/cjs/layouts/withLayoutContext.js +1 -1
  73. package/dist/cjs/layouts/withLayoutContext.js.map +1 -1
  74. package/dist/cjs/layouts/withLayoutContext.native.js +1 -1
  75. package/dist/cjs/layouts/withLayoutContext.native.js.map +1 -1
  76. package/dist/cjs/link/linking.cjs +4 -4
  77. package/dist/cjs/link/linking.js +3 -3
  78. package/dist/cjs/link/linking.js.map +2 -2
  79. package/dist/cjs/link/linking.native.js +3 -3
  80. package/dist/cjs/link/linking.native.js.map +1 -1
  81. package/dist/cjs/link/useLinkTo.cjs +2 -2
  82. package/dist/cjs/link/useLinkTo.js +2 -2
  83. package/dist/cjs/link/useLinkTo.js.map +1 -1
  84. package/dist/cjs/link/useLinkTo.native.js +2 -2
  85. package/dist/cjs/link/useLinkTo.native.js.map +1 -1
  86. package/dist/cjs/router/getNormalizedStatePath.cjs +2 -2
  87. package/dist/cjs/router/getNormalizedStatePath.js +2 -2
  88. package/dist/cjs/router/getNormalizedStatePath.js.map +1 -1
  89. package/dist/cjs/router/getNormalizedStatePath.native.js +2 -2
  90. package/dist/cjs/router/getNormalizedStatePath.native.js.map +1 -1
  91. package/dist/cjs/router/router.cjs +19 -3
  92. package/dist/cjs/router/router.js +25 -3
  93. package/dist/cjs/router/router.js.map +1 -1
  94. package/dist/cjs/router/router.native.js +39 -3
  95. package/dist/cjs/router/router.native.js.map +2 -2
  96. package/dist/cjs/utils/serverContext.cjs +7 -7
  97. package/dist/cjs/utils/serverContext.js +7 -7
  98. package/dist/cjs/utils/serverContext.js.map +1 -1
  99. package/dist/cjs/utils/serverContext.native.js +5 -5
  100. package/dist/cjs/utils/serverContext.native.js.map +2 -2
  101. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.cjs +6 -6
  102. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.js +3 -4
  103. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.js.map +2 -2
  104. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.native.js +3 -4
  105. package/dist/cjs/vite/plugins/fileSystemRouterPlugin.native.js.map +2 -2
  106. package/dist/esm/Root.js +6 -2
  107. package/dist/esm/Root.js.map +1 -1
  108. package/dist/esm/Root.mjs +1 -1
  109. package/dist/esm/Root.mjs.map +1 -1
  110. package/dist/esm/Root.native.js +1 -1
  111. package/dist/esm/Root.native.js.map +2 -2
  112. package/dist/esm/cli/build.js +2 -3
  113. package/dist/esm/cli/build.js.map +1 -1
  114. package/dist/esm/cli/build.mjs +2 -3
  115. package/dist/esm/cli/build.mjs.map +1 -1
  116. package/dist/esm/cli/build.native.js +2 -3
  117. package/dist/esm/cli/build.native.js.map +2 -2
  118. package/dist/esm/constants.js +7 -1
  119. package/dist/esm/constants.js.map +1 -1
  120. package/dist/esm/constants.mjs +8 -2
  121. package/dist/esm/constants.mjs.map +1 -1
  122. package/dist/esm/constants.native.js +7 -1
  123. package/dist/esm/constants.native.js.map +2 -2
  124. package/dist/esm/fork/NavigationContainer.js +43 -20
  125. package/dist/esm/fork/NavigationContainer.js.map +1 -1
  126. package/dist/esm/fork/NavigationContainer.mjs +42 -24
  127. package/dist/esm/fork/NavigationContainer.mjs.map +1 -1
  128. package/dist/esm/fork/NavigationContainer.native.js +44 -24
  129. package/dist/esm/fork/NavigationContainer.native.js.map +2 -2
  130. package/dist/esm/fork/createMemoryHistory.js +3 -2
  131. package/dist/esm/fork/createMemoryHistory.js.map +1 -1
  132. package/dist/esm/fork/createMemoryHistory.mjs +4 -3
  133. package/dist/esm/fork/createMemoryHistory.mjs.map +1 -1
  134. package/dist/esm/fork/createMemoryHistory.native.js +4 -4
  135. package/dist/esm/fork/createMemoryHistory.native.js.map +2 -2
  136. package/dist/esm/fork/extractPathFromURL.js +42 -11
  137. package/dist/esm/fork/extractPathFromURL.js.map +1 -1
  138. package/dist/esm/fork/extractPathFromURL.mjs +39 -11
  139. package/dist/esm/fork/extractPathFromURL.mjs.map +1 -1
  140. package/dist/esm/fork/extractPathFromURL.native.js +52 -15
  141. package/dist/esm/fork/extractPathFromURL.native.js.map +2 -2
  142. package/dist/esm/fork/findFocusedRoute.js.map +1 -1
  143. package/dist/esm/fork/findFocusedRoute.mjs.map +1 -1
  144. package/dist/esm/fork/findFocusedRoute.native.js.map +1 -1
  145. package/dist/esm/fork/getPathFromState-mods.js +40 -0
  146. package/dist/esm/fork/getPathFromState-mods.js.map +6 -0
  147. package/dist/esm/fork/getPathFromState-mods.mjs +40 -0
  148. package/dist/esm/fork/getPathFromState-mods.mjs.map +1 -0
  149. package/dist/esm/fork/getPathFromState-mods.native.js +47 -0
  150. package/dist/esm/fork/getPathFromState-mods.native.js.map +6 -0
  151. package/dist/esm/fork/getPathFromState.js +71 -228
  152. package/dist/esm/fork/getPathFromState.js.map +2 -2
  153. package/dist/esm/fork/getPathFromState.mjs +73 -221
  154. package/dist/esm/fork/getPathFromState.mjs.map +1 -1
  155. package/dist/esm/fork/getPathFromState.native.js +79 -260
  156. package/dist/esm/fork/getPathFromState.native.js.map +2 -2
  157. package/dist/esm/fork/getStateFromPath-mods.js +176 -0
  158. package/dist/esm/fork/getStateFromPath-mods.js.map +6 -0
  159. package/dist/esm/fork/getStateFromPath-mods.mjs +143 -0
  160. package/dist/esm/fork/getStateFromPath-mods.mjs.map +1 -0
  161. package/dist/esm/fork/getStateFromPath-mods.native.js +246 -0
  162. package/dist/esm/fork/getStateFromPath-mods.native.js.map +6 -0
  163. package/dist/esm/fork/getStateFromPath.js +160 -265
  164. package/dist/esm/fork/getStateFromPath.js.map +2 -2
  165. package/dist/esm/fork/getStateFromPath.mjs +181 -260
  166. package/dist/esm/fork/getStateFromPath.mjs.map +1 -1
  167. package/dist/esm/fork/getStateFromPath.native.js +164 -302
  168. package/dist/esm/fork/getStateFromPath.native.js.map +2 -2
  169. package/dist/esm/fork/useBackButton.js.map +1 -1
  170. package/dist/esm/fork/useBackButton.mjs.map +1 -1
  171. package/dist/esm/fork/useBackButton.native.js.map +1 -1
  172. package/dist/esm/fork/useDocumentTitle.js +4 -1
  173. package/dist/esm/fork/useDocumentTitle.js.map +1 -1
  174. package/dist/esm/fork/useDocumentTitle.mjs.map +1 -1
  175. package/dist/esm/fork/useDocumentTitle.native.js +1 -1
  176. package/dist/esm/fork/useDocumentTitle.native.js.map +2 -2
  177. package/dist/esm/fork/useLinking.js +22 -26
  178. package/dist/esm/fork/useLinking.js.map +1 -1
  179. package/dist/esm/fork/useLinking.mjs +20 -23
  180. package/dist/esm/fork/useLinking.mjs.map +1 -1
  181. package/dist/esm/fork/useLinking.native.js +65 -67
  182. package/dist/esm/fork/useLinking.native.js.map +2 -2
  183. package/dist/esm/fork/useThenable.js.map +1 -1
  184. package/dist/esm/fork/useThenable.mjs.map +1 -1
  185. package/dist/esm/fork/useThenable.native.js.map +1 -1
  186. package/dist/esm/fork/validatePathConfig.js +41 -11
  187. package/dist/esm/fork/validatePathConfig.js.map +1 -1
  188. package/dist/esm/fork/validatePathConfig.mjs +32 -11
  189. package/dist/esm/fork/validatePathConfig.mjs.map +1 -1
  190. package/dist/esm/fork/validatePathConfig.native.js +43 -18
  191. package/dist/esm/fork/validatePathConfig.native.js.map +2 -2
  192. package/dist/esm/layouts/withLayoutContext.js +1 -1
  193. package/dist/esm/layouts/withLayoutContext.js.map +1 -1
  194. package/dist/esm/layouts/withLayoutContext.mjs +1 -1
  195. package/dist/esm/layouts/withLayoutContext.mjs.map +1 -1
  196. package/dist/esm/layouts/withLayoutContext.native.js +1 -1
  197. package/dist/esm/layouts/withLayoutContext.native.js.map +1 -1
  198. package/dist/esm/link/linking.js +2 -2
  199. package/dist/esm/link/linking.js.map +1 -1
  200. package/dist/esm/link/linking.mjs +2 -2
  201. package/dist/esm/link/linking.mjs.map +1 -1
  202. package/dist/esm/link/linking.native.js +2 -2
  203. package/dist/esm/link/linking.native.js.map +1 -1
  204. package/dist/esm/link/useLinkTo.js +1 -1
  205. package/dist/esm/link/useLinkTo.mjs +1 -1
  206. package/dist/esm/link/useLinkTo.native.js +1 -1
  207. package/dist/esm/router/getNormalizedStatePath.js +1 -1
  208. package/dist/esm/router/getNormalizedStatePath.mjs +1 -1
  209. package/dist/esm/router/getNormalizedStatePath.native.js +1 -1
  210. package/dist/esm/router/router.js +24 -2
  211. package/dist/esm/router/router.js.map +1 -1
  212. package/dist/esm/router/router.mjs +18 -2
  213. package/dist/esm/router/router.mjs.map +1 -1
  214. package/dist/esm/router/router.native.js +38 -2
  215. package/dist/esm/router/router.native.js.map +2 -2
  216. package/dist/esm/utils/serverContext.js +2 -1
  217. package/dist/esm/utils/serverContext.js.map +1 -1
  218. package/dist/esm/utils/serverContext.mjs +1 -1
  219. package/dist/esm/utils/serverContext.mjs.map +1 -1
  220. package/dist/esm/utils/serverContext.native.js +2 -1
  221. package/dist/esm/utils/serverContext.native.js.map +2 -2
  222. package/dist/esm/vite/plugins/fileSystemRouterPlugin.js +3 -3
  223. package/dist/esm/vite/plugins/fileSystemRouterPlugin.js.map +1 -1
  224. package/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs +3 -3
  225. package/dist/esm/vite/plugins/fileSystemRouterPlugin.mjs.map +1 -1
  226. package/dist/esm/vite/plugins/fileSystemRouterPlugin.native.js +3 -3
  227. package/dist/esm/vite/plugins/fileSystemRouterPlugin.native.js.map +2 -2
  228. package/package.json +8 -9
  229. package/src/Root.tsx +6 -2
  230. package/src/cli/build.ts +2 -3
  231. package/src/constants.ts +8 -0
  232. package/src/fork/NavigationContainer.tsx +101 -39
  233. package/src/fork/createMemoryHistory.tsx +15 -13
  234. package/src/fork/extractPathFromURL.ts +85 -40
  235. package/src/fork/findFocusedRoute.tsx +9 -1
  236. package/src/fork/getPathFromState-mods.ts +142 -0
  237. package/src/fork/getPathFromState.ts +244 -501
  238. package/src/fork/getStateFromPath-mods.ts +400 -0
  239. package/src/fork/getStateFromPath.ts +447 -538
  240. package/src/fork/useBackButton.native.tsx +16 -2
  241. package/src/fork/useBackButton.tsx +11 -2
  242. package/src/fork/useDocumentTitle.native.tsx +9 -4
  243. package/src/fork/useDocumentTitle.tsx +12 -7
  244. package/src/fork/useLinking.native.ts +71 -63
  245. package/src/fork/useLinking.ts +75 -40
  246. package/src/fork/useThenable.tsx +7 -1
  247. package/src/fork/validatePathConfig.ts +64 -12
  248. package/src/layouts/withLayoutContext.tsx +1 -1
  249. package/src/link/linking.ts +2 -2
  250. package/src/link/useLinkTo.tsx +1 -1
  251. package/src/router/getNormalizedStatePath.tsx +1 -1
  252. package/src/router/router.ts +41 -2
  253. package/src/utils/serverContext.tsx +3 -1
  254. package/src/vite/plugins/fileSystemRouterPlugin.tsx +3 -3
  255. package/types/Root.d.ts.map +1 -1
  256. package/types/cli/build.d.ts.map +1 -1
  257. package/types/constants.d.ts +2 -0
  258. package/types/constants.d.ts.map +1 -1
  259. package/types/fork/NavigationContainer.d.ts +19 -8
  260. package/types/fork/NavigationContainer.d.ts.map +1 -1
  261. package/types/fork/createMemoryHistory.d.ts +10 -1
  262. package/types/fork/createMemoryHistory.d.ts.map +1 -1
  263. package/types/fork/extractPathFromURL.d.ts +7 -1
  264. package/types/fork/extractPathFromURL.d.ts.map +1 -1
  265. package/types/fork/findFocusedRoute.d.ts +9 -0
  266. package/types/fork/findFocusedRoute.d.ts.map +1 -1
  267. package/types/fork/getPathFromState-mods.d.ts +23 -0
  268. package/types/fork/getPathFromState-mods.d.ts.map +1 -0
  269. package/types/fork/getPathFromState.d.ts +15 -14
  270. package/types/fork/getPathFromState.d.ts.map +1 -1
  271. package/types/fork/getStateFromPath-mods.d.ts +58 -0
  272. package/types/fork/getStateFromPath-mods.d.ts.map +1 -0
  273. package/types/fork/getStateFromPath.d.ts +28 -29
  274. package/types/fork/getStateFromPath.d.ts.map +1 -1
  275. package/types/fork/useBackButton.d.ts +6 -0
  276. package/types/fork/useBackButton.d.ts.map +1 -1
  277. package/types/fork/useBackButton.native.d.ts +9 -1
  278. package/types/fork/useBackButton.native.d.ts.map +1 -1
  279. package/types/fork/useDocumentTitle.d.ts +8 -6
  280. package/types/fork/useDocumentTitle.d.ts.map +1 -1
  281. package/types/fork/useDocumentTitle.native.d.ts +5 -2
  282. package/types/fork/useDocumentTitle.native.d.ts.map +1 -1
  283. package/types/fork/useLinking.d.ts +8 -1
  284. package/types/fork/useLinking.d.ts.map +1 -1
  285. package/types/fork/useLinking.native.d.ts +9 -2
  286. package/types/fork/useLinking.native.d.ts.map +1 -1
  287. package/types/fork/useThenable.d.ts +6 -0
  288. package/types/fork/useThenable.d.ts.map +1 -1
  289. package/types/fork/validatePathConfig.d.ts +8 -1
  290. package/types/fork/validatePathConfig.d.ts.map +1 -1
  291. package/types/link/linking.d.ts +2 -2
  292. package/types/link/linking.d.ts.map +1 -1
  293. package/types/utils/serverContext.d.ts.map +1 -1
  294. package/types/vite/plugins/fileSystemRouterPlugin.d.ts.map +1 -1
@@ -1,70 +1,72 @@
1
- import type { PathConfigMap } from '@react-navigation/core'
2
- import type { InitialState } from '@react-navigation/routers'
3
- // biome-ignore lint/suspicious/noShadowRestrictedNames: <explanation>
1
+ /**
2
+ * This file is copied from the react-navigation repo:
3
+ * https://github.com/react-navigation/react-navigation/blob/%40react-navigation/core%407.1.2/packages/core/src/getStateFromPath.tsx
4
+ *
5
+ * Please refrain from making changes to this file, as it will make merging updates from the upstream harder.
6
+ * All modifications except formatting should be marked with `// @modified` comment.
7
+ */
8
+
9
+ import type { InitialState, NavigationState, PartialState } from '@react-navigation/routers'
10
+ // biome-ignore lint/suspicious/noShadowRestrictedNames: ignore from forked code // @modified
4
11
  import escape from 'escape-string-regexp'
5
- import type { RouteNode } from '../router/Route'
6
- import { matchGroupName, stripGroupSegmentsFromPath } from '../router/matchers'
7
- import { findFocusedRoute } from './findFocusedRoute'
8
- import validatePathConfig from './validatePathConfig'
9
- import type { OneRouter } from '../interfaces/router'
12
+ // import * as queryString from 'query-string'
10
13
 
11
- type Options<ParamList extends object> = {
14
+ import { findFocusedRoute } from './findFocusedRoute'
15
+ import type { PathConfigMap } from '@react-navigation/core' // @modified
16
+ import { validatePathConfig } from './validatePathConfig'
17
+ import {
18
+ type AdditionalRouteConfig,
19
+ appendIsInitial,
20
+ createConfigItemAdditionalProperties,
21
+ decodeURIComponentSafe,
22
+ formatRegexPattern,
23
+ getParamValue,
24
+ getRouteConfigSorter,
25
+ getUrlWithReactNavigationConcessions,
26
+ matchForEmptyPath,
27
+ parseQueryParamsExtended,
28
+ populateParams,
29
+ } from './getStateFromPath-mods'
30
+
31
+ type Options<ParamList extends {}> = {
32
+ path?: string
12
33
  initialRouteName?: string
13
34
  screens: PathConfigMap<ParamList>
14
35
  }
15
36
 
16
37
  type ParseConfig = Record<string, (value: string) => any>
17
38
 
18
- type RouteConfig = {
19
- isInitial?: boolean
39
+ // @modified: add export
40
+ export type RouteConfig = {
20
41
  screen: string
21
42
  regex?: RegExp
22
43
  path: string
23
44
  pattern: string
24
45
  routeNames: string[]
25
46
  parse?: ParseConfig
26
- hasChildren: boolean
27
- userReadableName: string
28
- _route?: RouteNode
29
- }
47
+ } & AdditionalRouteConfig // @modified: union with AdditionalRouteConfig
30
48
 
31
- type InitialRouteConfig = {
49
+ // @modified: add export
50
+ export type InitialRouteConfig = {
32
51
  initialRouteName: string
33
52
  parentScreens: string[]
34
53
  }
35
54
 
36
- type ParsedRoute = {
55
+ type ResultState = PartialState<NavigationState> & {
56
+ state?: ResultState
57
+ }
58
+
59
+ // @modified: add export
60
+ export type ParsedRoute = {
37
61
  name: string
38
62
  path?: string
39
- params?: Record<string, any>
63
+ params?: Record<string, any> | undefined
40
64
  }
41
65
 
42
- export function getUrlWithReactNavigationConcessions(
43
- path: string,
44
- baseUrl: string | undefined = process.env.EXPO_BASE_URL
45
- ) {
46
- let parsed: URL
47
- try {
48
- parsed = new URL(path, baseUrl || 'http://phony.example')
49
- } catch (err) {
50
- console.warn(`Error parsing url ${path}: ${err?.['message']}`)
51
- // Do nothing with invalid URLs.
52
- return {
53
- nonstandardPathname: path,
54
- inputPathnameWithoutHash: path.replace(/#.*$/g, ''),
55
- url: null,
56
- }
57
- }
58
-
59
- const pathname = parsed.pathname
60
-
61
- // Make sure there is a trailing slash
62
- return {
63
- // The slashes are at the end, not the beginning
64
- nonstandardPathname:
65
- stripBaseUrl(pathname, baseUrl).replace(/^\/+/g, '').replace(/\/+$/g, '') + '/',
66
- url: parsed,
67
- }
66
+ type ConfigResources = {
67
+ initialRoutes: InitialRouteConfig[]
68
+ configs: RouteConfig[]
69
+ configWithRegexes: RouteConfig[]
68
70
  }
69
71
 
70
72
  /**
@@ -88,322 +90,276 @@ export function getUrlWithReactNavigationConcessions(
88
90
  * @param path Path string to parse and convert, e.g. /foo/bar?count=42.
89
91
  * @param options Extra options to fine-tune how to parse the path.
90
92
  */
91
- export default function getStateFromPath<ParamList extends object>(
93
+ export function getStateFromPath<ParamList extends {}>(
92
94
  path: string,
93
95
  options?: Options<ParamList>
94
- ): OneRouter.ResultState | undefined {
95
- const { initialRoutes, configs } = getMatchableRouteConfigs(options)
96
- return getStateFromPathWithConfigs(path, configs, initialRoutes)
97
- }
98
-
99
- export function getMatchableRouteConfigs<ParamList extends object>(options?: Options<ParamList>) {
100
- if (options) {
101
- validatePathConfig(options)
102
- }
96
+ ): ResultState | undefined {
97
+ const { initialRoutes, configs, configWithRegexes } = getConfigResources(options)
103
98
 
104
99
  const screens = options?.screens
105
- // One disallows usage without a linking config.
106
- if (!screens) {
107
- throw Error("You must pass a 'screens' object to 'getStateFromPath' to generate a path.")
108
- }
109
100
 
110
- // This will be mutated...
111
- const initialRoutes: InitialRouteConfig[] = []
101
+ // @modified - start
102
+ const pathData = getUrlWithReactNavigationConcessions(path)
103
+ // @modified - end
112
104
 
113
- if (options?.initialRouteName) {
114
- initialRoutes.push({
115
- initialRouteName: options.initialRouteName,
116
- parentScreens: [],
117
- })
118
- }
105
+ let remaining = pathData.nonstandardPathname // @modified: use `pathData.nonstandardPathname` instead of `path`
106
+ .replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
107
+ .replace(/^\//, '') // Remove extra leading slash
108
+ .replace(/\?.*$/, '') // Remove query params which we will handle later
119
109
 
120
- // Create a normalized configs array which will be easier to use.
121
- const converted = Object.keys(screens)
122
- .flatMap((key) => createNormalizedConfigs(key, screens, [], initialRoutes))
123
- .flat()
124
-
125
- const resolvedInitialPatterns = initialRoutes.map((route) =>
126
- joinPaths(...route.parentScreens, route.initialRouteName)
127
- )
110
+ // Make sure there is a trailing slash
111
+ remaining = remaining.endsWith('/') ? remaining : `${remaining}/`
128
112
 
129
- const convertedWithInitial = converted.map((config) => ({
130
- ...config,
131
- // TODO: Probably a safer way to do this
132
- // Mark initial routes to give them potential priority over other routes that match.
133
- isInitial: resolvedInitialPatterns.includes(config.routeNames.join('/')),
134
- }))
113
+ const prefix = options?.path?.replace(/^\//, '') // Remove extra leading slash
135
114
 
136
- // Sort in order of resolution. This is extremely important for the algorithm to work.
137
- const configs = convertedWithInitial.sort(sortConfigs)
115
+ if (prefix) {
116
+ // Make sure there is a trailing slash
117
+ const normalizedPrefix = prefix.endsWith('/') ? prefix : `${prefix}/`
138
118
 
139
- // Assert any duplicates before we start parsing.
140
- assertConfigDuplicates(configs)
119
+ // If the path doesn't start with the prefix, it's not a match
120
+ if (!remaining.startsWith(normalizedPrefix)) {
121
+ return undefined
122
+ }
141
123
 
142
- return { configs, initialRoutes }
143
- }
124
+ // Remove the prefix from the path
125
+ remaining = remaining.replace(normalizedPrefix, '')
126
+ }
144
127
 
145
- function assertConfigDuplicates(configs: RouteConfig[]) {
146
- // Check for duplicate patterns in the config
147
- configs.reduce<Record<string, RouteConfig>>((acc, config) => {
148
- // NOTE: Uses the regex pattern as key to detect duplicate slugs.
149
- const indexedKey = config.regex?.toString() ?? config.pattern
150
- const alpha = acc[indexedKey]
128
+ if (screens === undefined) {
129
+ // When no config is specified, use the path segments as route names
130
+ const routes = remaining
131
+ .split('/')
132
+ .filter(Boolean)
133
+ .map((segment) => {
134
+ const name = decodeURIComponent(segment)
135
+ return { name }
136
+ })
151
137
 
152
- // NOTE: Skips checking nodes that have children.
153
- if (alpha && !alpha.hasChildren && !config.hasChildren) {
154
- const a = alpha.routeNames
155
- const b = config.routeNames
138
+ if (routes.length) {
139
+ // @modified - start
140
+ // return createNestedStateObject(path, routes, initialRoutes)
141
+ return createNestedStateObject(pathData, routes, initialRoutes, [], pathData.url.hash)
142
+ // @modified - end
143
+ }
156
144
 
157
- // It's not a problem if the path string omitted from a inner most screen
158
- // For example, it's ok if a path resolves to `A > B > C` or `A > B`
159
- const intersects =
160
- a.length > b.length ? b.every((it, i) => a[i] === it) : a.every((it, i) => b[i] === it)
145
+ return undefined
146
+ }
161
147
 
162
- if (!intersects) {
163
- // NOTE: Adds more context to the error message since we know about the
164
- // file-based routing.
165
- const last = config.pattern.split('/').pop()
166
-
167
- if (!last?.match(/^\*not-found$/)) {
168
- const routeType = last?.startsWith(':')
169
- ? 'dynamic route'
170
- : last?.startsWith('*')
171
- ? 'dynamic-rest route'
172
- : 'route'
173
-
174
- throw new Error(
175
- `The ${routeType} pattern '${config.pattern || '/'}' resolves to both '${
176
- alpha.userReadableName
177
- }' and '${
178
- config.userReadableName
179
- }'. Patterns must be unique and cannot resolve to more than one route.`
180
- )
181
- }
182
- }
148
+ if (remaining === '/') {
149
+ // We need to add special handling of empty path so navigation to empty path also works
150
+ // When handling empty path, we should only look at the root level config
151
+ // @modified - start
152
+ // const match = configs.find(
153
+ // (config) =>
154
+ // config.path === '' &&
155
+ // config.routeNames.every(
156
+ // // Make sure that none of the parent configs have a non-empty path defined
157
+ // (name) => !configs.find((c) => c.screen === name)?.path
158
+ // )
159
+ // )
160
+ const match = matchForEmptyPath(configWithRegexes)
161
+ // @modified - end
162
+
163
+ if (match) {
164
+ return createNestedStateObject(
165
+ pathData, // @modified: pass pathData instead of path
166
+ match.routeNames.map((name) => ({ name })),
167
+ initialRoutes,
168
+ configs
169
+ )
183
170
  }
184
171
 
185
- return Object.assign(acc, {
186
- [indexedKey]: config,
187
- })
188
- }, {})
189
- }
172
+ return undefined
173
+ }
190
174
 
191
- function sortConfigs(a: RouteConfig, b: RouteConfig): number {
192
- // Sort config so that:
193
- // - the most exhaustive ones are always at the beginning
194
- // - patterns with wildcard are always at the end
175
+ let result: PartialState<NavigationState> | undefined
176
+ let current: PartialState<NavigationState> | undefined
195
177
 
196
- // If 2 patterns are same, move the one with less route names up
197
- // This is an error state, so it's only useful for consistent error messages
198
- if (a.pattern === b.pattern) {
199
- return b.routeNames.join('>').localeCompare(a.routeNames.join('>'))
200
- }
178
+ // We match the whole path against the regex instead of segments
179
+ // This makes sure matches such as wildcard will catch any unmatched routes, even if nested
180
+ const { routes, remainingPath } = matchAgainstConfigs(remaining, configWithRegexes)
201
181
 
202
- // If one of the patterns starts with the other, it's more exhaustive
203
- // So move it up
204
- if (
205
- a.pattern.startsWith(b.pattern) &&
206
- // NOTE: This is a hack to make sure that `*` is always at the end
207
- b.screen !== 'index'
208
- ) {
209
- return -1
182
+ if (routes !== undefined) {
183
+ // This will always be empty if full path matched
184
+ // @modified: pass pathData instead of path
185
+ current = createNestedStateObject(pathData, routes, initialRoutes, configs)
186
+ remaining = remainingPath
187
+ result = current
210
188
  }
211
189
 
212
- if (b.pattern.startsWith(a.pattern) && a.screen !== 'index') {
213
- return 1
190
+ if (current == null || result == null) {
191
+ return undefined
214
192
  }
215
193
 
216
- // NOTE: Here we append `index` if the screen was `index` so the length is the same
217
- // as a slug or wildcard when nested more than one level deep.
218
- // This is so we can compare the length of the pattern, e.g. `foo/*` > `foo` vs `*` < ``.
219
- const aParts = a.pattern
220
- .split('/')
221
- // Strip out group names to ensure they don't affect the priority.
222
- .filter((part) => matchGroupName(part) == null)
223
- if (a.screen === 'index' || a.screen.match(/\/index$/)) {
224
- aParts.push('index')
225
- }
194
+ return result
195
+ }
226
196
 
227
- const bParts = b.pattern.split('/').filter((part) => matchGroupName(part) == null)
228
- if (b.screen === 'index' || b.screen.match(/\/index$/)) {
229
- bParts.push('index')
230
- }
197
+ /**
198
+ * Reference to the last used config resources. This is used to avoid recomputing the config resources when the options are the same.
199
+ */
200
+ const cachedConfigResources = new WeakMap<Options<{}>, ConfigResources>()
231
201
 
232
- for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
233
- // if b is longer, b get higher priority
234
- if (aParts[i] == null) {
235
- return 1
236
- }
237
- // if a is longer, a get higher priority
238
- if (bParts[i] == null) {
239
- return -1
240
- }
202
+ function getConfigResources<ParamList extends {}>(options: Options<ParamList> | undefined) {
203
+ if (!options) return prepareConfigResources()
241
204
 
242
- const aWildCard = aParts[i].startsWith('*')
243
- const bWildCard = bParts[i].startsWith('*')
244
- // if both are wildcard we compare next component
245
- if (aWildCard && bWildCard) {
246
- const aNotFound = aParts[i].match(/^[*]not-found$/)
247
- const bNotFound = bParts[i].match(/^[*]not-found$/)
205
+ const cached = cachedConfigResources.get(options)
248
206
 
249
- if (aNotFound && bNotFound) {
250
- continue
251
- }
252
- if (aNotFound) {
253
- return 1
254
- }
255
- if (bNotFound) {
256
- return -1
257
- }
258
- continue
259
- }
260
- // if only a is wild card, b get higher priority
261
- if (aWildCard) {
262
- return 1
263
- }
264
- // if only b is wild card, a get higher priority
265
- if (bWildCard) {
266
- return -1
267
- }
207
+ if (cached) return cached
268
208
 
269
- const aSlug = aParts[i].startsWith(':')
270
- const bSlug = bParts[i].startsWith(':')
209
+ const resources = prepareConfigResources(options)
271
210
 
272
- // if both are wildcard we compare next component
273
- if (aSlug && bSlug) {
274
- const aNotFound = aParts[i].match(/^[*]not-found$/)
275
- const bNotFound = bParts[i].match(/^[*]not-found$/)
211
+ cachedConfigResources.set(options, resources)
276
212
 
277
- if (aNotFound && bNotFound) {
278
- continue
279
- }
280
- if (aNotFound) {
281
- return 1
282
- }
283
- if (bNotFound) {
284
- return -1
285
- }
213
+ return resources
214
+ }
286
215
 
287
- continue
288
- }
289
- // if only a is wild card, b get higher priority
290
- if (aSlug) {
291
- return 1
292
- }
293
- // if only b is wild card, a get higher priority
294
- if (bSlug) {
295
- return -1
296
- }
216
+ // @modified: add previousSegments parameter
217
+ function prepareConfigResources(options?: Options<{}>, previousSegments?: string[]) {
218
+ if (options) {
219
+ validatePathConfig(options)
297
220
  }
298
221
 
299
- // Sort initial routes with a higher priority than routes which will push more screens
300
- // this ensures shared routes go to the shortest path.
301
- if (a.isInitial && !b.isInitial) {
302
- return -1
303
- }
304
- if (!a.isInitial && b.isInitial) {
305
- return 1
306
- }
222
+ const initialRoutes = getInitialRoutes(options)
307
223
 
308
- return bParts.length - aParts.length
309
- }
224
+ // @modified: pass previousSegments
225
+ const configs = getNormalizedConfigs(initialRoutes, options?.screens, previousSegments)
310
226
 
311
- function getStateFromEmptyPathWithConfigs(
312
- path: string,
313
- hash: string,
314
- configs: RouteConfig[],
315
- initialRoutes: InitialRouteConfig[]
316
- ): OneRouter.ResultState | undefined {
317
- // We need to add special handling of empty path so navigation to empty path also works
318
- // When handling empty path, we should only look at the root level config
319
-
320
- // NOTE: We only care about matching leaf nodes.
321
- const leafNodes = configs
322
- .filter((config) => !config.hasChildren)
323
- .map((value) => {
324
- return {
325
- ...value,
326
- // Collapse all levels of group segments before testing.
327
- // This enables `app/(one)/(two)/index.js` to be matched.
328
- path: stripGroupSegmentsFromPath(value.path),
329
- }
330
- })
227
+ checkForDuplicatedConfigs(configs)
331
228
 
332
- const match =
333
- leafNodes.find(
334
- (config) =>
335
- // NOTE: Test leaf node index routes that either don't have a regex or match an empty string.
336
- config.path === '' && (!config.regex || config.regex.test(''))
337
- ) ??
338
- leafNodes.find(
339
- (config) =>
340
- // NOTE: Test leaf node dynamic routes that match an empty string.
341
- config.path.startsWith(':') && config.regex!.test('')
342
- ) ??
343
- // NOTE: Test leaf node deep dynamic routes that match a slash.
344
- // This should be done last to enable dynamic routes having a higher priority.
345
- leafNodes.find((config) => config.path.startsWith('*') && config.regex!.test('/'))
346
-
347
- if (!match) {
348
- return undefined
229
+ const configWithRegexes = getConfigsWithRegexes(configs)
230
+
231
+ return {
232
+ initialRoutes,
233
+ configs,
234
+ configWithRegexes,
349
235
  }
236
+ }
350
237
 
351
- const routes = match.routeNames.map((name) => {
352
- if (!match._route) {
353
- return { name }
354
- }
355
- return {
356
- name,
357
- _route: match._route,
358
- }
359
- })
238
+ function getInitialRoutes(options?: Options<{}>) {
239
+ const initialRoutes: InitialRouteConfig[] = []
360
240
 
361
- return createNestedStateObject(path, hash, routes, configs, initialRoutes)
241
+ if (options?.initialRouteName) {
242
+ initialRoutes.push({
243
+ initialRouteName: options.initialRouteName,
244
+ parentScreens: [],
245
+ })
246
+ }
247
+
248
+ return initialRoutes
362
249
  }
363
250
 
364
- function getStateFromPathWithConfigs(
365
- path: string,
366
- configs: RouteConfig[],
251
+ function getNormalizedConfigs(
367
252
  initialRoutes: InitialRouteConfig[],
368
- baseUrl: string | undefined = process.env.EXPO_BASE_URL
369
- ): OneRouter.ResultState | undefined {
370
- const formattedPaths = getUrlWithReactNavigationConcessions(path)
371
-
372
- if (!formattedPaths.url) {
373
- console.warn(`No url found for ${path}`)
374
- return
375
- }
253
+ screens: PathConfigMap<object> = {},
254
+ // @modified - start
255
+ previousSegments?: string[]
256
+ // @modified - end
257
+ ) {
258
+ // Create a normalized configs array which will be easier to use
259
+ return (
260
+ ([] as RouteConfig[])
261
+ .concat(
262
+ ...Object.keys(screens).map((key) =>
263
+ createNormalizedConfigs(key, screens as PathConfigMap<object>, [], initialRoutes, [])
264
+ )
265
+ )
266
+ /* @modified - start */
267
+ // .sort((a, b) => {
268
+ // // Sort config so that:
269
+ // // - the most exhaustive ones are always at the beginning
270
+ // // - patterns with wildcard are always at the end
271
+
272
+ // // If 2 patterns are same, move the one with less route names up
273
+ // // This is an error state, so it's only useful for consistent error messages
274
+ // if (a.pattern === b.pattern) {
275
+ // return b.routeNames.join('>').localeCompare(a.routeNames.join('>'))
276
+ // }
277
+
278
+ // // If one of the patterns starts with the other, it's more exhaustive
279
+ // // So move it up
280
+ // if (a.pattern.startsWith(b.pattern)) {
281
+ // return -1
282
+ // }
283
+
284
+ // if (b.pattern.startsWith(a.pattern)) {
285
+ // return 1
286
+ // }
287
+
288
+ // const aParts = a.pattern.split('/')
289
+ // const bParts = b.pattern.split('/')
290
+
291
+ // for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
292
+ // // if b is longer, b get higher priority
293
+ // if (aParts[i] == null) {
294
+ // return 1
295
+ // }
296
+ // // if a is longer, a get higher priority
297
+ // if (bParts[i] == null) {
298
+ // return -1
299
+ // }
300
+ // const aWildCard = aParts[i] === '*' || aParts[i].startsWith(':')
301
+ // const bWildCard = bParts[i] === '*' || bParts[i].startsWith(':')
302
+ // // if both are wildcard we compare next component
303
+ // if (aWildCard && bWildCard) {
304
+ // continue
305
+ // }
306
+ // // if only a is wild card, b get higher priority
307
+ // if (aWildCard) {
308
+ // return 1
309
+ // }
310
+ // // if only b is wild card, a get higher priority
311
+ // if (bWildCard) {
312
+ // return -1
313
+ // }
314
+ // }
315
+ // return bParts.length - aParts.length
316
+ // })
317
+ .map(appendIsInitial(initialRoutes))
318
+ .sort(getRouteConfigSorter(previousSegments))
319
+ /* @modified - end */
320
+ )
321
+ }
376
322
 
377
- let cleanPath =
378
- stripBaseUrl(stripGroupSegmentsFromPath(formattedPaths.url.pathname), baseUrl) +
379
- formattedPaths.url.search
323
+ function checkForDuplicatedConfigs(configs: RouteConfig[]) {
324
+ // Check for duplicate patterns in the config
325
+ configs.reduce<Record<string, RouteConfig>>((acc, config) => {
326
+ if (acc[config.pattern]) {
327
+ const a = acc[config.pattern].routeNames
328
+ const b = config.routeNames
380
329
 
381
- if (!path.startsWith('/')) cleanPath = cleanPath.slice(1)
330
+ // It's not a problem if the path string omitted from a inner most screen
331
+ // For example, it's ok if a path resolves to `A > B > C` or `A > B`
332
+ const intersects =
333
+ a.length > b.length ? b.every((it, i) => a[i] === it) : a.every((it, i) => b[i] === it)
382
334
 
383
- if (formattedPaths.nonstandardPathname === '/') {
384
- return getStateFromEmptyPathWithConfigs(
385
- cleanPath,
386
- formattedPaths.url.hash.slice(1),
387
- configs,
388
- initialRoutes
389
- )
390
- }
335
+ if (!intersects) {
336
+ throw new Error(
337
+ `Found conflicting screens with the same pattern. The pattern '${
338
+ config.pattern
339
+ }' resolves to both '${a.join(' > ')}' and '${b.join(
340
+ ' > '
341
+ )}'. Patterns must be unique and cannot resolve to more than one screen.`
342
+ )
343
+ }
344
+ }
391
345
 
392
- // We match the whole path against the regex instead of segments
393
- // This makes sure matches such as wildcard will catch any unmatched routes, even if nested
394
- const routes = matchAgainstConfigs(formattedPaths.nonstandardPathname, configs)
346
+ return Object.assign(acc, {
347
+ [config.pattern]: config,
348
+ })
349
+ }, {})
350
+ }
395
351
 
396
- if (routes == null) {
397
- return undefined
398
- }
399
- // This will always be empty if full path matched
400
- return createNestedStateObject(
401
- cleanPath,
402
- formattedPaths.url.hash.slice(1),
403
- routes,
404
- configs,
405
- initialRoutes
406
- )
352
+ function getConfigsWithRegexes(configs: RouteConfig[]) {
353
+ return configs.map((c) => ({
354
+ ...c,
355
+ // Add `$` to the regex to make sure it matches till end of the path and not just beginning
356
+ // @modified - start
357
+ // regex: c.regex ? new RegExp(c.regex.source + '$') : undefined,
358
+ regex: c.pattern
359
+ ? new RegExp(`^(${c.pattern.split('/').map(formatRegexPattern).join('')})$`)
360
+ : undefined,
361
+ // @modified - end
362
+ }))
407
363
  }
408
364
 
409
365
  const joinPaths = (...paths: string[]): string =>
@@ -412,10 +368,14 @@ const joinPaths = (...paths: string[]): string =>
412
368
  .filter(Boolean)
413
369
  .join('/')
414
370
 
415
- function matchAgainstConfigs(remaining: string, configs: RouteConfig[]): ParsedRoute[] | undefined {
371
+ const matchAgainstConfigs = (remaining: string, configs: RouteConfig[]) => {
416
372
  let routes: ParsedRoute[] | undefined
417
373
  let remainingPath = remaining
418
374
 
375
+ // @modified - start
376
+ const allParams = Object.create(null)
377
+ // @modified - end
378
+
419
379
  // Go through all configs, and see if the next path segment matches our regex
420
380
  for (const config of configs) {
421
381
  if (!config.regex) {
@@ -425,112 +385,104 @@ function matchAgainstConfigs(remaining: string, configs: RouteConfig[]): ParsedR
425
385
  const match = remainingPath.match(config.regex)
426
386
 
427
387
  // If our regex matches, we need to extract params from the path
428
- if (!match) {
429
- continue
430
- }
431
-
432
- // TODO: Add support for wildcard routes
433
- const matchedParams = config.pattern
434
- ?.split('/')
435
- .filter((p) => p.match(/^[:*]/))
436
- .reduce<Record<string, any>>((acc, p, i) => {
437
- if (p.match(/^\*/)) {
438
- return {
439
- ...acc,
440
- [p]: match![(i + 1) * 2], //?.replace(/\//, ""),
388
+ if (match) {
389
+ const matchResult = config.pattern?.split('/').reduce<{
390
+ pos: number // Position of the current path param segment in the path (e.g in pattern `a/:b/:c`, `:a` is 0 and `:b` is 1)
391
+ matchedParams: Record<string, Record<string, string>> // The extracted params
392
+ }>(
393
+ (acc, p, index) => {
394
+ if (!p.startsWith(':')) {
395
+ return acc
441
396
  }
442
- }
443
- return Object.assign(acc, {
444
- // The param segments appear every second item starting from 2 in the regex match result.
445
- // This will only work if we ensure groups aren't included in the match.
446
- [p]: match![(i + 1) * 2]?.replace(/\//, ''),
447
- })
448
- }, {})
449
397
 
450
- const routeFromName = (name: string) => {
451
- const config = configs.find((c) => c.screen === name)
452
- if (!config?.path) {
453
- return { name }
454
- }
398
+ // Path parameter so increment position for the segment
399
+ acc.pos += 1
455
400
 
456
- const segments = config.path.split('/')
401
+ const decodedParamSegment = decodeURIComponentSafe(
402
+ // @modified: use decodeURIComponent**Safe**
403
+ // The param segments appear every second item starting from 2 in the regex match result
404
+ match![(acc.pos + 1) * 2]
405
+ // Remove trailing slash
406
+ .replace(/\/$/, '')
407
+ )
457
408
 
458
- const params: Record<string, any> = {}
409
+ Object.assign(acc.matchedParams, {
410
+ [p]: Object.assign(acc.matchedParams[p] || {}, {
411
+ [index]: decodedParamSegment,
412
+ }),
413
+ })
459
414
 
460
- segments
461
- .filter((p) => p.match(/^[:*]/))
462
- .forEach((p) => {
463
- let value = matchedParams[p]
464
- if (value) {
465
- if (p.match(/^\*/)) {
466
- // Convert to an array before providing as a route.
467
- value = value?.split('/').filter(Boolean)
468
- }
415
+ return acc
416
+ },
417
+ { pos: -1, matchedParams: {} }
418
+ )
469
419
 
470
- const key = p.replace(/^[:*]/, '').replace(/\?$/, '')
471
- params[key] = config.parse?.[key] ? config.parse[key](value) : value
472
- }
420
+ const matchedParams = matchResult.matchedParams || {}
421
+
422
+ routes = config.routeNames.map((name) => {
423
+ const routeConfig = configs.find((c) => {
424
+ // Check matching name AND pattern in case same screen is used at different levels in config
425
+ return c.screen === name && config.pattern.startsWith(c.pattern)
473
426
  })
474
427
 
475
- if (params && Object.keys(params).length) {
476
- return { name, params }
477
- }
428
+ // Normalize pattern to remove any leading, trailing slashes, duplicate slashes etc.
429
+ const normalizedPath = routeConfig?.path.split('/').filter(Boolean).join('/')
478
430
 
479
- return { name }
480
- }
431
+ // Get the number of segments in the initial pattern
432
+ const numInitialSegments = routeConfig?.pattern
433
+ // Extract the prefix from the pattern by removing the ending path pattern (e.g pattern=`a/b/c/d` and normalizedPath=`c/d` becomes `a/b`)
434
+ .replace(new RegExp(`${escape(normalizedPath!)}$`), '')
435
+ ?.split('/').length
481
436
 
482
- routes = config.routeNames.map((name) => {
483
- if (!config._route) {
484
- return { ...routeFromName(name) }
485
- }
486
- return {
487
- ...routeFromName(name),
488
- _route: config._route,
489
- }
490
- })
491
-
492
- // TODO: Maybe we should warn / assert if multiple slugs use the same param name.
493
- const combinedParams = routes.reduce<Record<string, any>>(
494
- (acc, r) => Object.assign(acc, r.params),
495
- {}
496
- )
437
+ const params = normalizedPath
438
+ ?.split('/')
439
+ .reduce<Record<string, unknown>>((acc, p, index) => {
440
+ if (!p.startsWith(':')) {
441
+ return acc
442
+ }
497
443
 
498
- const hasCombinedParams = Object.keys(combinedParams).length > 0
444
+ // Get the real index of the path parameter in the matched path
445
+ // by offsetting by the number of segments in the initial pattern
446
+ const offset = numInitialSegments ? numInitialSegments - 1 : 0
447
+ // @modified - start
448
+ // const value = matchedParams[p]?.[index + offset]
449
+ const value = getParamValue(p, matchedParams[p]?.[index + offset])
450
+ // @modified - end
451
+
452
+ if (value) {
453
+ const key = p.replace(/^:/, '').replace(/\?$/, '')
454
+ acc[key] = routeConfig?.parse?.[key] ? routeConfig.parse[key](value as any) : value
455
+ }
499
456
 
500
- // Combine all params so a route `[foo]/[bar]/other.js` has access to `{ foo, bar }`
501
- routes = routes.map((r) => {
502
- if (hasCombinedParams) {
503
- r.params = combinedParams
504
- }
505
- return r
506
- })
457
+ return acc
458
+ }, {})
507
459
 
508
- remainingPath = remainingPath.replace(match[1], '')
460
+ if (params && Object.keys(params).length) {
461
+ return { name, params }
462
+ }
509
463
 
510
- break
511
- }
464
+ return { name }
465
+ })
512
466
 
513
- return routes
514
- }
467
+ remainingPath = remainingPath.replace(match[1], '')
515
468
 
516
- function equalHeritage(a: string[], b: string[]): boolean {
517
- if (a.length !== b.length) {
518
- return false
519
- }
520
- for (let i = 0; i < a.length; i++) {
521
- if (a[i].localeCompare(b[i]) !== 0) {
522
- return false
469
+ break
523
470
  }
524
471
  }
525
- return true
472
+
473
+ // @modified - start
474
+ populateParams(routes, allParams)
475
+ // @modified - end
476
+
477
+ return { routes, remainingPath }
526
478
  }
527
479
 
528
480
  const createNormalizedConfigs = (
529
481
  screen: string,
530
482
  routeConfig: PathConfigMap<object>,
531
483
  routeNames: string[] = [],
532
- initials: InitialRouteConfig[] = [],
533
- parentScreens: string[] = [],
484
+ initials: InitialRouteConfig[],
485
+ parentScreens: string[],
534
486
  parentPattern?: string
535
487
  ): RouteConfig[] => {
536
488
  const configs: RouteConfig[] = []
@@ -539,19 +491,16 @@ const createNormalizedConfigs = (
539
491
 
540
492
  parentScreens.push(screen)
541
493
 
542
- const config = (routeConfig as any)[screen]
494
+ const config = routeConfig[screen]
543
495
 
544
496
  if (typeof config === 'string') {
545
- // TODO: This should never happen with the addition of `_route`
546
-
547
497
  // If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
548
498
  const pattern = parentPattern ? joinPaths(parentPattern, config) : config
549
499
 
550
- configs.push(createConfigItem(screen, routeNames, pattern, config, false))
500
+ configs.push(createConfigItem(screen, routeNames, pattern, config))
551
501
  } else if (typeof config === 'object') {
552
502
  let pattern: string | undefined
553
503
 
554
- const { _route } = config
555
504
  // if an object is specified as the value (e.g. Foo: { ... }),
556
505
  // it can have `path` property and
557
506
  // it could have `screens` prop which has nested configs
@@ -567,17 +516,12 @@ const createNormalizedConfigs = (
567
516
  ? joinPaths(parentPattern || '', config.path || '')
568
517
  : config.path || ''
569
518
 
519
+ // @modified - start
520
+ // configs.push(createConfigItem(screen, routeNames, pattern!, config.path, config.parse))
570
521
  configs.push(
571
- createConfigItem(
572
- screen,
573
- routeNames,
574
- pattern!,
575
- config.path,
576
- config.screens ? !!Object.keys(config.screens)?.length : false,
577
- config.parse,
578
- _route
579
- )
522
+ createConfigItem(screen, routeNames, pattern!, config.path, config.parse, config)
580
523
  )
524
+ // @modified - end
581
525
  }
582
526
 
583
527
  if (config.screens) {
@@ -609,44 +553,32 @@ const createNormalizedConfigs = (
609
553
  return configs
610
554
  }
611
555
 
612
- function formatRegexPattern(it: string): string {
613
- // Allow spaces in file path names.
614
- it = it.replace(' ', '%20')
615
-
616
- if (it.startsWith(':')) {
617
- // TODO: Remove unused match group
618
- return `(([^/]+\\/)${it.endsWith('?') ? '?' : ''})`
619
- }
620
-
621
- if (it.startsWith('*')) {
622
- return `((.*\\/)${it.endsWith('?') ? '?' : ''})`
623
- }
624
-
625
- // Strip groups from the matcher
626
- if (matchGroupName(it) != null) {
627
- // Groups are optional segments
628
- // this enables us to match `/bar` and `/(foo)/bar` for the same route
629
- // NOTE: Ignore this match in the regex to avoid capturing the group
630
- return `(?:${escape(it)}\\/)?`
631
- }
632
-
633
- return escape(it) + `\\/`
634
- }
635
-
636
556
  const createConfigItem = (
637
557
  screen: string,
638
558
  routeNames: string[],
639
559
  pattern: string,
640
560
  path: string,
641
- hasChildren?: boolean,
642
- parse?: ParseConfig,
643
- _route?: any
561
+ parse: ParseConfig | undefined = undefined,
562
+ // @modified - start
563
+ config: Record<string, any> = {}
564
+ // @modified - end
644
565
  ): RouteConfig => {
645
566
  // Normalize pattern to remove any leading, trailing slashes, duplicate slashes etc.
646
567
  pattern = pattern.split('/').filter(Boolean).join('/')
647
568
 
648
569
  const regex = pattern
649
- ? new RegExp(`^(${pattern.split('/').map(formatRegexPattern).join('')})$`)
570
+ ? new RegExp(
571
+ `^(${pattern
572
+ .split('/')
573
+ .map((it) => {
574
+ if (it.startsWith(':')) {
575
+ return `(([^/]+\\/)${it.endsWith('?') ? '?' : ''})`
576
+ }
577
+
578
+ return `${it === '*' ? '.*' : escape(it)}\\/`
579
+ })
580
+ .join('')})`
581
+ )
650
582
  : undefined
651
583
 
652
584
  return {
@@ -657,17 +589,17 @@ const createConfigItem = (
657
589
  // The routeNames array is mutated, so copy it to keep the current state
658
590
  routeNames: [...routeNames],
659
591
  parse,
660
- userReadableName: [...routeNames.slice(0, -1), path || screen].join('/'),
661
- hasChildren: !!hasChildren,
662
- _route,
592
+ // @modified - start
593
+ ...createConfigItemAdditionalProperties(screen, pattern, routeNames, config),
594
+ // @modified - end
663
595
  }
664
596
  }
665
597
 
666
598
  const findParseConfigForRoute = (
667
599
  routeName: string,
668
- routeConfigs: RouteConfig[]
600
+ flatConfig: RouteConfig[]
669
601
  ): ParseConfig | undefined => {
670
- for (const config of routeConfigs) {
602
+ for (const config of flatConfig) {
671
603
  if (routeName === config.routeNames[config.routeNames.length - 1]) {
672
604
  return config.parse
673
605
  }
@@ -683,10 +615,17 @@ const findInitialRoute = (
683
615
  initialRoutes: InitialRouteConfig[]
684
616
  ): string | undefined => {
685
617
  for (const config of initialRoutes) {
686
- if (equalHeritage(parentScreens, config.parentScreens)) {
687
- // If the parents are the same but the route name doesn't match the initial route
688
- // then we return the initial route.
689
- return routeName !== config.initialRouteName ? config.initialRouteName : undefined
618
+ if (parentScreens.length === config.parentScreens.length) {
619
+ let sameParents = true
620
+ for (let i = 0; i < parentScreens.length; i++) {
621
+ if (parentScreens[i].localeCompare(config.parentScreens[i]) !== 0) {
622
+ sameParents = false
623
+ break
624
+ }
625
+ }
626
+ if (sameParents) {
627
+ return routeName !== config.initialRouteName ? config.initialRouteName : undefined
628
+ }
690
629
  }
691
630
  }
692
631
  return undefined
@@ -695,9 +634,9 @@ const findInitialRoute = (
695
634
  // returns state object with values depending on whether
696
635
  // it is the end of state and if there is initialRoute for this level
697
636
  const createStateObject = (
637
+ initialRoute: string | undefined,
698
638
  route: ParsedRoute,
699
- isEmpty: boolean,
700
- initialRoute?: string
639
+ isEmpty: boolean
701
640
  ): InitialState => {
702
641
  if (isEmpty) {
703
642
  if (initialRoute) {
@@ -705,29 +644,37 @@ const createStateObject = (
705
644
  index: 1,
706
645
  routes: [{ name: initialRoute }, route],
707
646
  }
647
+ // biome-ignore lint/style/noUselessElse: not changing forked code
648
+ } else {
649
+ return {
650
+ routes: [route],
651
+ }
708
652
  }
709
- return {
710
- routes: [route],
711
- }
712
- }
713
-
714
- if (initialRoute) {
715
- return {
716
- index: 1,
717
- routes: [{ name: initialRoute }, { ...route, state: { routes: [] } }],
653
+ // biome-ignore lint/style/noUselessElse: not changing forked code
654
+ } else {
655
+ if (initialRoute) {
656
+ return {
657
+ index: 1,
658
+ routes: [{ name: initialRoute }, { ...route, state: { routes: [] } }],
659
+ }
660
+ // biome-ignore lint/style/noUselessElse: not changing forked code
661
+ } else {
662
+ return {
663
+ routes: [{ ...route, state: { routes: [] } }],
664
+ }
718
665
  }
719
666
  }
720
- return {
721
- routes: [{ ...route, state: { routes: [] } }],
722
- }
723
667
  }
724
668
 
725
669
  const createNestedStateObject = (
726
- path: string,
727
- hash: string | undefined,
670
+ // @modified - start
671
+ // path: string,
672
+ { path, ...restPathData }: ReturnType<typeof getUrlWithReactNavigationConcessions>,
673
+ // @modified - end
728
674
  routes: ParsedRoute[],
729
- routeConfigs: RouteConfig[],
730
- initialRoutes: InitialRouteConfig[]
675
+ initialRoutes: InitialRouteConfig[],
676
+ flatConfig?: RouteConfig[],
677
+ hash?: string // @modified: added
731
678
  ) => {
732
679
  let route = routes.shift() as ParsedRoute
733
680
  const parentScreens: string[] = []
@@ -736,7 +683,7 @@ const createNestedStateObject = (
736
683
 
737
684
  parentScreens.push(route.name)
738
685
 
739
- const state: InitialState = createStateObject(route, routes.length === 0, initialRoute)
686
+ const state: InitialState = createStateObject(initialRoute, route, routes.length === 0)
740
687
 
741
688
  if (routes.length > 0) {
742
689
  let nestedState = state
@@ -747,9 +694,9 @@ const createNestedStateObject = (
747
694
  const nestedStateIndex = nestedState.index || nestedState.routes.length - 1
748
695
 
749
696
  nestedState.routes[nestedStateIndex].state = createStateObject(
697
+ initialRoute,
750
698
  route,
751
- routes.length === 0,
752
- initialRoute
699
+ routes.length === 0
753
700
  )
754
701
 
755
702
  if (routes.length > 0) {
@@ -761,81 +708,43 @@ const createNestedStateObject = (
761
708
  }
762
709
 
763
710
  route = findFocusedRoute(state) as ParsedRoute
764
-
765
- // Remove groups from the path while preserving a trailing slash.
766
- route.path = path
767
-
768
- const params = parseQueryParams(route.path, findParseConfigForRoute(route.name, routeConfigs))
711
+ // @modified - start
712
+ // route.path = path
713
+ route.path = restPathData.pathWithoutGroups
714
+ // @modified - end
715
+
716
+ // @modified - start
717
+ // const params = parseQueryParams(
718
+ // path,
719
+ // flatConfig ? findParseConfigForRoute(route.name, flatConfig) : undefined
720
+ // )
721
+ const params = parseQueryParamsExtended(
722
+ path,
723
+ route,
724
+ flatConfig ? findParseConfigForRoute(route.name, flatConfig) : undefined,
725
+ hash
726
+ )
727
+ // @modified - end
769
728
 
770
729
  if (params) {
771
- route.params = Object.assign(Object.create(null), route.params) as Record<string, any>
772
- for (const [name, value] of Object.entries(params)) {
773
- if (route.params?.[name]) {
774
- if (process.env.NODE_ENV !== 'production') {
775
- console.warn(
776
- `Route '/${route.name}' with param '${name}' was specified both in the path and as a param, removing from path`
777
- )
778
- }
779
- }
780
-
781
- if (!route.params?.[name]) {
782
- route.params[name] = value
783
- continue
784
- }
785
- }
786
-
787
- if (Object.keys(route.params).length === 0) {
788
- delete route.params
789
- }
790
- }
791
-
792
- if (hash) {
793
- route.params = Object.assign(Object.create(null), route.params) as Record<string, any>
794
- route.params['#'] = hash
730
+ route.params = { ...route.params, ...params }
795
731
  }
796
732
 
797
733
  return state
798
734
  }
799
735
 
800
- const parseQueryParams = (path: string, parseConfig?: Record<string, (value: string) => any>) => {
801
- const query = path.split('?')[1]
802
- const searchParams = new URLSearchParams(query)
803
- const params = Object.fromEntries(
804
- // @ts-ignore: [Symbol.iterator] is indeed, available on every platform.
805
- searchParams
806
- )
807
-
808
- if (parseConfig) {
809
- Object.keys(params).forEach((name) => {
810
- if (Object.hasOwnProperty.call(parseConfig, name) && typeof params[name] === 'string') {
811
- params[name] = parseConfig[name](params[name] as string)
812
- }
813
- })
814
- }
815
-
816
- return Object.keys(params).length ? params : undefined
817
- }
818
-
819
- const baseUrlCache = new Map<string, RegExp>()
820
-
821
- function getBaseUrlRegex(baseUrl: string) {
822
- if (baseUrlCache.has(baseUrl)) {
823
- return baseUrlCache.get(baseUrl)!
824
- }
825
- const regex = new RegExp(`^\\/?${escape(baseUrl)}`, 'g')
826
- baseUrlCache.set(baseUrl, regex)
827
- return regex
828
- }
829
-
830
- export function stripBaseUrl(
831
- path: string,
832
- baseUrl: string | undefined = process.env.EXPO_BASE_URL
833
- ) {
834
- if (process.env.NODE_ENV !== 'development') {
835
- if (baseUrl) {
836
- const reg = getBaseUrlRegex(baseUrl)
837
- return path.replace(/^\/+/g, '/').replace(reg, '')
838
- }
839
- }
840
- return path
841
- }
736
+ // @modified: commenting out unused code
737
+ // const parseQueryParams = (path: string, parseConfig?: Record<string, (value: string) => any>) => {
738
+ // const query = path.split('?')[1]
739
+ // const params = queryString.parse(query)
740
+
741
+ // if (parseConfig) {
742
+ // Object.keys(params).forEach((name) => {
743
+ // if (Object.hasOwnProperty.call(parseConfig, name) && typeof params[name] === 'string') {
744
+ // params[name] = parseConfig[name](params[name] as string)
745
+ // }
746
+ // })
747
+ // }
748
+
749
+ // return Object.keys(params).length ? params : undefined
750
+ // }