first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
import { DocumentTitleOptions, LinkingOptions, LocaleDirection, NavigationContainerProps, NavigationContainerRef } from '@react-navigation/native';
import React from 'react';
declare global {
var REACT_NAVIGATION_DEVTOOLS: WeakMap<NavigationContainerRef<any>, {
readonly linking: LinkingOptions<any>;
}>;
}
type Props<ParamList extends object> = NavigationContainerProps & {
direction?: LocaleDirection;
linking?: LinkingOptions<ParamList>;
fallback?: React.ReactNode;
documentTitle?: DocumentTitleOptions;
};
export declare const NavigationContainer: <RootParamList extends object = ReactNavigation.RootParamList>(props: Props<RootParamList> & {
ref?: React.Ref<NavigationContainerRef<RootParamList>>;
}) => React.ReactElement;
export {};
//# sourceMappingURL=NavigationContainer.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"NavigationContainer.d.ts","sourceRoot":"","sources":["../../src/fork/NavigationContainer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,oBAAoB,EAEpB,cAAc,EAEd,eAAe,EACf,wBAAwB,EACxB,sBAAsB,EASvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,MAAM,OAAO,CAAC;AAU1B,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,yBAAyB,EAAE,OAAO,CACpC,sBAAsB,CAAC,GAAG,CAAC,EAC3B;QAAE,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC,CAAA;KAAE,CAC1C,CAAC;CACH;AAID,KAAK,KAAK,CAAC,SAAS,SAAS,MAAM,IAAI,wBAAwB,GAAG;IAChE,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,OAAO,CAAC,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IACpC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,aAAa,CAAC,EAAE,oBAAoB,CAAC;CACtC,CAAC;AAwIF,eAAO,MAAM,mBAAmB,EAAiD,CAC/E,aAAa,SAAS,MAAM,GAAG,eAAe,CAAC,aAAa,EAE5D,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG;IAC5B,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,sBAAsB,CAAC,aAAa,CAAC,CAAC,CAAC;CACxD,KACE,KAAK,CAAC,YAAY,CAAC"}

View File

@@ -0,0 +1,107 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NavigationContainer = void 0;
const native_1 = require("@react-navigation/native");
const react_1 = __importDefault(require("react"));
const react_native_1 = require("react-native");
const use_latest_callback_1 = __importDefault(require("use-latest-callback"));
const useBackButton_1 = require("./useBackButton");
const useDocumentTitle_1 = require("./useDocumentTitle");
const useLinking_1 = require("./useLinking");
const useThenable_1 = require("./useThenable");
const imperative_api_1 = require("../imperative-api");
globalThis.REACT_NAVIGATION_DEVTOOLS = new WeakMap();
/**
* Container component which holds the navigation state designed for React Native apps.
* This should be rendered at the root wrapping the whole app.
*
* @param props.initialState Initial state object for the navigation tree. When deep link handling is enabled, this will override deep links when specified. Make sure that you don't specify an `initialState` when there's a deep link (`Linking.getInitialURL()`).
* @param props.onReady Callback which is called after the navigation tree mounts.
* @param props.onStateChange Callback which is called with the latest navigation state when it changes.
* @param props.onUnhandledAction Callback which is called when an action is not handled.
* @param props.direction Text direction of the components. Defaults to `'ltr'`.
* @param props.theme Theme object for the UI elements.
* @param props.linking Options for deep linking. Deep link handling is enabled when this prop is provided, unless `linking.enabled` is `false`.
* @param props.fallback Fallback component to render until we have finished getting initial state when linking is enabled. Defaults to `null`.
* @param props.documentTitle Options to configure the document title on Web. Updating document title is handled by default unless `documentTitle.enabled` is `false`.
* @param props.children Child elements to render the content.
* @param props.ref Ref object which refers to the navigation object containing helper methods.
*/
function NavigationContainerInner({ direction = react_native_1.I18nManager.getConstants().isRTL ? 'rtl' : 'ltr', theme = native_1.DefaultTheme, linking, fallback = null, documentTitle, onReady, onStateChange, ...rest }, ref) {
const isLinkingEnabled = linking ? linking.enabled !== false : false;
if (linking?.config) {
(0, native_1.validatePathConfig)(linking.config);
}
const refContainer = react_1.default.useRef(null);
(0, useBackButton_1.useBackButton)(refContainer);
(0, useDocumentTitle_1.useDocumentTitle)(refContainer, documentTitle);
(0, imperative_api_1.useImperativeApiEmitter)(refContainer);
const [lastUnhandledLink, setLastUnhandledLink] = react_1.default.useState();
const { getInitialState } = (0, useLinking_1.useLinking)(refContainer, {
enabled: isLinkingEnabled,
prefixes: [],
...linking,
}, setLastUnhandledLink);
const linkingContext = react_1.default.useMemo(() => ({ options: linking }), [linking]);
const unhandledLinkingContext = react_1.default.useMemo(() => ({ lastUnhandledLink, setLastUnhandledLink }), [lastUnhandledLink, setLastUnhandledLink]);
const onReadyForLinkingHandling = (0, use_latest_callback_1.default)(() => {
// If the screen path matches lastUnhandledLink, we do not track it
const path = refContainer.current?.getCurrentRoute()?.path;
setLastUnhandledLink((previousLastUnhandledLink) => {
if (previousLastUnhandledLink === path) {
return undefined;
}
return previousLastUnhandledLink;
});
onReady?.();
});
const onStateChangeForLinkingHandling = (0, use_latest_callback_1.default)((state) => {
// If the screen path matches lastUnhandledLink, we do not track it
const path = refContainer.current?.getCurrentRoute()?.path;
setLastUnhandledLink((previousLastUnhandledLink) => {
if (previousLastUnhandledLink === path) {
return undefined;
}
return previousLastUnhandledLink;
});
onStateChange?.(state);
});
// Add additional linking related info to the ref
// This will be used by the devtools
react_1.default.useEffect(() => {
if (refContainer.current) {
REACT_NAVIGATION_DEVTOOLS.set(refContainer.current, {
get linking() {
return {
...linking,
enabled: isLinkingEnabled,
prefixes: linking?.prefixes ?? [],
getStateFromPath: linking?.getStateFromPath ?? native_1.getStateFromPath,
getPathFromState: linking?.getPathFromState ?? native_1.getPathFromState,
getActionFromState: linking?.getActionFromState ?? native_1.getActionFromState,
};
},
});
}
});
const [isResolved, initialState] = (0, useThenable_1.useThenable)(getInitialState);
react_1.default.useImperativeHandle(ref, () => refContainer.current);
const isLinkingReady = rest.initialState != null || !isLinkingEnabled || isResolved;
if (!isLinkingReady) {
// This is temporary until we have Suspense for data-fetching
// Then the fallback will be handled by a parent `Suspense` component
return <native_1.ThemeProvider value={theme}>{fallback}</native_1.ThemeProvider>;
}
return (<native_1.LocaleDirContext.Provider value={direction}>
<native_1.UNSTABLE_UnhandledLinkingContext.Provider value={unhandledLinkingContext}>
<native_1.LinkingContext.Provider value={linkingContext}>
<native_1.BaseNavigationContainer {...rest} theme={theme} onReady={onReadyForLinkingHandling} onStateChange={onStateChangeForLinkingHandling} initialState={rest.initialState == null ? initialState : rest.initialState} ref={refContainer}/>
</native_1.LinkingContext.Provider>
</native_1.UNSTABLE_UnhandledLinkingContext.Provider>
</native_1.LocaleDirContext.Provider>);
}
exports.NavigationContainer = react_1.default.forwardRef(NavigationContainerInner);
//# sourceMappingURL=NavigationContainer.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,25 @@
import type { NavigationState } from '@react-navigation/core';
type HistoryRecord = {
id: string;
state: NavigationState;
path: string;
};
export declare function createMemoryHistory(): {
readonly index: number;
get(index: number): HistoryRecord;
backIndex({ path }: {
path: string;
}): number;
push({ path, state }: {
path: string;
state: NavigationState;
}): void;
replace({ path, state }: {
path: string;
state: NavigationState;
}): void;
go(n: number): Promise<void> | undefined;
listen(listener: () => void): () => void;
};
export {};
//# sourceMappingURL=createMemoryHistory.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"createMemoryHistory.d.ts","sourceRoot":"","sources":["../../src/fork/createMemoryHistory.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAG9D,KAAK,aAAa,GAAG;IAEnB,EAAE,EAAE,MAAM,CAAC;IAEX,KAAK,EAAE,eAAe,CAAC;IAEvB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAgB,mBAAmB;oBAmBlB,MAAM;eAcR,MAAM;wBAIG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;0BAad;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,eAAe,CAAA;KAAE;6BAmBrC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,eAAe,CAAA;KAAE;UAoC3D,MAAM;qBA2FK,MAAM,IAAI;EAiB9B"}

View File

@@ -0,0 +1,180 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createMemoryHistory = createMemoryHistory;
const non_secure_1 = require("nanoid/non-secure");
function createMemoryHistory() {
let index = 0;
let items = [];
// Pending callbacks for `history.go(n)`
// We might modify the callback stored if it was interrupted, so we have a ref to identify it
const pending = [];
const interrupt = () => {
// If another history operation was performed we need to interrupt existing ones
// This makes sure that calls such as `history.replace` after `history.go` don't happen
// Since otherwise it won't be correct if something else has changed
pending.forEach((it) => {
const cb = it.cb;
it.cb = () => cb(true);
});
};
const history = {
get index() {
// We store an id in the state instead of an index
// Index could get out of sync with in-memory values if page reloads
const id = window.history.state?.id;
if (id) {
const index = items.findIndex((item) => item.id === id);
return index > -1 ? index : 0;
}
return 0;
},
get(index) {
return items[index];
},
backIndex({ path }) {
// We need to find the index from the element before current to get closest path to go back to
for (let i = index - 1; i >= 0; i--) {
const item = items[i];
if (item.path === path) {
return i;
}
}
return -1;
},
push({ path, state }) {
interrupt();
const id = (0, non_secure_1.nanoid)();
// When a new entry is pushed, all the existing entries after index will be inaccessible
// So we remove any existing entries after the current index to clean them up
items = items.slice(0, index + 1);
items.push({ path, state, id });
index = items.length - 1;
// We pass empty string for title because it's ignored in all browsers except safari
// We don't store state object in history.state because:
// - browsers have limits on how big it can be, and we don't control the size
// - while not recommended, there could be non-serializable data in state
window.history.pushState({ id }, '', path);
},
replace({ path, state }) {
interrupt();
const id = window.history.state?.id ?? (0, non_secure_1.nanoid)();
// Need to keep the hash part of the path if there was no previous history entry
// or the previous history entry had the same path
let pathWithHash = path;
const hash = pathWithHash.includes('#') ? '' : location.hash;
if (!items.length || items.findIndex((item) => item.id === id) < 0) {
// There are two scenarios for creating an array with only one history record:
// - When loaded id not found in the items array, this function by default will replace
// the first item. We need to keep only the new updated object, otherwise it will break
// the page when navigating forward in history.
// - This is the first time any state modifications are done
// So we need to push the entry as there's nothing to replace
pathWithHash = pathWithHash + hash;
items = [{ path: pathWithHash, state, id }];
index = 0;
}
else {
if (items[index].path === path) {
pathWithHash = pathWithHash + hash;
}
items[index] = { path, state, id };
}
window.history.replaceState({ id }, '', pathWithHash);
},
// `history.go(n)` is asynchronous, there are couple of things to keep in mind:
// - it won't do anything if we can't go `n` steps, the `popstate` event won't fire.
// - each `history.go(n)` call will trigger a separate `popstate` event with correct location.
// - the `popstate` event fires before the next frame after calling `history.go(n)`.
// This method differs from `history.go(n)` in the sense that it'll go back as many steps it can.
go(n) {
interrupt();
// To guard against unexpected navigation out of the app we will assume that browser history is only as deep as the length of our memory
// history. If we don't have an item to navigate to then update our index and navigate as far as we can without taking the user out of the app.
const nextIndex = index + n;
const lastItemIndex = items.length - 1;
if (n < 0 && !items[nextIndex]) {
// Attempted to navigate beyond the first index. Negating the current index will align the browser history with the first item.
n = -index;
index = 0;
}
else if (n > 0 && nextIndex > lastItemIndex) {
// Attempted to navigate past the last index. Calculate how many indices away from the last index and go there.
n = lastItemIndex - index;
index = lastItemIndex;
}
else {
index = nextIndex;
}
if (n === 0) {
return;
}
// When we call `history.go`, `popstate` will fire when there's history to go back to
// So we need to somehow handle following cases:
// - There's history to go back, `history.go` is called, and `popstate` fires
// - `history.go` is called multiple times, we need to resolve on respective `popstate`
// - No history to go back, but `history.go` was called, browser has no API to detect it
return new Promise((resolve, reject) => {
const done = (interrupted) => {
clearTimeout(timer);
if (interrupted) {
reject(new Error('History was changed during navigation.'));
return;
}
// There seems to be a bug in Chrome regarding updating the title
// If we set a title just before calling `history.go`, the title gets lost
// However the value of `document.title` is still what we set it to
// It's just not displayed in the tab bar
// To update the tab bar, we need to reset the title to something else first (e.g. '')
// And set the title to what it was before so it gets applied
// It won't work without setting it to empty string coz otherwise title isn't changing
// Which means that the browser won't do anything after setting the title
const { title } = window.document;
window.document.title = '';
window.document.title = title;
resolve();
};
pending.push({ ref: done, cb: done });
// If navigation didn't happen within 100ms, assume that it won't happen
// This may not be accurate, but hopefully it won't take so much time
// In Chrome, navigation seems to happen instantly in next microtask
// But on Firefox, it seems to take much longer, around 50ms from our testing
// We're using a hacky timeout since there doesn't seem to be way to know for sure
const timer = setTimeout(() => {
const index = pending.findIndex((it) => it.ref === done);
if (index > -1) {
pending[index].cb();
pending.splice(index, 1);
}
}, 100);
const onPopState = () => {
const id = window.history.state?.id;
const currentIndex = items.findIndex((item) => item.id === id);
// Fix createMemoryHistory.index variable's value
// as it may go out of sync when navigating in the browser.
index = Math.max(currentIndex, 0);
const last = pending.pop();
window.removeEventListener('popstate', onPopState);
last?.cb();
};
window.addEventListener('popstate', onPopState);
window.history.go(n);
});
},
// The `popstate` event is triggered when history changes, except `pushState` and `replaceState`
// If we call `history.go(n)` ourselves, we don't want it to trigger the listener
// Here we normalize it so that only external changes (e.g. user pressing back/forward) trigger the listener
listen(listener) {
const onPopState = () => {
if (pending.length) {
// This was triggered by `history.go(n)`, we shouldn't call the listener
return;
}
listener();
};
window.addEventListener('popstate', onPopState);
return () => window.removeEventListener('popstate', onPopState);
},
};
return history;
}
//# sourceMappingURL=createMemoryHistory.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,7 @@
export declare function parsePathAndParamsFromExpoGoLink(url: string): {
pathname: string;
queryString: string;
};
export declare function parsePathFromExpoGoLink(url: string): string;
export declare function extractExpoPathFromURL(_prefixes: string[], url?: string): string;
//# sourceMappingURL=extractPathFromURL.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"extractPathFromURL.d.ts","sourceRoot":"","sources":["../../src/fork/extractPathFromURL.ts"],"names":[],"mappings":"AAAA,wBAAgB,gCAAgC,CAAC,GAAG,EAAE,MAAM,GAAG;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB,CAUA;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI3D;AA2GD,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,GAAG,GAAE,MAAW,UAM3E"}

View File

