1d8009c4bSEvan Baconimport JsonFile from '@expo/json-file';
2d8009c4bSEvan Baconimport path from 'path';
3d8009c4bSEvan Bacon
4d8009c4bSEvan Baconimport { evaluateTsConfig, importTypeScriptFromProjectOptionally } from './evaluateTsConfig';
5*8a424bebSJames Ideimport { fileExistsAsync } from '../dir';
6d8009c4bSEvan Bacon
7d8009c4bSEvan Baconexport type TsConfigPaths = {
8d8009c4bSEvan Bacon  paths?: Record<string, string[]>;
9d8009c4bSEvan Bacon  baseUrl: string;
10d8009c4bSEvan Bacon};
11d8009c4bSEvan Bacon
12d8009c4bSEvan Bacontype ConfigReadResults = [
13d8009c4bSEvan Bacon  string,
14d8009c4bSEvan Bacon  {
15d8009c4bSEvan Bacon    compilerOptions?: {
16d8009c4bSEvan Bacon      baseUrl?: string;
17d8009c4bSEvan Bacon      paths?: Record<string, string[]>;
18d8009c4bSEvan Bacon    };
19*8a424bebSJames Ide  },
20d8009c4bSEvan Bacon];
21d8009c4bSEvan Bacon
22d8009c4bSEvan Baconconst debug = require('debug')('expo:utils:tsconfig:load') as typeof console.log;
23d8009c4bSEvan Bacon
24d8009c4bSEvan Baconexport async function loadTsConfigPathsAsync(dir: string): Promise<TsConfigPaths | null> {
25d8009c4bSEvan Bacon  const options = (await readTsconfigAsync(dir)) ?? (await readJsconfigAsync(dir));
26d8009c4bSEvan Bacon  if (options) {
27d8009c4bSEvan Bacon    const [filepath, config] = options;
28d8009c4bSEvan Bacon    if (config.compilerOptions?.baseUrl) {
29d8009c4bSEvan Bacon      return {
30d8009c4bSEvan Bacon        paths: config.compilerOptions?.paths,
31d8009c4bSEvan Bacon        baseUrl: path.resolve(dir, config.compilerOptions.baseUrl),
32d8009c4bSEvan Bacon      };
33d8009c4bSEvan Bacon    }
34d8009c4bSEvan Bacon    debug(`No baseUrl found in ${filepath}`);
35d8009c4bSEvan Bacon    return {
36d8009c4bSEvan Bacon      paths: config.compilerOptions?.paths,
37d8009c4bSEvan Bacon      baseUrl: dir,
38d8009c4bSEvan Bacon    };
39d8009c4bSEvan Bacon  }
40d8009c4bSEvan Bacon  return null;
41d8009c4bSEvan Bacon}
42d8009c4bSEvan Bacon
43d8009c4bSEvan Baconasync function readJsconfigAsync(projectRoot: string): Promise<null | ConfigReadResults> {
44d8009c4bSEvan Bacon  const configPath = path.join(projectRoot, 'jsconfig.json');
45d8009c4bSEvan Bacon  if (await fileExistsAsync(configPath)) {
46d8009c4bSEvan Bacon    const config = await JsonFile.readAsync(configPath, { json5: true });
47d8009c4bSEvan Bacon    if (config) {
48d8009c4bSEvan Bacon      return [configPath, config];
49d8009c4bSEvan Bacon    }
50d8009c4bSEvan Bacon  }
51d8009c4bSEvan Bacon  return null;
52d8009c4bSEvan Bacon}
53d8009c4bSEvan Bacon
54d8009c4bSEvan Bacon// TODO: Refactor for speed
551117330aSMark Lawlorexport async function readTsconfigAsync(projectRoot: string): Promise<null | ConfigReadResults> {
56d8009c4bSEvan Bacon  const configPath = path.join(projectRoot, 'tsconfig.json');
57d8009c4bSEvan Bacon  if (await fileExistsAsync(configPath)) {
58d8009c4bSEvan Bacon    // We need to fully evaluate the tsconfig to get the baseUrl and paths in case they were applied in `extends`.
59d8009c4bSEvan Bacon    const ts = importTypeScriptFromProjectOptionally(projectRoot);
60d8009c4bSEvan Bacon    if (ts) {
61d8009c4bSEvan Bacon      return [configPath, evaluateTsConfig(ts, configPath)];
62d8009c4bSEvan Bacon    }
63d8009c4bSEvan Bacon    debug(`typescript module not found in: ${projectRoot}`);
64d8009c4bSEvan Bacon  }
65d8009c4bSEvan Bacon  return null;
66d8009c4bSEvan Bacon}
67