1 |
|
2 | import Ast, { SourceFile, ts } from 'ts-simple-ast';
|
3 |
|
4 | import appRootDir from 'app-root-dir';
|
5 | import * as fs from 'fs-extra';
|
6 | import * as path from 'path';
|
7 |
|
8 | const findRoot = async (
|
9 | dir: string | string[],
|
10 | filename: string,
|
11 | ): Promise<string> => {
|
12 | let start = dir;
|
13 |
|
14 | if (typeof start === 'string') {
|
15 | if (start[start.length - 1] !== path.sep) {
|
16 | start += path.sep;
|
17 | }
|
18 | start = start.split(path.sep);
|
19 | }
|
20 | if (!start.length) {
|
21 | throw new Error('tsconfig.json not found in path');
|
22 | }
|
23 |
|
24 | start.pop();
|
25 | const currentDir = start.join(path.sep);
|
26 | const file = path.join(currentDir, filename);
|
27 | const exists = await fs.pathExists(file);
|
28 | if (exists) {
|
29 | return file;
|
30 | }
|
31 |
|
32 | return findRoot(start, filename);
|
33 | };
|
34 |
|
35 | export const makeAst = async (dir: string): Promise<Ast> => {
|
36 | const tsConfigFilePath = await findRoot(dir, 'tsconfig.json');
|
37 | const res = ts.readConfigFile(tsConfigFilePath, (value) =>
|
38 | fs.readFileSync(value, 'utf8'),
|
39 | );
|
40 | const parseConfigHost = {
|
41 | fileExists: fs.existsSync,
|
42 | readDirectory: ts.sys.readDirectory,
|
43 | readFile: ts.sys.readFile,
|
44 | useCaseSensitiveFileNames: true,
|
45 | };
|
46 | const parsed = ts.parseJsonConfigFileContent(
|
47 | res.config,
|
48 | parseConfigHost,
|
49 | path.dirname(tsConfigFilePath),
|
50 | );
|
51 |
|
52 | return new Ast({ compilerOptions: parsed.options });
|
53 | };
|
54 |
|
55 | export const getAst = async (dir: string): Promise<Ast> => {
|
56 | const ast = await makeAst(dir);
|
57 | ast.addExistingSourceFiles(path.join(dir, '**', '*.ts'));
|
58 |
|
59 | ast.getPreEmitDiagnostics();
|
60 | return ast;
|
61 | };
|
62 |
|
63 | export const getAstForSnippet = async (
|
64 | code: string,
|
65 | ): Promise<{ ast: Ast; sourceFile: SourceFile }> => {
|
66 | const ast = await makeAst(appRootDir.get());
|
67 | const sourceFile = ast.createSourceFile('code.ts', code);
|
68 |
|
69 | ast.getPreEmitDiagnostics();
|
70 | return { ast, sourceFile };
|
71 | };
|
72 |
|
73 | export function notNull<TValue>(
|
74 | value: TValue | null | undefined,
|
75 | ): value is TValue {
|
76 | return value != null;
|
77 | }
|