diff --git a/packages/expo-router/CHANGELOG.md b/packages/expo-router/CHANGELOG.md index 15f9f49d54885..ab17774b33aef 100644 --- a/packages/expo-router/CHANGELOG.md +++ b/packages/expo-router/CHANGELOG.md @@ -23,6 +23,7 @@ - Fix `push` navigation not pushing the same route multiple times ([#27307](https://github.com/expo/expo/pull/27307) by [@marklawlor](https://github.com/marklawlor)) - Fix router.navigate will only push when path parameters change ([#27285](https://github.com/expo/expo/pull/27285) by [@marklawlor](https://github.com/marklawlor)) - Fix incorrect route generation of array shared groups with brackets ([#27459](https://github.com/expo/expo/pull/27459) by [@marklawlor](https://github.com/marklawlor)) +- Fix incorrect initial URL on web when using baseUrl ([#27287](https://github.com/expo/expo/pull/27287) by [@marklawlor](https://github.com/marklawlor)) ### 💡 Others diff --git a/packages/expo-router/build/ExpoRoot.d.ts.map b/packages/expo-router/build/ExpoRoot.d.ts.map index 0e990540845fd..1815c7f96f044 100644 --- a/packages/expo-router/build/ExpoRoot.d.ts.map +++ b/packages/expo-router/build/ExpoRoot.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"ExpoRoot.d.ts","sourceRoot":"","sources":["../src/ExpoRoot.tsx"],"names":[],"mappings":"AAKA,OAAc,EAAE,KAAK,iBAAiB,EAAY,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAMpF,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,aAAa,CAAC,iBAAiB,CAAC,CAAC;CAC5C,CAAC;AAgBF,wBAAgB,QAAQ,CAAC,EAAE,OAAO,EAAE,aAAwB,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,eAqBtF"} \ No newline at end of file +{"version":3,"file":"ExpoRoot.d.ts","sourceRoot":"","sources":["../src/ExpoRoot.tsx"],"names":[],"mappings":"AAKA,OAAc,EAAE,KAAK,iBAAiB,EAAY,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AAOpF,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,aAAa,CAAC,iBAAiB,CAAC,CAAC;CAC5C,CAAC;AAgBF,wBAAgB,QAAQ,CAAC,EAAE,OAAO,EAAE,aAAwB,EAAE,GAAG,KAAK,EAAE,EAAE,aAAa,eAqBtF"} \ No newline at end of file diff --git a/packages/expo-router/build/ExpoRoot.js b/packages/expo-router/build/ExpoRoot.js index 8103ca843639b..735720ae2ee29 100644 --- a/packages/expo-router/build/ExpoRoot.js +++ b/packages/expo-router/build/ExpoRoot.js @@ -35,6 +35,7 @@ const react_native_1 = require("react-native"); const react_native_safe_area_context_1 = require("react-native-safe-area-context"); const NavigationContainer_1 = __importDefault(require("./fork/NavigationContainer")); const router_store_1 = require("./global-state/router-store"); +const serverLocationContext_1 = require("./global-state/serverLocationContext"); const Splash_1 = require("./views/Splash"); const isTestEnv = process.env.NODE_ENV === 'test'; const INITIAL_METRICS = react_native_1.Platform.OS === 'web' || isTestEnv @@ -87,9 +88,11 @@ function ContextNavigator({ context, location: initialLocation = initialUrl, wra return ( - - - + + + + + ); } let onUnhandledAction; diff --git a/packages/expo-router/build/ExpoRoot.js.map b/packages/expo-router/build/ExpoRoot.js.map index 99fee0fa4a60a..ec8c55472e639 100644 --- a/packages/expo-router/build/ExpoRoot.js.map +++ b/packages/expo-router/build/ExpoRoot.js.map @@ -1 +1 @@ -{"version":3,"file":"ExpoRoot.js","sourceRoot":"","sources":["../src/ExpoRoot.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { NavigationAction } from '@react-navigation/native';\nimport Constants from 'expo-constants';\nimport { StatusBar } from 'expo-status-bar';\nimport React, { type PropsWithChildren, Fragment, type ComponentType } from 'react';\nimport { Platform } from 'react-native';\nimport { SafeAreaProvider } from 'react-native-safe-area-context';\n\nimport UpstreamNavigationContainer from './fork/NavigationContainer';\nimport { useInitializeExpoRouter } from './global-state/router-store';\nimport { RequireContext } from './types';\nimport { SplashScreen } from './views/Splash';\n\nexport type ExpoRootProps = {\n context: RequireContext;\n location?: URL;\n wrapper?: ComponentType;\n};\n\nconst isTestEnv = process.env.NODE_ENV === 'test';\n\nconst INITIAL_METRICS =\n Platform.OS === 'web' || isTestEnv\n ? {\n frame: { x: 0, y: 0, width: 0, height: 0 },\n insets: { top: 0, left: 0, right: 0, bottom: 0 },\n }\n : undefined;\n\nconst hasViewControllerBasedStatusBarAppearance =\n Platform.OS === 'ios' &&\n !!Constants.expoConfig?.ios?.infoPlist?.UIViewControllerBasedStatusBarAppearance;\n\nexport function ExpoRoot({ wrapper: ParentWrapper = Fragment, ...props }: ExpoRootProps) {\n /*\n * Due to static rendering we need to wrap these top level views in second wrapper\n * View's like generate a
so if the parent wrapper\n * is a HTML document, we need to ensure its inside the \n */\n const wrapper = ({ children }: PropsWithChildren) => {\n return (\n \n \n {children}\n {/* Users can override this by adding another StatusBar element anywhere higher in the component tree. */}\n {!hasViewControllerBasedStatusBarAppearance && }\n \n \n );\n };\n\n return ;\n}\n\nconst initialUrl =\n Platform.OS === 'web' && typeof window !== 'undefined'\n ? new URL(window.location.href)\n : undefined;\n\nfunction ContextNavigator({\n context,\n location: initialLocation = initialUrl,\n wrapper: WrapperComponent = Fragment,\n}: ExpoRootProps) {\n const store = useInitializeExpoRouter(context, initialLocation);\n\n if (store.shouldShowTutorial()) {\n SplashScreen.hideAsync();\n if (process.env.NODE_ENV === 'development') {\n const Tutorial = require('./onboard/Tutorial').Tutorial;\n return (\n \n \n \n );\n } else {\n // Ensure tutorial styles are stripped in production.\n return null;\n }\n }\n\n const Component = store.rootComponent;\n\n return (\n \n \n \n \n \n );\n}\n\nlet onUnhandledAction: (action: NavigationAction) => void;\n\nif (process.env.NODE_ENV !== 'production') {\n onUnhandledAction = (action: NavigationAction) => {\n const payload: Record | undefined = action.payload;\n\n let message = `The action '${action.type}'${\n payload ? ` with payload ${JSON.stringify(action.payload)}` : ''\n } was not handled by any navigator.`;\n\n switch (action.type) {\n case 'NAVIGATE':\n case 'PUSH':\n case 'REPLACE':\n case 'JUMP_TO':\n if (payload?.name) {\n message += `\\n\\nDo you have a route named '${payload.name}'?`;\n } else {\n message += `\\n\\nYou need to pass the name of the screen to navigate to. This may be a bug.`;\n }\n\n break;\n case 'GO_BACK':\n case 'POP':\n case 'POP_TO_TOP':\n message += `\\n\\nIs there any screen to go back to?`;\n break;\n case 'OPEN_DRAWER':\n case 'CLOSE_DRAWER':\n case 'TOGGLE_DRAWER':\n message += `\\n\\nIs your screen inside a Drawer navigator?`;\n break;\n }\n\n message += `\\n\\nThis is a development-only warning and won't be shown in production.`;\n\n if (process.env.NODE_ENV === 'test') {\n throw new Error(message);\n }\n console.error(message);\n };\n} else {\n onUnhandledAction = function () {};\n}\n"]} \ No newline at end of file +{"version":3,"file":"ExpoRoot.js","sourceRoot":"","sources":["../src/ExpoRoot.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client';\n\nimport { NavigationAction } from '@react-navigation/native';\nimport Constants from 'expo-constants';\nimport { StatusBar } from 'expo-status-bar';\nimport React, { type PropsWithChildren, Fragment, type ComponentType } from 'react';\nimport { Platform } from 'react-native';\nimport { SafeAreaProvider } from 'react-native-safe-area-context';\n\nimport UpstreamNavigationContainer from './fork/NavigationContainer';\nimport { useInitializeExpoRouter } from './global-state/router-store';\nimport { ServerLocationContext } from './global-state/serverLocationContext';\nimport { RequireContext } from './types';\nimport { SplashScreen } from './views/Splash';\n\nexport type ExpoRootProps = {\n context: RequireContext;\n location?: URL;\n wrapper?: ComponentType;\n};\n\nconst isTestEnv = process.env.NODE_ENV === 'test';\n\nconst INITIAL_METRICS =\n Platform.OS === 'web' || isTestEnv\n ? {\n frame: { x: 0, y: 0, width: 0, height: 0 },\n insets: { top: 0, left: 0, right: 0, bottom: 0 },\n }\n : undefined;\n\nconst hasViewControllerBasedStatusBarAppearance =\n Platform.OS === 'ios' &&\n !!Constants.expoConfig?.ios?.infoPlist?.UIViewControllerBasedStatusBarAppearance;\n\nexport function ExpoRoot({ wrapper: ParentWrapper = Fragment, ...props }: ExpoRootProps) {\n /*\n * Due to static rendering we need to wrap these top level views in second wrapper\n * View's like generate a
so if the parent wrapper\n * is a HTML document, we need to ensure its inside the \n */\n const wrapper = ({ children }: PropsWithChildren) => {\n return (\n \n \n {children}\n {/* Users can override this by adding another StatusBar element anywhere higher in the component tree. */}\n {!hasViewControllerBasedStatusBarAppearance && }\n \n \n );\n };\n\n return ;\n}\n\nconst initialUrl =\n Platform.OS === 'web' && typeof window !== 'undefined'\n ? new URL(window.location.href)\n : undefined;\n\nfunction ContextNavigator({\n context,\n location: initialLocation = initialUrl,\n wrapper: WrapperComponent = Fragment,\n}: ExpoRootProps) {\n const store = useInitializeExpoRouter(context, initialLocation);\n\n if (store.shouldShowTutorial()) {\n SplashScreen.hideAsync();\n if (process.env.NODE_ENV === 'development') {\n const Tutorial = require('./onboard/Tutorial').Tutorial;\n return (\n \n \n \n );\n } else {\n // Ensure tutorial styles are stripped in production.\n return null;\n }\n }\n\n const Component = store.rootComponent;\n\n return (\n \n \n \n \n \n \n \n );\n}\n\nlet onUnhandledAction: (action: NavigationAction) => void;\n\nif (process.env.NODE_ENV !== 'production') {\n onUnhandledAction = (action: NavigationAction) => {\n const payload: Record | undefined = action.payload;\n\n let message = `The action '${action.type}'${\n payload ? ` with payload ${JSON.stringify(action.payload)}` : ''\n } was not handled by any navigator.`;\n\n switch (action.type) {\n case 'NAVIGATE':\n case 'PUSH':\n case 'REPLACE':\n case 'JUMP_TO':\n if (payload?.name) {\n message += `\\n\\nDo you have a route named '${payload.name}'?`;\n } else {\n message += `\\n\\nYou need to pass the name of the screen to navigate to. This may be a bug.`;\n }\n\n break;\n case 'GO_BACK':\n case 'POP':\n case 'POP_TO_TOP':\n message += `\\n\\nIs there any screen to go back to?`;\n break;\n case 'OPEN_DRAWER':\n case 'CLOSE_DRAWER':\n case 'TOGGLE_DRAWER':\n message += `\\n\\nIs your screen inside a Drawer navigator?`;\n break;\n }\n\n message += `\\n\\nThis is a development-only warning and won't be shown in production.`;\n\n if (process.env.NODE_ENV === 'test') {\n throw new Error(message);\n }\n console.error(message);\n };\n} else {\n onUnhandledAction = function () {};\n}\n"]} \ No newline at end of file diff --git a/packages/expo-router/build/fork/useLinking.d.ts b/packages/expo-router/build/fork/useLinking.d.ts index 38f1e3e7b4996..f80984967fcf6 100644 --- a/packages/expo-router/build/fork/useLinking.d.ts +++ b/packages/expo-router/build/fork/useLinking.d.ts @@ -1,3 +1,55 @@ -declare const _default: typeof import("./useLinking.native").default; -export default _default; +import { NavigationContainerRef, ParamListBase } from '@react-navigation/core'; +import * as React from 'react'; +import type { LinkingOptions } from '@react-navigation/native'; +/** + * Run async function in series as it's called. + */ +export declare const series: (cb: () => Promise) => () => void; +type Options = LinkingOptions & { + independent?: boolean; +}; +export default function useLinking(ref: React.RefObject>, { independent, enabled, config, getStateFromPath, getPathFromState, getActionFromState, }: Options): { + getInitialState: () => PromiseLike<(Partial & Readonly<{ + params?: Readonly; + }> & { + state?: Readonly | import("@react-navigation/core").PartialState> | undefined; + })[]; + type: string; + stale: false; + }>, "stale" | "routes">> & Readonly<{ + stale?: true | undefined; + routes: import("@react-navigation/core").PartialRoute>[]; + }> & { + state?: (Partial & Readonly<{ + params?: Readonly; + }> & { + state?: Readonly | import("@react-navigation/core").PartialState> | undefined; + })[]; + type: string; + stale: false; + }>, "stale" | "routes">> & Readonly<{ + stale?: true | undefined; + routes: import("@react-navigation/core").PartialRoute>[]; + }> & any) | undefined; + }) | undefined>; +}; +export {}; //# sourceMappingURL=useLinking.d.ts.map \ No newline at end of file diff --git a/packages/expo-router/build/fork/useLinking.d.ts.map b/packages/expo-router/build/fork/useLinking.d.ts.map index dcbf00cedbecb..c51c8b4fdcf55 100644 --- a/packages/expo-router/build/fork/useLinking.d.ts.map +++ b/packages/expo-router/build/fork/useLinking.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"useLinking.d.ts","sourceRoot":"","sources":["../../src/fork/useLinking.ts"],"names":[],"mappings":";AAEA,wBAA0E"} \ No newline at end of file +{"version":3,"file":"useLinking.d.ts","sourceRoot":"","sources":["../../src/fork/useLinking.ts"],"names":[],"mappings":"AAIA,OAAO,EAKL,sBAAsB,EAEtB,aAAa,EACd,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAS/B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AA6C/D;;GAEG;AACH,eAAO,MAAM,MAAM,OAAQ,MAAM,QAAQ,IAAI,CAAC,eAM7C,CAAC;AAIF,KAAK,OAAO,GAAG,cAAc,CAAC,aAAa,CAAC,GAAG;IAC7C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,UAAU,CAChC,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,sBAAsB,CAAC,aAAa,CAAC,CAAC,EAC3D,EACE,WAAW,EACX,OAAc,EACd,MAAM,EACN,gBAA0C,EAC1C,gBAA0C,EAC1C,kBAA8C,GAC/C,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsTX"} \ No newline at end of file diff --git a/packages/expo-router/build/fork/useLinking.js b/packages/expo-router/build/fork/useLinking.js index 9da2db68aecd1..5eeb228a89eaa 100644 --- a/packages/expo-router/build/fork/useLinking.js +++ b/packages/expo-router/build/fork/useLinking.js @@ -1,8 +1,331 @@ "use strict"; +/* eslint-disable */ +// Forked from react-navigation to add basePath functionality to web. +// https://github.com/react-navigation/react-navigation/blob/6.x/packages/native/src/useLinking.tsx +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); -const useLinking_1 = __importDefault(require("@react-navigation/native/lib/module/useLinking")); -exports.default = useLinking_1.default; +exports.series = void 0; +const core_1 = require("@react-navigation/core"); +const fast_deep_equal_1 = __importDefault(require("fast-deep-equal")); +const React = __importStar(require("react")); +/* Start of fork. Source: https://github.com/react-navigation/react-navigation/blob/13d4aa270b301faf07960b4cd861ffc91e9b2c46/packages/native/src/useLinking.tsx#L13 */ +// createMemoryHistory is a self-contained module with no side effects any only depends on `nanoid` and `tiny-warning` +const createMemoryHistory_1 = __importDefault(require("@react-navigation/native/lib/commonjs/createMemoryHistory")); +// This was removed as we don't use ServerContext +// import ServerContext from './ServerContext'; +const serverLocationContext_1 = require("../global-state/serverLocationContext"); +const getPathFromState_1 = require("./getPathFromState"); +/** + * Find the matching navigation state that changed between 2 navigation states + * e.g.: a -> b -> c -> d and a -> b -> c -> e -> f, if history in b changed, b is the matching state + */ +const findMatchingState = (a, b) => { + if (a === undefined || b === undefined || a.key !== b.key) { + return [undefined, undefined]; + } + // Tab and drawer will have `history` property, but stack will have history in `routes` + const aHistoryLength = a.history ? a.history.length : a.routes.length; + const bHistoryLength = b.history ? b.history.length : b.routes.length; + const aRoute = a.routes[a.index]; + const bRoute = b.routes[b.index]; + const aChildState = aRoute.state; + const bChildState = bRoute.state; + // Stop here if this is the state object that changed: + // - history length is different + // - focused routes are different + // - one of them doesn't have child state + // - child state keys are different + if (aHistoryLength !== bHistoryLength || + aRoute.key !== bRoute.key || + aChildState === undefined || + bChildState === undefined || + aChildState.key !== bChildState.key) { + return [a, b]; + } + return findMatchingState(aChildState, bChildState); +}; +/** + * Run async function in series as it's called. + */ +const series = (cb) => { + let queue = Promise.resolve(); + const callback = () => { + queue = queue.then(cb); + }; + return callback; +}; +exports.series = series; +let linkingHandlers = []; +function useLinking(ref, { independent, enabled = true, config, getStateFromPath = core_1.getStateFromPath, getPathFromState = core_1.getPathFromState, getActionFromState = core_1.getActionFromState, }) { + React.useEffect(() => { + if (process.env.NODE_ENV === 'production') { + return undefined; + } + if (independent) { + return undefined; + } + if (enabled !== false && linkingHandlers.length) { + console.error([ + 'Looks like you have configured linking in multiple places. This is likely an error since deep links should only be handled in one place to avoid conflicts. Make sure that:', + "- You don't have multiple NavigationContainers in the app each with 'linking' enabled", + '- Only a single instance of the root component is rendered', + ] + .join('\n') + .trim()); + } + const handler = Symbol(); + if (enabled !== false) { + linkingHandlers.push(handler); + } + return () => { + const index = linkingHandlers.indexOf(handler); + if (index > -1) { + linkingHandlers.splice(index, 1); + } + }; + }, [enabled, independent]); + const [history] = React.useState(createMemoryHistory_1.default); + // We store these options in ref to avoid re-creating getInitialState and re-subscribing listeners + // This lets user avoid wrapping the items in `React.useCallback` or `React.useMemo` + // Not re-creating `getInitialState` is important coz it makes it easier for the user to use in an effect + const enabledRef = React.useRef(enabled); + const configRef = React.useRef(config); + const getStateFromPathRef = React.useRef(getStateFromPath); + const getPathFromStateRef = React.useRef(getPathFromState); + const getActionFromStateRef = React.useRef(getActionFromState); + React.useEffect(() => { + enabledRef.current = enabled; + configRef.current = config; + getStateFromPathRef.current = getStateFromPath; + getPathFromStateRef.current = getPathFromState; + getActionFromStateRef.current = getActionFromState; + }); + /* Start of fork. Source: https://github.com/react-navigation/react-navigation/blob/13d4aa270b301faf07960b4cd861ffc91e9b2c46/packages/native/src/useLinking.tsx#L142 */ + // ServerContext is used inside ServerContainer to set the location during SSR: https://github.com/react-navigation/react-navigation/blob/13d4aa270b301faf07960b4cd861ffc91e9b2c46/packages/native/src/ServerContainer.tsx#L50-L54 + // Expo Router uses the `initialLocation` prop to set the initial location during SSR: + const location = React.useContext(serverLocationContext_1.ServerLocationContext); + const server = { location }; + /* End of fork */ + const getInitialState = React.useCallback(() => { + let value; + if (enabledRef.current) { + const location = server?.location ?? (typeof window !== 'undefined' ? window.location : undefined); + const path = location ? location.pathname + location.search : undefined; + if (path) { + value = getStateFromPathRef.current(path, configRef.current); + } + } + const thenable = { + then(onfulfilled) { + return Promise.resolve(onfulfilled ? onfulfilled(value) : value); + }, + catch() { + return thenable; + }, + }; + return thenable; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const previousIndexRef = React.useRef(undefined); + const previousStateRef = React.useRef(undefined); + const pendingPopStatePathRef = React.useRef(undefined); + React.useEffect(() => { + previousIndexRef.current = history.index; + return history.listen(() => { + const navigation = ref.current; + if (!navigation || !enabled) { + return; + } + const { location } = window; + const path = location.pathname + location.search; + const index = history.index; + const previousIndex = previousIndexRef.current ?? 0; + previousIndexRef.current = index; + pendingPopStatePathRef.current = path; + // When browser back/forward is clicked, we first need to check if state object for this index exists + // If it does we'll reset to that state object + // Otherwise, we'll handle it like a regular deep link + const record = history.get(index); + if (record?.path === path && record?.state) { + navigation.resetRoot(record.state); + return; + } + const state = getStateFromPathRef.current(path, configRef.current); + // We should only dispatch an action when going forward + // Otherwise the action will likely add items to history, which would mess things up + if (state) { + // Make sure that the routes in the state exist in the root navigator + // Otherwise there's an error in the linking configuration + const rootState = navigation.getRootState(); + if (state.routes.some((r) => !rootState?.routeNames.includes(r.name))) { + console.warn("The navigation state parsed from the URL contains routes not present in the root navigator. This usually means that the linking configuration doesn't match the navigation structure. See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration."); + return; + } + if (index > previousIndex) { + const action = getActionFromStateRef.current(state, configRef.current); + if (action !== undefined) { + try { + navigation.dispatch(action); + } + catch (e) { + // Ignore any errors from deep linking. + // This could happen in case of malformed links, navigation object not being initialized etc. + console.warn(`An error occurred when trying to handle the link '${path}': ${typeof e === 'object' && e != null && 'message' in e ? e.message : e}`); + } + } + else { + navigation.resetRoot(state); + } + } + else { + navigation.resetRoot(state); + } + } + else { + // if current path didn't return any state, we should revert to initial state + navigation.resetRoot(state); + } + }); + }, [enabled, history, ref]); + React.useEffect(() => { + if (!enabled) { + return; + } + const getPathForRoute = (route, state) => { + // If the `route` object contains a `path`, use that path as long as `route.name` and `params` still match + // This makes sure that we preserve the original URL for wildcard routes + if (route?.path) { + const stateForPath = getStateFromPathRef.current(route.path, configRef.current); + if (stateForPath) { + const focusedRoute = (0, core_1.findFocusedRoute)(stateForPath); + if (focusedRoute && + focusedRoute.name === route.name && + (0, fast_deep_equal_1.default)(focusedRoute.params, route.params)) { + /* Start of fork. Source: https://github.com/react-navigation/react-navigation/blob/13d4aa270b301faf07960b4cd861ffc91e9b2c46/packages/native/src/useLinking.tsx#L280 */ + return (0, getPathFromState_1.appendBaseUrl)(route.path); + /* End of fork */ + } + } + } + return getPathFromStateRef.current(state, configRef.current); + }; + if (ref.current) { + // We need to record the current metadata on the first render if they aren't set + // This will allow the initial state to be in the history entry + const state = ref.current.getRootState(); + if (state) { + const route = (0, core_1.findFocusedRoute)(state); + const path = getPathForRoute(route, state); + if (previousStateRef.current === undefined) { + previousStateRef.current = state; + } + history.replace({ path, state }); + } + } + const onStateChange = async () => { + const navigation = ref.current; + if (!navigation || !enabled) { + return; + } + const previousState = previousStateRef.current; + const state = navigation.getRootState(); + // root state may not available, for example when root navigators switch inside the container + if (!state) { + return; + } + const pendingPath = pendingPopStatePathRef.current; + const route = (0, core_1.findFocusedRoute)(state); + const path = getPathForRoute(route, state); + previousStateRef.current = state; + pendingPopStatePathRef.current = undefined; + // To detect the kind of state change, we need to: + // - Find the common focused navigation state in previous and current state + // - If only the route keys changed, compare history/routes.length to check if we go back/forward/replace + // - If no common focused navigation state found, it's a replace + const [previousFocusedState, focusedState] = findMatchingState(previousState, state); + if (previousFocusedState && + focusedState && + // We should only handle push/pop if path changed from what was in last `popstate` + // Otherwise it's likely a change triggered by `popstate` + path !== pendingPath) { + const historyDelta = (focusedState.history ? focusedState.history.length : focusedState.routes.length) - + (previousFocusedState.history + ? previousFocusedState.history.length + : previousFocusedState.routes.length); + if (historyDelta > 0) { + // If history length is increased, we should pushState + // Note that path might not actually change here, for example, drawer open should pushState + history.push({ path, state }); + } + else if (historyDelta < 0) { + // If history length is decreased, i.e. entries were removed, we want to go back + const nextIndex = history.backIndex({ path }); + const currentIndex = history.index; + try { + if (nextIndex !== -1 && + nextIndex < currentIndex && + // We should only go back if the entry exists and it's less than current index + history.get(nextIndex - currentIndex)) { + // An existing entry for this path exists and it's less than current index, go back to that + await history.go(nextIndex - currentIndex); + } + else { + // We couldn't find an existing entry to go back to, so we'll go back by the delta + // This won't be correct if multiple routes were pushed in one go before + // Usually this shouldn't happen and this is a fallback for that + await history.go(historyDelta); + } + // Store the updated state as well as fix the path if incorrect + history.replace({ path, state }); + } + catch (e) { + // The navigation was interrupted + } + } + else { + // If history length is unchanged, we want to replaceState + history.replace({ path, state }); + } + } + else { + // If no common navigation state was found, assume it's a replace + // This would happen if the user did a reset/conditionally changed navigators + history.replace({ path, state }); + } + }; + // We debounce onStateChange coz we don't want multiple state changes to be handled at one time + // This could happen since `history.go(n)` is asynchronous + // If `pushState` or `replaceState` were called before `history.go(n)` completes, it'll mess stuff up + return ref.current?.addListener('state', (0, exports.series)(onStateChange)); + }, [enabled, history, ref]); + return { + getInitialState, + }; +} +exports.default = useLinking; //# sourceMappingURL=useLinking.js.map \ No newline at end of file diff --git a/packages/expo-router/build/fork/useLinking.js.map b/packages/expo-router/build/fork/useLinking.js.map index 48404604f3f42..33a14a16239a3 100644 --- a/packages/expo-router/build/fork/useLinking.js.map +++ b/packages/expo-router/build/fork/useLinking.js.map @@ -1 +1 @@ -{"version":3,"file":"useLinking.js","sourceRoot":"","sources":["../../src/fork/useLinking.ts"],"names":[],"mappings":";;;;;AAAA,gGAAwE;AAExE,kBAAe,oBAA0D,CAAC","sourcesContent":["import useLinking from '@react-navigation/native/lib/module/useLinking';\n\nexport default useLinking as typeof import('./useLinking.native').default;\n"]} \ No newline at end of file +{"version":3,"file":"useLinking.js","sourceRoot":"","sources":["../../src/fork/useLinking.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable */\n// Forked from react-navigation to add basePath functionality to web.\n// https://github.com/react-navigation/react-navigation/blob/6.x/packages/native/src/useLinking.tsx\n\nimport {\n findFocusedRoute,\n getActionFromState as getActionFromStateDefault,\n getPathFromState as getPathFromStateDefault,\n getStateFromPath as getStateFromPathDefault,\n NavigationContainerRef,\n NavigationState,\n ParamListBase,\n} from '@react-navigation/core';\nimport isEqual from 'fast-deep-equal';\nimport * as React from 'react';\n\n/* Start of fork. Source: https://github.com/react-navigation/react-navigation/blob/13d4aa270b301faf07960b4cd861ffc91e9b2c46/packages/native/src/useLinking.tsx#L13 */\n// createMemoryHistory is a self-contained module with no side effects any only depends on `nanoid` and `tiny-warning`\nimport createMemoryHistory from '@react-navigation/native/lib/commonjs/createMemoryHistory';\n// This was removed as we don't use ServerContext\n// import ServerContext from './ServerContext';\nimport { ServerLocationContext } from '../global-state/serverLocationContext';\n/* End of fork */\nimport type { LinkingOptions } from '@react-navigation/native';\nimport { appendBaseUrl } from './getPathFromState';\n\ntype ResultState = ReturnType;\n\n/**\n * Find the matching navigation state that changed between 2 navigation states\n * e.g.: a -> b -> c -> d and a -> b -> c -> e -> f, if history in b changed, b is the matching state\n */\nconst findMatchingState = (\n a: T | undefined,\n b: T | undefined\n): [T | undefined, T | undefined] => {\n if (a === undefined || b === undefined || a.key !== b.key) {\n return [undefined, undefined];\n }\n\n // Tab and drawer will have `history` property, but stack will have history in `routes`\n const aHistoryLength = a.history ? a.history.length : a.routes.length;\n const bHistoryLength = b.history ? b.history.length : b.routes.length;\n\n const aRoute = a.routes[a.index];\n const bRoute = b.routes[b.index];\n\n const aChildState = aRoute.state as T | undefined;\n const bChildState = bRoute.state as T | undefined;\n\n // Stop here if this is the state object that changed:\n // - history length is different\n // - focused routes are different\n // - one of them doesn't have child state\n // - child state keys are different\n if (\n aHistoryLength !== bHistoryLength ||\n aRoute.key !== bRoute.key ||\n aChildState === undefined ||\n bChildState === undefined ||\n aChildState.key !== bChildState.key\n ) {\n return [a, b];\n }\n\n return findMatchingState(aChildState, bChildState);\n};\n\n/**\n * Run async function in series as it's called.\n */\nexport const series = (cb: () => Promise) => {\n let queue = Promise.resolve();\n const callback = () => {\n queue = queue.then(cb);\n };\n return callback;\n};\n\nlet linkingHandlers: Symbol[] = [];\n\ntype Options = LinkingOptions & {\n independent?: boolean;\n};\n\nexport default function useLinking(\n ref: React.RefObject>,\n {\n independent,\n enabled = true,\n config,\n getStateFromPath = getStateFromPathDefault,\n getPathFromState = getPathFromStateDefault,\n getActionFromState = getActionFromStateDefault,\n }: Options\n) {\n React.useEffect(() => {\n if (process.env.NODE_ENV === 'production') {\n return undefined;\n }\n\n if (independent) {\n return undefined;\n }\n\n if (enabled !== false && linkingHandlers.length) {\n console.error(\n [\n 'Looks like you have configured linking in multiple places. This is likely an error since deep links should only be handled in one place to avoid conflicts. Make sure that:',\n \"- You don't have multiple NavigationContainers in the app each with 'linking' enabled\",\n '- Only a single instance of the root component is rendered',\n ]\n .join('\\n')\n .trim()\n );\n }\n\n const handler = Symbol();\n\n if (enabled !== false) {\n linkingHandlers.push(handler);\n }\n\n return () => {\n const index = linkingHandlers.indexOf(handler);\n\n if (index > -1) {\n linkingHandlers.splice(index, 1);\n }\n };\n }, [enabled, independent]);\n\n const [history] = React.useState(createMemoryHistory);\n\n // We store these options in ref to avoid re-creating getInitialState and re-subscribing listeners\n // This lets user avoid wrapping the items in `React.useCallback` or `React.useMemo`\n // Not re-creating `getInitialState` is important coz it makes it easier for the user to use in an effect\n const enabledRef = React.useRef(enabled);\n const configRef = React.useRef(config);\n const getStateFromPathRef = React.useRef(getStateFromPath);\n const getPathFromStateRef = React.useRef(getPathFromState);\n const getActionFromStateRef = React.useRef(getActionFromState);\n\n React.useEffect(() => {\n enabledRef.current = enabled;\n configRef.current = config;\n getStateFromPathRef.current = getStateFromPath;\n getPathFromStateRef.current = getPathFromState;\n getActionFromStateRef.current = getActionFromState;\n });\n\n /* Start of fork. Source: https://github.com/react-navigation/react-navigation/blob/13d4aa270b301faf07960b4cd861ffc91e9b2c46/packages/native/src/useLinking.tsx#L142 */\n // ServerContext is used inside ServerContainer to set the location during SSR: https://github.com/react-navigation/react-navigation/blob/13d4aa270b301faf07960b4cd861ffc91e9b2c46/packages/native/src/ServerContainer.tsx#L50-L54\n // Expo Router uses the `initialLocation` prop to set the initial location during SSR:\n const location = React.useContext(ServerLocationContext);\n const server = { location };\n /* End of fork */\n\n const getInitialState = React.useCallback(() => {\n let value: ResultState | undefined;\n\n if (enabledRef.current) {\n const location =\n server?.location ?? (typeof window !== 'undefined' ? window.location : undefined);\n\n const path = location ? location.pathname + location.search : undefined;\n\n if (path) {\n value = getStateFromPathRef.current(path, configRef.current);\n }\n }\n\n const thenable = {\n then(onfulfilled?: (state: ResultState | undefined) => void) {\n return Promise.resolve(onfulfilled ? onfulfilled(value) : value);\n },\n catch() {\n return thenable;\n },\n };\n\n return thenable as PromiseLike;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const previousIndexRef = React.useRef(undefined);\n const previousStateRef = React.useRef(undefined);\n const pendingPopStatePathRef = React.useRef(undefined);\n\n React.useEffect(() => {\n previousIndexRef.current = history.index;\n\n return history.listen(() => {\n const navigation = ref.current;\n\n if (!navigation || !enabled) {\n return;\n }\n\n const { location } = window;\n\n const path = location.pathname + location.search;\n const index = history.index;\n\n const previousIndex = previousIndexRef.current ?? 0;\n\n previousIndexRef.current = index;\n pendingPopStatePathRef.current = path;\n\n // When browser back/forward is clicked, we first need to check if state object for this index exists\n // If it does we'll reset to that state object\n // Otherwise, we'll handle it like a regular deep link\n const record = history.get(index);\n\n if (record?.path === path && record?.state) {\n navigation.resetRoot(record.state);\n return;\n }\n\n const state = getStateFromPathRef.current(path, configRef.current);\n\n // We should only dispatch an action when going forward\n // Otherwise the action will likely add items to history, which would mess things up\n if (state) {\n // Make sure that the routes in the state exist in the root navigator\n // Otherwise there's an error in the linking configuration\n const rootState = navigation.getRootState();\n\n if (state.routes.some((r) => !rootState?.routeNames.includes(r.name))) {\n console.warn(\n \"The navigation state parsed from the URL contains routes not present in the root navigator. This usually means that the linking configuration doesn't match the navigation structure. See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration.\"\n );\n return;\n }\n\n if (index > previousIndex) {\n const action = getActionFromStateRef.current(state, configRef.current);\n\n if (action !== undefined) {\n try {\n navigation.dispatch(action);\n } catch (e) {\n // Ignore any errors from deep linking.\n // This could happen in case of malformed links, navigation object not being initialized etc.\n console.warn(\n `An error occurred when trying to handle the link '${path}': ${\n typeof e === 'object' && e != null && 'message' in e ? e.message : e\n }`\n );\n }\n } else {\n navigation.resetRoot(state);\n }\n } else {\n navigation.resetRoot(state);\n }\n } else {\n // if current path didn't return any state, we should revert to initial state\n navigation.resetRoot(state);\n }\n });\n }, [enabled, history, ref]);\n\n React.useEffect(() => {\n if (!enabled) {\n return;\n }\n\n const getPathForRoute = (\n route: ReturnType,\n state: NavigationState\n ): string => {\n // If the `route` object contains a `path`, use that path as long as `route.name` and `params` still match\n // This makes sure that we preserve the original URL for wildcard routes\n if (route?.path) {\n const stateForPath = getStateFromPathRef.current(route.path, configRef.current);\n\n if (stateForPath) {\n const focusedRoute = findFocusedRoute(stateForPath);\n\n if (\n focusedRoute &&\n focusedRoute.name === route.name &&\n isEqual(focusedRoute.params, route.params)\n ) {\n /* Start of fork. Source: https://github.com/react-navigation/react-navigation/blob/13d4aa270b301faf07960b4cd861ffc91e9b2c46/packages/native/src/useLinking.tsx#L280 */\n return appendBaseUrl(route.path);\n /* End of fork */\n }\n }\n }\n\n return getPathFromStateRef.current(state, configRef.current);\n };\n\n if (ref.current) {\n // We need to record the current metadata on the first render if they aren't set\n // This will allow the initial state to be in the history entry\n const state = ref.current.getRootState();\n\n if (state) {\n const route = findFocusedRoute(state);\n const path = getPathForRoute(route, state);\n\n if (previousStateRef.current === undefined) {\n previousStateRef.current = state;\n }\n\n history.replace({ path, state });\n }\n }\n\n const onStateChange = async () => {\n const navigation = ref.current;\n\n if (!navigation || !enabled) {\n return;\n }\n\n const previousState = previousStateRef.current;\n const state = navigation.getRootState();\n\n // root state may not available, for example when root navigators switch inside the container\n if (!state) {\n return;\n }\n\n const pendingPath = pendingPopStatePathRef.current;\n const route = findFocusedRoute(state);\n const path = getPathForRoute(route, state);\n\n previousStateRef.current = state;\n pendingPopStatePathRef.current = undefined;\n\n // To detect the kind of state change, we need to:\n // - Find the common focused navigation state in previous and current state\n // - If only the route keys changed, compare history/routes.length to check if we go back/forward/replace\n // - If no common focused navigation state found, it's a replace\n const [previousFocusedState, focusedState] = findMatchingState(previousState, state);\n\n if (\n previousFocusedState &&\n focusedState &&\n // We should only handle push/pop if path changed from what was in last `popstate`\n // Otherwise it's likely a change triggered by `popstate`\n path !== pendingPath\n ) {\n const historyDelta =\n (focusedState.history ? focusedState.history.length : focusedState.routes.length) -\n (previousFocusedState.history\n ? previousFocusedState.history.length\n : previousFocusedState.routes.length);\n\n if (historyDelta > 0) {\n // If history length is increased, we should pushState\n // Note that path might not actually change here, for example, drawer open should pushState\n history.push({ path, state });\n } else if (historyDelta < 0) {\n // If history length is decreased, i.e. entries were removed, we want to go back\n\n const nextIndex = history.backIndex({ path });\n const currentIndex = history.index;\n\n try {\n if (\n nextIndex !== -1 &&\n nextIndex < currentIndex &&\n // We should only go back if the entry exists and it's less than current index\n history.get(nextIndex - currentIndex)\n ) {\n // An existing entry for this path exists and it's less than current index, go back to that\n await history.go(nextIndex - currentIndex);\n } else {\n // We couldn't find an existing entry to go back to, so we'll go back by the delta\n // This won't be correct if multiple routes were pushed in one go before\n // Usually this shouldn't happen and this is a fallback for that\n await history.go(historyDelta);\n }\n\n // Store the updated state as well as fix the path if incorrect\n history.replace({ path, state });\n } catch (e) {\n // The navigation was interrupted\n }\n } else {\n // If history length is unchanged, we want to replaceState\n history.replace({ path, state });\n }\n } else {\n // If no common navigation state was found, assume it's a replace\n // This would happen if the user did a reset/conditionally changed navigators\n history.replace({ path, state });\n }\n };\n\n // We debounce onStateChange coz we don't want multiple state changes to be handled at one time\n // This could happen since `history.go(n)` is asynchronous\n // If `pushState` or `replaceState` were called before `history.go(n)` completes, it'll mess stuff up\n return ref.current?.addListener('state', series(onStateChange));\n }, [enabled, history, ref]);\n\n return {\n getInitialState,\n };\n}\n"]} \ No newline at end of file diff --git a/packages/expo-router/build/global-state/serverLocationContext.d.ts b/packages/expo-router/build/global-state/serverLocationContext.d.ts new file mode 100644 index 0000000000000..2fd0e45b387b2 --- /dev/null +++ b/packages/expo-router/build/global-state/serverLocationContext.d.ts @@ -0,0 +1,3 @@ +/// +export declare const ServerLocationContext: import("react").Context; +//# sourceMappingURL=serverLocationContext.d.ts.map \ No newline at end of file diff --git a/packages/expo-router/build/global-state/serverLocationContext.d.ts.map b/packages/expo-router/build/global-state/serverLocationContext.d.ts.map new file mode 100644 index 0000000000000..d919aeb0ae568 --- /dev/null +++ b/packages/expo-router/build/global-state/serverLocationContext.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"serverLocationContext.d.ts","sourceRoot":"","sources":["../../src/global-state/serverLocationContext.ts"],"names":[],"mappings":";AAEA,eAAO,MAAM,qBAAqB,0CAA4C,CAAC"} \ No newline at end of file diff --git a/packages/expo-router/build/global-state/serverLocationContext.js b/packages/expo-router/build/global-state/serverLocationContext.js new file mode 100644 index 0000000000000..fbbb1899e9b36 --- /dev/null +++ b/packages/expo-router/build/global-state/serverLocationContext.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ServerLocationContext = void 0; +const react_1 = require("react"); +exports.ServerLocationContext = (0, react_1.createContext)(undefined); +//# sourceMappingURL=serverLocationContext.js.map \ No newline at end of file diff --git a/packages/expo-router/build/global-state/serverLocationContext.js.map b/packages/expo-router/build/global-state/serverLocationContext.js.map new file mode 100644 index 0000000000000..43f59a4fbb3e1 --- /dev/null +++ b/packages/expo-router/build/global-state/serverLocationContext.js.map @@ -0,0 +1 @@ +{"version":3,"file":"serverLocationContext.js","sourceRoot":"","sources":["../../src/global-state/serverLocationContext.ts"],"names":[],"mappings":";;;AAAA,iCAAsC;AAEzB,QAAA,qBAAqB,GAAG,IAAA,qBAAa,EAAkB,SAAS,CAAC,CAAC","sourcesContent":["import { createContext } from 'react';\n\nexport const ServerLocationContext = createContext(undefined);\n"]} \ No newline at end of file diff --git a/packages/expo-router/src/ExpoRoot.tsx b/packages/expo-router/src/ExpoRoot.tsx index 2721401e9fa23..b4d48ec4be8e0 100644 --- a/packages/expo-router/src/ExpoRoot.tsx +++ b/packages/expo-router/src/ExpoRoot.tsx @@ -9,6 +9,7 @@ import { SafeAreaProvider } from 'react-native-safe-area-context'; import UpstreamNavigationContainer from './fork/NavigationContainer'; import { useInitializeExpoRouter } from './global-state/router-store'; +import { ServerLocationContext } from './global-state/serverLocationContext'; import { RequireContext } from './types'; import { SplashScreen } from './views/Splash'; @@ -93,9 +94,11 @@ function ContextNavigator({ documentTitle={{ enabled: false, }}> - - - + + + + + ); } diff --git a/packages/expo-router/src/fork/useLinking.ts b/packages/expo-router/src/fork/useLinking.ts index 6ed930e6fa8a2..8991e80bd9840 100644 --- a/packages/expo-router/src/fork/useLinking.ts +++ b/packages/expo-router/src/fork/useLinking.ts @@ -1,3 +1,405 @@ -import useLinking from '@react-navigation/native/lib/module/useLinking'; +/* eslint-disable */ +// Forked from react-navigation to add basePath functionality to web. +// https://github.com/react-navigation/react-navigation/blob/6.x/packages/native/src/useLinking.tsx -export default useLinking as typeof import('./useLinking.native').default; +import { + findFocusedRoute, + getActionFromState as getActionFromStateDefault, + getPathFromState as getPathFromStateDefault, + getStateFromPath as getStateFromPathDefault, + NavigationContainerRef, + NavigationState, + ParamListBase, +} from '@react-navigation/core'; +import isEqual from 'fast-deep-equal'; +import * as React from 'react'; + +/* Start of fork. Source: https://github.com/react-navigation/react-navigation/blob/13d4aa270b301faf07960b4cd861ffc91e9b2c46/packages/native/src/useLinking.tsx#L13 */ +// createMemoryHistory is a self-contained module with no side effects any only depends on `nanoid` and `tiny-warning` +import createMemoryHistory from '@react-navigation/native/lib/commonjs/createMemoryHistory'; +// This was removed as we don't use ServerContext +// import ServerContext from './ServerContext'; +import { ServerLocationContext } from '../global-state/serverLocationContext'; +/* End of fork */ +import type { LinkingOptions } from '@react-navigation/native'; +import { appendBaseUrl } from './getPathFromState'; + +type ResultState = ReturnType; + +/** + * Find the matching navigation state that changed between 2 navigation states + * e.g.: a -> b -> c -> d and a -> b -> c -> e -> f, if history in b changed, b is the matching state + */ +const findMatchingState = ( + a: T | undefined, + b: T | undefined +): [T | undefined, T | undefined] => { + if (a === undefined || b === undefined || a.key !== b.key) { + return [undefined, undefined]; + } + + // Tab and drawer will have `history` property, but stack will have history in `routes` + const aHistoryLength = a.history ? a.history.length : a.routes.length; + const bHistoryLength = b.history ? b.history.length : b.routes.length; + + const aRoute = a.routes[a.index]; + const bRoute = b.routes[b.index]; + + const aChildState = aRoute.state as T | undefined; + const bChildState = bRoute.state as T | undefined; + + // Stop here if this is the state object that changed: + // - history length is different + // - focused routes are different + // - one of them doesn't have child state + // - child state keys are different + if ( + aHistoryLength !== bHistoryLength || + aRoute.key !== bRoute.key || + aChildState === undefined || + bChildState === undefined || + aChildState.key !== bChildState.key + ) { + return [a, b]; + } + + return findMatchingState(aChildState, bChildState); +}; + +/** + * Run async function in series as it's called. + */ +export const series = (cb: () => Promise) => { + let queue = Promise.resolve(); + const callback = () => { + queue = queue.then(cb); + }; + return callback; +}; + +let linkingHandlers: Symbol[] = []; + +type Options = LinkingOptions & { + independent?: boolean; +}; + +export default function useLinking( + ref: React.RefObject>, + { + independent, + enabled = true, + config, + getStateFromPath = getStateFromPathDefault, + getPathFromState = getPathFromStateDefault, + getActionFromState = getActionFromStateDefault, + }: Options +) { + React.useEffect(() => { + if (process.env.NODE_ENV === 'production') { + return undefined; + } + + if (independent) { + return undefined; + } + + if (enabled !== false && linkingHandlers.length) { + console.error( + [ + 'Looks like you have configured linking in multiple places. This is likely an error since deep links should only be handled in one place to avoid conflicts. Make sure that:', + "- You don't have multiple NavigationContainers in the app each with 'linking' enabled", + '- Only a single instance of the root component is rendered', + ] + .join('\n') + .trim() + ); + } + + const handler = Symbol(); + + if (enabled !== false) { + linkingHandlers.push(handler); + } + + return () => { + const index = linkingHandlers.indexOf(handler); + + if (index > -1) { + linkingHandlers.splice(index, 1); + } + }; + }, [enabled, independent]); + + const [history] = React.useState(createMemoryHistory); + + // We store these options in ref to avoid re-creating getInitialState and re-subscribing listeners + // This lets user avoid wrapping the items in `React.useCallback` or `React.useMemo` + // Not re-creating `getInitialState` is important coz it makes it easier for the user to use in an effect + const enabledRef = React.useRef(enabled); + const configRef = React.useRef(config); + const getStateFromPathRef = React.useRef(getStateFromPath); + const getPathFromStateRef = React.useRef(getPathFromState); + const getActionFromStateRef = React.useRef(getActionFromState); + + React.useEffect(() => { + enabledRef.current = enabled; + configRef.current = config; + getStateFromPathRef.current = getStateFromPath; + getPathFromStateRef.current = getPathFromState; + getActionFromStateRef.current = getActionFromState; + }); + + /* Start of fork. Source: https://github.com/react-navigation/react-navigation/blob/13d4aa270b301faf07960b4cd861ffc91e9b2c46/packages/native/src/useLinking.tsx#L142 */ + // ServerContext is used inside ServerContainer to set the location during SSR: https://github.com/react-navigation/react-navigation/blob/13d4aa270b301faf07960b4cd861ffc91e9b2c46/packages/native/src/ServerContainer.tsx#L50-L54 + // Expo Router uses the `initialLocation` prop to set the initial location during SSR: + const location = React.useContext(ServerLocationContext); + const server = { location }; + /* End of fork */ + + const getInitialState = React.useCallback(() => { + let value: ResultState | undefined; + + if (enabledRef.current) { + const location = + server?.location ?? (typeof window !== 'undefined' ? window.location : undefined); + + const path = location ? location.pathname + location.search : undefined; + + if (path) { + value = getStateFromPathRef.current(path, configRef.current); + } + } + + const thenable = { + then(onfulfilled?: (state: ResultState | undefined) => void) { + return Promise.resolve(onfulfilled ? onfulfilled(value) : value); + }, + catch() { + return thenable; + }, + }; + + return thenable as PromiseLike; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const previousIndexRef = React.useRef(undefined); + const previousStateRef = React.useRef(undefined); + const pendingPopStatePathRef = React.useRef(undefined); + + React.useEffect(() => { + previousIndexRef.current = history.index; + + return history.listen(() => { + const navigation = ref.current; + + if (!navigation || !enabled) { + return; + } + + const { location } = window; + + const path = location.pathname + location.search; + const index = history.index; + + const previousIndex = previousIndexRef.current ?? 0; + + previousIndexRef.current = index; + pendingPopStatePathRef.current = path; + + // When browser back/forward is clicked, we first need to check if state object for this index exists + // If it does we'll reset to that state object + // Otherwise, we'll handle it like a regular deep link + const record = history.get(index); + + if (record?.path === path && record?.state) { + navigation.resetRoot(record.state); + return; + } + + const state = getStateFromPathRef.current(path, configRef.current); + + // We should only dispatch an action when going forward + // Otherwise the action will likely add items to history, which would mess things up + if (state) { + // Make sure that the routes in the state exist in the root navigator + // Otherwise there's an error in the linking configuration + const rootState = navigation.getRootState(); + + if (state.routes.some((r) => !rootState?.routeNames.includes(r.name))) { + console.warn( + "The navigation state parsed from the URL contains routes not present in the root navigator. This usually means that the linking configuration doesn't match the navigation structure. See https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration." + ); + return; + } + + if (index > previousIndex) { + const action = getActionFromStateRef.current(state, configRef.current); + + if (action !== undefined) { + try { + navigation.dispatch(action); + } catch (e) { + // Ignore any errors from deep linking. + // This could happen in case of malformed links, navigation object not being initialized etc. + console.warn( + `An error occurred when trying to handle the link '${path}': ${ + typeof e === 'object' && e != null && 'message' in e ? e.message : e + }` + ); + } + } else { + navigation.resetRoot(state); + } + } else { + navigation.resetRoot(state); + } + } else { + // if current path didn't return any state, we should revert to initial state + navigation.resetRoot(state); + } + }); + }, [enabled, history, ref]); + + React.useEffect(() => { + if (!enabled) { + return; + } + + const getPathForRoute = ( + route: ReturnType, + state: NavigationState + ): string => { + // If the `route` object contains a `path`, use that path as long as `route.name` and `params` still match + // This makes sure that we preserve the original URL for wildcard routes + if (route?.path) { + const stateForPath = getStateFromPathRef.current(route.path, configRef.current); + + if (stateForPath) { + const focusedRoute = findFocusedRoute(stateForPath); + + if ( + focusedRoute && + focusedRoute.name === route.name && + isEqual(focusedRoute.params, route.params) + ) { + /* Start of fork. Source: https://github.com/react-navigation/react-navigation/blob/13d4aa270b301faf07960b4cd861ffc91e9b2c46/packages/native/src/useLinking.tsx#L280 */ + return appendBaseUrl(route.path); + /* End of fork */ + } + } + } + + return getPathFromStateRef.current(state, configRef.current); + }; + + if (ref.current) { + // We need to record the current metadata on the first render if they aren't set + // This will allow the initial state to be in the history entry + const state = ref.current.getRootState(); + + if (state) { + const route = findFocusedRoute(state); + const path = getPathForRoute(route, state); + + if (previousStateRef.current === undefined) { + previousStateRef.current = state; + } + + history.replace({ path, state }); + } + } + + const onStateChange = async () => { + const navigation = ref.current; + + if (!navigation || !enabled) { + return; + } + + const previousState = previousStateRef.current; + const state = navigation.getRootState(); + + // root state may not available, for example when root navigators switch inside the container + if (!state) { + return; + } + + const pendingPath = pendingPopStatePathRef.current; + const route = findFocusedRoute(state); + const path = getPathForRoute(route, state); + + previousStateRef.current = state; + pendingPopStatePathRef.current = undefined; + + // To detect the kind of state change, we need to: + // - Find the common focused navigation state in previous and current state + // - If only the route keys changed, compare history/routes.length to check if we go back/forward/replace + // - If no common focused navigation state found, it's a replace + const [previousFocusedState, focusedState] = findMatchingState(previousState, state); + + if ( + previousFocusedState && + focusedState && + // We should only handle push/pop if path changed from what was in last `popstate` + // Otherwise it's likely a change triggered by `popstate` + path !== pendingPath + ) { + const historyDelta = + (focusedState.history ? focusedState.history.length : focusedState.routes.length) - + (previousFocusedState.history + ? previousFocusedState.history.length + : previousFocusedState.routes.length); + + if (historyDelta > 0) { + // If history length is increased, we should pushState + // Note that path might not actually change here, for example, drawer open should pushState + history.push({ path, state }); + } else if (historyDelta < 0) { + // If history length is decreased, i.e. entries were removed, we want to go back + + const nextIndex = history.backIndex({ path }); + const currentIndex = history.index; + + try { + if ( + nextIndex !== -1 && + nextIndex < currentIndex && + // We should only go back if the entry exists and it's less than current index + history.get(nextIndex - currentIndex) + ) { + // An existing entry for this path exists and it's less than current index, go back to that + await history.go(nextIndex - currentIndex); + } else { + // We couldn't find an existing entry to go back to, so we'll go back by the delta + // This won't be correct if multiple routes were pushed in one go before + // Usually this shouldn't happen and this is a fallback for that + await history.go(historyDelta); + } + + // Store the updated state as well as fix the path if incorrect + history.replace({ path, state }); + } catch (e) { + // The navigation was interrupted + } + } else { + // If history length is unchanged, we want to replaceState + history.replace({ path, state }); + } + } else { + // If no common navigation state was found, assume it's a replace + // This would happen if the user did a reset/conditionally changed navigators + history.replace({ path, state }); + } + }; + + // We debounce onStateChange coz we don't want multiple state changes to be handled at one time + // This could happen since `history.go(n)` is asynchronous + // If `pushState` or `replaceState` were called before `history.go(n)` completes, it'll mess stuff up + return ref.current?.addListener('state', series(onStateChange)); + }, [enabled, history, ref]); + + return { + getInitialState, + }; +} diff --git a/packages/expo-router/src/global-state/serverLocationContext.ts b/packages/expo-router/src/global-state/serverLocationContext.ts new file mode 100644 index 0000000000000..c32320e462b23 --- /dev/null +++ b/packages/expo-router/src/global-state/serverLocationContext.ts @@ -0,0 +1,3 @@ +import { createContext } from 'react'; + +export const ServerLocationContext = createContext(undefined);