@@ -0,0 +1,110 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parsePathAndParamsFromExpoGoLink = parsePathAndParamsFromExpoGoLink;
exports.parsePathFromExpoGoLink = parsePathFromExpoGoLink;
exports.extractExpoPathFromURL = extractExpoPathFromURL;
function parsePathAndParamsFromExpoGoLink(url) {
// If the URL is defined (default in Expo Go dev apps) and the URL has no path:
// `exp://192.168.87.39:19000/` then use the default `exp://192.168.87.39:19000/--/`
const href = parsePathFromExpoGoLink(url);
const results = href.match(/([^?]*)(\?.*)?/);
return {
pathname: results?.[1] ?? '',
queryString: results?.[2] ?? '',
};
}
function parsePathFromExpoGoLink(url) {
// If the URL is defined (default in Expo Go dev apps) and the URL has no path:
// `exp://192.168.87.39:19000/` then use the default `exp://192.168.87.39:19000/--/`
return url.match(/exps?:\/\/.*?\/--\/(.*)/)?.[1] ?? '';
}
// This is only run on native.
function extractExactPathFromURL(url) {
if (
// If a universal link / app link / web URL is used, we should use the path
// from the URL, while stripping the origin.
url.match(/^https?:\/\//)) {
const { origin, href, hostname } = new URL(url);
if (hostname === 'exp.host' || hostname === 'u.expo.dev') {
// These are QR code generate deep-link that always like to the '/' path
// TODO: In the future, QR code may link to a specific path and this logic will need to be udpated
return '';
}
return href.replace(origin, '');
}
const isExpoGo = typeof expo !== 'undefined' && globalThis.expo?.modules?.ExpoGo;
// Handle special URLs used in Expo Go: `/--/pathname` -> `pathname`
if (isExpoGo &&
// while not exhaustive, `exp` and `exps` are the only two schemes which
// are passed through to other apps in Expo Go.
url.match(/^exp(s)?:\/\//)) {
const pathname = parsePathFromExpoGoLink(url);
if (pathname) {
return fromDeepLink('a://' + pathname);
}
// Match the `?.*` segment of the URL.
const queryParams = url.match(/exps?:\/\/.*\?(.*)/)?.[1];
if (queryParams) {
return fromDeepLink('a://?' + queryParams);
}
return '';
}
// TODO: Support dev client URLs
return fromDeepLink(url);
}
/** Major hack to support the makeshift expo-development-client system. */
function isExpoDevelopmentClient(url) {
return url.hostname === 'expo-development-client';
}
function fromDeepLink(url) {
let res;
try {
// This is for all standard deep links, e.g. `foobar://` where everything
// after the `://` is the path.
res = new URL(url);
}
catch {
/**
* We failed to parse the URL. This can occur for a variety of reasons, including:
* - Its a partial URL (e.g. `/route?query=param`).
* - It has a valid App scheme, but the scheme isn't a valid URL scheme (e.g. `my_app://`)
*/
// If `url` is already a path (starts with `/`), return it as-is
if (url.startsWith('/')) {
return url;
}
/**
* App schemes are not valid URL schemes, so they will fail to parse.
* We need to strip the scheme from these URLs
*/
return url.replace(/^[^:]+:\/\//, '');
}
if (isExpoDevelopmentClient(res)) {
if (!res.searchParams.get('url')) {
return '';
}
const incomingUrl = res.searchParams.get('url');
return extractExactPathFromURL(decodeURI(incomingUrl));
}
let results = '';
if (res.host) {
results += res.host;
}
if (res.pathname) {
results += res.pathname;
}
const qs = !res.search
? ''
: // @ts-ignore: `entries` is not on `URLSearchParams` in some typechecks.
[...res.searchParams.entries()].map(([k, v]) => `${k}=${decodeURIComponent(v)}`).join('&');
if (qs) {
results += '?' + qs;
}
return results;
}
function extractExpoPathFromURL(_prefixes, url = '') {
return (extractExactPathFromURL(url)
// TODO: We should get rid of this, dropping specificities is not good
.replace(/^\//, ''));
}
//# sourceMappingURL=extractPathFromURL.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
import type { InitialState } from '@react-navigation/routers';
export declare function findFocusedRoute(state: InitialState): (Omit<import("@react-navigation/routers").Route<string, object | undefined>, "key"> & {
state?: InitialState;
}) | undefined;
//# sourceMappingURL=findFocusedRoute.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"findFocusedRoute.d.ts","sourceRoot":"","sources":["../../src/fork/findFocusedRoute.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAE9D,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY;;eAUnD"}

View File

@@ -0,0 +1,13 @@
"use strict";
// Forked so we can access without importing any React Native code in Node.js environments.
Object.defineProperty(exports, "__esModule", { value: true });
exports.findFocusedRoute = findFocusedRoute;
function findFocusedRoute(state) {
let current = state;
while (current?.routes[current.index ?? 0].state != null) {
current = current.routes[current.index ?? 0].state;
}
const route = current?.routes[current?.index ?? 0];
return route;
}
//# sourceMappingURL=findFocusedRoute.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"findFocusedRoute.js","sourceRoot":"","sources":["../../src/fork/findFocusedRoute.tsx"],"names":[],"mappings":";AAAA,2FAA2F;;AAI3F,4CAUC;AAVD,SAAgB,gBAAgB,CAAC,KAAmB;IAClD,IAAI,OAAO,GAA6B,KAAK,CAAC;IAE9C,OAAO,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;QACzD,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IACrD,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC;IAEnD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["// Forked so we can access without importing any React Native code in Node.js environments.\n\nimport type { InitialState } from '@react-navigation/routers';\n\nexport function findFocusedRoute(state: InitialState) {\n let current: InitialState | undefined = state;\n\n while (current?.routes[current.index ?? 0].state != null) {\n current = current.routes[current.index ?? 0].state;\n }\n\n const route = current?.routes[current?.index ?? 0];\n\n return route;\n}\n"]}

View File

@@ -0,0 +1,27 @@
import { type Route } from '@react-navigation/native';
import type { Options, State, StringifyConfig } from './getPathFromState';
export type ExpoOptions = {
preserveDynamicRoutes?: boolean;
preserveGroups?: boolean;
shouldEncodeURISegment?: boolean;
};
export type ExpoConfigItem = {
initialRouteName?: string;
};
export declare function validatePathConfig<ParamList extends object>({ preserveDynamicRoutes, preserveGroups, shouldEncodeURISegment, ...options }: Options<ParamList>): void;
export declare function fixCurrentParams(allParams: Record<string, any>, route: Route<string> & {
state?: State;
}, stringify?: StringifyConfig): {
[k: string]: string | string[];
};
export declare function appendQueryAndHash(path: string, { '#': hash, ...focusedParams }: Record<string, any>): string;
export declare function appendBaseUrl(path: string, baseUrl?: string | undefined): string;
export declare function getPathWithConventionsCollapsed({ pattern, route, params, preserveGroups, preserveDynamicRoutes, shouldEncodeURISegment, initialRouteName, }: ExpoOptions & {
pattern: string;
route: Route<any>;
params: Record<string, any>;
initialRouteName?: string;
}): string;
export declare const getParamName: (pattern: string) => string;
export declare function isDynamicPart(p: string): boolean;
//# sourceMappingURL=getPathFromState-forks.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getPathFromState-forks.d.ts","sourceRoot":"","sources":["../../src/fork/getPathFromState-forks.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,KAAK,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAGlG,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAG1E,MAAM,MAAM,WAAW,GAAG;IACxB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAE3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,SAAS,SAAS,MAAM,EAAE,EAC3D,qBAAqB,EACrB,cAAc,EACd,sBAAsB,EACtB,GAAG,OAAO,EACX,EAAE,OAAO,CAAC,SAAS,CAAC,QAEpB;AAED,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC9B,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,EACD,SAAS,CAAC,EAAE,eAAe;;EA0B5B;AAED,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,aAAa,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,UAarD;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAM,GAAG,SAAqC,UASxD;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,OAAO,EACP,KAAK,EACL,MAAM,EACN,cAAc,EACd,qBAAqB,EACrB,sBAA6B,EAC7B,gBAAgB,GACjB,EAAE,WAAW,GAAG;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,UAkEA;AAED,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,WAAoD,CAAC;AAEjG,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,WAEtC"}

View File

@@ -0,0 +1,171 @@
"use strict";
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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getParamName = void 0;
exports.validatePathConfig = validatePathConfig;
exports.fixCurrentParams = fixCurrentParams;
exports.appendQueryAndHash = appendQueryAndHash;
exports.appendBaseUrl = appendBaseUrl;
exports.getPathWithConventionsCollapsed = getPathWithConventionsCollapsed;
exports.isDynamicPart = isDynamicPart;
const native_1 = require("@react-navigation/native");
const queryString = __importStar(require("query-string"));
const matchers_1 = require("../matchers");
function validatePathConfig({ preserveDynamicRoutes, preserveGroups, shouldEncodeURISegment, ...options }) {
(0, native_1.validatePathConfig)(options);
}
function fixCurrentParams(allParams, route, stringify) {
// Better handle array params
const currentParams = Object.fromEntries(Object.entries(route.params).flatMap(([key, value]) => {
if (key === 'screen' || key === 'params') {
return [];
}
return [
[
key,
stringify?.[key]
? stringify[key](value)
: Array.isArray(value)
? value.map(String)
: String(value),
],
];
}));
// We always assign params, as non pattern routes may still have query params
Object.assign(allParams, currentParams);
return currentParams;
}
function appendQueryAndHash(path, { '#': hash, ...focusedParams }) {
const query = queryString.stringify(focusedParams, { sort: false });
if (query) {
path += `?${query}`;
}
if (hash) {
path += `#${hash}`;
}
return path;
}
function appendBaseUrl(path, baseUrl = process.env.EXPO_BASE_URL) {
if (process.env.NODE_ENV !== 'development') {
if (baseUrl) {
return `/${baseUrl.replace(/^\/+/, '').replace(/\/$/, '')}${path}`;
}
}
return path;
}
function getPathWithConventionsCollapsed({ pattern, route, params, preserveGroups, preserveDynamicRoutes, shouldEncodeURISegment = true, initialRouteName, }) {
const segments = pattern.split('/');
return segments
.map((p, i) => {
const name = (0, exports.getParamName)(p);
// Showing the route name seems ok, though whatever we show here will be incorrect
// Since the page doesn't actually exist
if (p.startsWith('*')) {
if (preserveDynamicRoutes) {
if (name === 'not-found') {
return '+not-found';
}
return `[...${name}]`;
}
else if (params[name]) {
if (Array.isArray(params[name])) {
return params[name].join('/');
}
return params[name];
}
else if (route.name.startsWith('[') && route.name.endsWith(']')) {
return '';
}
else if (p === '*not-found') {
return '';
}
else {
return route.name;
}
}
// If the path has a pattern for a param, put the param in the path
if (p.startsWith(':')) {
if (preserveDynamicRoutes) {
return `[${name}]`;
}
// Optional params without value assigned in route.params should be ignored
const value = params[name];
if (value === undefined && p.endsWith('?')) {
return;
}
return (shouldEncodeURISegment ? encodeURISegment(value) : value) ?? 'undefined';
}
if (!preserveGroups && (0, matchers_1.matchGroupName)(p) != null) {
// When the last part is a group it could be a shared URL
// if the route has an initialRouteName defined, then we should
// use that as the component path as we can assume it will be shown.
if (segments.length - 1 === i) {
if (initialRouteName) {
// Return an empty string if the init route is ambiguous.
if (segmentMatchesConvention(initialRouteName)) {
return '';
}
return shouldEncodeURISegment
? encodeURISegment(initialRouteName, { preserveBrackets: true })
: initialRouteName;
}
}
return '';
}
// Preserve dynamic syntax for rehydration
return shouldEncodeURISegment ? encodeURISegment(p, { preserveBrackets: true }) : p;
})
.map((v) => v ?? '')
.join('/');
}
const getParamName = (pattern) => pattern.replace(/^[:*]/, '').replace(/\?$/, '');
exports.getParamName = getParamName;
function isDynamicPart(p) {
return p.startsWith(':') || p.startsWith('*');
}
function segmentMatchesConvention(segment) {
return (segment === 'index' || (0, matchers_1.matchGroupName)(segment) != null || (0, matchers_1.matchDynamicName)(segment) != null);
}
function encodeURISegment(str, { preserveBrackets = false } = {}) {
// Valid characters according to
// https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 (see pchar definition)
str = String(str).replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]/g, (char) => encodeURIComponent(char));
if (preserveBrackets) {
// Preserve brackets
str = str.replace(/%5B/g, '[').replace(/%5D/g, ']');
}
return str;
}
//# sourceMappingURL=getPathFromState-forks.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,46 @@
import { PathConfigMap } from '@react-navigation/native';
import type { NavigationState, PartialState } from '@react-navigation/routers';
import type { ExpoOptions } from './getPathFromState-forks';
export type Options<ParamList extends object> = ExpoOptions & {
path?: string;
initialRouteName?: string;
screens: PathConfigMap<ParamList>;
};
export type State = NavigationState | Omit<PartialState<NavigationState>, 'stale'>;
export type StringifyConfig = Record<string, (value: any) => string>;
/**
* Utility to serialize a navigation state object to a path string.
*
* @example
* ```js
* getPathFromState(
* {
* routes: [
* {
* name: 'Chat',
* params: { author: 'Jane', id: 42 },
* },
* ],
* },
* {
* screens: {
* Chat: {
* path: 'chat/:author/:id',
* stringify: { author: author => author.toLowerCase() }
* }
* }
* }
* )
* ```
*
* @param state Navigation state to serialize.
* @param options Extra options to fine-tune how to serialize the path.
* @returns Path representing the state, e.g. /foo/bar?count=42.
*/
export declare function getPathFromState<ParamList extends object>(state: State, options?: Options<ParamList>): string;
export declare function getPathDataFromState<ParamList extends object>(state: State, options?: Options<ParamList>): {
path: string;
params: Record<string, any>;
};
export declare function appendBaseUrl(path: string, baseUrl?: string | undefined): string;
//# sourceMappingURL=getPathFromState.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getPathFromState.d.ts","sourceRoot":"","sources":["../../src/fork/getPathFromState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAS,MAAM,2BAA2B,CAAC;AAItF,OAAO,KAAK,EAAkB,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAI5E,MAAM,MAAM,OAAO,CAAC,SAAS,SAAS,MAAM,IAAI,WAAW,GAAG;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACnC,CAAC;AAGF,MAAM,MAAM,KAAK,GAAG,eAAe,GAAG,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC;AAEnF,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,MAAM,CAAC,CAAC;AA4BrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,SAAS,MAAM,EACvD,KAAK,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAC3B,MAAM,CAER;AAED,wBAAgB,oBAAoB,CAAC,SAAS,SAAS,MAAM,EAC3D,KAAK,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC;;;EA2P7B;AAsDD,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAM,GAAG,SAAqC,UAQxD"}

337
node_modules/expo-router/build/fork/getPathFromState.js generated vendored Normal file
View File

@@ -0,0 +1,337 @@
"use strict";
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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPathFromState = getPathFromState;
exports.getPathDataFromState = getPathDataFromState;
exports.appendBaseUrl = appendBaseUrl;
const queryString = __importStar(require("query-string"));
const expo = __importStar(require("./getPathFromState-forks"));
const navigationParams_1 = require("../navigationParams");
// END FORK
const getActiveRoute = (state) => {
const route = typeof state.index === 'number'
? state.routes[state.index]
: state.routes[state.routes.length - 1];
if (route.state) {
return getActiveRoute(route.state);
}
return route;
};
let cachedNormalizedConfigs = [
undefined,
{},
];
/**
* Utility to serialize a navigation state object to a path string.
*
* @example
* ```js
* getPathFromState(
* {
* routes: [
* {
* name: 'Chat',
* params: { author: 'Jane', id: 42 },
* },
* ],
* },
* {
* screens: {
* Chat: {
* path: 'chat/:author/:id',
* stringify: { author: author => author.toLowerCase() }
* }
* }
* }
* )
* ```
*
* @param state Navigation state to serialize.
* @param options Extra options to fine-tune how to serialize the path.
* @returns Path representing the state, e.g. /foo/bar?count=42.
*/
function getPathFromState(state, options) {
return getPathDataFromState(state, options).path;
}
function getPathDataFromState(state, options) {
if (state == null) {
throw Error("Got 'undefined' for the navigation state. You must pass a valid state object.");
}
if (options) {
// START FORK
expo.validatePathConfig(options);
// validatePathConfig(options);
// END FORK
}
// Create a normalized configs object which will be easier to use
if (cachedNormalizedConfigs[0] !== options?.screens) {
cachedNormalizedConfigs = [
options?.screens,
options?.screens ? createNormalizedConfigs(options.screens) : {},
];
}
const configs = cachedNormalizedConfigs[1];
let path = '/';
let current = state;
const allParams = {};
while (current) {
let index = typeof current.index === 'number' ? current.index : 0;
let route = current.routes[index];
let pattern;
let focusedParams;
const focusedRoute = getActiveRoute(state);
let currentOptions = configs;
// Keep all the route names that appeared during going deeper in config in case the pattern is resolved to undefined
const nestedRouteNames = [];
let hasNext = true;
while (route.name in currentOptions && hasNext) {
pattern = currentOptions[route.name].pattern;
nestedRouteNames.push(route.name);
if (route.params) {
const stringify = currentOptions[route.name]?.stringify;
// START FORK
// This mutates allParams
const currentParams = expo.fixCurrentParams(allParams, route, stringify);
// const currentParams = Object.fromEntries(
// Object.entries(route.params).map(([key, value]) => [
// key,
// stringify?.[key] ? stringify[key](value) : String(value),
// ])
// );
// if (pattern) {
// Object.assign(allParams, currentParams);
// }
// END FORK
if (focusedRoute === route) {
// If this is the focused route, keep the params for later use
// We save it here since it's been stringified already
focusedParams = { ...currentParams };
pattern
?.split('/')
.filter((p) => expo.isDynamicPart(p))
// eslint-disable-next-line no-loop-func
.forEach((p) => {
const name = expo.getParamName(p);
// Remove the params present in the pattern since we'll only use the rest for query string
if (focusedParams) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete focusedParams[name];
}
});
}
}
// If there is no `screens` property or no nested state, we return pattern
if (!currentOptions[route.name].screens || route.state === undefined) {
// START FORK
// Expo Router allows you to navigate to a (group) and not specify a target screen
// This is different from React Navigation, which requires a target screen
// We need to handle this case here, by selecting either the index screen or the first screen of the group
// IMPORTANT: This does not affect groups that use _layout files with initialRouteNames
// Layout files create a new route config. This only affects groups without layouts that have their screens
// hoisted.
// Example:
// - /home/_layout
// - /home/(a|b|c)/index --> Hoisted to /home/_layout navigator
// - /home/(a|b|c)/other --> Hoisted to /home/_layout navigator
// - /home/(profile)/me --> Hoisted to /home/_layout navigator
//
// route.push('/home/(a)') --> This should navigate to /home/(a)/index
// route.push('/home/(profile)') --> This should navigate to /home/(profile)/me
const screens = currentOptions[route.name].screens;
// Determine what screen the user wants to navigate to. If no screen is specified, assume there is an index screen
// In the examples above, this ensures that /home/(a) navigates to /home/(a)/index
const targetScreen =
// This is typed as unknown, so we need to add these extra assertions
route.params && 'screen' in route.params && typeof route.params.screen === 'string'
? route.params.screen
: 'index';
// If the target screen is not in the screens object, default to the first screen
// In the examples above, this ensures that /home/(profile) navigates to /home/(profile)/me
// As there is no index screen in the group
const screen = screens
? screens[targetScreen]
? targetScreen
: Object.keys(screens)[0]
: undefined;
if (screen && screens && currentOptions[route.name].screens?.[screen]) {
const nestedParams = route.params?.params;
route = { ...screens[screen], name: screen, key: screen, params: nestedParams };
currentOptions = screens;
}
else {
hasNext = false;
}
// hasNext = false;
// END FORK
}
else {
index =
typeof route.state.index === 'number' ? route.state.index : route.state.routes.length - 1;
const nextRoute = route.state.routes[index];
const nestedConfig = currentOptions[route.name].screens;
// if there is config for next route name, we go deeper
if (nestedConfig && nextRoute.name in nestedConfig) {
route = nextRoute;
currentOptions = nestedConfig;
}
else {
// If not, there is no sense in going deeper in config
hasNext = false;
}
}
}
if (pattern === undefined) {
pattern = nestedRouteNames.join('/');
}
if (currentOptions[route.name] !== undefined) {
// START FORK
path += expo.getPathWithConventionsCollapsed({
...options,
pattern,
route,
params: allParams,
initialRouteName: configs[route.name]?.initialRouteName,
});
// path += pattern
// .split('/')
// .map((p) => {
// const name = getParamName(p);
// // We don't know what to show for wildcard patterns
// // Showing the route name seems ok, though whatever we show here will be incorrect
// // Since the page doesn't actually exist
// if (p === '*') {
// return route.name;
// }
// // If the path has a pattern for a param, put the param in the path
// if (p.startsWith(':')) {
// const value = allParams[name];
// if (value === undefined && p.endsWith('?')) {
// // Optional params without value assigned in route.params should be ignored
// return '';
// }
// // Valid characters according to
// // https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 (see pchar definition)
// return String(value).replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]/g, (char) =>
// encodeURIComponent(char)
// );
// }
// return encodeURIComponent(p);
// })
// .join('/');
// } else {
}
else if (!route.name.startsWith('+')) {
path += encodeURIComponent(route.name);
}
// END FORK
if (!focusedParams) {
focusedParams = focusedRoute.params;
}
if (route.state) {
path += '/';
}
else if (focusedParams) {
for (const param in focusedParams) {
if (focusedParams[param] === 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete focusedParams[param];
}
}
// START FORK
delete focusedParams['#'];
focusedParams = (0, navigationParams_1.removeInternalExpoRouterParams)(focusedParams);
// END FORK
const query = queryString.stringify(focusedParams, { sort: false });
if (query) {
path += `?${query}`;
}
}
current = route.state;
}
// Remove multiple as well as trailing slashes
path = path.replace(/\/+/g, '/');
path = path.length > 1 ? path.replace(/\/$/, '') : path;
// Include the root path if specified
if (options?.path) {
path = joinPaths(options.path, path);
}
// START FORK
path = expo.appendBaseUrl(path);
if (allParams['#']) {
path += `#${allParams['#']}`;
}
// END FORK
// START FORK
return { path, params: allParams };
// END FORK
}
// const getParamName = (pattern: string) => pattern.replace(/^:/, '').replace(/\?$/, '');
const joinPaths = (...paths) => []
.concat(...paths.map((p) => p.split('/')))
.filter(Boolean)
.join('/');
const createConfigItem = (config, parentPattern) => {
if (typeof config === 'string') {
// If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
const pattern = parentPattern ? joinPaths(parentPattern, config) : config;
return { pattern };
}
if (config.exact && config.path === undefined) {
throw new Error("A 'path' needs to be specified when specifying 'exact: true'. If you don't want this screen in the URL, specify it as empty string, e.g. `path: ''`.");
}
// If an object is specified as the value (e.g. Foo: { ... }),
// It can have `path` property and `screens` prop which has nested configs
const pattern = config.exact !== true ? joinPaths(parentPattern || '', config.path || '') : config.path || '';
const screens = config.screens ? createNormalizedConfigs(config.screens, pattern) : undefined;
return {
// Normalize pattern to remove any leading, trailing slashes, duplicate slashes etc.
pattern: pattern?.split('/').filter(Boolean).join('/'),
stringify: config.stringify,
screens,
};
};
const createNormalizedConfigs = (options, pattern) => Object.fromEntries(Object.entries(options).map(([name, c]) => {
const result = createConfigItem(c, pattern);
return [name, result];
}));
function appendBaseUrl(path, baseUrl = process.env.EXPO_BASE_URL) {
if (process.env.NODE_ENV !== 'development') {
if (baseUrl) {
return `/${baseUrl.replace(/^\/+/, '').replace(/\/$/, '')}${path}`;
}
}
return path;
}
//# sourceMappingURL=getPathFromState.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,64 @@
import { InitialState } from '@react-navigation/native';
import * as queryString from 'query-string';
import type { InitialRouteConfig, Options, ParsedRoute, RouteConfig } from './getStateFromPath';
export type ExpoOptions = {
previousSegments?: string[];
};
export type ExpoRouteConfig = {
type: 'static' | 'dynamic' | 'layout';
userReadableName: string;
isIndex: boolean;
isInitial?: boolean;
hasChildren: boolean;
expandedRouteNames: string[];
parts: string[];
staticPartCount: number;
};
/**
* In Expo Router, the params are available at all levels of the routing config
* @param routes
* @returns
*/
export declare function populateParams(routes?: ParsedRoute[], params?: Record<string, any>): ParsedRoute[] | undefined;
export declare function safelyDecodeURIComponent(str: string): string;
interface UrlWithReactNavigationConcessions {
path: string;
nonstandardPathname: string;
hash: string;
pathWithoutGroups: string;
}
export declare function getUrlWithReactNavigationConcessions(path: string, baseUrl?: string | undefined): UrlWithReactNavigationConcessions;
export declare function createConfig(screen: string, pattern: string, routeNames: string[], config?: Record<string, any>): Omit<ExpoRouteConfig, 'isInitial'>;
export declare function assertScreens(options?: Options<object>): asserts options is Options<object>;
export declare function configRegExp(config: RouteConfig): RegExp | undefined;
export declare function isDynamicPart(p: string): boolean;
export declare function replacePart(p: string): string;
export declare function getParamValue(p: string, value: string): string | string[] | undefined;
export declare function handleUrlParams(route: ParsedRoute, params?: queryString.ParsedQuery): void;
export declare function spreadParamsAcrossAllStates(state: InitialState, params?: Record<string, any>): void;
export declare function stripBaseUrl(path: string, baseUrl?: string | undefined): string;
export declare function matchForEmptyPath(configs: RouteConfig[]): {
path: string;
type: "static" | "dynamic" | "layout";
userReadableName: string;
isIndex: boolean;
isInitial?: boolean;
hasChildren: boolean;
expandedRouteNames: string[];
parts: string[];
staticPartCount: number;
screen: string;
regex?: RegExp;
pattern: string;
routeNames: string[];
parse?: {
[x: string]: (value: string) => any;
};
} | undefined;
export declare function appendIsInitial(initialRoutes: InitialRouteConfig[]): (config: RouteConfig) => RouteConfig;
export declare function getRouteConfigSorter(previousSegments?: string[]): (a: RouteConfig, b: RouteConfig) => number;
export declare function parseQueryParams(path: string, route: ParsedRoute, parseConfig?: Record<string, (value: string) => any>, hash?: string): Record<string, string | string[]> | undefined;
export declare function cleanPath(path: string): string;
export declare function routePatternToRegex(pattern: string): RegExp;
export {};
//# sourceMappingURL=getStateFromPath-forks.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getStateFromPath-forks.d.ts","sourceRoot":"","sources":["../../src/fork/getStateFromPath-forks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD,OAAO,KAAK,WAAW,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAIhG,MAAM,MAAM,WAAW,GAAG;IACxB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACtC,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,6BAQlF;AAED,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,UAMnD;AAED,UAAU,iCAAiC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,oCAAoC,CAClD,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAM,GAAG,SAAqC,GACtD,iCAAiC,CAsBnC;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAAE,EACpB,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GAC/B,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,CA4CpC;AAED,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAI3F;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,sBAI/C;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,WAEtC;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,UAEpC;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,iCAOrD;AAwBD,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,WAAW,CAAC,WAAW,QAsBnF;AAED,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QAK5F;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,MAAM,GAAG,SAAqC,UAQxD;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE;;UApNhD,QAAQ,GAAG,SAAS,GAAG,QAAQ;sBACnB,MAAM;aACf,OAAO;gBACJ,OAAO;iBACN,OAAO;wBACA,MAAM,EAAE;WACrB,MAAM,EAAE;qBACE,MAAM;;;;;;;;cA6OxB;AAED,wBAAgB,eAAe,CAAC,aAAa,EAAE,kBAAkB,EAAE,IAKhD,QAAQ,WAAW,iBAMrC;AAQD,wBAAgB,oBAAoB,CAAC,gBAAgB,GAAE,MAAM,EAAO,IACtC,GAAG,WAAW,EAAE,GAAG,WAAW,YA2J3D;AAED,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,WAAW,EAClB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAC,EACpD,IAAI,CAAC,EAAE,MAAM,iDA4Bd;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,UAUrC;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,UAelD"}

View File

@@ -0,0 +1,414 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.populateParams = populateParams;
exports.safelyDecodeURIComponent = safelyDecodeURIComponent;
exports.getUrlWithReactNavigationConcessions = getUrlWithReactNavigationConcessions;
exports.createConfig = createConfig;
exports.assertScreens = assertScreens;
exports.configRegExp = configRegExp;
exports.isDynamicPart = isDynamicPart;
exports.replacePart = replacePart;
exports.getParamValue = getParamValue;
exports.handleUrlParams = handleUrlParams;
exports.spreadParamsAcrossAllStates = spreadParamsAcrossAllStates;
exports.stripBaseUrl = stripBaseUrl;
exports.matchForEmptyPath = matchForEmptyPath;
exports.appendIsInitial = appendIsInitial;
exports.getRouteConfigSorter = getRouteConfigSorter;
exports.parseQueryParams = parseQueryParams;
exports.cleanPath = cleanPath;
exports.routePatternToRegex = routePatternToRegex;
const escape_string_regexp_1 = __importDefault(require("escape-string-regexp"));
const matchers_1 = require("../matchers");
const url_1 = require("../utils/url");
/**
* In Expo Router, the params are available at all levels of the routing config
* @param routes
* @returns
*/
function populateParams(routes, params) {
if (!routes || !params || Object.keys(params).length === 0)
return;
for (const route of routes) {
Object.assign(route, { params });
}
return routes;
}
function safelyDecodeURIComponent(str) {
try {
return decodeURIComponent(str);
}
catch {
return str;
}
}
function getUrlWithReactNavigationConcessions(path, baseUrl = process.env.EXPO_BASE_URL) {
const pathWithoutGroups = (0, matchers_1.stripGroupSegmentsFromPath)(stripBaseUrl(path, baseUrl));
let pathname = '';
let hash = '';
try {
const parsed = (0, url_1.parseUrlUsingCustomBase)(path);
pathname = parsed.pathname;
hash = parsed.hash;
}
catch {
// Do nothing with invalid URLs.
}
const withoutBaseUrl = stripBaseUrl(pathname, baseUrl);
return {
path,
// Make sure there is a trailing slash
// The slashes are at the end, not the beginning
nonstandardPathname: withoutBaseUrl.replace(/^\/+/g, '').replace(/\/+$/g, '') + '/',
hash,
pathWithoutGroups,
};
}
function createConfig(screen, pattern, routeNames, config = {}) {
const parts = [];
let isDynamic = false;
const isIndex = screen === 'index' || screen.endsWith('/index');
let staticPartCount = 0;
for (const part of pattern.split('/')) {
if (part) {
// If any part is dynamic, then the route is dynamic
const isDynamicPart = part.startsWith(':') || part.startsWith('*') || part.includes('*not-found');
isDynamic ||= isDynamicPart;
if (!(0, matchers_1.matchGroupName)(part)) {
parts.push(part);
if (!isDynamicPart) {
staticPartCount++;
}
}
}
}
const hasChildren = config.screens ? !!Object.keys(config.screens)?.length : false;
const type = hasChildren ? 'layout' : isDynamic ? 'dynamic' : 'static';
if (isIndex) {
parts.push('index');
staticPartCount++;
}
return {
type,
isIndex,
hasChildren,
parts,
staticPartCount,
userReadableName: [...routeNames.slice(0, -1), config.path || screen].join('/'),
// Don't include the __root route name
expandedRouteNames: routeNames.slice(1).flatMap((name) => {
return name.split('/');
}),
};
}
function assertScreens(options) {
if (!options?.screens) {
throw Error("You must pass a 'screens' object to 'getStateFromPath' to generate a path.");
}
}
function configRegExp(config) {
return config.pattern
? new RegExp(`^(${config.pattern.split('/').map(formatRegexPattern).join('')})$`)
: undefined;
}
function isDynamicPart(p) {
return p.length > 1 && (p.startsWith(':') || p.startsWith('*'));
}
function replacePart(p) {
return p.replace(/^[:*]/, '').replace(/\?$/, '');
}
function getParamValue(p, value) {
if (p.startsWith('*')) {
const values = value.split('/').filter((v) => v !== '');
return values.length === 0 && p.endsWith('?') ? undefined : values;
}
else {
return value;
}
}
function formatRegexPattern(it) {
// Allow spaces in file path names.
it = it.replace(' ', '%20');
if (it.startsWith(':')) {
// TODO: Remove unused match group
return `(([^/]+\\/)${it.endsWith('?') ? '?' : ''})`;
}
else if (it.startsWith('*')) {
return `((.*\\/)${it.endsWith('?') ? '?' : ''})`;
}
// Strip groups from the matcher
if ((0, matchers_1.matchGroupName)(it) != null) {
// Groups are optional segments
// this enables us to match `/bar` and `/(foo)/bar` for the same route
// NOTE(EvanBacon): Ignore this match in the regex to avoid capturing the group
return `(?:${(0, escape_string_regexp_1.default)(it)}\\/)?`;
}
return (0, escape_string_regexp_1.default)(it) + `\\/`;
}
function handleUrlParams(route, params) {
if (params) {
route.params = Object.assign(Object.create(null), route.params);
for (const [name, value] of Object.entries(params)) {
if (route.params?.[name]) {
if (process.env.NODE_ENV !== 'production') {
console.warn(`Route '/${route.name}' with param '${name}' was specified both in the path and as a param, removing from path`);
}
}
if (!route.params?.[name]) {
route.params[name] = value;
continue;
}
}
if (Object.keys(route.params).length === 0) {
delete route.params;
}
}
}
function spreadParamsAcrossAllStates(state, params) {
while (state) {
const route = state.routes[0];
route.params = Object.assign({}, route.params, params);
}
}
function stripBaseUrl(path, baseUrl = process.env.EXPO_BASE_URL) {
if (process.env.NODE_ENV !== 'development') {
if (baseUrl) {
return path.replace(/^\/+/g, '/').replace(new RegExp(`^\\/?${(0, escape_string_regexp_1.default)(baseUrl)}`, 'g'), '');
}
}
return path;
}
function matchForEmptyPath(configs) {
// We need to add special handling of empty path so navigation to empty path also works
// When handling empty path, we should only look at the root level config
// NOTE(EvanBacon): We only care about matching leaf nodes.
const leafNodes = configs
.filter((config) => !config.hasChildren)
.map((value) => {
return {
...value,
// Collapse all levels of group segments before testing.
// This enables `app/(one)/(two)/index.js` to be matched.
path: (0, matchers_1.stripGroupSegmentsFromPath)(value.path),
};
});
const match = leafNodes.find((config) =>
// NOTE(EvanBacon): Test leaf node index routes that either don't have a regex or match an empty string.
config.path === '' && (!config.regex || config.regex.test(''))) ??
leafNodes.find((config) =>
// NOTE(EvanBacon): Test leaf node dynamic routes that match an empty string.
config.path.startsWith(':') && config.regex.test('')) ??
// NOTE(EvanBacon): Test leaf node deep dynamic routes that match a slash.
// This should be done last to enable dynamic routes having a higher priority.
leafNodes.find((config) => config.path.startsWith('*') && config.regex.test('/'));
return match;
}
function appendIsInitial(initialRoutes) {
const resolvedInitialPatterns = initialRoutes.map((route) => joinPaths(...route.parentScreens, route.initialRouteName));
return function (config) {
// TODO(EvanBacon): Probably a safer way to do this
// Mark initial routes to give them potential priority over other routes that match.
config.isInitial = resolvedInitialPatterns.includes(config.routeNames.join('/'));
return config;
};
}
const joinPaths = (...paths) => []
.concat(...paths.map((p) => p.split('/')))
.filter(Boolean)
.join('/');
function getRouteConfigSorter(previousSegments = []) {
return function sortConfigs(a, b) {
// Sort config so that:
// - the most exhaustive ones are always at the beginning
// - patterns with wildcard are always at the end
// If 2 patterns are same, move the one with less route names up
// This is an error state, so it's only useful for consistent error messages
if (a.pattern === b.pattern) {
return b.routeNames.join('>').localeCompare(a.routeNames.join('>'));
}
/*
* If one of the patterns starts with the other, it is earlier in the config sorting.
* However, configs are a mix of route configs and layout configs
* e.g There will be a config for `/(group)`, but maybe there isn't a `/(group)/index.tsx`
*
* This is because you can navigate to a directory and its navigator will determine the route
* These routes should be later in the config sorting, as their patterns are very open
* and will prevent routes from being matched
*
* Therefore before we compare segment parts, we force these layout configs later in the sorting
*
* NOTE(marklawlor): Is this a feature we want? I'm unsure if this is a gimmick or a feature.
*/
if (a.pattern.startsWith(b.pattern) && !b.isIndex) {
return -1;
}
if (b.pattern.startsWith(a.pattern) && !a.isIndex) {
return 1;
}
/*
* Static routes should always be higher than dynamic and layout routes.
*/
if (a.type === 'static' && b.type !== 'static') {
return -1;
}
else if (a.type !== 'static' && b.type === 'static') {
return 1;
}
/*
* If the routes have any static segments, the one the most static segments should be higher
*/
if (a.staticPartCount !== b.staticPartCount) {
return b.staticPartCount - a.staticPartCount;
}
/*
* If both are static/dynamic or a layout file, then we check group similarity
*/
const similarToPreviousA = previousSegments.filter((value, index) => {
return value === a.expandedRouteNames[index] && value.startsWith('(') && value.endsWith(')');
});
const similarToPreviousB = previousSegments.filter((value, index) => {
return value === b.expandedRouteNames[index] && value.startsWith('(') && value.endsWith(')');
});
if ((similarToPreviousA.length > 0 || similarToPreviousB.length > 0) &&
similarToPreviousA.length !== similarToPreviousB.length) {
// One matches more than the other, so pick the one that matches more
return similarToPreviousB.length - similarToPreviousA.length;
}
/*
* If there is not difference in similarity, then each non-group segment is compared against each other
*/
for (let i = 0; i < Math.max(a.parts.length, b.parts.length); i++) {
// if b is longer, b get higher priority
if (a.parts[i] == null) {
return 1;
}
// if a is longer, a get higher priority
if (b.parts[i] == null) {
return -1;
}
const aWildCard = a.parts[i].startsWith('*');
const bWildCard = b.parts[i].startsWith('*');
// if both are wildcard we compare next component
if (aWildCard && bWildCard) {
const aNotFound = a.parts[i].match(/^[*]not-found$/);
const bNotFound = b.parts[i].match(/^[*]not-found$/);
if (aNotFound && bNotFound) {
continue;
}
else if (aNotFound) {
return 1;
}
else if (bNotFound) {
return -1;
}
continue;
}
// if only a is wild card, b get higher priority
if (aWildCard) {
return 1;
}
// if only b is wild card, a get higher priority
if (bWildCard) {
return -1;
}
const aSlug = a.parts[i].startsWith(':');
const bSlug = b.parts[i].startsWith(':');
// if both are wildcard we compare next component
if (aSlug && bSlug) {
const aNotFound = a.parts[i].match(/^[*]not-found$/);
const bNotFound = b.parts[i].match(/^[*]not-found$/);
if (aNotFound && bNotFound) {
continue;
}
else if (aNotFound) {
return 1;
}
else if (bNotFound) {
return -1;
}
continue;
}
// if only a is wild card, b get higher priority
if (aSlug) {
return 1;
}
// if only b is wild card, a get higher priority
if (bSlug) {
return -1;
}
}
/*
* Both configs are identical in specificity and segments count/type
* Try and sort by initial instead.
*
* TODO: We don't differentiate between the default initialRoute and group specific default routes
*
* const unstable_settings = {
* "group": {
* initialRouteName: "article"
* }
* }
*
* "article" will be ranked higher because its an initialRoute for a group - even if not your not currently in
* that group. The current work around is to ways provide initialRouteName for all groups
*/
if (a.isInitial && !b.isInitial) {
return -1;
}
else if (!a.isInitial && b.isInitial) {
return 1;
}
return b.parts.length - a.parts.length;
};
}
function parseQueryParams(path, route, parseConfig, hash) {
const searchParams = (0, url_1.parseUrlUsingCustomBase)(path).searchParams;
const params = Object.create(null);
if (hash) {
params['#'] = hash.slice(1);
}
for (const name of searchParams.keys()) {
if (route.params?.[name]) {
if (process.env.NODE_ENV !== 'production') {
console.warn(`Route '/${route.name}' with param '${name}' was specified both in the path and as a param, removing from path`);
}
}
else {
const values = parseConfig?.hasOwnProperty(name)
? searchParams.getAll(name).map((value) => parseConfig[name](value))
: searchParams.getAll(name);
// searchParams.getAll returns an array.
// if we only have a single value, and its not an array param, we need to extract the value
params[name] = values.length === 1 ? values[0] : values;
}
}
return Object.keys(params).length ? params : undefined;
}
function cleanPath(path) {
path = path
// let remaining = path
// END FORK
.replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
.replace(/^\//, '') // Remove extra leading slash
.replace(/\?.*$/, ''); // Remove query params which we will handle later
// Make sure there is a trailing slash
return path.endsWith('/') ? path : `${path}/`;
}
function routePatternToRegex(pattern) {
return new RegExp(`^(${pattern
.split('/')
.map((it) => {
if (it.startsWith('(') && it.endsWith(')')) {
return `${it}?`;
}
else if (it.startsWith(':')) {
return `(([^/]+\\/)${it.endsWith('?') ? '?' : ''})`;
}
return `${it === '*' ? '.*' : (0, escape_string_regexp_1.default)(it)}\\/`;
})
.join('')})`);
}
//# sourceMappingURL=getStateFromPath-forks.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,53 @@
import { PathConfigMap } from '@react-navigation/native';
import type { NavigationState, PartialState } from '@react-navigation/routers';
import type { ExpoOptions, ExpoRouteConfig } from './getStateFromPath-forks';
export type Options<ParamList extends object> = ExpoOptions & {
path?: string;
initialRouteName?: string;
screens: PathConfigMap<ParamList>;
};
type ParseConfig = Record<string, (value: string) => any>;
export type RouteConfig = ExpoRouteConfig & {
screen: string;
regex?: RegExp;
path: string;
pattern: string;
routeNames: string[];
parse?: ParseConfig;
};
export type InitialRouteConfig = {
initialRouteName: string;
parentScreens: string[];
};
export type ResultState = PartialState<NavigationState> & {
state?: ResultState;
};
export type ParsedRoute = {
name: string;
path?: string;
params?: Record<string, any> | undefined;
};
/**
* Utility to parse a path string to initial state object accepted by the container.
* This is useful for deep linking when we need to handle the incoming URL.
*
* @example
* ```js
* getStateFromPath(
* '/chat/jane/42',
* {
* screens: {
* Chat: {
* path: 'chat/:author/:id',
* parse: { id: Number }
* }
* }
* }
* )
* ```
* @param path Path string to parse and convert, e.g. /foo/bar?count=42.
* @param options Extra options to fine-tune how to parse the path.
*/
export declare function getStateFromPath<ParamList extends object>(path: string, options?: Options<ParamList>, segments?: string[]): ResultState | undefined;
export {};
//# sourceMappingURL=getStateFromPath.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"getStateFromPath.d.ts","sourceRoot":"","sources":["../../src/fork/getStateFromPath.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAsB,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAgB,eAAe,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAI7F,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAI7E,MAAM,MAAM,OAAO,CAAC,SAAS,SAAS,MAAM,IAAI,WAAW,GAAG;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;CACnC,CAAC;AAEF,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,GAAG,CAAC,CAAC;AAE1D,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,YAAY,CAAC,eAAe,CAAC,GAAG;IACxD,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;CAC1C,CAAC;AAQF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,SAAS,MAAM,EACvD,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,EAE5B,QAAQ,GAAE,MAAM,EAAO,GAEtB,WAAW,GAAG,SAAS,CAwGzB"}

549
node_modules/expo-router/build/fork/getStateFromPath.js generated vendored Normal file
View File

@@ -0,0 +1,549 @@
"use strict";
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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__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 });
exports.getStateFromPath = getStateFromPath;
const native_1 = require("@react-navigation/native");
const escape_string_regexp_1 = __importDefault(require("escape-string-regexp"));
const findFocusedRoute_1 = require("./findFocusedRoute");
const expo = __importStar(require("./getStateFromPath-forks"));
const constants_1 = require("../constants");
/**
* Utility to parse a path string to initial state object accepted by the container.
* This is useful for deep linking when we need to handle the incoming URL.
*
* @example
* ```js
* getStateFromPath(
* '/chat/jane/42',
* {
* screens: {
* Chat: {
* path: 'chat/:author/:id',
* parse: { id: Number }
* }
* }
* }
* )
* ```
* @param path Path string to parse and convert, e.g. /foo/bar?count=42.
* @param options Extra options to fine-tune how to parse the path.
*/
function getStateFromPath(path, options,
// START FORK
segments = []
// END FORK
) {
const { initialRoutes, configs, configWithRegexes } = getConfigResources(options,
// START FORK
segments
// END FORK
);
const screens = options?.screens;
// START FORK
const expoPath = expo.getUrlWithReactNavigationConcessions(path);
// END FORK
// START FORK
let remaining = expo.cleanPath(expoPath.nonstandardPathname);
// let remaining = path
// .replace(/\/+/g, '/') // Replace multiple slash (//) with single ones
// .replace(/^\//, '') // Remove extra leading slash
// .replace(/\?.*$/, ''); // Remove query params which we will handle later
// // Make sure there is a trailing slash
// remaining = remaining.endsWith('/') ? remaining : `${remaining}/`;
// END FORK
const prefix = options?.path?.replace(/^\//, ''); // Remove extra leading slash
if (prefix) {
// Make sure there is a trailing slash
const normalizedPrefix = prefix.endsWith('/') ? prefix : `${prefix}/`;
// If the path doesn't start with the prefix, it's not a match
if (!remaining.startsWith(normalizedPrefix)) {
return undefined;
}
// Remove the prefix from the path
remaining = remaining.replace(normalizedPrefix, '');
}
if (screens === undefined) {
// When no config is specified, use the path segments as route names
const routes = remaining
.split('/')
.filter(Boolean)
.map((segment) => {
const name = decodeURIComponent(segment);
return { name };
});
if (routes.length) {
return createNestedStateObject(expoPath, routes, initialRoutes, [], expoPath.hash);
}
return undefined;
}
if (remaining === '/') {
// We need to add special handling of empty path so navigation to empty path also works
// When handling empty path, we should only look at the root level config
// START FORK
const match = expo.matchForEmptyPath(configWithRegexes);
// const match = configs.find(
// (config) =>
// config.path === '' &&
// config.routeNames.every(
// // Make sure that none of the parent configs have a non-empty path defined
// (name) => !configs.find((c) => c.screen === name)?.path
// )
// );
// END FORK
if (match) {
return createNestedStateObject(expoPath, match.routeNames.map((name) => ({ name })), initialRoutes, configs, expoPath.hash);
}
return undefined;
}
let result;
let current;
// We match the whole path against the regex instead of segments
// This makes sure matches such as wildcard will catch any unmatched routes, even if nested
const { routes, remainingPath } = matchAgainstConfigs(remaining, configWithRegexes);
if (routes !== undefined) {
// This will always be empty if full path matched
current = createNestedStateObject(expoPath, routes, initialRoutes, configs, expoPath.hash);
remaining = remainingPath;
result = current;
}
if (current == null || result == null) {
return undefined;
}
return result;
}
/**
* Reference to the last used config resources. This is used to avoid recomputing the config resources when the options are the same.
*/
let cachedConfigResources = [
undefined,
prepareConfigResources(),
];
function getConfigResources(options,
// START FORK
previousSegments
// END FORK
) {
// START FORK - We need to disable this caching as our configs can change based upon the current state
// if (cachedConfigResources[0] !== options) {
cachedConfigResources = [options, prepareConfigResources(options, previousSegments)];
// }
// END FORK FORK
return cachedConfigResources[1];
}
function prepareConfigResources(options, previousSegments) {
if (options) {
(0, native_1.validatePathConfig)(options);
}
const initialRoutes = getInitialRoutes(options);
const configs = getNormalizedConfigs(initialRoutes, options?.screens, previousSegments);
checkForDuplicatedConfigs(configs);
const configWithRegexes = getConfigsWithRegexes(configs);
return {
initialRoutes,
configs,
configWithRegexes,
};
}
function getInitialRoutes(options) {
const initialRoutes = [];
if (options?.initialRouteName) {
initialRoutes.push({
initialRouteName: options.initialRouteName,
parentScreens: [],
});
}
return initialRoutes;
}
function getNormalizedConfigs(initialRoutes, screens = {},
// START FORK
previousSegments
// END FORK
) {
// Create a normalized configs array which will be easier to use
return []
.concat(...Object.keys(screens).map((key) => createNormalizedConfigs(key, screens, [], initialRoutes, [])))
.map(expo.appendIsInitial(initialRoutes))
.sort(expo.getRouteConfigSorter(previousSegments));
// .sort((a, b) => {
// // Sort config so that:
// // - the most exhaustive ones are always at the beginning
// // - patterns with wildcard are always at the end
// // If 2 patterns are same, move the one with less route names up
// // This is an error state, so it's only useful for consistent error messages
// if (a.pattern === b.pattern) {
// return b.routeNames.join('>').localeCompare(a.routeNames.join('>'));
// }
// // If one of the patterns starts with the other, it's more exhaustive
// // So move it up
// if (a.pattern.startsWith(b.pattern)) {
// return -1;
// }
// if (b.pattern.startsWith(a.pattern)) {
// return 1;
// }
// const aParts = a.pattern.split('/');
// const bParts = b.pattern.split('/');
// for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
// // if b is longer, b get higher priority
// if (aParts[i] == null) {
// return 1;
// }
// // if a is longer, a get higher priority
// if (bParts[i] == null) {
// return -1;
// }
// const aWildCard = aParts[i] === '*' || aParts[i].startsWith(':');
// const bWildCard = bParts[i] === '*' || bParts[i].startsWith(':');
// // if both are wildcard we compare next component
// if (aWildCard && bWildCard) {
// continue;
// }
// // if only a is wild card, b get higher priority
// if (aWildCard) {
// return 1;
// }
// // if only b is wild card, a get higher priority
// if (bWildCard) {
// return -1;
// }
// }
// return bParts.length - aParts.length;
// });
}
function checkForDuplicatedConfigs(configs) {
// Check for duplicate patterns in the config
configs.reduce((acc, config) => {
if (acc[config.pattern]) {
const a = acc[config.pattern].routeNames;
const b = config.routeNames;
// It's not a problem if the path string omitted from a inner most screen
// For example, it's ok if a path resolves to `A > B > C` or `A > B`
const intersects = a.length > b.length ? b.every((it, i) => a[i] === it) : a.every((it, i) => b[i] === it);
if (!intersects) {
throw new Error(`Found conflicting screens with the same pattern. The pattern '${config.pattern}' resolves to both '${a.join(' > ')}' and '${b.join(' > ')}'. Patterns must be unique and cannot resolve to more than one screen.`);
}
}
return Object.assign(acc, {
[config.pattern]: config,
});
}, {});
}
function getConfigsWithRegexes(configs) {
return configs.map((c) => ({
...c,
// Add `$` to the regex to make sure it matches till end of the path and not just beginning
// START FORK
// regex: c.regex ? new RegExp(c.regex.source + '$') : undefined,
regex: expo.configRegExp(c),
// END FORK
}));
}
const joinPaths = (...paths) => []
.concat(...paths.map((p) => p.split('/')))
.filter(Boolean)
.join('/');
const matchAgainstConfigs = (remaining, configs) => {
let routes;
let remainingPath = remaining;
// START FORK
const allParams = Object.create(null);
// END FORK
// Go through all configs, and see if the next path segment matches our regex
for (const config of configs) {
if (!config.regex) {
continue;
}
const match = remainingPath.match(config.regex);
// If our regex matches, we need to extract params from the path
if (match) {
const matchResult = config.pattern?.split('/').reduce((acc, p, index) => {
if (!expo.isDynamicPart(p)) {
return acc;
}
acc.pos += 1;
// START FORK
const decodedParamSegment = expo.safelyDecodeURIComponent(
// const decodedParamSegment = decodeURIComponent(
// The param segments appear every second item starting from 2 in the regex match result
match[(acc.pos + 1) * 2]
// Remove trailing slash
.replace(/\/$/, ''));
// END FORK
Object.assign(acc.matchedParams, {
[p]: Object.assign(acc.matchedParams[p] || {}, {
[index]: decodedParamSegment,
}),
});
return acc;
}, { pos: -1, matchedParams: {} });
const matchedParams = matchResult.matchedParams || {};
routes = config.routeNames.map((name) => {
const routeConfig = configs.find((c) => {
// Check matching name AND pattern in case same screen is used at different levels in config
return c.screen === name && config.pattern.startsWith(c.pattern);
});
// Normalize pattern to remove any leading, trailing slashes, duplicate slashes etc.
const normalizedPath = routeConfig?.path.split('/').filter(Boolean).join('/');
// Get the number of segments in the initial pattern
const numInitialSegments = routeConfig?.pattern
// Extract the prefix from the pattern by removing the ending path pattern (e.g pattern=`a/b/c/d` and normalizedPath=`c/d` becomes `a/b`)
.replace(new RegExp(`${(0, escape_string_regexp_1.default)(normalizedPath)}$`), '')
?.split('/').length;
const params = normalizedPath
?.split('/')
.reduce((acc, p, index) => {
if (!expo.isDynamicPart(p)) {
return acc;
}
// Get the real index of the path parameter in the matched path
// by offsetting by the number of segments in the initial pattern
const offset = numInitialSegments ? numInitialSegments - 1 : 0;
// START FORK
// const value = matchedParams[p]?.[index + offset];
const value = expo.getParamValue(p, matchedParams[p]?.[index + offset]);
// END FORK
if (value) {
// START FORK
// const key = p.replace(/^:/, '').replace(/\?$/, '');
const key = expo.replacePart(p);
// END FORK
acc[key] = routeConfig?.parse?.[key] ? routeConfig.parse[key](value) : value;
}
return acc;
}, {});
if (params && Object.keys(params).length) {
Object.assign(allParams, params);
return { name, params };
}
return { name };
});
remainingPath = remainingPath.replace(match[1], '');
break;
}
}
// START FORK
expo.populateParams(routes, allParams);
// END FORK
return { routes, remainingPath };
};
const createNormalizedConfigs = (screen, routeConfig, routeNames = [], initials, parentScreens, parentPattern) => {
const configs = [];
routeNames.push(screen);
parentScreens.push(screen);
// @ts-expect-error: TODO(@kitten): This is entirely untyped. The index access just flags this, but we're not typing the config properly here
const config = routeConfig[screen];
if (typeof config === 'string') {
// If a string is specified as the value of the key(e.g. Foo: '/path'), use it as the pattern
const pattern = parentPattern ? joinPaths(parentPattern, config) : config;
configs.push(createConfigItem(screen, routeNames, pattern, config));
}
else if (typeof config === 'object') {
let pattern;
// if an object is specified as the value (e.g. Foo: { ... }),
// it can have `path` property and
// it could have `screens` prop which has nested configs
if (typeof config.path === 'string') {
if (config.exact && config.path === undefined) {
throw new Error("A 'path' needs to be specified when specifying 'exact: true'. If you don't want this screen in the URL, specify it as empty string, e.g. `path: ''`.");
}
pattern =
config.exact !== true
? joinPaths(parentPattern || '', config.path || '')
: config.path || '';
if (screen !== constants_1.INTERNAL_SLOT_NAME) {
configs.push(createConfigItem(screen, routeNames, pattern, config.path, config.parse, config));
}
}
if (config.screens) {
// property `initialRouteName` without `screens` has no purpose
if (config.initialRouteName) {
initials.push({
initialRouteName: config.initialRouteName,
parentScreens,
});
}
Object.keys(config.screens).forEach((nestedConfig) => {
const result = createNormalizedConfigs(nestedConfig, config.screens, routeNames, initials, [...parentScreens], pattern ?? parentPattern);
configs.push(...result);
});
}
}
routeNames.pop();
return configs;
};
const createConfigItem = (screen, routeNames, pattern, path, parse = undefined, config = {}) => {
// Normalize pattern to remove any leading, trailing slashes, duplicate slashes etc.
pattern = pattern.split('/').filter(Boolean).join('/');
// START FORK
const regex = pattern ? expo.routePatternToRegex(pattern) : undefined;
// const regex = pattern
// ? new RegExp(
// `^(${pattern
// .split('/')
// .map((it) => {
// if (it.startsWith(':')) {
// return `(([^/]+\\/)${it.endsWith('?') ? '?' : ''})`;
// }
// return `${it === '*' ? '.*' : escape(it)}\\/`;
// })
// .join('')})`
// )
// : undefined;
// END FORK
return {
screen,
regex,
pattern,
path,
// The routeNames array is mutated, so copy it to keep the current state
routeNames: [...routeNames],
parse,
// START FORK
...expo.createConfig(screen, pattern, routeNames, config),
// END FORK
};
};
const findParseConfigForRoute = (routeName, flatConfig) => {
for (const config of flatConfig) {
if (routeName === config.routeNames[config.routeNames.length - 1]) {
return config.parse;
}
}
return undefined;
};
// Try to find an initial route connected with the one passed
const findInitialRoute = (routeName, parentScreens, initialRoutes) => {
for (const config of initialRoutes) {
if (parentScreens.length === config.parentScreens.length) {
let sameParents = true;
for (let i = 0; i < parentScreens.length; i++) {
if (parentScreens[i].localeCompare(config.parentScreens[i]) !== 0) {
sameParents = false;
break;
}
}
if (sameParents) {
return routeName !== config.initialRouteName ? config.initialRouteName : undefined;
}
}
}
return undefined;
};
// returns state object with values depending on whether
// it is the end of state and if there is initialRoute for this level
const createStateObject = (initialRoute, route, isEmpty) => {
if (isEmpty) {
if (initialRoute) {
return {
index: 1,
routes: [{ name: initialRoute, params: route.params }, route],
};
}
else {
return {
routes: [route],
};
}
}
else {
if (initialRoute) {
return {
index: 1,
routes: [
{ name: initialRoute, params: route.params },
{ ...route, state: { routes: [] } },
],
};
}
else {
return {
routes: [{ ...route, state: { routes: [] } }],
};
}
}
};
const createNestedStateObject = ({ path, ...expoURL }, routes, initialRoutes, flatConfig, hash) => {
let route = routes.shift();
const parentScreens = [];
let initialRoute = findInitialRoute(route.name, parentScreens, initialRoutes);
parentScreens.push(route.name);
const state = createStateObject(initialRoute, route, routes.length === 0);
if (routes.length > 0) {
let nestedState = state;
while ((route = routes.shift())) {
initialRoute = findInitialRoute(route.name, parentScreens, initialRoutes);
const nestedStateIndex = nestedState.index || nestedState.routes.length - 1;
nestedState.routes[nestedStateIndex].state = createStateObject(initialRoute, route, routes.length === 0);
if (routes.length > 0) {
nestedState = nestedState.routes[nestedStateIndex].state;
}
parentScreens.push(route.name);
}
}
route = (0, findFocusedRoute_1.findFocusedRoute)(state);
// START FORK
route.path = expoURL.pathWithoutGroups;
// route.path = path;
// END FORK
// START FORK
// const params = parseQueryParams(
const params = expo.parseQueryParams(path, route, flatConfig ? findParseConfigForRoute(route.name, flatConfig) : undefined, hash);
// END FORK
// START FORK
// expo.handleUrlParams(route, params, hash);
if (params) {
route.params = { ...route.params, ...params };
}
// END FORK
return state;
};
// START FORK
// const parseQueryParams = (path: string, parseConfig?: Record<string, (value: string) => any>) => {
// const query = path.split('?')[1];
// const params = queryString.parse(query);
// if (parseConfig) {
// Object.keys(params).forEach((name) => {
// if (Object.hasOwnProperty.call(parseConfig, name) && typeof params[name] === 'string') {
// params[name] = parseConfig[name](params[name] as string);
// }
// });
// }
// return Object.keys(params).length ? params : undefined;
// };
// END FORK
//# sourceMappingURL=getStateFromPath.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
import { NativeStackView as RNNativeStackView } from '@react-navigation/native-stack';
import { ComponentProps } from 'react';
export declare function NativeStackView(props: ComponentProps<typeof RNNativeStackView>): import("react").JSX.Element;
//# sourceMappingURL=NativeStackView.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"NativeStackView.d.ts","sourceRoot":"","sources":["../../../src/fork/native-stack/NativeStackView.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,IAAI,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACtF,OAAO,EAAE,cAAc,EAAgB,MAAM,OAAO,CAAC;AAIrD,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,CAAC,OAAO,iBAAiB,CAAC,+BAM9E"}

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NativeStackView = NativeStackView;
const native_stack_1 = require("@react-navigation/native-stack");
const react_1 = require("react");
const RootModal_1 = require("../../layouts/RootModal");
function NativeStackView(props) {
return (<RootModal_1.RootModalProvider>
<NativeStackViewInner {...props}/>
</RootModal_1.RootModalProvider>);
}
function NativeStackViewInner(props) {
const rootModals = (0, react_1.use)(RootModal_1.RootModalContext);
// Append the root modals to the state
const state = (0, react_1.useMemo)(() => {
if (rootModals.routes.length === 0) {
return props.state;
}
return {
...props.state,
routes: props.state.routes.concat(rootModals.routes),
};
}, [props.state, rootModals.routes]);
return <native_stack_1.NativeStackView {...props} state={state}/>;
}
//# sourceMappingURL=NativeStackView.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"NativeStackView.js","sourceRoot":"","sources":["../../../src/fork/native-stack/NativeStackView.tsx"],"names":[],"mappings":";;AAKA,0CAMC;AAXD,iEAAsF;AACtF,iCAAqD;AAErD,uDAA8E;AAE9E,SAAgB,eAAe,CAAC,KAA+C;IAC7E,OAAO,CACL,CAAC,6BAAiB,CAChB;MAAA,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,EAClC;IAAA,EAAE,6BAAiB,CAAC,CACrB,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAAC,KAA+C;IAC3E,MAAM,UAAU,GAAG,IAAA,WAAG,EAAC,4BAAgB,CAAC,CAAC;IAEzC,sCAAsC;IACtC,MAAM,KAAK,GAAG,IAAA,eAAO,EAAC,GAAG,EAAE;QACzB,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnC,OAAO,KAAK,CAAC,KAAK,CAAC;QACrB,CAAC;QAED,OAAO;YACL,GAAG,KAAK,CAAC,KAAK;YACd,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;SACrD,CAAC;IACJ,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAErC,OAAO,CAAC,8BAAiB,CAAC,IAAI,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,EAAG,CAAC;AACxD,CAAC","sourcesContent":["import { NativeStackView as RNNativeStackView } from '@react-navigation/native-stack';\nimport { ComponentProps, use, useMemo } from 'react';\n\nimport { RootModalContext, RootModalProvider } from '../../layouts/RootModal';\n\nexport function NativeStackView(props: ComponentProps<typeof RNNativeStackView>) {\n return (\n <RootModalProvider>\n <NativeStackViewInner {...props} />\n </RootModalProvider>\n );\n}\n\nfunction NativeStackViewInner(props: ComponentProps<typeof RNNativeStackView>) {\n const rootModals = use(RootModalContext);\n\n // Append the root modals to the state\n const state = useMemo(() => {\n if (rootModals.routes.length === 0) {\n return props.state;\n }\n\n return {\n ...props.state,\n routes: props.state.routes.concat(rootModals.routes),\n };\n }, [props.state, rootModals.routes]);\n\n return <RNNativeStackView {...props} state={state} />;\n}\n"]}

View File

@@ -0,0 +1,38 @@
import type { NativeStackNavigationOptions } from '@react-navigation/native-stack';
import type { CompositionContextValue, CompositionRegistry } from './types';
/** @internal */
export declare const CompositionContext: import("react").Context<CompositionContextValue | null>;
type RegistryAction = {
type: 'set';
routeKey: string;
options: Partial<NativeStackNavigationOptions>;
} | {
type: 'unset';
routeKey: string;
options: Partial<NativeStackNavigationOptions>;
};
/** @internal */
export declare function registryReducer(state: CompositionRegistry, action: RegistryAction): CompositionRegistry;
/**
* Provides the composition registry to descendant composition components.
*
* Uses useReducer with immutable object updates for React Compiler compatibility.
* Each set/unset call produces a new object reference, which the compiler can
* track as a reactive dependency.
*/
export declare function useCompositionRegistry(): {
registry: CompositionRegistry;
contextValue: {
set: (routeKey: string, options: Partial<NativeStackNavigationOptions>) => void;
unset: (routeKey: string, options: Partial<NativeStackNavigationOptions>) => void;
};
};
/**
* Hook used by composition components to register their options in the composition registry.
*
* Registers options on mount/update via useSafeLayoutEffect, and unregisters on unmount.
* Callers should memoize the options object to avoid unnecessary re-registrations.
*/
export declare function useCompositionOption(options: Partial<NativeStackNavigationOptions>): void;
export {};
//# sourceMappingURL=CompositionOptionsContext.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CompositionOptionsContext.d.ts","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/CompositionOptionsContext.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAGnF,OAAO,KAAK,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAG5E,gBAAgB;AAChB,eAAO,MAAM,kBAAkB,yDAAsD,CAAC;AAEtF,KAAK,cAAc,GACf;IACE,IAAI,EAAE,KAAK,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAC;CAChD,GACD;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAA;CAAE,CAAC;AAExF,gBAAgB;AAChB,wBAAgB,eAAe,CAC7B,KAAK,EAAE,mBAAmB,EAC1B,MAAM,EAAE,cAAc,GACrB,mBAAmB,CAuBrB;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB;;;wBAGD,MAAM,WAAW,OAAO,CAAC,4BAA4B,CAAC;0BAIpD,MAAM,WAAW,OAAO,CAAC,4BAA4B,CAAC;;EAS5F;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,4BAA4B,CAAC,QAiBlF"}

View File

@@ -0,0 +1,75 @@
"use strict";
'use client';
Object.defineProperty(exports, "__esModule", { value: true });
exports.CompositionContext = void 0;
exports.registryReducer = registryReducer;
exports.useCompositionRegistry = useCompositionRegistry;
exports.useCompositionOption = useCompositionOption;
const native_1 = require("@react-navigation/native");
const react_1 = require("react");
const useSafeLayoutEffect_1 = require("../../../views/useSafeLayoutEffect");
/** @internal */
exports.CompositionContext = (0, react_1.createContext)(null);
/** @internal */
function registryReducer(state, action) {
if (action.type === 'set') {
const { routeKey, options } = action;
if (state[routeKey]?.includes(options)) {
return state;
}
return { ...state, [routeKey]: [...(state[routeKey] ?? []), options] };
}
if (action.type === 'unset') {
const { routeKey, options } = action;
const existing = state[routeKey];
const filtered = existing?.filter((o) => o !== options);
if (!existing || filtered?.length === existing.length) {
return state;
}
if (filtered.length === 0) {
const { [routeKey]: _, ...newState } = state;
return newState;
}
return { ...state, [routeKey]: filtered };
}
return state;
}
/**
* Provides the composition registry to descendant composition components.
*
* Uses useReducer with immutable object updates for React Compiler compatibility.
* Each set/unset call produces a new object reference, which the compiler can
* track as a reactive dependency.
*/
function useCompositionRegistry() {
const [registry, dispatch] = (0, react_1.useReducer)(registryReducer, {});
const set = (0, react_1.useCallback)((routeKey, options) => {
dispatch({ type: 'set', routeKey, options });
}, []);
const unset = (0, react_1.useCallback)((routeKey, options) => {
dispatch({ type: 'unset', routeKey, options });
}, []);
const contextValue = (0, react_1.useMemo)(() => ({ set, unset }), [set, unset]);
return { registry, contextValue };
}
/**
* Hook used by composition components to register their options in the composition registry.
*
* Registers options on mount/update via useSafeLayoutEffect, and unregisters on unmount.
* Callers should memoize the options object to avoid unnecessary re-registrations.
*/
function useCompositionOption(options) {
const context = (0, react_1.use)(exports.CompositionContext);
if (!context) {
throw new Error('useCompositionOption must be used within a RouterCompositionOptionsProvider. This is likely a bug in Expo Router.');
}
const route = (0, native_1.useRoute)();
const { set, unset } = context;
(0, useSafeLayoutEffect_1.useSafeLayoutEffect)(() => {
set(route.key, options);
return () => {
unset(route.key, options);
};
}, [route.key, set, unset, options]);
}
//# sourceMappingURL=CompositionOptionsContext.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
export { mergeOptions } from './mergeOptions';
export { CompositionContext, useCompositionRegistry, useCompositionOption, } from './CompositionOptionsContext';
export type { CompositionRegistry, CompositionContextValue } from './types';
//# sourceMappingURL=index.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,6BAA6B,CAAC;AACrC,YAAY,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC"}

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useCompositionOption = exports.useCompositionRegistry = exports.CompositionContext = exports.mergeOptions = void 0;
var mergeOptions_1 = require("./mergeOptions");
Object.defineProperty(exports, "mergeOptions", { enumerable: true, get: function () { return mergeOptions_1.mergeOptions; } });
var CompositionOptionsContext_1 = require("./CompositionOptionsContext");
Object.defineProperty(exports, "CompositionContext", { enumerable: true, get: function () { return CompositionOptionsContext_1.CompositionContext; } });
Object.defineProperty(exports, "useCompositionRegistry", { enumerable: true, get: function () { return CompositionOptionsContext_1.useCompositionRegistry; } });
Object.defineProperty(exports, "useCompositionOption", { enumerable: true, get: function () { return CompositionOptionsContext_1.useCompositionOption; } });
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/index.ts"],"names":[],"mappings":";;;AAAA,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AACrB,yEAIqC;AAHnC,+HAAA,kBAAkB,OAAA;AAClB,mIAAA,sBAAsB,OAAA;AACtB,iIAAA,oBAAoB,OAAA","sourcesContent":["export { mergeOptions } from './mergeOptions';\nexport {\n CompositionContext,\n useCompositionRegistry,\n useCompositionOption,\n} from './CompositionOptionsContext';\nexport type { CompositionRegistry, CompositionContextValue } from './types';\n"]}

View File

@@ -0,0 +1,13 @@
import type { ParamListBase, StackNavigationState } from '@react-navigation/native';
import type { NativeStackDescriptorMap } from '../descriptors-context';
import type { CompositionRegistry } from './types';
/**
* Merges composition component options into navigation descriptors.
*
* For each descriptor:
* 1. If no composition options registered → pass through unchanged
* 2. If route is preloaded AND not focused → skip composition (pass through)
* 3. Otherwise → merge descriptor.options with composition options (composition wins)
*/
export declare function mergeOptions(descriptors: NativeStackDescriptorMap, registry: CompositionRegistry, state: StackNavigationState<ParamListBase>): NativeStackDescriptorMap;
//# sourceMappingURL=mergeOptions.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"mergeOptions.d.ts","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/mergeOptions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAEpF,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEnD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,WAAW,EAAE,wBAAwB,EACrC,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,oBAAoB,CAAC,aAAa,CAAC,GACzC,wBAAwB,CAiC1B"}

View File

@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.mergeOptions = mergeOptions;
/**
* Merges composition component options into navigation descriptors.
*
* For each descriptor:
* 1. If no composition options registered → pass through unchanged
* 2. If route is preloaded AND not focused → skip composition (pass through)
* 3. Otherwise → merge descriptor.options with composition options (composition wins)
*/
function mergeOptions(descriptors, registry, state) {
const result = {};
const focusedKey = state.routes[state.index]?.key;
for (const key in descriptors) {
const descriptor = descriptors[key];
const routeOptions = registry[key];
// No composition options or empty array → pass through
if (!routeOptions || routeOptions.length === 0) {
result[key] = descriptor;
continue;
}
// Check if route is preloaded and not focused → skip composition
const isPreloaded = state.preloadedRoutes?.some((r) => r.key === key) ?? false;
if (isPreloaded && key !== focusedKey) {
result[key] = descriptor;
continue;
}
// Merge: descriptor options as base, composition options override
const mergedOptions = Object.assign({}, descriptor.options, ...routeOptions);
const merged = {
...descriptor,
options: mergedOptions,
};
result[key] = merged;
}
return result;
}
//# sourceMappingURL=mergeOptions.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"mergeOptions.js","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/mergeOptions.ts"],"names":[],"mappings":";;AAaA,oCAqCC;AA7CD;;;;;;;GAOG;AACH,SAAgB,YAAY,CAC1B,WAAqC,EACrC,QAA6B,EAC7B,KAA0C;IAE1C,MAAM,MAAM,GAA6B,EAAE,CAAC;IAC5C,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC;IAElD,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAEnC,uDAAuD;QACvD,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/C,MAAM,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC;YACzB,SAAS;QACX,CAAC;QAED,iEAAiE;QACjE,MAAM,WAAW,GAAG,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC;QAC/E,IAAI,WAAW,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YACtC,MAAM,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC;YACzB,SAAS;QACX,CAAC;QAED,kEAAkE;QAClE,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,EAAE,GAAG,YAAY,CAAC,CAAC;QAE7E,MAAM,MAAM,GAAG;YACb,GAAG,UAAU;YACb,OAAO,EAAE,aAAa;SACvB,CAAC;QAEF,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { ParamListBase, StackNavigationState } from '@react-navigation/native';\n\nimport type { NativeStackDescriptorMap } from '../descriptors-context';\nimport type { CompositionRegistry } from './types';\n\n/**\n * Merges composition component options into navigation descriptors.\n *\n * For each descriptor:\n * 1. If no composition options registered → pass through unchanged\n * 2. If route is preloaded AND not focused → skip composition (pass through)\n * 3. Otherwise → merge descriptor.options with composition options (composition wins)\n */\nexport function mergeOptions(\n descriptors: NativeStackDescriptorMap,\n registry: CompositionRegistry,\n state: StackNavigationState<ParamListBase>\n): NativeStackDescriptorMap {\n const result: NativeStackDescriptorMap = {};\n const focusedKey = state.routes[state.index]?.key;\n\n for (const key in descriptors) {\n const descriptor = descriptors[key];\n const routeOptions = registry[key];\n\n // No composition options or empty array → pass through\n if (!routeOptions || routeOptions.length === 0) {\n result[key] = descriptor;\n continue;\n }\n\n // Check if route is preloaded and not focused → skip composition\n const isPreloaded = state.preloadedRoutes?.some((r) => r.key === key) ?? false;\n if (isPreloaded && key !== focusedKey) {\n result[key] = descriptor;\n continue;\n }\n\n // Merge: descriptor options as base, composition options override\n const mergedOptions = Object.assign({}, descriptor.options, ...routeOptions);\n\n const merged = {\n ...descriptor,\n options: mergedOptions,\n };\n\n result[key] = merged;\n }\n\n return result;\n}\n"]}

View File

@@ -0,0 +1,25 @@
import type { NativeStackNavigationOptions } from '@react-navigation/native-stack';
/**
* Registry mapping route keys to composition component options.
*
* Structure: Record<routeKey, options[]>
*
* Each composition component (Title, BackButton, Header, Toolbar) registers
* its memoized options object. Array order reflects registration order,
* so later registrations override earlier ones during merge.
*
* @internal
*/
export type CompositionRegistry = Record<string, Partial<NativeStackNavigationOptions>[]>;
/** @internal */
export interface CompositionContextValue {
/**
* Register or update options for a composition component.
*/
set(routeKey: string, options: Partial<NativeStackNavigationOptions>): void;
/**
* Remove a composition component's options by reference (should be called on unmount).
*/
unset(routeKey: string, options: Partial<NativeStackNavigationOptions>): void;
}
//# sourceMappingURL=types.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAEnF;;;;;;;;;;GAUG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,4BAA4B,CAAC,EAAE,CAAC,CAAC;AAE1F,gBAAgB;AAChB,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,4BAA4B,CAAC,GAAG,IAAI,CAAC;IAE5E;;OAEG;IACH,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,4BAA4B,CAAC,GAAG,IAAI,CAAC;CAC/E"}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/fork/native-stack/composition-options/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { NativeStackNavigationOptions } from '@react-navigation/native-stack';\n\n/**\n * Registry mapping route keys to composition component options.\n *\n * Structure: Record<routeKey, options[]>\n *\n * Each composition component (Title, BackButton, Header, Toolbar) registers\n * its memoized options object. Array order reflects registration order,\n * so later registrations override earlier ones during merge.\n *\n * @internal\n */\nexport type CompositionRegistry = Record<string, Partial<NativeStackNavigationOptions>[]>;\n\n/** @internal */\nexport interface CompositionContextValue {\n /**\n * Register or update options for a composition component.\n */\n set(routeKey: string, options: Partial<NativeStackNavigationOptions>): void;\n\n /**\n * Remove a composition component's options by reference (should be called on unmount).\n */\n unset(routeKey: string, options: Partial<NativeStackNavigationOptions>): void;\n}\n"]}

View File

@@ -0,0 +1,17 @@
import { type NavigatorTypeBagBase, type ParamListBase, type StackNavigationState, type StaticConfig, type TypedNavigator } from '@react-navigation/native';
import { type NativeStackNavigationEventMap, type NativeStackNavigationOptions, type NativeStackNavigationProp, type NativeStackNavigatorProps } from '@react-navigation/native-stack';
import * as React from 'react';
declare function NativeStackNavigator({ id, initialRouteName, children, layout, screenListeners, screenOptions, screenLayout, UNSTABLE_router, ...rest }: NativeStackNavigatorProps): React.JSX.Element;
export declare function createNativeStackNavigator<const ParamList extends ParamListBase, const NavigatorID extends string | undefined = undefined, const TypeBag extends NavigatorTypeBagBase = {
ParamList: ParamList;
NavigatorID: NavigatorID;
State: StackNavigationState<ParamList>;
ScreenOptions: NativeStackNavigationOptions;
EventMap: NativeStackNavigationEventMap;
NavigationList: {
[RouteName in keyof ParamList]: NativeStackNavigationProp<ParamList, RouteName, NavigatorID>;
};
Navigator: typeof NativeStackNavigator;
}, const Config extends StaticConfig<TypeBag> = StaticConfig<TypeBag>>(config?: Config): TypedNavigator<TypeBag, Config>;
export {};
//# sourceMappingURL=createNativeStackNavigator.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"createNativeStackNavigator.d.ts","sourceRoot":"","sources":["../../../src/fork/native-stack/createNativeStackNavigator.tsx"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAGlB,KAAK,oBAAoB,EAGzB,KAAK,YAAY,EACjB,KAAK,cAAc,EAEpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,6BAA6B,EAClC,KAAK,4BAA4B,EACjC,KAAK,yBAAyB,EAE9B,KAAK,yBAAyB,EAC/B,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAe/B,iBAAS,oBAAoB,CAAC,EAC5B,EAAE,EACF,gBAAgB,EAChB,QAAQ,EACR,MAAM,EACN,eAAe,EACf,aAAa,EACb,YAAY,EACZ,eAAe,EACf,GAAG,IAAI,EACR,EAAE,yBAAyB,qBAwH3B;AAED,wBAAgB,0BAA0B,CACxC,KAAK,CAAC,SAAS,SAAS,aAAa,EACrC,KAAK,CAAC,WAAW,SAAS,MAAM,GAAG,SAAS,GAAG,SAAS,EACxD,KAAK,CAAC,OAAO,SAAS,oBAAoB,GAAG;IAC3C,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,KAAK,EAAE,oBAAoB,CAAC,SAAS,CAAC,CAAC;IACvC,aAAa,EAAE,4BAA4B,CAAC;IAC5C,QAAQ,EAAE,6BAA6B,CAAC;IACxC,cAAc,EAAE;SACb,SAAS,IAAI,MAAM,SAAS,GAAG,yBAAyB,CAAC,SAAS,EAAE,SAAS,EAAE,WAAW,CAAC;KAC7F,CAAC;IACF,SAAS,EAAE,OAAO,oBAAoB,CAAC;CACxC,EACD,KAAK,CAAC,MAAM,SAAS,YAAY,CAAC,OAAO,CAAC,GAAG,YAAY,CAAC,OAAO,CAAC,EAClE,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,CAElD"}

View File

@@ -0,0 +1,143 @@
"use strict";
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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.createNativeStackNavigator = createNativeStackNavigator;
const native_1 = require("@react-navigation/native");
const native_stack_1 = require("@react-navigation/native-stack");
const expo_glass_effect_1 = require("expo-glass-effect");
const React = __importStar(require("react"));
const composition_options_1 = require("./composition-options");
const descriptors_context_1 = require("./descriptors-context");
const usePreviewTransition_1 = require("./usePreviewTransition");
const navigationParams_1 = require("../../navigationParams");
const GLASS = (0, expo_glass_effect_1.isLiquidGlassAvailable)();
function NativeStackNavigator({ id, initialRouteName, children, layout, screenListeners, screenOptions, screenLayout, UNSTABLE_router, ...rest }) {
const { state, describe, descriptors, navigation, NavigationContent } = (0, native_1.useNavigationBuilder)(native_1.StackRouter, {
id,
initialRouteName,
children,
layout,
screenListeners,
screenOptions,
screenLayout,
UNSTABLE_router,
});
React.useEffect(() =>
// @ts-expect-error: there may not be a tab navigator in parent
navigation?.addListener?.('tabPress', (e) => {
const isFocused = navigation.isFocused();
// Run the operation in the next frame so we're sure all listeners have been run
// This is necessary to know if preventDefault() has been called
requestAnimationFrame(() => {
if (state.index > 0 && isFocused && !e.defaultPrevented) {
// When user taps on already focused tab and we're inside the tab,
// reset the stack to replicate native behaviour
// START FORK
// navigation.dispatch({
// ...StackActions.popToTop(),
// target: state.key,
// });
// The popToTop will be automatically triggered on native side for native tabs
if (e.data?.__internalTabsType !== 'native') {
navigation.dispatch({
...native_1.StackActions.popToTop(),
target: state.key,
});
}
// END FORK
}
});
}), [navigation, state.index, state.key]);
// START FORK
const { computedState, computedDescriptors, navigationWrapper } = (0, usePreviewTransition_1.usePreviewTransition)(state, navigation, descriptors, describe);
// Map internal gesture option to React Navigation's gestureEnabled option
// This allows Expo Router to override gesture behavior without affecting user settings
const finalDescriptors = React.useMemo(() => {
let needsNewMap = false;
const result = {};
for (const key of Object.keys(computedDescriptors)) {
const descriptor = computedDescriptors[key];
const options = descriptor.options;
const internalGestureEnabled = options?.[navigationParams_1.INTERNAL_EXPO_ROUTER_GESTURE_ENABLED_OPTION_NAME];
const needsGestureFix = internalGestureEnabled !== undefined;
const needsGlassFix = GLASS && options?.presentation === 'formSheet';
if (needsGestureFix || needsGlassFix) {
needsNewMap = true;
const newOptions = { ...options };
if (needsGestureFix) {
newOptions.gestureEnabled = internalGestureEnabled;
}
if (needsGlassFix) {
newOptions.headerTransparent ??= true;
newOptions.contentStyle ??= { backgroundColor: 'transparent' };
newOptions.headerShadowVisible ??= false;
newOptions.headerLargeTitleShadowVisible ??= false;
}
result[key] = { ...descriptor, options: newOptions };
}
else {
result[key] = descriptor;
}
}
return needsNewMap ? result : computedDescriptors;
}, [computedDescriptors]);
const { registry, contextValue } = (0, composition_options_1.useCompositionRegistry)();
const mergedDescriptors = React.useMemo(() => (0, composition_options_1.mergeOptions)(finalDescriptors, registry, computedState), [finalDescriptors, computedState, registry]);
// END FORK
return (
// START FORK
<descriptors_context_1.DescriptorsContext value={descriptors}>
{/* END FORK */}
<NavigationContent>
<composition_options_1.CompositionContext value={contextValue}>
<native_stack_1.NativeStackView {...rest}
// START FORK
state={computedState} navigation={navigationWrapper} descriptors={mergedDescriptors}
// state={state}
// navigation={navigation}
// descriptors={descriptors}
// END FORK
describe={describe}/>
</composition_options_1.CompositionContext>
</NavigationContent>
{/* START FORK */}
</descriptors_context_1.DescriptorsContext>
// END FORK
);
}
function createNativeStackNavigator(config) {
return (0, native_1.createNavigatorFactory)(NativeStackNavigator)(config);
}
//# sourceMappingURL=createNativeStackNavigator.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
import type { Descriptor, ParamListBase, RouteProp } from '@react-navigation/native';
import type { NativeStackNavigationOptions, NativeStackNavigationProp } from '@react-navigation/native-stack';
export type NativeStackDescriptor = Descriptor<NativeStackNavigationOptions, NativeStackNavigationProp<ParamListBase>, RouteProp<ParamListBase>>;
export type NativeStackDescriptorMap = {
[key: string]: NativeStackDescriptor;
};
export declare const DescriptorsContext: import("react").Context<NativeStackDescriptorMap>;
//# sourceMappingURL=descriptors-context.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"descriptors-context.d.ts","sourceRoot":"","sources":["../../../src/fork/native-stack/descriptors-context.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrF,OAAO,KAAK,EACV,4BAA4B,EAC5B,yBAAyB,EAC1B,MAAM,gCAAgC,CAAC;AAIxC,MAAM,MAAM,qBAAqB,GAAG,UAAU,CAC5C,4BAA4B,EAC5B,yBAAyB,CAAC,aAAa,CAAC,EACxC,SAAS,CAAC,aAAa,CAAC,CACzB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,CAAC,GAAG,EAAE,MAAM,GAAG,qBAAqB,CAAC;CACtC,CAAC;AAEF,eAAO,MAAM,kBAAkB,mDAA8C,CAAC"}

View File

@@ -0,0 +1,6 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DescriptorsContext = void 0;
const react_1 = require("react");
exports.DescriptorsContext = (0, react_1.createContext)({});
//# sourceMappingURL=descriptors-context.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"descriptors-context.js","sourceRoot":"","sources":["../../../src/fork/native-stack/descriptors-context.tsx"],"names":[],"mappings":";;;AAKA,iCAAsC;AAazB,QAAA,kBAAkB,GAAG,IAAA,qBAAa,EAA2B,EAAE,CAAC,CAAC","sourcesContent":["import type { Descriptor, ParamListBase, RouteProp } from '@react-navigation/native';\nimport type {\n NativeStackNavigationOptions,\n NativeStackNavigationProp,\n} from '@react-navigation/native-stack';\nimport { createContext } from 'react';\n\n// Copied from @react-navigation/native\nexport type NativeStackDescriptor = Descriptor<\n NativeStackNavigationOptions,\n NativeStackNavigationProp<ParamListBase>,\n RouteProp<ParamListBase>\n>;\n\nexport type NativeStackDescriptorMap = {\n [key: string]: NativeStackDescriptor;\n};\n\nexport const DescriptorsContext = createContext<NativeStackDescriptorMap>({});\n"]}

View File

@@ -0,0 +1,21 @@
import type { ParamListBase, StackNavigationState } from '@react-navigation/native';
import type { NativeStackDescriptor, NativeStackDescriptorMap } from './descriptors-context';
/** Mirrors the `describe` function returned by `useNavigationBuilder` */
type DescribeFn = (route: StackNavigationState<ParamListBase>['preloadedRoutes'][number], placeholder: boolean) => NativeStackDescriptor;
/**
* Manages the preview transition state for link previews.
*
* Tracks when a preloaded screen is transitioning on the native side (after
* the preview is committed) but before React Navigation state is updated.
* During this window, the hook synthesizes state/descriptors to keep native
* and JS state in sync.
*/
export declare function usePreviewTransition<TNavigation extends {
emit: (...args: any[]) => any;
}>(state: StackNavigationState<ParamListBase>, navigation: TNavigation, descriptors: NativeStackDescriptorMap, describe: DescribeFn): {
computedState: StackNavigationState<ParamListBase>;
computedDescriptors: NativeStackDescriptorMap;
navigationWrapper: TNavigation;
};
export {};
//# sourceMappingURL=usePreviewTransition.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"usePreviewTransition.d.ts","sourceRoot":"","sources":["../../../src/fork/native-stack/usePreviewTransition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAGpF,OAAO,KAAK,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAG7F,yEAAyE;AACzE,KAAK,UAAU,GAAG,CAChB,KAAK,EAAE,oBAAoB,CAAC,aAAa,CAAC,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,EACrE,WAAW,EAAE,OAAO,KACjB,qBAAqB,CAAC;AAE3B;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,SAAS;IAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAA;CAAE,EACxF,KAAK,EAAE,oBAAoB,CAAC,aAAa,CAAC,EAC1C,UAAU,EAAE,WAAW,EACvB,WAAW,EAAE,wBAAwB,EACrC,QAAQ,EAAE,UAAU;;;;EAuFrB"}

View File

@@ -0,0 +1,119 @@
"use strict";
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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePreviewTransition = usePreviewTransition;
const React = __importStar(require("react"));
const LinkPreviewContext_1 = require("../../link/preview/LinkPreviewContext");
/**
* Manages the preview transition state for link previews.
*
* Tracks when a preloaded screen is transitioning on the native side (after
* the preview is committed) but before React Navigation state is updated.
* During this window, the hook synthesizes state/descriptors to keep native
* and JS state in sync.
*/
function usePreviewTransition(state, navigation, descriptors, describe) {
const { openPreviewKey, setOpenPreviewKey } = (0, LinkPreviewContext_1.useLinkPreviewContext)();
// Track the preview screen currently transitioning on the native side
const [previewTransitioningScreenId, setPreviewTransitioningScreenId] = React.useState();
React.useEffect(() => {
if (previewTransitioningScreenId) {
// State was updated after the preview transition
if (state.routes.some((route) => route.key === previewTransitioningScreenId)) {
// No longer need to track the preview transitioning screen
setPreviewTransitioningScreenId(undefined);
}
}
}, [state, previewTransitioningScreenId]);
const navigationWrapper = React.useMemo(() => {
if (openPreviewKey) {
const emit = (...args) => {
const { target, type, data } = args[0];
if (target === openPreviewKey && data && 'closing' in data && !data.closing) {
// onWillAppear
if (type === 'transitionStart') {
// The screen from preview will appear, so we need to start tracking it
setPreviewTransitioningScreenId(openPreviewKey);
}
// onAppear
else if (type === 'transitionEnd') {
// The screen from preview appeared.
// We can now restore the stack animation
setOpenPreviewKey(undefined);
}
}
return navigation.emit(...args);
};
return {
...navigation,
emit,
};
}
return navigation;
}, [navigation, openPreviewKey, setOpenPreviewKey]);
const { computedState, computedDescriptors } = React.useMemo(() => {
// The preview screen was pushed on the native side, but react-navigation state was not updated yet
if (previewTransitioningScreenId) {
const preloadedRoute = state.preloadedRoutes.find((route) => route.key === previewTransitioningScreenId);
if (preloadedRoute) {
const newState = {
...state,
// On native side the screen is already pushed, so we need to update the state
preloadedRoutes: state.preloadedRoutes.filter((route) => route.key !== previewTransitioningScreenId),
routes: [...state.routes, preloadedRoute],
index: state.index + 1,
};
const newDescriptors = previewTransitioningScreenId in descriptors
? descriptors
: {
...descriptors,
// We need to add the descriptor. For react-navigation this is still preloaded screen
// Replicating the logic from https://github.com/react-navigation/react-navigation/blob/eaf1100ac7d99cb93ba11a999549dd0752809a78/packages/native-stack/src/views/NativeStackView.native.tsx#L489
[previewTransitioningScreenId]: describe(preloadedRoute, true),
};
return {
computedState: newState,
computedDescriptors: newDescriptors,
};
}
}
return {
computedState: state,
computedDescriptors: descriptors,
};
}, [state, previewTransitioningScreenId, describe, descriptors]);
return { computedState, computedDescriptors, navigationWrapper };
}
//# sourceMappingURL=usePreviewTransition.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
import type { NavigationContainerRef, ParamListBase } from '@react-navigation/native';
export declare function useBackButton(_: React.RefObject<NavigationContainerRef<ParamListBase> | null>): void;
//# sourceMappingURL=useBackButton.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useBackButton.d.ts","sourceRoot":"","sources":["../../src/fork/useBackButton.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEtF,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,sBAAsB,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,QAG7F"}

8
node_modules/expo-router/build/fork/useBackButton.js generated vendored Normal file
View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useBackButton = useBackButton;
function useBackButton(_) {
// No-op
// BackHandler is not available on web
}
//# sourceMappingURL=useBackButton.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useBackButton.js","sourceRoot":"","sources":["../../src/fork/useBackButton.ts"],"names":[],"mappings":";;AAKA,sCAGC;AAHD,SAAgB,aAAa,CAAC,CAAgE;IAC5F,QAAQ;IACR,sCAAsC;AACxC,CAAC","sourcesContent":["/*\n * This file is unchanged, except for removing eslint comments\n */\nimport type { NavigationContainerRef, ParamListBase } from '@react-navigation/native';\n\nexport function useBackButton(_: React.RefObject<NavigationContainerRef<ParamListBase> | null>) {\n // No-op\n // BackHandler is not available on web\n}\n"]}

View File

@@ -0,0 +1,7 @@
/**
* This file is unchanged, except for removing eslint comments
*/
import type { NavigationContainerRef, ParamListBase } from '@react-navigation/native';
import * as React from 'react';
export declare function useBackButton(ref: React.RefObject<NavigationContainerRef<ParamListBase>>): void;
//# sourceMappingURL=useBackButton.native.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useBackButton.native.d.ts","sourceRoot":"","sources":["../../src/fork/useBackButton.native.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACtF,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,wBAAgB,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,sBAAsB,CAAC,aAAa,CAAC,CAAC,QAoBxF"}

View File

@@ -0,0 +1,55 @@
"use strict";
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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.useBackButton = useBackButton;
const React = __importStar(require("react"));
const react_native_1 = require("react-native");
function useBackButton(ref) {
React.useEffect(() => {
const subscription = react_native_1.BackHandler.addEventListener('hardwareBackPress', () => {
const navigation = ref.current;
if (navigation == null) {
return false;
}
if (navigation.canGoBack()) {
navigation.goBack();
return true;
}
return false;
});
return () => subscription.remove();
}, [ref]);
}
//# sourceMappingURL=useBackButton.native.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useBackButton.native.js","sourceRoot":"","sources":["../../src/fork/useBackButton.native.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,sCAoBC;AAvBD,6CAA+B;AAC/B,+CAA2C;AAE3C,SAAgB,aAAa,CAAC,GAA2D;IACvF,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,MAAM,YAAY,GAAG,0BAAW,CAAC,gBAAgB,CAAC,mBAAmB,EAAE,GAAG,EAAE;YAC1E,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC;YAE/B,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;gBACvB,OAAO,KAAK,CAAC;YACf,CAAC;YAED,IAAI,UAAU,CAAC,SAAS,EAAE,EAAE,CAAC;gBAC3B,UAAU,CAAC,MAAM,EAAE,CAAC;gBAEpB,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;IACrC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;AACZ,CAAC","sourcesContent":["/**\n * This file is unchanged, except for removing eslint comments\n */\nimport type { NavigationContainerRef, ParamListBase } from '@react-navigation/native';\nimport * as React from 'react';\nimport { BackHandler } from 'react-native';\n\nexport function useBackButton(ref: React.RefObject<NavigationContainerRef<ParamListBase>>) {\n React.useEffect(() => {\n const subscription = BackHandler.addEventListener('hardwareBackPress', () => {\n const navigation = ref.current;\n\n if (navigation == null) {\n return false;\n }\n\n if (navigation.canGoBack()) {\n navigation.goBack();\n\n return true;\n }\n\n return false;\n });\n\n return () => subscription.remove();\n }, [ref]);\n}\n"]}

View File

@@ -0,0 +1,7 @@
import type { DocumentTitleOptions, NavigationContainerRef, ParamListBase } from '@react-navigation/native';
import * as React from 'react';
/**
* Set the document title for the active screen
*/
export declare function useDocumentTitle(ref: React.RefObject<NavigationContainerRef<ParamListBase> | null>, { enabled, formatter, }?: DocumentTitleOptions): void;
//# sourceMappingURL=useDocumentTitle.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useDocumentTitle.d.ts","sourceRoot":"","sources":["../../src/fork/useDocumentTitle.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,oBAAoB,EACpB,sBAAsB,EACtB,aAAa,EACd,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,sBAAsB,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,EAClE,EACE,OAAc,EACd,SAA6D,GAC9D,GAAE,oBAAyB,QAqB7B"}

View File

@@ -0,0 +1,58 @@
"use strict";
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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.useDocumentTitle = useDocumentTitle;
const React = __importStar(require("react"));
// import type { DocumentTitleOptions } from './types';
/**
* Set the document title for the active screen
*/
function useDocumentTitle(ref, { enabled = true, formatter = (options, route) => options?.title ?? route?.name, } = {}) {
React.useEffect(() => {
if (!enabled) {
return;
}
const navigation = ref.current;
if (navigation) {
const title = formatter(navigation.getCurrentOptions(), navigation.getCurrentRoute());
document.title = title;
}
return navigation?.addListener('options', (e) => {
const title = formatter(e.data.options, navigation?.getCurrentRoute());
document.title = title;
});
});
}
//# sourceMappingURL=useDocumentTitle.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useDocumentTitle.js","sourceRoot":"","sources":["../../src/fork/useDocumentTitle.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,4CA0BC;AAjCD,6CAA+B;AAE/B,uDAAuD;AAEvD;;GAEG;AACH,SAAgB,gBAAgB,CAC9B,GAAkE,EAClE,EACE,OAAO,GAAG,IAAI,EACd,SAAS,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,EAAE,KAAK,IAAI,KAAK,EAAE,IAAI,MACrC,EAAE;IAE5B,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QAED,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC;QAE/B,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,CAAC,iBAAiB,EAAE,EAAE,UAAU,CAAC,eAAe,EAAE,CAAC,CAAC;YAEtF,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC;QACzB,CAAC;QAED,OAAO,UAAU,EAAE,WAAW,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE;YAC9C,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,CAAC,CAAC;YAEvE,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/*\n * This file is unchanged, except for fixing imports and removing eslint comments\n */\nimport type {\n DocumentTitleOptions,\n NavigationContainerRef,\n ParamListBase,\n} from '@react-navigation/native';\nimport * as React from 'react';\n\n// import type { DocumentTitleOptions } from './types';\n\n/**\n * Set the document title for the active screen\n */\nexport function useDocumentTitle(\n ref: React.RefObject<NavigationContainerRef<ParamListBase> | null>,\n {\n enabled = true,\n formatter = (options, route) => options?.title ?? route?.name,\n }: DocumentTitleOptions = {}\n) {\n React.useEffect(() => {\n if (!enabled) {\n return;\n }\n\n const navigation = ref.current;\n\n if (navigation) {\n const title = formatter(navigation.getCurrentOptions(), navigation.getCurrentRoute());\n\n document.title = title;\n }\n\n return navigation?.addListener('options', (e) => {\n const title = formatter(e.data.options, navigation?.getCurrentRoute());\n\n document.title = title;\n });\n });\n}\n"]}

View File

@@ -0,0 +1,2 @@
export declare function useDocumentTitle(): void;
//# sourceMappingURL=useDocumentTitle.native.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useDocumentTitle.native.d.ts","sourceRoot":"","sources":["../../src/fork/useDocumentTitle.native.ts"],"names":[],"mappings":"AAGA,wBAAgB,gBAAgB,SAE/B"}

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useDocumentTitle = useDocumentTitle;
/*
* This file is unchanged, except for removing eslint comments
*/
function useDocumentTitle() {
// Noop for native platforms
}
//# sourceMappingURL=useDocumentTitle.native.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useDocumentTitle.native.js","sourceRoot":"","sources":["../../src/fork/useDocumentTitle.native.ts"],"names":[],"mappings":";;AAGA,4CAEC;AALD;;GAEG;AACH,SAAgB,gBAAgB;IAC9B,4BAA4B;AAC9B,CAAC","sourcesContent":["/*\n * This file is unchanged, except for removing eslint comments\n */\nexport function useDocumentTitle() {\n // Noop for native platforms\n}\n"]}

14
node_modules/expo-router/build/fork/useLinking.d.ts generated vendored Normal file
View File

@@ -0,0 +1,14 @@
import { LinkingOptions, getStateFromPath as getStateFromPathDefault, type NavigationContainerRef, type ParamListBase } from '@react-navigation/native';
import * as React from 'react';
type ResultState = ReturnType<typeof getStateFromPathDefault>;
/**
* Run async function in series as it's called.
*/
export declare const series: (cb: () => Promise<void>) => () => void;
type Options = LinkingOptions<ParamListBase>;
export declare function useLinking(ref: React.RefObject<NavigationContainerRef<ParamListBase> | null>, { enabled, config, getStateFromPath, getPathFromState, getActionFromState, }: Options, onUnhandledLinking: (lastUnhandledLining: string | undefined) => void): {
getInitialState: () => PromiseLike<ResultState | undefined>;
};
export declare function getInitialURLWithTimeout(): string | null | Promise<string | null>;
export {};
//# sourceMappingURL=useLinking.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useLinking.d.ts","sourceRoot":"","sources":["../../src/fork/useLinking.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EAId,gBAAgB,IAAI,uBAAuB,EAC3C,KAAK,sBAAsB,EAE3B,KAAK,aAAa,EAEnB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAQ/B,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;AA0C9D;;GAEG;AACH,eAAO,MAAM,MAAM,GAAI,IAAI,MAAM,OAAO,CAAC,IAAI,CAAC,eAM7C,CAAC;AAIF,KAAK,OAAO,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC;AAE7C,wBAAgB,UAAU,CACxB,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,sBAAsB,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,EAClE,EACE,OAAc,EACd,MAAM,EACN,gBAA0C,EAC1C,gBAA0C,EAC1C,kBAA8C,GAC/C,EAAE,OAAO,EACV,kBAAkB,EAAE,CAAC,mBAAmB,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI;2BA4GhD,WAAW,CAAC,WAAW,GAAG,SAAS,CAAC;EA8R1D;AAED,wBAAgB,wBAAwB,IAAI,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAEjF"}

411
node_modules/expo-router/build/fork/useLinking.js generated vendored Normal file
View File

@@ -0,0 +1,411 @@
"use strict";
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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__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 });
exports.series = void 0;
exports.useLinking = useLinking;
exports.getInitialURLWithTimeout = getInitialURLWithTimeout;
const native_1 = require("@react-navigation/native");
const fast_deep_equal_1 = __importDefault(require("fast-deep-equal"));
const React = __importStar(require("react"));
const createMemoryHistory_1 = require("./createMemoryHistory");
const getPathFromState_1 = require("./getPathFromState");
const serverLocationContext_1 = require("../global-state/serverLocationContext");
const storeContext_1 = require("../global-state/storeContext");
const utils_1 = require("../global-state/utils");
/**
* 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;
const linkingHandlers = [];
function useLinking(ref, { enabled = true, config, getStateFromPath = native_1.getStateFromPath, getPathFromState = native_1.getPathFromState, getActionFromState = native_1.getActionFromState, }, onUnhandledLinking) {
const independent = (0, native_1.useNavigationIndependentTree)();
const store = (0, storeContext_1.useExpoRouterStore)();
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.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;
});
const validateRoutesNotExistInRootState = React.useCallback((state) => {
// START FORK
// Instead of using the rootState, we use INTERNAL_SLOT_NAME, which is the only route in the root navigator in Expo Router
// const navigation = ref.current;
// const rootState = navigation?.getRootState();
const routeNames = (0, utils_1.getRootStackRouteNames)();
// END FORK
// Make sure that the routes in the state exist in the root navigator
// Otherwise there's an error in the linking configuration
// START FORK
// return state?.routes.some((r) => !rootState?.routeNames?.includes(r.name));
return state?.routes.some((r) => !routeNames.includes(r.name));
// END FORK
}, [ref]);
const server = React.use(serverLocationContext_1.ServerContext);
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);
}
// If the link were handled, it gets cleared in NavigationContainer
onUnhandledLinking(path);
}
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 + location.hash;
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) {
// If the link were handled, it gets cleared in NavigationContainer
onUnhandledLinking(path);
// Make sure that the routes in the state exist in the root navigator
// Otherwise there's an error in the linking configuration
if (validateRoutesNotExistInRootState(state)) {
return;
}
if (index > previousIndex ||
/* START FORK
*
* This is a workaround for React Navigation's handling of hashes (it doesn't handle them)
* When you click on <a href="#hash">, the browser will first fire a popstate event
* and this callback will be called.
*
* From React Navigation's perspective, it's treating the new hash change like a back/forward
* button press, so it thinks it should reset the state. When we should
* be to be pushing the new state
*
* Our fix is to check if the index is the same as the previous index
* and if the incoming path is the same as the old path but with the hash added,
* then treat it as a push instead of a reset
*
* This also works for subsequent hash changes, as internally RN
* doesn't store the hash in the history state.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event#when_popstate_is_sent
*/
(index === previousIndex && (!record || `${record?.path}${location.hash}` === path))
// END FORK
) {
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, onUnhandledLinking, ref, validateRoutesNotExistInRootState]);
React.useEffect(() => {
if (!enabled) {
return;
}
const getPathForRoute = (route, state) => {
let path;
// 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, native_1.findFocusedRoute)(stateForPath);
if (focusedRoute &&
focusedRoute.name === route.name &&
(0, fast_deep_equal_1.default)({ ...focusedRoute.params }, { ...route.params })) {
// START FORK - Ensure paths coming from events (e.g refresh) have the base URL
// path = route.path;
path = (0, getPathFromState_1.appendBaseUrl)(route.path);
// END FORK
}
}
}
if (path == null) {
path = getPathFromStateRef.current(state, configRef.current);
}
// START FORK - ExpoRouter manually handles hashes. This code is intentionally removed
// const previousRoute = previousStateRef.current
// ? findFocusedRoute(previousStateRef.current)
// : undefined;
// Preserve the hash if the route didn't change
// if (
// previousRoute &&
// route &&
// 'key' in previousRoute &&
// 'key' in route &&
// previousRoute.key === route.key
// ) {
// path = path + location.hash;
// }
// END FORK
return path;
};
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
// START FORK
// Instead of using the rootState (which might be stale) we should use the focused state
// const state = ref.current.getRootState();
const rootState = ref.current.getRootState();
const state = store.state;
// END FORK
if (state) {
const route = (0, native_1.findFocusedRoute)(state);
const path = getPathForRoute(route, state);
if (previousStateRef.current === undefined) {
// START FORK
// previousStateRef.current = state;
previousStateRef.current = rootState;
// END FORK
}
history.replace({ path, state });
}
}
const onStateChange = async () => {
const navigation = ref.current;
if (!navigation || !enabled) {
return;
}
const previousState = previousStateRef.current;
// START FORK
// Instead of using the rootState (which might be stale) we should use the focused state
// const state = navigation.getRootState();
const rootState = navigation.getRootState();
const state = store.state;
// END FORK
// root state may not available, for example when root navigators switch inside the container
if (!state) {
return;
}
const pendingPath = pendingPopStatePathRef.current;
const route = (0, native_1.findFocusedRoute)(state);
const path = getPathForRoute(route, state);
// START FORK
// previousStateRef.current = state;
previousStateRef.current = rootState;
// END FORK
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 {
// 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,
};
}
function getInitialURLWithTimeout() {
return typeof window === 'undefined' ? '' : window.location.href;
}
//# sourceMappingURL=useLinking.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,34 @@
import { type NavigationContainerRef, type ParamListBase } from '@react-navigation/native';
import { LinkingOptions } from '@react-navigation/native';
import * as React from 'react';
type Options = LinkingOptions<ParamListBase>;
export declare function useLinking(ref: React.RefObject<NavigationContainerRef<ParamListBase>>, { enabled, prefixes, filter, config, getInitialURL, subscribe, getStateFromPath, getActionFromState, }: Options, onUnhandledLinking: (lastUnhandledLining: string | undefined) => void): {
getInitialState: () => PromiseLike<(Partial<Omit<Readonly<{
key: string;
index: number;
routeNames: string[];
history?: unknown[];
routes: import("@react-navigation/native").NavigationRoute<ParamListBase, string>[];
type: string;
stale: false;
}>, "stale" | "routes">> & Readonly<{
stale?: true;
routes: import("@react-navigation/native").PartialRoute<import("@react-navigation/native").Route<string, object | undefined>>[];
}> & {
state?: Partial<Omit<Readonly<{
key: string;
index: number;
routeNames: string[];
history?: unknown[];
routes: import("@react-navigation/native").NavigationRoute<ParamListBase, string>[];
type: string;
stale: false;
}>, "stale" | "routes">> & Readonly<{
stale?: true;
routes: import("@react-navigation/native").PartialRoute<import("@react-navigation/native").Route<string, object | undefined>>[];
}> & /*elided*/ any;
}) | undefined>;
};
export declare function getInitialURLWithTimeout(): string | null | Promise<string | null>;
export {};
//# sourceMappingURL=useLinking.native.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useLinking.native.d.ts","sourceRoot":"","sources":["../../src/fork/useLinking.native.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,sBAAsB,EAC3B,KAAK,aAAa,EAEnB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,KAAK,OAAO,GAAG,cAAc,CAAC,aAAa,CAAC,CAAC;AAI7C,wBAAgB,UAAU,CACxB,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,sBAAsB,CAAC,aAAa,CAAC,CAAC,EAC3D,EACE,OAAc,EACd,QAAQ,EACR,MAAM,EACN,MAAM,EACN,aAAgD,EAChD,SAmBC,EACD,gBAA0C,EAC1C,kBAA8C,GAC/C,EAAE,OAAO,EACV,kBAAkB,EAAE,CAAC,mBAAmB,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;EA8JtE;AAED,wBAAgB,wBAAwB,IAAI,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiBjF"}

