• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16/*
17  AST Printer.
18
19  how to use:
20    npm run ast -- file1 file2 ...
21
22  Because of large output size, it is better to write to file:
23    npm run ast -- file1 file2 ... >out.txt
24
25  API:
26    AST.Printer.tabSize        - indent of inner structs
27    AST.Printer.SyntaxKind     - Array of Arrays of strings with ts.SyntaxKind names
28    AST.Printer.File2String    - converts ts.SourceFile to string
29    AST.Printer.Convert2String - compiles files with options and converts them to string
30*/
31
32import * as ts from "typescript";
33import * as path from "path";
34
35export namespace AST {
36    export namespace Printer {
37        export let tabSize = 2;
38        export let SyntaxKind = Fill();
39
40        function Fill(): Array<Array<string>> {
41            let retVal: Array<Array<string>> = [];
42
43            for (const [key, value] of Object.entries(ts.SyntaxKind)) {
44                // ex.: '330': SyntheticReferenceExpression
45                if (typeof value === "string") {
46                    let index: number = +key;
47                    if (!retVal[index]) {
48                        retVal[index] = [];
49                    }
50
51                    if (!retVal[index].includes(value)) {
52                        retVal[index].push(value);
53                    }
54                }
55                // ex.: AmpersandAmpersandToken: 55
56                if (typeof value === "number") {
57                    let index: number = +value;
58                    if (!retVal[index]) {
59                        retVal[index] = [];
60                    }
61
62                    if (!retVal[index].includes(key)) {
63                        retVal[index].push(key);
64                    }
65                }
66            }
67
68            return retVal;
69        }
70
71        function isArray(node: any): node is ts.Node[] {
72            return ((typeof (node) === "object") && (node instanceof Array));
73        }
74
75        function isNode(node: any): node is ts.Node {
76            return ((typeof (node) === "object") && ("kind" in node));
77        }
78
79        function isFile(node: any): node is ts.SourceFile {
80            return ((typeof (node) === "object") && ("kind" in node) && (node.kind === ts.SyntaxKind.SourceFile));
81        }
82
83        function array2String(name: (undefined | string), array: ts.Node[], tab: number): string {
84            const repeat = ' '.repeat(tab);
85            let retVal = "";
86            let subStr = "";
87
88            if (name) {
89                retVal += repeat + name + ':' + '\n';
90            }
91
92            for (const [key, value] of Object.entries(array)) {
93                subStr += node2String(undefined, value, tab + tabSize);
94            }
95
96            if (subStr) {
97                retVal += repeat + '[' + '\n';
98                retVal += subStr;
99                retVal += repeat + ']' + '\n';
100            } else {
101                retVal += repeat + '[' + ']' + '\n';
102            }
103
104            return retVal;
105        }
106
107        function node2String(name: (undefined | string), node: ts.Node, tab: number): string {
108            if (!isNode(node)) {
109                return "";
110            }
111
112            const repeat: string = ' '.repeat(tab);
113            let retVal: string = "";
114            let subStr: string = "";
115            const kind: number = +node.kind;
116            const kindNames: Array<string> = SyntaxKind[node.kind];
117
118            if (name) {
119                retVal += repeat + name + ':' + '\n';
120            }
121
122            subStr = kindNames.join('/');
123            retVal += repeat + subStr + ',' + ' ' + kind + '\n';
124            subStr = "";
125
126            for (const [key, value] of Object.entries(node)) {
127                if (isArray(value)) {
128                    subStr += array2String(key, value, tab + tabSize);
129                }
130
131                if (isNode(value)) {
132                    subStr += node2String(key, value, tab + tabSize);
133                }
134            }
135
136            if (subStr) {
137                retVal += repeat + '{' + '\n';
138                retVal += subStr;
139                retVal += repeat + '}' + '\n';
140            } else {
141                retVal += repeat + '{' + '}' + '\n';
142            }
143
144            return retVal;
145        }
146
147        export function File2String(name: (undefined | string), file: ts.SourceFile, tab: number): string {
148            const repeat = ' '.repeat(tab);
149            let retVal = "";
150            let subStr = "";
151
152            if (name) {
153                retVal += repeat + "File " + name + ':' + '\n';
154            }
155
156            for (const value of file.statements) {
157                if (isNode(value)) {
158                    subStr += node2String(undefined, value, tab + tabSize);
159                }
160            }
161
162            if (subStr) {
163                retVal += repeat + '[' + '\n';
164                retVal += subStr;
165                retVal += repeat + ']' + '\n';
166            } else {
167                retVal += repeat + '[' + ']' + '\n';
168            }
169
170            return retVal;
171        }
172
173        export function Convert2String(fileNames: string[], options: ts.CompilerOptions): string {
174            const program = ts.createProgram(fileNames, options);
175            const files = program.getSourceFiles();
176            let retVal = "";
177
178            for (const file of files) {
179                if (fileNames.includes(file.fileName)) {
180                    retVal += File2String(file.fileName, file, 0);
181                    retVal += '\n';
182                }
183            }
184
185            return retVal;
186        }
187    }
188}
189
190function run(args: string[], useDefault?: boolean): void {
191    let defaultOptions = {
192        outDir: "../build",
193        allowJs: true,
194        noEmitOnError: true,
195        noImplicitAny: true,
196        target: ts.ScriptTarget.ES5,
197        module: ts.ModuleKind.CommonJS,
198        strictNullChecks: true
199    };
200
201    let parsed = ts.parseCommandLine(args);
202
203    parsed.fileNames = parsed.fileNames.map(function(file) {
204        return path.resolve(file);
205    });
206
207    if (useDefault === true) {
208        parsed.options = Object.assign(defaultOptions, parsed.options);
209    }
210
211    console.log(AST.Printer.Convert2String(parsed.fileNames, parsed.options));
212}
213
214run(process.argv.slice(2), true);
215