1import { getConfig } from '@expo/config';
2import fs from 'fs/promises';
3import { Server } from 'metro';
4import path from 'path';
5
6import { removeExpoEnvDTS, writeExpoEnvDTS } from './expo-env';
7import { setupTypedRoutes } from './routes';
8import { forceRemovalTSConfig, forceUpdateTSConfig } from './tsconfig';
9import { removeFromGitIgnore, upsertGitIgnoreContents } from '../../../utils/mergeGitIgnorePaths';
10import { ensureDotExpoProjectDirectoryInitialized } from '../../project/dotExpo';
11import { ServerLike } from '../BundlerDevServer';
12import { getRouterDirectoryWithManifest } from '../metro/router';
13
14export interface TypeScriptTypeGenerationOptions {
15  server?: ServerLike;
16  metro?: Server | null;
17  projectRoot: string;
18}
19
20const debug = require('debug')('expo:typed-routes') as typeof console.log;
21
22/** Setup all requisite features for statically typed routes in Expo Router v2 / SDK +49. */
23export async function startTypescriptTypeGenerationAsync({
24  metro,
25  projectRoot,
26  server,
27}: TypeScriptTypeGenerationOptions) {
28  const { exp } = getConfig(projectRoot);
29
30  // If typed routes are disabled, remove any files that were added.
31  if (!exp.experiments?.typedRoutes) {
32    debug('Removing typed routes side-effects (experiments.typedRoutes: false)');
33    const gitIgnorePath = path.join(projectRoot, '.gitignore');
34    await Promise.all([
35      forceRemovalTSConfig(projectRoot),
36      removeExpoEnvDTS(projectRoot),
37      removeFromGitIgnore(gitIgnorePath, 'expo-env.d.ts'),
38    ]);
39  } else {
40    const dotExpoDir = ensureDotExpoProjectDirectoryInitialized(projectRoot);
41    const typesDirectory = path.resolve(dotExpoDir, './types');
42    debug(
43      'Ensuring typed routes side-effects are setup (experiments.typedRoutes: true, typesDirectory: %s)',
44      typesDirectory
45    );
46
47    // Ensure the types directory exists.
48    await fs.mkdir(typesDirectory, { recursive: true });
49
50    await Promise.all([
51      upsertGitIgnoreContents(path.join(projectRoot, '.gitignore'), 'expo-env.d.ts'),
52      writeExpoEnvDTS(projectRoot),
53      forceUpdateTSConfig(projectRoot),
54      setupTypedRoutes({
55        metro,
56        server,
57        typesDirectory,
58        projectRoot,
59        routerDirectory: getRouterDirectoryWithManifest(projectRoot, exp),
60      }),
61    ]);
62  }
63}
64