expo-router 1.7.2 → 1.7.3

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.
@@ -1,8 +1,4 @@
1
1
  /// <reference types="react" />
2
- import { RequireContext } from "./types";
3
- export type ExpoRootProps = {
4
- context: RequireContext;
5
- location?: URL;
6
- };
2
+ import { ExpoRootProps } from "./useCreateExpoRouterContext";
7
3
  export declare function ExpoRoot({ context, location }: ExpoRootProps): JSX.Element;
8
4
  //# sourceMappingURL=ExpoRoot.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoRoot.d.ts","sourceRoot":"","sources":["../src/ExpoRoot.tsx"],"names":[],"mappings":";AAqBA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAwBzC,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,CAAC,EAAE,GAAG,CAAC;CAChB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,aAAa,eAa5D"}
1
+ {"version":3,"file":"ExpoRoot.d.ts","sourceRoot":"","sources":["../src/ExpoRoot.tsx"],"names":[],"mappings":";AAaA,OAAO,EACL,aAAa,EAEd,MAAM,8BAA8B,CAAC;AA8BtC,wBAAgB,QAAQ,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,aAAa,eAa5D"}
@@ -16,7 +16,7 @@ export declare function findFocusedRoute(state: InitialState): (Omit<import("@re
16
16
  })[];
17
17
  type: string;
18
18
  stale: false;
19
- }>, "stale" | "routes">> & {
19
+ }>, "routes" | "stale">> & {
20
20
  routes: (Omit<import("@react-navigation/routers").Route<string, object | undefined>, "key"> & any)[];
21
21
  }> | undefined;
22
22
  }) | undefined;
@@ -19,7 +19,7 @@ export default function useLinking(ref: React.RefObject<NavigationContainerRef<P
19
19
  })[];
20
20
  type: string;
21
21
  stale: false;
22
- }>, "stale" | "routes">> & Readonly<{
22
+ }>, "routes" | "stale">> & Readonly<{
23
23
  stale?: true | undefined;
24
24
  routes: import("@react-navigation/core").PartialRoute<import("@react-navigation/core").Route<string, object | undefined>>[];
25
25
  }> & {
@@ -39,7 +39,7 @@ export default function useLinking(ref: React.RefObject<NavigationContainerRef<P
39
39
  })[];
40
40
  type: string;
41
41
  stale: false;
42
- }>, "stale" | "routes">> & Readonly<{
42
+ }>, "routes" | "stale">> & Readonly<{
43
43
  stale?: true | undefined;
44
44
  routes: import("@react-navigation/core").PartialRoute<import("@react-navigation/core").Route<string, object | undefined>>[];
45
45
  }> & any) | undefined;
@@ -27,7 +27,7 @@ export declare const Drawer: import("react").ForwardRefExoticComponent<Omit<Omit
27
27
  backBehavior?: import("@react-navigation/routers/lib/typescript/src/TabRouter").BackBehavior | undefined;
28
28
  } & {
29
29
  defaultStatus?: import("@react-navigation/routers").DrawerStatus | undefined;
30
- } & import("@react-navigation/drawer/lib/typescript/src/types").DrawerNavigationConfig, "initialRouteName" | "children" | "id" | "screenListeners" | "screenOptions"> & import("@react-navigation/routers").DefaultRouterOptions<string> & {
30
+ } & import("@react-navigation/drawer/lib/typescript/src/types").DrawerNavigationConfig, "children" | "id" | "initialRouteName" | "screenListeners" | "screenOptions"> & import("@react-navigation/routers").DefaultRouterOptions<string> & {
31
31
  id?: string | undefined;
32
32
  children: import("react").ReactNode;
33
33
  screenListeners?: Partial<{
@@ -77,7 +77,7 @@ export declare const Drawer: import("react").ForwardRefExoticComponent<Omit<Omit
77
77
  backBehavior?: import("@react-navigation/routers/lib/typescript/src/TabRouter").BackBehavior | undefined;
78
78
  } & {
79
79
  defaultStatus?: import("@react-navigation/routers").DrawerStatus | undefined;
80
- } & import("@react-navigation/drawer/lib/typescript/src/types").DrawerNavigationConfig, "initialRouteName" | "children" | "id" | "screenListeners" | "screenOptions"> & import("@react-navigation/routers").DefaultRouterOptions<string> & {
80
+ } & import("@react-navigation/drawer/lib/typescript/src/types").DrawerNavigationConfig, "children" | "id" | "initialRouteName" | "screenListeners" | "screenOptions"> & import("@react-navigation/routers").DefaultRouterOptions<string> & {
81
81
  id?: string | undefined;
82
82
  children: import("react").ReactNode;
83
83
  screenListeners?: Partial<{
@@ -25,7 +25,7 @@ export declare const Stack: import("react").ForwardRefExoticComponent<Omit<Omit<
25
25
  route: import("@react-navigation/core").RouteProp<import("@react-navigation/routers").ParamListBase, string>;
26
26
  navigation: any;
27
27
  }) => NativeStackNavigationOptions) | undefined;