View File

@@ -0,0 +1,200 @@
"use strict";
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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.useLinking = useLinking;
exports.getInitialURLWithTimeout = getInitialURLWithTimeout;
const native_1 = require("@react-navigation/native");
const ExpoLinking = __importStar(require("expo-linking"));
const React = __importStar(require("react"));
const react_native_1 = require("react-native");
const extractPathFromURL_1 = require("./extractPathFromURL");
const linkingHandlers = [];
function useLinking(ref, { enabled = true, prefixes, filter, config, getInitialURL = () => getInitialURLWithTimeout(), subscribe = (listener) => {
const callback = ({ url }) => listener(url);
const subscription = react_native_1.Linking.addEventListener('url', callback);
// Storing this in a local variable stops Jest from complaining about import after teardown
// @ts-expect-error: removeEventListener is not present in newer RN versions
const removeEventListener = react_native_1.Linking.removeEventListener?.bind(react_native_1.Linking);
return () => {
// https://github.com/facebook/react-native/commit/6d1aca806cee86ad76de771ed3a1cc62982ebcd7
if (subscription?.remove) {
subscription.remove();
}
else {
removeEventListener?.('url', callback);
}
};
}, getStateFromPath = native_1.getStateFromPath, getActionFromState = native_1.getActionFromState, }, onUnhandledLinking) {
const independent = (0, native_1.useNavigationIndependentTree)();
React.useEffect(() => {
if (process.env.NODE_ENV === 'production') {
return undefined;
}
if (independent) {
return undefined;
}
if (enabled !== false && linkingHandlers.length && process.env.EXPO_OS !== 'android') {
if (linkingHandlers.length > 1) {
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]);
// 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 prefixesRef = React.useRef(prefixes);
const filterRef = React.useRef(filter);
const configRef = React.useRef(config);
const getInitialURLRef = React.useRef(getInitialURL);
const getStateFromPathRef = React.useRef(getStateFromPath);
const getActionFromStateRef = React.useRef(getActionFromState);
React.useEffect(() => {
enabledRef.current = enabled;
prefixesRef.current = prefixes;
filterRef.current = filter;
configRef.current = config;
getInitialURLRef.current = getInitialURL;
getStateFromPathRef.current = getStateFromPath;
getActionFromStateRef.current = getActionFromState;
});
const getStateFromURL = React.useCallback((url) => {
if (!url || (filterRef.current && !filterRef.current(url))) {
return undefined;
}
const path = (0, extractPathFromURL_1.extractExpoPathFromURL)(prefixesRef.current, url);
return path !== undefined ? getStateFromPathRef.current(path, configRef.current) : undefined;
}, []);
const getInitialState = React.useCallback(() => {
let state;
if (enabledRef.current) {
const url = getInitialURLRef.current();
if (url != null) {
if (typeof url !== 'string') {
return url.then((url) => {
const state = getStateFromURL(url);
if (typeof url === 'string') {
// If the link were handled, it gets cleared in NavigationContainer
onUnhandledLinking((0, extractPathFromURL_1.extractExpoPathFromURL)(prefixes, url));
}
return state;
});
}
else {
onUnhandledLinking((0, extractPathFromURL_1.extractExpoPathFromURL)(prefixes, url));
}
}
state = getStateFromURL(url);
}
const thenable = {
then(onfulfilled) {
return Promise.resolve(onfulfilled ? onfulfilled(state) : state);
},
catch() {
return thenable;
},
};
return thenable;
}, [getStateFromURL, onUnhandledLinking, prefixes]);
React.useEffect(() => {
const listener = (url) => {
if (!enabled) {
return;
}
const navigation = ref.current;
const state = navigation ? getStateFromURL(url) : undefined;
if (navigation && state) {
// If the link were handled, it gets cleared in NavigationContainer
onUnhandledLinking((0, extractPathFromURL_1.extractExpoPathFromURL)(prefixes, url));
const rootState = navigation.getRootState();
if (state.routes.some((r) => !rootState?.routeNames.includes(r.name))) {
return;
}
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 '${url}': ${typeof e === 'object' && e != null && 'message' in e ? e.message : e}`);
}
}
else {
navigation.resetRoot(state);
}
}
};
return subscribe(listener);
}, [enabled, getStateFromURL, onUnhandledLinking, prefixes, ref, subscribe]);
return {
getInitialState,
};
}
function getInitialURLWithTimeout() {
if (typeof window === 'undefined') {
return '';
}
else if (react_native_1.Platform.OS === 'ios') {
// Use the new Expo API for iOS. This has better support for App Clips and handoff.
return ExpoLinking.getLinkingURL();
}
return Promise.race([
// TODO: Phase this out in favor of expo-linking on Android.
react_native_1.Linking.getInitialURL(),
new Promise((resolve) =>
// Timeout in 150ms if `getInitialState` doesn't resolve
// Workaround for https://github.com/facebook/react-native/issues/25675
setTimeout(() => resolve(null), 150)),
]);
}
//# sourceMappingURL=useLinking.native.js.map

File diff suppressed because one or more lines are too long

2
node_modules/expo-router/build/fork/useThenable.d.ts generated vendored Normal file
View File

@@ -0,0 +1,2 @@
export declare function useThenable<T>(create: () => PromiseLike<T>): [boolean, T | undefined];
//# sourceMappingURL=useThenable.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useThenable.d.ts","sourceRoot":"","sources":["../../src/fork/useThenable.ts"],"names":[],"mappings":"AAKA,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,4BAsC1D"}

72
node_modules/expo-router/build/fork/useThenable.js generated vendored Normal file
View File

@@ -0,0 +1,72 @@
"use strict";
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 () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.useThenable = useThenable;
/*
* This file is unchanged, except for moving eslint comments
*/
const React = __importStar(require("react"));
function useThenable(create) {
const [promise] = React.useState(create);
let initialState = [false, undefined];
// Check if our thenable is synchronous
promise.then((result) => {
initialState = [true, result];
});
const [state, setState] = React.useState(initialState);
const [resolved] = state;
React.useEffect(() => {
let cancelled = false;
const resolve = async () => {
let result;
try {
result = await promise;
}
finally {
if (!cancelled) {
setState([true, result]);
}
}
};
if (!resolved) {
resolve();
}
return () => {
cancelled = true;
};
}, [promise, resolved]);
return state;
}
//# sourceMappingURL=useThenable.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"useThenable.js","sourceRoot":"","sources":["../../src/fork/useThenable.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAKA,kCAsCC;AA3CD;;GAEG;AACH,6CAA+B;AAE/B,SAAgB,WAAW,CAAI,MAA4B;IACzD,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEzC,IAAI,YAAY,GAA6B,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAEhE,uCAAuC;IACvC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;QACtB,YAAY,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IACvD,MAAM,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC;IAEzB,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE;QACnB,IAAI,SAAS,GAAG,KAAK,CAAC;QAEtB,MAAM,OAAO,GAAG,KAAK,IAAI,EAAE;YACzB,IAAI,MAAM,CAAC;YAEX,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,OAAO,CAAC;YACzB,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,QAAQ,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEF,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC;IAExB,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["/*\n * This file is unchanged, except for moving eslint comments\n */\nimport * as React from 'react';\n\nexport function useThenable<T>(create: () => PromiseLike<T>) {\n const [promise] = React.useState(create);\n\n let initialState: [boolean, T | undefined] = [false, undefined];\n\n // Check if our thenable is synchronous\n promise.then((result) => {\n initialState = [true, result];\n });\n\n const [state, setState] = React.useState(initialState);\n const [resolved] = state;\n\n React.useEffect(() => {\n let cancelled = false;\n\n const resolve = async () => {\n let result;\n\n try {\n result = await promise;\n } finally {\n if (!cancelled) {\n setState([true, result]);\n }\n }\n };\n\n if (!resolved) {\n resolve();\n }\n\n return () => {\n cancelled = true;\n };\n }, [promise, resolved]);\n\n return state;\n}\n"]}

View File

@@ -0,0 +1,2 @@
export default function validatePathConfig(config: any, root?: boolean): void;
//# sourceMappingURL=validatePathConfig.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"validatePathConfig.d.ts","sourceRoot":"","sources":["../../src/fork/validatePathConfig.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,UAAO,QA0BlE"}

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = validatePathConfig;
const formatToList = (items) => items.map((key) => `- ${key}`).join('\n');
function validatePathConfig(config, root = true) {
const validKeys = ['initialRouteName', 'screens', '_route'];
if (!root) {
validKeys.push('path', 'exact', 'stringify', 'parse');
}
const invalidKeys = Object.keys(config).filter((key) => !validKeys.includes(key));
if (invalidKeys.length) {
throw new Error(`Found invalid properties in the configuration:\n${formatToList(invalidKeys)}\n\nDid you forget to specify them under a 'screens' property?\n\nYou can only specify the following properties:\n${formatToList(validKeys)}\n\nSee https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration.`);
}
if (config.screens) {
Object.entries(config.screens).forEach(([_, value]) => {
if (typeof value !== 'string') {
validatePathConfig(value, false);
}
});
}
}
//# sourceMappingURL=validatePathConfig.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"validatePathConfig.js","sourceRoot":"","sources":["../../src/fork/validatePathConfig.ts"],"names":[],"mappings":";;AAEA,qCA0BC;AA5BD,MAAM,YAAY,GAAG,CAAC,KAAe,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEpF,SAAwB,kBAAkB,CAAC,MAAW,EAAE,IAAI,GAAG,IAAI;IACjE,MAAM,SAAS,GAAG,CAAC,kBAAkB,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;IAE5D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;IAElF,IAAI,WAAW,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,mDAAmD,YAAY,CAC7D,WAAW,CACZ,qHAAqH,YAAY,CAChI,SAAS,CACV,wHAAwH,CAC1H,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE;YACpD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YACnC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC","sourcesContent":["const formatToList = (items: string[]) => items.map((key) => `- ${key}`).join('\\n');\n\nexport default function validatePathConfig(config: any, root = true) {\n const validKeys = ['initialRouteName', 'screens', '_route'];\n\n if (!root) {\n validKeys.push('path', 'exact', 'stringify', 'parse');\n }\n\n const invalidKeys = Object.keys(config).filter((key) => !validKeys.includes(key));\n\n if (invalidKeys.length) {\n throw new Error(\n `Found invalid properties in the configuration:\\n${formatToList(\n invalidKeys\n )}\\n\\nDid you forget to specify them under a 'screens' property?\\n\\nYou can only specify the following properties:\\n${formatToList(\n validKeys\n )}\\n\\nSee https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration.`\n );\n }\n\n if (config.screens) {\n Object.entries(config.screens).forEach(([_, value]) => {\n if (typeof value !== 'string') {\n validatePathConfig(value, false);\n }\n });\n }\n}\n"]}