expo-router 0.0.35 → 0.0.37

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 (60) hide show
  1. package/build/Route.d.ts +6 -5
  2. package/build/Route.d.ts.map +1 -1
  3. package/build/Route.js +13 -0
  4. package/build/Route.js.map +1 -1
  5. package/build/fork/getPathFromState.d.ts +4 -1
  6. package/build/fork/getPathFromState.d.ts.map +1 -1
  7. package/build/fork/getPathFromState.js +272 -143
  8. package/build/fork/getPathFromState.js.map +1 -1
  9. package/build/fork/getStateFromPath.d.ts.map +1 -1
  10. package/build/fork/getStateFromPath.js +260 -238
  11. package/build/fork/getStateFromPath.js.map +1 -1
  12. package/build/getLinkingConfig.d.ts +1 -0
  13. package/build/getLinkingConfig.d.ts.map +1 -1
  14. package/build/getLinkingConfig.js +21 -8
  15. package/build/getLinkingConfig.js.map +1 -1
  16. package/build/getRoutes.d.ts +1 -1
  17. package/build/getRoutes.d.ts.map +1 -1
  18. package/build/getRoutes.js +76 -25
  19. package/build/getRoutes.js.map +1 -1
  20. package/build/layouts/withLayoutContext.d.ts +3 -1
  21. package/build/layouts/withLayoutContext.d.ts.map +1 -1
  22. package/build/layouts/withLayoutContext.js +7 -5
  23. package/build/layouts/withLayoutContext.js.map +1 -1
  24. package/build/link/Link.js +3 -1
  25. package/build/link/Link.js.map +1 -1
  26. package/build/link/href.d.ts.map +1 -1
  27. package/build/link/href.js +3 -10
  28. package/build/link/href.js.map +1 -1
  29. package/build/link/path.d.ts +2 -0
  30. package/build/link/path.d.ts.map +1 -0
  31. package/build/link/path.js +143 -0
  32. package/build/link/path.js.map +1 -0
  33. package/build/link/useHref.d.ts.map +1 -1
  34. package/build/link/useHref.js +15 -31
  35. package/build/link/useHref.js.map +1 -1
  36. package/build/link/useLinkToPath.d.ts.map +1 -1
  37. package/build/link/useLinkToPath.js +22 -9
  38. package/build/link/useLinkToPath.js.map +1 -1
  39. package/build/link/useLinkToPathProps.d.ts.map +1 -1
  40. package/build/link/useLinkToPathProps.js +2 -1
  41. package/build/link/useLinkToPathProps.js.map +1 -1
  42. package/build/link/useLinkingContext.d.ts +3 -0
  43. package/build/link/useLinkingContext.d.ts.map +1 -0
  44. package/build/link/useLinkingContext.js +13 -0
  45. package/build/link/useLinkingContext.js.map +1 -0
  46. package/build/matchers.d.ts +1 -0
  47. package/build/matchers.d.ts.map +1 -1
  48. package/build/matchers.js +11 -0
  49. package/build/matchers.js.map +1 -1
  50. package/build/useScreens.d.ts.map +1 -1
  51. package/build/useScreens.js +14 -9
  52. package/build/useScreens.js.map +1 -1
  53. package/build/views/Layout.js +1 -1
  54. package/build/views/Layout.js.map +1 -1
  55. package/build/views/Sitemap.js +35 -28
  56. package/build/views/Sitemap.js.map +1 -1
  57. package/build/views/Unmatched.d.ts.map +1 -1
  58. package/build/views/Unmatched.js +6 -4
  59. package/build/views/Unmatched.js.map +1 -1
  60. package/package.json +2 -2
package/build/Route.d.ts CHANGED
@@ -1,18 +1,18 @@
1
- import React, { ReactNode } from "react";
1
+ import { ReactNode } from "react";
2
2
  /** The list of input keys will become optional, everything else will remain the same. */
3
3
  export declare type PickPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
