UNPKG

12.7 kBPlain TextView Raw
1import * as ts from 'typescript';
2
3/**
4 * General utility class.
5 */
6export 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 /* tslint:disable:no-console */
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 /* tslint:enable:no-console */
109 }
110
111 export function isPrivate(node: ts.Node): boolean {
112 /* tslint:disable:no-bitwise */
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 /* tslint:enable:no-bitwise */
119 }
120
121 export function isProtected(node: ts.Node): boolean {
122 /* tslint:disable:no-bitwise */
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 /* tslint:enable:no-bitwise */
129 }
130
131 export function isPublic(node: ts.Node): boolean {
132 /* tslint:disable:no-bitwise */
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 /* tslint:enable:no-bitwise */
139 }
140
141 export function isStatic(node: ts.Node): boolean {
142 /* tslint:disable:no-bitwise */
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 /* tslint:enable:no-bitwise */
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 /* tslint:disable:no-bitwise */
173 flags |= node.flags;
174 /* tslint:enable:no-bitwise */
175 node = node.parent;
176 }
177
178 if (node && node.kind === ts.SyntaxKind.VariableStatement) {
179 /* tslint:disable:no-bitwise */
180 flags |= node.flags;
181 /* tslint:enable:no-bitwise */
182 }
183
184 return flags;
185 }
186
187 export function isLet(node: ts.Node): boolean {
188 /* tslint:disable:no-bitwise */
189 return !!(getCombinedNodeFlags(node) & ts.NodeFlags.Let);
190 /* tslint:enable:no-bitwise */
191 }
192
193 export function isExported(node: ts.Node): boolean {
194 /* tslint:disable:no-bitwise */
195 if ((<any>ts).NodeFlags.Export != null) {
196 return !!(getCombinedNodeFlags(node) & (<any>ts).NodeFlags.Export);
197 } else {
198 // typescript 2.1.4 introduces a new edge case for when
199 // top level variables are exported from a source file
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 /* tslint:enable:no-bitwise */
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}