From c46e44cd676164a4fad2233272e06693dfdd1df3 Mon Sep 17 00:00:00 2001 From: Mark Lawlor Date: Fri, 26 Apr 2024 10:51:40 +1000 Subject: [PATCH] [router] Support platform extensions for _layout and routes (#27408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # Why Revised version #26047 Blocked by #27407 # How Rules: * API routes cannot have platform extensions * Layouts and routes can have extensions only if a non-extension version exists For example, you have have `/page.web.tsx` only if `/page.tsx` also exists. This makes platform extensions an additive change and won't break concepts like universal deep linking and typed routes API routes do not support platform extensions as they are independent of a platform. # Implementation `getRoutes` allows multiple files to be assigned to the same route, but all files were given the same specificity. This PR adds logic to calculate the specificity of a file and to select the most specific file. Specificity highest-to-lowest `2`: Platform extension matches platform `1`: `.native` platform extension `0`: No platform extensions `-1`: Invalid route If no platform is provided to `getRoutes` the current behaviour is retained and files with platform extensions will be ignored. # `getRoutes` checklist These are the locations we use `getRoutes` and whether they should use platform extensions or not. **[renderStaticContent](https://github.com/expo/expo/blob/269625f5b70a95656212ad14ca7f3e82b45b9f61/packages/expo-router/src/static/renderStaticContent.tsx#L19)** - Used in `getManifest()` ❌ - Used in `getBuildTimeServerManifestAsync` ❌ - I'm not 100% sure on this one. Need to understand how to test this correctly **[routes-manifest]( https://github.com/expo/expo/blob/269625f5b70a95656212ad14ca7f3e82b45b9f61/packages/expo-router/src/routes-manifest.ts#L4)** - Used in `createRoutesManifest` ❌ **[router-store](https://github.com/expo/expo/blob/269625f5b70a95656212ad14ca7f3e82b45b9f61/packages/expo-router/src/global-state/router-store.tsx#L27)** - Used in `RouterStore` ✅ **[typed-routes](https://github.com/expo/expo/blob/269625f5b70a95656212ad14ca7f3e82b45b9f61/packages/expo-router/src/typed-routes/generate.ts#L5)** - Used in `getTypedRoutesDeclarationFile` ❌ There is also an `getExactRoutes` function, but this is only used for testing purposes. ** # Test Plan # Checklist - [ ] Documentation is up to date to reflect these changes (eg: https://docs.expo.dev and README.md). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). --------- Co-authored-by: Evan Bacon Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com> --- packages/@expo/cli/CHANGELOG.md | 1 + .../server/metro/MetroBundlerDevServer.ts | 7 +- .../metro/createServerRouteMiddleware.ts | 2 +- .../start/server/metro/fetchRouterManifest.ts | 7 +- packages/expo-router/CHANGELOG.md | 1 + packages/expo-router/_ctx.android.js | 2 +- packages/expo-router/_ctx.ios.js | 2 +- packages/expo-router/_ctx.web.js | 2 +- packages/expo-router/build/getRoutes.d.ts | 2 + packages/expo-router/build/getRoutes.d.ts.map | 2 +- packages/expo-router/build/getRoutes.js | 60 ++++- packages/expo-router/build/getRoutes.js.map | 2 +- .../build/global-state/router-store.d.ts.map | 2 +- .../build/global-state/router-store.js | 11 +- .../build/global-state/router-store.js.map | 2 +- .../expo-router/build/routes-manifest.d.ts | 4 +- .../build/routes-manifest.d.ts.map | 2 +- packages/expo-router/build/routes-manifest.js | 4 +- .../expo-router/build/routes-manifest.js.map | 2 +- .../build/static/renderStaticContent.d.ts | 6 +- .../build/static/renderStaticContent.d.ts.map | 2 +- .../build/static/renderStaticContent.js | 11 +- .../build/static/renderStaticContent.js.map | 2 +- .../build/typed-routes/generate.d.ts.map | 2 +- .../build/typed-routes/generate.js | 1 + .../build/typed-routes/generate.js.map | 2 +- packages/expo-router/plugin/options.json | 4 + .../src/__tests__/getRoutes.test.ios.ts | 19 ++ .../platform-routes.test.android.tsx | 73 ++++++ .../__tests__/platform-routes.test.ios.tsx | 73 ++++++ .../__tests__/platform-routes.test.node.ts | 71 ++++++ .../src/__tests__/platform-routes.test.web.ts | 211 ++++++++++++++++++ packages/expo-router/src/getRoutes.ts | 71 +++++- .../src/global-state/router-store.tsx | 8 +- packages/expo-router/src/routes-manifest.ts | 11 +- .../src/static/renderStaticContent.tsx | 17 +- .../expo-router/src/typed-routes/generate.ts | 1 + 37 files changed, 650 insertions(+), 52 deletions(-) create mode 100644 packages/expo-router/src/__tests__/platform-routes.test.android.tsx create mode 100644 packages/expo-router/src/__tests__/platform-routes.test.ios.tsx create mode 100644 packages/expo-router/src/__tests__/platform-routes.test.node.ts create mode 100644 packages/expo-router/src/__tests__/platform-routes.test.web.ts diff --git a/packages/@expo/cli/CHANGELOG.md b/packages/@expo/cli/CHANGELOG.md index 70461a68892fe..33a7bbab44229 100644 --- a/packages/@expo/cli/CHANGELOG.md +++ b/packages/@expo/cli/CHANGELOG.md @@ -51,6 +51,7 @@ _This version does not introduce any user-facing changes._ - Fixed `TypeError: osascript(...) is not a function` when pressing "j" to open JS debugger. ([#28315](https://github.com/expo/expo/pull/28315) by [@kudo](https://github.com/kudo)) - Fix API routes in folders starting with `.` characters. ([#28366](https://github.com/expo/expo/pull/28366) by [@byCedric](https://github.com/byCedric)) +- Allow platform extensions for layout and route files ([#27408](https://github.com/expo/expo/pull/27408) by [@marklawlor](https://github.com/marklawlor)) ## 0.18.0 — 2024-04-18 diff --git a/packages/@expo/cli/src/start/server/metro/MetroBundlerDevServer.ts b/packages/@expo/cli/src/start/server/metro/MetroBundlerDevServer.ts index 40cd63c063099..248aed590eddb 100644 --- a/packages/@expo/cli/src/start/server/metro/MetroBundlerDevServer.ts +++ b/packages/@expo/cli/src/start/server/metro/MetroBundlerDevServer.ts @@ -181,7 +181,9 @@ export class MetroBundlerDevServer extends BundlerDevServer { async getExpoRouterRoutesManifestAsync({ appDir }: { appDir: string }) { // getBuiltTimeServerManifest + const { exp } = getConfig(this.projectRoot); const manifest = await fetchManifest(this.projectRoot, { + ...exp.extra?.router?.platformRoutes, asJson: true, appDir, }); @@ -219,10 +221,12 @@ export class MetroBundlerDevServer extends BundlerDevServer { } ); + const { exp } = getConfig(this.projectRoot); + return { serverManifest: await getBuildTimeServerManifestAsync(), // Get routes from Expo Router. - manifest: await getManifest({ preserveApiRoutes: false }), + manifest: await getManifest({ preserveApiRoutes: false, ...exp.extra?.router }), // Get route generating function async renderAsync(path: string) { return await getStaticContent(new URL(path, url)); @@ -574,6 +578,7 @@ export class MetroBundlerDevServer extends BundlerDevServer { appDir, routerRoot, config, + ...config.exp.extra?.router, bundleApiRoute: (functionFilePath) => this.ssrImportApiRoute(functionFilePath), getStaticPageAsync: (pathname) => { return this.getStaticPageAsync(pathname); diff --git a/packages/@expo/cli/src/start/server/metro/createServerRouteMiddleware.ts b/packages/@expo/cli/src/start/server/metro/createServerRouteMiddleware.ts index 07ca68dcacfee..ebbd660c9b1ee 100644 --- a/packages/@expo/cli/src/start/server/metro/createServerRouteMiddleware.ts +++ b/packages/@expo/cli/src/start/server/metro/createServerRouteMiddleware.ts @@ -33,7 +33,7 @@ export function createRouteHandlerMiddleware( functionFilePath: string ) => Promise | Response>; config: ProjectConfig; - } + } & import('expo-router/build/routes-manifest').Options ) { if (!resolveFrom.silent(projectRoot, 'expo-router')) { throw new CommandError( diff --git a/packages/@expo/cli/src/start/server/metro/fetchRouterManifest.ts b/packages/@expo/cli/src/start/server/metro/fetchRouterManifest.ts index 1b141510afbac..bd7bfae2779c0 100644 --- a/packages/@expo/cli/src/start/server/metro/fetchRouterManifest.ts +++ b/packages/@expo/cli/src/start/server/metro/fetchRouterManifest.ts @@ -30,12 +30,15 @@ function getExpoRouteManifestBuilderAsync(projectRoot: string) { // TODO: Simplify this now that we use Node.js directly, no need for the Metro bundler caching layer. export async function fetchManifest( projectRoot: string, - options: { asJson?: boolean; appDir: string } + options: { + asJson?: boolean; + appDir: string; + } & import('expo-router/build/routes-manifest').Options ): Promise | null> { const getManifest = getExpoRouteManifestBuilderAsync(projectRoot); const paths = getRoutePaths(options.appDir); // Get the serialized manifest - const jsonManifest = getManifest(paths); + const jsonManifest = getManifest(paths, options); if (!jsonManifest) { return null; diff --git a/packages/expo-router/CHANGELOG.md b/packages/expo-router/CHANGELOG.md index 898f050a71960..176b0ea3f26f8 100644 --- a/packages/expo-router/CHANGELOG.md +++ b/packages/expo-router/CHANGELOG.md @@ -31,6 +31,7 @@ _This version does not introduce any user-facing changes._ - Mark React client components with "use client" directives. ([#27300](https://github.com/expo/expo/pull/27300) by [@EvanBacon](https://github.com/EvanBacon)) - Add URL hash support ([#27105](https://github.com/expo/expo/pull/27105) by [@marklawlor](https://github.com/marklawlor)) - Type `Href` is no longer generic ([#27690](https://github.com/expo/expo/pull/27690) by [@marklawlor](https://github.com/marklawlor)) +- Allow platform extensions for layout and route files ([#27408](https://github.com/expo/expo/pull/27408) by [@marklawlor](https://github.com/marklawlor)) ### 🐛 Bug fixes diff --git a/packages/expo-router/_ctx.android.js b/packages/expo-router/_ctx.android.js index d0830afd34a3d..1b2a005e2de31 100644 --- a/packages/expo-router/_ctx.android.js +++ b/packages/expo-router/_ctx.android.js @@ -1,6 +1,6 @@ export const ctx = require.context( process.env.EXPO_ROUTER_APP_ROOT, true, - /^(?:\.\/)(?!(?:(?:(?:.*\+api)|(?:\+html)))\.[tj]sx?$).*\.[tj]sx?$/, + /^(?:\.\/)(?!(?:(?:(?:.*\+api)|(?:\+html)))\.[tj]sx?$).*(?:\.ios|\.web)\.[tj]sx?$/, process.env.EXPO_ROUTER_IMPORT_MODE ); diff --git a/packages/expo-router/_ctx.ios.js b/packages/expo-router/_ctx.ios.js index d0830afd34a3d..f35780090224e 100644 --- a/packages/expo-router/_ctx.ios.js +++ b/packages/expo-router/_ctx.ios.js @@ -1,6 +1,6 @@ export const ctx = require.context( process.env.EXPO_ROUTER_APP_ROOT, true, - /^(?:\.\/)(?!(?:(?:(?:.*\+api)|(?:\+html)))\.[tj]sx?$).*\.[tj]sx?$/, + /^(?:\.\/)(?!(?:(?:(?:.*\+api)|(?:\+html)))\.[tj]sx?$).*(?:\.android|\.web)?\.[tj]sx?$/, process.env.EXPO_ROUTER_IMPORT_MODE ); diff --git a/packages/expo-router/_ctx.web.js b/packages/expo-router/_ctx.web.js index d0830afd34a3d..5936dd3a66a57 100644 --- a/packages/expo-router/_ctx.web.js +++ b/packages/expo-router/_ctx.web.js @@ -1,6 +1,6 @@ export const ctx = require.context( process.env.EXPO_ROUTER_APP_ROOT, true, - /^(?:\.\/)(?!(?:(?:(?:.*\+api)|(?:\+html)))\.[tj]sx?$).*\.[tj]sx?$/, + /^(?:\.\/)(?!(?:(?:(?:.*\+api)|(?:\+html)))\.[tj]sx?$).*(?:\.android|\.ios|\.native)?\.[tj]sx?$/, process.env.EXPO_ROUTER_IMPORT_MODE ); diff --git a/packages/expo-router/build/getRoutes.d.ts b/packages/expo-router/build/getRoutes.d.ts index f776cd63ce888..3673cb8616b8e 100644 --- a/packages/expo-router/build/getRoutes.d.ts +++ b/packages/expo-router/build/getRoutes.d.ts @@ -8,6 +8,8 @@ export type Options = { internal_stripLoadRoute?: boolean; skipGenerated?: boolean; importMode?: string; + platformRoutes?: boolean; + platform?: string; }; /** * Given a Metro context module, return an array of nested routes. diff --git a/packages/expo-router/build/getRoutes.d.ts.map b/packages/expo-router/build/getRoutes.d.ts.map index 50f69c3730dc4..f83281fafa874 100644 --- a/packages/expo-router/build/getRoutes.d.ts.map +++ b/packages/expo-router/build/getRoutes.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"getRoutes.d.ts","sourceRoot":"","sources":["../src/getRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAQvD,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,OAAO,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAQF;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,aAAa,EAAE,cAAc,EAAE,OAAO,GAAE,OAAY,GAAG,SAAS,GAAG,IAAI,CAehG;AAED,wBAAgB,cAAc,CAC5B,aAAa,EAAE,cAAc,EAC7B,OAAO,GAAE,OAAY,GACpB,SAAS,GAAG,IAAI,CAKlB;AAqSD,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,OAAO,YAM9C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,GAAG,GAAG,CAAC,MAAM,CAAC,CAwBzF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,GAAG,IAAI,CAqBxE"} \ No newline at end of file +{"version":3,"file":"getRoutes.d.ts","sourceRoot":"","sources":["../src/getRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAQvD,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,OAAO,GAAG;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAE5B,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAUF;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,aAAa,EAAE,cAAc,EAAE,OAAO,GAAE,OAAY,GAAG,SAAS,GAAG,IAAI,CAehG;AAED,wBAAgB,cAAc,CAC5B,aAAa,EAAE,cAAc,EAC7B,OAAO,GAAE,OAAY,GACpB,SAAS,GAAG,IAAI,CAKlB;AAsUD,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,OAAO,YAM9C;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,GAAG,GAAG,CAAC,MAAM,CAAC,CAwBzF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,GAAG,IAAI,CAqBxE"} \ No newline at end of file diff --git a/packages/expo-router/build/getRoutes.js b/packages/expo-router/build/getRoutes.js index 57772a504d428..b949084e80961 100644 --- a/packages/expo-router/build/getRoutes.js +++ b/packages/expo-router/build/getRoutes.js @@ -2,6 +2,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.generateDynamic = exports.extrapolateGroups = exports.getIgnoreList = exports.getExactRoutes = exports.getRoutes = void 0; const matchers_1 = require("./matchers"); +const validPlatforms = new Set(['android', 'ios', 'native', 'web']); /** * Given a Metro context module, return an array of nested routes. * @@ -57,7 +58,7 @@ function getDirectoryTree(contextModule, options) { continue; } isValid = true; - const meta = getFileMeta(filePath); + const meta = getFileMeta(filePath, options); // This is a file that should be ignored. e.g maybe it has an invalid platform? if (meta.specificity < 0) { continue; @@ -135,7 +136,7 @@ function getDirectoryTree(contextModule, options) { nodes = []; directory.files.set(fileKey, nodes); } - // TODO(Platform Route): Throw error if specificity > 0, as you cannot specify platform extensions for api routes + // API Routes have no specificity, they are always the first node const existing = nodes[0]; if (existing) { // In production, use the first route found @@ -220,7 +221,7 @@ pathToRemove = '') { */ if (directory.layout) { const previousLayout = layout; - layout = directory.layout[0]; // TODO(Platform Routes): We need to pick the most specific layout. + layout = getMostSpecific(directory.layout); // Add the new layout as a child of its parent if (previousLayout) { previousLayout.children.push(layout); @@ -239,8 +240,7 @@ pathToRemove = '') { if (!layout) throw new Error('Expo Router Internal Error: No nearest layout'); for (const routes of directory.files.values()) { - // TODO(Platform Routes): We need to pick the most specific layout and ensure that all routes have a non-platform route. - const routeNode = routes[0]; + const routeNode = getMostSpecific(routes); // `route` is the absolute pathname. We need to make this relative to the nearest layout routeNode.route = routeNode.route.replace(pathToRemove, ''); routeNode.dynamic = generateDynamic(routeNode.route); @@ -255,14 +255,15 @@ pathToRemove = '') { } return layout; } -function getFileMeta(key) { +function getFileMeta(key, options) { // Remove the leading `./` key = key.replace(/^\.\//, ''); const parts = key.split('/'); + let route = (0, matchers_1.removeSupportedExtensions)(key); const filename = parts[parts.length - 1]; const filenameWithoutExtensions = (0, matchers_1.removeSupportedExtensions)(filename); const isLayout = filenameWithoutExtensions === '_layout'; - const isApi = filename.match(/\+api\.[jt]sx?$/); + const isApi = filename.match(/\+api\.(\w+\.)?[jt]sx?$/); if (filenameWithoutExtensions.startsWith('(') && filenameWithoutExtensions.endsWith(')')) { throw new Error(`Invalid route ./${key}. Routes cannot end with '(group)' syntax`); } @@ -271,9 +272,41 @@ function getFileMeta(key) { const renamedRoute = [...parts.slice(0, -1), filename.slice(1)].join('/'); throw new Error(`Invalid route ./${key}. Route nodes cannot start with the '+' character. "Please rename to ${renamedRoute}"`); } + let specificity = 0; + const platformExtension = filenameWithoutExtensions.split('.')[1]; + const hasPlatformExtension = validPlatforms.has(platformExtension); + const usePlatformRoutes = options.platformRoutes ?? true; + if (hasPlatformExtension) { + if (!usePlatformRoutes) { + // If the user has disabled platform routes, then we should ignore this file + specificity = -1; + } + else if (!options.platform) { + // If we don't have a platform, then we should ignore this file + // This used by typed routes, sitemap, etc + specificity = -1; + } + else if (platformExtension === options.platform) { + // If the platform extension is the same as the options.platform, then it is the most specific + specificity = 2; + } + else if (platformExtension === 'native' && options.platform !== 'web') { + // `native` is allow but isn't as specific as the platform + specificity = 1; + } + else if (platformExtension !== options.platform) { + // Somehow we have a platform extension that doesn't match the options.platform and it isn't native + // This is an invalid file and we will ignore it + specificity = -1; + } + if (isApi && specificity !== 0) { + throw new Error(`Api routes cannot have platform extensions. Please remove '.${platformExtension}' from './${key}'`); + } + route = route.replace(new RegExp(`.${platformExtension}$`), ''); + } return { - route: (0, matchers_1.removeSupportedExtensions)(key), - specificity: 0, + route, + specificity, isLayout, isApi, }; @@ -457,4 +490,13 @@ function crawlAndAppendInitialRoutesAndEntryFiles(node, options, entryPoints = [ } } } +function getMostSpecific(routes) { + const route = routes[routes.length - 1]; + if (!routes[0]) { + throw new Error(`The file ${route.contextKey} does not have a fallback sibling file without a platform extension.`); + } + // This works even tho routes is holey array (e.g it might have index 0 and 2 but not 1) + // `.length` includes the holes in its count + return routes[routes.length - 1]; +} //# sourceMappingURL=getRoutes.js.map \ No newline at end of file diff --git a/packages/expo-router/build/getRoutes.js.map b/packages/expo-router/build/getRoutes.js.map index cb103d8c20749..47474aab4c1c4 100644 --- a/packages/expo-router/build/getRoutes.js.map +++ b/packages/expo-router/build/getRoutes.js.map @@ -1 +1 @@ -{"version":3,"file":"getRoutes.js","sourceRoot":"","sources":["../src/getRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["import { DynamicConvention, RouteNode } from './Route';\nimport {\n matchArrayGroupName,\n matchDeepDynamicRouteName,\n matchDynamicName,\n matchGroupName,\n removeSupportedExtensions,\n} from './matchers';\nimport { RequireContext } from './types';\n\nexport type Options = {\n ignore?: RegExp[];\n preserveApiRoutes?: boolean;\n ignoreRequireErrors?: boolean;\n ignoreEntryPoints?: boolean;\n /* Used to simplify testing for toEqual() comparison */\n internal_stripLoadRoute?: boolean;\n /* Used to simplify by skipping the generated routes */\n skipGenerated?: boolean;\n importMode?: string;\n};\n\ntype DirectoryNode = {\n layout?: RouteNode[];\n files: Map;\n subdirectories: Map;\n};\n\n/**\n * Given a Metro context module, return an array of nested routes.\n *\n * This is a two step process:\n * 1. Convert the RequireContext keys (file paths) into a directory tree.\n * - This should extrapolate array syntax into multiple routes\n * - Routes are given a specificity score\n * 2. Flatten the directory tree into routes\n * - Routes in directories without _layout files are hoisted to the nearest _layout\n * - The name of the route is relative to the nearest _layout\n * - If multiple routes have the same name, the most specific route is used\n */\nexport function getRoutes(contextModule: RequireContext, options: Options = {}): RouteNode | null {\n const directoryTree = getDirectoryTree(contextModule, options);\n\n // If there are no routes\n if (!directoryTree) {\n return null;\n }\n\n const rootNode = flattenDirectoryTreeToRoutes(directoryTree, options);\n\n if (!options.ignoreEntryPoints) {\n crawlAndAppendInitialRoutesAndEntryFiles(rootNode, options);\n }\n\n return rootNode;\n}\n\nexport function getExactRoutes(\n contextModule: RequireContext,\n options: Options = {}\n): RouteNode | null {\n return getRoutes(contextModule, {\n ...options,\n skipGenerated: true,\n });\n}\n\n/**\n * Converts the RequireContext keys (file paths) into a directory tree.\n */\nfunction getDirectoryTree(contextModule: RequireContext, options: Options) {\n const importMode = options.importMode || process.env.EXPO_ROUTER_IMPORT_MODE;\n\n const ignoreList: RegExp[] = [/^\\.\\/\\+html\\.[tj]sx?$/]; // Ignore the top level ./+html file\n\n if (options.ignore) {\n ignoreList.push(...options.ignore);\n }\n if (!options.preserveApiRoutes) {\n ignoreList.push(/\\+api\\.[tj]sx?$/);\n }\n\n const rootDirectory: DirectoryNode = {\n files: new Map(),\n subdirectories: new Map(),\n };\n\n let hasRoutes = false;\n let isValid = false;\n\n for (const filePath of contextModule.keys()) {\n if (ignoreList.some((regex) => regex.test(filePath))) {\n continue;\n }\n\n isValid = true;\n\n const meta = getFileMeta(filePath);\n\n // This is a file that should be ignored. e.g maybe it has an invalid platform?\n if (meta.specificity < 0) {\n continue;\n }\n\n let node: RouteNode = {\n type: meta.isApi ? 'api' : meta.isLayout ? 'layout' : 'route',\n loadRoute() {\n if (options.ignoreRequireErrors) {\n try {\n return contextModule(filePath);\n } catch {\n return {};\n }\n } else {\n return contextModule(filePath);\n }\n },\n contextKey: filePath,\n route: '', // This is overwritten during hoisting based upon the _layout\n dynamic: null,\n children: [], // While we are building the directory tree, we don't know the node's children just yet. This is added during hoisting\n };\n\n if (process.env.NODE_ENV === 'development') {\n // If the user has set the `EXPO_ROUTER_IMPORT_MODE` to `sync` then we should\n // filter the missing routes.\n if (node.type !== 'api' && importMode === 'sync') {\n if (!node.loadRoute()?.default) {\n continue;\n }\n }\n }\n\n /**\n * A single filepath may be extrapolated into multiple routes if it contains array syntax.\n * Another way to thinking about is that a filepath node is present in multiple leaves of the directory tree.\n */\n for (const route of extrapolateGroups(meta.route)) {\n // Traverse the directory tree to its leaf node, creating any missing directories along the way\n const subdirectoryParts = route.split('/').slice(0, -1);\n\n // Start at the root directory and traverse the path to the leaf directory\n let directory = rootDirectory;\n\n for (const part of subdirectoryParts) {\n let subDirectory = directory.subdirectories.get(part);\n\n // Create any missing subdirectories\n if (!subDirectory) {\n subDirectory = {\n files: new Map(),\n subdirectories: new Map(),\n };\n directory.subdirectories.set(part, subDirectory);\n }\n\n directory = subDirectory;\n }\n\n // Clone the node for this route\n node = { ...node, route };\n\n if (meta.isLayout) {\n directory.layout ??= [];\n const existing = directory.layout[meta.specificity];\n if (existing) {\n // In production, use the first route found\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `The layouts \"${filePath}\" and \"${existing.contextKey}\" conflict on the route \"/${route}\". Please remove or rename one of these files.`\n );\n }\n } else {\n node = getLayoutNode(node, options);\n directory.layout[meta.specificity] = node;\n }\n } else if (meta.isApi) {\n const fileKey = `${route}+api`;\n let nodes = directory.files.get(fileKey);\n\n if (!nodes) {\n nodes = [];\n directory.files.set(fileKey, nodes);\n }\n\n // TODO(Platform Route): Throw error if specificity > 0, as you cannot specify platform extensions for api routes\n\n const existing = nodes[0];\n\n if (existing) {\n // In production, use the first route found\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `The API route file \"${filePath}\" and \"${existing.contextKey}\" conflict on the route \"/${route}\". Please remove or rename one of these files.`\n );\n }\n } else {\n nodes[0] = node;\n }\n } else {\n let nodes = directory.files.get(route);\n\n if (!nodes) {\n nodes = [];\n directory.files.set(route, nodes);\n }\n\n /**\n * If there is an existing node with the same specificity, then we have a conflict.\n * NOTE(Platform Routes):\n * We cannot check for specificity conflicts here, as we haven't processed all the context keys yet!\n * This will be checked during hoisting, as well as enforcing that all routes have a non-platform route.\n */\n const existing = nodes[meta.specificity];\n if (existing) {\n // In production, use the first route found\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `The route files \"${filePath}\" and \"${existing.contextKey}\" conflict on the route \"/${route}\". Please remove or rename one of these files.`\n );\n }\n } else {\n hasRoutes ||= true;\n nodes[meta.specificity] = node;\n }\n }\n }\n }\n\n // If there are no routes/layouts then we should display the tutorial.\n if (!isValid) {\n return null;\n }\n\n /**\n * If there are no top-level _layout, add a default _layout\n * While this is a generated route, it will still be generated even if skipGenerated is true.\n */\n if (!rootDirectory.layout) {\n rootDirectory.layout = [\n {\n type: 'layout',\n loadRoute: () => ({\n default: (require('./views/Navigator') as typeof import('./views/Navigator'))\n .DefaultNavigator,\n }),\n // Generate a fake file name for the directory\n contextKey: 'expo-router/build/views/Navigator.js',\n route: '',\n generated: true,\n dynamic: null,\n children: [],\n },\n ];\n }\n\n // Only include the sitemap if there are routes.\n if (!options.skipGenerated) {\n if (hasRoutes) {\n appendSitemapRoute(rootDirectory);\n }\n appendNotFoundRoute(rootDirectory);\n }\n return rootDirectory;\n}\n\n/**\n * Flatten the directory tree into routes, hoisting routes to the nearest _layout.\n */\nfunction flattenDirectoryTreeToRoutes(\n directory: DirectoryNode,\n options: Options,\n /* The nearest _layout file in the directory tree */\n layout?: RouteNode,\n /* Route names are relative to their layout */\n pathToRemove = ''\n) {\n /**\n * This directory has a _layout file so it becomes the new target for hoisting routes.\n */\n if (directory.layout) {\n const previousLayout = layout;\n layout = directory.layout[0]; // TODO(Platform Routes): We need to pick the most specific layout.\n\n // Add the new layout as a child of its parent\n if (previousLayout) {\n previousLayout.children.push(layout);\n }\n\n if (options.internal_stripLoadRoute) {\n delete (layout as any).loadRoute;\n }\n\n // `route` is the absolute pathname. We need to make this relative to the last _layout\n const newRoute = layout.route.replace(pathToRemove, '');\n pathToRemove = layout.route ? `${layout.route}/` : '';\n\n // Now update this layout with the new relative route and dynamic conventions\n layout.route = newRoute;\n layout.dynamic = generateDynamic(layout.route);\n }\n\n // This should never occur as there will always be a root layout, but it makes the type system happy\n if (!layout) throw new Error('Expo Router Internal Error: No nearest layout');\n\n for (const routes of directory.files.values()) {\n // TODO(Platform Routes): We need to pick the most specific layout and ensure that all routes have a non-platform route.\n const routeNode = routes[0];\n\n // `route` is the absolute pathname. We need to make this relative to the nearest layout\n routeNode.route = routeNode.route.replace(pathToRemove, '');\n routeNode.dynamic = generateDynamic(routeNode.route);\n\n if (options.internal_stripLoadRoute) {\n delete (routeNode as any).loadRoute;\n }\n\n layout.children.push(routeNode);\n }\n\n // Recursively flatten the subdirectories\n for (const child of directory.subdirectories.values()) {\n flattenDirectoryTreeToRoutes(child, options, layout, pathToRemove);\n }\n\n return layout;\n}\n\nfunction getFileMeta(key: string) {\n // Remove the leading `./`\n key = key.replace(/^\\.\\//, '');\n\n const parts = key.split('/');\n const filename = parts[parts.length - 1];\n const filenameWithoutExtensions = removeSupportedExtensions(filename);\n const isLayout = filenameWithoutExtensions === '_layout';\n const isApi = filename.match(/\\+api\\.[jt]sx?$/);\n\n if (filenameWithoutExtensions.startsWith('(') && filenameWithoutExtensions.endsWith(')')) {\n throw new Error(`Invalid route ./${key}. Routes cannot end with '(group)' syntax`);\n }\n\n // Nested routes cannot start with the '+' character, except for the '+not-found' route\n if (!isApi && filename.startsWith('+') && filenameWithoutExtensions !== '+not-found') {\n const renamedRoute = [...parts.slice(0, -1), filename.slice(1)].join('/');\n throw new Error(\n `Invalid route ./${key}. Route nodes cannot start with the '+' character. \"Please rename to ${renamedRoute}\"`\n );\n }\n\n return {\n route: removeSupportedExtensions(key),\n specificity: 0,\n isLayout,\n isApi,\n };\n}\n\nexport function getIgnoreList(options?: Options) {\n const ignore: RegExp[] = [/^\\.\\/\\+html\\.[tj]sx?$/, ...(options?.ignore ?? [])];\n if (options?.preserveApiRoutes !== true) {\n ignore.push(/\\+api\\.[tj]sx?$/);\n }\n return ignore;\n}\n\n/**\n * Generates a set of strings which have the router array syntax extrapolated.\n *\n * /(a,b)/(c,d)/e.tsx => new Set(['a/c/e.tsx', 'a/d/e.tsx', 'b/c/e.tsx', 'b/d/e.tsx'])\n */\nexport function extrapolateGroups(key: string, keys: Set = new Set()): Set {\n const match = matchArrayGroupName(key);\n\n if (!match) {\n keys.add(key);\n return keys;\n }\n const groups = match.split(',');\n const groupsSet = new Set(groups);\n\n if (groupsSet.size !== groups.length) {\n throw new Error(`Array syntax cannot contain duplicate group name \"${groups}\" in \"${key}\".`);\n }\n\n if (groups.length === 1) {\n keys.add(key);\n return keys;\n }\n\n for (const group of groups) {\n extrapolateGroups(key.replace(match, group.trim()), keys);\n }\n\n return keys;\n}\n\nexport function generateDynamic(path: string): DynamicConvention[] | null {\n const dynamic = path\n .split('/')\n .map((part): DynamicConvention | null => {\n if (part === '+not-found') {\n return {\n name: '+not-found',\n deep: true,\n notFound: true,\n };\n }\n\n const deepDynamicName = matchDeepDynamicRouteName(part);\n const dynamicName = deepDynamicName ?? matchDynamicName(part);\n\n if (!dynamicName) return null;\n return { name: dynamicName, deep: !!deepDynamicName };\n })\n .filter((part): part is DynamicConvention => !!part);\n\n return dynamic.length === 0 ? null : dynamic;\n}\n\nfunction appendSitemapRoute(directory: DirectoryNode) {\n if (!directory.files.has('_sitemap')) {\n directory.files.set('_sitemap', [\n {\n loadRoute() {\n const { Sitemap, getNavOptions } = require('./views/Sitemap');\n return { default: Sitemap, getNavOptions };\n },\n route: '_sitemap',\n type: 'route',\n contextKey: 'expo-router/build/views/Sitemap.js',\n generated: true,\n internal: true,\n dynamic: null,\n children: [],\n },\n ]);\n }\n}\n\nfunction appendNotFoundRoute(directory: DirectoryNode) {\n if (!directory.files.has('+not-found')) {\n directory.files.set('+not-found', [\n {\n loadRoute() {\n return { default: require('./views/Unmatched').Unmatched };\n },\n type: 'route',\n route: '+not-found',\n contextKey: 'expo-router/build/views/Unmatched.js',\n generated: true,\n internal: true,\n dynamic: [{ name: '+not-found', deep: true, notFound: true }],\n children: [],\n },\n ]);\n }\n}\n\nfunction getLayoutNode(node: RouteNode, options: Options) {\n /**\n * A file called `(a,b)/(c)/_layout.tsx` will generate two _layout routes: `(a)/(c)/_layout` and `(b)/(c)/_layout`.\n * Each of these layouts will have a different initialRouteName based upon the first group name.\n *\n * So\n */\n\n // We may strip loadRoute during testing\n const groupName = matchGroupName(node.route);\n const childMatchingGroup = node.children.find((child) => {\n return child.route.replace(/\\/index$/, '') === groupName;\n });\n let initialRouteName = childMatchingGroup?.route;\n const loaded = node.loadRoute();\n if (loaded?.unstable_settings) {\n // Allow unstable_settings={ initialRouteName: '...' } to override the default initial route name.\n initialRouteName = loaded.unstable_settings.initialRouteName ?? initialRouteName;\n\n if (groupName) {\n // Allow unstable_settings={ 'custom': { initialRouteName: '...' } } to override the less specific initial route name.\n const groupSpecificInitialRouteName = loaded.unstable_settings?.[groupName]?.initialRouteName;\n\n initialRouteName = groupSpecificInitialRouteName ?? initialRouteName;\n }\n }\n\n return {\n ...node,\n route: node.route.replace(/\\/?_layout$/, ''),\n children: [], // Each layout should have its own children\n initialRouteName,\n };\n}\n\nfunction crawlAndAppendInitialRoutesAndEntryFiles(\n node: RouteNode,\n options: Options,\n entryPoints: string[] = []\n) {\n if (node.type === 'route') {\n node.entryPoints = [...new Set([...entryPoints, node.contextKey])];\n } else if (node.type === 'layout') {\n if (!node.children) {\n throw new Error(`Layout \"${node.contextKey}\" does not contain any child routes`);\n }\n\n // Every node below this layout will have it as an entryPoint\n entryPoints = [...entryPoints, node.contextKey];\n\n /**\n * Calculate the initialRouteNode\n *\n * A file called `(a,b)/(c)/_layout.tsx` will generate two _layout routes: `(a)/(c)/_layout` and `(b)/(c)/_layout`.\n * Each of these layouts will have a different initialRouteName based upon the first group.\n */\n const groupName = matchGroupName(node.route);\n const childMatchingGroup = node.children.find((child) => {\n return child.route.replace(/\\/index$/, '') === groupName;\n });\n let initialRouteName = childMatchingGroup?.route;\n // We may strip loadRoute during testing\n if (!options.internal_stripLoadRoute) {\n const loaded = node.loadRoute();\n if (loaded?.unstable_settings) {\n // Allow unstable_settings={ initialRouteName: '...' } to override the default initial route name.\n initialRouteName = loaded.unstable_settings.initialRouteName ?? initialRouteName;\n\n if (groupName) {\n // Allow unstable_settings={ 'custom': { initialRouteName: '...' } } to override the less specific initial route name.\n const groupSpecificInitialRouteName =\n loaded.unstable_settings?.[groupName]?.initialRouteName;\n\n initialRouteName = groupSpecificInitialRouteName ?? initialRouteName;\n }\n }\n }\n\n if (initialRouteName) {\n const initialRoute = node.children.find((child) => child.route === initialRouteName);\n if (!initialRoute) {\n const validInitialRoutes = node.children\n .filter((child) => !child.generated)\n .map((child) => `'${child.route}'`)\n .join(', ');\n\n if (groupName) {\n throw new Error(\n `Layout ${node.contextKey} has invalid initialRouteName '${initialRouteName}' for group '(${groupName})'. Valid options are: ${validInitialRoutes}`\n );\n } else {\n throw new Error(\n `Layout ${node.contextKey} has invalid initialRouteName '${initialRouteName}'. Valid options are: ${validInitialRoutes}`\n );\n }\n }\n\n // Navigators can add initialsRoutes into the history, so they need to be to be included in the entryPoints\n node.initialRouteName = initialRouteName;\n entryPoints.push(initialRoute.contextKey);\n }\n\n for (const child of node.children) {\n crawlAndAppendInitialRoutesAndEntryFiles(child, options, entryPoints);\n }\n }\n}\n"]} \ No newline at end of file +{"version":3,"file":"getRoutes.js","sourceRoot":"","sources":["../src/getRoutes.ts"],"names":[],"mappings":"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","sourcesContent":["import { DynamicConvention, RouteNode } from './Route';\nimport {\n matchArrayGroupName,\n matchDeepDynamicRouteName,\n matchDynamicName,\n matchGroupName,\n removeSupportedExtensions,\n} from './matchers';\nimport { RequireContext } from './types';\n\nexport type Options = {\n ignore?: RegExp[];\n preserveApiRoutes?: boolean;\n ignoreRequireErrors?: boolean;\n ignoreEntryPoints?: boolean;\n /* Used to simplify testing for toEqual() comparison */\n internal_stripLoadRoute?: boolean;\n /* Used to simplify by skipping the generated routes */\n skipGenerated?: boolean;\n importMode?: string;\n platformRoutes?: boolean;\n platform?: string;\n};\n\ntype DirectoryNode = {\n layout?: RouteNode[];\n files: Map;\n subdirectories: Map;\n};\n\nconst validPlatforms = new Set(['android', 'ios', 'native', 'web']);\n\n/**\n * Given a Metro context module, return an array of nested routes.\n *\n * This is a two step process:\n * 1. Convert the RequireContext keys (file paths) into a directory tree.\n * - This should extrapolate array syntax into multiple routes\n * - Routes are given a specificity score\n * 2. Flatten the directory tree into routes\n * - Routes in directories without _layout files are hoisted to the nearest _layout\n * - The name of the route is relative to the nearest _layout\n * - If multiple routes have the same name, the most specific route is used\n */\nexport function getRoutes(contextModule: RequireContext, options: Options = {}): RouteNode | null {\n const directoryTree = getDirectoryTree(contextModule, options);\n\n // If there are no routes\n if (!directoryTree) {\n return null;\n }\n\n const rootNode = flattenDirectoryTreeToRoutes(directoryTree, options);\n\n if (!options.ignoreEntryPoints) {\n crawlAndAppendInitialRoutesAndEntryFiles(rootNode, options);\n }\n\n return rootNode;\n}\n\nexport function getExactRoutes(\n contextModule: RequireContext,\n options: Options = {}\n): RouteNode | null {\n return getRoutes(contextModule, {\n ...options,\n skipGenerated: true,\n });\n}\n\n/**\n * Converts the RequireContext keys (file paths) into a directory tree.\n */\nfunction getDirectoryTree(contextModule: RequireContext, options: Options) {\n const importMode = options.importMode || process.env.EXPO_ROUTER_IMPORT_MODE;\n\n const ignoreList: RegExp[] = [/^\\.\\/\\+html\\.[tj]sx?$/]; // Ignore the top level ./+html file\n\n if (options.ignore) {\n ignoreList.push(...options.ignore);\n }\n if (!options.preserveApiRoutes) {\n ignoreList.push(/\\+api\\.[tj]sx?$/);\n }\n\n const rootDirectory: DirectoryNode = {\n files: new Map(),\n subdirectories: new Map(),\n };\n\n let hasRoutes = false;\n let isValid = false;\n\n for (const filePath of contextModule.keys()) {\n if (ignoreList.some((regex) => regex.test(filePath))) {\n continue;\n }\n\n isValid = true;\n\n const meta = getFileMeta(filePath, options);\n\n // This is a file that should be ignored. e.g maybe it has an invalid platform?\n if (meta.specificity < 0) {\n continue;\n }\n\n let node: RouteNode = {\n type: meta.isApi ? 'api' : meta.isLayout ? 'layout' : 'route',\n loadRoute() {\n if (options.ignoreRequireErrors) {\n try {\n return contextModule(filePath);\n } catch {\n return {};\n }\n } else {\n return contextModule(filePath);\n }\n },\n contextKey: filePath,\n route: '', // This is overwritten during hoisting based upon the _layout\n dynamic: null,\n children: [], // While we are building the directory tree, we don't know the node's children just yet. This is added during hoisting\n };\n\n if (process.env.NODE_ENV === 'development') {\n // If the user has set the `EXPO_ROUTER_IMPORT_MODE` to `sync` then we should\n // filter the missing routes.\n if (node.type !== 'api' && importMode === 'sync') {\n if (!node.loadRoute()?.default) {\n continue;\n }\n }\n }\n\n /**\n * A single filepath may be extrapolated into multiple routes if it contains array syntax.\n * Another way to thinking about is that a filepath node is present in multiple leaves of the directory tree.\n */\n for (const route of extrapolateGroups(meta.route)) {\n // Traverse the directory tree to its leaf node, creating any missing directories along the way\n const subdirectoryParts = route.split('/').slice(0, -1);\n\n // Start at the root directory and traverse the path to the leaf directory\n let directory = rootDirectory;\n\n for (const part of subdirectoryParts) {\n let subDirectory = directory.subdirectories.get(part);\n\n // Create any missing subdirectories\n if (!subDirectory) {\n subDirectory = {\n files: new Map(),\n subdirectories: new Map(),\n };\n directory.subdirectories.set(part, subDirectory);\n }\n\n directory = subDirectory;\n }\n\n // Clone the node for this route\n node = { ...node, route };\n\n if (meta.isLayout) {\n directory.layout ??= [];\n const existing = directory.layout[meta.specificity];\n if (existing) {\n // In production, use the first route found\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `The layouts \"${filePath}\" and \"${existing.contextKey}\" conflict on the route \"/${route}\". Please remove or rename one of these files.`\n );\n }\n } else {\n node = getLayoutNode(node, options);\n directory.layout[meta.specificity] = node;\n }\n } else if (meta.isApi) {\n const fileKey = `${route}+api`;\n let nodes = directory.files.get(fileKey);\n\n if (!nodes) {\n nodes = [];\n directory.files.set(fileKey, nodes);\n }\n\n // API Routes have no specificity, they are always the first node\n const existing = nodes[0];\n\n if (existing) {\n // In production, use the first route found\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `The API route file \"${filePath}\" and \"${existing.contextKey}\" conflict on the route \"/${route}\". Please remove or rename one of these files.`\n );\n }\n } else {\n nodes[0] = node;\n }\n } else {\n let nodes = directory.files.get(route);\n\n if (!nodes) {\n nodes = [];\n directory.files.set(route, nodes);\n }\n\n /**\n * If there is an existing node with the same specificity, then we have a conflict.\n * NOTE(Platform Routes):\n * We cannot check for specificity conflicts here, as we haven't processed all the context keys yet!\n * This will be checked during hoisting, as well as enforcing that all routes have a non-platform route.\n */\n const existing = nodes[meta.specificity];\n if (existing) {\n // In production, use the first route found\n if (process.env.NODE_ENV !== 'production') {\n throw new Error(\n `The route files \"${filePath}\" and \"${existing.contextKey}\" conflict on the route \"/${route}\". Please remove or rename one of these files.`\n );\n }\n } else {\n hasRoutes ||= true;\n nodes[meta.specificity] = node;\n }\n }\n }\n }\n\n // If there are no routes/layouts then we should display the tutorial.\n if (!isValid) {\n return null;\n }\n\n /**\n * If there are no top-level _layout, add a default _layout\n * While this is a generated route, it will still be generated even if skipGenerated is true.\n */\n if (!rootDirectory.layout) {\n rootDirectory.layout = [\n {\n type: 'layout',\n loadRoute: () => ({\n default: (require('./views/Navigator') as typeof import('./views/Navigator'))\n .DefaultNavigator,\n }),\n // Generate a fake file name for the directory\n contextKey: 'expo-router/build/views/Navigator.js',\n route: '',\n generated: true,\n dynamic: null,\n children: [],\n },\n ];\n }\n\n // Only include the sitemap if there are routes.\n if (!options.skipGenerated) {\n if (hasRoutes) {\n appendSitemapRoute(rootDirectory);\n }\n appendNotFoundRoute(rootDirectory);\n }\n return rootDirectory;\n}\n\n/**\n * Flatten the directory tree into routes, hoisting routes to the nearest _layout.\n */\nfunction flattenDirectoryTreeToRoutes(\n directory: DirectoryNode,\n options: Options,\n /* The nearest _layout file in the directory tree */\n layout?: RouteNode,\n /* Route names are relative to their layout */\n pathToRemove = ''\n) {\n /**\n * This directory has a _layout file so it becomes the new target for hoisting routes.\n */\n if (directory.layout) {\n const previousLayout = layout;\n layout = getMostSpecific(directory.layout);\n\n // Add the new layout as a child of its parent\n if (previousLayout) {\n previousLayout.children.push(layout);\n }\n\n if (options.internal_stripLoadRoute) {\n delete (layout as any).loadRoute;\n }\n\n // `route` is the absolute pathname. We need to make this relative to the last _layout\n const newRoute = layout.route.replace(pathToRemove, '');\n pathToRemove = layout.route ? `${layout.route}/` : '';\n\n // Now update this layout with the new relative route and dynamic conventions\n layout.route = newRoute;\n layout.dynamic = generateDynamic(layout.route);\n }\n\n // This should never occur as there will always be a root layout, but it makes the type system happy\n if (!layout) throw new Error('Expo Router Internal Error: No nearest layout');\n\n for (const routes of directory.files.values()) {\n const routeNode = getMostSpecific(routes);\n\n // `route` is the absolute pathname. We need to make this relative to the nearest layout\n routeNode.route = routeNode.route.replace(pathToRemove, '');\n routeNode.dynamic = generateDynamic(routeNode.route);\n\n if (options.internal_stripLoadRoute) {\n delete (routeNode as any).loadRoute;\n }\n\n layout.children.push(routeNode);\n }\n\n // Recursively flatten the subdirectories\n for (const child of directory.subdirectories.values()) {\n flattenDirectoryTreeToRoutes(child, options, layout, pathToRemove);\n }\n\n return layout;\n}\n\nfunction getFileMeta(key: string, options: Options) {\n // Remove the leading `./`\n key = key.replace(/^\\.\\//, '');\n\n const parts = key.split('/');\n let route = removeSupportedExtensions(key);\n const filename = parts[parts.length - 1];\n const filenameWithoutExtensions = removeSupportedExtensions(filename);\n const isLayout = filenameWithoutExtensions === '_layout';\n const isApi = filename.match(/\\+api\\.(\\w+\\.)?[jt]sx?$/);\n\n if (filenameWithoutExtensions.startsWith('(') && filenameWithoutExtensions.endsWith(')')) {\n throw new Error(`Invalid route ./${key}. Routes cannot end with '(group)' syntax`);\n }\n\n // Nested routes cannot start with the '+' character, except for the '+not-found' route\n if (!isApi && filename.startsWith('+') && filenameWithoutExtensions !== '+not-found') {\n const renamedRoute = [...parts.slice(0, -1), filename.slice(1)].join('/');\n throw new Error(\n `Invalid route ./${key}. Route nodes cannot start with the '+' character. \"Please rename to ${renamedRoute}\"`\n );\n }\n let specificity = 0;\n\n const platformExtension = filenameWithoutExtensions.split('.')[1];\n const hasPlatformExtension = validPlatforms.has(platformExtension);\n const usePlatformRoutes = options.platformRoutes ?? true;\n\n if (hasPlatformExtension) {\n if (!usePlatformRoutes) {\n // If the user has disabled platform routes, then we should ignore this file\n specificity = -1;\n } else if (!options.platform) {\n // If we don't have a platform, then we should ignore this file\n // This used by typed routes, sitemap, etc\n specificity = -1;\n } else if (platformExtension === options.platform) {\n // If the platform extension is the same as the options.platform, then it is the most specific\n specificity = 2;\n } else if (platformExtension === 'native' && options.platform !== 'web') {\n // `native` is allow but isn't as specific as the platform\n specificity = 1;\n } else if (platformExtension !== options.platform) {\n // Somehow we have a platform extension that doesn't match the options.platform and it isn't native\n // This is an invalid file and we will ignore it\n specificity = -1;\n }\n\n if (isApi && specificity !== 0) {\n throw new Error(\n `Api routes cannot have platform extensions. Please remove '.${platformExtension}' from './${key}'`\n );\n }\n\n route = route.replace(new RegExp(`.${platformExtension}$`), '');\n }\n\n return {\n route,\n specificity,\n isLayout,\n isApi,\n };\n}\n\nexport function getIgnoreList(options?: Options) {\n const ignore: RegExp[] = [/^\\.\\/\\+html\\.[tj]sx?$/, ...(options?.ignore ?? [])];\n if (options?.preserveApiRoutes !== true) {\n ignore.push(/\\+api\\.[tj]sx?$/);\n }\n return ignore;\n}\n\n/**\n * Generates a set of strings which have the router array syntax extrapolated.\n *\n * /(a,b)/(c,d)/e.tsx => new Set(['a/c/e.tsx', 'a/d/e.tsx', 'b/c/e.tsx', 'b/d/e.tsx'])\n */\nexport function extrapolateGroups(key: string, keys: Set = new Set()): Set {\n const match = matchArrayGroupName(key);\n\n if (!match) {\n keys.add(key);\n return keys;\n }\n const groups = match.split(',');\n const groupsSet = new Set(groups);\n\n if (groupsSet.size !== groups.length) {\n throw new Error(`Array syntax cannot contain duplicate group name \"${groups}\" in \"${key}\".`);\n }\n\n if (groups.length === 1) {\n keys.add(key);\n return keys;\n }\n\n for (const group of groups) {\n extrapolateGroups(key.replace(match, group.trim()), keys);\n }\n\n return keys;\n}\n\nexport function generateDynamic(path: string): DynamicConvention[] | null {\n const dynamic = path\n .split('/')\n .map((part): DynamicConvention | null => {\n if (part === '+not-found') {\n return {\n name: '+not-found',\n deep: true,\n notFound: true,\n };\n }\n\n const deepDynamicName = matchDeepDynamicRouteName(part);\n const dynamicName = deepDynamicName ?? matchDynamicName(part);\n\n if (!dynamicName) return null;\n return { name: dynamicName, deep: !!deepDynamicName };\n })\n .filter((part): part is DynamicConvention => !!part);\n\n return dynamic.length === 0 ? null : dynamic;\n}\n\nfunction appendSitemapRoute(directory: DirectoryNode) {\n if (!directory.files.has('_sitemap')) {\n directory.files.set('_sitemap', [\n {\n loadRoute() {\n const { Sitemap, getNavOptions } = require('./views/Sitemap');\n return { default: Sitemap, getNavOptions };\n },\n route: '_sitemap',\n type: 'route',\n contextKey: 'expo-router/build/views/Sitemap.js',\n generated: true,\n internal: true,\n dynamic: null,\n children: [],\n },\n ]);\n }\n}\n\nfunction appendNotFoundRoute(directory: DirectoryNode) {\n if (!directory.files.has('+not-found')) {\n directory.files.set('+not-found', [\n {\n loadRoute() {\n return { default: require('./views/Unmatched').Unmatched };\n },\n type: 'route',\n route: '+not-found',\n contextKey: 'expo-router/build/views/Unmatched.js',\n generated: true,\n internal: true,\n dynamic: [{ name: '+not-found', deep: true, notFound: true }],\n children: [],\n },\n ]);\n }\n}\n\nfunction getLayoutNode(node: RouteNode, options: Options) {\n /**\n * A file called `(a,b)/(c)/_layout.tsx` will generate two _layout routes: `(a)/(c)/_layout` and `(b)/(c)/_layout`.\n * Each of these layouts will have a different initialRouteName based upon the first group name.\n *\n * So\n */\n\n // We may strip loadRoute during testing\n const groupName = matchGroupName(node.route);\n const childMatchingGroup = node.children.find((child) => {\n return child.route.replace(/\\/index$/, '') === groupName;\n });\n let initialRouteName = childMatchingGroup?.route;\n const loaded = node.loadRoute();\n if (loaded?.unstable_settings) {\n // Allow unstable_settings={ initialRouteName: '...' } to override the default initial route name.\n initialRouteName = loaded.unstable_settings.initialRouteName ?? initialRouteName;\n\n if (groupName) {\n // Allow unstable_settings={ 'custom': { initialRouteName: '...' } } to override the less specific initial route name.\n const groupSpecificInitialRouteName = loaded.unstable_settings?.[groupName]?.initialRouteName;\n\n initialRouteName = groupSpecificInitialRouteName ?? initialRouteName;\n }\n }\n\n return {\n ...node,\n route: node.route.replace(/\\/?_layout$/, ''),\n children: [], // Each layout should have its own children\n initialRouteName,\n };\n}\n\nfunction crawlAndAppendInitialRoutesAndEntryFiles(\n node: RouteNode,\n options: Options,\n entryPoints: string[] = []\n) {\n if (node.type === 'route') {\n node.entryPoints = [...new Set([...entryPoints, node.contextKey])];\n } else if (node.type === 'layout') {\n if (!node.children) {\n throw new Error(`Layout \"${node.contextKey}\" does not contain any child routes`);\n }\n\n // Every node below this layout will have it as an entryPoint\n entryPoints = [...entryPoints, node.contextKey];\n\n /**\n * Calculate the initialRouteNode\n *\n * A file called `(a,b)/(c)/_layout.tsx` will generate two _layout routes: `(a)/(c)/_layout` and `(b)/(c)/_layout`.\n * Each of these layouts will have a different initialRouteName based upon the first group.\n */\n const groupName = matchGroupName(node.route);\n const childMatchingGroup = node.children.find((child) => {\n return child.route.replace(/\\/index$/, '') === groupName;\n });\n let initialRouteName = childMatchingGroup?.route;\n // We may strip loadRoute during testing\n if (!options.internal_stripLoadRoute) {\n const loaded = node.loadRoute();\n if (loaded?.unstable_settings) {\n // Allow unstable_settings={ initialRouteName: '...' } to override the default initial route name.\n initialRouteName = loaded.unstable_settings.initialRouteName ?? initialRouteName;\n\n if (groupName) {\n // Allow unstable_settings={ 'custom': { initialRouteName: '...' } } to override the less specific initial route name.\n const groupSpecificInitialRouteName =\n loaded.unstable_settings?.[groupName]?.initialRouteName;\n\n initialRouteName = groupSpecificInitialRouteName ?? initialRouteName;\n }\n }\n }\n\n if (initialRouteName) {\n const initialRoute = node.children.find((child) => child.route === initialRouteName);\n if (!initialRoute) {\n const validInitialRoutes = node.children\n .filter((child) => !child.generated)\n .map((child) => `'${child.route}'`)\n .join(', ');\n\n if (groupName) {\n throw new Error(\n `Layout ${node.contextKey} has invalid initialRouteName '${initialRouteName}' for group '(${groupName})'. Valid options are: ${validInitialRoutes}`\n );\n } else {\n throw new Error(\n `Layout ${node.contextKey} has invalid initialRouteName '${initialRouteName}'. Valid options are: ${validInitialRoutes}`\n );\n }\n }\n\n // Navigators can add initialsRoutes into the history, so they need to be to be included in the entryPoints\n node.initialRouteName = initialRouteName;\n entryPoints.push(initialRoute.contextKey);\n }\n\n for (const child of node.children) {\n crawlAndAppendInitialRoutesAndEntryFiles(child, options, entryPoints);\n }\n }\n}\n\nfunction getMostSpecific(routes: RouteNode[]) {\n const route = routes[routes.length - 1];\n\n if (!routes[0]) {\n throw new Error(\n `The file ${route.contextKey} does not have a fallback sibling file without a platform extension.`\n );\n }\n\n // This works even tho routes is holey array (e.g it might have index 0 and 2 but not 1)\n // `.length` includes the holes in its count\n return routes[routes.length - 1];\n}\n"]} \ No newline at end of file diff --git a/packages/expo-router/build/global-state/router-store.d.ts.map b/packages/expo-router/build/global-state/router-store.d.ts.map index 25757ced5cf7f..0279a9b227daf 100644 --- a/packages/expo-router/build/global-state/router-store.d.ts.map +++ b/packages/expo-router/build/global-state/router-store.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"router-store.d.ts","sourceRoot":"","sources":["../../src/global-state/router-store.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,iCAAiC,EAGlC,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAiC,aAAa,EAAY,MAAM,OAAO,CAAC;AAe/E,OAAO,EAAE,SAAS,EAAyB,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAoB,MAAM,qBAAqB,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG1C;;;;GAIG;AACH,qBAAa,WAAW;IACtB,SAAS,EAAG,SAAS,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAG,aAAa,CAAC;IAC9B,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,OAAO,CAAC,wBAAwB,CAAkB;IAElD,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAEpC,aAAa,EAAG,iCAAiC,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;IACjF,yBAAyB,EAAG,MAAM,IAAI,CAAC;IAEvC,oBAAoB,YAAiB,IAAI,EAAI;IAC7C,gBAAgB,YAAiB,IAAI,EAAI;IAEzC,MAAM,MAAqB;IAC3B,eAAe,MAA8B;IAC7C,MAAM,MAAqB;IAC3B,SAAS,MAAwB;IACjC,IAAI,MAAmB;IACvB,OAAO,MAAsB;IAC7B,OAAO,MAAsB;IAC7B,UAAU,MAAyB;IACnC,UAAU,MAAyB;IACnC,SAAS,MAAwB;IACjC,QAAQ,MAAuB;IAE/B,UAAU,CACR,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,iCAAiC,CAAC,eAAe,CAAC,aAAa,CAAC,EAC/E,eAAe,CAAC,EAAE,GAAG;IAgGvB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,cAAQ;IAWjD,YAAY,CAAC,KAAK,EAAE,WAAW;IAgB/B,kBAAkB;IAIlB,uEAAuE;IACvE,oBAAoB,eAAgB,MAAM,IAAI,mBAG5C;IACF,gBAAgB,eAAgB,MAAM,IAAI,mBAGxC;IACF,QAAQ,aAEN;IACF,iBAAiB,oBAEf;IACF,iBAAiB,kBAEf;IAEF,OAAO;CAKR;AAED,eAAO,MAAM,KAAK,aAAoB,CAAC;AAEvC,wBAAgB,aAAa,gBAE5B;AAYD,wBAAgB,iBAAiB,gBAOhC;AAED,wBAAgB,iBAAiB,cAOhC;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,GAAG,GAAG,SAAS,eAQhG"} \ No newline at end of file +{"version":3,"file":"router-store.d.ts","sourceRoot":"","sources":["../../src/global-state/router-store.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,iCAAiC,EAGlC,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAiC,aAAa,EAAY,MAAM,OAAO,CAAC;AAgB/E,OAAO,EAAE,SAAS,EAAyB,MAAM,qBAAqB,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAoB,MAAM,qBAAqB,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAG1C;;;;GAIG;AACH,qBAAa,WAAW;IACtB,SAAS,EAAG,SAAS,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAG,aAAa,CAAC;IAC9B,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,OAAO,CAAC,wBAAwB,CAAkB;IAElD,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAEpC,aAAa,EAAG,iCAAiC,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;IACjF,yBAAyB,EAAG,MAAM,IAAI,CAAC;IAEvC,oBAAoB,YAAiB,IAAI,EAAI;IAC7C,gBAAgB,YAAiB,IAAI,EAAI;IAEzC,MAAM,MAAqB;IAC3B,eAAe,MAA8B;IAC7C,MAAM,MAAqB;IAC3B,SAAS,MAAwB;IACjC,IAAI,MAAmB;IACvB,OAAO,MAAsB;IAC7B,OAAO,MAAsB;IAC7B,UAAU,MAAyB;IACnC,UAAU,MAAyB;IACnC,SAAS,MAAwB;IACjC,QAAQ,MAAuB;IAE/B,UAAU,CACR,OAAO,EAAE,cAAc,EACvB,aAAa,EAAE,iCAAiC,CAAC,eAAe,CAAC,aAAa,CAAC,EAC/E,eAAe,CAAC,EAAE,GAAG;IAoGvB,WAAW,CAAC,KAAK,EAAE,WAAW,EAAE,SAAS,cAAQ;IAWjD,YAAY,CAAC,KAAK,EAAE,WAAW;IAgB/B,kBAAkB;IAIlB,uEAAuE;IACvE,oBAAoB,eAAgB,MAAM,IAAI,mBAG5C;IACF,gBAAgB,eAAgB,MAAM,IAAI,mBAGxC;IACF,QAAQ,aAEN;IACF,iBAAiB,oBAEf;IACF,iBAAiB,kBAEf;IAEF,OAAO;CAKR;AAED,eAAO,MAAM,KAAK,aAAoB,CAAC;AAEvC,wBAAgB,aAAa,gBAE5B;AAYD,wBAAgB,iBAAiB,gBAOhC;AAED,wBAAgB,iBAAiB,cAOhC;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,GAAG,GAAG,SAAS,eAQhG"} \ No newline at end of file diff --git a/packages/expo-router/build/global-state/router-store.js b/packages/expo-router/build/global-state/router-store.js index 6da083096071a..4f9fca0a32603 100644 --- a/packages/expo-router/build/global-state/router-store.js +++ b/packages/expo-router/build/global-state/router-store.js @@ -22,11 +22,16 @@ var __importStar = (this && this.__importStar) || function (mod) { __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.useInitializeExpoRouter = exports.useStoreRouteInfo = exports.useStoreRootState = exports.useExpoRouter = exports.store = exports.RouterStore = void 0; const native_1 = require("@react-navigation/native"); +const expo_constants_1 = __importDefault(require("expo-constants")); const SplashScreen = __importStar(require("expo-splash-screen")); const react_1 = require("react"); +const react_native_1 = require("react-native"); const routing_1 = require("./routing"); const sort_routes_1 = require("./sort-routes"); const LocationProvider_1 = require("../LocationProvider"); @@ -74,7 +79,11 @@ class RouterStore { this.navigationRefSubscription?.(); this.rootStateSubscribers.clear(); this.storeSubscribers.clear(); - this.routeNode = (0, getRoutes_1.getRoutes)(context, { ignoreEntryPoints: true }); + this.routeNode = (0, getRoutes_1.getRoutes)(context, { + ...expo_constants_1.default.expoConfig?.extra?.router, + ignoreEntryPoints: true, + platform: react_native_1.Platform.OS, + }); this.rootComponent = this.routeNode ? (0, useScreens_1.getQualifiedRouteComponent)(this.routeNode) : react_1.Fragment; // Only error in production, in development we will show the onboarding screen if (!this.routeNode && process.env.NODE_ENV === 'production') { diff --git a/packages/expo-router/build/global-state/router-store.js.map b/packages/expo-router/build/global-state/router-store.js.map index 5255f95cab2bc..5e404cb3b3f5f 100644 --- a/packages/expo-router/build/global-state/router-store.js.map +++ b/packages/expo-router/build/global-state/router-store.js.map @@ -1 +1 @@ -{"version":3,"file":"router-store.js","sourceRoot":"","sources":["../../src/global-state/router-store.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,qDAIkC;AAClC,iEAAmD;AACnD,iCAA+E;AAE/E,uCAWmB;AACnB,+CAAgD;AAChD,0DAAuE;AAEvE,+DAA2E;AAE3E,0DAA2E;AAC3E,4CAAyC;AAEzC,8CAA2D;AAE3D;;;;GAIG;AACH,MAAa,WAAW;IACtB,SAAS,CAAoB;IAC7B,aAAa,CAAiB;IAC9B,OAAO,CAAsB;IACrB,wBAAwB,GAAY,KAAK,CAAC;IAElD,YAAY,CAAe;IAC3B,SAAS,CAAe;IACxB,SAAS,CAAe;IACxB,SAAS,CAAa;IACtB,0BAA0B,CAAU;IAEpC,aAAa,CAAoE;IACjF,yBAAyB,CAAc;IAEvC,oBAAoB,GAAG,IAAI,GAAG,EAAc,CAAC;IAC7C,gBAAgB,GAAG,IAAI,GAAG,EAAc,CAAC;IAEzC,MAAM,GAAG,gBAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,eAAe,GAAG,6BAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,GAAG,gBAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,SAAS,GAAG,mBAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,GAAG,iBAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,GAAG,iBAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,UAAU,GAAG,oBAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,UAAU,GAAG,oBAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,SAAS,GAAG,mBAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjC,QAAQ,GAAG,kBAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAE/B,UAAU,CACR,OAAuB,EACvB,aAA+E,EAC/E,eAAqB;QAErB,8BAA8B;QAC9B,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;QAC9B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QACzB,IAAI,CAAC,yBAAyB,EAAE,EAAE,CAAC;QACnC,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,CAAC;QAClC,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;QAE9B,IAAI,CAAC,SAAS,GAAG,IAAA,qBAAS,EAAC,OAAO,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjE,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAA,uCAA0B,EAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,gBAAQ,CAAC;QAE5F,8EAA8E;QAC9E,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,EAAE;YAC5D,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;SACpC;QAED,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QAEnC,IAAI,IAAI,CAAC,SAAS,EAAE;YAClB,IAAI,CAAC,OAAO,GAAG,IAAA,mCAAgB,EAAC,IAAI,CAAC,SAAU,CAAC,CAAC;YAEjD,IAAI,eAAe,EAAE;gBACnB,IAAI,CAAC,OAAO,CAAC,aAAa,GAAG,GAAG,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,CAAC;gBAC9D,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,CACjD,eAAe,CAAC,QAAQ,GAAG,eAAe,CAAC,MAAM,EACjD,IAAI,CAAC,OAAO,CAAC,MAAM,CACpB,CAAC;aACH;SACF;QAED,qEAAqE;QACrE,2CAA2C;QAC3C,IAAI,IAAI,CAAC,YAAY,EAAE;YACrB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC;YACnC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;SACvD;aAAM;YACL,IAAI,CAAC,SAAS,GAAG;gBACf,mBAAmB,EAAE,EAAE;gBACvB,QAAQ,EAAE,EAAE;gBACZ,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,EAAE;gBACV,QAAQ,EAAE,EAAE;aACb,CAAC;SACH;QAED;;;;;;;;;;WAUG;QACH,IAAI,CAAC,yBAAyB,GAAG,aAAa,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAoB,CAAC;YAE7C,IAAI,CAAC,IAAI,CAAC,wBAAwB,EAAE;gBAClC,IAAI,CAAC,wBAAwB,GAAG,IAAI,CAAC;gBACrC,iGAAiG;gBACjG,IAAI,CAAC,0BAA0B,GAAG,qBAAqB,CAAC,GAAG,EAAE;oBAC3D,4EAA4E;oBAC5E,YAAY,CAAC,wBAAwB,EAAE,EAAE,CAAC;gBAC5C,CAAC,CAAC,CAAC;aACJ;YAED,IAAI,uBAAuB,GAAG,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC;YACvD,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;YAE3B,oFAAoF;YACpF,0FAA0F;YAC1F,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,CAAC,SAAS,EAAE;gBACrC,aAAK,CAAC,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;gBACpC,uBAAuB,GAAG,IAAI,CAAC;aAChC;YAED,2FAA2F;YAC3F,IAAI,uBAAuB,EAAE;gBAC3B,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,oBAAoB,EAAE;oBAClD,UAAU,EAAE,CAAC;iBACd;aACF;QACH,CAAC,CAAC,CAAC;QAEH,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,gBAAgB,EAAE;YAC9C,UAAU,EAAE,CAAC;SACd;IACH,CAAC;IAED,WAAW,CAAC,KAAkB,EAAE,SAAS,GAAG,KAAK;QAC/C,aAAK,CAAC,SAAS,GAAG,KAAK,CAAC;QACxB,aAAK,CAAC,SAAS,GAAG,SAAS,CAAC;QAE5B,MAAM,aAAa,GAAG,aAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QAEhD,IAAI,CAAC,IAAA,4BAAS,EAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE;YAC7C,aAAK,CAAC,SAAS,GAAG,aAAa,CAAC;SACjC;IACH,CAAC;IAED,YAAY,CAAC,KAAkB;QAC7B,OAAO,IAAA,wCAAqB,EAC1B,CAAC,KAA6C,EAAE,MAAe,EAAE,EAAE;YACjE,OAAO,IAAA,uCAAoB,EAAC,KAAK,EAAE;gBACjC,OAAO,EAAE,EAAE;gBACX,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM;gBACvB,qBAAqB,EAAE,MAAM;gBAC7B,cAAc,EAAE,MAAM;aACvB,CAAC,CAAC;QACL,CAAC,EACD,KAAK,CACN,CAAC;IACJ,CAAC;IAED,kEAAkE;IAClE,iEAAiE;IACjE,kBAAkB;QAChB,OAAO,CAAC,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC;IACnE,CAAC;IAED,uEAAuE;IACvE,oBAAoB,GAAG,CAAC,UAAsB,EAAE,EAAE;QAChD,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC1C,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC5D,CAAC,CAAC;IACF,gBAAgB,GAAG,CAAC,UAAsB,EAAE,EAAE;QAC5C,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACtC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACxD,CAAC,CAAC;IACF,QAAQ,GAAG,GAAG,EAAE;QACd,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,iBAAiB,GAAG,GAAG,EAAE;QACvB,OAAO,IAAI,CAAC,SAAU,CAAC;IACzB,CAAC,CAAC;IACF,iBAAiB,GAAG,GAAG,EAAE;QACvB,OAAO,IAAI,CAAC,SAAU,CAAC;IACzB,CAAC,CAAC;IAEF,OAAO;QACL,IAAI,IAAI,CAAC,0BAA0B,EAAE;YACnC,oBAAoB,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;SACvD;IACH,CAAC;CACF;AAxLD,kCAwLC;AAEY,QAAA,KAAK,GAAG,IAAI,WAAW,EAAE,CAAC;AAEvC,SAAgB,aAAa;IAC3B,OAAO,IAAA,4BAAoB,EAAC,aAAK,CAAC,gBAAgB,EAAE,aAAK,CAAC,QAAQ,EAAE,aAAK,CAAC,QAAQ,CAAC,CAAC;AACtF,CAAC;AAFD,sCAEC;AAED,SAAS,kBAAkB;IACzB,IAAI,aAAK,CAAC,aAAa,CAAC,OAAO,EAAE,EAAE;QACjC,MAAM,YAAY,GAAG,aAAK,CAAC,aAAa,CAAC,YAAY,EAA4B,CAAC;QAElF,IAAI,aAAK,CAAC,SAAS,KAAK,YAAY,EAAE;YACpC,aAAK,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;SACjC;KACF;AACH,CAAC;AAED,SAAgB,iBAAiB;IAC/B,kBAAkB,EAAE,CAAC;IACrB,OAAO,IAAA,4BAAoB,EACzB,aAAK,CAAC,oBAAoB,EAC1B,aAAK,CAAC,iBAAiB,EACvB,aAAK,CAAC,iBAAiB,CACxB,CAAC;AACJ,CAAC;AAPD,8CAOC;AAED,SAAgB,iBAAiB;IAC/B,kBAAkB,EAAE,CAAC;IACrB,OAAO,IAAA,4BAAoB,EACzB,aAAK,CAAC,oBAAoB,EAC1B,aAAK,CAAC,iBAAiB,EACvB,aAAK,CAAC,iBAAiB,CACxB,CAAC;AACJ,CAAC;AAPD,8CAOC;AAED,SAAgB,uBAAuB,CAAC,OAAuB,EAAE,eAAgC;IAC/F,MAAM,aAAa,GAAG,IAAA,kCAAyB,GAAE,CAAC;IAClD,IAAA,eAAO,EACL,GAAG,EAAE,CAAC,aAAK,CAAC,UAAU,CAAC,OAAO,EAAE,aAAa,EAAE,eAAe,CAAC,EAC/D,CAAC,OAAO,EAAE,eAAe,CAAC,CAC3B,CAAC;IACF,aAAa,EAAE,CAAC;IAChB,OAAO,aAAK,CAAC;AACf,CAAC;AARD,0DAQC","sourcesContent":["import {\n NavigationContainerRefWithCurrent,\n getPathFromState,\n useNavigationContainerRef,\n} from '@react-navigation/native';\nimport * as SplashScreen from 'expo-splash-screen';\nimport { useSyncExternalStore, useMemo, ComponentType, Fragment } from 'react';\n\nimport {\n canGoBack,\n canDismiss,\n goBack,\n linkTo,\n navigate,\n dismiss,\n dismissAll,\n push,\n replace,\n setParams,\n} from './routing';\nimport { getSortedRoutes } from './sort-routes';\nimport { UrlObject, getRouteInfoFromState } from '../LocationProvider';\nimport { RouteNode } from '../Route';\nimport { deepEqual, getPathDataFromState } from '../fork/getPathFromState';\nimport { ResultState } from '../fork/getStateFromPath';\nimport { ExpoLinkingOptions, getLinkingConfig } from '../getLinkingConfig';\nimport { getRoutes } from '../getRoutes';\nimport { RequireContext } from '../types';\nimport { getQualifiedRouteComponent } from '../useScreens';\n\n/**\n * This is the global state for the router. It is used to keep track of the current route, and to provide a way to navigate to other routes.\n *\n * There should only be one instance of this class and be initialized via `useInitializeExpoRouter`\n */\nexport class RouterStore {\n routeNode!: RouteNode | null;\n rootComponent!: ComponentType;\n linking?: ExpoLinkingOptions;\n private hasAttemptedToHideSplash: boolean = false;\n\n initialState?: ResultState;\n rootState?: ResultState;\n nextState?: ResultState;\n routeInfo?: UrlObject;\n splashScreenAnimationFrame?: number;\n\n navigationRef!: NavigationContainerRefWithCurrent;\n navigationRefSubscription!: () => void;\n\n rootStateSubscribers = new Set<() => void>();\n storeSubscribers = new Set<() => void>();\n\n linkTo = linkTo.bind(this);\n getSortedRoutes = getSortedRoutes.bind(this);\n goBack = goBack.bind(this);\n canGoBack = canGoBack.bind(this);\n push = push.bind(this);\n dismiss = dismiss.bind(this);\n replace = replace.bind(this);\n dismissAll = dismissAll.bind(this);\n canDismiss = canDismiss.bind(this);\n setParams = setParams.bind(this);\n navigate = navigate.bind(this);\n\n initialize(\n context: RequireContext,\n navigationRef: NavigationContainerRefWithCurrent,\n initialLocation?: URL\n ) {\n // Clean up any previous state\n this.initialState = undefined;\n this.rootState = undefined;\n this.nextState = undefined;\n this.routeInfo = undefined;\n this.linking = undefined;\n this.navigationRefSubscription?.();\n this.rootStateSubscribers.clear();\n this.storeSubscribers.clear();\n\n this.routeNode = getRoutes(context, { ignoreEntryPoints: true });\n\n this.rootComponent = this.routeNode ? getQualifiedRouteComponent(this.routeNode) : Fragment;\n\n // Only error in production, in development we will show the onboarding screen\n if (!this.routeNode && process.env.NODE_ENV === 'production') {\n throw new Error('No routes found');\n }\n\n this.navigationRef = navigationRef;\n\n if (this.routeNode) {\n this.linking = getLinkingConfig(this.routeNode!);\n\n if (initialLocation) {\n this.linking.getInitialURL = () => initialLocation.toString();\n this.initialState = this.linking.getStateFromPath?.(\n initialLocation.pathname + initialLocation.search,\n this.linking.config\n );\n }\n }\n\n // There is no routeNode, so we will be showing the onboarding screen\n // In the meantime, just mock the routeInfo\n if (this.initialState) {\n this.rootState = this.initialState;\n this.routeInfo = this.getRouteInfo(this.initialState);\n } else {\n this.routeInfo = {\n unstable_globalHref: '',\n pathname: '',\n isIndex: false,\n params: {},\n segments: [],\n };\n }\n\n /**\n * Counter intuitively - this fires AFTER both React Navigation's state changes and the subsequent paint.\n * This poses a couple of issues for Expo Router,\n * - Ensuring hooks (e.g. useSearchParams()) have data in the initial render\n * - Reacting to state changes after a navigation event\n *\n * This is why the initial render renders a Fragment and we wait until `onReady()` is called\n * Additionally, some hooks compare the state from both the store and the navigationRef. If the store it stale,\n * that hooks will manually update the store.\n *\n */\n this.navigationRefSubscription = navigationRef.addListener('state', (data) => {\n const state = data.data.state as ResultState;\n\n if (!this.hasAttemptedToHideSplash) {\n this.hasAttemptedToHideSplash = true;\n // NOTE(EvanBacon): `navigationRef.isReady` is sometimes not true when state is called initially.\n this.splashScreenAnimationFrame = requestAnimationFrame(() => {\n // @ts-expect-error: This function is native-only and for internal-use only.\n SplashScreen._internal_maybeHideAsync?.();\n });\n }\n\n let shouldUpdateSubscribers = this.nextState === state;\n this.nextState = undefined;\n\n // This can sometimes be undefined when an error is thrown in the Root Layout Route.\n // Additionally that state may already equal the rootState if it was updated within a hook\n if (state && state !== this.rootState) {\n store.updateState(state, undefined);\n shouldUpdateSubscribers = true;\n }\n\n // If the state has changed, or was changed inside a hook we need to update the subscribers\n if (shouldUpdateSubscribers) {\n for (const subscriber of this.rootStateSubscribers) {\n subscriber();\n }\n }\n });\n\n for (const subscriber of this.storeSubscribers) {\n subscriber();\n }\n }\n\n updateState(state: ResultState, nextState = state) {\n store.rootState = state;\n store.nextState = nextState;\n\n const nextRouteInfo = store.getRouteInfo(state);\n\n if (!deepEqual(this.routeInfo, nextRouteInfo)) {\n store.routeInfo = nextRouteInfo;\n }\n }\n\n getRouteInfo(state: ResultState) {\n return getRouteInfoFromState(\n (state: Parameters[0], asPath: boolean) => {\n return getPathDataFromState(state, {\n screens: [],\n ...this.linking?.config,\n preserveDynamicRoutes: asPath,\n preserveGroups: asPath,\n });\n },\n state\n );\n }\n\n // This is only used in development, to show the onboarding screen\n // In production we should have errored during the initialization\n shouldShowTutorial() {\n return !this.routeNode && process.env.NODE_ENV === 'development';\n }\n\n /** Make sure these are arrow functions so `this` is correctly bound */\n subscribeToRootState = (subscriber: () => void) => {\n this.rootStateSubscribers.add(subscriber);\n return () => this.rootStateSubscribers.delete(subscriber);\n };\n subscribeToStore = (subscriber: () => void) => {\n this.storeSubscribers.add(subscriber);\n return () => this.storeSubscribers.delete(subscriber);\n };\n snapshot = () => {\n return this;\n };\n rootStateSnapshot = () => {\n return this.rootState!;\n };\n routeInfoSnapshot = () => {\n return this.routeInfo!;\n };\n\n cleanup() {\n if (this.splashScreenAnimationFrame) {\n cancelAnimationFrame(this.splashScreenAnimationFrame);\n }\n }\n}\n\nexport const store = new RouterStore();\n\nexport function useExpoRouter() {\n return useSyncExternalStore(store.subscribeToStore, store.snapshot, store.snapshot);\n}\n\nfunction syncStoreRootState() {\n if (store.navigationRef.isReady()) {\n const currentState = store.navigationRef.getRootState() as unknown as ResultState;\n\n if (store.rootState !== currentState) {\n store.updateState(currentState);\n }\n }\n}\n\nexport function useStoreRootState() {\n syncStoreRootState();\n return useSyncExternalStore(\n store.subscribeToRootState,\n store.rootStateSnapshot,\n store.rootStateSnapshot\n );\n}\n\nexport function useStoreRouteInfo() {\n syncStoreRootState();\n return useSyncExternalStore(\n store.subscribeToRootState,\n store.routeInfoSnapshot,\n store.routeInfoSnapshot\n );\n}\n\nexport function useInitializeExpoRouter(context: RequireContext, initialLocation: URL | undefined) {\n const navigationRef = useNavigationContainerRef();\n useMemo(\n () => store.initialize(context, navigationRef, initialLocation),\n [context, initialLocation]\n );\n useExpoRouter();\n return store;\n}\n"]} \ No newline at end of file +{"version":3,"file":"router-store.js","sourceRoot":"","sources":["../../src/global-state/router-store.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n NavigationContainerRefWithCurrent,\n getPathFromState,\n useNavigationContainerRef,\n} from '@react-navigation/native';\nimport Constants from 'expo-constants';\nimport * as SplashScreen from 'expo-splash-screen';\nimport { useSyncExternalStore, useMemo, ComponentType, Fragment } from 'react';\nimport { Platform } from 'react-native';\n\nimport {\n canGoBack,\n canDismiss,\n goBack,\n linkTo,\n navigate,\n dismiss,\n dismissAll,\n push,\n replace,\n setParams,\n} from './routing';\nimport { getSortedRoutes } from './sort-routes';\nimport { UrlObject, getRouteInfoFromState } from '../LocationProvider';\nimport { RouteNode } from '../Route';\nimport { deepEqual, getPathDataFromState } from '../fork/getPathFromState';\nimport { ResultState } from '../fork/getStateFromPath';\nimport { ExpoLinkingOptions, getLinkingConfig } from '../getLinkingConfig';\nimport { getRoutes } from '../getRoutes';\nimport { RequireContext } from '../types';\nimport { getQualifiedRouteComponent } from '../useScreens';\n\n/**\n * This is the global state for the router. It is used to keep track of the current route, and to provide a way to navigate to other routes.\n *\n * There should only be one instance of this class and be initialized via `useInitializeExpoRouter`\n */\nexport class RouterStore {\n routeNode!: RouteNode | null;\n rootComponent!: ComponentType;\n linking?: ExpoLinkingOptions;\n private hasAttemptedToHideSplash: boolean = false;\n\n initialState?: ResultState;\n rootState?: ResultState;\n nextState?: ResultState;\n routeInfo?: UrlObject;\n splashScreenAnimationFrame?: number;\n\n navigationRef!: NavigationContainerRefWithCurrent;\n navigationRefSubscription!: () => void;\n\n rootStateSubscribers = new Set<() => void>();\n storeSubscribers = new Set<() => void>();\n\n linkTo = linkTo.bind(this);\n getSortedRoutes = getSortedRoutes.bind(this);\n goBack = goBack.bind(this);\n canGoBack = canGoBack.bind(this);\n push = push.bind(this);\n dismiss = dismiss.bind(this);\n replace = replace.bind(this);\n dismissAll = dismissAll.bind(this);\n canDismiss = canDismiss.bind(this);\n setParams = setParams.bind(this);\n navigate = navigate.bind(this);\n\n initialize(\n context: RequireContext,\n navigationRef: NavigationContainerRefWithCurrent,\n initialLocation?: URL\n ) {\n // Clean up any previous state\n this.initialState = undefined;\n this.rootState = undefined;\n this.nextState = undefined;\n this.routeInfo = undefined;\n this.linking = undefined;\n this.navigationRefSubscription?.();\n this.rootStateSubscribers.clear();\n this.storeSubscribers.clear();\n\n this.routeNode = getRoutes(context, {\n ...Constants.expoConfig?.extra?.router,\n ignoreEntryPoints: true,\n platform: Platform.OS,\n });\n\n this.rootComponent = this.routeNode ? getQualifiedRouteComponent(this.routeNode) : Fragment;\n\n // Only error in production, in development we will show the onboarding screen\n if (!this.routeNode && process.env.NODE_ENV === 'production') {\n throw new Error('No routes found');\n }\n\n this.navigationRef = navigationRef;\n\n if (this.routeNode) {\n this.linking = getLinkingConfig(this.routeNode!);\n\n if (initialLocation) {\n this.linking.getInitialURL = () => initialLocation.toString();\n this.initialState = this.linking.getStateFromPath?.(\n initialLocation.pathname + initialLocation.search,\n this.linking.config\n );\n }\n }\n\n // There is no routeNode, so we will be showing the onboarding screen\n // In the meantime, just mock the routeInfo\n if (this.initialState) {\n this.rootState = this.initialState;\n this.routeInfo = this.getRouteInfo(this.initialState);\n } else {\n this.routeInfo = {\n unstable_globalHref: '',\n pathname: '',\n isIndex: false,\n params: {},\n segments: [],\n };\n }\n\n /**\n * Counter intuitively - this fires AFTER both React Navigation's state changes and the subsequent paint.\n * This poses a couple of issues for Expo Router,\n * - Ensuring hooks (e.g. useSearchParams()) have data in the initial render\n * - Reacting to state changes after a navigation event\n *\n * This is why the initial render renders a Fragment and we wait until `onReady()` is called\n * Additionally, some hooks compare the state from both the store and the navigationRef. If the store it stale,\n * that hooks will manually update the store.\n *\n */\n this.navigationRefSubscription = navigationRef.addListener('state', (data) => {\n const state = data.data.state as ResultState;\n\n if (!this.hasAttemptedToHideSplash) {\n this.hasAttemptedToHideSplash = true;\n // NOTE(EvanBacon): `navigationRef.isReady` is sometimes not true when state is called initially.\n this.splashScreenAnimationFrame = requestAnimationFrame(() => {\n // @ts-expect-error: This function is native-only and for internal-use only.\n SplashScreen._internal_maybeHideAsync?.();\n });\n }\n\n let shouldUpdateSubscribers = this.nextState === state;\n this.nextState = undefined;\n\n // This can sometimes be undefined when an error is thrown in the Root Layout Route.\n // Additionally that state may already equal the rootState if it was updated within a hook\n if (state && state !== this.rootState) {\n store.updateState(state, undefined);\n shouldUpdateSubscribers = true;\n }\n\n // If the state has changed, or was changed inside a hook we need to update the subscribers\n if (shouldUpdateSubscribers) {\n for (const subscriber of this.rootStateSubscribers) {\n subscriber();\n }\n }\n });\n\n for (const subscriber of this.storeSubscribers) {\n subscriber();\n }\n }\n\n updateState(state: ResultState, nextState = state) {\n store.rootState = state;\n store.nextState = nextState;\n\n const nextRouteInfo = store.getRouteInfo(state);\n\n if (!deepEqual(this.routeInfo, nextRouteInfo)) {\n store.routeInfo = nextRouteInfo;\n }\n }\n\n getRouteInfo(state: ResultState) {\n return getRouteInfoFromState(\n (state: Parameters[0], asPath: boolean) => {\n return getPathDataFromState(state, {\n screens: [],\n ...this.linking?.config,\n preserveDynamicRoutes: asPath,\n preserveGroups: asPath,\n });\n },\n state\n );\n }\n\n // This is only used in development, to show the onboarding screen\n // In production we should have errored during the initialization\n shouldShowTutorial() {\n return !this.routeNode && process.env.NODE_ENV === 'development';\n }\n\n /** Make sure these are arrow functions so `this` is correctly bound */\n subscribeToRootState = (subscriber: () => void) => {\n this.rootStateSubscribers.add(subscriber);\n return () => this.rootStateSubscribers.delete(subscriber);\n };\n subscribeToStore = (subscriber: () => void) => {\n this.storeSubscribers.add(subscriber);\n return () => this.storeSubscribers.delete(subscriber);\n };\n snapshot = () => {\n return this;\n };\n rootStateSnapshot = () => {\n return this.rootState!;\n };\n routeInfoSnapshot = () => {\n return this.routeInfo!;\n };\n\n cleanup() {\n if (this.splashScreenAnimationFrame) {\n cancelAnimationFrame(this.splashScreenAnimationFrame);\n }\n }\n}\n\nexport const store = new RouterStore();\n\nexport function useExpoRouter() {\n return useSyncExternalStore(store.subscribeToStore, store.snapshot, store.snapshot);\n}\n\nfunction syncStoreRootState() {\n if (store.navigationRef.isReady()) {\n const currentState = store.navigationRef.getRootState() as unknown as ResultState;\n\n if (store.rootState !== currentState) {\n store.updateState(currentState);\n }\n }\n}\n\nexport function useStoreRootState() {\n syncStoreRootState();\n return useSyncExternalStore(\n store.subscribeToRootState,\n store.rootStateSnapshot,\n store.rootStateSnapshot\n );\n}\n\nexport function useStoreRouteInfo() {\n syncStoreRootState();\n return useSyncExternalStore(\n store.subscribeToRootState,\n store.routeInfoSnapshot,\n store.routeInfoSnapshot\n );\n}\n\nexport function useInitializeExpoRouter(context: RequireContext, initialLocation: URL | undefined) {\n const navigationRef = useNavigationContainerRef();\n useMemo(\n () => store.initialize(context, navigationRef, initialLocation),\n [context, initialLocation]\n );\n useExpoRouter();\n return store;\n}\n"]} \ No newline at end of file diff --git a/packages/expo-router/build/routes-manifest.d.ts b/packages/expo-router/build/routes-manifest.d.ts index 0f656eda52142..586057d893248 100644 --- a/packages/expo-router/build/routes-manifest.d.ts +++ b/packages/expo-router/build/routes-manifest.d.ts @@ -1,3 +1,5 @@ +import { Options } from './getRoutes'; +export { Options } from './getRoutes'; export type RouteInfo = { file: string; page: string; @@ -9,5 +11,5 @@ export type ExpoRoutesManifestV1 = { htmlRoutes: RouteInfo[]; notFoundRoutes: RouteInfo[]; }; -export declare function createRoutesManifest(paths: string[]): ExpoRoutesManifestV1 | null; +export declare function createRoutesManifest(paths: string[], options: Options): ExpoRoutesManifestV1 | null; //# sourceMappingURL=routes-manifest.d.ts.map \ No newline at end of file diff --git a/packages/expo-router/build/routes-manifest.d.ts.map b/packages/expo-router/build/routes-manifest.d.ts.map index 661c258c6cd3c..c8707b7226ae9 100644 --- a/packages/expo-router/build/routes-manifest.d.ts.map +++ b/packages/expo-router/build/routes-manifest.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"routes-manifest.d.ts","sourceRoot":"","sources":["../src/routes-manifest.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,SAAS,CAAC,MAAM,GAAG,MAAM,IAAI;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,oBAAoB,CAAC,MAAM,GAAG,MAAM,IAAI;IAClD,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAC/B,UAAU,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAChC,cAAc,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;CACrC,CAAC;AAYF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,oBAAoB,GAAG,IAAI,CAYjF"} \ No newline at end of file +{"version":3,"file":"routes-manifest.d.ts","sourceRoot":"","sources":["../src/routes-manifest.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAa,MAAM,aAAa,CAAC;AAIjD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,MAAM,SAAS,CAAC,MAAM,GAAG,MAAM,IAAI;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,oBAAoB,CAAC,MAAM,GAAG,MAAM,IAAI;IAClD,SAAS,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAC/B,UAAU,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;IAChC,cAAc,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;CACrC,CAAC;AAYF,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EAAE,EACf,OAAO,EAAE,OAAO,GACf,oBAAoB,GAAG,IAAI,CAc7B"} \ No newline at end of file diff --git a/packages/expo-router/build/routes-manifest.js b/packages/expo-router/build/routes-manifest.js index bec68d4bc0338..07959aadae662 100644 --- a/packages/expo-router/build/routes-manifest.js +++ b/packages/expo-router/build/routes-manifest.js @@ -12,12 +12,14 @@ function createMockContextModule(map = []) { }); return contextModule; } -function createRoutesManifest(paths) { +function createRoutesManifest(paths, options) { // TODO: Drop this part for Node.js const routeTree = (0, getRoutes_1.getRoutes)(createMockContextModule(paths), { + ...options, preserveApiRoutes: true, ignoreRequireErrors: true, ignoreEntryPoints: true, + platform: 'web', }); if (!routeTree) { return null; diff --git a/packages/expo-router/build/routes-manifest.js.map b/packages/expo-router/build/routes-manifest.js.map index a0c781ec7a2f0..a5c5b323bb6d9 100644 --- a/packages/expo-router/build/routes-manifest.js.map +++ b/packages/expo-router/build/routes-manifest.js.map @@ -1 +1 @@ -{"version":3,"file":"routes-manifest.js","sourceRoot":"","sources":["../src/routes-manifest.ts"],"names":[],"mappings":";;;AAAA,0CAA0C;AAC1C,sBAAsB;AACtB,2CAAwC;AACxC,2DAAwD;AAgBxD,SAAS,uBAAuB,CAAC,MAAgB,EAAE;IACjD,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,KAAI,CAAC,EAAE,CAAC,CAAC;IAElD,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE;QAC3C,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG;KACjB,CAAC,CAAC;IAEH,OAAO,aAA+B,CAAC;AACzC,CAAC;AAED,SAAgB,oBAAoB,CAAC,KAAe;IAClD,mCAAmC;IACnC,MAAM,SAAS,GAAG,IAAA,qBAAS,EAAC,uBAAuB,CAAC,KAAK,CAAC,EAAE;QAC1D,iBAAiB,EAAE,IAAI;QACvB,mBAAmB,EAAE,IAAI;QACzB,iBAAiB,EAAE,IAAI;KACxB,CAAC,CAAC;IAEH,IAAI,CAAC,SAAS,EAAE;QACd,OAAO,IAAI,CAAC;KACb;IACD,OAAO,IAAA,qCAAiB,EAAC,SAAS,CAAC,CAAC;AACtC,CAAC;AAZD,oDAYC","sourcesContent":["// This file runs in Node.js environments.\n// no relative imports\nimport { getRoutes } from './getRoutes';\nimport { getServerManifest } from './getServerManifest';\nimport { RequireContext } from './types';\n\nexport type RouteInfo = {\n file: string;\n page: string;\n namedRegex: TRegex;\n routeKeys: Record;\n};\n\nexport type ExpoRoutesManifestV1 = {\n apiRoutes: RouteInfo[];\n htmlRoutes: RouteInfo[];\n notFoundRoutes: RouteInfo[];\n};\n\nfunction createMockContextModule(map: string[] = []) {\n const contextModule = (key) => ({ default() {} });\n\n Object.defineProperty(contextModule, 'keys', {\n value: () => map,\n });\n\n return contextModule as RequireContext;\n}\n\nexport function createRoutesManifest(paths: string[]): ExpoRoutesManifestV1 | null {\n // TODO: Drop this part for Node.js\n const routeTree = getRoutes(createMockContextModule(paths), {\n preserveApiRoutes: true,\n ignoreRequireErrors: true,\n ignoreEntryPoints: true,\n });\n\n if (!routeTree) {\n return null;\n }\n return getServerManifest(routeTree);\n}\n"]} \ No newline at end of file +{"version":3,"file":"routes-manifest.js","sourceRoot":"","sources":["../src/routes-manifest.ts"],"names":[],"mappings":";;;AAAA,0CAA0C;AAC1C,sBAAsB;AACtB,2CAAiD;AACjD,2DAAwD;AAkBxD,SAAS,uBAAuB,CAAC,MAAgB,EAAE;IACjD,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,KAAI,CAAC,EAAE,CAAC,CAAC;IAElD,MAAM,CAAC,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE;QAC3C,KAAK,EAAE,GAAG,EAAE,CAAC,GAAG;KACjB,CAAC,CAAC;IAEH,OAAO,aAA+B,CAAC;AACzC,CAAC;AAED,SAAgB,oBAAoB,CAClC,KAAe,EACf,OAAgB;IAEhB,mCAAmC;IACnC,MAAM,SAAS,GAAG,IAAA,qBAAS,EAAC,uBAAuB,CAAC,KAAK,CAAC,EAAE;QAC1D,GAAG,OAAO;QACV,iBAAiB,EAAE,IAAI;QACvB,mBAAmB,EAAE,IAAI;QACzB,iBAAiB,EAAE,IAAI;QACvB,QAAQ,EAAE,KAAK;KAChB,CAAC,CAAC;IAEH,IAAI,CAAC,SAAS,EAAE;QACd,OAAO,IAAI,CAAC;KACb;IACD,OAAO,IAAA,qCAAiB,EAAC,SAAS,CAAC,CAAC;AACtC,CAAC;AAjBD,oDAiBC","sourcesContent":["// This file runs in Node.js environments.\n// no relative imports\nimport { Options, getRoutes } from './getRoutes';\nimport { getServerManifest } from './getServerManifest';\nimport { RequireContext } from './types';\n\nexport { Options } from './getRoutes';\n\nexport type RouteInfo = {\n file: string;\n page: string;\n namedRegex: TRegex;\n routeKeys: Record;\n};\n\nexport type ExpoRoutesManifestV1 = {\n apiRoutes: RouteInfo[];\n htmlRoutes: RouteInfo[];\n notFoundRoutes: RouteInfo[];\n};\n\nfunction createMockContextModule(map: string[] = []) {\n const contextModule = (key) => ({ default() {} });\n\n Object.defineProperty(contextModule, 'keys', {\n value: () => map,\n });\n\n return contextModule as RequireContext;\n}\n\nexport function createRoutesManifest(\n paths: string[],\n options: Options\n): ExpoRoutesManifestV1 | null {\n // TODO: Drop this part for Node.js\n const routeTree = getRoutes(createMockContextModule(paths), {\n ...options,\n preserveApiRoutes: true,\n ignoreRequireErrors: true,\n ignoreEntryPoints: true,\n platform: 'web',\n });\n\n if (!routeTree) {\n return null;\n }\n return getServerManifest(routeTree);\n}\n"]} \ No newline at end of file diff --git a/packages/expo-router/build/static/renderStaticContent.d.ts b/packages/expo-router/build/static/renderStaticContent.d.ts index 05c14c08e9cf0..463c8de2bc43a 100644 --- a/packages/expo-router/build/static/renderStaticContent.d.ts +++ b/packages/expo-router/build/static/renderStaticContent.d.ts @@ -5,10 +5,10 @@ * LICENSE file in the root directory of this source tree. */ import '@expo/metro-runtime'; -import { getRoutes } from '../getRoutes'; +import { Options } from '../getRoutes'; import { ExpoRouterServerManifestV1 } from '../getServerManifest'; /** Get the linking manifest from a Node.js process. */ -declare function getManifest(options?: Parameters[1]): Promise<{ +declare function getManifest(options?: Options): Promise<{ initialRouteName?: string | undefined; screens: Record; }>; @@ -19,7 +19,7 @@ declare function getManifest(options?: Parameters[1]): Promise * * This is used for the production manifest where we pre-render certain pages and should no longer treat them as dynamic. */ -declare function getBuildTimeServerManifestAsync(options?: Parameters[1]): Promise; +declare function getBuildTimeServerManifestAsync(options?: Options): Promise; export declare function getStaticContent(location: URL): Promise; export { getManifest, getBuildTimeServerManifestAsync }; //# sourceMappingURL=renderStaticContent.d.ts.map \ No newline at end of file diff --git a/packages/expo-router/build/static/renderStaticContent.d.ts.map b/packages/expo-router/build/static/renderStaticContent.d.ts.map index b1118fcbb830d..e84fb8cbcc288 100644 --- a/packages/expo-router/build/static/renderStaticContent.d.ts.map +++ b/packages/expo-router/build/static/renderStaticContent.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"renderStaticContent.d.ts","sourceRoot":"","sources":["../../src/static/renderStaticContent.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,qBAAqB,CAAC;AAY7B,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,0BAA0B,EAAqB,MAAM,sBAAsB,CAAC;AAQrF,uDAAuD;AACvD,iBAAe,WAAW,CAAC,OAAO,GAAE,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAM;;;GAWvE;AAED;;;;;;GAMG;AACH,iBAAe,+BAA+B,CAC5C,OAAO,GAAE,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAM,GAC5C,OAAO,CAAC,0BAA0B,CAAC,CAWrC;AAYD,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CA0DrE;AAmBD,OAAO,EAAE,WAAW,EAAE,+BAA+B,EAAE,CAAC"} \ No newline at end of file +{"version":3,"file":"renderStaticContent.d.ts","sourceRoot":"","sources":["../../src/static/renderStaticContent.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,qBAAqB,CAAC;AAY7B,OAAO,EAAa,OAAO,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,0BAA0B,EAAqB,MAAM,sBAAsB,CAAC;AAQrF,uDAAuD;AACvD,iBAAe,WAAW,CAAC,OAAO,GAAE,OAAY;;;GAe/C;AAED;;;;;;GAMG;AACH,iBAAe,+BAA+B,CAC5C,OAAO,GAAE,OAAY,GACpB,OAAO,CAAC,0BAA0B,CAAC,CAcrC;AAYD,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CA0DrE;AAmBD,OAAO,EAAE,WAAW,EAAE,+BAA+B,EAAE,CAAC"} \ No newline at end of file diff --git a/packages/expo-router/build/static/renderStaticContent.js b/packages/expo-router/build/static/renderStaticContent.js index 04dfa70f74e46..fe970ec7fd408 100644 --- a/packages/expo-router/build/static/renderStaticContent.js +++ b/packages/expo-router/build/static/renderStaticContent.js @@ -51,7 +51,11 @@ const debug = require('debug')('expo:router:renderStaticContent'); react_native_web_1.AppRegistry.registerComponent('App', () => ExpoRoot_1.ExpoRoot); /** Get the linking manifest from a Node.js process. */ async function getManifest(options = {}) { - const routeTree = (0, getRoutes_1.getRoutes)(_ctx_1.ctx, { preserveApiRoutes: true, ...options }); + const routeTree = (0, getRoutes_1.getRoutes)(_ctx_1.ctx, { + preserveApiRoutes: true, + platform: 'web', + ...options, + }); if (!routeTree) { throw new Error('No routes found'); } @@ -68,7 +72,10 @@ exports.getManifest = getManifest; * This is used for the production manifest where we pre-render certain pages and should no longer treat them as dynamic. */ async function getBuildTimeServerManifestAsync(options = {}) { - const routeTree = (0, getRoutes_1.getRoutes)(_ctx_1.ctx, options); + const routeTree = (0, getRoutes_1.getRoutes)(_ctx_1.ctx, { + platform: 'web', + ...options, + }); if (!routeTree) { throw new Error('No routes found'); } diff --git a/packages/expo-router/build/static/renderStaticContent.js.map b/packages/expo-router/build/static/renderStaticContent.js.map index 01aebdcc8c97f..2c9e684d0450c 100644 --- a/packages/expo-router/build/static/renderStaticContent.js.map +++ b/packages/expo-router/build/static/renderStaticContent.js.map @@ -1 +1 @@ -{"version":3,"file":"renderStaticContent.js","sourceRoot":"","sources":["../../src/static/renderStaticContent.tsx"],"names":[],"mappings":"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","sourcesContent":["/**\n * Copyright © 2023 650 Industries.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\nimport '@expo/metro-runtime';\n\nimport { ServerContainer, ServerContainerRef } from '@react-navigation/native';\nimport * as Font from 'expo-font/build/server';\nimport React from 'react';\nimport ReactDOMServer from 'react-dom/server.node';\nimport { AppRegistry } from 'react-native-web';\n\nimport { getRootComponent } from './getRootComponent';\nimport { ctx } from '../../_ctx';\nimport { ExpoRoot } from '../ExpoRoot';\nimport { getReactNavigationConfig } from '../getReactNavigationConfig';\nimport { getRoutes } from '../getRoutes';\nimport { ExpoRouterServerManifestV1, getServerManifest } from '../getServerManifest';\nimport { Head } from '../head';\nimport { loadStaticParamsAsync } from '../loadStaticParamsAsync';\n\nconst debug = require('debug')('expo:router:renderStaticContent');\n\nAppRegistry.registerComponent('App', () => ExpoRoot);\n\n/** Get the linking manifest from a Node.js process. */\nasync function getManifest(options: Parameters[1] = {}) {\n const routeTree = getRoutes(ctx, { preserveApiRoutes: true, ...options });\n\n if (!routeTree) {\n throw new Error('No routes found');\n }\n\n // Evaluate all static params\n await loadStaticParamsAsync(routeTree);\n\n return getReactNavigationConfig(routeTree, false);\n}\n\n/**\n * Get the server manifest with all dynamic routes loaded with `generateStaticParams`.\n * Unlike the `expo-router/src/routes-manifest.ts` method, this requires loading the entire app in-memory, which\n * takes substantially longer and requires Metro bundling.\n *\n * This is used for the production manifest where we pre-render certain pages and should no longer treat them as dynamic.\n */\nasync function getBuildTimeServerManifestAsync(\n options: Parameters[1] = {}\n): Promise {\n const routeTree = getRoutes(ctx, options);\n\n if (!routeTree) {\n throw new Error('No routes found');\n }\n\n // Evaluate all static params\n await loadStaticParamsAsync(routeTree);\n\n return getServerManifest(routeTree);\n}\n\nfunction resetReactNavigationContexts() {\n // https://github.com/expo/router/discussions/588\n // https://github.com/react-navigation/react-navigation/blob/9fe34b445fcb86e5666f61e144007d7540f014fa/packages/elements/src/getNamedContext.tsx#LL3C1-L4C1\n\n // React Navigation is storing providers in a global, this is fine for the first static render\n // but subsequent static renders of Stack or Tabs will cause React to throw a warning. To prevent this warning, we'll reset the globals before rendering.\n const contexts = '__react_navigation__elements_contexts';\n global[contexts] = new Map>();\n}\n\nexport async function getStaticContent(location: URL): Promise {\n const headContext: { helmet?: any } = {};\n\n const ref = React.createRef();\n\n const {\n // NOTE: The `element` that's returned adds two extra Views and\n // the seemingly unused `RootTagContext.Provider`.\n element,\n getStyleElement,\n } = AppRegistry.getApplication('App', {\n initialProps: {\n location,\n context: ctx,\n wrapper: ({ children }) => (\n \n
{children}
\n
\n ),\n },\n });\n\n const Root = getRootComponent();\n\n // Clear any existing static resources from the global scope to attempt to prevent leaking between pages.\n // This could break if pages are rendered in parallel or if fonts are loaded outside of the React tree\n Font.resetServerContext();\n\n // This MUST be run before `ReactDOMServer.renderToString` to prevent\n // \"Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.\"\n resetReactNavigationContexts();\n\n const stream = await ReactDOMServer.renderToStaticNodeStream(\n \n {element}\n \n );\n\n let html = '';\n\n for await (const chunk of stream) {\n html += chunk;\n }\n\n // Eval the CSS after the HTML is rendered so that the CSS is in the same order\n const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());\n\n let output = mixHeadComponentsWithStaticResults(headContext.helmet, html);\n\n output = output.replace('', `${css}`);\n\n const fonts = Font.getServerResources();\n debug(`Pushing static fonts: (count: ${fonts.length})`, fonts);\n // debug('Push static fonts:', fonts)\n // Inject static fonts loaded with expo-font\n output = output.replace('', `${fonts.join('')}`);\n\n return '' + output;\n}\n\nfunction mixHeadComponentsWithStaticResults(helmet: any, html: string) {\n // Head components\n for (const key of ['title', 'priority', 'meta', 'link', 'script', 'style'].reverse()) {\n const result = helmet?.[key]?.toString();\n if (result) {\n html = html.replace('', `${result}`);\n }\n }\n\n // attributes\n html = html.replace(' ExpoRoot);\n\n/** Get the linking manifest from a Node.js process. */\nasync function getManifest(options: Options = {}) {\n const routeTree = getRoutes(ctx, {\n preserveApiRoutes: true,\n platform: 'web',\n ...options,\n });\n\n if (!routeTree) {\n throw new Error('No routes found');\n }\n\n // Evaluate all static params\n await loadStaticParamsAsync(routeTree);\n\n return getReactNavigationConfig(routeTree, false);\n}\n\n/**\n * Get the server manifest with all dynamic routes loaded with `generateStaticParams`.\n * Unlike the `expo-router/src/routes-manifest.ts` method, this requires loading the entire app in-memory, which\n * takes substantially longer and requires Metro bundling.\n *\n * This is used for the production manifest where we pre-render certain pages and should no longer treat them as dynamic.\n */\nasync function getBuildTimeServerManifestAsync(\n options: Options = {}\n): Promise {\n const routeTree = getRoutes(ctx, {\n platform: 'web',\n ...options,\n });\n\n if (!routeTree) {\n throw new Error('No routes found');\n }\n\n // Evaluate all static params\n await loadStaticParamsAsync(routeTree);\n\n return getServerManifest(routeTree);\n}\n\nfunction resetReactNavigationContexts() {\n // https://github.com/expo/router/discussions/588\n // https://github.com/react-navigation/react-navigation/blob/9fe34b445fcb86e5666f61e144007d7540f014fa/packages/elements/src/getNamedContext.tsx#LL3C1-L4C1\n\n // React Navigation is storing providers in a global, this is fine for the first static render\n // but subsequent static renders of Stack or Tabs will cause React to throw a warning. To prevent this warning, we'll reset the globals before rendering.\n const contexts = '__react_navigation__elements_contexts';\n global[contexts] = new Map>();\n}\n\nexport async function getStaticContent(location: URL): Promise {\n const headContext: { helmet?: any } = {};\n\n const ref = React.createRef();\n\n const {\n // NOTE: The `element` that's returned adds two extra Views and\n // the seemingly unused `RootTagContext.Provider`.\n element,\n getStyleElement,\n } = AppRegistry.getApplication('App', {\n initialProps: {\n location,\n context: ctx,\n wrapper: ({ children }) => (\n \n
{children}
\n
\n ),\n },\n });\n\n const Root = getRootComponent();\n\n // Clear any existing static resources from the global scope to attempt to prevent leaking between pages.\n // This could break if pages are rendered in parallel or if fonts are loaded outside of the React tree\n Font.resetServerContext();\n\n // This MUST be run before `ReactDOMServer.renderToString` to prevent\n // \"Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.\"\n resetReactNavigationContexts();\n\n const stream = await ReactDOMServer.renderToStaticNodeStream(\n \n {element}\n \n );\n\n let html = '';\n\n for await (const chunk of stream) {\n html += chunk;\n }\n\n // Eval the CSS after the HTML is rendered so that the CSS is in the same order\n const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());\n\n let output = mixHeadComponentsWithStaticResults(headContext.helmet, html);\n\n output = output.replace('', `${css}`);\n\n const fonts = Font.getServerResources();\n debug(`Pushing static fonts: (count: ${fonts.length})`, fonts);\n // debug('Push static fonts:', fonts)\n // Inject static fonts loaded with expo-font\n output = output.replace('', `${fonts.join('')}`);\n\n return '' + output;\n}\n\nfunction mixHeadComponentsWithStaticResults(helmet: any, html: string) {\n // Head components\n for (const key of ['title', 'priority', 'meta', 'link', 'script', 'style'].reverse()) {\n const result = helmet?.[key]?.toString();\n if (result) {\n html = html.replace('', `${result}`);\n }\n }\n\n // attributes\n html = html.replace('();\n const dynamicRoutes = new Set();\n const dynamicRouteContextKeys = new Set();\n\n walkRouteNode(\n getRoutes(ctx, {\n ignoreEntryPoints: true,\n ignoreRequireErrors: true,\n importMode: 'async',\n }),\n staticRoutes,\n dynamicRoutes,\n dynamicRouteContextKeys\n );\n\n // If the user has expo-router v3+ installed, we can use the types from the package\n return (\n fs\n .readFileSync(path.join(__dirname, '../../types/expo-router.d.ts'), 'utf-8')\n // Swap from being a namespace to a module\n .replace('declare namespace ExpoRouter {', `declare module \"expo-router\" {`)\n // Add the route values\n .replace(\n 'type StaticRoutes = string;',\n `type StaticRoutes = ${setToUnionType(staticRoutes)};`\n )\n .replace(\n 'type DynamicRoutes = string;',\n `type DynamicRoutes = ${setToUnionType(dynamicRoutes)};`\n )\n .replace(\n 'type DynamicRouteTemplate = never;',\n `type DynamicRouteTemplate = ${setToUnionType(dynamicRouteContextKeys)};`\n )\n );\n}\n\n/**\n * Walks a RouteNode tree and adds the routes to the provided sets\n */\nfunction walkRouteNode(\n routeNode: RouteNode | null,\n staticRoutes: Set,\n dynamicRoutes: Set,\n dynamicRouteContextKeys: Set\n) {\n if (!routeNode) return;\n\n addRouteNode(routeNode, staticRoutes, dynamicRoutes, dynamicRouteContextKeys);\n\n for (const child of routeNode.children) {\n walkRouteNode(child, staticRoutes, dynamicRoutes, dynamicRouteContextKeys);\n }\n}\n\n/**\n * Given a RouteNode, adds the route to the correct sets\n * Modifies the RouteNode.route to be a typed-route string\n */\nfunction addRouteNode(\n routeNode: RouteNode | null,\n staticRoutes: Set,\n dynamicRoutes: Set,\n dynamicRouteContextKeys: Set\n) {\n if (!routeNode?.route) return;\n if (!isTypedRoute(routeNode.route)) return;\n\n let routePath = `${removeSupportedExtensions(routeNode.route).replace(/\\/?index$/, '')}`; // replace /index with /\n\n if (!routePath.startsWith('/')) {\n routePath = `/${routePath}`;\n }\n\n if (routeNode.dynamic) {\n for (const path of generateCombinations(routePath)) {\n dynamicRouteContextKeys.add(path);\n dynamicRoutes.add(\n `${path\n .replaceAll(CATCH_ALL, '${CatchAllRoutePart}')\n .replaceAll(SLUG, '${SingleRoutePart}')}`\n );\n }\n } else {\n for (const combination of generateCombinations(routePath)) {\n staticRoutes.add(combination);\n }\n }\n}\n\n/**\n * Converts a Set to a TypeScript union type\n */\nconst setToUnionType = (set: Set) => {\n return set.size > 0 ? [...set].map((s) => `\\`${s}\\``).join(' | ') : 'never';\n};\n\nfunction generateCombinations(pathname) {\n const groups = pathname.split('/').filter((part) => part.startsWith('(') && part.endsWith(')'));\n const combinations: string[] = [];\n\n function generate(currentIndex, currentPath) {\n if (currentIndex === groups.length) {\n combinations.push(currentPath.replace(/\\/{2,}/g, '/'));\n return;\n }\n\n const group = groups[currentIndex];\n const withoutGroup = currentPath.replace(group, '');\n generate(currentIndex + 1, withoutGroup);\n generate(currentIndex + 1, currentPath);\n }\n\n generate(0, pathname);\n return combinations;\n}\n"]} \ No newline at end of file +{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/typed-routes/generate.ts"],"names":[],"mappings":"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","sourcesContent":["import fs from 'node:fs';\nimport path from 'path';\n\nimport { RouteNode } from '../Route';\nimport { getRoutes } from '../getRoutes';\nimport { isTypedRoute, removeSupportedExtensions } from '../matchers';\nimport { RequireContext } from '../types';\n\n// /[...param1]/ - Match [...param1]\nconst CATCH_ALL = /\\[\\.\\.\\..+?\\]/g;\n// /[param1] - Match [param1]\nconst SLUG = /\\[.+?\\]/g;\n\nexport function getTypedRoutesDeclarationFile(ctx: RequireContext) {\n const staticRoutes = new Set();\n const dynamicRoutes = new Set();\n const dynamicRouteContextKeys = new Set();\n\n walkRouteNode(\n getRoutes(ctx, {\n platformRoutes: false, // We don't need to generate platform specific routes\n ignoreEntryPoints: true,\n ignoreRequireErrors: true,\n importMode: 'async',\n }),\n staticRoutes,\n dynamicRoutes,\n dynamicRouteContextKeys\n );\n\n // If the user has expo-router v3+ installed, we can use the types from the package\n return (\n fs\n .readFileSync(path.join(__dirname, '../../types/expo-router.d.ts'), 'utf-8')\n // Swap from being a namespace to a module\n .replace('declare namespace ExpoRouter {', `declare module \"expo-router\" {`)\n // Add the route values\n .replace(\n 'type StaticRoutes = string;',\n `type StaticRoutes = ${setToUnionType(staticRoutes)};`\n )\n .replace(\n 'type DynamicRoutes = string;',\n `type DynamicRoutes = ${setToUnionType(dynamicRoutes)};`\n )\n .replace(\n 'type DynamicRouteTemplate = never;',\n `type DynamicRouteTemplate = ${setToUnionType(dynamicRouteContextKeys)};`\n )\n );\n}\n\n/**\n * Walks a RouteNode tree and adds the routes to the provided sets\n */\nfunction walkRouteNode(\n routeNode: RouteNode | null,\n staticRoutes: Set,\n dynamicRoutes: Set,\n dynamicRouteContextKeys: Set\n) {\n if (!routeNode) return;\n\n addRouteNode(routeNode, staticRoutes, dynamicRoutes, dynamicRouteContextKeys);\n\n for (const child of routeNode.children) {\n walkRouteNode(child, staticRoutes, dynamicRoutes, dynamicRouteContextKeys);\n }\n}\n\n/**\n * Given a RouteNode, adds the route to the correct sets\n * Modifies the RouteNode.route to be a typed-route string\n */\nfunction addRouteNode(\n routeNode: RouteNode | null,\n staticRoutes: Set,\n dynamicRoutes: Set,\n dynamicRouteContextKeys: Set\n) {\n if (!routeNode?.route) return;\n if (!isTypedRoute(routeNode.route)) return;\n\n let routePath = `${removeSupportedExtensions(routeNode.route).replace(/\\/?index$/, '')}`; // replace /index with /\n\n if (!routePath.startsWith('/')) {\n routePath = `/${routePath}`;\n }\n\n if (routeNode.dynamic) {\n for (const path of generateCombinations(routePath)) {\n dynamicRouteContextKeys.add(path);\n dynamicRoutes.add(\n `${path\n .replaceAll(CATCH_ALL, '${CatchAllRoutePart}')\n .replaceAll(SLUG, '${SingleRoutePart}')}`\n );\n }\n } else {\n for (const combination of generateCombinations(routePath)) {\n staticRoutes.add(combination);\n }\n }\n}\n\n/**\n * Converts a Set to a TypeScript union type\n */\nconst setToUnionType = (set: Set) => {\n return set.size > 0 ? [...set].map((s) => `\\`${s}\\``).join(' | ') : 'never';\n};\n\nfunction generateCombinations(pathname) {\n const groups = pathname.split('/').filter((part) => part.startsWith('(') && part.endsWith(')'));\n const combinations: string[] = [];\n\n function generate(currentIndex, currentPath) {\n if (currentIndex === groups.length) {\n combinations.push(currentPath.replace(/\\/{2,}/g, '/'));\n return;\n }\n\n const group = groups[currentIndex];\n const withoutGroup = currentPath.replace(group, '');\n generate(currentIndex + 1, withoutGroup);\n generate(currentIndex + 1, currentPath);\n }\n\n generate(0, pathname);\n return combinations;\n}\n"]} \ No newline at end of file diff --git a/packages/expo-router/plugin/options.json b/packages/expo-router/plugin/options.json index f7aa4e1a14ff4..d5b0ba6a03bd9 100644 --- a/packages/expo-router/plugin/options.json +++ b/packages/expo-router/plugin/options.json @@ -40,6 +40,10 @@ "description": "Changes the routes directory from `app` to another value. Defaults to `app`. Avoid using this property.", "type": "string" }, + "platformRoutes": { + "description": "Enable or disable platform-specific routes. Defaults to `true`.", + "type": "boolean" + }, "asyncRoutes": { "description": "Should Async Routes be enabled. `production` is currently web-only and will be disabled on native.", "oneOf": [ diff --git a/packages/expo-router/src/__tests__/getRoutes.test.ios.ts b/packages/expo-router/src/__tests__/getRoutes.test.ios.ts index 1adb5ce70c68f..6596fc2af2077 100644 --- a/packages/expo-router/src/__tests__/getRoutes.test.ios.ts +++ b/packages/expo-router/src/__tests__/getRoutes.test.ios.ts @@ -769,6 +769,25 @@ describe('api routes', () => { }); }); +it('ignores API routes with platform extensions', () => { + expect(() => { + getRoutes( + inMemoryContext({ + './folder/one.tsx': () => null, + './folder/two+api.web.tsx': () => null, + }), + { + internal_stripLoadRoute: true, + platform: 'web', + skipGenerated: true, + preserveApiRoutes: true, + } + ); + }).toThrowErrorMatchingInlineSnapshot( + `"Api routes cannot have platform extensions. Please remove '.web' from './folder/two+api.web.tsx'"` + ); +}); + describe('group expansion', () => { it(`array syntax`, () => { expect( diff --git a/packages/expo-router/src/__tests__/platform-routes.test.android.tsx b/packages/expo-router/src/__tests__/platform-routes.test.android.tsx new file mode 100644 index 0000000000000..dd686e01fbd6e --- /dev/null +++ b/packages/expo-router/src/__tests__/platform-routes.test.android.tsx @@ -0,0 +1,73 @@ +import { Platform } from 'react-native'; + +import { getRoutes } from '../getRoutes'; +import { inMemoryContext } from '../testing-library/context-stubs'; + +// This test needs to run in a web environment, as it needs to follow the platform extensions for `ctx-web.tsx` + +it(`should only load android and native routes`, () => { + expect( + getRoutes( + inMemoryContext({ + './(app)/index': () => null, + './(app)/page.ts': () => null, + './(app)/page.web.ts': () => null, + './(app)/page2.ts': () => null, + './(app)/page2.native.ts': () => null, + './(app)/page3.ts': () => null, + './(app)/page3.android.ts': () => null, + './(app)/page4.ts': () => null, + './(app)/page4.ios.ts': () => null, + }), + { internal_stripLoadRoute: true, platform: Platform.OS, skipGenerated: true } + ) + ).toEqual({ + children: [ + { + children: [], + contextKey: './(app)/index.js', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/index.js'], + route: '(app)/index', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page.ts'], + route: '(app)/page', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page2.native.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page2.native.ts'], + route: '(app)/page2', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page3.android.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page3.android.ts'], + route: '(app)/page3', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page4.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page4.ts'], + route: '(app)/page4', + type: 'route', + }, + ], + contextKey: 'expo-router/build/views/Navigator.js', + dynamic: null, + generated: true, + route: '', + type: 'layout', + }); +}); diff --git a/packages/expo-router/src/__tests__/platform-routes.test.ios.tsx b/packages/expo-router/src/__tests__/platform-routes.test.ios.tsx new file mode 100644 index 0000000000000..64bc15dafc146 --- /dev/null +++ b/packages/expo-router/src/__tests__/platform-routes.test.ios.tsx @@ -0,0 +1,73 @@ +import { Platform } from 'react-native'; + +import { getRoutes } from '../getRoutes'; +import { inMemoryContext } from '../testing-library/context-stubs'; + +// This test needs to run in a web environment, as it needs to follow the platform extensions for `ctx-web.tsx` + +it(`should only load android and native routes`, () => { + expect( + getRoutes( + inMemoryContext({ + './(app)/index': () => null, + './(app)/page.ts': () => null, + './(app)/page.web.ts': () => null, + './(app)/page2.ts': () => null, + './(app)/page2.native.ts': () => null, + './(app)/page3.ts': () => null, + './(app)/page3.android.ts': () => null, + './(app)/page4.ts': () => null, + './(app)/page4.ios.ts': () => null, + }), + { internal_stripLoadRoute: true, platform: Platform.OS, skipGenerated: true } + ) + ).toEqual({ + children: [ + { + children: [], + contextKey: './(app)/index.js', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/index.js'], + route: '(app)/index', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page.ts'], + route: '(app)/page', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page2.native.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page2.native.ts'], + route: '(app)/page2', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page3.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page3.ts'], + route: '(app)/page3', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page4.ios.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page4.ios.ts'], + route: '(app)/page4', + type: 'route', + }, + ], + contextKey: 'expo-router/build/views/Navigator.js', + dynamic: null, + generated: true, + route: '', + type: 'layout', + }); +}); diff --git a/packages/expo-router/src/__tests__/platform-routes.test.node.ts b/packages/expo-router/src/__tests__/platform-routes.test.node.ts new file mode 100644 index 0000000000000..3b62106984294 --- /dev/null +++ b/packages/expo-router/src/__tests__/platform-routes.test.node.ts @@ -0,0 +1,71 @@ +import { getRoutes } from '../getRoutes'; +import { inMemoryContext } from '../testing-library/context-stubs'; + +// This test needs to run in a node environment, as it needs to follow the platform extensions for `ctx.tsx` + +it(`should skip platform routes when no platform has been provided`, () => { + expect( + getRoutes( + inMemoryContext({ + './(app)/index': () => null, + './(app)/page.ts': () => null, + './(app)/page.web.ts': () => null, + './(app)/page2.ts': () => null, + './(app)/page2.native.ts': () => null, + './(app)/page3.ts': () => null, + './(app)/page3.android.ts': () => null, + './(app)/page4.ts': () => null, + './(app)/page4.ios.ts': () => null, + }), + { internal_stripLoadRoute: true, platform: undefined, skipGenerated: true } + ) + ).toEqual({ + children: [ + { + children: [], + contextKey: './(app)/index.js', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/index.js'], + route: '(app)/index', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page.ts'], + route: '(app)/page', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page2.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page2.ts'], + route: '(app)/page2', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page3.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page3.ts'], + route: '(app)/page3', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page4.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page4.ts'], + route: '(app)/page4', + type: 'route', + }, + ], + contextKey: 'expo-router/build/views/Navigator.js', + dynamic: null, + generated: true, + route: '', + type: 'layout', + }); +}); diff --git a/packages/expo-router/src/__tests__/platform-routes.test.web.ts b/packages/expo-router/src/__tests__/platform-routes.test.web.ts new file mode 100644 index 0000000000000..a9d537bd6f582 --- /dev/null +++ b/packages/expo-router/src/__tests__/platform-routes.test.web.ts @@ -0,0 +1,211 @@ +import { Platform } from 'react-native'; + +import { getRoutes } from '../getRoutes'; +import { inMemoryContext } from '../testing-library/context-stubs'; + +// This test needs to run in a web environment, as it needs to follow the platform extensions for `ctx-web.tsx` + +it(`should only load web routes`, () => { + expect( + getRoutes( + inMemoryContext({ + './(app)/index': () => null, + './(app)/page.ts': () => null, + './(app)/page.web.ts': () => null, + './(app)/page2.ts': () => null, + './(app)/page2.native.ts': () => null, + './(app)/page3.ts': () => null, + './(app)/page3.android.ts': () => null, + './(app)/page4.ts': () => null, + './(app)/page4.ios.ts': () => null, + }), + { internal_stripLoadRoute: true, platform: Platform.OS, skipGenerated: true } + ) + ).toEqual({ + children: [ + { + children: [], + contextKey: './(app)/index.js', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/index.js'], + route: '(app)/index', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page.web.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page.web.ts'], + route: '(app)/page', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page2.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page2.ts'], + route: '(app)/page2', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page3.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page3.ts'], + route: '(app)/page3', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page4.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page4.ts'], + route: '(app)/page4', + type: 'route', + }, + ], + contextKey: 'expo-router/build/views/Navigator.js', + dynamic: null, + generated: true, + route: '', + type: 'layout', + }); +}); + +it(`should skip platform routes when no platform has been provided`, () => { + expect( + getRoutes( + inMemoryContext({ + './(app)/index': () => null, + './(app)/page.ts': () => null, + './(app)/page.web.ts': () => null, + './(app)/page2.ts': () => null, + './(app)/page2.native.ts': () => null, + './(app)/page3.ts': () => null, + './(app)/page3.android.ts': () => null, + './(app)/page4.ts': () => null, + './(app)/page4.ios.ts': () => null, + }), + { internal_stripLoadRoute: true, platform: undefined, skipGenerated: true } + ) + ).toEqual({ + children: [ + { + children: [], + contextKey: './(app)/index.js', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/index.js'], + route: '(app)/index', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page.ts'], + route: '(app)/page', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page2.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page2.ts'], + route: '(app)/page2', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page3.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page3.ts'], + route: '(app)/page3', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page4.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page4.ts'], + route: '(app)/page4', + type: 'route', + }, + ], + contextKey: 'expo-router/build/views/Navigator.js', + dynamic: null, + generated: true, + route: '', + type: 'layout', + }); +}); + +describe(`will throw if a route does not have a platform fallback`, () => { + it('layouts', () => { + expect(() => { + getRoutes( + inMemoryContext({ + './folder/_layout.web.tsx': () => null, + './folder/page.tsx': () => null, + }), + { internal_stripLoadRoute: true, platform: Platform.OS, skipGenerated: true } + ); + }).toThrowErrorMatchingInlineSnapshot( + `"The file ./folder/_layout.web.tsx does not have a fallback sibling file without a platform extension."` + ); + }); + + it('pages', () => { + expect(() => { + getRoutes( + inMemoryContext({ + './folder/page.web.tsx': () => null, + }), + { internal_stripLoadRoute: true, platform: Platform.OS, skipGenerated: true } + ); + }).toThrowErrorMatchingInlineSnapshot( + `"The file ./folder/page.web.tsx does not have a fallback sibling file without a platform extension."` + ); + }); +}); + +it(`can display platform routes`, () => { + expect( + getRoutes( + inMemoryContext({ + './(app)/index': () => null, + './(app)/page.ts': () => null, + './(app)/page.web.ts': () => null, + }), + { + internal_stripLoadRoute: true, + platform: Platform.OS, + skipGenerated: true, + platformRoutes: false, + } + ) + ).toEqual({ + children: [ + { + children: [], + contextKey: './(app)/index.js', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/index.js'], + route: '(app)/index', + type: 'route', + }, + { + children: [], + contextKey: './(app)/page.ts', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(app)/page.ts'], + route: '(app)/page', + type: 'route', + }, + ], + contextKey: 'expo-router/build/views/Navigator.js', + dynamic: null, + generated: true, + route: '', + type: 'layout', + }); +}); diff --git a/packages/expo-router/src/getRoutes.ts b/packages/expo-router/src/getRoutes.ts index 345ebe4c78429..5f3ea70855894 100644 --- a/packages/expo-router/src/getRoutes.ts +++ b/packages/expo-router/src/getRoutes.ts @@ -18,6 +18,8 @@ export type Options = { /* Used to simplify by skipping the generated routes */ skipGenerated?: boolean; importMode?: string; + platformRoutes?: boolean; + platform?: string; }; type DirectoryNode = { @@ -26,6 +28,8 @@ type DirectoryNode = { subdirectories: Map; }; +const validPlatforms = new Set(['android', 'ios', 'native', 'web']); + /** * Given a Metro context module, return an array of nested routes. * @@ -95,7 +99,7 @@ function getDirectoryTree(contextModule: RequireContext, options: Options) { isValid = true; - const meta = getFileMeta(filePath); + const meta = getFileMeta(filePath, options); // This is a file that should be ignored. e.g maybe it has an invalid platform? if (meta.specificity < 0) { @@ -183,8 +187,7 @@ function getDirectoryTree(contextModule: RequireContext, options: Options) { directory.files.set(fileKey, nodes); } - // TODO(Platform Route): Throw error if specificity > 0, as you cannot specify platform extensions for api routes - + // API Routes have no specificity, they are always the first node const existing = nodes[0]; if (existing) { @@ -280,7 +283,7 @@ function flattenDirectoryTreeToRoutes( */ if (directory.layout) { const previousLayout = layout; - layout = directory.layout[0]; // TODO(Platform Routes): We need to pick the most specific layout. + layout = getMostSpecific(directory.layout); // Add the new layout as a child of its parent if (previousLayout) { @@ -304,8 +307,7 @@ function flattenDirectoryTreeToRoutes( if (!layout) throw new Error('Expo Router Internal Error: No nearest layout'); for (const routes of directory.files.values()) { - // TODO(Platform Routes): We need to pick the most specific layout and ensure that all routes have a non-platform route. - const routeNode = routes[0]; + const routeNode = getMostSpecific(routes); // `route` is the absolute pathname. We need to make this relative to the nearest layout routeNode.route = routeNode.route.replace(pathToRemove, ''); @@ -326,15 +328,16 @@ function flattenDirectoryTreeToRoutes( return layout; } -function getFileMeta(key: string) { +function getFileMeta(key: string, options: Options) { // Remove the leading `./` key = key.replace(/^\.\//, ''); const parts = key.split('/'); + let route = removeSupportedExtensions(key); const filename = parts[parts.length - 1]; const filenameWithoutExtensions = removeSupportedExtensions(filename); const isLayout = filenameWithoutExtensions === '_layout'; - const isApi = filename.match(/\+api\.[jt]sx?$/); + const isApi = filename.match(/\+api\.(\w+\.)?[jt]sx?$/); if (filenameWithoutExtensions.startsWith('(') && filenameWithoutExtensions.endsWith(')')) { throw new Error(`Invalid route ./${key}. Routes cannot end with '(group)' syntax`); @@ -347,10 +350,44 @@ function getFileMeta(key: string) { `Invalid route ./${key}. Route nodes cannot start with the '+' character. "Please rename to ${renamedRoute}"` ); } + let specificity = 0; + + const platformExtension = filenameWithoutExtensions.split('.')[1]; + const hasPlatformExtension = validPlatforms.has(platformExtension); + const usePlatformRoutes = options.platformRoutes ?? true; + + if (hasPlatformExtension) { + if (!usePlatformRoutes) { + // If the user has disabled platform routes, then we should ignore this file + specificity = -1; + } else if (!options.platform) { + // If we don't have a platform, then we should ignore this file + // This used by typed routes, sitemap, etc + specificity = -1; + } else if (platformExtension === options.platform) { + // If the platform extension is the same as the options.platform, then it is the most specific + specificity = 2; + } else if (platformExtension === 'native' && options.platform !== 'web') { + // `native` is allow but isn't as specific as the platform + specificity = 1; + } else if (platformExtension !== options.platform) { + // Somehow we have a platform extension that doesn't match the options.platform and it isn't native + // This is an invalid file and we will ignore it + specificity = -1; + } + + if (isApi && specificity !== 0) { + throw new Error( + `Api routes cannot have platform extensions. Please remove '.${platformExtension}' from './${key}'` + ); + } + + route = route.replace(new RegExp(`.${platformExtension}$`), ''); + } return { - route: removeSupportedExtensions(key), - specificity: 0, + route, + specificity, isLayout, isApi, }; @@ -564,3 +601,17 @@ function crawlAndAppendInitialRoutesAndEntryFiles( } } } + +function getMostSpecific(routes: RouteNode[]) { + const route = routes[routes.length - 1]; + + if (!routes[0]) { + throw new Error( + `The file ${route.contextKey} does not have a fallback sibling file without a platform extension.` + ); + } + + // This works even tho routes is holey array (e.g it might have index 0 and 2 but not 1) + // `.length` includes the holes in its count + return routes[routes.length - 1]; +} diff --git a/packages/expo-router/src/global-state/router-store.tsx b/packages/expo-router/src/global-state/router-store.tsx index 81d7565901f0e..cf393e61c0863 100644 --- a/packages/expo-router/src/global-state/router-store.tsx +++ b/packages/expo-router/src/global-state/router-store.tsx @@ -3,8 +3,10 @@ import { getPathFromState, useNavigationContainerRef, } from '@react-navigation/native'; +import Constants from 'expo-constants'; import * as SplashScreen from 'expo-splash-screen'; import { useSyncExternalStore, useMemo, ComponentType, Fragment } from 'react'; +import { Platform } from 'react-native'; import { canGoBack, @@ -78,7 +80,11 @@ export class RouterStore { this.rootStateSubscribers.clear(); this.storeSubscribers.clear(); - this.routeNode = getRoutes(context, { ignoreEntryPoints: true }); + this.routeNode = getRoutes(context, { + ...Constants.expoConfig?.extra?.router, + ignoreEntryPoints: true, + platform: Platform.OS, + }); this.rootComponent = this.routeNode ? getQualifiedRouteComponent(this.routeNode) : Fragment; diff --git a/packages/expo-router/src/routes-manifest.ts b/packages/expo-router/src/routes-manifest.ts index ebdc1f7dc0832..4cf2e5ee23834 100644 --- a/packages/expo-router/src/routes-manifest.ts +++ b/packages/expo-router/src/routes-manifest.ts @@ -1,9 +1,11 @@ // This file runs in Node.js environments. // no relative imports -import { getRoutes } from './getRoutes'; +import { Options, getRoutes } from './getRoutes'; import { getServerManifest } from './getServerManifest'; import { RequireContext } from './types'; +export { Options } from './getRoutes'; + export type RouteInfo = { file: string; page: string; @@ -27,12 +29,17 @@ function createMockContextModule(map: string[] = []) { return contextModule as RequireContext; } -export function createRoutesManifest(paths: string[]): ExpoRoutesManifestV1 | null { +export function createRoutesManifest( + paths: string[], + options: Options +): ExpoRoutesManifestV1 | null { // TODO: Drop this part for Node.js const routeTree = getRoutes(createMockContextModule(paths), { + ...options, preserveApiRoutes: true, ignoreRequireErrors: true, ignoreEntryPoints: true, + platform: 'web', }); if (!routeTree) { diff --git a/packages/expo-router/src/static/renderStaticContent.tsx b/packages/expo-router/src/static/renderStaticContent.tsx index 69929ea7dc2ff..0c65c650555fa 100644 --- a/packages/expo-router/src/static/renderStaticContent.tsx +++ b/packages/expo-router/src/static/renderStaticContent.tsx @@ -16,7 +16,7 @@ import { getRootComponent } from './getRootComponent'; import { ctx } from '../../_ctx'; import { ExpoRoot } from '../ExpoRoot'; import { getReactNavigationConfig } from '../getReactNavigationConfig'; -import { getRoutes } from '../getRoutes'; +import { getRoutes, Options } from '../getRoutes'; import { ExpoRouterServerManifestV1, getServerManifest } from '../getServerManifest'; import { Head } from '../head'; import { loadStaticParamsAsync } from '../loadStaticParamsAsync'; @@ -26,8 +26,12 @@ const debug = require('debug')('expo:router:renderStaticContent'); AppRegistry.registerComponent('App', () => ExpoRoot); /** Get the linking manifest from a Node.js process. */ -async function getManifest(options: Parameters[1] = {}) { - const routeTree = getRoutes(ctx, { preserveApiRoutes: true, ...options }); +async function getManifest(options: Options = {}) { + const routeTree = getRoutes(ctx, { + preserveApiRoutes: true, + platform: 'web', + ...options, + }); if (!routeTree) { throw new Error('No routes found'); @@ -47,9 +51,12 @@ async function getManifest(options: Parameters[1] = {}) { * This is used for the production manifest where we pre-render certain pages and should no longer treat them as dynamic. */ async function getBuildTimeServerManifestAsync( - options: Parameters[1] = {} + options: Options = {} ): Promise { - const routeTree = getRoutes(ctx, options); + const routeTree = getRoutes(ctx, { + platform: 'web', + ...options, + }); if (!routeTree) { throw new Error('No routes found'); diff --git a/packages/expo-router/src/typed-routes/generate.ts b/packages/expo-router/src/typed-routes/generate.ts index 3c3e42f2a12da..c851c90ca5ab0 100644 --- a/packages/expo-router/src/typed-routes/generate.ts +++ b/packages/expo-router/src/typed-routes/generate.ts @@ -18,6 +18,7 @@ export function getTypedRoutesDeclarationFile(ctx: RequireContext) { walkRouteNode( getRoutes(ctx, { + platformRoutes: false, // We don't need to generate platform specific routes ignoreEntryPoints: true, ignoreRequireErrors: true, importMode: 'async',