diff --git a/.eslintignore b/.eslintignore index 92693b86382..3c9816ccb5c 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,5 +4,6 @@ jest.config.js fixtures shared-fixtures coverage +__snapshots__ packages/eslint-plugin-tslint/tests diff --git a/packages/eslint-plugin/src/rules/comma-spacing.ts b/packages/eslint-plugin/src/rules/comma-spacing.ts index 0651142efcb..d8ab4b42b8d 100644 --- a/packages/eslint-plugin/src/rules/comma-spacing.ts +++ b/packages/eslint-plugin/src/rules/comma-spacing.ts @@ -54,7 +54,7 @@ export default createRule({ create(context, [{ before: spaceBefore, after: spaceAfter }]) { const sourceCode = context.getSourceCode(); const tokensAndComments = sourceCode.tokensAndComments; - const ignoredTokens = new Set(); + const ignoredTokens = new Set(); /** * Adds null elements of the ArrayExpression or ArrayPattern node to the ignore list @@ -67,7 +67,7 @@ export default createRule({ for (const element of node.elements) { let token: TSESTree.Token | null; if (element === null) { - token = sourceCode.getTokenAfter(previousToken as TSESTree.Token); + token = sourceCode.getTokenAfter(previousToken!); if (token && isCommaToken(token)) { ignoredTokens.add(token); } @@ -100,9 +100,9 @@ export default createRule({ * @param nextToken The first token after the comma */ function validateCommaSpacing( - commaToken: TSESTree.Token, - prevToken: TSESTree.Token | null, - nextToken: TSESTree.Token | null, + commaToken: TSESTree.PunctuatorToken, + prevToken: TSESTree.Token | TSESTree.Comment | null, + nextToken: TSESTree.Token | TSESTree.Comment | null, ): void { if ( prevToken && @@ -166,20 +166,15 @@ export default createRule({ return; } - if (token.type === AST_TOKEN_TYPES.JSXText) { - return; - } - - const commaToken = token as TSESTree.Token; - const prevToken = tokensAndComments[i - 1] as TSESTree.Token; - const nextToken = tokensAndComments[i + 1] as TSESTree.Token; + const prevToken = tokensAndComments[i - 1]; + const nextToken = tokensAndComments[i + 1]; validateCommaSpacing( - commaToken, - isCommaToken(prevToken) || ignoredTokens.has(commaToken) + token, + isCommaToken(prevToken) || ignoredTokens.has(token) ? null : prevToken, - isCommaToken(nextToken) || ignoredTokens.has(commaToken) + isCommaToken(nextToken) || ignoredTokens.has(token) ? null : nextToken, ); diff --git a/packages/eslint-plugin/src/util/astUtils.ts b/packages/eslint-plugin/src/util/astUtils.ts index 060165a28ed..6c92d90b637 100644 --- a/packages/eslint-plugin/src/util/astUtils.ts +++ b/packages/eslint-plugin/src/util/astUtils.ts @@ -8,7 +8,7 @@ const LINEBREAK_MATCHER = /\r\n|[\r\n\u2028\u2029]/; function isOptionalChainPunctuator( token: TSESTree.Token | TSESTree.Comment, -): boolean { +): token is TSESTree.PunctuatorToken & { value: '?.' } { return token.type === AST_TOKEN_TYPES.Punctuator && token.value === '?.'; } function isNotOptionalChainPunctuator( @@ -19,7 +19,7 @@ function isNotOptionalChainPunctuator( function isNonNullAssertionPunctuator( token: TSESTree.Token | TSESTree.Comment, -): boolean { +): token is TSESTree.PunctuatorToken & { value: '!' } { return token.type === AST_TOKEN_TYPES.Punctuator && token.value === '!'; } function isNotNonNullAssertionPunctuator( @@ -33,7 +33,7 @@ function isNotNonNullAssertionPunctuator( */ function isOptionalOptionalChain( node: TSESTree.Node, -): node is TSESTree.OptionalCallExpression { +): node is TSESTree.OptionalCallExpression & { optional: true } { return ( node.type === AST_NODE_TYPES.OptionalCallExpression && // this flag means the call expression itself is option @@ -45,7 +45,9 @@ function isOptionalOptionalChain( /** * Returns true if and only if the node represents logical OR */ -function isLogicalOrOperator(node: TSESTree.Node): boolean { +function isLogicalOrOperator( + node: TSESTree.Node, +): node is TSESTree.LogicalExpression & { operator: '||' } { return ( node.type === AST_NODE_TYPES.LogicalExpression && node.operator === '||' ); diff --git a/packages/eslint-plugin/typings/eslint-utils.d.ts b/packages/eslint-plugin/typings/eslint-utils.d.ts index 6b3b1827a90..f2fc090c7a6 100644 --- a/packages/eslint-plugin/typings/eslint-utils.d.ts +++ b/packages/eslint-plugin/typings/eslint-utils.d.ts @@ -113,67 +113,67 @@ declare module 'eslint-utils' { export function isArrowToken( token: TSESTree.Token | TSESTree.Comment, - ): boolean; + ): token is TSESTree.PunctuatorToken & { value: '=>' }; export function isNotArrowToken( token: TSESTree.Token | TSESTree.Comment, ): boolean; export function isClosingBraceToken( token: TSESTree.Token | TSESTree.Comment, - ): boolean; + ): token is TSESTree.PunctuatorToken & { value: '}' }; export function isNotClosingBraceToken( token: TSESTree.Token | TSESTree.Comment, ): boolean; export function isClosingBracketToken( token: TSESTree.Token | TSESTree.Comment, - ): boolean; + ): token is TSESTree.PunctuatorToken & { value: ']' }; export function isNotClosingBracketToken( token: TSESTree.Token | TSESTree.Comment, ): boolean; export function isClosingParenToken( token: TSESTree.Token | TSESTree.Comment, - ): boolean; + ): token is TSESTree.PunctuatorToken & { value: ')' }; export function isNotClosingParenToken( token: TSESTree.Token | TSESTree.Comment, ): boolean; export function isColonToken( token: TSESTree.Token | TSESTree.Comment, - ): boolean; + ): token is TSESTree.PunctuatorToken & { value: ':' }; export function isNotColonToken( token: TSESTree.Token | TSESTree.Comment, ): boolean; export function isCommaToken( token: TSESTree.Token | TSESTree.Comment, - ): boolean; + ): token is TSESTree.PunctuatorToken & { value: ',' }; export function isNotCommaToken( token: TSESTree.Token | TSESTree.Comment, ): boolean; export function isCommentToken( token: TSESTree.Token | TSESTree.Comment, - ): boolean; - export function isNotCommentToken( - token: TSESTree.Token | TSESTree.Comment, - ): boolean; + ): token is TSESTree.Comment; + export function isNotCommentToken< + T extends TSESTree.Token | TSESTree.Comment + >(token: T): token is Exclude; export function isOpeningBraceToken( token: TSESTree.Token | TSESTree.Comment, - ): boolean; + ): token is TSESTree.PunctuatorToken & { value: '{' }; export function isNotOpeningBraceToken( token: TSESTree.Token | TSESTree.Comment, ): boolean; export function isOpeningBracketToken( token: TSESTree.Token | TSESTree.Comment, - ): boolean; + ): token is TSESTree.PunctuatorToken & { value: '[' }; export function isNotOpeningBracketToken( token: TSESTree.Token | TSESTree.Comment, ): boolean; export function isOpeningParenToken( token: TSESTree.Token | TSESTree.Comment, - ): boolean; + ): token is TSESTree.PunctuatorToken & { value: '(' }; export function isNotOpeningParenToken( token: TSESTree.Token | TSESTree.Comment, ): boolean; export function isSemicolonToken( token: TSESTree.Token | TSESTree.Comment, - ): boolean; + ): token is TSESTree.PunctuatorToken & { value: ';' }; export function isNotSemicolonToken( token: TSESTree.Token | TSESTree.Comment, ): boolean; diff --git a/packages/experimental-utils/src/ts-eslint/SourceCode.ts b/packages/experimental-utils/src/ts-eslint/SourceCode.ts index 6744e988d8d..eeaa906f37d 100644 --- a/packages/experimental-utils/src/ts-eslint/SourceCode.ts +++ b/packages/experimental-utils/src/ts-eslint/SourceCode.ts @@ -33,8 +33,8 @@ declare interface SourceCode { getNodeByRangeIndex(index: number): TSESTree.Node | null; isSpaceBetween( - first: TSESTree.Token | TSESTree.Node, - second: TSESTree.Token | TSESTree.Node, + first: TSESTree.Token | TSESTree.Comment | TSESTree.Node, + second: TSESTree.Token | TSESTree.Comment | TSESTree.Node, ): boolean; /** @@ -49,93 +49,90 @@ declare interface SourceCode { // Inherited methods from TokenStore // --------------------------------- - getTokenByRangeStart( + getTokenByRangeStart( offset: number, - options?: { includeComments?: boolean }, - ): TSESTree.Token | null; + options?: T, + ): SourceCode.ReturnTypeFromOptions | null; - getFirstToken( + getFirstToken( node: TSESTree.Node, - options?: SourceCode.CursorWithSkipOptions, - ): TSESTree.Token | null; + options?: T, + ): SourceCode.ReturnTypeFromOptions | null; - getFirstTokens( + getFirstTokens( node: TSESTree.Node, - options?: SourceCode.CursorWithCountOptions, - ): TSESTree.Token[]; + options?: T, + ): SourceCode.ReturnTypeFromOptions[]; - getLastToken( + getLastToken( node: TSESTree.Node, - options?: SourceCode.CursorWithSkipOptions, - ): TSESTree.Token | null; + options?: T, + ): SourceCode.ReturnTypeFromOptions | null; - getLastTokens( + getLastTokens( node: TSESTree.Node, - options?: SourceCode.CursorWithCountOptions, - ): TSESTree.Token[]; + options?: T, + ): SourceCode.ReturnTypeFromOptions[]; - getTokenBefore( + getTokenBefore( node: TSESTree.Node | TSESTree.Token | TSESTree.Comment, - options?: SourceCode.CursorWithSkipOptions, - ): TSESTree.Token | null; + options?: T, + ): SourceCode.ReturnTypeFromOptions | null; - getTokensBefore( + getTokensBefore( node: TSESTree.Node | TSESTree.Token | TSESTree.Comment, - options?: SourceCode.CursorWithCountOptions, - ): TSESTree.Token[]; + options?: T, + ): SourceCode.ReturnTypeFromOptions[]; - getTokenAfter( + getTokenAfter( node: TSESTree.Node | TSESTree.Token | TSESTree.Comment, - options?: SourceCode.CursorWithSkipOptions, - ): TSESTree.Token | null; + options?: T, + ): SourceCode.ReturnTypeFromOptions | null; - getTokensAfter( + getTokensAfter( node: TSESTree.Node | TSESTree.Token | TSESTree.Comment, - options?: SourceCode.CursorWithCountOptions, - ): TSESTree.Token[]; + options?: T, + ): SourceCode.ReturnTypeFromOptions[]; - getFirstTokenBetween( + getFirstTokenBetween( left: TSESTree.Node | TSESTree.Token | TSESTree.Comment, right: TSESTree.Node | TSESTree.Token | TSESTree.Comment, - options?: SourceCode.CursorWithSkipOptions, - ): TSESTree.Token | null; + options?: T, + ): SourceCode.ReturnTypeFromOptions | null; - getFirstTokensBetween( + getFirstTokensBetween( left: TSESTree.Node | TSESTree.Token | TSESTree.Comment, right: TSESTree.Node | TSESTree.Token | TSESTree.Comment, - options?: SourceCode.CursorWithCountOptions, - ): TSESTree.Token[]; + options?: T, + ): SourceCode.ReturnTypeFromOptions[]; - getLastTokenBetween( + getLastTokenBetween( left: TSESTree.Node | TSESTree.Token | TSESTree.Comment, right: TSESTree.Node | TSESTree.Token | TSESTree.Comment, - options?: SourceCode.CursorWithSkipOptions, - ): TSESTree.Token | null; + options?: T, + ): SourceCode.ReturnTypeFromOptions | null; - getLastTokensBetween( + getLastTokensBetween( left: TSESTree.Node | TSESTree.Token | TSESTree.Comment, right: TSESTree.Node | TSESTree.Token | TSESTree.Comment, - options?: SourceCode.CursorWithCountOptions, - ): TSESTree.Token[]; + options?: T, + ): SourceCode.ReturnTypeFromOptions[]; - getTokensBetween( + getTokensBetween( left: TSESTree.Node | TSESTree.Token | TSESTree.Comment, right: TSESTree.Node | TSESTree.Token | TSESTree.Comment, - padding?: - | number - | SourceCode.FilterPredicate - | SourceCode.CursorWithCountOptions, - ): TSESTree.Token[]; + padding?: T, + ): SourceCode.ReturnTypeFromOptions[]; getTokens( node: TSESTree.Node, beforeCount?: number, afterCount?: number, ): TSESTree.Token[]; - getTokens( + getTokens( node: TSESTree.Node, - options: SourceCode.FilterPredicate | SourceCode.CursorWithCountOptions, - ): TSESTree.Token[]; + options: T, + ): SourceCode.ReturnTypeFromOptions[]; commentsExistBetween( left: TSESTree.Node | TSESTree.Token, @@ -175,6 +172,10 @@ namespace SourceCode { tokenOrComment: TSESTree.Token | TSESTree.Comment, ) => boolean; + export type ReturnTypeFromOptions = T extends { includeComments: true } + ? TSESTree.Token | TSESTree.Comment + : TSESTree.Token; + export type CursorWithSkipOptions = | number | FilterPredicate diff --git a/packages/typescript-estree/src/convert-comments.ts b/packages/typescript-estree/src/convert-comments.ts index 753e82f7b55..737b01b242f 100644 --- a/packages/typescript-estree/src/convert-comments.ts +++ b/packages/typescript-estree/src/convert-comments.ts @@ -1,7 +1,7 @@ import * as ts from 'typescript'; import { forEachComment } from 'tsutils/util/util'; import { getLocFor } from './node-utils'; -import { TSESTree } from './ts-estree'; +import { AST_TOKEN_TYPES, TSESTree } from './ts-estree'; /** * Convert all comments for the given AST. @@ -21,8 +21,8 @@ export function convertComments( (_, comment) => { const type = comment.kind == ts.SyntaxKind.SingleLineCommentTrivia - ? 'Line' - : 'Block'; + ? AST_TOKEN_TYPES.Line + : AST_TOKEN_TYPES.Block; const range: TSESTree.Range = [comment.pos, comment.end]; const loc = getLocFor(range[0], range[1], ast); diff --git a/packages/typescript-estree/src/node-utils.ts b/packages/typescript-estree/src/node-utils.ts index 3f9e4549bc5..6cbc6b0ba82 100644 --- a/packages/typescript-estree/src/node-utils.ts +++ b/packages/typescript-estree/src/node-utils.ts @@ -455,7 +455,7 @@ export function isOptional(node: { */ export function getTokenType( token: ts.Identifier | ts.Token, -): AST_TOKEN_TYPES { +): Exclude { if ('originalKeywordKind' in token && token.originalKeywordKind) { if (token.originalKeywordKind === SyntaxKind.NullKeyword) { return AST_TOKEN_TYPES.Null; @@ -561,21 +561,27 @@ export function convertToken( : token.getStart(ast); const end = token.getEnd(); const value = ast.text.slice(start, end); - const newToken: TSESTree.Token = { - type: getTokenType(token), - value, - range: [start, end], - loc: getLocFor(start, end, ast), - }; - - if (newToken.type === AST_TOKEN_TYPES.RegularExpression) { - newToken.regex = { - pattern: value.slice(1, value.lastIndexOf('/')), - flags: value.slice(value.lastIndexOf('/') + 1), + const tokenType = getTokenType(token); + + if (tokenType === AST_TOKEN_TYPES.RegularExpression) { + return { + type: tokenType, + value, + range: [start, end], + loc: getLocFor(start, end, ast), + regex: { + pattern: value.slice(1, value.lastIndexOf('/')), + flags: value.slice(value.lastIndexOf('/') + 1), + }, + }; + } else { + return { + type: tokenType, + value, + range: [start, end], + loc: getLocFor(start, end, ast), }; } - - return newToken; } /** diff --git a/packages/typescript-estree/src/ts-estree/ts-estree.ts b/packages/typescript-estree/src/ts-estree/ts-estree.ts index d0aa4b17c6c..94d4b7c2440 100644 --- a/packages/typescript-estree/src/ts-estree/ts-estree.ts +++ b/packages/typescript-estree/src/ts-estree/ts-estree.ts @@ -52,20 +52,80 @@ export interface BaseNode { * They are not included in the `Node` union below on purpose because they * are not ever included as part of the standard AST tree. */ - -export interface Token extends BaseNode { - type: AST_TOKEN_TYPES; +interface BaseToken extends BaseNode { value: string; - regex?: { +} + +export interface BooleanToken extends BaseToken { + type: AST_TOKEN_TYPES.Boolean; +} + +export interface IdentifierToken extends BaseToken { + type: AST_TOKEN_TYPES.Identifier; +} + +export interface JSXIdentifierToken extends BaseToken { + type: AST_TOKEN_TYPES.JSXIdentifier; +} + +export interface JSXTextToken extends BaseToken { + type: AST_TOKEN_TYPES.JSXText; +} + +export interface KeywordToken extends BaseToken { + type: AST_TOKEN_TYPES.Keyword; +} + +export interface NullToken extends BaseToken { + type: AST_TOKEN_TYPES.Null; +} + +export interface NumericToken extends BaseToken { + type: AST_TOKEN_TYPES.Numeric; +} + +export interface PunctuatorToken extends BaseToken { + type: AST_TOKEN_TYPES.Punctuator; +} + +export interface RegularExpressionToken extends BaseToken { + type: AST_TOKEN_TYPES.RegularExpression; + regex: { pattern: string; flags: string; }; } -export interface Comment extends BaseNode { - type: 'Line' | 'Block'; - value: string; + +export interface StringToken extends BaseToken { + type: AST_TOKEN_TYPES.String; } +export interface TemplateToken extends BaseToken { + type: AST_TOKEN_TYPES.Template; +} + +export interface BlockComment extends BaseToken { + type: AST_TOKEN_TYPES.Block; +} + +export interface LineComment extends BaseToken { + type: AST_TOKEN_TYPES.Line; +} + +export type Comment = BlockComment | LineComment; +export type Token = + | BooleanToken + | IdentifierToken + | JSXIdentifierToken + | JSXTextToken + | KeywordToken + | NullToken + | NumericToken + | PunctuatorToken + | RegularExpressionToken + | StringToken + | TemplateToken; + export type OptionalRangeAndLoc = Pick< T, Exclude