1 | import * as ts from 'typescript';
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | export namespace AstUtils {
|
7 | export function getLanguageVariant(node: ts.SourceFile): ts.LanguageVariant {
|
8 | if (/.*\.tsx/i.test(node.fileName)) {
|
9 | return ts.LanguageVariant.JSX;
|
10 | } else {
|
11 | return ts.LanguageVariant.Standard;
|
12 | }
|
13 | }
|
14 |
|
15 | export function getFunctionName(node: ts.CallExpression | ts.NewExpression): string {
|
16 | const expression: ts.Expression = node.expression;
|
17 | let functionName: string = (<any>expression).text;
|
18 | if (functionName === undefined && (<any>expression).name) {
|
19 | functionName = (<any>expression).name.text;
|
20 | }
|
21 | return functionName;
|
22 | }
|
23 |
|
24 | export function getFunctionTarget(expression: ts.CallExpression): string {
|
25 | if (expression.expression.kind === ts.SyntaxKind.PropertyAccessExpression) {
|
26 | const propExp: ts.PropertyAccessExpression = <ts.PropertyAccessExpression>expression.expression;
|
27 | return propExp.expression.getText();
|
28 | }
|
29 | return null;
|
30 | }
|
31 |
|
32 | export function isJQuery(functionTarget: string): boolean {
|
33 | return functionTarget === '$' || /^(jquery)$/i.test(functionTarget);
|
34 | }
|
35 |
|
36 | export function hasModifier(modifiers: ts.ModifiersArray, modifierKind: number): boolean {
|
37 | if (modifiers == null) {
|
38 | return false;
|
39 | }
|
40 | let result: boolean = false;
|
41 | modifiers.forEach(
|
42 | (modifier: ts.Node): void => {
|
43 | if (modifier.kind === modifierKind) {
|
44 | result = true;
|
45 | }
|
46 | }
|
47 | );
|
48 | return result;
|
49 | }
|
50 |
|
51 | export function dumpTypeInfo(expression: ts.Expression, languageServices: ts.LanguageService, typeChecker: ts.TypeChecker): void {
|
52 |
|
53 | console.log(expression.getFullText());
|
54 | console.log('\tkind: ' + expression.kind);
|
55 |
|
56 | if (expression.kind === ts.SyntaxKind.Identifier || expression.kind === ts.SyntaxKind.PropertyAccessExpression) {
|
57 | const definitionInfo: ts.DefinitionInfo[] = languageServices.getDefinitionAtPosition('file.ts', expression.getStart());
|
58 | if (definitionInfo) {
|
59 | definitionInfo.forEach(
|
60 | (info: ts.DefinitionInfo, index: number): void => {
|
61 | console.log('\tdefinitionInfo-' + index);
|
62 | console.log('\t\tkind: ' + info.kind);
|
63 | console.log('\t\tname: ' + info.name);
|
64 | }
|
65 | );
|
66 | }
|
67 |
|
68 | const typeInfo: ts.DefinitionInfo[] = languageServices.getTypeDefinitionAtPosition('file.ts', expression.getStart());
|
69 | if (typeInfo) {
|
70 | typeInfo.forEach(
|
71 | (info: ts.DefinitionInfo, index: number): void => {
|
72 | console.log('\ttypeDefinitionInfo-' + index);
|
73 | console.log('\t\tkind: ' + info.kind);
|
74 | console.log('\t\tname: ' + info.name);
|
75 | }
|
76 | );
|
77 | }
|
78 |
|
79 | const quickInfo: ts.QuickInfo = languageServices.getQuickInfoAtPosition('file.ts', expression.getStart());
|
80 | console.log('\tquickInfo.kind = ' + quickInfo.kind);
|
81 | console.log('\tquickInfo.kindModifiers= ' + quickInfo.kindModifiers);
|
82 | console.log('\tquickInfo.textSpan = ' + quickInfo.textSpan.start);
|
83 | console.log('\tquickInfo.displayParts = ' + quickInfo.displayParts[0].text);
|
84 | console.log('\tquickInfo.displayParts = ' + quickInfo.displayParts[0].kind);
|
85 |
|
86 | const expressionType: ts.Type = typeChecker.getTypeAtLocation(expression);
|
87 | console.log('\ttypeChecker.typeToString : ' + typeChecker.typeToString(expressionType));
|
88 | console.log('\ttype.flags: ' + expressionType.flags);
|
89 | console.log('\ttype.symbol: ' + expressionType.symbol);
|
90 |
|
91 | const expressionSymbol: ts.Symbol = typeChecker.getSymbolAtLocation(expression);
|
92 | if (expressionSymbol == null) {
|
93 | console.log('\tsymbol: ' + expressionSymbol);
|
94 | } else {
|
95 | console.log('\tsymbol.flags: ' + expressionSymbol.flags);
|
96 | console.log('\tsymbol.name: ' + expressionSymbol.name);
|
97 | console.log('\tsymbol.declarations: ' + expressionSymbol.declarations);
|
98 | }
|
99 |
|
100 | const contextualType: ts.Type = typeChecker.getContextualType(expression);
|
101 | if (contextualType == null) {
|
102 | console.log('\tcontextualType: ' + contextualType);
|
103 | } else {
|
104 | console.log('\tcontextualType.flags: ' + contextualType.flags);
|
105 | console.log('\tcontextualType.symbol: ' + contextualType.symbol);
|
106 | }
|
107 | }
|
108 |
|
109 | }
|
110 |
|
111 | export function isPrivate(node: ts.Node): boolean {
|
112 |
|
113 | if ((<any>ts).NodeFlags.Private != null) {
|
114 | return !!(node.flags & (<any>ts).NodeFlags.Private);
|
115 | } else {
|
116 | return !!((<any>ts).getCombinedModifierFlags(node) & (<any>ts).ModifierFlags.Private);
|
117 | }
|
118 |
|
119 | }
|
120 |
|
121 | export function isProtected(node: ts.Node): boolean {
|
122 |
|
123 | if ((<any>ts).NodeFlags.Protected != null) {
|
124 | return !!(node.flags & (<any>ts).NodeFlags.Protected);
|
125 | } else {
|
126 | return !!((<any>ts).getCombinedModifierFlags(node) & (<any>ts).ModifierFlags.Protected);
|
127 | }
|
128 |
|
129 | }
|
130 |
|
131 | export function isPublic(node: ts.Node): boolean {
|
132 |
|
133 | if ((<any>ts).NodeFlags.Public != null) {
|
134 | return !!(node.flags & (<any>ts).NodeFlags.Public);
|
135 | } else {
|
136 | return !!((<any>ts).getCombinedModifierFlags(node) & (<any>ts).ModifierFlags.Public);
|
137 | }
|
138 |
|
139 | }
|
140 |
|
141 | export function isStatic(node: ts.Node): boolean {
|
142 |
|
143 | if ((<any>ts).NodeFlags.Static != null) {
|
144 | return !!(node.flags & (<any>ts).NodeFlags.Static);
|
145 | } else {
|
146 | return !!((<any>ts).getCombinedModifierFlags(node) & (<any>ts).ModifierFlags.Static);
|
147 | }
|
148 |
|
149 | }
|
150 |
|
151 | function isBindingPattern(node: ts.Node): node is ts.BindingPattern {
|
152 | return node != null && (node.kind === ts.SyntaxKind.ArrayBindingPattern || node.kind === ts.SyntaxKind.ObjectBindingPattern);
|
153 | }
|
154 |
|
155 | function walkUpBindingElementsAndPatterns(node: ts.Node): ts.Node {
|
156 | while (node && (node.kind === ts.SyntaxKind.BindingElement || isBindingPattern(node))) {
|
157 | node = node.parent;
|
158 | }
|
159 |
|
160 | return node;
|
161 | }
|
162 |
|
163 | function getCombinedNodeFlags(node: ts.Node): ts.NodeFlags {
|
164 | node = walkUpBindingElementsAndPatterns(node);
|
165 |
|
166 | let flags = node.flags;
|
167 | if (node.kind === ts.SyntaxKind.VariableDeclaration) {
|
168 | node = node.parent;
|
169 | }
|
170 |
|
171 | if (node && node.kind === ts.SyntaxKind.VariableDeclarationList) {
|
172 |
|
173 | flags |= node.flags;
|
174 |
|
175 | node = node.parent;
|
176 | }
|
177 |
|
178 | if (node && node.kind === ts.SyntaxKind.VariableStatement) {
|
179 |
|
180 | flags |= node.flags;
|
181 |
|
182 | }
|
183 |
|
184 | return flags;
|
185 | }
|
186 |
|
187 | export function isLet(node: ts.Node): boolean {
|
188 |
|
189 | return !!(getCombinedNodeFlags(node) & ts.NodeFlags.Let);
|
190 |
|
191 | }
|
192 |
|
193 | export function isExported(node: ts.Node): boolean {
|
194 |
|
195 | if ((<any>ts).NodeFlags.Export != null) {
|
196 | return !!(getCombinedNodeFlags(node) & (<any>ts).NodeFlags.Export);
|
197 | } else {
|
198 |
|
199 |
|
200 | if (
|
201 | node.kind === ts.SyntaxKind.VariableDeclaration &&
|
202 | node.parent.kind === ts.SyntaxKind.VariableDeclarationList &&
|
203 | node.parent.parent.kind === ts.SyntaxKind.VariableStatement
|
204 | ) {
|
205 | if (AstUtils.hasModifier(node.parent.parent.modifiers, ts.SyntaxKind.ExportKeyword)) {
|
206 | return true;
|
207 | }
|
208 | }
|
209 | return !!(getCombinedNodeFlags(node) & ts.NodeFlags.ExportContext);
|
210 | }
|
211 |
|
212 | }
|
213 |
|
214 | export function isAssignmentOperator(token: ts.SyntaxKind): boolean {
|
215 | return token >= ts.SyntaxKind.FirstAssignment && token <= ts.SyntaxKind.LastAssignment;
|
216 | }
|
217 |
|
218 | export function isBindingLiteralExpression(node: ts.Node): node is ts.ArrayLiteralExpression | ts.ObjectLiteralExpression {
|
219 | return !!node && (node.kind === ts.SyntaxKind.ObjectLiteralExpression || node.kind === ts.SyntaxKind.ArrayLiteralExpression);
|
220 | }
|
221 |
|
222 | export function findParentBlock(child: ts.Node): ts.Node {
|
223 | let parent: ts.Node = child.parent;
|
224 | while (parent != null) {
|
225 | if (parent.kind === ts.SyntaxKind.Block) {
|
226 | return parent;
|
227 | }
|
228 | parent = parent.parent;
|
229 | }
|
230 | throw new Error('Could not determine parent block of node: ' + child);
|
231 | }
|
232 |
|
233 | export function isSameIdentifer(source: ts.Node, target: ts.Node): boolean {
|
234 | if (source == null || target == null) {
|
235 | return false;
|
236 | }
|
237 | if (source.kind === ts.SyntaxKind.Identifier && target.kind === ts.SyntaxKind.Identifier) {
|
238 | return source.getText() === target.getText();
|
239 | }
|
240 | return false;
|
241 | }
|
242 |
|
243 | export function getDeclaredMethodNames(node: ts.ClassDeclaration): string[] {
|
244 | const result: string[] = [];
|
245 | node.members.forEach(
|
246 | (classElement: ts.ClassElement): void => {
|
247 | if (classElement.kind === ts.SyntaxKind.MethodDeclaration) {
|
248 | const methodDeclaration: ts.MethodDeclaration = <ts.MethodDeclaration>classElement;
|
249 | if (methodDeclaration.name.kind === ts.SyntaxKind.Identifier) {
|
250 | result.push((<ts.Identifier>methodDeclaration.name).text);
|
251 | }
|
252 | }
|
253 | }
|
254 | );
|
255 | return result;
|
256 | }
|
257 |
|
258 | export function isDeclarationFunctionType(node: ts.PropertyDeclaration | ts.VariableDeclaration | ts.ParameterDeclaration): boolean {
|
259 | if (node.type != null) {
|
260 | if (node.type.getText() === 'Function') {
|
261 | return true;
|
262 | }
|
263 | return node.type.kind === ts.SyntaxKind.FunctionType;
|
264 | } else if (node.initializer != null) {
|
265 | return node.initializer.kind === ts.SyntaxKind.ArrowFunction || node.initializer.kind === ts.SyntaxKind.FunctionExpression;
|
266 | }
|
267 | return false;
|
268 | }
|
269 |
|
270 | export function isUndefined(node: ts.Expression): boolean {
|
271 | if (node != null) {
|
272 | if (node.kind === ts.SyntaxKind.Identifier) {
|
273 | return node.getText() === 'undefined';
|
274 | }
|
275 | }
|
276 | return false;
|
277 | }
|
278 |
|
279 | export function isConstant(node: ts.Expression): boolean {
|
280 | if (node == null) {
|
281 | return false;
|
282 | }
|
283 | return (
|
284 | node.kind === ts.SyntaxKind.NullKeyword ||
|
285 | node.kind === ts.SyntaxKind.StringLiteral ||
|
286 | node.kind === ts.SyntaxKind.FalseKeyword ||
|
287 | node.kind === ts.SyntaxKind.TrueKeyword ||
|
288 | node.kind === ts.SyntaxKind.NumericLiteral
|
289 | );
|
290 | }
|
291 |
|
292 | export function isConstantExpression(node: ts.Expression): boolean {
|
293 | if (node.kind === ts.SyntaxKind.BinaryExpression) {
|
294 | const expression: ts.BinaryExpression = <ts.BinaryExpression>node;
|
295 | const kind: ts.SyntaxKind = expression.operatorToken.kind;
|
296 | if (kind >= ts.SyntaxKind.FirstBinaryOperator && kind <= ts.SyntaxKind.LastBinaryOperator) {
|
297 | return isConstantExpression(expression.left) && isConstantExpression(expression.right);
|
298 | }
|
299 | }
|
300 | if (node.kind === ts.SyntaxKind.PrefixUnaryExpression || node.kind === ts.SyntaxKind.PostfixUnaryExpression) {
|
301 | const expression: ts.PostfixUnaryExpression | ts.PrefixUnaryExpression = <ts.PostfixUnaryExpression | ts.PrefixUnaryExpression>(
|
302 | node
|
303 | );
|
304 | return isConstantExpression(expression.operand);
|
305 | }
|
306 | return isConstant(node);
|
307 | }
|
308 | }
|