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