28
- } & import("@react-navigation/routers").StackRouterOptions, "initialRouteName" | "children" | "id" | "screenListeners" | "screenOptions"> & import("@react-navigation/routers").DefaultRouterOptions<string> & {
28
+ } & import("@react-navigation/routers").StackRouterOptions, "children" | "id" | "initialRouteName" | "screenListeners" | "screenOptions"> & import("@react-navigation/routers").DefaultRouterOptions<string> & {
29
29
  id?: string | undefined;
30
30
  children: import("react").ReactNode;
31
31
  screenListeners?: Partial<{
@@ -75,7 +75,7 @@ export declare const Stack: import("react").ForwardRefExoticComponent<Omit<Omit<
75
75
  route: import("@react-navigation/core").RouteProp<import("@react-navigation/routers").ParamListBase, string>;
76
76
  navigation: any;
77
77
  }) => NativeStackNavigationOptions) | undefined;
78
- } & import("@react-navigation/routers").StackRouterOptions, "initialRouteName" | "children" | "id" | "screenListeners" | "screenOptions"> & import("@react-navigation/routers").DefaultRouterOptions<string> & {
78
+ } & import("@react-navigation/routers").StackRouterOptions, "children" | "id" | "initialRouteName" | "screenListeners" | "screenOptions"> & import("@react-navigation/routers").DefaultRouterOptions<string> & {
79
79
  id?: string | undefined;
80
80
  children: import("react").ReactNode;
81
81
  screenListeners?: Partial<{
@@ -28,7 +28,7 @@ export declare const Tabs: React.ForwardRefExoticComponent<Omit<Omit<import("@re
28
28
  }) => BottomTabNavigationOptions) | undefined;
29
29
  } & import("@react-navigation/routers").DefaultRouterOptions & {
30
30
  backBehavior?: import("@react-navigation/routers/lib/typescript/src/TabRouter").BackBehavior | undefined;
31
- } & import("@react-navigation/bottom-tabs/lib/typescript/src/types").BottomTabNavigationConfig, "initialRouteName" | "children" | "id" | "screenListeners" | "screenOptions"> & import("@react-navigation/routers").DefaultRouterOptions<string> & {
31
+ } & import("@react-navigation/bottom-tabs/lib/typescript/src/types").BottomTabNavigationConfig, "children" | "id" | "initialRouteName" | "screenListeners" | "screenOptions"> & import("@react-navigation/routers").DefaultRouterOptions<string> & {
32
32
  id?: string | undefined;
33
33
  children: React.ReactNode;
34
34
  screenListeners?: Partial<{
@@ -80,7 +80,7 @@ export declare const Tabs: React.ForwardRefExoticComponent<Omit<Omit<import("@re
80
80
  }) => BottomTabNavigationOptions) | undefined;
81
81
  } & import("@react-navigation/routers").DefaultRouterOptions & {
82
82
  backBehavior?: import("@react-navigation/routers/lib/typescript/src/TabRouter").BackBehavior | undefined;
83
- } & import("@react-navigation/bottom-tabs/lib/typescript/src/types").BottomTabNavigationConfig, "initialRouteName" | "children" | "id" | "screenListeners" | "screenOptions"> & import("@react-navigation/routers").DefaultRouterOptions<string> & {
83
+ } & import("@react-navigation/bottom-tabs/lib/typescript/src/types").BottomTabNavigationConfig, "children" | "id" | "initialRouteName" | "screenListeners" | "screenOptions"> & import("@react-navigation/routers").DefaultRouterOptions<string> & {
84
84
  id?: string | undefined;
85
85
  children: React.ReactNode;
86
86
  screenListeners?: Partial<{
@@ -30,7 +30,7 @@ export declare function findTopRouteForTarget(state: ResultState): Omit<import("
30
30
  })[];
31
31
  type: string;
32
32
  stale: false;
33
- }>, "stale" | "routes">> & {
33
+ }>, "routes" | "stale">> & {
34
34
  routes: (Omit<import("@react-navigation/native").Route<string, object | undefined>, "key"> & any)[];
35
35
  }> | undefined;
36
36
  };
@@ -52,7 +52,7 @@ export declare function getQualifiedStateForTopOfTargetState(rootState: InitialS
52
52
  })[];
53
53
  type: string;
54
54
  stale: false;
55
- }>, "stale" | "routes">> & {
55
+ }>, "routes" | "stale">> & {
56
56
  routes: (Omit<import("@react-navigation/native").Route<string, object | undefined>, "key"> & {
57
57
  state?: Readonly<Partial<Omit<Readonly<{
58
58
  key: string;
@@ -70,7 +70,7 @@ export declare function getQualifiedStateForTopOfTargetState(rootState: InitialS
70
70
  })[];
71
71
  type: string;
72
72
  stale: false;
73
- }>, "stale" | "routes">> & any> | undefined;
73
+ }>, "routes" | "stale">> & any> | undefined;
74
74
  })[];
75
75
  }>;