4
4
  export declare type RouteNode = {
5
+ /** Load a route into memory. Returns the exports from a route. */
6
+ loadRoute: () => any;
7
+ /** Loaded initial route name. */
8
+ initialRouteName?: string;
5
9
  /** nested routes */
6
10
  children: RouteNode[];
7
- /** Lazily get the React component */
8
- getComponent: () => React.ComponentType<any>;
9
11
  /** Is the route a dynamic path */
10
12
  dynamic: null | {
11
13
  name: string;
12
14
  deep: boolean;
13
15
  };
14
- /** All static exports from the file. */
15
- getExtras: () => Record<string, any>;
16
16
  /** `index`, `error-boundary`, etc. */
17
17
  route: string;
18
18
  /** require.context key, used for matching children. */
@@ -31,5 +31,6 @@ export declare function Route({ children, node, }: {
31
31
  node: RouteNode;
32
32
  }): JSX.Element;
33
33
  export declare function useRootRoute(): RouteNode | null;
34
+ export declare function sortRoutesWithInitial(initialRouteName?: string): (a: RouteNode, b: RouteNode) => number;
34
35
  export declare function sortRoutes(a: RouteNode, b: RouteNode): number;
35
36
  //# sourceMappingURL=Route.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Route.d.ts","sourceRoot":"","sources":["../src/Route.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAc,MAAM,OAAO,CAAC;AAKrD,yFAAyF;AACzF,oBAAY,WAAW,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GACxD,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAEtB,oBAAY,SAAS,GAAG;IACtB,oBAAoB;IACpB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,qCAAqC;IACrC,YAAY,EAAE,MAAM,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAC7C,kCAAkC;IAClC,OAAO,EAAE,IAAI,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;IAChD,wCAAwC;IACxC,SAAS,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACrC,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,wFAAwF;IACxF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAWF,+DAA+D;AAC/D,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAE/C;AAED,wBAAgB,aAAa,IAAI,MAAM,CAMtC;AAED,iEAAiE;AACjE,wBAAgB,KAAK,CAAC,EACpB,QAAQ,EACR,IAAI,GACL,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;CACjB,eAkBA;AAED,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAE/C;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CA4B7D"}
1
+ {"version":3,"file":"Route.d.ts","sourceRoot":"","sources":["../src/Route.tsx"],"names":[],"mappings":"AAAA,OAAc,EAAE,SAAS,EAAc,MAAM,OAAO,CAAC;AAKrD,yFAAyF;AACzF,oBAAY,WAAW,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GACxD,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAEtB,oBAAY,SAAS,GAAG;IACtB,kEAAkE;IAClE,SAAS,EAAE,MAAM,GAAG,CAAC;IAErB,iCAAiC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB;IACpB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,kCAAkC;IAClC,OAAO,EAAE,IAAI,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;IAChD,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB,wFAAwF;IACxF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAWF,+DAA+D;AAC/D,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAE/C;AAED,wBAAgB,aAAa,IAAI,MAAM,CAMtC;AAED,iEAAiE;AACjE,wBAAgB,KAAK,CAAC,EACpB,QAAQ,EACR,IAAI,GACL,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;CACjB,eAkBA;AAED,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAE/C;AAED,wBAAgB,qBAAqB,CAAC,gBAAgB,CAAC,EAAE,MAAM,OAClD,SAAS,KAAK,SAAS,KAAG,MAAM,CAW5C;AACD,wBAAgB,UAAU,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,MAAM,CA4B7D"}
package/build/Route.js CHANGED
@@ -35,6 +35,19 @@ export function Route({ children, node, }) {
35
35
  export function useRootRoute() {
36
36
  return useContext(RootRouteNodeContext);
37
37
  }
38
+ export function sortRoutesWithInitial(initialRouteName) {
39
+ return (a, b) => {
40
+ if (initialRouteName) {
41
+ if (a.route === initialRouteName) {
42
+ return -1;
43
+ }
44
+ if (b.route === initialRouteName) {
45
+ return 1;
46
+ }
47
+ }
48
+ return sortRoutes(a, b);
49
+ };
50
+ }
38
51
  export function sortRoutes(a, b) {
39
52
  if (a.dynamic && !b.dynamic) {
40
53
  return 1;
@@ -1 +1 @@
1
- {"version":3,"file":"Route.js","sourceRoot":"","sources":["../src/Route.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { ReactNode, useContext } from \"react\";\n\nimport { RootRouteNodeContext } from \"./context\";\nimport { getNameFromFilePath, matchFragmentName } from \"./matchers\";\n\n/** The list of input keys will become optional, everything else will remain the same. */\nexport type PickPartial<T, K extends keyof T> = Omit<T, K> &\n Partial<Pick<T, K>>;\n\nexport type RouteNode = {\n /** nested routes */\n children: RouteNode[];\n /** Lazily get the React component */\n getComponent: () => React.ComponentType<any>;\n /** Is the route a dynamic path */\n dynamic: null | { name: string; deep: boolean };\n /** All static exports from the file. */\n getExtras: () => Record<string, any>;\n /** `index`, `error-boundary`, etc. */\n route: string;\n /** require.context key, used for matching children. */\n contextKey: string;\n /** Added in-memory */\n generated?: boolean;\n\n /** Internal screens like the directory or the auto 404 should be marked as internal. */\n internal?: boolean;\n};\n\nconst CurrentRoutePathContext = React.createContext<string | null>(null);\n\nconst CurrentRouteContext = React.createContext<RouteNode | null>(null);\n\nif (process.env.NODE_ENV !== \"production\") {\n CurrentRoutePathContext.displayName = \"RoutePath\";\n CurrentRouteContext.displayName = \"Route\";\n}\n\n/** Return the RouteNode at the current contextual boundary. */\nexport function useRouteNode(): RouteNode | null {\n return useContext(CurrentRouteContext);\n}\n\nexport function useContextKey(): string {\n const filename = useContext(CurrentRoutePathContext);\n if (filename == null) {\n throw new Error(\"No filename found. This is likely a bug in expo-router.\");\n }\n return filename;\n}\n\n/** Provides the matching routes and filename to the children. */\nexport function Route({\n children,\n node,\n}: {\n children: ReactNode;\n node: RouteNode;\n}) {\n const normalName = React.useMemo(() => {\n // The root path is `` (empty string) so always prepend `/` to ensure\n // there is some value.\n const normal = \"/\" + getNameFromFilePath(node.contextKey);\n if (!normal.endsWith(\"_layout\")) {\n return normal;\n }\n return normal.replace(/\\/?_layout$/, \"\");\n }, [node.contextKey]);\n\n return (\n <CurrentRoutePathContext.Provider value={normalName}>\n <CurrentRouteContext.Provider value={node}>\n {children}\n </CurrentRouteContext.Provider>\n </CurrentRoutePathContext.Provider>\n );\n}\n\nexport function useRootRoute(): RouteNode | null {\n return useContext(RootRouteNodeContext);\n}\n\nexport function sortRoutes(a: RouteNode, b: RouteNode): number {\n if (a.dynamic && !b.dynamic) {\n return 1;\n }\n if (!a.dynamic && b.dynamic) {\n return -1;\n }\n if (a.dynamic && b.dynamic) {\n if (a.dynamic.deep && !b.dynamic.deep) {\n return 1;\n }\n if (!a.dynamic.deep && b.dynamic.deep) {\n return -1;\n }\n return 0;\n }\n\n const aIndex = a.route === \"index\" || matchFragmentName(a.route) != null;\n const bIndex = b.route === \"index\" || matchFragmentName(b.route) != null;\n\n if (aIndex && !bIndex) {\n return -1;\n }\n if (!aIndex && bIndex) {\n return 1;\n }\n\n return a.route.length - b.route.length;\n}\n"]}
1
+ {"version":3,"file":"Route.js","sourceRoot":"","sources":["../src/Route.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAa,UAAU,EAAE,MAAM,OAAO,CAAC;AAErD,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AA2BpE,MAAM,uBAAuB,GAAG,KAAK,CAAC,aAAa,CAAgB,IAAI,CAAC,CAAC;AAEzE,MAAM,mBAAmB,GAAG,KAAK,CAAC,aAAa,CAAmB,IAAI,CAAC,CAAC;AAExE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE;IACzC,uBAAuB,CAAC,WAAW,GAAG,WAAW,CAAC;IAClD,mBAAmB,CAAC,WAAW,GAAG,OAAO,CAAC;CAC3C;AAED,+DAA+D;AAC/D,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC,mBAAmB,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,MAAM,QAAQ,GAAG,UAAU,CAAC,uBAAuB,CAAC,CAAC;IACrD,IAAI,QAAQ,IAAI,IAAI,EAAE;QACpB,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;KAC5E;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,KAAK,CAAC,EACpB,QAAQ,EACR,IAAI,GAIL;IACC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;QACpC,qEAAqE;QACrE,uBAAuB;QACvB,MAAM,MAAM,GAAG,GAAG,GAAG,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE;YAC/B,OAAO,MAAM,CAAC;SACf;QACD,OAAO,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAC3C,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAEtB,OAAO,CACL,oBAAC,uBAAuB,CAAC,QAAQ,IAAC,KAAK,EAAE,UAAU;QACjD,oBAAC,mBAAmB,CAAC,QAAQ,IAAC,KAAK,EAAE,IAAI,IACtC,QAAQ,CACoB,CACE,CACpC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,UAAU,CAAC,oBAAoB,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,gBAAyB;IAC7D,OAAO,CAAC,CAAY,EAAE,CAAY,EAAU,EAAE;QAC5C,IAAI,gBAAgB,EAAE;YACpB,IAAI,CAAC,CAAC,KAAK,KAAK,gBAAgB,EAAE;gBAChC,OAAO,CAAC,CAAC,CAAC;aACX;YACD,IAAI,CAAC,CAAC,KAAK,KAAK,gBAAgB,EAAE;gBAChC,OAAO,CAAC,CAAC;aACV;SACF;QACD,OAAO,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1B,CAAC,CAAC;AACJ,CAAC;AACD,MAAM,UAAU,UAAU,CAAC,CAAY,EAAE,CAAY;IACnD,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE;QAC3B,OAAO,CAAC,CAAC;KACV;IACD,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,EAAE;QAC3B,OAAO,CAAC,CAAC,CAAC;KACX;IACD,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,EAAE;QAC1B,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE;YACrC,OAAO,CAAC,CAAC;SACV;QACD,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE;YACrC,OAAO,CAAC,CAAC,CAAC;SACX;QACD,OAAO,CAAC,CAAC;KACV;IAED,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,KAAK,OAAO,IAAI,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;IACzE,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,KAAK,OAAO,IAAI,iBAAiB,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;IAEzE,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE;QACrB,OAAO,CAAC,CAAC,CAAC;KACX;IACD,IAAI,CAAC,MAAM,IAAI,MAAM,EAAE;QACrB,OAAO,CAAC,CAAC;KACV;IAED,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;AACzC,CAAC","sourcesContent":["import React, { ReactNode, useContext } from \"react\";\n\nimport { RootRouteNodeContext } from \"./context\";\nimport { getNameFromFilePath, matchFragmentName } from \"./matchers\";\n\n/** The list of input keys will become optional, everything else will remain the same. */\nexport type PickPartial<T, K extends keyof T> = Omit<T, K> &\n Partial<Pick<T, K>>;\n\nexport type RouteNode = {\n /** Load a route into memory. Returns the exports from a route. */\n loadRoute: () => any;\n\n /** Loaded initial route name. */\n initialRouteName?: string;\n /** nested routes */\n children: RouteNode[];\n /** Is the route a dynamic path */\n dynamic: null | { name: string; deep: boolean };\n /** `index`, `error-boundary`, etc. */\n route: string;\n /** require.context key, used for matching children. */\n contextKey: string;\n /** Added in-memory */\n generated?: boolean;\n\n /** Internal screens like the directory or the auto 404 should be marked as internal. */\n internal?: boolean;\n};\n\nconst CurrentRoutePathContext = React.createContext<string | null>(null);\n\nconst CurrentRouteContext = React.createContext<RouteNode | null>(null);\n\nif (process.env.NODE_ENV !== \"production\") {\n CurrentRoutePathContext.displayName = \"RoutePath\";\n CurrentRouteContext.displayName = \"Route\";\n}\n\n/** Return the RouteNode at the current contextual boundary. */\nexport function useRouteNode(): RouteNode | null {\n return useContext(CurrentRouteContext);\n}\n\nexport function useContextKey(): string {\n const filename = useContext(CurrentRoutePathContext);\n if (filename == null) {\n throw new Error(\"No filename found. This is likely a bug in expo-router.\");\n }\n return filename;\n}\n\n/** Provides the matching routes and filename to the children. */\nexport function Route({\n children,\n node,\n}: {\n children: ReactNode;\n node: RouteNode;\n}) {\n const normalName = React.useMemo(() => {\n // The root path is `` (empty string) so always prepend `/` to ensure\n // there is some value.\n const normal = \"/\" + getNameFromFilePath(node.contextKey);\n if (!normal.endsWith(\"_layout\")) {\n return normal;\n }\n return normal.replace(/\\/?_layout$/, \"\");\n }, [node.contextKey]);\n\n return (\n <CurrentRoutePathContext.Provider value={normalName}>\n <CurrentRouteContext.Provider value={node}>\n {children}\n </CurrentRouteContext.Provider>\n </CurrentRoutePathContext.Provider>\n );\n}\n\nexport function useRootRoute(): RouteNode | null {\n return useContext(RootRouteNodeContext);\n}\n\nexport function sortRoutesWithInitial(initialRouteName?: string) {\n return (a: RouteNode, b: RouteNode): number => {\n if (initialRouteName) {\n if (a.route === initialRouteName) {\n return -1;\n }\n if (b.route === initialRouteName) {\n return 1;\n }\n }\n return sortRoutes(a, b);\n };\n}\nexport function sortRoutes(a: RouteNode, b: RouteNode): number {\n if (a.dynamic && !b.dynamic) {\n return 1;\n }\n if (!a.dynamic && b.dynamic) {\n return -1;\n }\n if (a.dynamic && b.dynamic) {\n if (a.dynamic.deep && !b.dynamic.deep) {\n return 1;\n }\n if (!a.dynamic.deep && b.dynamic.deep) {\n return -1;\n }\n return 0;\n }\n\n const aIndex = a.route === \"index\" || matchFragmentName(a.route) != null;\n const bIndex = b.route === \"index\" || matchFragmentName(b.route) != null;\n\n if (aIndex && !bIndex) {\n return -1;\n }\n if (!aIndex && bIndex) {\n return 1;\n }\n\n return a.route.length - b.route.length;\n}\n"]}
@@ -34,6 +34,9 @@ export declare type State = NavigationState | Omit<PartialState<NavigationState>
34
34
  * @param options Extra options to fine-tune how to serialize the path.
35
35
  * @returns Path representing the state, e.g. /foo/bar?count=42.
36
36
  */
37
- export default function getPathFromState<ParamList extends object>(state: State, options?: Options<ParamList>): string;
37
+ export default function getPathFromState<ParamList extends object>(state: State, _options?: Options<ParamList> & {
38
+ preserveFragments?: boolean;
39
+ preserveDynamicRoutes?: boolean;
40
+ }): string;
38
41
  export {};
39
42
  //# sourceMappingURL=getPathFromState.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getPathFromState.d.ts","sourceRoot":"","sources":["../../src/fork/getPathFromState.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,aAAa,EAEd,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EAEb,MAAM,2BAA2B,CAAC;AAKnC,aAAK,OAAO,CAAC,SAAS,SAAS,MAAM,IAAI;IACvC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACnC,CAAC;AAEF,oBAAY,KAAK,GACb,eAAe,GACf,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC;AA6CjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,SAAS,SAAS,MAAM,EAC/D,KAAK,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAC3B,MAAM,CAiMR"}
1
+ {"version":3,"file":"getPathFromState.d.ts","sourceRoot":"","sources":["../../src/fork/getPathFromState.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,aAAa,EAEd,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EAEb,MAAM,2BAA2B,CAAC;AASnC,aAAK,OAAO,CAAC,SAAS,SAAS,MAAM,IAAI;IACvC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACnC,CAAC;AAEF,oBAAY,KAAK,GACb,eAAe,GACf,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC;AAgEjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,SAAS,SAAS,MAAM,EAC/D,KAAK,EAAE,KAAK,EAEZ,QAAQ,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,qBAAqB,CAAC,EAAE,OAAO,CAAC;CAC5B,GACL,MAAM,CA2BR"}
@@ -1,6 +1,6 @@
1
1
  import { validatePathConfig, } from "@react-navigation/core";
2
2
  import * as queryString from "query-string";
3
- import { matchDeepDynamicRouteName, matchFragmentName } from "../matchers";
3
+ import { matchDeepDynamicRouteName, matchDynamicName, matchFragmentName, } from "../matchers";
4
4
  const getActiveRoute = (state) => {
5
5
  const route = typeof state.index === "number"
6
6
  ? state.routes[state.index]
@@ -30,6 +30,15 @@ function createFakeState(params) {
30
30
  ],
31
31
  };
32
32
  }
33
+ function segmentMatchesConvention(segment) {
34
+ return (segment === "index" ||
35
+ matchDynamicName(segment) != null ||
36
+ matchFragmentName(segment) != null ||
37
+ matchDeepDynamicRouteName(segment) != null);
38
+ }
39
+ function encodeURIComponentPreservingBrackets(str) {
40
+ return encodeURIComponent(str).replace(/%5B/g, "[").replace(/%5D/g, "]");
41
+ }
33
42
  /**
34
43
  * Utility to serialize a navigation state object to a path string.
35
44
  *
@@ -59,175 +68,294 @@ function createFakeState(params) {
59
68
  * @param options Extra options to fine-tune how to serialize the path.
60
69
  * @returns Path representing the state, e.g. /foo/bar?count=42.
61
70
  */
62
- export default function getPathFromState(state, options) {
71
+ export default function getPathFromState(state,
72
+ // @ts-expect-error: non-standard options
73
+ _options = {}) {
63
74
  if (state == null) {
64
75
  throw Error("Got 'undefined' for the navigation state. You must pass a valid state object.");
65
76
  }
66
- if (options) {
77
+ const { preserveFragments, preserveDynamicRoutes, ...options } = _options;
78
+ if (_options) {
67
79
  validatePathConfig(options);
68
80
  }
81
+ const screens = options?.screens;
82
+ // Expo Router disallows usage without a linking config.
83
+ if (!screens) {
84
+ throw Error("You must pass a 'screens' object to 'getPathFromState' to generate a path.");
85
+ }
86
+ return getPathFromResolvedState(state,
69
87
  // Create a normalized configs object which will be easier to use
70
- const configs = options?.screens
71
- ? createNormalizedConfigs(options?.screens)
72
- : {};
73
- let path = "/";
74
- let current = state;
75
- const allParams = {};
76
- while (current) {
77
- let index = typeof current.index === "number" ? current.index : 0;
78
- let route = current.routes[index];
79
- // NOTE(EvanBacon): Fill in current route using state that was passed as params.
88
+ createNormalizedConfigs(screens), { preserveFragments, preserveDynamicRoutes });
89
+ }
90
+ function processParamsWithUserSettings(configItem, params) {
91
+ const stringify = configItem?.stringify;
92
+ return Object.fromEntries(Object.entries(params).map(([key, value]) => [
93
+ key,
94
+ // TODO: Strip nullish values here.
95
+ stringify?.[key] ? stringify[key](value) : String(value),
96
+ ]));
97
+ }
98
+ function deepEqual(a, b) {
99
+ if (a === b) {
100
+ return true;
101
+ }
102
+ if (Array.isArray(a) && Array.isArray(b)) {
103
+ if (a.length !== b.length) {
104
+ return false;
105
+ }
106
+ for (let i = 0; i < a.length; i++) {
107
+ if (!deepEqual(a[i], b[i])) {
108
+ return false;
109
+ }
110
+ }
111
+ return true;
112
+ }
113
+ if (typeof a === "object" && typeof b === "object") {
114
+ const keysA = Object.keys(a);
115
+ const keysB = Object.keys(b);
116
+ if (keysA.length !== keysB.length) {
117
+ return false;
118
+ }
119
+ for (const key of keysA) {
120
+ if (!deepEqual(a[key], b[key])) {
121
+ return false;
122
+ }
123
+ }
124
+ return true;
125
+ }
126
+ return false;
127
+ }
128
+ function walkConfigItems(route, focusedRoute, configs) {
129
+ // NOTE(EvanBacon): Fill in current route using state that was passed as params.
130
+ if (!route.state && isInvalidParams(route.params)) {
131
+ route.state = createFakeState(route.params);
132
+ }
133
+ let pattern = null;
134
+ let focusedParams;
135
+ const collectedParams = {};
136
+ while (route.name in configs) {
137
+ const configItem = configs[route.name];
138
+ const inputPattern = configItem.pattern;
139
+ if (inputPattern == null) {
140
+ // This should never happen in Expo Router.
141
+ throw new Error("Unexpected: No pattern found for route " + route.name);
142
+ }
143
+ pattern = inputPattern;
144
+ if (route.params) {
145
+ const params = processParamsWithUserSettings(configItem, route.params);
146
+ // TODO: Does this need to be a null check?
147
+ if (pattern) {
148
+ Object.assign(collectedParams, params);
149
+ }
150
+ if (deepEqual(focusedRoute, route)) {
151
+ // If this is the focused route, keep the params for later use
152
+ // We save it here since it's been stringified already
153
+ focusedParams = getParamsWithConventionsCollapsed({
154
+ params,
155
+ pattern,
156
+ routeName: route.name,
157
+ });
158
+ }
159
+ }
80
160
  if (!route.state && isInvalidParams(route.params)) {
81
161
  route.state = createFakeState(route.params);
82
162
  }
83
- let pattern;
84
- let focusedParams;
85
- const focusedRoute = getActiveRoute(state);
86
- let currentOptions = configs;
87
- // Keep all the route names that appeared during going deeper in config in case the pattern is resolved to undefined
88
- const nestedRouteNames = [];
89
- let hasNext = true;
90
- while (route.name in currentOptions && hasNext) {
91
- pattern = currentOptions[route.name].pattern;
92
- // @ts-expect-error
93
- nestedRouteNames.push(route.name);
94
- if (route.params) {
95
- const stringify = currentOptions[route.name]?.stringify;
96
- const currentParams = Object.fromEntries(Object.entries(route.params).map(([key, value]) => [
97
- key,
98
- stringify?.[key] ? stringify[key](value) : String(value),
99
- ]));
100
- if (pattern) {
101
- Object.assign(allParams, currentParams);
102
- }
103
- if (focusedRoute === route) {
163
+ // If there is no `screens` property or no nested state, we return pattern
164
+ if (!configItem.screens || route.state === undefined) {
165
+ if (configItem.initialRouteName &&
166
+ configItem.screens &&
167
+ configItem.initialRouteName in configItem.screens &&
168
+ configItem.screens[configItem.initialRouteName]?.pattern) {
169
+ const initialRouteConfig = configItem.screens[configItem.initialRouteName];
170
+ // NOTE(EvanBacon): Big hack to support initial route changes in tab bars.
171
+ pattern = initialRouteConfig.pattern;
172
+ if (focusedParams) {
104
173
  // If this is the focused route, keep the params for later use
105
174
  // We save it here since it's been stringified already
106
- focusedParams = { ...currentParams };
107
- pattern
108
- ?.split("/")
109
- .filter((p) => p.startsWith(":") || p === "*")
110
- // eslint-disable-next-line no-loop-func
111
- .forEach((p) => {
112
- let name;
113
- if (p === "*") {
114
- // NOTE(EvanBacon): Drop the param name matching the wildcard route name -- this is specific to Expo Router.
115
- name = matchDeepDynamicRouteName(route.name) ?? route.name;
116
- }
117
- else {
118
- name = getParamName(p);
119
- }
120
- // Remove the params present in the pattern since we'll only use the rest for query string
121
- if (focusedParams) {
122
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
123
- delete focusedParams[name];
124
- }
175
+ focusedParams = getParamsWithConventionsCollapsed({
176
+ params: focusedParams,
177
+ pattern,
178
+ routeName: route.name,
125
179
  });
126
180
  }
127
181
  }
128
- // If there is no `screens` property or no nested state, we return pattern
129
- if (!currentOptions[route.name].screens || route.state === undefined) {
130
- hasNext = false;
131
- }
132
- else {
133
- index =
134
- typeof route.state.index === "number"
135
- ? route.state.index
136
- : route.state.routes.length - 1;
137
- const nextRoute = route.state.routes[index];
138
- const nestedConfig = currentOptions[route.name].screens;
139
- // if there is config for next route name, we go deeper
140
- if (nestedConfig && nextRoute.name in nestedConfig) {
141
- route = nextRoute;
142
- currentOptions = nestedConfig;
143
- }
144
- else {
145
- // If not, there is no sense in going deeper in config
146
- hasNext = false;
147
- }
148
- }
182
+ break;
149
183
  }
150
- if (pattern === undefined) {
151
- pattern = nestedRouteNames.join("/");
184
+ const index = route.state.index ?? route.state.routes.length - 1;
185
+ const nextRoute = route.state.routes[index];
186
+ const nestedScreens = configItem.screens;
187
+ // if there is config for next route name, we go deeper
188
+ if (nestedScreens && nextRoute.name in nestedScreens) {
189
+ route = nextRoute;
190
+ configs = nestedScreens;
152
191
  }
153
- if (currentOptions[route.name] !== undefined) {
154
- path += pattern
155
- .split("/")
156
- .map((p, i) => {
157
- const name = getParamName(p);
158
- // We don't know what to show for wildcard patterns
159
- // Showing the route name seems ok, though whatever we show here will be incorrect
160
- // Since the page doesn't actually exist
161
- if (p === "*") {
162
- if (i === 0) {
163
- // This can occur when a wildcard matches all routes and the given path was `/`.
164
- return route.path ?? "";
192
+ else {
193
+ // If not, there is no sense in going deeper in config
194
+ break;
195
+ }
196
+ }
197
+ if (pattern && !focusedParams && focusedRoute.params) {
198
+ // If this is the focused route, keep the params for later use
199
+ // We save it here since it's been stringified already
200
+ focusedParams = getParamsWithConventionsCollapsed({
201
+ params: focusedRoute.params,
202
+ pattern,
203
+ routeName: route.name,
204
+ });
205
+ Object.assign(focusedParams, collectedParams);
206
+ }
207
+ if (pattern == null) {
208
+ throw new Error(`No pattern found for route "${route.name}". Options are: ${Object.keys(configs).join(", ")}.`);
209
+ }
210
+ return {
211
+ pattern,
212
+ nextRoute: route,
213
+ focusedParams,
214
+ params: collectedParams,
215
+ };
216
+ }
217
+ function getPathFromResolvedState(state, configs, { preserveFragments, preserveDynamicRoutes, }) {
218
+ let path = "";
219
+ let current = state;
220
+ const allParams = {};
221
+ while (current) {
222
+ path += "/";
223
+ const route = current.routes[current.index ?? 0];
224
+ // NOTE(EvanBacon): Fill in current route using state that was passed as params.
225
+ // if (isInvalidParams(route.params)) {
226
+ if (!route.state && isInvalidParams(route.params)) {
227
+ route.state = createFakeState(route.params);
228
+ }
229
+ const { pattern, params, nextRoute, focusedParams } = walkConfigItems(route, getActiveRoute(current), { ...configs });
230
+ Object.assign(allParams, params);
231
+ path += getPathWithConventionsCollapsed({
232
+ pattern,
233
+ routePath: nextRoute.path,
234
+ params: allParams,
235
+ initialRouteName: configs[nextRoute.name]?.initialRouteName,
236
+ preserveFragments,
237
+ preserveDynamicRoutes,
238
+ });
239
+ if (nextRoute.state) {
240
+ // Continue looping with the next state if available.
241
+ current = nextRoute.state;
242
+ }
243
+ else {
244
+ // Finished crawling state.
245
+ // Check for query params before exiting.
246
+ if (focusedParams) {
247
+ for (const param in focusedParams) {
248
+ // TODO: This is not good. We shouldn't squat strings named "undefined".
249
+ if (focusedParams[param] === "undefined") {
250
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
251
+ delete focusedParams[param];
165
252
  }
166
- // remove existing segments from route.path and return it
167
- // this is used for nested wildcard routes. Without this, the path would add
168
- // all nested segments to the beginning of the wildcard route.
169
- const path = route.path
170
- ?.split("/")
171
- .slice(i + 1)
172
- .join("/");
173
- return path ?? "";
174
253
  }
175
- // If the path has a pattern for a param, put the param in the path
176
- if (p.startsWith(":")) {
177
- const value = allParams[name];
178
- if (value == null) {
179
- // Optional params without value assigned in route.params should be ignored
180
- return "";
181
- }
182
- return value;
254
+ const query = queryString.stringify(focusedParams, { sort: false });
255
+ if (query) {
256
+ path += `?${query}`;
183
257
  }
184
- return encodeURIComponent(p);
185
- })
186
- .join("/");
187
- }
188
- else {
189
- // TODO: Probably fragment routes shouldn't get this far
190
- path += encodeURIComponent(matchFragmentName(route.name)
191
- ? ""
192
- : route.name === "index"
193
- ? ""
194
- : route.name);
258
+ }
259
+ break;
195
260
  }
196
- if (!focusedParams) {
197
- focusedParams = focusedRoute.params;
261
+ }
262
+ return basicSanitizePath(path);
263
+ }
264
+ function getPathWithConventionsCollapsed({ pattern, routePath, params, preserveFragments, preserveDynamicRoutes, initialRouteName, }) {
265
+ const segments = pattern.split("/");
266
+ return segments
267
+ .map((p, i) => {
268
+ const name = getParamName(p);
269
+ // We don't know what to show for wildcard patterns
270
+ // Showing the route name seems ok, though whatever we show here will be incorrect
271
+ // Since the page doesn't actually exist
272
+ if (p === "*") {
273
+ if (i === 0) {
274
+ // This can occur when a wildcard matches all routes and the given path was `/`.
275
+ return routePath;
276
+ }
277
+ // remove existing segments from route.path and return it
278
+ // this is used for nested wildcard routes. Without this, the path would add
279
+ // all nested segments to the beginning of the wildcard route.
280
+ return routePath
281
+ ?.split("/")
282
+ .slice(i + 1)
283
+ .join("/");
198
284
  }
199
- if (route.state) {
200
- path += "/";
285
+ // If the path has a pattern for a param, put the param in the path
286
+ if (p.startsWith(":")) {
287
+ if (preserveDynamicRoutes) {
288
+ return `[${name}]`;
289
+ }
290
+ // Optional params without value assigned in route.params should be ignored
291
+ return params[name];
201
292
  }
202
- else if (focusedParams) {
203
- for (const param in focusedParams) {
204
- if (focusedParams[param] === "undefined") {
205
- // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
206
- delete focusedParams[param];
293
+ if (!preserveFragments && matchFragmentName(p) != null) {
294
+ // When the last part is a fragment it could be a shared URL
295
+ // if the route has an initialRouteName defined, then we should
296
+ // use that as the component path as we can assume it will be shown.
297
+ if (segments.length - 1 === i) {
298
+ if (initialRouteName) {
299
+ // Return an empty string if the init route is ambiguous.
300
+ if (segmentMatchesConvention(initialRouteName)) {
301
+ return "";
302
+ }
303
+ return encodeURIComponentPreservingBrackets(initialRouteName);
207
304
  }
208
305
  }
209
- const query = queryString.stringify(focusedParams, { sort: false });
210
- if (query) {
211
- path += `?${query}`;
212
- }
306
+ return "";
213
307
  }
214
- current = route.state;
308
+ // Preserve dynamic syntax for rehydration
309
+ return encodeURIComponentPreservingBrackets(p);
310
+ })
311
+ .map((v) => v ?? "")
312
+ .join("/");
313
+ }
314
+ /** Given a set of query params and a pattern with possible conventions, collapse the conventions and return the remaining params. */
315
+ function getParamsWithConventionsCollapsed({ pattern, routeName, params, }) {
316
+ const processedParams = { ...params };
317
+ // Remove the params present in the pattern since we'll only use the rest for query string
318
+ const segments = pattern.split("/");
319
+ // Dynamic Routes
320
+ segments
321
+ .filter((segment) => segment.startsWith(":"))
322
+ .forEach((segment) => {
323
+ const name = getParamName(segment);
324
+ delete processedParams[name];
325
+ });
326
+ // Deep Dynamic Routes
327
+ if (segments.some((segment) => segment === "*")) {
328
+ // NOTE(EvanBacon): Drop the param name matching the wildcard route name -- this is specific to Expo Router.
329
+ const name = matchDeepDynamicRouteName(routeName) ?? routeName;
330
+ delete processedParams[name];
215
331
  }
216
- // Remove multiple as well as trailing slashes
217
- path = path.replace(/\/+/g, "/");
218
- path = path.length > 1 ? path.replace(/\/$/, "") : path;
219
- return path;
332
+ return processedParams;
333
+ }
334
+ // Remove multiple as well as trailing slashes
335
+ function basicSanitizePath(path) {
336
+ // Remove duplicate slashes like `foo//bar` -> `foo/bar`
337
+ const simplifiedPath = path.replace(/\/+/g, "/");
338
+ if (simplifiedPath.length <= 1) {
339
+ return simplifiedPath;
340
+ }
341
+ // Remove trailing slash like `foo/bar/` -> `foo/bar`
342
+ return simplifiedPath.replace(/\/$/, "");
220
343
  }
221
344
  // TODO: Make StackRouter not do this...
222
345
  // Detect if the params came from StackRouter using `params` to pass around internal state.
223
346
  function isInvalidParams(params) {
224
- return (!!params &&
225
- "initial" in params &&
226
- "path" in params &&
227
- "screen" in params &&
228
- "params" in params &&
347
+ if (!params) {
348
+ return false;
349
+ }
350
+ if ("params" in params &&
229
351
  typeof params.params === "object" &&
230
- !!params.params);
352
+ !!params.params) {
353
+ return true;
354
+ }
355
+ return ("initial" in params &&
356
+ typeof params.initial === "boolean" &&
357
+ // "path" in params &&
358
+ "screen" in params);
231
359
  }
232
360
  const getParamName = (pattern) => pattern.replace(/^:/, "").replace(/\?$/, "");
233
361
  const joinPaths = (...paths) => []
@@ -256,10 +384,11 @@ const createConfigItem = (config, parentPattern) => {
256
384
  pattern: pattern?.split("/").filter(Boolean).join("/"),
257
385
  stringify: config.stringify,
258
386
  screens,
387
+ initialRouteName: config.initialRouteName,
259
388
  };
260
389
  };
261
- const createNormalizedConfigs = (options, pattern) => Object.fromEntries(Object.entries(options).map(([name, c]) => {
262
- const result = createConfigItem(c, pattern);
263
- return [name, result];
264
- }));
390
+ const createNormalizedConfigs = (options, pattern) => Object.fromEntries(Object.entries(options).map(([name, c]) => [
391
+ name,
392
+ createConfigItem(c, pattern),
393
+ ]));
265
394
  //# sourceMappingURL=getPathFromState.js.map