UNPKG

24 kBJavaScriptView Raw
1/**
2 * @fileoverview Utilities for finding and converting TSNodes into ESTreeNodes
3 * @author James Henry <https://github.com/JamesHenry>
4 * @copyright jQuery Foundation and other contributors, https://jquery.org/
5 * MIT License
6 */
7
8"use strict";
9
10//------------------------------------------------------------------------------
11// Requirements
12//------------------------------------------------------------------------------
13
14const ts = require("typescript"),
15 unescape = require("lodash.unescape");
16
17//------------------------------------------------------------------------------
18// Private
19//------------------------------------------------------------------------------
20
21const SyntaxKind = ts.SyntaxKind;
22
23const ASSIGNMENT_OPERATORS = [
24 SyntaxKind.EqualsToken,
25 SyntaxKind.PlusEqualsToken,
26 SyntaxKind.MinusEqualsToken,
27 SyntaxKind.AsteriskEqualsToken,
28 SyntaxKind.SlashEqualsToken,
29 SyntaxKind.PercentEqualsToken,
30 SyntaxKind.LessThanLessThanEqualsToken,
31 SyntaxKind.GreaterThanGreaterThanEqualsToken,
32 SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken,
33 SyntaxKind.AmpersandEqualsToken,
34 SyntaxKind.BarEqualsToken,
35 SyntaxKind.CaretEqualsToken
36];
37
38const LOGICAL_OPERATORS = [
39 SyntaxKind.BarBarToken,
40 SyntaxKind.AmpersandAmpersandToken
41];
42
43const TOKEN_TO_TEXT = {};
44TOKEN_TO_TEXT[SyntaxKind.OpenBraceToken] = "{";
45TOKEN_TO_TEXT[SyntaxKind.CloseBraceToken] = "}";
46TOKEN_TO_TEXT[SyntaxKind.OpenParenToken] = "(";
47TOKEN_TO_TEXT[SyntaxKind.CloseParenToken] = ")";
48TOKEN_TO_TEXT[SyntaxKind.OpenBracketToken] = "[";
49TOKEN_TO_TEXT[SyntaxKind.CloseBracketToken] = "]";
50TOKEN_TO_TEXT[SyntaxKind.DotToken] = ".";
51TOKEN_TO_TEXT[SyntaxKind.DotDotDotToken] = "...";
52TOKEN_TO_TEXT[SyntaxKind.SemicolonToken] = ";";
53TOKEN_TO_TEXT[SyntaxKind.CommaToken] = ",";
54TOKEN_TO_TEXT[SyntaxKind.LessThanToken] = "<";
55TOKEN_TO_TEXT[SyntaxKind.GreaterThanToken] = ">";
56TOKEN_TO_TEXT[SyntaxKind.LessThanEqualsToken] = "<=";
57TOKEN_TO_TEXT[SyntaxKind.GreaterThanEqualsToken] = ">=";
58TOKEN_TO_TEXT[SyntaxKind.EqualsEqualsToken] = "==";
59TOKEN_TO_TEXT[SyntaxKind.ExclamationEqualsToken] = "!=";
60TOKEN_TO_TEXT[SyntaxKind.EqualsEqualsEqualsToken] = "===";
61TOKEN_TO_TEXT[SyntaxKind.InstanceOfKeyword] = "instanceof";
62TOKEN_TO_TEXT[SyntaxKind.ExclamationEqualsEqualsToken] = "!==";
63TOKEN_TO_TEXT[SyntaxKind.EqualsGreaterThanToken] = "=>";
64TOKEN_TO_TEXT[SyntaxKind.PlusToken] = "+";
65TOKEN_TO_TEXT[SyntaxKind.MinusToken] = "-";
66TOKEN_TO_TEXT[SyntaxKind.AsteriskToken] = "*";
67TOKEN_TO_TEXT[SyntaxKind.AsteriskAsteriskToken] = "**";
68TOKEN_TO_TEXT[SyntaxKind.SlashToken] = "/";
69TOKEN_TO_TEXT[SyntaxKind.PercentToken] = "%";
70TOKEN_TO_TEXT[SyntaxKind.PlusPlusToken] = "++";
71TOKEN_TO_TEXT[SyntaxKind.MinusMinusToken] = "--";
72TOKEN_TO_TEXT[SyntaxKind.LessThanLessThanToken] = "<<";
73TOKEN_TO_TEXT[SyntaxKind.LessThanSlashToken] = "</";
74TOKEN_TO_TEXT[SyntaxKind.GreaterThanGreaterThanToken] = ">>";
75TOKEN_TO_TEXT[SyntaxKind.GreaterThanGreaterThanGreaterThanToken] = ">>>";
76TOKEN_TO_TEXT[SyntaxKind.AmpersandToken] = "&";
77TOKEN_TO_TEXT[SyntaxKind.BarToken] = "|";
78TOKEN_TO_TEXT[SyntaxKind.CaretToken] = "^";
79TOKEN_TO_TEXT[SyntaxKind.ExclamationToken] = "!";
80TOKEN_TO_TEXT[SyntaxKind.TildeToken] = "~";
81TOKEN_TO_TEXT[SyntaxKind.AmpersandAmpersandToken] = "&&";
82TOKEN_TO_TEXT[SyntaxKind.BarBarToken] = "||";
83TOKEN_TO_TEXT[SyntaxKind.QuestionToken] = "?";
84TOKEN_TO_TEXT[SyntaxKind.ColonToken] = ":";
85TOKEN_TO_TEXT[SyntaxKind.EqualsToken] = "=";
86TOKEN_TO_TEXT[SyntaxKind.PlusEqualsToken] = "+=";
87TOKEN_TO_TEXT[SyntaxKind.MinusEqualsToken] = "-=";
88TOKEN_TO_TEXT[SyntaxKind.AsteriskEqualsToken] = "*=";
89TOKEN_TO_TEXT[SyntaxKind.AsteriskAsteriskEqualsToken] = "**=";
90TOKEN_TO_TEXT[SyntaxKind.SlashEqualsToken] = "/=";
91TOKEN_TO_TEXT[SyntaxKind.PercentEqualsToken] = "%=";
92TOKEN_TO_TEXT[SyntaxKind.LessThanLessThanEqualsToken] = "<<=";
93TOKEN_TO_TEXT[SyntaxKind.GreaterThanGreaterThanEqualsToken] = ">>=";
94TOKEN_TO_TEXT[SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken] = ">>>=";
95TOKEN_TO_TEXT[SyntaxKind.AmpersandEqualsToken] = "&=";
96TOKEN_TO_TEXT[SyntaxKind.BarEqualsToken] = "|=";
97TOKEN_TO_TEXT[SyntaxKind.CaretEqualsToken] = "^=";
98TOKEN_TO_TEXT[SyntaxKind.AtToken] = "@";
99TOKEN_TO_TEXT[SyntaxKind.InKeyword] = "in";
100TOKEN_TO_TEXT[SyntaxKind.UniqueKeyword] = "unique";
101TOKEN_TO_TEXT[SyntaxKind.KeyOfKeyword] = "keyof";
102
103/**
104 * Find the first matching child based on the given sourceFile and predicate function.
105 * @param {TSNode} node The current TSNode
106 * @param {Object} sourceFile The full AST source file
107 * @param {Function} predicate The predicate function to apply to each checked child
108 * @returns {TSNode|undefined} a matching child TSNode
109 */
110function findFirstMatchingChild(node, sourceFile, predicate) {
111 const children = node.getChildren(sourceFile);
112 for (let i = 0; i < children.length; i++) {
113 const child = children[i];
114 if (child && predicate(child)) {
115 return child;
116 }
117
118 const grandChild = findFirstMatchingChild(child, sourceFile, predicate);
119 if (grandChild) {
120 return grandChild;
121 }
122 }
123 return undefined;
124}
125
126/**
127 * Returns true if the given TSNode is a let variable declaration
128 * @param {TSNode} node The TSNode
129 * @returns {boolean} whether or not the given node is a let variable declaration
130 */
131function isLet(node) {
132 /**
133 * TODO: Remove dependency on private TypeScript method
134 */
135 return ts.isLet(node);
136}
137
138/**
139 * Returns true if the given TSNode is a const variable declaration
140 * @param {TSNode} node The TSNode
141 * @returns {boolean} whether or not the given node is a const variable declaration
142 */
143function isConst(node) {
144 /**
145 * TODO: Remove dependency on private TypeScript method
146 */
147 return ts.isConst(node);
148}
149
150//------------------------------------------------------------------------------
151// Public
152//------------------------------------------------------------------------------
153
154/* eslint-disable no-use-before-define */
155module.exports = {
156 /**
157 * Expose the enum of possible TSNode `kind`s.
158 */
159 SyntaxKind,
160 isAssignmentOperator,
161 isLogicalOperator,
162 getTextForTokenKind,
163 isESTreeClassMember,
164 hasModifier,
165 isComma,
166 getBinaryExpressionType,
167 getLocFor,
168 getLoc,
169 isToken,
170 isJSXToken,
171 getDeclarationKind,
172 getTSNodeAccessibility,
173 hasStaticModifierFlag,
174 findNextToken,
175 findFirstMatchingToken,
176 findChildOfKind,
177 findFirstMatchingAncestor,
178 findAncestorOfKind,
179 hasJSXAncestor,
180 unescapeIdentifier,
181 unescapeStringLiteralText,
182 isComputedProperty,
183 isOptional,
184 fixExports,
185 getTokenType,
186 convertToken,
187 convertTokens,
188 getNodeContainer,
189 isWithinTypeAnnotation,
190 isTypeKeyword,
191 isComment,
192 isJSDocComment
193};
194/* eslint-enable no-use-before-define */
195
196/**
197 * Returns true if the given TSToken is the assignment operator
198 * @param {TSToken} operator the operator token
199 * @returns {boolean} is assignment
200 */
201function isAssignmentOperator(operator) {
202 return ASSIGNMENT_OPERATORS.indexOf(operator.kind) > -1;
203}
204
205/**
206 * Returns true if the given TSToken is a logical operator
207 * @param {TSToken} operator the operator token
208 * @returns {boolean} is a logical operator
209 */
210function isLogicalOperator(operator) {
211 return LOGICAL_OPERATORS.indexOf(operator.kind) > -1;
212}
213
214/**
215 * Returns the string form of the given TSToken SyntaxKind
216 * @param {number} kind the token's SyntaxKind
217 * @returns {string} the token applicable token as a string
218 */
219function getTextForTokenKind(kind) {
220 return TOKEN_TO_TEXT[kind];
221}
222
223/**
224 * Returns true if the given TSNode is a valid ESTree class member
225 * @param {TSNode} node TypeScript AST node
226 * @returns {boolean} is valid ESTree class member
227 */
228function isESTreeClassMember(node) {
229 return node.kind !== SyntaxKind.SemicolonClassElement;
230}
231
232/**
233 * Checks if a TSNode has a modifier
234 * @param {SyntaxKind} modifierKind TypeScript SyntaxKind modifier
235 * @param {TSNode} node TypeScript AST node
236 * @returns {boolean} has the modifier specified
237 */
238function hasModifier(modifierKind, node) {
239 return !!node.modifiers && !!node.modifiers.length && node.modifiers.some(modifier => modifier.kind === modifierKind);
240}
241
242/**
243 * Returns true if the given TSToken is a comma
244 * @param {TSToken} token the TypeScript token
245 * @returns {boolean} is comma
246 */
247function isComma(token) {
248 return token.kind === SyntaxKind.CommaToken;
249}
250
251/**
252 * Returns true if the given TSNode is a comment
253 * @param {TSNode} node the TypeScript node
254 * @returns {boolean} is commment
255 */
256function isComment(node) {
257 return node.kind === SyntaxKind.SingleLineCommentTrivia || node.kind === SyntaxKind.MultiLineCommentTrivia;
258}
259
260/**
261 * Returns true if the given TSNode is a JSDoc comment
262 * @param {TSNode} node the TypeScript node
263 * @returns {boolean} is JSDoc comment
264 */
265function isJSDocComment(node) {
266 return node.kind === SyntaxKind.JSDocComment;
267}
268
269/**
270 * Returns the binary expression type of the given TSToken
271 * @param {TSToken} operator the operator token
272 * @returns {string} the binary expression type
273 */
274function getBinaryExpressionType(operator) {
275 if (isAssignmentOperator(operator)) {
276 return "AssignmentExpression";
277 } else if (isLogicalOperator(operator)) {
278 return "LogicalExpression";
279 }
280 return "BinaryExpression";
281}
282
283/**
284 * Returns line and column data for the given start and end positions,
285 * for the given AST
286 * @param {Object} start start data
287 * @param {Object} end end data
288 * @param {Object} ast the AST object
289 * @returns {Object} the loc data
290 */
291function getLocFor(start, end, ast) {
292 const startLoc = ast.getLineAndCharacterOfPosition(start),
293 endLoc = ast.getLineAndCharacterOfPosition(end);
294
295 return {
296 start: {
297 line: startLoc.line + 1,
298 column: startLoc.character
299 },
300 end: {
301 line: endLoc.line + 1,
302 column: endLoc.character
303 }
304 };
305}
306
307/**
308 * Returns line and column data for the given ESTreeNode or ESTreeToken,
309 * for the given AST
310 * @param {ESTreeToken|ESTreeNode} nodeOrToken the ESTreeNode or ESTreeToken
311 * @param {Object} ast the AST object
312 * @returns {Object} the loc data
313 */
314function getLoc(nodeOrToken, ast) {
315 return getLocFor(nodeOrToken.getStart(), nodeOrToken.end, ast);
316}
317
318/**
319 * Returns true if a given TSNode is a token
320 * @param {TSNode} node the TSNode
321 * @returns {boolean} is a token
322 */
323function isToken(node) {
324 return node.kind >= SyntaxKind.FirstToken && node.kind <= SyntaxKind.LastToken;
325}
326
327/**
328 * Returns true if a given TSNode is a JSX token
329 * @param {TSNode} node TSNode to be checked
330 * @returns {boolean} is a JSX token
331 */
332function isJSXToken(node) {
333 return (
334 node.kind >= SyntaxKind.JsxElement &&
335 node.kind <= SyntaxKind.JsxAttribute
336 );
337}
338
339/**
340 * Returns true if the given TSNode.kind value corresponds to a type keyword
341 * @param {number} kind TypeScript SyntaxKind
342 * @returns {boolean} is a type keyword
343 */
344function isTypeKeyword(kind) {
345 switch (kind) {
346 case SyntaxKind.AnyKeyword:
347 case SyntaxKind.BooleanKeyword:
348 case SyntaxKind.NeverKeyword:
349 case SyntaxKind.NumberKeyword:
350 case SyntaxKind.ObjectKeyword:
351 case SyntaxKind.StringKeyword:
352 case SyntaxKind.SymbolKeyword:
353 case SyntaxKind.VoidKeyword:
354 return true;
355 default:
356 return false;
357 }
358}
359
360/**
361 * Returns the declaration kind of the given TSNode
362 * @param {TSNode} node TypeScript AST node
363 * @returns {string} declaration kind
364 */
365function getDeclarationKind(node) {
366 let varDeclarationKind;
367 switch (node.kind) {
368 case SyntaxKind.TypeAliasDeclaration:
369 varDeclarationKind = "type";
370 break;
371 case SyntaxKind.VariableDeclarationList:
372 if (isLet(node)) {
373 varDeclarationKind = "let";
374 } else if (isConst(node)) {
375 varDeclarationKind = "const";
376 } else {
377 varDeclarationKind = "var";
378 }
379 break;
380 default:
381 throw "Unable to determine declaration kind.";
382 }
383 return varDeclarationKind;
384}
385
386/**
387 * Gets a TSNode's accessibility level
388 * @param {TSNode} node The TSNode
389 * @returns {string | null} accessibility "public", "protected", "private", or null
390 */
391function getTSNodeAccessibility(node) {
392 const modifiers = node.modifiers;
393 if (!modifiers) {
394 return null;
395 }
396 for (let i = 0; i < modifiers.length; i++) {
397 const modifier = modifiers[i];
398 switch (modifier.kind) {
399 case SyntaxKind.PublicKeyword:
400 return "public";
401 case SyntaxKind.ProtectedKeyword:
402 return "protected";
403 case SyntaxKind.PrivateKeyword:
404 return "private";
405 default:
406 continue;
407 }
408 }
409 return null;
410}
411
412/**
413 * Returns true if the given TSNode has the modifier flag set which corresponds
414 * to the static keyword.
415 * @param {TSNode} node The TSNode
416 * @returns {boolean} whether or not the static modifier flag is set
417 */
418function hasStaticModifierFlag(node) {
419 /**
420 * TODO: Remove dependency on private TypeScript method
421 */
422 return Boolean(ts.getModifierFlags(node) & ts.ModifierFlags.Static);
423}
424
425/**
426 * Finds the next token based on the previous one and its parent
427 * @param {TSToken} previousToken The previous TSToken
428 * @param {TSNode} parent The parent TSNode
429 * @returns {TSToken} the next TSToken
430 */
431function findNextToken(previousToken, parent) {
432 /**
433 * TODO: Remove dependency on private TypeScript method
434 */
435 return ts.findNextToken(previousToken, parent);
436}
437
438/**
439 * Find the first matching token based on the given predicate function.
440 * @param {TSToken} previousToken The previous TSToken
441 * @param {TSNode} parent The parent TSNode
442 * @param {Function} predicate The predicate function to apply to each checked token
443 * @returns {TSToken|undefined} a matching TSToken
444 */
445function findFirstMatchingToken(previousToken, parent, predicate) {
446 while (previousToken) {
447 if (predicate(previousToken)) {
448 return previousToken;
449 }
450 previousToken = findNextToken(previousToken, parent);
451 }
452 return undefined;
453}
454
455/**
456 * Finds the first child TSNode which matches the given kind
457 * @param {TSNode} node The parent TSNode
458 * @param {number} kind The TSNode kind to match against
459 * @param {Object} sourceFile The full AST source file
460 * @returns {TSNode|undefined} a matching TSNode
461 */
462function findChildOfKind(node, kind, sourceFile) {
463 return findFirstMatchingChild(node, sourceFile, child => child.kind === kind);
464}
465
466/**
467 * Find the first matching ancestor based on the given predicate function.
468 * @param {TSNode} node The current TSNode
469 * @param {Function} predicate The predicate function to apply to each checked ancestor
470 * @returns {TSNode|undefined} a matching parent TSNode
471 */
472function findFirstMatchingAncestor(node, predicate) {
473 while (node) {
474 if (predicate(node)) {
475 return node;
476 }
477 node = node.parent;
478 }
479 return undefined;
480}
481
482/**
483 * Finds the first parent TSNode which mastches the given kind
484 * @param {TSNode} node The current TSNode
485 * @param {number} kind The TSNode kind to match against
486 * @returns {TSNode|undefined} a matching parent TSNode
487 */
488function findAncestorOfKind(node, kind) {
489 return findFirstMatchingAncestor(node, parent => parent.kind === kind);
490}
491
492
493/**
494 * Returns true if a given TSNode has a JSX token within its hierarchy
495 * @param {TSNode} node TSNode to be checked
496 * @returns {boolean} has JSX ancestor
497 */
498function hasJSXAncestor(node) {
499 return !!findFirstMatchingAncestor(node, isJSXToken);
500}
501
502/**
503 * Remove extra underscore from escaped identifier text content.
504 * @param {string} identifier The escaped identifier text.
505 * @returns {string} The unescaped identifier text.
506 */
507function unescapeIdentifier(identifier) {
508 return ts.unescapeIdentifier(identifier);
509}
510
511/**
512 * Unescape the text content of string literals, e.g. &amp; -> &
513 * @param {string} text The escaped string literal text.
514 * @returns {string} The unescaped string literal text.
515 */
516function unescapeStringLiteralText(text) {
517 return unescape(text);
518}
519
520/**
521 * Returns true if a given TSNode is a computed property
522 * @param {TSNode} node TSNode to be checked
523 * @returns {boolean} is Computed Property
524 */
525function isComputedProperty(node) {
526 return node.kind === SyntaxKind.ComputedPropertyName;
527}
528
529/**
530 * Returns true if a given TSNode is optional (has QuestionToken)
531 * @param {TSNode} node TSNode to be checked
532 * @returns {boolean} is Optional
533 */
534function isOptional(node) {
535 return (node.questionToken)
536 ? (node.questionToken.kind === SyntaxKind.QuestionToken) : false;
537}
538
539/**
540 * Returns true if the given TSNode is within the context of a "typeAnnotation",
541 * which effectively means - is it coming from its parent's `type` or `types` property
542 * @param {TSNode} node TSNode to be checked
543 * @returns {boolean} is within "typeAnnotation context"
544 */
545function isWithinTypeAnnotation(node) {
546 return node.parent.type === node || (node.parent.types && node.parent.types.indexOf(node) > -1);
547}
548
549/**
550 * Fixes the exports of the given TSNode
551 * @param {TSNode} node the TSNode
552 * @param {Object} result result
553 * @param {Object} ast the AST
554 * @returns {TSNode} the TSNode with fixed exports
555 */
556function fixExports(node, result, ast) {
557 // check for exports
558 if (node.modifiers && node.modifiers[0].kind === SyntaxKind.ExportKeyword) {
559 const exportKeyword = node.modifiers[0],
560 nextModifier = node.modifiers[1],
561 lastModifier = node.modifiers[node.modifiers.length - 1],
562 declarationIsDefault = nextModifier && (nextModifier.kind === SyntaxKind.DefaultKeyword),
563 varToken = findNextToken(lastModifier, ast);
564
565 result.range[0] = varToken.getStart();
566 result.loc = getLocFor(result.range[0], result.range[1], ast);
567
568 const declarationType = declarationIsDefault ? "ExportDefaultDeclaration" : "ExportNamedDeclaration";
569
570 const newResult = {
571 type: declarationType,
572 declaration: result,
573 range: [exportKeyword.getStart(), result.range[1]],
574 loc: getLocFor(exportKeyword.getStart(), result.range[1], ast)
575 };
576
577 if (!declarationIsDefault) {
578 newResult.specifiers = [];
579 newResult.source = null;
580 }
581
582 return newResult;
583
584 }
585
586 return result;
587}
588
589/**
590 * Returns the type of a given ESTreeToken
591 * @param {ESTreeToken} token the ESTreeToken
592 * @returns {string} the token type
593 */
594function getTokenType(token) {
595 // Need two checks for keywords since some are also identifiers
596 if (token.originalKeywordKind) {
597
598 switch (token.originalKeywordKind) {
599 case SyntaxKind.NullKeyword:
600 return "Null";
601
602 case SyntaxKind.GetKeyword:
603 case SyntaxKind.SetKeyword:
604 case SyntaxKind.TypeKeyword:
605 case SyntaxKind.ModuleKeyword:
606 return "Identifier";
607
608 default:
609 return "Keyword";
610 }
611 }
612
613 if (token.kind >= SyntaxKind.FirstKeyword && token.kind <= SyntaxKind.LastFutureReservedWord) {
614 if (token.kind === SyntaxKind.FalseKeyword || token.kind === SyntaxKind.TrueKeyword) {
615 return "Boolean";
616 }
617
618 return "Keyword";
619 }
620
621 if (token.kind >= SyntaxKind.FirstPunctuation && token.kind <= SyntaxKind.LastBinaryOperator) {
622 return "Punctuator";
623 }
624
625 if (token.kind >= SyntaxKind.NoSubstitutionTemplateLiteral && token.kind <= SyntaxKind.TemplateTail) {
626 return "Template";
627 }
628
629 switch (token.kind) {
630 case SyntaxKind.NumericLiteral:
631 return "Numeric";
632
633 case SyntaxKind.JsxText:
634 return "JSXText";
635
636 case SyntaxKind.StringLiteral:
637
638 // A TypeScript-StringLiteral token with a TypeScript-JsxAttribute or TypeScript-JsxElement parent,
639 // must actually be an ESTree-JSXText token
640 if (token.parent && (token.parent.kind === SyntaxKind.JsxAttribute || token.parent.kind === SyntaxKind.JsxElement)) {
641 return "JSXText";
642 }
643
644 return "String";
645
646 case SyntaxKind.RegularExpressionLiteral:
647 return "RegularExpression";
648
649 case SyntaxKind.Identifier:
650 case SyntaxKind.ConstructorKeyword:
651 case SyntaxKind.GetKeyword:
652 case SyntaxKind.SetKeyword:
653
654 // falls through
655 default:
656 }
657
658 // Some JSX tokens have to be determined based on their parent
659 if (token.parent) {
660 if (token.kind === SyntaxKind.Identifier && token.parent.kind === SyntaxKind.PropertyAccessExpression && hasJSXAncestor(token)) {
661 return "JSXIdentifier";
662 }
663
664 if (isJSXToken(token.parent)) {
665 if (token.kind === SyntaxKind.PropertyAccessExpression) {
666 return "JSXMemberExpression";
667 }
668
669 if (token.kind === SyntaxKind.Identifier) {
670 return "JSXIdentifier";
671 }
672 }
673 }
674
675 return "Identifier";
676}
677
678/**
679 * Extends and formats a given ESTreeToken, for a given AST
680 * @param {ESTreeToken} token the ESTreeToken
681 * @param {Object} ast the AST object
682 * @returns {ESTreeToken} the converted ESTreeToken
683 */
684function convertToken(token, ast) {
685 const start = (token.kind === SyntaxKind.JsxText) ? token.getFullStart() : token.getStart(),
686 end = token.getEnd(),
687 value = ast.text.slice(start, end),
688 newToken = {
689 type: getTokenType(token),
690 value,
691 range: [start, end],
692 loc: getLocFor(start, end, ast)
693 };
694
695 if (newToken.type === "RegularExpression") {
696 newToken.regex = {
697 pattern: value.slice(1, value.lastIndexOf("/")),
698 flags: value.slice(value.lastIndexOf("/") + 1)
699 };
700 }
701
702 return newToken;
703}
704
705/**
706 * Converts all tokens for the given AST
707 * @param {Object} ast the AST object
708 * @returns {ESTreeToken[]} the converted ESTreeTokens
709 */
710function convertTokens(ast) {
711 const result = [];
712 /**
713 * @param {TSNode} node the TSNode
714 * @returns {undefined}
715 */
716 function walk(node) {
717 // TypeScript generates tokens for types in JSDoc blocks. Comment tokens
718 // and their children should not be walked or added to the resulting tokens list.
719 if (isComment(node) || isJSDocComment(node)) {
720 return;
721 }
722
723 if (isToken(node) && node.kind !== SyntaxKind.EndOfFileToken) {
724 const converted = convertToken(node, ast);
725
726 if (converted) {
727 result.push(converted);
728 }
729 } else {
730 node.getChildren().forEach(walk);
731 }
732 }
733 walk(ast);
734 return result;
735}
736
737/**
738 * Get container token node between range
739 * @param {Object} ast the AST object
740 * @param {int} start The index at which the comment starts.
741 * @param {int} end The index at which the comment ends.
742 * @returns {TSToken} typescript container token
743 * @private
744 */
745function getNodeContainer(ast, start, end) {
746 let container = null;
747
748 /**
749 * @param {TSNode} node the TSNode
750 * @returns {undefined}
751 */
752 function walk(node) {
753 const nodeStart = node.pos;
754 const nodeEnd = node.end;
755
756 if (start >= nodeStart && end <= nodeEnd) {
757 if (isToken(node)) {
758 container = node;
759 } else {
760 node.getChildren().forEach(walk);
761 }
762 }
763 }
764 walk(ast);
765
766 return container;
767}