expo-router 2.0.0-rc.9 → 2.0.1

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 (44) hide show
  1. package/build/ExpoRoot.d.ts.map +1 -1
  2. package/build/fork/getStateFromPath.d.ts +4 -0
  3. package/build/fork/getStateFromPath.d.ts.map +1 -1
  4. package/build/global-state/router-store.d.ts +2 -0
  5. package/build/global-state/router-store.d.ts.map +1 -1
  6. package/build/global-state/routing.d.ts +1 -0
  7. package/build/global-state/routing.d.ts.map +1 -1
  8. package/build/global-state/sort-routes.d.ts +1 -2
  9. package/build/global-state/sort-routes.d.ts.map +1 -1
  10. package/build/hooks.d.ts.map +1 -1
  11. package/build/imperative-api.d.ts.map +1 -1
  12. package/build/link/href.d.ts.map +1 -1
  13. package/build/onboard/Tutorial.d.ts.map +1 -1
  14. package/build/types.d.ts +2 -0
  15. package/build/types.d.ts.map +1 -1
  16. package/build/useDeprecated.d.ts +3 -0
  17. package/build/useDeprecated.d.ts.map +1 -0
  18. package/build/useScreens.d.ts +3 -0
  19. package/build/useScreens.d.ts.map +1 -1
  20. package/build/views/ErrorBoundary.d.ts.map +1 -1
  21. package/build/views/Screen.d.ts.map +1 -1
  22. package/build/views/Sitemap.d.ts.map +1 -1
  23. package/build/views/Splash.d.ts.map +1 -1
  24. package/build/views/Unmatched.d.ts.map +1 -1
  25. package/package.json +4 -4
  26. package/src/ExpoRoot.tsx +14 -2
  27. package/src/fork/getStateFromPath.ts +46 -19
  28. package/src/global-state/router-store.tsx +13 -6
  29. package/src/global-state/routing.ts +8 -1
  30. package/src/global-state/sort-routes.ts +2 -38
  31. package/src/hooks.ts +13 -9
  32. package/src/imperative-api.ts +1 -0
  33. package/src/link/href.ts +9 -7
  34. package/src/onboard/Tutorial.tsx +5 -11
  35. package/src/static/html.tsx +1 -1
  36. package/src/types.ts +2 -0
  37. package/src/useDeprecated.ts +35 -0
  38. package/src/useScreens.tsx +11 -2
  39. package/src/views/ErrorBoundary.tsx +1 -5
  40. package/src/views/Screen.tsx +17 -1
  41. package/src/views/Sitemap.tsx +3 -6
  42. package/src/views/Splash.tsx +5 -5
  43. package/src/views/Unmatched.tsx +4 -12
  44. package/types/react-native-web.d.ts +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoRoot.d.ts","sourceRoot":"","sources":["../src/ExpoRoot.tsx"],"names":[],"mappings":"AACA,OAAc,EAAE,iBAAiB,EAAE,SAAS,EAAY,MAAM,OAAO,CAAC;AAMtE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGzC,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,OAAO,CAAC,EAAE,iBAAiB,CAAC;QAAE,QAAQ,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;CACtD,CAAC;AAgCF,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EAAE,aAAwB,EACjC,GAAG,KAAK,EACT,EAAE,aAAa,eAyBf"}
