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