1import { getActionFromState, LinkingOptions } from '@react-navigation/native';
2
3import { RouteNode } from './Route';
4import { State } from './fork/getPathFromState';
5import { getReactNavigationConfig, Screen } from './getReactNavigationConfig';
6import {
7  addEventListener,
8  getInitialURL,
9  getPathFromState,
10  getStateFromPath,
11} from './link/linking';
12
13export function getNavigationConfig(routes: RouteNode): {
14  initialRouteName?: string;
15  screens: Record<string, Screen>;
16} {
17  return getReactNavigationConfig(routes, true);
18}
19
20export type ExpoLinkingOptions = LinkingOptions<object> & {
21  getPathFromState?: typeof getPathFromState;
22};
23
24export function getLinkingConfig(routes: RouteNode): ExpoLinkingOptions {
25  return {
26    prefixes: [],
27    // @ts-expect-error
28    config: getNavigationConfig(routes),
29    // A custom getInitialURL is used on native to ensure the app always starts at
30    // the root path if it's launched from something other than a deep link.
31    // This helps keep the native functionality working like the web functionality.
32    // For example, if you had a root navigator where the first screen was `/settings` and the second was `/index`
33    // then `/index` would be used on web and `/settings` would be used on native.
34    getInitialURL,
35    subscribe: addEventListener,
36    getStateFromPath: getStateFromPathMemoized,
37    getPathFromState(state: State, options: Parameters<typeof getPathFromState>[1]) {
38      return (
39        getPathFromState(state, {
40          screens: [],
41          ...this.config,
42          ...options,
43        }) ?? '/'
44      );
45    },
46    // Add all functions to ensure the types never need to fallback.
47    // This is a convenience for usage in the package.
48    getActionFromState,
49  };
50}
51
52export const stateCache = new Map<string, any>();
53
54/** We can reduce work by memoizing the state by the pathname. This only works because the options (linking config) theoretically never change.  */
55function getStateFromPathMemoized(path: string, options: Parameters<typeof getStateFromPath>[1]) {
56  const cached = stateCache.get(path);
57  if (cached) {
58    return cached;
59  }
60  const result = getStateFromPath(path, options);
61  stateCache.set(path, result);
62  return result;
63}
64