UNPKG

@redshank/native-router

Version:

@redshank/native-router is a file-based router for React Native CLI

1,666 lines (1,482 loc) 108 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var native = require('@react-navigation/native'); var reactNative = require('react-native'); var React = require('react'); var jsxDevRuntime = require('react/jsx-dev-runtime'); require('react/jsx-runtime'); var URL = require('url-parse'); var nativeStack = require('@react-navigation/native-stack'); var bottomTabs = require('@react-navigation/bottom-tabs'); var drawer = require('@react-navigation/drawer'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React); function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; } var _jsxFileName$6 = "/Users/rivaslive/Documentos/npm/redshank/packages/native-router/src/components/PageNotFound.tsx"; const PageNotFound = ({}) => { return /*#__PURE__*/jsxDevRuntime.jsxDEV(reactNative.SafeAreaView, { style: styles$1.container, children: /*#__PURE__*/jsxDevRuntime.jsxDEV(reactNative.Text, { style: styles$1.title, children: "Page Not Found" }, void 0, false, { fileName: _jsxFileName$6, lineNumber: 7, columnNumber: 7 }, undefined) }, void 0, false, { fileName: _jsxFileName$6, lineNumber: 6, columnNumber: 5 }, undefined); }; const styles$1 = reactNative.StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', paddingHorizontal: 16 }, title: { fontSize: 24, marginBottom: 16 }, subtitle: { textAlign: 'center' }, dirText: { fontWeight: 'bold' } }); /** Match `[page]` -> `page` */ function matchDynamicName(name) { var _name$match; // Don't match `...` or `[` or `]` inside the brackets // eslint-disable-next-line no-useless-escape return (_name$match = name.match(/^\[([^[\](?:\.\.\.)]+?)\]$/)) == null ? void 0 : _name$match[1]; } /** Match `[...page]` -> `page` */ function matchDeepDynamicRouteName(name) { var _name$match2; return (_name$match2 = name.match(/^\[\.\.\.([^/]+?)\]$/)) == null ? void 0 : _name$match2[1]; } /** Match `(page)` -> `page` */ function matchGroupName(name) { var _name$match3; return (_name$match3 = name.match(/^(?:[^\\(\\)])*?\(([^\\/]+)\).*?$/)) == null ? void 0 : _name$match3[1]; } function getNameFromFilePath(name) { return removeSupportedExtensions(removeFileSystemDots(name)); } function getContextKey(name) { // The root path is `` (empty string) so always prepend `/` to ensure // there is some value. const normal = '/' + getNameFromFilePath(name); if (!normal.endsWith('_layout')) { return normal; } return normal.replace(/\/?_layout$/, ''); } /** Remove `.js`, `.ts`, `.jsx`, `.tsx` */ function removeSupportedExtensions(name) { return name.replace(/\.[jt]sx?$/g, ''); } // Remove any amount of `./` and `../` from the start of the string function removeFileSystemDots(filePath) { return filePath.replace(/^(?:\.\.?\/)+/g, ''); } function stripGroupSegmentsFromPath(path) { return path.split('/').reduce((acc, v) => { if (matchGroupName(v) == null) { acc.push(v); } return acc; }, []).join('/'); } function stripInvisibleSegmentsFromPath(path) { return stripGroupSegmentsFromPath(path).replace(/\/?index$/, ''); } var _jsxFileName$5 = "/Users/rivaslive/Documentos/npm/redshank/packages/native-router/src/Route.tsx"; const CurrentRouteContext = /*#__PURE__*/React.createContext(null); /** Return the RouteNode at the current contextual boundary. */ function useRouteNode() { return React.useContext(CurrentRouteContext); } function useContextKey() { const node = useRouteNode(); if (node == null) { throw new Error('No filename found'); } return getContextKey(node.contextKey); } /** Provides the matching routes and filename to the children. */ function Route({ children, node }) { return /*#__PURE__*/jsxDevRuntime.jsxDEV(CurrentRouteContext.Provider, { value: node, children: children }, void 0, false, { fileName: _jsxFileName$5, lineNumber: 62, columnNumber: 5 }, this); } var _jsxFileName$4 = "/Users/rivaslive/Documentos/npm/redshank/packages/native-router/src/components/ErrorBoundary.tsx"; const ErrorBoundary = ({ children, name }) => { const [hasError, setHasError] = React.useState(false); if (hasError) { return /*#__PURE__*/jsxDevRuntime.jsxDEV(reactNative.SafeAreaView, { style: styles.container, children: /*#__PURE__*/jsxDevRuntime.jsxDEV(reactNative.Text, { style: styles.text, children: `Failed to load ${name}` }, void 0, false, { fileName: _jsxFileName$4, lineNumber: 15, columnNumber: 9 }, undefined) }, void 0, false, { fileName: _jsxFileName$4, lineNumber: 14, columnNumber: 7 }, undefined); } try { return /*#__PURE__*/jsxDevRuntime.jsxDEV(jsxDevRuntime.Fragment, { children: children }, void 0, false); } catch (error) { console.log(error); setHasError(true); return /*#__PURE__*/jsxDevRuntime.jsxDEV(reactNative.SafeAreaView, { style: styles.container, children: /*#__PURE__*/jsxDevRuntime.jsxDEV(reactNative.Text, { style: styles.text, children: `Failed to load ${name}` }, void 0, false, { fileName: _jsxFileName$4, lineNumber: 27, columnNumber: 9 }, undefined) }, void 0, false, { fileName: _jsxFileName$4, lineNumber: 26, columnNumber: 7 }, undefined); } }; const styles = reactNative.StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center' }, text: { fontSize: 24, textAlign: 'center' } }); var _jsxFileName$3 = "/Users/rivaslive/Documentos/npm/redshank/packages/native-router/src/routeBuilder.tsx"; const importModule = route => { const component = route.loadRoute(); if (component instanceof Promise) { return component; } const response = { default: component.default || PageNotFound }; return new Promise(resolve => resolve(response)); }; const routeStored = new WeakMap(); function buildRouter(value, fallback) { if (routeStored.has(value)) { return routeStored.get(value); } let getLoadable; if (process.env.IMPORT_MODE === 'lazy') { const AsyncComponent = /*#__PURE__*/React.lazy(() => importModule(value)); getLoadable = (props, ref) => { return /*#__PURE__*/jsxDevRuntime.jsxDEV(ErrorBoundary, { name: value.contextKey, children: /*#__PURE__*/jsxDevRuntime.jsxDEV(React.Suspense, { fallback: /*#__PURE__*/jsxDevRuntime.jsxDEV(reactNative.View, {}, void 0, false, { fileName: _jsxFileName$3, lineNumber: 32, columnNumber: 49 }, this), children: /*#__PURE__*/jsxDevRuntime.jsxDEV(AsyncComponent, _extends({}, _extends({}, props, { ref, segment: value.route })), void 0, false, { fileName: _jsxFileName$3, lineNumber: 33, columnNumber: 13 }, this) }, void 0, false, { fileName: _jsxFileName$3, lineNumber: 32, columnNumber: 11 }, this) }, void 0, false, { fileName: _jsxFileName$3, lineNumber: 31, columnNumber: 9 }, this); }; } else { const SyncComponent = /*#__PURE__*/React.forwardRef((props, ref) => { const Component = value.loadRoute().default; if (Component) { return /*#__PURE__*/jsxDevRuntime.jsxDEV(Component, _extends({}, props, { ref: ref }), void 0, false, { fileName: _jsxFileName$3, lineNumber: 48, columnNumber: 16 }, this); } return /*#__PURE__*/jsxDevRuntime.jsxDEV(PageNotFound, {}, void 0, false, { fileName: _jsxFileName$3, lineNumber: 50, columnNumber: 14 }, this); }); getLoadable = (props, ref) => /*#__PURE__*/jsxDevRuntime.jsxDEV(SyncComponent, _extends({}, _extends({}, props, { ref, segment: value.route })), void 0, false, { fileName: _jsxFileName$3, lineNumber: 54, columnNumber: 7 }, this); } const RouteWithRef = /*#__PURE__*/React.forwardRef((props, ref) => { return /*#__PURE__*/jsxDevRuntime.jsxDEV(Route, { node: value, children: getLoadable(props, ref) }, void 0, false, { fileName: _jsxFileName$3, lineNumber: 65, columnNumber: 12 }, this); }); RouteWithRef.displayName = `Route(${value.route})`; routeStored.set(value, RouteWithRef); return RouteWithRef; } function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var useIsomorphicLayoutEffect$1 = {}; Object.defineProperty(useIsomorphicLayoutEffect$1, "__esModule", { value: true }); var react_1 = React; /** * Use `useEffect` during SSR and `useLayoutEffect` in the browser to avoid warnings. */ var useIsomorphicLayoutEffect = typeof document !== 'undefined' ? react_1.useLayoutEffect : react_1.useEffect; useIsomorphicLayoutEffect$1.default = useIsomorphicLayoutEffect; const ThemeContext = /*#__PURE__*/React__namespace.createContext(undefined); ThemeContext.displayName = 'ThemeContext'; var queryString = {}; var strictUriEncode = str => encodeURIComponent(str).replace(/[!'()*]/g, x => `%${x.charCodeAt(0).toString(16).toUpperCase()}`); var token = '%[a-f0-9]{2}'; var singleMatcher = new RegExp('(' + token + ')|([^%]+?)', 'gi'); var multiMatcher = new RegExp('(' + token + ')+', 'gi'); function decodeComponents(components, split) { try { // Try to decode the entire string first return [decodeURIComponent(components.join(''))]; } catch (err) { // Do nothing } if (components.length === 1) { return components; } split = split || 1; // Split the array in 2 parts var left = components.slice(0, split); var right = components.slice(split); return Array.prototype.concat.call([], decodeComponents(left), decodeComponents(right)); } function decode(input) { try { return decodeURIComponent(input); } catch (err) { var tokens = input.match(singleMatcher) || []; for (var i = 1; i < tokens.length; i++) { input = decodeComponents(tokens, i).join(''); tokens = input.match(singleMatcher) || []; } return input; } } function customDecodeURIComponent(input) { // Keep track of all the replacements and prefill the map with the `BOM` var replaceMap = { '%FE%FF': '\uFFFD\uFFFD', '%FF%FE': '\uFFFD\uFFFD' }; var match = multiMatcher.exec(input); while (match) { try { // Decode as big chunks as possible replaceMap[match[0]] = decodeURIComponent(match[0]); } catch (err) { var result = decode(match[0]); if (result !== match[0]) { replaceMap[match[0]] = result; } } match = multiMatcher.exec(input); } // Add `%C2` at the end of the map to make sure it does not replace the combinator before everything else replaceMap['%C2'] = '\uFFFD'; var entries = Object.keys(replaceMap); for (var i = 0; i < entries.length; i++) { // Replace all decoded components var key = entries[i]; input = input.replace(new RegExp(key, 'g'), replaceMap[key]); } return input; } var decodeUriComponent = function (encodedURI) { if (typeof encodedURI !== 'string') { throw new TypeError('Expected `encodedURI` to be of type `string`, got `' + typeof encodedURI + '`'); } try { encodedURI = encodedURI.replace(/\+/g, ' '); // Try the built in decoder first return decodeURIComponent(encodedURI); } catch (err) { // Fallback to a more advanced decoder return customDecodeURIComponent(encodedURI); } }; var splitOnFirst = (string, separator) => { if (!(typeof string === 'string' && typeof separator === 'string')) { throw new TypeError('Expected the arguments to be of type `string`'); } if (separator === '') { return [string]; } const separatorIndex = string.indexOf(separator); if (separatorIndex === -1) { return [string]; } return [ string.slice(0, separatorIndex), string.slice(separatorIndex + separator.length) ]; }; var filterObj = function (obj, predicate) { var ret = {}; var keys = Object.keys(obj); var isArr = Array.isArray(predicate); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var val = obj[key]; if (isArr ? predicate.indexOf(key) !== -1 : predicate(key, val, obj)) { ret[key] = val; } } return ret; }; (function (exports) { const strictUriEncode$1 = strictUriEncode; const decodeComponent = decodeUriComponent; const splitOnFirst$1 = splitOnFirst; const filterObject = filterObj; const isNullOrUndefined = value => value === null || value === undefined; const encodeFragmentIdentifier = Symbol('encodeFragmentIdentifier'); function encoderForArrayFormat(options) { switch (options.arrayFormat) { case 'index': return key => (result, value) => { const index = result.length; if ( value === undefined || (options.skipNull && value === null) || (options.skipEmptyString && value === '') ) { return result; } if (value === null) { return [...result, [encode(key, options), '[', index, ']'].join('')]; } return [ ...result, [encode(key, options), '[', encode(index, options), ']=', encode(value, options)].join('') ]; }; case 'bracket': return key => (result, value) => { if ( value === undefined || (options.skipNull && value === null) || (options.skipEmptyString && value === '') ) { return result; } if (value === null) { return [...result, [encode(key, options), '[]'].join('')]; } return [...result, [encode(key, options), '[]=', encode(value, options)].join('')]; }; case 'colon-list-separator': return key => (result, value) => { if ( value === undefined || (options.skipNull && value === null) || (options.skipEmptyString && value === '') ) { return result; } if (value === null) { return [...result, [encode(key, options), ':list='].join('')]; } return [...result, [encode(key, options), ':list=', encode(value, options)].join('')]; }; case 'comma': case 'separator': case 'bracket-separator': { const keyValueSep = options.arrayFormat === 'bracket-separator' ? '[]=' : '='; return key => (result, value) => { if ( value === undefined || (options.skipNull && value === null) || (options.skipEmptyString && value === '') ) { return result; } // Translate null to an empty string so that it doesn't serialize as 'null' value = value === null ? '' : value; if (result.length === 0) { return [[encode(key, options), keyValueSep, encode(value, options)].join('')]; } return [[result, encode(value, options)].join(options.arrayFormatSeparator)]; }; } default: return key => (result, value) => { if ( value === undefined || (options.skipNull && value === null) || (options.skipEmptyString && value === '') ) { return result; } if (value === null) { return [...result, encode(key, options)]; } return [...result, [encode(key, options), '=', encode(value, options)].join('')]; }; } } function parserForArrayFormat(options) { let result; switch (options.arrayFormat) { case 'index': return (key, value, accumulator) => { result = /\[(\d*)\]$/.exec(key); key = key.replace(/\[\d*\]$/, ''); if (!result) { accumulator[key] = value; return; } if (accumulator[key] === undefined) { accumulator[key] = {}; } accumulator[key][result[1]] = value; }; case 'bracket': return (key, value, accumulator) => { result = /(\[\])$/.exec(key); key = key.replace(/\[\]$/, ''); if (!result) { accumulator[key] = value; return; } if (accumulator[key] === undefined) { accumulator[key] = [value]; return; } accumulator[key] = [].concat(accumulator[key], value); }; case 'colon-list-separator': return (key, value, accumulator) => { result = /(:list)$/.exec(key); key = key.replace(/:list$/, ''); if (!result) { accumulator[key] = value; return; } if (accumulator[key] === undefined) { accumulator[key] = [value]; return; } accumulator[key] = [].concat(accumulator[key], value); }; case 'comma': case 'separator': return (key, value, accumulator) => { const isArray = typeof value === 'string' && value.includes(options.arrayFormatSeparator); const isEncodedArray = (typeof value === 'string' && !isArray && decode(value, options).includes(options.arrayFormatSeparator)); value = isEncodedArray ? decode(value, options) : value; const newValue = isArray || isEncodedArray ? value.split(options.arrayFormatSeparator).map(item => decode(item, options)) : value === null ? value : decode(value, options); accumulator[key] = newValue; }; case 'bracket-separator': return (key, value, accumulator) => { const isArray = /(\[\])$/.test(key); key = key.replace(/\[\]$/, ''); if (!isArray) { accumulator[key] = value ? decode(value, options) : value; return; } const arrayValue = value === null ? [] : value.split(options.arrayFormatSeparator).map(item => decode(item, options)); if (accumulator[key] === undefined) { accumulator[key] = arrayValue; return; } accumulator[key] = [].concat(accumulator[key], arrayValue); }; default: return (key, value, accumulator) => { if (accumulator[key] === undefined) { accumulator[key] = value; return; } accumulator[key] = [].concat(accumulator[key], value); }; } } function validateArrayFormatSeparator(value) { if (typeof value !== 'string' || value.length !== 1) { throw new TypeError('arrayFormatSeparator must be single character string'); } } function encode(value, options) { if (options.encode) { return options.strict ? strictUriEncode$1(value) : encodeURIComponent(value); } return value; } function decode(value, options) { if (options.decode) { return decodeComponent(value); } return value; } function keysSorter(input) { if (Array.isArray(input)) { return input.sort(); } if (typeof input === 'object') { return keysSorter(Object.keys(input)) .sort((a, b) => Number(a) - Number(b)) .map(key => input[key]); } return input; } function removeHash(input) { const hashStart = input.indexOf('#'); if (hashStart !== -1) { input = input.slice(0, hashStart); } return input; } function getHash(url) { let hash = ''; const hashStart = url.indexOf('#'); if (hashStart !== -1) { hash = url.slice(hashStart); } return hash; } function extract(input) { input = removeHash(input); const queryStart = input.indexOf('?'); if (queryStart === -1) { return ''; } return input.slice(queryStart + 1); } function parseValue(value, options) { if (options.parseNumbers && !Number.isNaN(Number(value)) && (typeof value === 'string' && value.trim() !== '')) { value = Number(value); } else if (options.parseBooleans && value !== null && (value.toLowerCase() === 'true' || value.toLowerCase() === 'false')) { value = value.toLowerCase() === 'true'; } return value; } function parse(query, options) { options = Object.assign({ decode: true, sort: true, arrayFormat: 'none', arrayFormatSeparator: ',', parseNumbers: false, parseBooleans: false }, options); validateArrayFormatSeparator(options.arrayFormatSeparator); const formatter = parserForArrayFormat(options); // Create an object with no prototype const ret = Object.create(null); if (typeof query !== 'string') { return ret; } query = query.trim().replace(/^[?#&]/, ''); if (!query) { return ret; } for (const param of query.split('&')) { if (param === '') { continue; } let [key, value] = splitOnFirst$1(options.decode ? param.replace(/\+/g, ' ') : param, '='); // Missing `=` should be `null`: // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters value = value === undefined ? null : ['comma', 'separator', 'bracket-separator'].includes(options.arrayFormat) ? value : decode(value, options); formatter(decode(key, options), value, ret); } for (const key of Object.keys(ret)) { const value = ret[key]; if (typeof value === 'object' && value !== null) { for (const k of Object.keys(value)) { value[k] = parseValue(value[k], options); } } else { ret[key] = parseValue(value, options); } } if (options.sort === false) { return ret; } return (options.sort === true ? Object.keys(ret).sort() : Object.keys(ret).sort(options.sort)).reduce((result, key) => { const value = ret[key]; if (Boolean(value) && typeof value === 'object' && !Array.isArray(value)) { // Sort object keys, not values result[key] = keysSorter(value); } else { result[key] = value; } return result; }, Object.create(null)); } exports.extract = extract; exports.parse = parse; exports.stringify = (object, options) => { if (!object) { return ''; } options = Object.assign({ encode: true, strict: true, arrayFormat: 'none', arrayFormatSeparator: ',' }, options); validateArrayFormatSeparator(options.arrayFormatSeparator); const shouldFilter = key => ( (options.skipNull && isNullOrUndefined(object[key])) || (options.skipEmptyString && object[key] === '') ); const formatter = encoderForArrayFormat(options); const objectCopy = {}; for (const key of Object.keys(object)) { if (!shouldFilter(key)) { objectCopy[key] = object[key]; } } const keys = Object.keys(objectCopy); if (options.sort !== false) { keys.sort(options.sort); } return keys.map(key => { const value = object[key]; if (value === undefined) { return ''; } if (value === null) { return encode(key, options); } if (Array.isArray(value)) { if (value.length === 0 && options.arrayFormat === 'bracket-separator') { return encode(key, options) + '[]'; } return value .reduce(formatter(key), []) .join('&'); } return encode(key, options) + '=' + encode(value, options); }).filter(x => x.length > 0).join('&'); }; exports.parseUrl = (url, options) => { options = Object.assign({ decode: true }, options); const [url_, hash] = splitOnFirst$1(url, '#'); return Object.assign( { url: url_.split('?')[0] || '', query: parse(extract(url), options) }, options && options.parseFragmentIdentifier && hash ? {fragmentIdentifier: decode(hash, options)} : {} ); }; exports.stringifyUrl = (object, options) => { options = Object.assign({ encode: true, strict: true, [encodeFragmentIdentifier]: true }, options); const url = removeHash(object.url).split('?')[0] || ''; const queryFromUrl = exports.extract(object.url); const parsedQueryFromUrl = exports.parse(queryFromUrl, {sort: false}); const query = Object.assign(parsedQueryFromUrl, object.query); let queryString = exports.stringify(query, options); if (queryString) { queryString = `?${queryString}`; } let hash = getHash(object.url); if (object.fragmentIdentifier) { hash = `#${options[encodeFragmentIdentifier] ? encode(object.fragmentIdentifier, options) : object.fragmentIdentifier}`; } return `${url}${queryString}${hash}`; }; exports.pick = (input, filter, options) => { options = Object.assign({ parseFragmentIdentifier: true, [encodeFragmentIdentifier]: false }, options); const {url, query, fragmentIdentifier} = exports.parseUrl(input, options); return exports.stringifyUrl({ url, query: filterObject(query, filter), fragmentIdentifier }, options); }; exports.exclude = (input, filter, options) => { const exclusionFilter = Array.isArray(filter) ? key => !filter.includes(key) : (key, value) => !filter(key, value); return exports.pick(input, exclusionFilter, options); }; } (queryString)); const formatToList$1 = items => Object.entries(items).map(([key, value]) => `- ${key} (${value})`).join('\n'); function validatePathConfig$1(config, root = true) { const validation = { path: 'string', initialRouteName: 'string', screens: 'object', ...(root ? null : { alias: 'array', exact: 'boolean', stringify: 'object', parse: 'object' }) }; if (typeof config !== 'object' || config === null) { throw new Error(`Expected the configuration to be an object, but got ${JSON.stringify(config)}.`); } const validationErrors = Object.fromEntries(Object.keys(config).map(key => { if (key in validation) { const type = validation[key]; // @ts-expect-error: we know the key exists const value = config[key]; if (value !== undefined) { if (type === 'array') { if (!Array.isArray(value)) { return [key, `expected 'Array', got '${typeof value}'`]; } } else if (typeof value !== type) { return [key, `expected '${type}', got '${typeof value}'`]; } } } else { return [key, 'extraneous']; } return null; }).filter(Boolean)); if (Object.keys(validationErrors).length) { throw new Error(`Found invalid properties in the configuration:\n${formatToList$1(validationErrors)}\n\nYou can only specify the following properties:\n${formatToList$1(validation)}\n\nIf you want to specify configuration for screens, you need to specify them under a 'screens' property.\n\nSee https://reactnavigation.org/docs/configuring-links for more details on how to specify a linking configuration.`); } if (root && 'path' in config && typeof config.path === 'string' && config.path.includes(':')) { throw new Error(`Found invalid path '${config.path}'. The 'path' in the top-level configuration cannot contain patterns for params.`); } if ('screens' in config && config.screens) { Object.entries(config.screens).forEach(([_, value]) => { if (typeof value !== 'string') { validatePathConfig$1(value, false); } }); } } var escapeStringRegexp = string => { if (typeof string !== 'string') { throw new TypeError('Expected a string'); } // Escape characters with special meaning either inside or outside character sets. // Use a simple backslash escape when it’s always valid, and a \unnnn escape when the simpler form would be disallowed by Unicode patterns’ stricter grammar. return string .replace(/[|\\{}()[\]^$+*?.]/g, '\\$&') .replace(/-/g, '\\x2d'); }; var escape = /*@__PURE__*/getDefaultExportFromCjs(escapeStringRegexp); /** * Context which holds the route prop for a screen. */ const NavigationRouteContext = /*#__PURE__*/React__namespace.createContext(undefined); /** * Hook to access the route prop of the parent screen anywhere. * * @returns Route prop of the parent screen. */ function useRoute() { const route = React__namespace.useContext(NavigationRouteContext); if (route === undefined) { throw new Error("Couldn't find a route object. Is your component inside a screen in a navigator?"); } return route; } const MemoizedScreen = /*#__PURE__*/React__namespace.memo(({ component }) => { const route = useRoute(); const children = /*#__PURE__*/React__namespace.createElement(component, { route }); return children; }); MemoizedScreen.displayName = 'Memo(Screen)'; var useSyncExternalStoreWithSelector_production = {}; /** * @license React * use-sync-external-store-with-selector.production.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var hasRequiredUseSyncExternalStoreWithSelector_production; function requireUseSyncExternalStoreWithSelector_production () { if (hasRequiredUseSyncExternalStoreWithSelector_production) return useSyncExternalStoreWithSelector_production; hasRequiredUseSyncExternalStoreWithSelector_production = 1; var React$1 = React; function is(x, y) { return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y); } var objectIs = "function" === typeof Object.is ? Object.is : is, useSyncExternalStore = React$1.useSyncExternalStore, useRef = React$1.useRef, useEffect = React$1.useEffect, useMemo = React$1.useMemo, useDebugValue = React$1.useDebugValue; useSyncExternalStoreWithSelector_production.useSyncExternalStoreWithSelector = function ( subscribe, getSnapshot, getServerSnapshot, selector, isEqual ) { var instRef = useRef(null); if (null === instRef.current) { var inst = { hasValue: false, value: null }; instRef.current = inst; } else inst = instRef.current; instRef = useMemo( function () { function memoizedSelector(nextSnapshot) { if (!hasMemo) { hasMemo = true; memoizedSnapshot = nextSnapshot; nextSnapshot = selector(nextSnapshot); if (void 0 !== isEqual && inst.hasValue) { var currentSelection = inst.value; if (isEqual(currentSelection, nextSnapshot)) return (memoizedSelection = currentSelection); } return (memoizedSelection = nextSnapshot); } currentSelection = memoizedSelection; if (objectIs(memoizedSnapshot, nextSnapshot)) return currentSelection; var nextSelection = selector(nextSnapshot); if (void 0 !== isEqual && isEqual(currentSelection, nextSelection)) return (memoizedSnapshot = nextSnapshot), currentSelection; memoizedSnapshot = nextSnapshot; return (memoizedSelection = nextSelection); } var hasMemo = false, memoizedSnapshot, memoizedSelection, maybeGetServerSnapshot = void 0 === getServerSnapshot ? null : getServerSnapshot; return [ function () { return memoizedSelector(getSnapshot()); }, null === maybeGetServerSnapshot ? void 0 : function () { return memoizedSelector(maybeGetServerSnapshot()); } ]; }, [getSnapshot, getServerSnapshot, selector, isEqual] ); var value = useSyncExternalStore(subscribe, instRef[0], instRef[1]); useEffect( function () { inst.hasValue = true; inst.value = value; }, [value] ); useDebugValue(value); return value; }; return useSyncExternalStoreWithSelector_production; } var useSyncExternalStoreWithSelector_development = {}; /** * @license React * use-sync-external-store-with-selector.development.js * * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ var hasRequiredUseSyncExternalStoreWithSelector_development; function requireUseSyncExternalStoreWithSelector_development () { if (hasRequiredUseSyncExternalStoreWithSelector_development) return useSyncExternalStoreWithSelector_development; hasRequiredUseSyncExternalStoreWithSelector_development = 1; "production" !== process.env.NODE_ENV && (function () { function is(x, y) { return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y); } "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error()); var React$1 = React, objectIs = "function" === typeof Object.is ? Object.is : is, useSyncExternalStore = React$1.useSyncExternalStore, useRef = React$1.useRef, useEffect = React$1.useEffect, useMemo = React$1.useMemo, useDebugValue = React$1.useDebugValue; useSyncExternalStoreWithSelector_development.useSyncExternalStoreWithSelector = function ( subscribe, getSnapshot, getServerSnapshot, selector, isEqual ) { var instRef = useRef(null); if (null === instRef.current) { var inst = { hasValue: false, value: null }; instRef.current = inst; } else inst = instRef.current; instRef = useMemo( function () { function memoizedSelector(nextSnapshot) { if (!hasMemo) { hasMemo = true; memoizedSnapshot = nextSnapshot; nextSnapshot = selector(nextSnapshot); if (void 0 !== isEqual && inst.hasValue) { var currentSelection = inst.value; if (isEqual(currentSelection, nextSnapshot)) return (memoizedSelection = currentSelection); } return (memoizedSelection = nextSnapshot); } currentSelection = memoizedSelection; if (objectIs(memoizedSnapshot, nextSnapshot)) return currentSelection; var nextSelection = selector(nextSnapshot); if (void 0 !== isEqual && isEqual(currentSelection, nextSelection)) return (memoizedSnapshot = nextSnapshot), currentSelection; memoizedSnapshot = nextSnapshot; return (memoizedSelection = nextSelection); } var hasMemo = false, memoizedSnapshot, memoizedSelection, maybeGetServerSnapshot = void 0 === getServerSnapshot ? null : getServerSnapshot; return [ function () { return memoizedSelector(getSnapshot()); }, null === maybeGetServerSnapshot ? void 0 : function () { return memoizedSelector(maybeGetServerSnapshot()); } ]; }, [getSnapshot, getServerSnapshot, selector, isEqual] ); var value = useSyncExternalStore(subscribe, instRef[0], instRef[1]); useEffect( function () { inst.hasValue = true; inst.value = value; }, [value] ); useDebugValue(value); return value; }; "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error()); })(); return useSyncExternalStoreWithSelector_development; } if (process.env.NODE_ENV === 'production') { requireUseSyncExternalStoreWithSelector_production(); } else { requireUseSyncExternalStoreWithSelector_development(); } const _excluded$2 = ["preserveGroups", "preserveDynamicRoutes"]; const DEFAULT_SCREENS = {}; 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); } if (route && isInvalidParams(route.params)) { return getActiveRoute(createFakeState(route.params)); } return route; }; function createFakeState(params) { return { stale: false, type: 'UNKNOWN', key: 'UNKNOWN', index: 0, routeNames: [], routes: [{ key: 'UNKNOWN', name: params.screen, params: params.params, path: params.path }] }; } function segmentMatchesConvention(segment) { return segment === 'index' || matchDynamicName(segment) != null || matchGroupName(segment) != null || matchDeepDynamicRouteName(segment) != null; } function encodeURIComponentPreservingBrackets(str) { return encodeURIComponent(str).replace(/%5B/g, '[').replace(/%5D/g, ']'); } /** * 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 = { screens: DEFAULT_SCREENS }) { if (state == null) { throw Error("Got 'undefined' for the navigation state. You must pass a valid state object."); } const { preserveGroups, preserveDynamicRoutes } = _options, options = _objectWithoutPropertiesLoose(_options, _excluded$2); validatePathConfig$1(options); // Expo Router disallows usage without a linking config. if (Object.is(options.screens, DEFAULT_SCREENS)) { throw Error("You must pass a 'screens' object to 'getPathFromState' to generate a path."); } return getPathFromResolvedState(JSON.parse(JSON.stringify(state)), // Create a normalized configs object which will be easier to use createNormalizedConfigs$1(options.screens), { preserveGroups, preserveDynamicRoutes }); } function processParamsWithUserSettings(configItem, params) { const stringify = configItem == null ? void 0 : configItem.stringify; return Object.fromEntries(Object.entries(params).map(([key, value]) => [key, // TODO: Strip nullish values here. stringify != null && stringify[key] ? stringify[key](value) : // Preserve rest params Array.isArray(value) ? value : String(value)])); } function deepEqual(a, b) { if (a === b) { return true; } if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { if (!deepEqual(a[i], b[i])) { return false; } } return true; } if (typeof a === 'object' && typeof b === 'object') { const keysA = Object.keys(a); const keysB = Object.keys(b); if (keysA.length !== keysB.length) { return false; } for (const key of keysA) { if (!deepEqual(a[key], b[key])) { return false; } } return true; } return false; } function walkConfigItems(route, focusedRoute, configs, { preserveDynamicRoutes }) { // NOTE(EvanBacon): Fill in current route using state that was passed as params. if (!route.state && isInvalidParams(route.params)) { route.state = createFakeState(route.params); } let pattern = null; let focusedParams; const collectedParams = {}; while (route.name in configs) { var _route$state$index; const configItem = configs[route.name]; const inputPattern = configItem.pattern; if (inputPattern == null) { // This should never happen in Expo Router. throw new Error('Unexpected: No pattern found for route ' + route.name); } pattern = inputPattern; if (route.params) { const params = processParamsWithUserSettings(configItem, route.params); // TODO: Does this need to be a null check? if (pattern) { Object.assign(collectedParams, params); } if (deepEqual(focusedRoute, route)) { if (preserveDynamicRoutes) { focusedParams = params; } else { // If this is the focused route, keep the params for later use // We save it here since it's been stringified already focusedParams = getParamsWithConventionsCollapsed({ params, pattern, routeName: route.name }); } } } if (!route.state && isInvalidParams(route.params)) { route.state = createFakeState(route.params); } // If there is no `screens` property or no nested state, we return pattern if (!configItem.screens || route.state === undefined) { var _configItem$screens$c; if (configItem.initialRouteName && configItem.screens && configItem.initialRouteName in configItem.screens && (_configItem$screens$c = configItem.screens[configItem.initialRouteName]) != null && _configItem$screens$c.pattern) { const initialRouteConfig = configItem.screens[configItem.initialRouteName]; // NOTE(EvanBacon): Big hack to support initial route changes in tab bars. pattern = initialRouteConfig.pattern; if (focusedParams) { if (!preserveDynamicRoutes) { // If this is the focused route, keep the params for later use // We save it here since it's been stringified already focusedParams = getParamsWithConventionsCollapsed({ params: focusedParams, pattern, routeName: route.name }); } } } break; } const index = (_route$state$index = route.state.index) != null ? _route$state$index : route.state.routes.length - 1; const nextRoute = route.state.routes[index]; const nestedScreens = configItem.screens; // if there is config for next route name, we go deeper if (nestedScreens && nextRoute.name in nestedScreens) { route = nextRoute; configs = nestedScreens; } else { // If not, there is no sense in going deeper in config break; } } if (pattern == null) { throw new Error(`No pattern found for route "${route.name}". Options are: ${Object.keys(configs).join(', ')}.`); } if (pattern && !focusedParams && focusedRoute.params) { if (preserveDynamicRoutes) { focusedParams = focusedRoute.params; } else { // If this is the focused route, keep the params for later use // We save it here since it's been stringified already focusedParams = getParamsWithConventionsCollapsed({ params: focusedRoute.params, pattern, routeName: route.name }); } Object.assign(focusedParams, collectedParams); } return { pattern, nextRoute: route, focusedParams, params: collectedParams }; } function getPathFromResolvedState(state, configs, { preserveGroups, preserveDynamicRoutes }) { let path = ''; let current = state; const allParams = {}; while (current) { var _current$index, _configs$nextRoute$na, _nextRoute$state$rout, _nextRoute$state$inde, _nextRoute$state; path += '/'; const route = _extends({}, current.routes[(_current$index = current.index) != null ? _current$index : 0]); // NOTE(EvanBacon): Fill in current route using state that was passed as params. // if (isInvalidParams(route.params)) { if (!route.state && isInvalidParams(route.params)) { route.state = createFakeState(route.params); } const { pattern, params, nextRoute, focusedParams } = walkConfigItems(route, getActiveRoute(current), _extends({}, configs), { preserveDynamicRoutes }); Object.assign(allParams, params); path += getPathWithConventionsCollapsed({ pattern, routePath: nextRoute.path, params: allParams, initialRouteName: (_configs$nextRoute$na = configs[nextRoute.name]) == null ? void 0 : _configs$nextRoute$na.initialRouteName, preserveGroups, preserveDynamicRoutes }); if (nextRoute.state && // NOTE(EvanBacon): The upstream implementation allows for sending in synthetic states (states that weren't generated by `getStateFromPath`) // and any invalid routes will simply be ignored. // Because of this, we need to check if the next route is valid before continuing, otherwise our more strict // implementation will throw an error. configs[(_nextRoute$state$rout = nextRoute.state.routes) == null || (_nextRoute$state$rout = _nextRoute$state$rout[(_nextRoute$state$inde = (_nextRoute$state = nextRoute.state) == null ? void 0 : _nextRoute$state.index) != null ? _nextRoute$state$inde : 0]) == null ? void 0 : _nextRoute$state$rout.name]) { // Continue looping with the next state if available. current = nextRoute.state; } else { // Finished crawling state. // Check for query params before exiting. if (focusedParams) { for (const param in focusedParams) { if (focusedParams[param] === 'undefined') { delete focusedParams[param]; } } const query = queryString.stringify(focusedParams, { sort: false }); if (query) { path += `?${query}`; } } break; } } return { path: basicSanitizePath(path), params: decodeParams(allParams) }; } function decodeParams(params) { const parsed = {}; for (const [key, value] of Object.entries(params)) { parsed[key] = decodeURIComponent(value); } return parsed; } function getPathWithConventionsCollapsed({ pattern, routePath, params, preserveGroups, preserveDynamicRoutes, initialRouteName }) { const segments = pattern.split('/'); return segments.map((p, i) => { 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.startsWith('*')) { if (preserveDynamicRoutes) { return `[...${name}]`; } if (params[name]) { return params[name].join('/'); } if (i === 0) { // This can occur when a wildcard matches all routes and the given path was `/`. return routePath; } // remove existing segments from route.path and return it // this is used for nested wildcard routes. Without this, the path would add // all nested segments to the beginning of the wildcard route. return routePath == null ? void 0 : routePath.split('/').slice(i + 1).join('/'); } // 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 return params[name]; } if (!preserveGroups && 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 encodeURIComponentPreservingBrackets(initialRouteName); } } return ''; } // Preserve dynamic syntax for rehydration return encodeURIComponentPreservingBrackets(p); }).map(v => v != null ? v : '').join('/'); } /** Given a set of query params and a pattern with possible conventions, collapse the conventions and return the remaining params. */ function getParamsWithConventionsCollapsed({ pattern, routeName, params }) { const processedParams = _extends({}, params); // Remove the params present in the pattern since we'll only use the rest for query string const segments = pattern.split('/'); // Dynamic Routes segments.filter(segment => segment.startsWith(':')).forEach(segment => { const name = getParamName(segment); delete processedParams[name]; }); // Deep Dynamic Routes if (segments.some(segment => segment.startsWith('*'))) { var _matchDeepDynamicRout; // NOTE(EvanBacon): Drop the param name matching the wildcard route name -- this is specific to Expo Router. const name = (_matchDeepDynamicRout = matchDeepDynamicRouteName(routeName)) != null ? _matchDeepDynamicRout : routeName; delete processedParams[name]; } return processedParams; } // Remove multiple as well as trailing slashes function basicSanitizePath(path) { // Remove duplicate slashes like `foo//bar` -> `foo/bar` const simplifiedPath = path.replace(/\/+/g, '/'); if (simplifiedPath.length <= 1) { return simplifiedPath; } // Remove trailing slash like `foo/bar/` -> `foo/bar` return simplifiedPath.replace(/\/$/, ''); } // TODO: Make StackRouter not do this... // Detect if the params came from StackRouter using `params` to pass around internal state. function isInvalidParams(params) { if (!params) { return false; } if ('params' in params && typeof params.params === 'object' && !!params.params) { return true; } return 'initial' in params && typeof params.initial === 'boolean' && // "path" in params