1
+ {"version":3,"file":"ExpoRoot.d.ts","sourceRoot":"","sources":["../src/ExpoRoot.tsx"],"names":[],"mappings":"AAEA,OAAc,EAAE,iBAAiB,EAAE,SAAS,EAAY,MAAM,OAAO,CAAC;AAMtE,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGzC,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,OAAO,CAAC,EAAE,iBAAiB,CAAC;QAAE,QAAQ,EAAE,SAAS,CAAA;KAAE,CAAC,CAAC;CACtD,CAAC;AAqCF,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EAAE,aAAwB,EACjC,GAAG,KAAK,EACT,EAAE,aAAa,eA2Bf"}
@@ -13,6 +13,10 @@ type InitialRouteConfig = {
13
13
  export type ResultState = PartialState<NavigationState> & {
14
14
  state?: ResultState;
15
15
  };
16
+ export declare function getUrlWithReactNavigationConcessions(path: string): {
17
+ nonstandardPathname: string;
18
+ inputPathnameWithoutHash: string;
19
+ };
16
20
  /**
17
21
  * Utility to parse a path string to initial state object accepted by the container.
18
22
  * This is useful for deep linking when we need to handle the incoming URL.
@@ -1 +1 @@
1
- {"version":3,"file":"getStateFromPath.d.ts","sourceRoot":"","sources":["../../src/fork/getStateFromPath.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAEV,eAAe,EACf,YAAY,EACb,MAAM,2BAA2B,CAAC;AAInC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAKrC,KAAK,OAAO,CAAC,SAAS,SAAS,MAAM,IAAI;IACvC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACnC,CAAC;AAEF,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAC,CAAC;AAe1D,KAAK,kBAAkB,GAAG;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,YAAY,CAAC,eAAe,CAAC,GAAG;IACxD,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAkBF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,SAAS,SAAS,MAAM,EAC/D,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAC3B,WAAW,GAAG,SAAS,CAIzB;AAED,wBAAgB,wBAAwB,CAAC,SAAS,SAAS,MAAM,EAC/D,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC;;;;;;;;;;;;;;EA+C7B"}
1
+ {"version":3,"file":"getStateFromPath.d.ts","sourceRoot":"","sources":["../../src/fork/getStateFromPath.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAEV,eAAe,EACf,YAAY,EACb,MAAM,2BAA2B,CAAC;AAKnC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAKrC,KAAK,OAAO,CAAC,SAAS,SAAS,MAAM,IAAI;IACvC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACnC,CAAC;AAEF,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAC,CAAC;AAe1D,KAAK,kBAAkB,GAAG;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,YAAY,CAAC,eAAe,CAAC,GAAG;IACxD,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAQF,wBAAgB,oCAAoC,CAAC,IAAI,EAAE,MAAM;;;EAYhE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,SAAS,SAAS,MAAM,EAC/D,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAC3B,WAAW,GAAG,SAAS,CAIzB;AAED,wBAAgB,wBAAwB,CAAC,SAAS,SAAS,MAAM,EAC/D,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC;;;;;;;;;;;;;;EA+C7B"}
@@ -15,6 +15,7 @@ export declare class RouterStore {
15
15
  rootComponent: ComponentType;
16
16
  linking: ExpoLinkingOptions | undefined;
17
17
  isReady: boolean;
18
+ private hasAttemptedToHideSplash;
18
19
  initialState: ResultState | undefined;
19
20
  rootState: ResultState | undefined;
20
21
  nextState: ResultState | undefined;
@@ -26,6 +27,7 @@ export declare class RouterStore {
26
27
  linkTo: (href: string, event?: string | undefined) => void;
27
28
  getSortedRoutes: () => RouteNode[];
28
29
  goBack: () => void;
30
+ canGoBack: () => boolean;
29
31
  push: (url: import("../link/href").Href) => void;
30
32
  replace: (url: import("../link/href").Href) => void;
31
33
  setParams: (params?: Record<string, string | number> | undefined) => any;
@@ -1 +1 @@
1
- {"version":3,"file":"router-store.d.ts","sourceRoot":"","sources":["../../src/global-state/router-store.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,iCAAiC,EAGlC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAiC,aAAa,EAAY,MAAM,OAAO,CAAC;AAE/E,OAAO,EAAE,SAAS,EAAyB,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAoB,MAAM,qBAAqB,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAM1C;;;;GAIG;AACH,qBAAa,WAAW;IACtB,SAAS,EAAG,SAAS,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAG,aAAa,CAAC;IAC9B,OAAO,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACxC,OAAO,EAAE,OAAO,CAAS;IAEzB,YAAY,EAAE,WAAW,GAAG,SAAS,CAAC;IACtC,SAAS,EAAE,WAAW,GAAG,SAAS,CAAC;IACnC,SAAS,EAAE,WAAW,GAAG,SAAS,CAAC;IACnC,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAElC,aAAa,EAAG,iCAAiC,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;IACjF,yBAAyB,EAAG,MAAM,IAAI,CAAC;IAEvC,oBAAoB,YAAiB,IAAI,EAAI;IAC7C,gBAAgB,YAAiB,IAAI,EAAI;IAEzC,MAAM,qDAAqB;IAC3B,eAAe,oBAA8B;IAC7C,MAAM,aAAqB;IAC3B,IAAI,6CAAmB;IACvB,OAAO,6CAAsB;IAC7B,SAAS,gEAAwB;IAEjC,UAAU,CACR,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,iCAAiC,CAAC,eAAe,CAAC,aAAa,CAAC,EAC/E,eAAe,CAAC,EAAE,GAAG;IAgGvB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,cAAQ;IAWjD,YAAY,CAAC,KAAK,EAAE,WAAW;IAgB/B,kBAAkB;IAIlB,uEAAuE;IACvE,OAAO,aAKL;IACF,oBAAoB,eAAgB,MAAM,IAAI,mBAG5C;IACF,gBAAgB,eAAgB,MAAM,IAAI,mBAGxC;IACF,QAAQ,aAEN;IACF,iBAAiB,oBAEf;IACF,iBAAiB,kBAEf;CACH;AAED,eAAO,MAAM,KAAK,aAAoB,CAAC;AAEvC,wBAAgB,aAAa,gBAM5B;AAaD,wBAAgB,iBAAiB,gBAOhC;AAED,wBAAgB,iBAAiB,cAOhC;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,cAAc,EACvB,eAAe,EAAE,GAAG,GAAG,SAAS,eASjC"}
1
+ {"version":3,"file":"router-store.d.ts","sourceRoot":"","sources":["../../src/global-state/router-store.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,iCAAiC,EAGlC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAiC,aAAa,EAAY,MAAM,OAAO,CAAC;AAE/E,OAAO,EAAE,SAAS,EAAyB,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAoB,MAAM,qBAAqB,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAM1C;;;;GAIG;AACH,qBAAa,WAAW;IACtB,SAAS,EAAG,SAAS,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAG,aAAa,CAAC;IAC9B,OAAO,EAAE,kBAAkB,GAAG,SAAS,CAAC;IACxC,OAAO,EAAE,OAAO,CAAS;IACzB,OAAO,CAAC,wBAAwB,CAAkB;IAElD,YAAY,EAAE,WAAW,GAAG,SAAS,CAAC;IACtC,SAAS,EAAE,WAAW,GAAG,SAAS,CAAC;IACnC,SAAS,EAAE,WAAW,GAAG,SAAS,CAAC;IACnC,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAElC,aAAa,EAAG,iCAAiC,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;IACjF,yBAAyB,EAAG,MAAM,IAAI,CAAC;IAEvC,oBAAoB,YAAiB,IAAI,EAAI;IAC7C,gBAAgB,YAAiB,IAAI,EAAI;IAEzC,MAAM,qDAAqB;IAC3B,eAAe,oBAA8B;IAC7C,MAAM,aAAqB;IAC3B,SAAS,gBAAwB;IACjC,IAAI,6CAAmB;IACvB,OAAO,6CAAsB;IAC7B,SAAS,gEAAwB;IAEjC,UAAU,CACR,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,iCAAiC,CAAC,eAAe,CAAC,aAAa,CAAC,EAC/E,eAAe,CAAC,EAAE,GAAG;IAwGvB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,cAAQ;IAWjD,YAAY,CAAC,KAAK,EAAE,WAAW;IAgB/B,kBAAkB;IAIlB,uEAAuE;IACvE,OAAO,aAEL;IACF,oBAAoB,eAAgB,MAAM,IAAI,mBAG5C;IACF,gBAAgB,eAAgB,MAAM,IAAI,mBAGxC;IACF,QAAQ,aAEN;IACF,iBAAiB,oBAEf;IACF,iBAAiB,kBAEf;CACH;AAED,eAAO,MAAM,KAAK,aAAoB,CAAC;AAEvC,wBAAgB,aAAa,gBAM5B;AAaD,wBAAgB,iBAAiB,gBAOhC;AAED,wBAAgB,iBAAiB,cAOhC;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,cAAc,EACvB,eAAe,EAAE,GAAG,GAAG,SAAS,eASjC"}
@@ -5,6 +5,7 @@ import type { RouterStore } from "./router-store";
5
5
  export declare function push(this: RouterStore, url: Href): void;
6
6
  export declare function replace(this: RouterStore, url: Href): void;
7
7
  export declare function goBack(this: RouterStore): void;
8
+ export declare function canGoBack(this: RouterStore): boolean;
8
9
  export declare function setParams(this: RouterStore, params?: Record<string, string | number>): any;
9
10
  export declare function linkTo(this: RouterStore, href: string, event?: string): void;
10
11
  /** @returns `true` if the action is moving to the first screen of all the navigators in the action. */
@@ -1 +1 @@
1
- {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/global-state/routing.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,kBAAkB,EAEnB,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAE,IAAI,EAAe,MAAM,cAAc,CAAC;AAEjD,OAAO,EACL,cAAc,EAKf,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAQlD,wBAAgB,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,IAAI,QAEhD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,IAAI,QAEnD;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,WAAW,QAGvC;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,WAAW,EACjB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAM,OAI7C;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,QAwHrE;AAED,uGAAuG;AACvG,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,GAC5C,MAAM,IAAI,cAAc,CAqB1B"}
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/global-state/routing.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,kBAAkB,EAEnB,MAAM,wBAAwB,CAAC;AAIhC,OAAO,EAAE,IAAI,EAAe,MAAM,cAAc,CAAC;AAEjD,OAAO,EACL,cAAc,EAKf,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAUlD,wBAAgB,IAAI,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,IAAI,QAEhD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,IAAI,QAEnD;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,WAAW,QAGvC;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAGpD;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,WAAW,EACjB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAM,OAI7C;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,QAwHrE;AAED,uGAAuG;AACvG,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,GAC5C,MAAM,IAAI,cAAc,CAqB1B"}
@@ -1,4 +1,3 @@
1
- import { RouteNode } from "../Route";
2
1
  import type { RouterStore } from "./router-store";
3
- export declare function getSortedRoutes(this: RouterStore): RouteNode[];
2
+ export declare function getSortedRoutes(this: RouterStore): import("../Route").RouteNode[];
4
3
  //# sourceMappingURL=sort-routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sort-routes.d.ts","sourceRoot":"","sources":["../../src/global-state/sort-routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,eA2ChD"}
1
+ {"version":3,"file":"sort-routes.d.ts","sourceRoot":"","sources":["../../src/global-state/sort-routes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,kCAQhD"}
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;AAEtD,wBAAgB,sBAAsB,kDAErC;AAED,wBAAgB,YAAY,2CAE3B;AAED,wBAAgB,iBAAiB,oGAEhC;AAGD,wBAAgB,OAAO,WAGtB;AAED,wBAAgB,SAAS,IAAI,MAAM,CASlC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CACzB,SAAS,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,KAClC,SAAS,CAEb;AAED,kEAAkE;AAClE,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,SAAS,YAAY,GAAG,YAAY,KACxC,OAAO,CAAC,OAAO,CAAC,CAEpB;AAED,qDAAqD;AACrD,wBAAgB,eAAe,CAC7B,OAAO,SAAS,YAAY,GAAG,YAAY,KACxC,OAAO,CAAC,OAAO,CAAC,CAEpB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,SAAS,YAAY,GAAG,YAAY,KACxC,OAAO,CAAC,OAAO,CAAC,CAEpB"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,KAAK,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;AAEtD,wBAAgB,sBAAsB,kDAErC;AAED,wBAAgB,YAAY,2CAE3B;AAED,wBAAgB,iBAAiB,oGAEhC;AAGD,wBAAgB,OAAO,WAGtB;AAED,wBAAgB,SAAS,IAAI,MAAM,CAYlC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW,CACzB,SAAS,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,KAClC,SAAS,CAEb;AAED,kEAAkE;AAClE,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,SAAS,YAAY,GAAG,YAAY,KACxC,OAAO,CAAC,OAAO,CAAC,CAEpB;AAED,qDAAqD;AACrD,wBAAgB,eAAe,CAC7B,OAAO,SAAS,YAAY,GAAG,YAAY,KACxC,OAAO,CAAC,OAAO,CAAC,CAEpB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,SAAS,YAAY,GAAG,YAAY,KACxC,OAAO,CAAC,OAAO,CAAC,CAEpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"imperative-api.d.ts","sourceRoot":"","sources":["../src/imperative-api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,eAAO,MAAM,MAAM,EAAE,MAKpB,CAAC"}
1
+ {"version":3,"file":"imperative-api.d.ts","sourceRoot":"","sources":["../src/imperative-api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,eAAO,MAAM,MAAM,EAAE,MAMpB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"href.d.ts","sourceRoot":"","sources":["../../src/link/href.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,UAAU,CAAC;AAEvC,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,oEAAoE;AACpE,eAAO,MAAM,WAAW,SAAU,IAAI,KAAG,MAexC,CAAC"}
1
+ {"version":3,"file":"href.d.ts","sourceRoot":"","sources":["../../src/link/href.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,UAAU,CAAC;AAEvC,MAAM,WAAW,UAAU;IACzB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,oEAAoE;AACpE,eAAO,MAAM,WAAW,SAAU,IAAI,KAAG,MAaxC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Tutorial.d.ts","sourceRoot":"","sources":["../../src/onboard/Tutorial.tsx"],"names":[],"mappings":";AAqCA,wBAAgB,QAAQ,gBAyCvB"}
1
+ {"version":3,"file":"Tutorial.d.ts","sourceRoot":"","sources":["../../src/onboard/Tutorial.tsx"],"names":[],"mappings":";AAqCA,wBAAgB,QAAQ,gBAqCvB"}
package/build/types.d.ts CHANGED
@@ -18,6 +18,8 @@ export type Router = {
18
18
  replace: (href: Href) => void;
19
19
  /** Go back in the history. */
20
20
  back: () => void;
21
+ /** If there's history that supports invoking the `back` function. */
22
+ canGoBack: () => boolean;
21
23
  /** Update the current route query params. */
22
24
  setParams: (params?: Record<string, string>) => void;
23
25
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGnC,MAAM,WAAW,cAAc;IAC7B,4CAA4C;IAC5C,IAAI,IAAI,MAAM,EAAE,CAAC;IACjB,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,CAAC;IAClB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;IACnB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,yFAAyF;AACzF,MAAM,MAAM,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,MAAM,MAAM,MAAM,GAAG;IACnB,qCAAqC;IACrC,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAC3B,0DAA0D;IAC1D,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAC9B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,6CAA6C;IAC7C,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;CACtD,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAGnC,MAAM,WAAW,cAAc;IAC7B,4CAA4C;IAC5C,IAAI,IAAI,MAAM,EAAE,CAAC;IACjB,CAAC,EAAE,EAAE,MAAM,GAAG,GAAG,CAAC;IAClB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;IACnB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,yFAAyF;AACzF,MAAM,MAAM,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,MAAM,MAAM,MAAM,GAAG;IACnB,qCAAqC;IACrC,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAC3B,0DAA0D;IAC1D,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAC9B,8BAA8B;IAC9B,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,qEAAqE;IACrE,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,6CAA6C;IAC7C,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;CACtD,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function useWarnOnce(message: string, guard?: unknown, key?: string): void;
2
+ export declare function useDeprecated(message: string, guard?: unknown, key?: string): void;
3
+ //# sourceMappingURL=useDeprecated.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDeprecated.d.ts","sourceRoot":"","sources":["../src/useDeprecated.ts"],"names":[],"mappings":"AAaA,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,OAAc,EACrB,GAAG,SAAU,QAUd;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,OAAc,EACrB,GAAG,SAAU,QAGd"}
@@ -13,6 +13,9 @@ export type ScreenProps<TOptions extends Record<string, any> = Record<string, an
13
13
  };
14
14
  options?: TOptions;
15
15
  listeners?: any;
16
+ getId?: ({ params, }: {
17
+ params?: Record<string, any> | undefined;
18
+ }) => string | undefined;
16
19
  };
17
20
  /**
18
21
  * @returns React Navigation screens sorted by the `route` property.
@@ -1 +1 @@
1
- {"version":3,"file":"useScreens.d.ts","sourceRoot":"","sources":["../src/useScreens.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAIL,SAAS,EAGV,MAAM,SAAS,CAAC;AAOjB,MAAM,MAAM,WAAW,CACrB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IACxD;IACF,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;IACvC,OAAO,CAAC,EAAE,QAAQ,CAAC;IAGnB,SAAS,CAAC,EAAE,GAAG,CAAC;CACjB,CAAC;AA8DF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAUxE;AAsCD,mFAAmF;AACnF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,SAAS,kIAuE1D;AAED,oGAAoG;AACpG,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC;;sCA0B5C"}
1
+ {"version":3,"file":"useScreens.d.ts","sourceRoot":"","sources":["../src/useScreens.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAIL,SAAS,EAGV,MAAM,SAAS,CAAC;AAOjB,MAAM,MAAM,WAAW,CACrB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IACxD;IACF,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;IACvC,OAAO,CAAC,EAAE,QAAQ,CAAC;IAGnB,SAAS,CAAC,EAAE,GAAG,CAAC;IAEhB,KAAK,CAAC,EAAE,CAAC,EACP,MAAM,GACP,EAAE;QACD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;KAC1C,KAAK,MAAM,GAAG,SAAS,CAAC;CAC1B,CAAC;AAiEF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAUxE;AAsCD,mFAAmF;AACnF,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,SAAS,kIAuE1D;AAED,oGAAoG;AACpG,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC;;sCA0B5C"}
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../src/views/ErrorBoundary.tsx"],"names":[],"mappings":";AAUA,OAAO,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAmC3C,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,kBAAkB,eA2DjE"}
1
+ {"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../src/views/ErrorBoundary.tsx"],"names":[],"mappings":";AAUA,OAAO,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAmC3C,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,kBAAkB,eAuDjE"}
@@ -1 +1 @@
1
- {"version":3,"file":"Screen.d.ts","sourceRoot":"","sources":["../../src/views/Screen.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,WAAW,CACrB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IACxD;IACF;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,aAAa,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;IACvC,OAAO,CAAC,EAAE,QAAQ,CAAC;CACpB,CAAC;AAKF,sEAAsE;AACtE,wBAAgB,MAAM,CAAC,QAAQ,SAAS,MAAM,GAAG,MAAM,EAAE,EACvD,IAAI,EACJ,QAAQ,EACR,OAAO,GACR,EAAE,WAAW,CAAC,QAAQ,CAAC,QAmBvB"}
1
+ {"version":3,"file":"Screen.d.ts","sourceRoot":"","sources":["../../src/views/Screen.tsx"],"names":[],"mappings":"AAKA,MAAM,MAAM,WAAW,CACrB,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,IACxD;IACF;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB,aAAa,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAC;IACvC,OAAO,CAAC,EAAE,QAAQ,CAAC;CACpB,CAAC;AAKF,sEAAsE;AACtE,wBAAgB,MAAM,CAAC,QAAQ,SAAS,MAAM,GAAG,MAAM,EAAE,EACvD,IAAI,EACJ,QAAQ,EACR,OAAO,GACR,EAAE,WAAW,CAAC,QAAQ,CAAC,QAkCvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"Sitemap.d.ts","sourceRoot":"","sources":["../../src/views/Sitemap.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAiB9E,wBAAgB,aAAa,IAAI,4BAA4B,CAqB5D;AAED,wBAAgB,OAAO,gBA6BtB"}
1
+ {"version":3,"file":"Sitemap.d.ts","sourceRoot":"","sources":["../../src/views/Sitemap.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAkB9E,wBAAgB,aAAa,IAAI,4BAA4B,CAoB5D;AAED,wBAAgB,OAAO,gBA6BtB"}
@@ -1 +1 @@
1
- {"version":3,"file":"Splash.d.ts","sourceRoot":"","sources":["../../src/views/Splash.tsx"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,SAQ3B;yBARe,YAAY;;;;;;AAqC5B,eAAO,MAAM,8BAA8B,YAe1C,CAAC;AAEF,eAAO,MAAM,wBAAwB,YAOpC,CAAC"}
1
+ {"version":3,"file":"Splash.d.ts","sourceRoot":"","sources":["../../src/views/Splash.tsx"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,SAM3B;yBANe,YAAY;;;;;;AAmC5B,eAAO,MAAM,8BAA8B,YAe1C,CAAC;AAEF,eAAO,MAAM,wBAAwB,YAOpC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Unmatched.d.ts","sourceRoot":"","sources":["../../src/views/Unmatched.tsx"],"names":[],"mappings":";AAwBA,2CAA2C;AAC3C,wBAAgB,SAAS,gBAoDxB"}
1
+ {"version":3,"file":"Unmatched.d.ts","sourceRoot":"","sources":["../../src/views/Unmatched.tsx"],"names":[],"mappings":";AAwBA,2CAA2C;AAC3C,wBAAgB,SAAS,gBA4CxB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-router",
3
- "version": "2.0.0-rc.9",
3
+ "version": "2.0.1",
4
4
  "main": "src/index.tsx",
5
5
  "types": "build/index.d.ts",
6
6
  "files": [
@@ -61,7 +61,7 @@
61
61
  },
62
62
  "peerDependencies": {
63
63
  "@react-navigation/drawer": "^6.5.8",
64
- "expo": "^49.0.0-alpha.6",
64
+ "expo": "^49.0.0",
65
65
  "expo-constants": "*",
66
66
  "expo-linking": "*",
67
67
  "expo-status-bar": "*",
@@ -104,13 +104,13 @@
104
104
  },
105
105
  "dependencies": {
106
106
  "@bacons/react-views": "^1.1.3",
107
- "@expo/metro-runtime": "2.2.0",
107
+ "@expo/metro-runtime": "2.2.4",
108
108
  "@radix-ui/react-slot": "1.0.1",
109
109
  "@react-navigation/bottom-tabs": "~6.5.7",
110
110
  "@react-navigation/native": "~6.1.6",
111
111
  "@react-navigation/native-stack": "~6.9.12",
112
112
  "expo-head": "0.0.11",
113
- "expo-splash-screen": "*",
113
+ "expo-splash-screen": "~0.20.2",
114
114
  "query-string": "7.1.3",
115
115
  "react-helmet-async": "^1.3.0",
116
116
  "schema-utils": "^4.0.1",
package/src/ExpoRoot.tsx CHANGED
@@ -1,3 +1,4 @@
1
+ import Constants from "expo-constants";
1
2
  import { StatusBar } from "expo-status-bar";
2
3
  import React, { FunctionComponent, ReactNode, Fragment } from "react";
3
4
  import { Platform } from "react-native";
@@ -44,6 +45,11 @@ const INITIAL_METRICS = {
44
45
  insets: { top: 0, left: 0, right: 0, bottom: 0 },
45
46
  };
46
47
 
48
+ const hasViewControllerBasedStatusBarAppearance =
49
+ Platform.OS === "ios" &&
50
+ !!Constants.expoConfig?.ios?.infoPlist
51
+ ?.UIViewControllerBasedStatusBarAppearance;
52
+
47
53
  export function ExpoRoot({
48
54
  wrapper: ParentWrapper = Fragment,
49
55
  ...props
@@ -64,7 +70,9 @@ export function ExpoRoot({
64
70
  {children}
65
71
 
66
72
  {/* Users can override this by adding another StatusBar element anywhere higher in the component tree. */}
67
- <StatusBar style="auto" />
73
+ {!hasViewControllerBasedStatusBarAppearance && (
74
+ <StatusBar style="auto" />
75
+ )}
68
76
  </SafeAreaProvider>
69
77
  </GestureHandlerRootView>
70
78
  </ParentWrapper>
@@ -90,7 +98,11 @@ function ContextNavigator({
90
98
  SplashScreen.hideAsync();
91
99
  if (process.env.NODE_ENV === "development") {
92
100
  const Tutorial = require("./onboard/Tutorial").Tutorial;
93
- return <Tutorial />;
101
+ return (
102
+ <WrapperComponent>
103
+ <Tutorial />
104
+ </WrapperComponent>
105
+ );
94
106
  } else {
95
107
  // Ensure tutorial styles are stripped in production.
96
108
  return null;
@@ -6,6 +6,7 @@ import type {
6
6
  } from "@react-navigation/routers";
7
7
  import escape from "escape-string-regexp";
8
8
  import * as queryString from "query-string";
9
+ import URL from "url-parse";
9
10
 
10
11
  import { RouteNode } from "../Route";
11
12
  import { matchGroupName, stripGroupSegmentsFromPath } from "../matchers";
@@ -47,14 +48,18 @@ type ParsedRoute = {
47
48
  params?: Record<string, any> | undefined;
48
49
  };
49
50
 
50
- function getPathname(path: string) {
51
- const remaining = path
52
- .replace(/\/+/g, "/") // Replace multiple slash (//) with single ones
53
- .replace(/^\//, "") // Remove extra leading slash
54
- .replace(/\?.*$/, ""); // Remove query params which we will handle later
51
+ export function getUrlWithReactNavigationConcessions(path: string) {
52
+ const parsed = new URL(path, "https://acme.com");
53
+ const pathname = parsed.pathname;
55
54
 
56
55
  // Make sure there is a trailing slash
57
- return remaining.endsWith("/") ? remaining : `${remaining}/`;
56
+ return {
57
+ // The slashes are at the end, not the beginning
58
+ nonstandardPathname:
59
+ pathname.replace(/^\/+/g, "").replace(/\/+$/g, "") + "/",
60
+ // React Navigation doesn't support hashes, so here
61
+ inputPathnameWithoutHash: path.replace(/#.*$/, ""),
62
+ };
58
63
  }
59
64
 
60
65
  /**
@@ -316,10 +321,15 @@ function getStateFromEmptyPathWithConfigs(
316
321
  return undefined;
317
322
  }
318
323
 
319
- const routes = match.routeNames.map((name) => ({
320
- name,
321
- _route: match._route,
322
- }));
324
+ const routes = match.routeNames.map((name) => {
325
+ if (!match._route) {
326
+ return { name };
327
+ }
328
+ return {
329
+ name,
330
+ _route: match._route,
331
+ };
332
+ });
323
333
 
324
334
  return createNestedStateObject(path, routes, configs, initialRoutes);
325
335
  }
@@ -329,21 +339,33 @@ function getStateFromPathWithConfigs(
329
339
  configs: RouteConfig[],
330
340
  initialRoutes: InitialRouteConfig[]
331
341
  ): ResultState | undefined {
332
- const pathname = getPathname(path);
342
+ const formattedPaths = getUrlWithReactNavigationConcessions(path);
333
343
 
334
- if (pathname === "/") {
335
- return getStateFromEmptyPathWithConfigs(path, configs, initialRoutes);
344
+ if (formattedPaths.nonstandardPathname === "/") {
345
+ return getStateFromEmptyPathWithConfigs(
346
+ formattedPaths.inputPathnameWithoutHash,
347
+ configs,
348
+ initialRoutes
349
+ );
336
350
  }
337
351
 
338
352
  // We match the whole path against the regex instead of segments
339
353
  // This makes sure matches such as wildcard will catch any unmatched routes, even if nested
340
- const routes = matchAgainstConfigs(pathname, configs);
354
+ const routes = matchAgainstConfigs(
355
+ formattedPaths.nonstandardPathname,
356
+ configs
357
+ );
341
358
 
342
359
  if (routes == null) {
343
360
  return undefined;
344
361
  }
345
362
  // This will always be empty if full path matched
346
- return createNestedStateObject(path, routes, configs, initialRoutes);
363
+ return createNestedStateObject(
364
+ formattedPaths.inputPathnameWithoutHash,
365
+ routes,
366
+ configs,
367
+ initialRoutes
368
+ );
347
369
  }
348
370
 
349
371
  const joinPaths = (...paths: string[]): string =>
@@ -424,10 +446,15 @@ function matchAgainstConfigs(
424
446
  return { name };
425
447
  };
426
448
 
427
- routes = config.routeNames.map((name) => ({
428
- ...routeFromName(name),
429
- _route: config._route,
430
- }));
449
+ routes = config.routeNames.map((name) => {
450
+ if (!config._route) {
451
+ return { ...routeFromName(name) };
452
+ }
453
+ return {
454
+ ...routeFromName(name),
455
+ _route: config._route,
456
+ };
457
+ });
431
458
 
432
459
  // TODO(EvanBacon): Maybe we should warn / assert if multiple slugs use the same param name.
433
460
  const combinedParams = routes.reduce<Record<string, any>>(
@@ -14,7 +14,7 @@ import { getRoutes } from "../getRoutes";
14
14
  import { RequireContext } from "../types";
15
15
  import { getQualifiedRouteComponent } from "../useScreens";
16
16
  import { _internal_maybeHideAsync } from "../views/Splash";
17
- import { goBack, linkTo, push, replace, setParams } from "./routing";
17
+ import { canGoBack, goBack, linkTo, push, replace, setParams } from "./routing";
18
18
  import { getSortedRoutes } from "./sort-routes";
19
19
 
20
20
  /**
@@ -27,6 +27,7 @@ export class RouterStore {
27
27
  rootComponent!: ComponentType;
28
28
  linking: ExpoLinkingOptions | undefined;
29
29
  isReady: boolean = false;
30
+ private hasAttemptedToHideSplash: boolean = false;
30
31
 
31
32
  initialState: ResultState | undefined;
32
33
  rootState: ResultState | undefined;
@@ -42,6 +43,7 @@ export class RouterStore {
42
43
  linkTo = linkTo.bind(this);
43
44
  getSortedRoutes = getSortedRoutes.bind(this);
44
45
  goBack = goBack.bind(this);
46
+ canGoBack = canGoBack.bind(this);
45
47
  push = push.bind(this);
46
48
  replace = replace.bind(this);
47
49
  setParams = setParams.bind(this);
@@ -117,8 +119,16 @@ export class RouterStore {
117
119
  (data) => {
118
120
  const state = data.data.state as ResultState;
119
121
 
120
- if (navigationRef.isReady()) {
121
- this.onReady();
122
+ if (!this.isReady) {
123
+ if (!this.hasAttemptedToHideSplash) {
124
+ this.hasAttemptedToHideSplash = true;
125
+ // NOTE(EvanBacon): `navigationRef.isReady` is sometimes not true when state is called initially.
126
+ requestAnimationFrame(() => _internal_maybeHideAsync());
127
+ }
128
+
129
+ if (navigationRef.isReady()) {
130
+ this.onReady();
131
+ }
122
132
  }
123
133
 
124
134
  let shouldUpdateSubscribers = this.nextState === state;
@@ -178,9 +188,6 @@ export class RouterStore {
178
188
 
179
189
  /** Make sure these are arrow functions so `this` is correctly bound */
180
190
  onReady = () => {
181
- if (!this.isReady) {
182
- requestAnimationFrame(() => _internal_maybeHideAsync());
183
- }
184
191
  this.isReady = true;
185
192
  };
186
193
  subscribeToRootState = (subscriber: () => void) => {
@@ -20,7 +20,9 @@ import type { RouterStore } from "./router-store";
20
20
 
21
21
  function assertIsReady(store: RouterStore) {
22
22
  if (!store.isReady || !store.navigationRef.current) {
23
- throw new Error("Attempted to use navigation outside of Expo Router");
23
+ throw new Error(
24
+ "Attempted to navigate before mounting the Root Layout component. Ensure the Root Layout component is rendering a Slot, or other navigator on the first render."
25
+ );
24
26
  }
25
27
  }
26
28
 
@@ -37,6 +39,11 @@ export function goBack(this: RouterStore) {
37
39
  this.navigationRef?.current?.goBack();
38
40
  }
39
41
 
42
+ export function canGoBack(this: RouterStore): boolean {
43
+ assertIsReady(this);
44
+ return this.navigationRef?.current?.canGoBack() ?? false;
45
+ }
46
+
40
47
  export function setParams(
41
48
  this: RouterStore,
42
49
  params: Record<string, string | number> = {}
@@ -1,5 +1,4 @@
1
- import { RouteNode } from "../Route";
2
- import { matchGroupName } from "../matchers";
1
+ import { sortRoutes } from "../Route";
3
2
  import type { RouterStore } from "./router-store";
4
3
 
5
4
  export function getSortedRoutes(this: RouterStore) {
@@ -9,40 +8,5 @@ export function getSortedRoutes(this: RouterStore) {
9
8
 
10
9
  return this.routeNode.children
11
10
  .filter((route) => !route.internal)
12
- .sort((a: RouteNode, b: RouteNode): number => {
13
- if (a.dynamic && !b.dynamic) {
14
- return 1;
15
- }
16
- if (!a.dynamic && b.dynamic) {
17
- return -1;
18
- }
19
- if (a.dynamic && b.dynamic) {
20
- if (a.dynamic.length !== b.dynamic.length) {
21
- return b.dynamic.length - a.dynamic.length;
22
- }
23
- for (let i = 0; i < a.dynamic.length; i++) {
24
- const aDynamic = a.dynamic[i];
25
- const bDynamic = b.dynamic[i];
26
- if (aDynamic.deep && !bDynamic.deep) {
27
- return 1;
28
- }
29
- if (!aDynamic.deep && bDynamic.deep) {
30
- return -1;
31
- }
32
- }
33
- return 0;
34
- }
35
-
36
- const aIndex = a.route === "index" || matchGroupName(a.route) != null;
37
- const bIndex = b.route === "index" || matchGroupName(b.route) != null;
38
-
39
- if (aIndex && !bIndex) {
40
- return -1;
41
- }
42
- if (!aIndex && bIndex) {
43
- return 1;
44
- }
45
-
46
- return a.route.length - b.route.length;
47
- });
11
+ .sort(sortRoutes);
48
12
  }
package/src/hooks.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  useStoreRouteInfo,
12
12
  } from "./global-state/router-store";
13
13
  import { Router } from "./types";
14
+ import { useDeprecated } from "./useDeprecated";
14
15
 
15
16
  type SearchParams = Record<string, string | string[]>;
16
17
 
@@ -28,19 +29,22 @@ export function useRootNavigation() {
28
29
 
29
30
  // Wraps useLinkTo to provide an API which is similar to the Link component.
30
31
  export function useLink() {
31
- console.warn("`useLink()` is deprecated in favor of `useRouter()`");
32
+ useDeprecated("`useLink()` is deprecated in favor of `useRouter()`");
32
33
  return useRouter();
33
34
  }
34
35
 
35
36
  export function useRouter(): Router {
36
- return {
37
- push: store.push,
38
- back: store.goBack,
39
- replace: store.replace,
40
- setParams: store.setParams,
41
- // TODO(EvanBacon): add `reload`
42
- // TODO(EvanBacon): add `canGoBack` but maybe more like a `hasContext`
43
- };
37
+ return React.useMemo(
38
+ () => ({
39
+ push: store.push,
40
+ back: store.goBack,
41
+ replace: store.replace,
42
+ setParams: store.setParams,
43
+ canGoBack: store.canGoBack,
44
+ // TODO(EvanBacon): add `reload`
45
+ }),
46
+ []
47
+ );
44
48
  }
45
49
 
46
50
  /**
@@ -5,5 +5,6 @@ export const router: Router = {
5
5
  push: (href) => store.push(href),
6
6
  replace: (href) => store.replace(href),
7
7
  back: () => store.goBack(),
8
+ canGoBack: () => store.canGoBack(),
8
9
  setParams: (params) => store.setParams(params),
9
10
  };
package/src/link/href.ts CHANGED
@@ -19,10 +19,8 @@ export const resolveHref = (href: Href): string => {
19
19
  const { pathname, params } = createQualifiedPathname(path, {
20
20
  ...href.params,
21
21
  });
22
- return (
23
- pathname +
24
- (Object.keys(params).length ? `?${createQueryParams(params)}` : "")
25
- );
22
+ const paramsString = createQueryParams(params);
23
+ return pathname + (paramsString ? `?${paramsString}` : "");
26
24
  };
27
25
 
28
26
  function createQualifiedPathname(
@@ -54,7 +52,11 @@ function encodeParam(param: any): string {
54
52
  }
55
53
 
56
54
  function createQueryParams(params: Record<string, any>): string {
57
- return Object.entries(params)
58
- .map(([key, value]) => `${key}=${encodeURIComponent(value.toString())}`)
59
- .join("&");
55
+ return (
56
+ Object.entries(params)
57
+ // Allow nullish params
58
+ .filter(([, value]) => value != null)
59
+ .map(([key, value]) => `${key}=${encodeURIComponent(value.toString())}`)
60
+ .join("&")
61
+ );
60
62
  }
@@ -11,8 +11,8 @@ function Header() {
11
11
  <Pressable>
12
12
  {({ hovered }) => (
13
13
  <Text
14
- accessibilityRole="header"
15
- accessibilityLevel={1}
14
+ role="heading"
15
+ aria-level={1}
16
16
  style={[styles.title, Platform.OS !== "web" && { textAlign: "left" }]}
17
17
  >
18
18
  Welcome to{" "}
@@ -62,11 +62,7 @@ export function Tutorial() {
62
62
  <SafeAreaView style={styles.safeArea}>
63
63
  <View style={styles.container}>
64
64
  <Header />
65
- <Text
66
- accessibilityRole="header"
67
- accessibilityLevel={2}
68
- style={styles.subtitle}
69
- >
65
+ <Text role="heading" aria-level={2} style={styles.subtitle}>
70
66
  Start by creating a file{"\n"}in the{" "}
71
67
  <Text style={{ fontWeight: "bold" }}>{getRootDir()}</Text>{" "}
72
68
  directory.
@@ -122,10 +118,7 @@ function Button() {
122
118
  },
123
119
  ]}
124
120
  >
125
- <Text
126
- selectable={false}
127
- style={[styles.code, hovered && { color: "black" }]}
128
- >
121
+ <Text style={[styles.code, hovered && { color: "black" }]}>
129
122
  <Text style={{ color: "#BCC3CD" }}>$</Text> touch {getRootDir()}
130
123
  /index.js
131
124
  </Text>
@@ -179,6 +172,7 @@ const styles = StyleSheet.create({
179
172
  color: "black",
180
173
  },
181
174
  code: {
175
+ userSelect: "none",
182
176
  fontSize: 18,
183
177
  transitionDuration: "200ms",
184
178
  fontWeight: "bold",
@@ -14,7 +14,7 @@ export function ScrollViewStyleReset() {
14
14
  <style
15
15
  id="expo-reset"
16
16
  dangerouslySetInnerHTML={{
17
- __html: `#root,body,html{height:100%}body{overflow:hidden}#root{display:flex}`,
17
+ __html: `#root,body{display:flex}#root,body,html{width:100%;-webkit-overflow-scrolling:touch;margin:0;padding:0;min-height:100%}#root{flex-shrink:0;flex-basis:auto;flex-grow:1;flex:1}html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;height:calc(100% + env(safe-area-inset-top))}body{overflow-y:auto;overscroll-behavior-y:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;-ms-overflow-style:scrollbar}`,
18
18
  }}
19
19
  />
20
20
  );
package/src/types.ts CHANGED
@@ -23,6 +23,8 @@ export type Router = {
23
23
  replace: (href: Href) => void;
24
24
  /** Go back in the history. */
25
25
  back: () => void;
26
+ /** If there's history that supports invoking the `back` function. */
27
+ canGoBack: () => boolean;
26
28
  /** Update the current route query params. */
27
29
  setParams: (params?: Record<string, string>) => void;
28
30
  };
@@ -0,0 +1,35 @@
1
+ import { useLayoutEffect } from "react";
2
+ import { Platform } from "react-native";
3
+
4
+ // Node environment may render in multiple processes causing the warning to log mutiple times
5
+ // Hence we skip the warning in these environments.
6
+ const canWarn = Platform.select({
7
+ native: process.env.NODE_ENV !== "production",
8
+ default:
9
+ process.env.NODE_ENV !== "production" && typeof window !== "undefined",
10
+ });
11
+
12
+ const warned = new Set<string>();
13
+
14
+ export function useWarnOnce(
15
+ message: string,
16
+ guard: unknown = true,
17
+ key = message
18
+ ) {
19
+ // useLayoutEffect typically doesn't run in node environments.
20
+ // Combined with skipWarn, this should prevent unwanted warnings
21
+ useLayoutEffect(() => {
22
+ if (guard && canWarn && !warned.has(key)) {
23
+ warned.add(key);
24
+ console.warn(message);
25
+ }
26
+ }, [guard]);
27
+ }
28
+
29
+ export function useDeprecated(
30
+ message: string,
31
+ guard: unknown = true,
32
+ key = message
33
+ ) {
34
+ return useWarnOnce(key, guard, `Expo Router: ${message}`);
35
+ }
@@ -29,6 +29,12 @@ export type ScreenProps<
29
29
 
30
30
  // TODO: types
31
31
  listeners?: any;
32
+
33
+ getId?: ({
34
+ params,
35
+ }: {
36
+ params?: Record<string, any> | undefined;
37
+ }) => string | undefined;
32
38
  };
33
39
 
34
40
  function getSortedChildren(
@@ -44,7 +50,7 @@ function getSortedChildren(
44
50
  const entries = [...children];
45
51
 
46
52
  const ordered = order
47
- .map(({ name, redirect, initialParams, listeners, options }) => {
53
+ .map(({ name, redirect, initialParams, listeners, options, getId }) => {
48
54
  if (!entries.length) {
49
55
  console.warn(
50
56
  `[Layout children]: Too many screens defined. Route "${name}" is extraneous.`
@@ -73,7 +79,10 @@ function getSortedChildren(
73
79
  return null;
74
80
  }
75
81
 
76
- return { route: match, props: { initialParams, listeners, options } };
82
+ return {
83
+ route: match,
84
+ props: { initialParams, listeners, options, getId },
85
+ };
77
86
  }
78
87
  })
79
88
  .filter(Boolean) as {
@@ -62,11 +62,7 @@ export function ErrorBoundary({ error, retry }: ErrorBoundaryProps) {
62
62
  alignItems: "center",
63
63
  }}
64
64
  >
65
- <Text
66
- accessibilityRole="header"
67
- accessibilityLevel={1}
68
- style={styles.title}
69
- >
65
+ <Text role="heading" aria-level={1} style={styles.title}>
70
66
  Something went wrong
71
67
  </Text>
72
68
  </View>
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
2
 
3
+ import { useDeprecated } from "../useDeprecated";
3
4
  import { useNavigation } from "../useNavigation";
4
5
 
5
6
  export type ScreenProps<
@@ -33,9 +34,24 @@ export function Screen<TOptions extends object = object>({
33
34
  const navigation = useNavigation(name);
34
35
 
35
36
  useLayoutEffect(() => {
36
- navigation.setOptions(options ?? {});
37
+ if (
38
+ options &&
39
+ // React Navigation will infinitely loop in some cases if an empty object is passed to setOptions.
40
+ // https://github.com/expo/router/issues/452
41
+ Object.keys(options).length
42
+ ) {
43
+ navigation.setOptions(options);
44
+ }
37
45
  }, [navigation, options]);
38
46
 
47
+ if (process.env.NODE_ENV === "development") {
48
+ // eslint-disable-next-line react-hooks/rules-of-hooks
49
+ useDeprecated(
50
+ "The `redirect` prop on <Screen /> is deprecated and will be removed. Please use `router.redirect` instead",
51
+ redirect
52
+ );
53
+ }
54
+
39
55
  if (process.env.NODE_ENV !== "production") {
40
56
  // eslint-disable-next-line react-hooks/rules-of-hooks
41
57
  React.useEffect(() => {
@@ -1,5 +1,4 @@
1
1
  import { Image, Pressable, StyleSheet, Text, View } from "@bacons/react-views";
2
- import { useNavigation } from "@react-navigation/native";
3
2
  import { NativeStackNavigationOptions } from "@react-navigation/native-stack";
4
3
  import React from "react";
5
4
  import {
@@ -12,6 +11,7 @@ import { useSafeAreaInsets } from "react-native-safe-area-context";
12
11
 
13
12
  import { RouteNode } from "../Route";
14
13
  import { useExpoRouter } from "../global-state/router-store";
14
+ import { router } from "../imperative-api";
15
15
  import { Link } from "../link/Link";
16
16
  import { matchDeepDynamicRouteName } from "../matchers";
17
17
 
@@ -27,7 +27,6 @@ export function getNavOptions(): NativeStackNavigationOptions {
27
27
  headerTitleStyle: {
28
28
  color: "white",
29
29
  },
30
-
31
30
  headerTintColor: "white",
32
31
  headerLargeTitleStyle: {
33
32
  color: "white",
@@ -98,8 +97,6 @@ function FileItem({
98
97
  }) {
99
98
  const disabled = route.children.length > 0;
100
99
 
101
- const navigation = useNavigation();
102
-
103
100
  const segments = React.useMemo(
104
101
  () => [...parents, ...route.route.split("/")],
105
102
  [parents, route.route]
@@ -147,9 +144,9 @@ function FileItem({
147
144
  accessibilityLabel={route.contextKey}
148
145
  href={href}
149
146
  onPress={() => {
150
- if (Platform.OS !== "web") {
147
+ if (Platform.OS !== "web" && router.canGoBack()) {
151
148
  // Ensure the modal pops
152
- navigation.goBack();
149
+ router.back();
153
150
  }
154
151
  }}
155
152
  style={{ flex: 1, display: "flex" }}
@@ -3,6 +3,8 @@ import { nanoid } from "nanoid/non-secure";
3
3
  import * as React from "react";
4
4
  import { Platform } from "react-native";
5
5
 
6
+ import { useDeprecated } from "../useDeprecated";
7
+
6
8
  const globalStack: string[] = [];
7
9
 
8
10
  /**
@@ -25,11 +27,9 @@ const globalStack: string[] = [];
25
27
  */
26
28
  export function SplashScreen() {
27
29
  useGlobalSplash();
28
- React.useEffect(() => {
29
- console.warn(
30
- "The <SplashScreen /> component is deprecated. Use `SplashScreen.preventAutoHideAsync()` and `SplashScreen.hideAsync` from `expo-router` instead."
31
- );
32
- }, []);
30
+ useDeprecated(
31
+ "The <SplashScreen /> component is deprecated. Use `SplashScreen.preventAutoHideAsync()` and `SplashScreen.hideAsync` from `expo-router` instead."
32
+ );
33
33
  return null;
34
34
  }
35
35
 
@@ -37,23 +37,15 @@ export function Unmatched() {
37
37
 
38
38
  return (
39
39
  <View style={styles.container}>
40
- <Text
41
- accessibilityRole="header"
42
- accessibilityLevel={1}
43
- style={styles.title}
44
- >
40
+ <Text role="heading" aria-level={1} style={styles.title}>
45
41
  Unmatched Route
46
42
  </Text>
47
- <Text
48
- accessibilityRole="header"
49
- accessibilityLevel={2}
50
- style={styles.subtitle}
51
- >
43
+ <Text role="heading" aria-level={2} style={styles.subtitle}>
52
44
  Page could not be found.{" "}
53
45
  <Text
54
46
  onPress={() => {
55
- if (navigation.canGoBack()) {
56
- navigation.goBack();
47
+ if (router.canGoBack()) {
48
+ router.back();
57
49
  } else {
58
50
  router.replace("/");
59
51
  }
@@ -127,8 +127,6 @@ declare module "react-native" {
127
127
  /** @platform web */
128
128
  tabIndex?: number;
129
129
  /** @platform web */
130
- accessibilityLevel?: number;
131
- /** @platform web */
132
130
  lang?: string;
133
131
  }
134
132