diff --git a/packages/expo-router/CHANGELOG.md b/packages/expo-router/CHANGELOG.md index be8f7d6065ef2..5f1d8fab56e9d 100644 --- a/packages/expo-router/CHANGELOG.md +++ b/packages/expo-router/CHANGELOG.md @@ -21,6 +21,7 @@ - Fix using parenthesis in urls ([#27120](https://github.com/expo/expo/pull/27120) by [@marklawlor](https://github.com/marklawlor)) - Fix `push` navigation not pushing the same route multiple times ([#27307](https://github.com/expo/expo/pull/27307) by [@marklawlor](https://github.com/marklawlor)) - Fix router.navigate will only push when path parameters change ([#27285](https://github.com/expo/expo/pull/27285) by [@marklawlor](https://github.com/marklawlor)) +- Fix incorrect route generation of array shared groups with brackets ([#27459](https://github.com/expo/expo/pull/27459) by [@marklawlor](https://github.com/marklawlor)) ### 💡 Others @@ -63,7 +64,7 @@ _This version does not introduce any user-facing changes._ - Deprecate `useRootNavigation` in favor of `useNavigationContainerRef`. ([#26529](https://github.com/expo/expo/pull/26529) by [@EvanBacon](https://github.com/EvanBacon)) - Remove duplicate context mocking functions ([#26651](https://github.com/expo/expo/pull/26651) by [@marklawlor](https://github.com/marklawlor)) -- Update to remove `ExpoRequest/`ExpoResponse` imports from `@expo/server`. ([#27261](https://github.com/expo/expo/pull/27261) by [@kitten](https://github.com/kitten)) +- Update to remove `ExpoRequest`/`ExpoResponse` imports from `@expo/server`. ([#27261](https://github.com/expo/expo/pull/27261) by [@kitten](https://github.com/kitten)) ## 3.4.3 - 2024-01-18 diff --git a/packages/expo-router/build/getRoutes.js b/packages/expo-router/build/getRoutes.js index 0499c6d443dbe..fded604b6f4ea 100644 --- a/packages/expo-router/build/getRoutes.js +++ b/packages/expo-router/build/getRoutes.js @@ -292,7 +292,7 @@ exports.getIgnoreList = getIgnoreList; * /(a,b)/(c,d)/e.tsx => new Set(['a/c/e.tsx', 'a/d/e.tsx', 'b/c/e.tsx', 'b/d/e.tsx']) */ function extrapolateGroups(key, keys = new Set()) { - const match = (0, matchers_1.matchArrayGroupName)(key)?.[0]; + const match = (0, matchers_1.matchArrayGroupName)(key); if (!match) { keys.add(key); return keys; diff --git a/packages/expo-router/build/getRoutes.js.map b/packages/expo-router/build/getRoutes.js.map index 6e4bf29d28b2c..9e32d10cc9683 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 */\nfunction extrapolateGroups(key: string, keys: Set = new Set()): Set {\n const match = matchArrayGroupName(key)?.[0];\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};\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 */\nfunction 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 diff --git a/packages/expo-router/build/matchers.d.ts b/packages/expo-router/build/matchers.d.ts index 194b2154b8740..7bd324ae82269 100644 --- a/packages/expo-router/build/matchers.d.ts +++ b/packages/expo-router/build/matchers.d.ts @@ -6,8 +6,8 @@ export declare function matchDeepDynamicRouteName(name: string): string | undefi export declare function testNotFound(name: string): boolean; /** Match `(page)` -> `page` */ export declare function matchGroupName(name: string): string | undefined; -/** Match `(a,b,c)/(d,c)` -> `[['a','b','c'], ['d','e']]` */ -export declare function matchArrayGroupName(name: string): string[] | undefined; +/** Match the first array group name `(a,b,c)/(d,c)` -> `'a,b,c'` */ +export declare function matchArrayGroupName(name: string): string | undefined; export declare function getNameFromFilePath(name: string): string; export declare function getContextKey(name: string): string; /** Remove `.js`, `.ts`, `.jsx`, `.tsx` */ diff --git a/packages/expo-router/build/matchers.d.ts.map b/packages/expo-router/build/matchers.d.ts.map index df2d9df82eea8..d3d5a4d2e80ba 100644 --- a/packages/expo-router/build/matchers.d.ts.map +++ b/packages/expo-router/build/matchers.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../src/matchers.tsx"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIjE;AAED,kCAAkC;AAClC,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE1E;AAED,yBAAyB;AACzB,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,+BAA+B;AAC/B,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE/D;AAED,4DAA4D;AAC5D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,wBAE/C;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQlD;AAED,0CAA0C;AAC1C,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAGD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAU/D;AAED,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,WAExC"} \ No newline at end of file +{"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../src/matchers.tsx"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIjE;AAED,kCAAkC;AAClC,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE1E;AAED,yBAAyB;AACzB,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,+BAA+B;AAC/B,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE/D;AAED,oEAAoE;AACpE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,sBAE/C;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQlD;AAED,0CAA0C;AAC1C,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9D;AAGD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAU/D;AAED,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,WAExC"} \ No newline at end of file diff --git a/packages/expo-router/build/matchers.js b/packages/expo-router/build/matchers.js index 1f612543c2d07..54b0e32eab24d 100644 --- a/packages/expo-router/build/matchers.js +++ b/packages/expo-router/build/matchers.js @@ -23,9 +23,9 @@ function matchGroupName(name) { return name.match(/^(?:[^\\(\\)])*?\(([^\\/]+)\).*?$/)?.[1]; } exports.matchGroupName = matchGroupName; -/** Match `(a,b,c)/(d,c)` -> `[['a','b','c'], ['d','e']]` */ +/** Match the first array group name `(a,b,c)/(d,c)` -> `'a,b,c'` */ function matchArrayGroupName(name) { - return name.match(/\(\s*\w[\w\s]*?,.*?\)/g)?.map((match) => match.slice(1, -1)); + return name.match(/(?:[^\\(\\)])*?\(([^\\/]+,[^\\/]+)\).*?$/)?.[1]; } exports.matchArrayGroupName = matchArrayGroupName; function getNameFromFilePath(name) { diff --git a/packages/expo-router/build/matchers.js.map b/packages/expo-router/build/matchers.js.map index b19e827107636..243ab6641a754 100644 --- a/packages/expo-router/build/matchers.js.map +++ b/packages/expo-router/build/matchers.js.map @@ -1 +1 @@ -{"version":3,"file":"matchers.js","sourceRoot":"","sources":["../src/matchers.tsx"],"names":[],"mappings":";;;AAAA,+BAA+B;AAC/B,SAAgB,gBAAgB,CAAC,IAAY;IAC3C,sDAAsD;IACtD,6CAA6C;IAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,4BAA4B,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC;AAJD,4CAIC;AAED,kCAAkC;AAClC,SAAgB,yBAAyB,CAAC,IAAY;IACpD,OAAO,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC;AAFD,8DAEC;AAED,yBAAyB;AACzB,SAAgB,YAAY,CAAC,IAAY;IACvC,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAFD,oCAEC;AAED,+BAA+B;AAC/B,SAAgB,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,mCAAmC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAFD,wCAEC;AAED,4DAA4D;AAC5D,SAAgB,mBAAmB,CAAC,IAAY;IAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,wBAAwB,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAClF,CAAC;AAFD,kDAEC;AAED,SAAgB,mBAAmB,CAAC,IAAY;IAC9C,OAAO,yBAAyB,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC;AAC/D,CAAC;AAFD,kDAEC;AAED,SAAgB,aAAa,CAAC,IAAY;IACxC,qEAAqE;IACrE,uBAAuB;IACvB,MAAM,MAAM,GAAG,GAAG,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE;QAC/B,OAAO,MAAM,CAAC;KACf;IACD,OAAO,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC;AARD,sCAQC;AAED,0CAA0C;AAC1C,SAAgB,yBAAyB,CAAC,IAAY;IACpD,OAAO,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;AACjD,CAAC;AAFD,8DAEC;AAED,mEAAmE;AACnE,SAAgB,oBAAoB,CAAC,QAAgB;IACnD,OAAO,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;AAChD,CAAC;AAFD,oDAEC;AAED,SAAgB,0BAA0B,CAAC,IAAY;IACrD,OAAO,IAAI;SACR,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;QACjB,IAAI,cAAc,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE;YAC7B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACb;QACD,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,EAAc,CAAC;SACjB,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAVD,gEAUC;AAED,SAAgB,8BAA8B,CAAC,IAAY;IACzD,OAAO,0BAA0B,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACnE,CAAC;AAFD,wEAEC;AAED;;;;GAIG;AACH,SAAgB,YAAY,CAAC,IAAY;IACvC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,oCAAoC,CAAC,KAAK,IAAI,CAAC;AAC5F,CAAC;AAFD,oCAEC","sourcesContent":["/** Match `[page]` -> `page` */\nexport function matchDynamicName(name: string): string | undefined {\n // Don't match `...` or `[` or `]` inside the brackets\n // eslint-disable-next-line no-useless-escape\n return name.match(/^\\[([^[\\](?:\\.\\.\\.)]+?)\\]$/)?.[1];\n}\n\n/** Match `[...page]` -> `page` */\nexport function matchDeepDynamicRouteName(name: string): string | undefined {\n return name.match(/^\\[\\.\\.\\.([^/]+?)\\]$/)?.[1];\n}\n\n/** Test `/` -> `page` */\nexport function testNotFound(name: string): boolean {\n return /\\+not-found$/.test(name);\n}\n\n/** Match `(page)` -> `page` */\nexport function matchGroupName(name: string): string | undefined {\n return name.match(/^(?:[^\\\\(\\\\)])*?\\(([^\\\\/]+)\\).*?$/)?.[1];\n}\n\n/** Match `(a,b,c)/(d,c)` -> `[['a','b','c'], ['d','e']]` */\nexport function matchArrayGroupName(name: string) {\n return name.match(/\\(\\s*\\w[\\w\\s]*?,.*?\\)/g)?.map((match) => match.slice(1, -1));\n}\n\nexport function getNameFromFilePath(name: string): string {\n return removeSupportedExtensions(removeFileSystemDots(name));\n}\n\nexport function getContextKey(name: string): string {\n // The root path is `` (empty string) so always prepend `/` to ensure\n // there is some value.\n const normal = '/' + getNameFromFilePath(name);\n if (!normal.endsWith('_layout')) {\n return normal;\n }\n return normal.replace(/\\/?_layout$/, '');\n}\n\n/** Remove `.js`, `.ts`, `.jsx`, `.tsx` */\nexport function removeSupportedExtensions(name: string): string {\n return name.replace(/(\\+api)?\\.[jt]sx?$/g, '');\n}\n\n// Remove any amount of `./` and `../` from the start of the string\nexport function removeFileSystemDots(filePath: string): string {\n return filePath.replace(/^(?:\\.\\.?\\/)+/g, '');\n}\n\nexport function stripGroupSegmentsFromPath(path: string): string {\n return path\n .split('/')\n .reduce((acc, v) => {\n if (matchGroupName(v) == null) {\n acc.push(v);\n }\n return acc;\n }, [] as string[])\n .join('/');\n}\n\nexport function stripInvisibleSegmentsFromPath(path: string): string {\n return stripGroupSegmentsFromPath(path).replace(/\\/?index$/, '');\n}\n\n/**\n * Match:\n * - _layout files, +html, +not-found, string+api, etc\n * - Routes can still use `+`, but it cannot be in the last segment.\n */\nexport function isTypedRoute(name: string) {\n return !name.startsWith('+') && name.match(/(_layout|[^/]*?\\+[^/]*?)\\.[tj]sx?$/) === null;\n}\n"]} \ No newline at end of file +{"version":3,"file":"matchers.js","sourceRoot":"","sources":["../src/matchers.tsx"],"names":[],"mappings":";;;AAAA,+BAA+B;AAC/B,SAAgB,gBAAgB,CAAC,IAAY;IAC3C,sDAAsD;IACtD,6CAA6C;IAC7C,OAAO,IAAI,CAAC,KAAK,CAAC,4BAA4B,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC;AAJD,4CAIC;AAED,kCAAkC;AAClC,SAAgB,yBAAyB,CAAC,IAAY;IACpD,OAAO,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC;AAFD,8DAEC;AAED,yBAAyB;AACzB,SAAgB,YAAY,CAAC,IAAY;IACvC,OAAO,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC;AAFD,oCAEC;AAED,+BAA+B;AAC/B,SAAgB,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,mCAAmC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAFD,wCAEC;AAED,oEAAoE;AACpE,SAAgB,mBAAmB,CAAC,IAAY;IAC9C,OAAO,IAAI,CAAC,KAAK,CAAC,0CAA0C,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACrE,CAAC;AAFD,kDAEC;AAED,SAAgB,mBAAmB,CAAC,IAAY;IAC9C,OAAO,yBAAyB,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC;AAC/D,CAAC;AAFD,kDAEC;AAED,SAAgB,aAAa,CAAC,IAAY;IACxC,qEAAqE;IACrE,uBAAuB;IACvB,MAAM,MAAM,GAAG,GAAG,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE;QAC/B,OAAO,MAAM,CAAC;KACf;IACD,OAAO,MAAM,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC;AARD,sCAQC;AAED,0CAA0C;AAC1C,SAAgB,yBAAyB,CAAC,IAAY;IACpD,OAAO,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;AACjD,CAAC;AAFD,8DAEC;AAED,mEAAmE;AACnE,SAAgB,oBAAoB,CAAC,QAAgB;IACnD,OAAO,QAAQ,CAAC,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC;AAChD,CAAC;AAFD,oDAEC;AAED,SAAgB,0BAA0B,CAAC,IAAY;IACrD,OAAO,IAAI;SACR,KAAK,CAAC,GAAG,CAAC;SACV,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE;QACjB,IAAI,cAAc,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE;YAC7B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACb;QACD,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,EAAc,CAAC;SACjB,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAVD,gEAUC;AAED,SAAgB,8BAA8B,CAAC,IAAY;IACzD,OAAO,0BAA0B,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACnE,CAAC;AAFD,wEAEC;AAED;;;;GAIG;AACH,SAAgB,YAAY,CAAC,IAAY;IACvC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,oCAAoC,CAAC,KAAK,IAAI,CAAC;AAC5F,CAAC;AAFD,oCAEC","sourcesContent":["/** Match `[page]` -> `page` */\nexport function matchDynamicName(name: string): string | undefined {\n // Don't match `...` or `[` or `]` inside the brackets\n // eslint-disable-next-line no-useless-escape\n return name.match(/^\\[([^[\\](?:\\.\\.\\.)]+?)\\]$/)?.[1];\n}\n\n/** Match `[...page]` -> `page` */\nexport function matchDeepDynamicRouteName(name: string): string | undefined {\n return name.match(/^\\[\\.\\.\\.([^/]+?)\\]$/)?.[1];\n}\n\n/** Test `/` -> `page` */\nexport function testNotFound(name: string): boolean {\n return /\\+not-found$/.test(name);\n}\n\n/** Match `(page)` -> `page` */\nexport function matchGroupName(name: string): string | undefined {\n return name.match(/^(?:[^\\\\(\\\\)])*?\\(([^\\\\/]+)\\).*?$/)?.[1];\n}\n\n/** Match the first array group name `(a,b,c)/(d,c)` -> `'a,b,c'` */\nexport function matchArrayGroupName(name: string) {\n return name.match(/(?:[^\\\\(\\\\)])*?\\(([^\\\\/]+,[^\\\\/]+)\\).*?$/)?.[1];\n}\n\nexport function getNameFromFilePath(name: string): string {\n return removeSupportedExtensions(removeFileSystemDots(name));\n}\n\nexport function getContextKey(name: string): string {\n // The root path is `` (empty string) so always prepend `/` to ensure\n // there is some value.\n const normal = '/' + getNameFromFilePath(name);\n if (!normal.endsWith('_layout')) {\n return normal;\n }\n return normal.replace(/\\/?_layout$/, '');\n}\n\n/** Remove `.js`, `.ts`, `.jsx`, `.tsx` */\nexport function removeSupportedExtensions(name: string): string {\n return name.replace(/(\\+api)?\\.[jt]sx?$/g, '');\n}\n\n// Remove any amount of `./` and `../` from the start of the string\nexport function removeFileSystemDots(filePath: string): string {\n return filePath.replace(/^(?:\\.\\.?\\/)+/g, '');\n}\n\nexport function stripGroupSegmentsFromPath(path: string): string {\n return path\n .split('/')\n .reduce((acc, v) => {\n if (matchGroupName(v) == null) {\n acc.push(v);\n }\n return acc;\n }, [] as string[])\n .join('/');\n}\n\nexport function stripInvisibleSegmentsFromPath(path: string): string {\n return stripGroupSegmentsFromPath(path).replace(/\\/?index$/, '');\n}\n\n/**\n * Match:\n * - _layout files, +html, +not-found, string+api, etc\n * - Routes can still use `+`, but it cannot be in the last segment.\n */\nexport function isTypedRoute(name: string) {\n return !name.startsWith('+') && name.match(/(_layout|[^/]*?\\+[^/]*?)\\.[tj]sx?$/) === null;\n}\n"]} \ No newline at end of file diff --git a/packages/expo-router/src/__tests__/getRoutes.test.web.ts b/packages/expo-router/src/__tests__/getRoutes.test.web.ts index 2017284e5ae5b..1adb5ce70c68f 100644 --- a/packages/expo-router/src/__tests__/getRoutes.test.web.ts +++ b/packages/expo-router/src/__tests__/getRoutes.test.web.ts @@ -67,57 +67,6 @@ describe('getRoutes', () => { }); }); - it(`should ensure grouped routes expanded to all possible routes`, () => { - expect( - getRoutes( - inMemoryContext({ - './(a,b)/(c,d)/page': () => null, - }), - { internal_stripLoadRoute: true, skipGenerated: true } - ) - ).toEqual({ - children: [ - { - type: 'route', - contextKey: './(a,b)/(c,d)/page.js', - dynamic: null, - entryPoints: ['expo-router/build/views/Navigator.js', './(a,b)/(c,d)/page.js'], - route: '(a)/(c)/page', - children: [], - }, - { - type: 'route', - contextKey: './(a,b)/(c,d)/page.js', - dynamic: null, - entryPoints: ['expo-router/build/views/Navigator.js', './(a,b)/(c,d)/page.js'], - route: '(a)/(d)/page', - children: [], - }, - { - type: 'route', - contextKey: './(a,b)/(c,d)/page.js', - dynamic: null, - entryPoints: ['expo-router/build/views/Navigator.js', './(a,b)/(c,d)/page.js'], - route: '(b)/(c)/page', - children: [], - }, - { - type: 'route', - contextKey: './(a,b)/(c,d)/page.js', - dynamic: null, - entryPoints: ['expo-router/build/views/Navigator.js', './(a,b)/(c,d)/page.js'], - route: '(b)/(d)/page', - children: [], - }, - ], - contextKey: 'expo-router/build/views/Navigator.js', - dynamic: null, - generated: true, - type: 'layout', - route: '', - }); - }); - it(`should not append a _layout if there already is a top level layout`, () => { expect( getRoutes( @@ -819,3 +768,182 @@ describe('api routes', () => { }); }); }); + +describe('group expansion', () => { + it(`array syntax`, () => { + expect( + getRoutes( + inMemoryContext({ + './(single)/directory/(a,b)/mixed': () => null, + }), + { internal_stripLoadRoute: true, skipGenerated: true } + ) + ).toEqual({ + children: [ + { + children: [], + contextKey: './(single)/directory/(a,b)/mixed.js', + dynamic: null, + entryPoints: [ + 'expo-router/build/views/Navigator.js', + './(single)/directory/(a,b)/mixed.js', + ], + route: '(single)/directory/(a)/mixed', + type: 'route', + }, + { + children: [], + contextKey: './(single)/directory/(a,b)/mixed.js', + dynamic: null, + entryPoints: [ + 'expo-router/build/views/Navigator.js', + './(single)/directory/(a,b)/mixed.js', + ], + route: '(single)/directory/(b)/mixed', + type: 'route', + }, + ], + contextKey: 'expo-router/build/views/Navigator.js', + dynamic: null, + generated: true, + route: '', + type: 'layout', + }); + }); + + it(`multiple arrays`, () => { + expect( + getRoutes( + inMemoryContext({ + './(a,b)/(c,d)/multiple-arrays': () => null, + }), + { internal_stripLoadRoute: true, skipGenerated: true } + ) + ).toEqual({ + children: [ + { + children: [], + contextKey: './(a,b)/(c,d)/multiple-arrays.js', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(a,b)/(c,d)/multiple-arrays.js'], + route: '(a)/(c)/multiple-arrays', + type: 'route', + }, + { + children: [], + contextKey: './(a,b)/(c,d)/multiple-arrays.js', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(a,b)/(c,d)/multiple-arrays.js'], + route: '(a)/(d)/multiple-arrays', + type: 'route', + }, + { + children: [], + contextKey: './(a,b)/(c,d)/multiple-arrays.js', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(a,b)/(c,d)/multiple-arrays.js'], + route: '(b)/(c)/multiple-arrays', + type: 'route', + }, + { + children: [], + contextKey: './(a,b)/(c,d)/multiple-arrays.js', + dynamic: null, + entryPoints: ['expo-router/build/views/Navigator.js', './(a,b)/(c,d)/multiple-arrays.js'], + route: '(b)/(d)/multiple-arrays', + type: 'route', + }, + ], + contextKey: 'expo-router/build/views/Navigator.js', + dynamic: null, + generated: true, + route: '', + type: 'layout', + }); + }); + + it(`multiple arrays with brackets`, () => { + expect( + getRoutes( + inMemoryContext({ + './(a,b)/((c),d,(e))/multiple-arrays-with-brackets': () => null, + }), + { internal_stripLoadRoute: true, skipGenerated: true } + ) + ).toEqual({ + children: [ + { + children: [], + contextKey: './(a,b)/((c),d,(e))/multiple-arrays-with-brackets.js', + dynamic: null, + entryPoints: [ + 'expo-router/build/views/Navigator.js', + './(a,b)/((c),d,(e))/multiple-arrays-with-brackets.js', + ], + route: '(a)/((c))/multiple-arrays-with-brackets', + type: 'route', + }, + { + children: [], + contextKey: './(a,b)/((c),d,(e))/multiple-arrays-with-brackets.js', + dynamic: null, + entryPoints: [ + 'expo-router/build/views/Navigator.js', + './(a,b)/((c),d,(e))/multiple-arrays-with-brackets.js', + ], + route: '(a)/(d)/multiple-arrays-with-brackets', + type: 'route', + }, + { + children: [], + contextKey: './(a,b)/((c),d,(e))/multiple-arrays-with-brackets.js', + dynamic: null, + entryPoints: [ + 'expo-router/build/views/Navigator.js', + './(a,b)/((c),d,(e))/multiple-arrays-with-brackets.js', + ], + route: '(a)/((e))/multiple-arrays-with-brackets', + type: 'route', + }, + { + children: [], + contextKey: './(a,b)/((c),d,(e))/multiple-arrays-with-brackets.js', + dynamic: null, + entryPoints: [ + 'expo-router/build/views/Navigator.js', + './(a,b)/((c),d,(e))/multiple-arrays-with-brackets.js', + ], + route: '(b)/((c))/multiple-arrays-with-brackets', + type: 'route', + }, + { + children: [], + contextKey: './(a,b)/((c),d,(e))/multiple-arrays-with-brackets.js', + dynamic: null, + entryPoints: [ + 'expo-router/build/views/Navigator.js', + './(a,b)/((c),d,(e))/multiple-arrays-with-brackets.js', + ], + route: '(b)/(d)/multiple-arrays-with-brackets', + type: 'route', + }, + { + children: [], + contextKey: './(a,b)/((c),d,(e))/multiple-arrays-with-brackets.js', + dynamic: null, + entryPoints: [ + 'expo-router/build/views/Navigator.js', + './(a,b)/((c),d,(e))/multiple-arrays-with-brackets.js', + ], + route: '(b)/((e))/multiple-arrays-with-brackets', + type: 'route', + }, + ], + contextKey: 'expo-router/build/views/Navigator.js', + dynamic: null, + generated: true, + route: '', + type: 'layout', + }); + }); +}); diff --git a/packages/expo-router/src/__tests__/matchers.test.web.ts b/packages/expo-router/src/__tests__/matchers.test.web.ts index 6117e35ec8c88..99dbc237e2335 100644 --- a/packages/expo-router/src/__tests__/matchers.test.web.ts +++ b/packages/expo-router/src/__tests__/matchers.test.web.ts @@ -4,6 +4,7 @@ import { getNameFromFilePath, matchGroupName, stripGroupSegmentsFromPath, + matchArrayGroupName, } from '../matchers'; describe(stripGroupSegmentsFromPath, () => { @@ -36,6 +37,7 @@ describe(matchGroupName, () => { expect(matchGroupName('leading/((foobar))/trailing')).toEqual('(foobar)'); expect(matchGroupName('leading/(...foobar)/trailing')).toEqual('...foobar'); expect(matchGroupName('leading/(foo,bar)/trailing)')).toEqual('foo,bar'); + expect(matchGroupName('leading/(foo,bar)/(fruit,apple)')).toEqual('foo,bar'); }); }); describe(matchDynamicName, () => { @@ -68,3 +70,49 @@ describe(getNameFromFilePath, () => { ); }); }); + +describe(matchArrayGroupName, () => { + it(`should not match routes without groups`, () => { + expect(matchArrayGroupName('[[...foobar]]')).toEqual(undefined); + expect(matchArrayGroupName('[[foobar]]')).toEqual(undefined); + expect(matchArrayGroupName('[...foobar]')).toEqual(undefined); + expect(matchArrayGroupName('[foobar]')).toEqual(undefined); + expect(matchArrayGroupName('foobar')).toEqual(undefined); + expect(matchArrayGroupName('leading/foobar')).toEqual(undefined); + expect(matchArrayGroupName('leading/foobar/trailing')).toEqual(undefined); + }); + it(`should not match routes with a single group`, () => { + expect(matchArrayGroupName('(foobar)')).toEqual(undefined); + expect(matchArrayGroupName('((foobar))')).toEqual(undefined); + expect(matchArrayGroupName('(...foobar)')).toEqual(undefined); + expect(matchArrayGroupName('leading/(foobar)')).toEqual(undefined); + expect(matchArrayGroupName('leading/((foobar))')).toEqual(undefined); + expect(matchArrayGroupName('leading/(...foobar)')).toEqual(undefined); + expect(matchArrayGroupName('leading/(foobar)/trailing')).toEqual(undefined); + expect(matchArrayGroupName('leading/((foobar))/trailing')).toEqual(undefined); + expect(matchArrayGroupName('leading/(...foobar)/trailing')).toEqual(undefined); + expect(matchArrayGroupName('(leading)/foobar')).toEqual(undefined); + expect(matchArrayGroupName('(leading)/(foobar)')).toEqual(undefined); + expect(matchArrayGroupName('(leading)/((foobar))')).toEqual(undefined); + expect(matchArrayGroupName('(leading)/(...foobar)')).toEqual(undefined); + expect(matchArrayGroupName('(leading)/foobar/trailing')).toEqual(undefined); + expect(matchArrayGroupName('(leading)/(foobar)/trailing')).toEqual(undefined); + expect(matchArrayGroupName('(leading)/((foobar))/trailing')).toEqual(undefined); + expect(matchArrayGroupName('(leading)/(...foobar)/trailing')).toEqual(undefined); + }); + it(`should match routes with array group syntax`, () => { + expect(matchArrayGroupName('(foo,bar)')).toEqual('foo,bar'); + expect(matchArrayGroupName('leading/(foo,bar)')).toEqual('foo,bar'); + expect(matchArrayGroupName('leading/(foo,bar)/trailing)')).toEqual('foo,bar'); + expect(matchArrayGroupName('leading/((foo),(bar))/trailing)')).toEqual('(foo),(bar)'); + expect(matchArrayGroupName('leading/(foo,bar)/(fruit,apple)')).toEqual('foo,bar'); + expect(matchArrayGroupName('(leading)/(foo,bar)')).toEqual('foo,bar'); + expect(matchArrayGroupName('(leading)/(foo,bar)/trailing)')).toEqual('foo,bar'); + expect(matchArrayGroupName('(leading)/((foo),(bar))/trailing)')).toEqual('(foo),(bar)'); + }); + it(`should only match the first group with array group syntax`, () => { + expect(matchArrayGroupName('(leading)/(foo,bar)/(fruit,apple)')).toEqual('foo,bar'); + expect(matchArrayGroupName('(leading)/((foo),bar)/(fruit,apple)')).toEqual('(foo),bar'); + expect(matchArrayGroupName('(leading)/(foo,bar)/((fruit),apple)')).toEqual('foo,bar'); + }); +}); diff --git a/packages/expo-router/src/getRoutes.ts b/packages/expo-router/src/getRoutes.ts index 1bf4d385f5169..f346fc60e3510 100644 --- a/packages/expo-router/src/getRoutes.ts +++ b/packages/expo-router/src/getRoutes.ts @@ -370,7 +370,7 @@ export function getIgnoreList(options?: Options) { * /(a,b)/(c,d)/e.tsx => new Set(['a/c/e.tsx', 'a/d/e.tsx', 'b/c/e.tsx', 'b/d/e.tsx']) */ function extrapolateGroups(key: string, keys: Set = new Set()): Set { - const match = matchArrayGroupName(key)?.[0]; + const match = matchArrayGroupName(key); if (!match) { keys.add(key); diff --git a/packages/expo-router/src/matchers.tsx b/packages/expo-router/src/matchers.tsx index 3dfef422ecf8c..5f04c9346d8cf 100644 --- a/packages/expo-router/src/matchers.tsx +++ b/packages/expo-router/src/matchers.tsx @@ -20,9 +20,9 @@ export function matchGroupName(name: string): string | undefined { return name.match(/^(?:[^\\(\\)])*?\(([^\\/]+)\).*?$/)?.[1]; } -/** Match `(a,b,c)/(d,c)` -> `[['a','b','c'], ['d','e']]` */ +/** Match the first array group name `(a,b,c)/(d,c)` -> `'a,b,c'` */ export function matchArrayGroupName(name: string) { - return name.match(/\(\s*\w[\w\s]*?,.*?\)/g)?.map((match) => match.slice(1, -1)); + return name.match(/(?:[^\\(\\)])*?\(([^\\/]+,[^\\/]+)\).*?$/)?.[1]; } export function getNameFromFilePath(name: string): string {