76
76
  export declare function getEarliestMismatchedRoute<T extends ParamListBase>(rootState: NavigationState<T> | undefined, actionParams: NavigateActionParams): {
@@ -0,0 +1,20 @@
1
+ /// <reference types="react" />
2
+ import requireContext from "./require-context-ponyfill";
3
+ export type ReactComponent = () => React.ReactElement<any, any> | null;
4
+ export type FileStub = {
5
+ default: ReactComponent;
6
+ } | ReactComponent;
7
+ export { requireContext };
8
+ export declare function inMemoryContext(context: Record<string, FileStub>): ((id: string) => ReactComponent | {
9
+ default: FileStub;
10
+ }) & {
11
+ keys: () => string[];
12
+ resolve: (key: string) => string;
13
+ id: string;
14
+ };
15
+ export declare function requireContextWithOverrides(dir: string, overrides: Record<string, FileStub>): ((id: string) => any) & {
16
+ keys: () => string[];
17
+ resolve: (key: string) => string;
18
+ id: string;
19
+ };
20
+ //# sourceMappingURL=context-stubs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-stubs.d.ts","sourceRoot":"","sources":["../../src/testing-library/context-stubs.ts"],"names":[],"mappings":";AAEA,OAAO,cAAc,MAAM,4BAA4B,CAAC;AAExD,MAAM,MAAM,cAAc,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;AACvE,MAAM,MAAM,QAAQ,GAAG;IAAE,OAAO,EAAE,cAAc,CAAA;CAAE,GAAG,cAAc,CAAC;AAEpE,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,SAE/C,MAAM;;;;mBAQH,MAAM;;EAI1B;AAED,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,SAKnB,MAAM;;mBAUH,MAAM;;EAI1B"}
@@ -1,22 +1,18 @@
1
1
  import "./expect";
2
2
  import { render } from "@testing-library/react-native";
3
- import React from "react";
3
+ import { FileStub } from "./context-stubs";
4
4
  export * from "@testing-library/react-native";
5
5
  type RenderRouterOptions = Parameters<typeof render>[1] & {
6
6
  initialUrl?: string;
7
7
  };
8
- type RouteOverrideFunction = () => React.ReactElement<any, any> | null;
9
- type RouteOverride = {
10
- default: RouteOverrideFunction;
11
- } | RouteOverrideFunction;
12
8
  type Result = ReturnType<typeof render> & {
13
9
  getPathname(): string;
14
10
  getSearchParams(): URLSearchParams;
15
11
  };
16
12
  export declare function renderRouter(context?: string, options?: RenderRouterOptions): Result;
17
- export declare function renderRouter(context: Record<string, RouteOverride>, options?: RenderRouterOptions): Result;
13
+ export declare function renderRouter(context: Record<string, FileStub>, options?: RenderRouterOptions): Result;
18
14
  export declare function renderRouter(context: {
19
15
  appDir: string;
20
- overrides: Record<string, RouteOverride>;
16
+ overrides: Record<string, FileStub>;
21
17
  }, options?: RenderRouterOptions): Result;
22
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing-library/index.tsx"],"names":[],"mappings":"AACA,OAAO,UAAU,CAAC;AAGlB,OAAO,EAAE,MAAM,EAAgB,MAAM,+BAA+B,CAAC;AAGrE,OAAO,KAAK,MAAM,OAAO,CAAC;AAS1B,cAAc,+BAA+B,CAAC;AAE9C,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,qBAAqB,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;AAEvE,KAAK,aAAa,GAAG;IAAE,OAAO,EAAE,qBAAqB,CAAA;CAAE,GAAG,qBAAqB,CAAC;AAEhF,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,GAAG;IACxC,WAAW,IAAI,MAAM,CAAC;IACtB,eAAe,IAAI,eAAe,CAAC;CACpC,CAAC;AAQF,wBAAgB,YAAY,CAC1B,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,MAAM,CAAC;AACV,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,EACtC,OAAO,CAAC,EAAE,mBAAmB,GAC5B,MAAM,CAAC;AACV,wBAAgB,YAAY,CAC1B,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;CAAE,EACrE,OAAO,CAAC,EAAE,mBAAmB,GAC5B,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing-library/index.tsx"],"names":[],"mappings":"AACA,OAAO,UAAU,CAAC;AAGlB,OAAO,EAAE,MAAM,EAAgB,MAAM,+BAA+B,CAAC;AAQrE,OAAO,EACL,QAAQ,EAIT,MAAM,iBAAiB,CAAC;AAIzB,cAAc,+BAA+B,CAAC;AAE9C,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,MAAM,CAAC,GAAG;IACxC,WAAW,IAAI,MAAM,CAAC;IACtB,eAAe,IAAI,eAAe,CAAC;CACpC,CAAC;AAQF,wBAAgB,YAAY,CAC1B,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,MAAM,CAAC;AACV,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EACjC,OAAO,CAAC,EAAE,mBAAmB,GAC5B,MAAM,CAAC;AACV,wBAAgB,YAAY,CAC1B,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;CAAE,EAChE,OAAO,CAAC,EAAE,mBAAmB,GAC5B,MAAM,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { ResultState } from "./fork/getStateFromPath";
2
+ import { ExpoRouterContextType, OnboardingExpoRouterContextType } from "./hooks";
3
+ import { RequireContext } from "./types";
4
+ export type ExpoRootProps = {
5
+ context: RequireContext;
6
+ location?: URL;
7
+ };
8
+ /** @private */
9
+ export declare function createExpoRouterContext({ context, location, }: ExpoRootProps): {
10
+ routeNode: null;
11
+ linking: {
12
+ prefixes: never[];
13
+ };
14
+ initialState: undefined;
15
+ getRouteInfo(): never;
16
+ } | {
17
+ routeNode: import("./Route").RouteNode;
18
+ linking: import("./getLinkingConfig").ExpoLinkingOptions;
19
+ initialState: ResultState | undefined;
20
+ getRouteInfo: (state: ResultState) => import("./LocationProvider").UrlObject;
21
+ };
22
+ export declare function useCreateExpoRouterContext(props: ExpoRootProps): Omit<ExpoRouterContextType, "navigationRef"> | Omit<OnboardingExpoRouterContextType, "navigationRef">;
23
+ //# sourceMappingURL=useCreateExpoRouterContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCreateExpoRouterContext.d.ts","sourceRoot":"","sources":["../src/useCreateExpoRouterContext.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAGtD,OAAO,EACL,qBAAqB,EACrB,+BAA+B,EAChC,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGzC,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,CAAC,EAAE,GAAG,CAAC;CAChB,CAAC;AAOF,eAAe;AACf,wBAAgB,uBAAuB,CAAC,EACtC,OAAO,EACP,QAAqB,GACtB,EAAE,aAAa;;;;;;;;;;;0BAyBe,WAAW;EAsBzC;AAED,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,aAAa,yGAO9D"}
@@ -1 +1 @@
1
- {"version":3,"file":"ErrorBoundary.d.ts","sourceRoot":"","sources":["../../src/views/ErrorBoundary.tsx"],"names":[],"mappings":";AAMA,OAAO,EAAE,kBAAkB,EAAE,MAAM,OAAO,CAAC;AAE3C,wBAAgB,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,kBAAkB,eAkEjE"}
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"}
package/entry.js CHANGED
@@ -1,6 +1,8 @@
1
+ // `@expo/metro-runtime` MUST be the first import to ensure Fast Refresh works
2
+ // on web.
3
+ import "@expo/metro-runtime";
1
4
  import { ExpoRoot } from "expo-router";
2
5
  import Head from "expo-router/head";
3
- import "@expo/metro-runtime";
4
6
  import { renderRootComponent } from "expo-router/src/renderRootComponent";
5
7
 
6
8
  const ctx = require.context(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-router",
3
- "version": "1.7.2",
3
+ "version": "1.7.3",
4
4
  "main": "src/index.tsx",
5
5
  "types": "build/index.d.ts",
6
6
  "files": [
@@ -102,12 +102,12 @@
102
102
  },
103
103
  "dependencies": {
104
104
  "@bacons/react-views": "^1.1.3",
105
- "@expo/metro-runtime": "2.1.1",
105
+ "@expo/metro-runtime": "2.1.2",
106
106
  "@radix-ui/react-slot": "^1.0.0",
107
107
  "@react-navigation/bottom-tabs": "~6.5.7",
108
108
  "@react-navigation/native": "~6.1.6",
109
109
  "@react-navigation/native-stack": "~6.9.12",
110
- "expo-head": "0.0.3",
110
+ "expo-head": "0.0.4",
111
111
  "expo-splash-screen": "*",
112
112
  "query-string": "7.1.3",
113
113
  "react-helmet-async": "^1.3.0",
package/src/ExpoRoot.tsx CHANGED
@@ -4,22 +4,17 @@ import React from "react";
4
4
  import { Platform } from "react-native";
5
5
  import { SafeAreaProvider } from "react-native-safe-area-context";
6
6
 
7
- import { getRouteInfoFromState } from "./LocationProvider";
8
7
  import UpstreamNavigationContainer from "./fork/NavigationContainer";
9
- import getPathFromState, {
10
- getPathDataFromState,
11
- } from "./fork/getPathFromState";
12
8
  import { ResultState } from "./fork/getStateFromPath";
13
- import { getLinkingConfig } from "./getLinkingConfig";
14
- import { getRoutes } from "./getRoutes";
15
9
  import {
16
- ExpoRouterContextType,
17
10
  ExpoRouterContext,
18
11
  RootStateContext,
19
12
  RootStateContextType,
20
- OnboardingExpoRouterContextType,
21
13
  } from "./hooks";
22
- import { RequireContext } from "./types";
14
+ import {
15
+ ExpoRootProps,
16
+ useCreateExpoRouterContext,
17
+ } from "./useCreateExpoRouterContext";
23
18
  import { getQualifiedRouteComponent } from "./useScreens";
24
19
  import { SplashScreen } from "./views/Splash";
25
20
 
@@ -28,9 +23,15 @@ function getGestureHandlerRootView() {
28
23
  const { GestureHandlerRootView } =
29
24
  require("react-native-gesture-handler") as typeof import("react-native-gesture-handler");
30
25
 
31
- return function GestureHandler(props: any) {
26
+ // eslint-disable-next-line no-inner-declarations
27
+ function GestureHandler(props: any) {
32
28
  return <GestureHandlerRootView style={{ flex: 1 }} {...props} />;
33
- };
29
+ }
30
+ if (process.env.NODE_ENV === "development") {
31
+ // @ts-expect-error
32
+ GestureHandler.displayName = "GestureHandlerRootView";
33
+ }
34
+ return GestureHandler;
34
35
  } catch {
35
36
  return React.Fragment;
36
37
  }
@@ -43,11 +44,6 @@ const INITIAL_METRICS = {
43
44
  insets: { top: 0, left: 0, right: 0, bottom: 0 },
44
45
  };
45
46
 
46
- export type ExpoRootProps = {
47
- context: RequireContext;
48
- location?: URL;
49
- };
50
-
51
47
  export function ExpoRoot({ context, location }: ExpoRootProps) {
52
48
  return (
53
49
  <GestureHandlerRootView>
@@ -63,57 +59,13 @@ export function ExpoRoot({ context, location }: ExpoRootProps) {
63
59
  );
64
60
  }
65
61
 
66
- const initialUrl =
67
- Platform.OS === "web" && typeof window !== "undefined"
68
- ? new URL(window.location.href)
69
- : undefined;
70
-
71
- function ContextNavigator({
72
- context,
73
- location: initialLocation = initialUrl,
74
- }: ExpoRootProps) {
62
+ function ContextNavigator(props: ExpoRootProps) {
75
63
  const navigationRef = useNavigationContainerRef();
76
64
  const [shouldShowSplash, setShowSplash] = React.useState(
77
65
  Platform.OS !== "web"
78
66
  );
79
67
 
80
- const expoContext = React.useMemo<
81
- ExpoRouterContextType | OnboardingExpoRouterContextType
82
- >(() => {
83
- const routeNode = getRoutes(context);
84
- const linking = getLinkingConfig(routeNode!);
85
- let initialState: ResultState | undefined;
86
-
87
- if (initialLocation) {
88
- initialState = linking.getStateFromPath?.(
89
- initialLocation.pathname + initialLocation.search,
90
- linking.config
91
- );
92
- }
93
-
94
- function getRouteInfo(state: ResultState) {
95
- return getRouteInfoFromState(
96
- (state: Parameters<typeof getPathFromState>[0], asPath: boolean) => {
97
- return getPathDataFromState(state, {
98
- screens: [],
99
- ...linking.config,
100
- preserveDynamicRoutes: asPath,
101
- preserveGroups: asPath,
102
- });
103
- },
104
- state
105
- );
106
- }
107
-
108
- // This looks redundant but it makes TypeScript correctly infer the union return type.
109
- return {
110
- routeNode,
111
- linking,
112
- navigationRef,
113
- initialState,
114
- getRouteInfo,
115
- };
116
- }, [context, navigationRef, initialLocation]);
68
+ const expoContext = useCreateExpoRouterContext(props);
117
69
 
118
70
  const { routeNode, initialState, linking, getRouteInfo } = expoContext;
119
71
 
@@ -147,6 +99,15 @@ function ContextNavigator({
147
99
  return () => subscription?.();
148
100
  }, [navigationRef, getRouteInfo]);
149
101
 
102
+ const Component = routeNode ? getQualifiedRouteComponent(routeNode) : null;
103
+
104
+ const comp = React.useMemo(() => {
105
+ if (!Component) {
106
+ return null;
107
+ }
108
+ return <Component />;
109
+ }, [Component, shouldShowSplash]);
110
+
150
111
  if (!routeNode) {
151
112
  if (process.env.NODE_ENV === "development") {
152
113
  const Tutorial = require("./onboard/Tutorial").Tutorial;
@@ -157,12 +118,10 @@ function ContextNavigator({
157
118
  }
158
119
  }
159
120
 
160
- const Component = getQualifiedRouteComponent(routeNode);
161
-
162
121
  return (
163
122
  <>
164
123
  {shouldShowSplash && <SplashScreen />}
165
- <ExpoRouterContext.Provider value={expoContext}>
124
+ <ExpoRouterContext.Provider value={{ ...expoContext, navigationRef }}>
166
125
  <UpstreamNavigationContainer
167
126
  ref={navigationRef}
168
127
  initialState={initialState}
@@ -170,7 +129,7 @@ function ContextNavigator({
170
129
  onReady={() => requestAnimationFrame(() => setShowSplash(false))}
171
130
  >
172
131
  <RootStateContext.Provider value={rootState}>
173
- {!shouldShowSplash && <Component />}
132
+ {!shouldShowSplash && comp}
174
133
  </RootStateContext.Provider>
175
134
  </UpstreamNavigationContainer>
176
135
  </ExpoRouterContext.Provider>
package/src/link/href.ts CHANGED
@@ -55,6 +55,6 @@ function encodeParam(param: any): string {
55
55
 
56
56
  function createQueryParams(params: Record<string, any>): string {
57
57
  return Object.entries(params)
58
- .map((props) => props.join("="))
58
+ .map(([key, value]) => `${key}=${encodeURIComponent(value.toString())}`)
59
59
  .join("&");
60
60
  }
@@ -0,0 +1,47 @@
1
+ import path from "path";
2
+
3
+ import requireContext from "./require-context-ponyfill";
4
+
5
+ export type ReactComponent = () => React.ReactElement<any, any> | null;
6
+ export type FileStub = { default: ReactComponent } | ReactComponent;
7
+
8
+ export { requireContext };
9
+
10
+ export function inMemoryContext(context: Record<string, FileStub>) {
11
+ return Object.assign(
12
+ function (id: string) {
13
+ id = id.replace(/^\.\//, "").replace(/\.js$/, "");
14
+ return typeof context[id] === "function"
15
+ ? { default: context[id] }
16
+ : context[id];
17
+ },
18
+ {
19
+ keys: () => Object.keys(context).map((key) => "./" + key + ".js"),
20
+ resolve: (key: string) => key,
21
+ id: "0",
22
+ }
23
+ );
24
+ }
25
+
26
+ export function requireContextWithOverrides(
27
+ dir: string,
28
+ overrides: Record<string, FileStub>
29
+ ) {
30
+ const existingContext = requireContext(path.resolve(process.cwd(), dir));
31
+
32
+ return Object.assign(
33
+ function (id: string) {
34
+ if (id in overrides) {
35
+ const route = overrides[id];
36
+ return typeof route === "function" ? { default: route } : route;
37
+ } else {
38
+ return existingContext(id);
39
+ }
40
+ },
41
+ {
42
+ keys: () => [...Object.keys(overrides), ...existingContext.keys()],
43
+ resolve: (key: string) => key,
44
+ id: "0",
45
+ }
46
+ );
47
+ }
@@ -10,8 +10,13 @@ import React from "react";
10
10
  import { ExpoRoot } from "../ExpoRoot";
11
11
  import { stateCache } from "../getLinkingConfig";
12
12
  import { RequireContext } from "../types";
13
+ import {
14
+ FileStub,
15
+ inMemoryContext,
16
+ requireContext,
17
+ requireContextWithOverrides,
18
+ } from "./context-stubs";
13
19
  import { initialUrlRef } from "./mocks";
14
- import requireContext from "./require-context-ponyfill";
15
20
 
16
21
  // re-export everything
17
22
  export * from "@testing-library/react-native";
@@ -20,10 +25,6 @@ type RenderRouterOptions = Parameters<typeof render>[1] & {
20
25
  initialUrl?: string;
21
26
  };
22
27
 
23
- type RouteOverrideFunction = () => React.ReactElement<any, any> | null;
24
-
25
- type RouteOverride = { default: RouteOverrideFunction } | RouteOverrideFunction;
26
-
27
28
  type Result = ReturnType<typeof render> & {
28
29
  getPathname(): string;
29
30
  getSearchParams(): URLSearchParams;
@@ -31,7 +32,7 @@ type Result = ReturnType<typeof render> & {
31
32
 
32
33
  function isOverrideContext(
33
34
  context: object
34
- ): context is { appDir: string; overrides: Record<string, RouteOverride> } {
35
+ ): context is { appDir: string; overrides: Record<string, FileStub> } {
35
36
  return Boolean(typeof context === "object" && "appDir" in context);
36
37
  }
37
38
 
@@ -40,18 +41,18 @@ export function renderRouter(
40
41
  options?: RenderRouterOptions
41
42
  ): Result;
42
43
  export function renderRouter(
43
- context: Record<string, RouteOverride>,
44
+ context: Record<string, FileStub>,
44
45
  options?: RenderRouterOptions
45
46
  ): Result;
46
47
  export function renderRouter(
47
- context: { appDir: string; overrides: Record<string, RouteOverride> },
48
+ context: { appDir: string; overrides: Record<string, FileStub> },
48
49
  options?: RenderRouterOptions
49
50
  ): Result;
50
51
  export function renderRouter(
51
52
  context:
52
53
  | string
53
- | { appDir: string; overrides: Record<string, RouteOverride> }
54
- | Record<string, RouteOverride> = "./app",
54
+ | { appDir: string; overrides: Record<string, FileStub> }
55
+ | Record<string, FileStub> = "./app",
55
56
  { initialUrl = "/", ...options }: RenderRouterOptions = {}
56
57
  ): Result {
57
58
  jest.useFakeTimers();
@@ -67,42 +68,9 @@ export function renderRouter(
67
68
  if (typeof context === "string") {
68
69
  ctx = requireContext(path.resolve(process.cwd(), context));
69
70
  } else if (isOverrideContext(context)) {
70
- const existingContext = requireContext(
71
- path.resolve(process.cwd(), context.appDir)
72
- );
73
-
74
- ctx = Object.assign(
75
- function (id: string) {
76
- if (id in context.overrides) {
77
- const route = context.overrides[id];
78
- return typeof route === "function" ? { default: route } : route;
79
- } else {
80
- return existingContext(id);
81
- }
82
- },
83
- {
84
- keys: () => [
85
- ...Object.keys(context.overrides),
86
- ...existingContext.keys(),
87
- ],
88
- resolve: (key: string) => key,
89
- id: "0",
90
- }
91
- );
71
+ ctx = requireContextWithOverrides(context.appDir, context.overrides);
92
72
  } else {
93
- ctx = Object.assign(
94
- function (id: string) {
95
- id = id.replace(/^\.\//, "").replace(/\.js$/, "");
96
- return typeof context[id] === "function"
97
- ? { default: context[id] }
98
- : context[id];
99
- },
100
- {
101
- keys: () => Object.keys(context).map((key) => "./" + key + ".js"),
102
- resolve: (key: string) => key,
103
- id: "0",
104
- }
105
- );
73
+ ctx = inMemoryContext(context);
106
74
  }
107
75
 
108
76
  stateCache.clear();
@@ -0,0 +1,88 @@
1
+ import React from "react";
2
+ import { Platform } from "react-native";
3
+
4
+ import { getRouteInfoFromState } from "./LocationProvider";
5
+ import getPathFromState, {
6
+ getPathDataFromState,
7
+ } from "./fork/getPathFromState";
8
+ import { ResultState } from "./fork/getStateFromPath";
9
+ import { getLinkingConfig } from "./getLinkingConfig";
10
+ import { getRoutes } from "./getRoutes";
11
+ import {
12
+ ExpoRouterContextType,
13
+ OnboardingExpoRouterContextType,
14
+ } from "./hooks";
15
+ import { RequireContext } from "./types";
16
+ // import URL from "url-parse";
17
+
18
+ export type ExpoRootProps = {
19
+ context: RequireContext;
20
+ location?: URL;
21
+ };
22
+
23
+ const initialUrl =
24
+ Platform.OS === "web" && typeof window !== "undefined"
25
+ ? new URL(window.location.href)
26
+ : undefined;
27
+
28
+ /** @private */
29
+ export function createExpoRouterContext({
30
+ context,
31
+ location = initialUrl,
32
+ }: ExpoRootProps) {
33
+ const routeNode = getRoutes(context);
34
+
35
+ // No app dir exists
36
+ if (!routeNode) {
37
+ return {
38
+ routeNode,
39
+ linking: { prefixes: [] },
40
+ initialState: undefined,
41
+ getRouteInfo() {
42
+ throw new Error("invalid");
43
+ },
44
+ };
45
+ }
46
+
47
+ const linking = getLinkingConfig(routeNode);
48
+ let initialState: ResultState | undefined;
49
+
50
+ if (location) {
51
+ initialState = linking.getStateFromPath?.(
52
+ location.pathname + location.search,
53
+ linking.config
54
+ );
55
+ }
56
+
57
+ function getRouteInfo(state: ResultState) {
58
+ return getRouteInfoFromState(
59
+ (state: Parameters<typeof getPathFromState>[0], asPath: boolean) => {
60
+ return getPathDataFromState(state, {
61
+ screens: [],
62
+ ...linking.config,
63
+ preserveDynamicRoutes: asPath,
64
+ preserveGroups: asPath,
65
+ });
66
+ },
67
+ state
68
+ );
69
+ }
70
+
71
+ // This looks redundant but it makes TypeScript correctly infer the union return type.
72
+ return {
73
+ routeNode,
74
+ linking,
75
+
76
+ initialState,
77
+ getRouteInfo,
78
+ };
79
+ }
80
+
81
+ export function useCreateExpoRouterContext(props: ExpoRootProps) {
82
+ return React.useMemo<
83
+ | Omit<ExpoRouterContextType, "navigationRef">
84
+ | Omit<OnboardingExpoRouterContextType, "navigationRef">
85
+ >(() => {
86
+ return createExpoRouterContext(props);
87
+ }, [props.context, props.location]);
88
+ }
@@ -1,16 +1,57 @@
1
1
  import { Pressable, StyleSheet, Text, View } from "@bacons/react-views";
2
+ import { LogContext } from "@expo/metro-runtime/build/error-overlay/Data/LogContext";
3
+ import { LogBoxInspectorStackFrames } from "@expo/metro-runtime/build/error-overlay/overlay/LogBoxInspectorStackFrames";
4
+ import { LogBoxLog, parseErrorStack } from "@expo/metro-runtime/symbolicate";
5
+ import { BottomTabBarHeightContext } from "@react-navigation/bottom-tabs";
2
6
  import React from "react";
3
- import { Platform, ScrollView, TouchableOpacity } from "react-native";
7
+ import { Platform, ScrollView } from "react-native";
4
8
  import { SafeAreaView } from "react-native-safe-area-context";
5
9
 
6
10
  import { Link } from "../link/Link";
7
11
  import { ErrorBoundaryProps } from "./Try";
8
12
 
13
+ function useMetroSymbolication(error: Error) {
14
+ const [logBoxLog, setLogBoxLog] = React.useState<LogBoxLog | null>(null);
15
+
16
+ React.useEffect(() => {
17
+ let isMounted = true;
18
+ const stack = parseErrorStack(error.stack);
19
+
20
+ const log = new LogBoxLog({
21
+ level: "error",
22
+ message: {
23
+ content: error.message,
24
+ substitutions: [],
25
+ },
26
+ isComponentError: false,
27
+ stack,
28
+ category: error.message,
29
+ componentStack: [],
30
+ });
31
+
32
+ log.symbolicate("stack", (symbolicatedLog) => {
33
+ if (isMounted) {
34
+ setLogBoxLog(log);
35
+ }
36
+ });
37
+
38
+ return () => {
39
+ isMounted = false;
40
+ };
41
+ }, [error]);
42
+
43
+ return logBoxLog;
44
+ }
45
+
9
46
  export function ErrorBoundary({ error, retry }: ErrorBoundaryProps) {
47
+ const logBoxLog = useMetroSymbolication(error);
48
+ const inTabBar = React.useContext(BottomTabBarHeightContext);
49
+ const Wrapper = inTabBar ? View : SafeAreaView;
50
+
10
51
  return (
11
- <View style={[styles.container]}>
12
- <SafeAreaView
13
- style={{ flex: 1, maxWidth: 720, marginHorizontal: "auto" }}
52
+ <View style={styles.container}>
53
+ <Wrapper
54
+ style={{ flex: 1, gap: 8, maxWidth: 720, marginHorizontal: "auto" }}
14
55
  >
15
56
  <View
16
57
  style={{
@@ -28,63 +69,56 @@ export function ErrorBoundary({ error, retry }: ErrorBoundaryProps) {
28
69
  >
29
70
  Something went wrong
30
71
  </Text>
31
- <View style={{ flexDirection: "row", alignItems: "center" }}>
32
- <Pressable>
33
- {({ hovered }) => (
34
- <TouchableOpacity onPress={retry}>
35
- <View
36
- style={[
37
- {
38
- transitionDuration: "100ms",
39
- paddingVertical: 12,
40
- paddingHorizontal: 24,
41
- borderColor: "white",
42
- borderWidth: 2,
43
- marginLeft: 8,
44
- },
45
- hovered && { backgroundColor: "white" },
46
- ]}
47
- >
48
- <Text
49
- style={[
50
- styles.buttonText,
51
- {
52
- transitionDuration: "100ms",
53
- color: hovered ? "black" : "white",
54
- },
55
- ]}
56
- >
57
- Retry
58
- </Text>
59
- </View>
60
- </TouchableOpacity>
61
- )}
62
- </Pressable>
63
- </View>
64
72
  </View>
65
73
 
66
- <StackTrace error={error} />
74
+ <StackTrace logData={logBoxLog} />
67
75
  {process.env.NODE_ENV === "development" && (
68
76
  <Link href="/_sitemap" style={styles.link}>
69
77
  Sitemap
70
78
  </Link>
71
79
  )}
72
- </SafeAreaView>
80
+ <Pressable onPress={retry}>
81
+ {({ hovered, pressed }) => (
82
+ <View
83
+ style={[
84
+ styles.buttonInner,
85
+ (hovered || pressed) && { backgroundColor: "white" },
86
+ ]}
87
+ >
88
+ <Text
89
+ style={[
90
+ styles.buttonText,
91
+ {
92
+ transitionDuration: "100ms",
93
+ color: hovered || pressed ? "black" : "white",
94
+ },
95
+ ]}
96
+ >
97
+ Retry
98
+ </Text>
99
+ </View>
100
+ )}
101
+ </Pressable>
102
+ </Wrapper>
73
103
  </View>
74
104
  );
75
105
  }
76
106
 
77
- function StackTrace({ error }: { error: Error }) {
107
+ function StackTrace({ logData }: { logData: LogBoxLog | null }) {
108
+ if (!logData?.symbolicated?.stack?.stack) {
109
+ return null;
110
+ }
78
111
  return (
79
- <ScrollView
80
- style={{
81
- marginVertical: 8,
82
- borderColor: "rgba(255,255,255,0.5)",
83
- borderWidth: 1,
84
- padding: 12,
85
- }}
86
- >
87
- <Text style={[styles.code, { color: "white" }]}>{error.stack}</Text>
112
+ <ScrollView style={{ flex: 1 }}>
113
+ <LogContext.Provider
114
+ value={{
115
+ isDisabled: false,
116
+ logs: [logData],
117
+ selectedLogIndex: 0,
118
+ }}
119
+ >
120
+ <LogBoxInspectorStackFrames onRetry={function () {}} type="stack" />
121
+ </LogContext.Provider>
88
122
  </ScrollView>
89
123
  );
90
124
  }
@@ -99,9 +133,7 @@ const styles = StyleSheet.create({
99
133
  },
100
134
  title: {
101
135
  color: "white",
102
- fontSize: 36,
103
-
104
- // textAlign: "center",
136
+ fontSize: Platform.select({ web: 32, default: 24 }),
105
137
  fontWeight: "bold",
106
138
  },
107
139
  buttonText: {
@@ -109,6 +141,16 @@ const styles = StyleSheet.create({
109
141
  fontWeight: "bold",
110
142
  color: "black",
111
143
  },
144
+ buttonInner: {
145
+ transitionDuration: "100ms",
146
+ paddingVertical: 12,
147
+ paddingHorizontal: 24,
148
+ borderColor: "white",
149
+ borderWidth: 2,
150
+ marginLeft: 8,
151
+ justifyContent: "center",
152
+ alignItems: "center",
153
+ },
112
154
  code: {
113
155
  fontFamily: Platform.select({
114
156
  default: "Courier",
@@ -1,10 +0,0 @@
1
- import { HrefObject } from "./href";
2
- /** @deprecated */
3
- type RouteInfo = Omit<Required<HrefObject>, "query"> & {
4
- /** Normalized path representing the selected route `/[id]?id=normal` -> `/normal` */
5
- href: string;
6
- };
7
- /** @deprecated */
8
- export declare function useHref(): RouteInfo;
9
- export {};
10
- //# sourceMappingURL=useHref.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useHref.d.ts","sourceRoot":"","sources":["../../src/link/useHref.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAEpC,kBAAkB;AAClB,KAAK,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,GAAG;IACrD,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,kBAAkB;AAClB,wBAAgB,OAAO,IAAI,SAAS,CAUnC"}
@@ -1,21 +0,0 @@
1
- import { usePathname, useSearchParams, useSegments } from "../hooks";
2
- import { HrefObject } from "./href";
3
-
4
- /** @deprecated */
5
- type RouteInfo = Omit<Required<HrefObject>, "query"> & {
6
- /** Normalized path representing the selected route `/[id]?id=normal` -> `/normal` */
7
- href: string;
8
- };
9
-
10
- /** @deprecated */
11
- export function useHref(): RouteInfo {
12
- console.warn(
13
- "useHref is deprecated in favor of usePathname, useSearchParams, and useSegments"
14
- );
15
-
16
- return {
17
- href: usePathname(),
18
- pathname: useSegments().join("/"),
19
- params: useSearchParams(),
20
- };
21
- }