UNPKG

expo-router

Version:

Expo Router is a file-based router for React Native and web applications.

269 lines (267 loc) • 9.64 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); 0 && (module.exports = { areUrlObjectsEqual: null, defaultRouteInfo: null, getRouteInfoFromState: null, getRouteSegmentsFromState: null }); function _export(target, all) { for(var name in all)Object.defineProperty(target, name, { enumerable: true, get: Object.getOwnPropertyDescriptor(all, name).get }); } _export(exports, { get areUrlObjectsEqual () { return areUrlObjectsEqual; }, get defaultRouteInfo () { return defaultRouteInfo; }, get getRouteInfoFromState () { return getRouteInfoFromState; }, get getRouteSegmentsFromState () { return getRouteSegmentsFromState; } }); function _reactfastcompare() { const data = /*#__PURE__*/ _interop_require_default(require("react-fast-compare")); _reactfastcompare = function() { return data; }; return data; } const _constants = require("../constants"); const _getPathFromStateforks = require("../fork/getPathFromState-forks"); const _navigationParams = require("../navigationParams"); const _isArrayEqual = require("../react-navigation/core/isArrayEqual"); const _url = require("../utils/url"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const defaultRouteInfo = { unstable_globalHref: '', searchParams: new URLSearchParams(), pathname: '/', params: {}, segments: [], pathnameWithParams: '/', // TODO: Remove this, it is not used anywhere isIndex: false }; function areUrlObjectsEqual(a, b) { return a.pathname === b.pathname && a.pathnameWithParams === b.pathnameWithParams && a.unstable_globalHref === b.unstable_globalHref && (0, _isArrayEqual.isArrayEqual)(a.segments, b.segments) && (0, _reactfastcompare().default)(a.params, b.params); } function getRouteInfoFromState(state) { if (!state) return defaultRouteInfo; const index = 'index' in state ? state.index ?? 0 : 0; const route = state.routes[index]; (0, _navigationParams.warnIfNestedParams)(route.params); if (route.name === _constants.NOT_FOUND_ROUTE_NAME || route.name === _constants.SITEMAP_ROUTE_NAME) { const path = route.path || (route.name === _constants.NOT_FOUND_ROUTE_NAME ? '/' : `/${route.name}`); return { ...defaultRouteInfo, unstable_globalHref: (0, _getPathFromStateforks.appendBaseUrl)(path), pathname: path, pathnameWithParams: path, segments: [ route.name ] }; } if (route.name !== _constants.INTERNAL_SLOT_NAME) { throw new Error(`Expected the first route to be ${_constants.INTERNAL_SLOT_NAME}, but got ${route.name}`); } const { segments, params: mergedParams } = collectRouteState(route.state); const params = decodeParams(mergedParams); const { pathname, pathParams } = resolvePathname(segments, params); const { searchParams, pathnameWithParams } = serializeQueryAndHash(pathname, params, pathParams); return { segments, pathname, // Navigation params can contain ordinary object values at runtime despite the public search-param type. // TODO: address this together with other params serialization issues params: params, unstable_globalHref: (0, _getPathFromStateforks.appendBaseUrl)(pathnameWithParams), searchParams, pathnameWithParams, // TODO: Remove this, it is not used anywhere isIndex: false }; } function getRouteSegmentsFromState(state) { if (!state) return []; const index = 'index' in state ? state.index ?? 0 : 0; let route = state.routes[index]; (0, _navigationParams.warnIfNestedParams)(route.params); if (route.name === _constants.NOT_FOUND_ROUTE_NAME || route.name === _constants.SITEMAP_ROUTE_NAME) { return [ route.name ]; } if (route.name !== _constants.INTERNAL_SLOT_NAME) { throw new Error(`Expected the first route to be ${_constants.INTERNAL_SLOT_NAME}, but got ${route.name}`); } const segments = []; state = route.state; while(state){ route = state.routes['index' in state && state.index ? state.index : 0]; (0, _navigationParams.warnIfNestedParams)(route.params); const routeName = route.name.startsWith('/') ? route.name.slice(1) : route.name; segments.push(...routeName.split('/')); state = route.state; } if (segments[segments.length - 1] === 'index') { segments.pop(); } return segments; } function collectRouteState(state) { const segments = []; const params = Object.create(null); while(state){ const route = state.routes['index' in state && state.index ? state.index : 0]; (0, _navigationParams.warnIfNestedParams)(route.params); Object.assign(params, route.params); let routeName = route.name; if (routeName.startsWith('/')) { routeName = routeName.slice(1); } segments.push(...routeName.split('/')); state = route.state; } if (segments[segments.length - 1] === 'index') { segments.pop(); } return { segments, params }; } function decodeParams(params) { return Object.fromEntries(Object.entries(params).map(([key, value])=>{ if (typeof value === 'string') { return [ key, (0, _url.safeDecodeURIComponent)(value) ]; } else if (Array.isArray(value)) { return [ key, value.map((v)=>typeof v === 'string' ? (0, _url.safeDecodeURIComponent)(v) : v) ]; } else { return [ key, value ]; } })); } function resolvePathname(segments, params) { const resolvedSegments = segments.filter((segment)=>!(segment.startsWith('(') && segment.endsWith(')'))).map((segment)=>{ if (segment === '+not-found') { const notFoundPath = params['not-found']; if (typeof notFoundPath === 'undefined') { // Not founds are optional, do nothing if its not present return { parts: [], paramName: 'not-found' }; } else if (Array.isArray(notFoundPath)) { return { parts: notFoundPath.map(String), paramName: 'not-found' }; } else { return { parts: [ String(notFoundPath) ], paramName: 'not-found' }; } } else if (segment.startsWith('[...') && segment.endsWith(']')) { let paramName = segment.slice(4, -1); // Legacy for React Navigation optional params if (paramName.endsWith('?')) { paramName = paramName.slice(0, -1); } const values = params[paramName]; // Catchall params are optional const parts = Array.isArray(values) ? values.filter(isSerializableParam).map(String) : isSerializableParam(values) && values ? [ String(values) ] : []; return { parts, paramName }; } else if (segment.startsWith('[') && segment.endsWith(']')) { const paramName = segment.slice(1, -1); const value = params[paramName]; // Optional params are optional return { parts: isSerializableParam(value) && value ? [ String(value) ] : [], paramName }; } else { return { parts: [ segment ] }; } }); const pathname = '/' + resolvedSegments.flatMap(({ parts })=>parts).join('/'); const pathParams = new Set(resolvedSegments.map(({ paramName })=>paramName).filter((paramName)=>paramName !== undefined)); return { pathname, pathParams }; } function serializeQueryAndHash(pathname, params, pathParams) { const searchParams = new URLSearchParams(Object.entries(params).flatMap(([key, value])=>{ // Search params should not include path params if (pathParams.has(key)) { return []; } else if (Array.isArray(value)) { return value.filter(isSerializableParam).map((v)=>[ key, String(v) ]); } else if (!isSerializableParam(value)) { return []; } return [ [ key, String(value) ] ]; })); let hash; if (searchParams.has('#')) { hash = searchParams.get('#') || undefined; searchParams.delete('#'); } // We cannot use searchParams.size because it is not included in the React Native polyfill const searchParamString = searchParams.toString(); let pathnameWithParams = searchParamString ? pathname + '?' + searchParamString : pathname; pathnameWithParams = hash ? pathnameWithParams + '#' + hash : pathnameWithParams; return { searchParams, pathnameWithParams }; } function isSerializableParam(value) { return value == null || typeof value !== 'object'; } //# sourceMappingURL=getRouteInfoFromState.js.map