1import path from 'path';
2import resolveFrom from 'resolve-from';
3
4export function evaluateTsConfig(ts: typeof import('typescript'), tsConfigPath: string) {
5  const formatDiagnosticsHost: import('typescript').FormatDiagnosticsHost = {
6    getNewLine: () => require('os').EOL,
7    getCurrentDirectory: ts.sys.getCurrentDirectory,
8    getCanonicalFileName: (fileName: string) => fileName,
9  };
10
11  try {
12    const { config, error } = ts.readConfigFile(tsConfigPath, ts.sys.readFile);
13
14    if (error) {
15      throw new Error(ts.formatDiagnostic(error, formatDiagnosticsHost));
16    }
17
18    const jsonFileContents = ts.parseJsonConfigFileContent(
19      config,
20      {
21        ...ts.sys,
22        readDirectory: (_, ext) => [ext ? `file${ext[0]}` : `file.ts`],
23      },
24      path.dirname(tsConfigPath)
25    );
26
27    if (jsonFileContents.errors) {
28      // filter out "no inputs were found in config file" error
29      jsonFileContents.errors = jsonFileContents.errors.filter(({ code }) => code !== 18003);
30    }
31
32    if (jsonFileContents.errors?.length) {
33      throw new Error(ts.formatDiagnostic(jsonFileContents.errors[0], formatDiagnosticsHost));
34    }
35
36    return { compilerOptions: jsonFileContents.options, raw: config.raw };
37  } catch (error: any) {
38    if (error?.name === 'SyntaxError') {
39      throw new Error('tsconfig.json is invalid:\n' + (error.message ?? ''));
40    }
41    throw error;
42  }
43}
44
45export function importTypeScriptFromProjectOptionally(
46  projectRoot: string
47): typeof import('typescript') | null {
48  const resolvedPath = resolveFrom.silent(projectRoot, 'typescript');
49  if (!resolvedPath) {
50    return null;
51  }
52  return require(resolvedPath);
53}
54