diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index e28166f1eb8..f4798d0d307 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -43,7 +43,8 @@ "glob": "^7.1.4", "is-glob": "^4.0.1", "lodash.unescape": "4.0.1", - "semver": "^6.3.0" + "semver": "^6.3.0", + "tsutils": "^3.17.1" }, "devDependencies": { "@babel/code-frame": "7.5.5", diff --git a/packages/typescript-estree/src/convert-comments.ts b/packages/typescript-estree/src/convert-comments.ts index d3738774cd9..9a7de3e112a 100644 --- a/packages/typescript-estree/src/convert-comments.ts +++ b/packages/typescript-estree/src/convert-comments.ts @@ -1,82 +1,8 @@ import * as ts from 'typescript'; // leave this as * as ts so people using util package don't need syntheticDefaultImports -import { getLocFor, getNodeContainer } from './node-utils'; +import { forEachComment } from 'tsutils'; +import { getLocFor } from './node-utils'; import { TSESTree } from './ts-estree'; -/** - * Converts a TypeScript comment to an Esprima comment. - * @param block True if it's a block comment, false if not. - * @param text The text of the comment. - * @param start The index at which the comment starts. - * @param end The index at which the comment ends. - * @param startLoc The location at which the comment starts. - * @param endLoc The location at which the comment ends. - * @returns The comment object. - * @internal - */ -function convertTypeScriptCommentToEsprimaComment( - block: boolean, - text: string, - start: number, - end: number, - startLoc: TSESTree.LineAndColumnData, - endLoc: TSESTree.LineAndColumnData, -): TSESTree.Comment { - const comment: TSESTree.OptionalRangeAndLoc = { - type: block ? 'Block' : 'Line', - value: text, - }; - - if (typeof start === 'number') { - comment.range = [start, end]; - } - - if (typeof startLoc === 'object') { - comment.loc = { - start: startLoc, - end: endLoc, - }; - } - - return comment as TSESTree.Comment; -} - -/** - * Convert comment from TypeScript Triva Scanner. - * @param triviaScanner TS Scanner - * @param ast the AST object - * @param code TypeScript code - * @returns the converted Comment - * @private - */ -function getCommentFromTriviaScanner( - triviaScanner: ts.Scanner, - ast: ts.SourceFile, - code: string, -): TSESTree.Comment { - const kind = triviaScanner.getToken(); - const isBlock = kind === ts.SyntaxKind.MultiLineCommentTrivia; - const range = { - pos: triviaScanner.getTokenPos(), - end: triviaScanner.getTextPos(), - kind: triviaScanner.getToken(), - }; - - const comment = code.substring(range.pos, range.end); - const text = isBlock - ? comment.replace(/^\/\*/, '').replace(/\*\/$/, '') - : comment.replace(/^\/\//, ''); - const loc = getLocFor(range.pos, range.end, ast); - - return convertTypeScriptCommentToEsprimaComment( - isBlock, - text, - range.pos, - range.end, - loc.start, - loc.end, - ); -} - /** * Convert all comments for the given AST. * @param ast the AST object @@ -90,93 +16,33 @@ export function convertComments( ): TSESTree.Comment[] { const comments: TSESTree.Comment[] = []; - /** - * Create a TypeScript Scanner, with skipTrivia set to false so that - * we can parse the comments - */ - const triviaScanner = ts.createScanner( - ast.languageVersion, - false, - ast.languageVariant, - code, + forEachComment( + ast, + (_, comment) => { + const type = + comment.kind == ts.SyntaxKind.SingleLineCommentTrivia + ? 'Line' + : 'Block'; + const range: TSESTree.Range = [comment.pos, comment.end]; + const loc = getLocFor(range[0], range[1], ast); + + // both comments start with 2 characters - /* or // + const textStart = range[0] + 2; + const textEnd = + comment.kind === ts.SyntaxKind.SingleLineCommentTrivia + ? // single line comments end at the end + range[1] - textStart + : // multiline comments end 2 characters early + range[1] - textStart - 2; + comments.push({ + type, + value: code.substr(textStart, textEnd), + range, + loc, + }); + }, + ast, ); - let kind = triviaScanner.scan(); - while (kind !== ts.SyntaxKind.EndOfFileToken) { - const start = triviaScanner.getTokenPos(); - const end = triviaScanner.getTextPos(); - - let container: ts.Node | null = null; - switch (kind) { - case ts.SyntaxKind.SingleLineCommentTrivia: - case ts.SyntaxKind.MultiLineCommentTrivia: { - const comment = getCommentFromTriviaScanner(triviaScanner, ast, code); - - comments.push(comment); - break; - } - case ts.SyntaxKind.GreaterThanToken: - container = getNodeContainer(ast, start, end); - if ( - (container.parent && - container.parent.parent && - // Rescan after an opening element or fragment - (container.parent.kind === ts.SyntaxKind.JsxOpeningElement && - // Make sure this is the end of a tag like `>` - container.parent.end === end)) || - container.parent.kind === ts.SyntaxKind.JsxOpeningFragment || - // Rescan after a self-closing element if it's inside another JSX element - (container.parent.kind === ts.SyntaxKind.JsxSelfClosingElement && - (container.parent.parent.kind === ts.SyntaxKind.JsxElement || - container.parent.parent.kind === ts.SyntaxKind.JsxFragment)) || - // Rescan after a closing element if it's inside another JSX element - ((container.parent.kind === ts.SyntaxKind.JsxClosingElement || - container.parent.kind === ts.SyntaxKind.JsxClosingFragment) && - container.parent.parent.parent && - (container.parent.parent.parent.kind === ts.SyntaxKind.JsxElement || - container.parent.parent.parent.kind === - ts.SyntaxKind.JsxFragment)) - ) { - kind = triviaScanner.reScanJsxToken(); - continue; - } - break; - case ts.SyntaxKind.CloseBraceToken: - container = getNodeContainer(ast, start, end); - - // Rescan after a JSX expression - if ( - container.parent && - container.parent.kind === ts.SyntaxKind.JsxExpression && - container.parent.parent && - container.parent.parent.kind === ts.SyntaxKind.JsxElement - ) { - kind = triviaScanner.reScanJsxToken(); - continue; - } - - if ( - container.kind === ts.SyntaxKind.TemplateMiddle || - container.kind === ts.SyntaxKind.TemplateTail - ) { - kind = triviaScanner.reScanTemplateToken(); - continue; - } - break; - case ts.SyntaxKind.SlashToken: - case ts.SyntaxKind.SlashEqualsToken: - container = getNodeContainer(ast, start, end); - - if (container.kind === ts.SyntaxKind.RegularExpressionLiteral) { - kind = triviaScanner.reScanSlashToken(); - continue; - } - break; - default: - break; - } - kind = triviaScanner.scan(); - } - return comments; } diff --git a/packages/typescript-estree/src/node-utils.ts b/packages/typescript-estree/src/node-utils.ts index 392c08fabb8..da0034ee6ae 100644 --- a/packages/typescript-estree/src/node-utils.ts +++ b/packages/typescript-estree/src/node-utils.ts @@ -612,41 +612,6 @@ export function convertTokens(ast: ts.SourceFile): TSESTree.Token[] { return result; } -/** - * Get container token node between range - * @param ast the AST object - * @param start The index at which the comment starts. - * @param end The index at which the comment ends. - * @returns typescript container token - * @private - */ -export function getNodeContainer( - ast: ts.SourceFile, - start: number, - end: number, -): ts.Node { - let container: ts.Node | null = null; - - /** - * @param node the ts.Node - */ - function walk(node: ts.Node): void { - const nodeStart = node.pos; - const nodeEnd = node.end; - - if (start >= nodeStart && end <= nodeEnd) { - if (isToken(node)) { - container = node; - } else { - node.getChildren().forEach(walk); - } - } - } - walk(ast); - - return container!; -} - export interface TSError { index: number; lineNumber: number;