1import chalk from 'chalk'; 2import path from 'path'; 3import resolveFrom from 'resolve-from'; 4 5import { Log } from '../../../log'; 6import { directoryExistsSync } from '../../../utils/dir'; 7 8const debug = require('debug')('expo:start:server:metro:router') as typeof console.log; 9 10/** 11 * Get the relative path for requiring the `/app` folder relative to the `expo-router/entry` file. 12 * This mechanism does require the server to restart after the `expo-router` package is installed. 13 */ 14export function getAppRouterRelativeEntryPath( 15 projectRoot: string, 16 routerDirectory: string = getRouterDirectory(projectRoot) 17): string | undefined { 18 // Auto pick App entry 19 const routerEntry = 20 resolveFrom.silent(projectRoot, 'expo-router/entry') ?? getFallbackEntryRoot(projectRoot); 21 if (!routerEntry) { 22 return undefined; 23 } 24 // It doesn't matter if the app folder exists. 25 const appFolder = path.join(projectRoot, routerDirectory); 26 const appRoot = path.relative(path.dirname(routerEntry), appFolder); 27 debug('routerEntry', routerEntry, appFolder, appRoot); 28 return appRoot; 29} 30 31/** If the `expo-router` package is not installed, then use the `expo` package to determine where the node modules are relative to the project. */ 32function getFallbackEntryRoot(projectRoot: string): string { 33 const expoRoot = resolveFrom.silent(projectRoot, 'expo/package.json'); 34 if (expoRoot) { 35 return path.join(path.dirname(path.dirname(expoRoot)), 'expo-router/entry'); 36 } 37 return path.join(projectRoot, 'node_modules/expo-router/entry'); 38} 39 40export function getRouterDirectory(projectRoot: string): string { 41 // more specific directories first 42 if (directoryExistsSync(path.join(projectRoot, 'src/app'))) { 43 Log.log(chalk.gray('Using src/app as the root directory for Expo Router.')); 44 return 'src/app'; 45 } 46 47 Log.debug('Using app as the root directory for Expo Router.'); 48 return 'app'; 49} 50