From 671a7b63018e18f5a1404633adfe94ff16e1bfac Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 25 Jun 2022 20:24:37 -0400 Subject: [PATCH 01/13] feat\!: made BaseNode.parent non-optional --- packages/ast-spec/src/base/BaseNode.ts | 8 ++------ packages/ast-spec/src/index.ts | 1 + packages/ast-spec/src/special/Program/spec.ts | 4 ++-- packages/eslint-plugin/src/rules/array-type.ts | 3 +-- .../src/rules/consistent-type-assertions.ts | 1 - .../src/rules/explicit-module-boundary-types.ts | 2 +- .../src/rules/indent-new-do-not-use/index.ts | 15 +++++---------- packages/eslint-plugin/src/rules/indent.ts | 2 ++ .../eslint-plugin/src/rules/init-declarations.ts | 2 +- .../eslint-plugin/src/rules/no-base-to-string.ts | 5 +---- .../src/rules/no-confusing-void-expression.ts | 2 +- .../eslint-plugin/src/rules/no-explicit-any.ts | 1 - .../src/rules/no-extraneous-class.ts | 5 ++--- .../src/rules/no-invalid-void-type.ts | 7 +------ .../eslint-plugin/src/rules/no-misused-new.ts | 5 +---- packages/eslint-plugin/src/rules/no-namespace.ts | 3 +-- .../src/rules/no-redundant-type-constituents.ts | 1 - .../src/rules/no-use-before-define.ts | 5 +---- .../src/rules/no-useless-constructor.ts | 1 - .../src/rules/prefer-function-type.ts | 9 +-------- .../src/rules/prefer-nullish-coalescing.ts | 2 +- .../src/rules/promise-function-async.ts | 10 +++------- .../src/rules/restrict-template-expressions.ts | 2 +- .../rules/sort-type-union-intersection-members.ts | 2 +- packages/eslint-plugin/src/rules/typedef.ts | 2 +- .../eslint-plugin/src/rules/unified-signatures.ts | 5 ++--- .../src/util/explicitReturnTypeUtils.ts | 2 +- .../eslint-plugin/src/util/getFunctionHeadLoc.ts | 8 ++------ .../scope-manager/src/referencer/VisitorBase.ts | 4 ++-- packages/types/src/ts-estree.ts | 12 ++++-------- packages/typescript-estree/src/convert.ts | 15 ++++++--------- packages/typescript-estree/src/node-utils.ts | 12 +++++++----- packages/utils/src/ts-eslint/Rule.ts | 4 +++- 33 files changed, 58 insertions(+), 104 deletions(-) diff --git a/packages/ast-spec/src/base/BaseNode.ts b/packages/ast-spec/src/base/BaseNode.ts index 797b3d35132..d27ae9a11b7 100644 --- a/packages/ast-spec/src/base/BaseNode.ts +++ b/packages/ast-spec/src/base/BaseNode.ts @@ -3,12 +3,8 @@ import type { AST_NODE_TYPES } from '../ast-node-types'; import type { NodeOrTokenData } from './NodeOrTokenData'; export interface BaseNode extends NodeOrTokenData { - /** - * The parent node of the current node - * - * This is added in the @typescript-eslint/types package as ESLint adds it - * while traversing. - */ + // The parent node of the current node + // (added in @typescript-eslint/types as ESLint adds it while traversing) // parent?: Node; type: AST_NODE_TYPES; diff --git a/packages/ast-spec/src/index.ts b/packages/ast-spec/src/index.ts index ba9ee680000..cbc1380d08c 100644 --- a/packages/ast-spec/src/index.ts +++ b/packages/ast-spec/src/index.ts @@ -1,5 +1,6 @@ export * from './base/Accessibility'; export * from './base/BaseNode'; // this is exported so that the `types` package can merge the decl and add the `parent` property +export * from './base/NodeOrTokenData'; export * from './base/OptionalRangeAndLoc'; export * from './base/Position'; export * from './base/Range'; diff --git a/packages/ast-spec/src/special/Program/spec.ts b/packages/ast-spec/src/special/Program/spec.ts index 81d69e1e604..8d84824f563 100644 --- a/packages/ast-spec/src/special/Program/spec.ts +++ b/packages/ast-spec/src/special/Program/spec.ts @@ -1,10 +1,10 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; -import type { BaseNode } from '../../base/BaseNode'; +import type { NodeOrTokenData } from '../../base/NodeOrTokenData'; import type { Comment } from '../../unions/Comment'; import type { ProgramStatement } from '../../unions/Statement'; import type { Token } from '../../unions/Token'; -export interface Program extends BaseNode { +export interface Program extends NodeOrTokenData { type: AST_NODE_TYPES.Program; body: ProgramStatement[]; sourceType: 'module' | 'script'; diff --git a/packages/eslint-plugin/src/rules/array-type.ts b/packages/eslint-plugin/src/rules/array-type.ts index c84afee195f..069f84709a1 100644 --- a/packages/eslint-plugin/src/rules/array-type.ts +++ b/packages/eslint-plugin/src/rules/array-type.ts @@ -138,7 +138,6 @@ export default util.createRule({ return { TSArrayType(node): void { const isReadonly = - node.parent && node.parent.type === AST_NODE_TYPES.TSTypeOperator && node.parent.operator === 'readonly'; @@ -155,7 +154,7 @@ export default util.createRule({ currentOption === 'generic' ? 'errorStringGeneric' : 'errorStringGenericSimple'; - const errorNode = isReadonly ? node.parent! : node; + const errorNode = isReadonly ? node.parent : node; context.report({ node: errorNode, diff --git a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts index 6626b728afb..dabc836ed08 100644 --- a/packages/eslint-plugin/src/rules/consistent-type-assertions.ts +++ b/packages/eslint-plugin/src/rules/consistent-type-assertions.ts @@ -132,7 +132,6 @@ export default util.createRule({ if ( options.objectLiteralTypeAssertions === 'allow-as-parameter' && - node.parent && (node.parent.type === AST_NODE_TYPES.NewExpression || node.parent.type === AST_NODE_TYPES.CallExpression || node.parent.type === AST_NODE_TYPES.ThrowStatement || diff --git a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts index 3f9f50086cc..8c40436c916 100644 --- a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts +++ b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts @@ -256,7 +256,7 @@ export default util.createRule({ } function isExportedHigherOrderFunction(node: FunctionNode): boolean { - let current = node.parent; + let current: TSESTree.Node | undefined = node.parent; while (current) { if (current.type === AST_NODE_TYPES.ReturnStatement) { // the parent of a return will always be a block statement, so we can skip over it diff --git a/packages/eslint-plugin/src/rules/indent-new-do-not-use/index.ts b/packages/eslint-plugin/src/rules/indent-new-do-not-use/index.ts index fa7013b9837..a9b59100a67 100644 --- a/packages/eslint-plugin/src/rules/indent-new-do-not-use/index.ts +++ b/packages/eslint-plugin/src/rules/indent-new-do-not-use/index.ts @@ -552,7 +552,6 @@ export default createRule({ !!statement && (statement.type === AST_NODE_TYPES.ExpressionStatement || statement.type === AST_NODE_TYPES.VariableDeclaration) && - !!statement.parent && statement.parent.type === AST_NODE_TYPES.Program ); } @@ -933,18 +932,14 @@ export default createRule({ ) { let blockIndentLevel; - if (node.parent && isOuterIIFE(node.parent)) { + if (isOuterIIFE(node.parent)) { blockIndentLevel = options.outerIIFEBody; } else if ( - node.parent && - (node.parent.type === AST_NODE_TYPES.FunctionExpression || - node.parent.type === AST_NODE_TYPES.ArrowFunctionExpression) + node.parent.type === AST_NODE_TYPES.FunctionExpression || + node.parent.type === AST_NODE_TYPES.ArrowFunctionExpression ) { blockIndentLevel = options.FunctionExpression.body; - } else if ( - node.parent && - node.parent.type === AST_NODE_TYPES.FunctionDeclaration - ) { + } else if (node.parent.type === AST_NODE_TYPES.FunctionDeclaration) { blockIndentLevel = options.FunctionDeclaration.body; } else { blockIndentLevel = 1; @@ -954,7 +949,7 @@ export default createRule({ * For blocks that aren't lone statements, ensure that the opening curly brace * is aligned with the parent. */ - if (node.parent && !STATEMENT_LIST_PARENTS.has(node.parent.type)) { + if (!STATEMENT_LIST_PARENTS.has(node.parent.type)) { offsets.setDesiredOffset( sourceCode.getFirstToken(node)!, sourceCode.getFirstToken(node.parent), diff --git a/packages/eslint-plugin/src/rules/indent.ts b/packages/eslint-plugin/src/rules/indent.ts index 748d720f451..2f512409ea0 100644 --- a/packages/eslint-plugin/src/rules/indent.ts +++ b/packages/eslint-plugin/src/rules/indent.ts @@ -208,6 +208,7 @@ export default util.createRule({ return rules.ConditionalExpression({ type: AST_NODE_TYPES.ConditionalExpression, test: { + parent: node, type: AST_NODE_TYPES.BinaryExpression, operator: 'extends', left: node.checkType as any, @@ -369,6 +370,7 @@ export default util.createRule({ type: AST_NODE_TYPES.ObjectExpression, properties: [ { + parent: node, type: AST_NODE_TYPES.Property, key: node.typeParameter as any, value: node.typeAnnotation as any, diff --git a/packages/eslint-plugin/src/rules/init-declarations.ts b/packages/eslint-plugin/src/rules/init-declarations.ts index df20ff572a2..19d312f2d50 100644 --- a/packages/eslint-plugin/src/rules/init-declarations.ts +++ b/packages/eslint-plugin/src/rules/init-declarations.ts @@ -47,7 +47,7 @@ export default createRule({ function isAncestorNamespaceDeclared( node: TSESTree.VariableDeclaration, ): boolean { - let ancestor = node.parent; + let ancestor: TSESTree.Node | undefined = node.parent; while (ancestor) { if ( diff --git a/packages/eslint-plugin/src/rules/no-base-to-string.ts b/packages/eslint-plugin/src/rules/no-base-to-string.ts index 3cb099e7cfc..500512d8919 100644 --- a/packages/eslint-plugin/src/rules/no-base-to-string.ts +++ b/packages/eslint-plugin/src/rules/no-base-to-string.ts @@ -177,10 +177,7 @@ export default util.createRule({ checkExpression(memberExpr.object); }, TemplateLiteral(node: TSESTree.TemplateLiteral): void { - if ( - node.parent && - node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression - ) { + if (node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) { return; } for (const expression of node.expressions) { diff --git a/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts b/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts index 15b152c0002..139bdbbf9fd 100644 --- a/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts +++ b/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts @@ -184,7 +184,7 @@ export default util.createRule({ // put a semicolon at the beginning of the line newReturnStmtText = `;${newReturnStmtText}`; } - if (returnStmt.parent?.type !== AST_NODE_TYPES.BlockStatement) { + if (returnStmt.parent.type !== AST_NODE_TYPES.BlockStatement) { // e.g. `if (cond) return console.error();` // add braces if not inside a block newReturnStmtText = `{ ${newReturnStmtText} }`; diff --git a/packages/eslint-plugin/src/rules/no-explicit-any.ts b/packages/eslint-plugin/src/rules/no-explicit-any.ts index e0e8bfd2b9c..6a4df9a97e0 100644 --- a/packages/eslint-plugin/src/rules/no-explicit-any.ts +++ b/packages/eslint-plugin/src/rules/no-explicit-any.ts @@ -79,7 +79,6 @@ export default util.createRule({ function isNodeRestElementInFunction(node: TSESTree.Node): boolean { return ( node.type === AST_NODE_TYPES.RestElement && - typeof node.parent !== 'undefined' && isNodeValidFunction(node.parent) ); } diff --git a/packages/eslint-plugin/src/rules/no-extraneous-class.ts b/packages/eslint-plugin/src/rules/no-extraneous-class.ts index f280780ef89..94697e090d6 100644 --- a/packages/eslint-plugin/src/rules/no-extraneous-class.ts +++ b/packages/eslint-plugin/src/rules/no-extraneous-class.ts @@ -72,10 +72,9 @@ export default util.createRule({ ClassBody(node): void { const parent = node.parent as | TSESTree.ClassDeclaration - | TSESTree.ClassExpression - | undefined; + | TSESTree.ClassExpression; - if (!parent || parent.superClass || isAllowWithDecorator(parent)) { + if (parent.superClass || isAllowWithDecorator(parent)) { return; } diff --git a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts index a6e798217ca..c37b763e0af 100644 --- a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts +++ b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts @@ -146,11 +146,6 @@ export default util.createRule<[Options], MessageIds>({ return { TSVoidKeyword(node: TSESTree.TSVoidKeyword): void { - /* istanbul ignore next */ - if (!node.parent?.parent) { - return; - } - // checks T<..., void, ...> against specification of allowInGenericArguments option if ( node.parent.type === AST_NODE_TYPES.TSTypeParameterInstantiation && @@ -181,7 +176,7 @@ export default util.createRule<[Options], MessageIds>({ // default cases if ( validParents.includes(node.parent.type) && - !invalidGrandParents.includes(node.parent.parent.type) + !invalidGrandParents.includes(node.parent.parent!.type) ) { return; } diff --git a/packages/eslint-plugin/src/rules/no-misused-new.ts b/packages/eslint-plugin/src/rules/no-misused-new.ts index da5f034c965..1f361e9bc51 100644 --- a/packages/eslint-plugin/src/rules/no-misused-new.ts +++ b/packages/eslint-plugin/src/rules/no-misused-new.ts @@ -91,10 +91,7 @@ export default util.createRule({ node: TSESTree.MethodDefinition, ): void { if (node.value.type === AST_NODE_TYPES.TSEmptyBodyFunctionExpression) { - if ( - node.parent && - isMatchingParentType(node.parent.parent, node.value.returnType) - ) { + if (isMatchingParentType(node.parent.parent, node.value.returnType)) { context.report({ node, messageId: 'errorMessageClass', diff --git a/packages/eslint-plugin/src/rules/no-namespace.ts b/packages/eslint-plugin/src/rules/no-namespace.ts index 2a840fda314..84ef6722629 100644 --- a/packages/eslint-plugin/src/rules/no-namespace.ts +++ b/packages/eslint-plugin/src/rules/no-namespace.ts @@ -61,8 +61,7 @@ export default util.createRule({ node: TSESTree.TSModuleDeclaration, ): void { if ( - (node.parent && - node.parent.type === AST_NODE_TYPES.TSModuleDeclaration) || + node.parent.type === AST_NODE_TYPES.TSModuleDeclaration || (allowDefinitionFiles && util.isDefinitionFile(filename)) || (allowDeclarations && isDeclaration(node)) ) { diff --git a/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts b/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts index 1ee8c3c4959..a5d0dac2830 100644 --- a/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts +++ b/packages/eslint-plugin/src/rules/no-redundant-type-constituents.ts @@ -154,7 +154,6 @@ function describeLiteralTypeNode(typeNode: TSESTree.TypeNode): string { function isNodeInsideReturnType(node: TSESTree.TSUnionType): boolean { return !!( node.parent?.type === AST_NODE_TYPES.TSTypeAnnotation && - node.parent.parent && (util.isFunctionType(node.parent.parent) || util.isFunction(node.parent.parent)) ); diff --git a/packages/eslint-plugin/src/rules/no-use-before-define.ts b/packages/eslint-plugin/src/rules/no-use-before-define.ts index 3a5b51938b7..ed820296a06 100644 --- a/packages/eslint-plugin/src/rules/no-use-before-define.ts +++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts @@ -100,9 +100,6 @@ function referenceContainsTypeQuery(node: TSESTree.Node): boolean { case AST_NODE_TYPES.TSQualifiedName: case AST_NODE_TYPES.Identifier: - if (!node.parent) { - return false; - } return referenceContainsTypeQuery(node.parent); default: @@ -180,7 +177,7 @@ function isInInitializer( return false; } - let node = variable.identifiers[0].parent; + let node: TSESTree.Node | undefined = variable.identifiers[0].parent; const location = reference.identifier.range[1]; while (node) { diff --git a/packages/eslint-plugin/src/rules/no-useless-constructor.ts b/packages/eslint-plugin/src/rules/no-useless-constructor.ts index 10e55cfbe0e..c8736ce0a44 100644 --- a/packages/eslint-plugin/src/rules/no-useless-constructor.ts +++ b/packages/eslint-plugin/src/rules/no-useless-constructor.ts @@ -17,7 +17,6 @@ function checkAccessibility(node: TSESTree.MethodDefinition): boolean { return false; case 'public': if ( - node.parent && node.parent.type === AST_NODE_TYPES.ClassBody && node.parent.parent && 'superClass' in node.parent.parent && diff --git a/packages/eslint-plugin/src/rules/prefer-function-type.ts b/packages/eslint-plugin/src/rules/prefer-function-type.ts index b5d2875e01c..a784b953bd4 100644 --- a/packages/eslint-plugin/src/rules/prefer-function-type.ts +++ b/packages/eslint-plugin/src/rules/prefer-function-type.ts @@ -102,7 +102,6 @@ export default util.createRule({ } const fixable = - node.parent && node.parent.type === AST_NODE_TYPES.ExportDefaultDeclaration; const fix = fixable @@ -140,7 +139,6 @@ export default util.createRule({ } const isParentExported = - node.parent && node.parent.type === AST_NODE_TYPES.ExportNamedDeclaration; if ( @@ -157,12 +155,7 @@ export default util.createRule({ ); }, ''); // comments should move before export and not between export and interface declaration - fixes.push( - fixer.insertTextBefore( - node.parent as TSESTree.Node | TSESTree.Token, - commentsText, - ), - ); + fixes.push(fixer.insertTextBefore(node.parent, commentsText)); } else { comments.forEach(comment => { let commentText = diff --git a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts index c1676d3fd32..4124caac832 100644 --- a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts +++ b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts @@ -93,7 +93,7 @@ export default util.createRule({ function* fix( fixer: TSESLint.RuleFixer, ): IterableIterator { - if (node.parent && util.isLogicalOrOperator(node.parent)) { + if (util.isLogicalOrOperator(node.parent)) { // '&&' and '??' operations cannot be mixed without parentheses (e.g. a && b ?? c) if ( node.left.type === AST_NODE_TYPES.LogicalExpression && diff --git a/packages/eslint-plugin/src/rules/promise-function-async.ts b/packages/eslint-plugin/src/rules/promise-function-async.ts index b1ade7bf01b..f72eb8005af 100644 --- a/packages/eslint-plugin/src/rules/promise-function-async.ts +++ b/packages/eslint-plugin/src/rules/promise-function-async.ts @@ -119,13 +119,12 @@ export default util.createRule({ return; } - if (node.parent?.type === AST_NODE_TYPES.TSAbstractMethodDefinition) { + if (node.parent.type === AST_NODE_TYPES.TSAbstractMethodDefinition) { // Abstract method can't be async return; } if ( - node.parent && (node.parent.type === AST_NODE_TYPES.Property || node.parent.type === AST_NODE_TYPES.MethodDefinition) && (node.parent.kind === 'get' || node.parent.kind === 'set') @@ -151,10 +150,8 @@ export default util.createRule({ loc: util.getFunctionHeadLoc(node, sourceCode), fix: fixer => { if ( - node.parent && - (node.parent.type === AST_NODE_TYPES.MethodDefinition || - (node.parent.type === AST_NODE_TYPES.Property && - node.parent.method)) + node.parent.type === AST_NODE_TYPES.MethodDefinition || + (node.parent.type === AST_NODE_TYPES.Property && node.parent.method) ) { // this function is a class method or object function property shorthand const method = node.parent; @@ -214,7 +211,6 @@ export default util.createRule({ node: TSESTree.FunctionExpression, ): void { if ( - node.parent && node.parent.type === AST_NODE_TYPES.MethodDefinition && node.parent.kind === 'method' ) { diff --git a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts index 0140c3dc0d4..6567f923738 100644 --- a/packages/eslint-plugin/src/rules/restrict-template-expressions.ts +++ b/packages/eslint-plugin/src/rules/restrict-template-expressions.ts @@ -95,7 +95,7 @@ export default util.createRule({ return { TemplateLiteral(node: TSESTree.TemplateLiteral): void { // don't check tagged template literals - if (node.parent!.type === AST_NODE_TYPES.TaggedTemplateExpression) { + if (node.parent.type === AST_NODE_TYPES.TaggedTemplateExpression) { return; } diff --git a/packages/eslint-plugin/src/rules/sort-type-union-intersection-members.ts b/packages/eslint-plugin/src/rules/sort-type-union-intersection-members.ts index f96c4677506..e2ad4d23317 100644 --- a/packages/eslint-plugin/src/rules/sort-type-union-intersection-members.ts +++ b/packages/eslint-plugin/src/rules/sort-type-union-intersection-members.ts @@ -204,7 +204,7 @@ export default util.createRule({ ? 'Intersection' : 'Union', }; - if (node.parent?.type === AST_NODE_TYPES.TSTypeAliasDeclaration) { + if (node.parent.type === AST_NODE_TYPES.TSTypeAliasDeclaration) { messageId = 'notSortedNamed'; data.name = node.parent.id.name; } diff --git a/packages/eslint-plugin/src/rules/typedef.ts b/packages/eslint-plugin/src/rules/typedef.ts index a995fe41b37..b6bc0781616 100644 --- a/packages/eslint-plugin/src/rules/typedef.ts +++ b/packages/eslint-plugin/src/rules/typedef.ts @@ -152,7 +152,7 @@ export default util.createRule<[Options], MessageIds>({ function isAncestorHasTypeAnnotation( node: TSESTree.ObjectPattern, ): boolean { - let ancestor = node.parent; + let ancestor: TSESTree.Node | undefined = node.parent; while (ancestor) { if ( diff --git a/packages/eslint-plugin/src/rules/unified-signatures.ts b/packages/eslint-plugin/src/rules/unified-signatures.ts index dc1f683ee35..3ce722da24e 100644 --- a/packages/eslint-plugin/src/rules/unified-signatures.ts +++ b/packages/eslint-plugin/src/rules/unified-signatures.ts @@ -584,9 +584,8 @@ function getExportingNode( | TSESTree.ExportNamedDeclaration | TSESTree.ExportDefaultDeclaration | undefined { - return node.parent && - (node.parent.type === AST_NODE_TYPES.ExportNamedDeclaration || - node.parent.type === AST_NODE_TYPES.ExportDefaultDeclaration) + return node.parent.type === AST_NODE_TYPES.ExportNamedDeclaration || + node.parent.type === AST_NODE_TYPES.ExportDefaultDeclaration ? node.parent : undefined; } diff --git a/packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts b/packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts index 4d4ec6023a7..f109e47456d 100644 --- a/packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts +++ b/packages/eslint-plugin/src/util/explicitReturnTypeUtils.ts @@ -297,7 +297,7 @@ function checkFunctionExpressionReturnType( * Check whether any ancestor of the provided function has a valid return type. */ function ancestorHasReturnType(node: FunctionNode): boolean { - let ancestor = node.parent; + let ancestor: TSESTree.Node | undefined = node.parent; if (ancestor?.type === AST_NODE_TYPES.Property) { ancestor = ancestor.value; diff --git a/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts b/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts index a9d30838d2d..eccb18a7666 100644 --- a/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts +++ b/packages/eslint-plugin/src/util/getFunctionHeadLoc.ts @@ -32,7 +32,7 @@ export function getFunctionHeadLoc( sourceCode: TSESLint.SourceCode, ): TSESTree.SourceLocation { function getLocStart(): TSESTree.Position { - if (node.parent && node.parent.type === AST_NODE_TYPES.MethodDefinition) { + if (node.parent.type === AST_NODE_TYPES.MethodDefinition) { // return the start location for class method if (node.parent.decorators && node.parent.decorators.length > 0) { @@ -45,11 +45,7 @@ export function getFunctionHeadLoc( return node.parent.loc.start; } - if ( - node.parent && - node.parent.type === AST_NODE_TYPES.Property && - node.parent.method - ) { + if (node.parent.type === AST_NODE_TYPES.Property && node.parent.method) { // return the start location for object method shorthand return node.parent.loc.start; } diff --git a/packages/scope-manager/src/referencer/VisitorBase.ts b/packages/scope-manager/src/referencer/VisitorBase.ts index 8e06863a229..8bae2e7012a 100644 --- a/packages/scope-manager/src/referencer/VisitorBase.ts +++ b/packages/scope-manager/src/referencer/VisitorBase.ts @@ -35,11 +35,11 @@ abstract class VisitorBase { node: T | null | undefined, excludeArr: (keyof T)[] = [], ): void { - if (node == null || node.type == null) { + if (node?.type == null) { return; } - const exclude = new Set(excludeArr.concat(['parent'])) as Set; + const exclude = new Set([...excludeArr, 'parent'] as string[]); const children = this.#childVisitorKeys[node.type] ?? Object.keys(node); for (const key of children) { if (exclude.has(key)) { diff --git a/packages/types/src/ts-estree.ts b/packages/types/src/ts-estree.ts index 5f46887c44a..25ff8072ca8 100644 --- a/packages/types/src/ts-estree.ts +++ b/packages/types/src/ts-estree.ts @@ -2,20 +2,16 @@ import * as TSESTree from './generated/ast-spec'; // augment to add the parent property, which isn't part of the spec declare module './generated/ast-spec' { - interface BaseNode { - parent?: TSESTree.Node; - } - - // TODO - make this change as a breaking change - /* interface BaseNode { parent: TSESTree.Node; } interface Program { - parent?: undefined; + /** + * @remarks This never-used property exists only as a convenience for code that tries to access node parents repeatedly. + */ + parent?: never; } - */ } export * as TSESTree from './generated/ast-spec'; diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 864f85d2cd2..cc7fe0daae7 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -250,16 +250,13 @@ export class Converter { } private createNode( - node: TSESTreeToTSNode, - data: TSESTree.OptionalRangeAndLoc, + // The 'parent' property will be added later if specified + node: Omit, 'parent'>, + data: Omit, 'parent'>, ): T { const result = data; if (!result.range) { - result.range = getRange( - // this is completely valid, but TS hates it - node as never, - this.ast, - ); + result.range = getRange(node, this.ast); } if (!result.loc) { result.loc = getLocFor(result.range[0], result.range[1], this.ast); @@ -311,7 +308,7 @@ export class Converter { loc, range: [annotationStartCol, child.end], typeAnnotation: this.convertType(child), - }; + } as TSESTree.TSTypeAnnotation; } /** @@ -390,7 +387,7 @@ export class Converter { params: typeParameters.map(typeParameter => this.convertType(typeParameter), ), - }; + } as TSESTree.TSTypeParameterDeclaration; } /** diff --git a/packages/typescript-estree/src/node-utils.ts b/packages/typescript-estree/src/node-utils.ts index 132971f6632..88dc1d8207e 100644 --- a/packages/typescript-estree/src/node-utils.ts +++ b/packages/typescript-estree/src/node-utils.ts @@ -223,7 +223,10 @@ export function canContainDirective( * @param ast the AST object * @returns the range data */ -export function getRange(node: ts.Node, ast: ts.SourceFile): [number, number] { +export function getRange( + node: Pick, + ast: ts.SourceFile, +): [number, number] { return [node.getStart(ast), node.getEnd()]; } @@ -485,9 +488,8 @@ export function getTokenType( // A TypeScript-StringLiteral token with a TypeScript-JsxAttribute or TypeScript-JsxElement parent, // must actually be an ESTree-JSXText token if ( - token.parent && - (token.parent.kind === SyntaxKind.JsxAttribute || - token.parent.kind === SyntaxKind.JsxElement) + token.parent.kind === SyntaxKind.JsxAttribute || + token.parent.kind === SyntaxKind.JsxElement ) { return AST_TOKEN_TYPES.JSXText; } @@ -507,7 +509,7 @@ export function getTokenType( } // Some JSX tokens have to be determined based on their parent - if (token.parent && token.kind === SyntaxKind.Identifier) { + if (token.kind === SyntaxKind.Identifier) { if (isJSXToken(token.parent)) { return AST_TOKEN_TYPES.JSXIdentifier; } diff --git a/packages/utils/src/ts-eslint/Rule.ts b/packages/utils/src/ts-eslint/Rule.ts index 25d29bddf11..73b6be66baa 100644 --- a/packages/utils/src/ts-eslint/Rule.ts +++ b/packages/utils/src/ts-eslint/Rule.ts @@ -264,7 +264,9 @@ interface RuleContext< // This isn't the correct signature, but it makes it easier to do custom unions within reusable listeners // never will break someone's code unless they specifically type the function argument -type RuleFunction = (node: T) => void; +type RuleFunction = ( + node: T, +) => void; interface RuleListener { [nodeSelector: string]: RuleFunction | undefined; From 946894b29ad49d781dea8a7ee4420edcb6c0d25a Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 25 Jun 2022 20:35:57 -0400 Subject: [PATCH 02/13] Also fixed up for non-null assertions now being redundant --- .../src/rules/consistent-generic-constructors.ts | 4 ++-- packages/eslint-plugin/src/rules/no-misused-new.ts | 2 +- packages/eslint-plugin/src/rules/no-redeclare.ts | 2 +- packages/eslint-plugin/src/rules/no-unused-vars.ts | 4 ++-- packages/eslint-plugin/src/rules/no-useless-constructor.ts | 1 - packages/eslint-plugin/src/rules/prefer-as-const.ts | 2 +- packages/eslint-plugin/src/rules/prefer-return-this-type.ts | 4 ++-- .../eslint-plugin/src/rules/require-array-sort-compare.ts | 2 +- .../eslint-plugin/src/rules/space-before-function-paren.ts | 2 +- packages/eslint-plugin/src/util/collectUnusedVariables.ts | 6 +++--- packages/eslint-plugin/src/util/getWrappingFixer.ts | 2 +- 11 files changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts index 0df1412bd7b..f456618cf9d 100644 --- a/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts +++ b/packages/eslint-plugin/src/rules/consistent-generic-constructors.ts @@ -71,7 +71,7 @@ export default createRule({ const hasParens = sourceCode.getTokenAfter(rhs.callee)?.value === '('; const extraComments = new Set( - sourceCode.getCommentsInside(lhs.parent!), + sourceCode.getCommentsInside(lhs.parent), ); sourceCode .getCommentsInside(lhs.typeParameters) @@ -80,7 +80,7 @@ export default createRule({ node, messageId: 'preferConstructor', *fix(fixer) { - yield fixer.remove(lhs.parent!); + yield fixer.remove(lhs.parent); for (const comment of extraComments) { yield fixer.insertTextAfter( rhs.callee, diff --git a/packages/eslint-plugin/src/rules/no-misused-new.ts b/packages/eslint-plugin/src/rules/no-misused-new.ts index 1f361e9bc51..110cfb85084 100644 --- a/packages/eslint-plugin/src/rules/no-misused-new.ts +++ b/packages/eslint-plugin/src/rules/no-misused-new.ts @@ -68,7 +68,7 @@ export default util.createRule({ ): void { if ( isMatchingParentType( - node.parent!.parent as TSESTree.TSInterfaceDeclaration, + node.parent.parent as TSESTree.TSInterfaceDeclaration, node.returnType, ) ) { diff --git a/packages/eslint-plugin/src/rules/no-redeclare.ts b/packages/eslint-plugin/src/rules/no-redeclare.ts index d81c5225d08..f9d37a28fa1 100644 --- a/packages/eslint-plugin/src/rules/no-redeclare.ts +++ b/packages/eslint-plugin/src/rules/no-redeclare.ts @@ -101,7 +101,7 @@ export default util.createRule({ const identifiers = variable.identifiers .map(id => ({ identifier: id, - parent: id.parent!, + parent: id.parent, })) // ignore function declarations because TS will treat them as an overload .filter( diff --git a/packages/eslint-plugin/src/rules/no-unused-vars.ts b/packages/eslint-plugin/src/rules/no-unused-vars.ts index 00956e9daf9..0d93fe13aee 100644 --- a/packages/eslint-plugin/src/rules/no-unused-vars.ts +++ b/packages/eslint-plugin/src/rules/no-unused-vars.ts @@ -165,10 +165,10 @@ export default util.createRule({ ): boolean { if (options.ignoreRestSiblings) { const hasRestSiblingDefinition = variable.defs.some(def => - hasRestSibling(def.name.parent!), + hasRestSibling(def.name.parent), ); const hasRestSiblingReference = variable.references.some(ref => - hasRestSibling(ref.identifier.parent!), + hasRestSibling(ref.identifier.parent), ); return hasRestSiblingDefinition || hasRestSiblingReference; diff --git a/packages/eslint-plugin/src/rules/no-useless-constructor.ts b/packages/eslint-plugin/src/rules/no-useless-constructor.ts index c8736ce0a44..6522ce9b4fd 100644 --- a/packages/eslint-plugin/src/rules/no-useless-constructor.ts +++ b/packages/eslint-plugin/src/rules/no-useless-constructor.ts @@ -18,7 +18,6 @@ function checkAccessibility(node: TSESTree.MethodDefinition): boolean { case 'public': if ( node.parent.type === AST_NODE_TYPES.ClassBody && - node.parent.parent && 'superClass' in node.parent.parent && node.parent.parent.superClass ) { diff --git a/packages/eslint-plugin/src/rules/prefer-as-const.ts b/packages/eslint-plugin/src/rules/prefer-as-const.ts index ca6b9921656..bc1397fc968 100644 --- a/packages/eslint-plugin/src/rules/prefer-as-const.ts +++ b/packages/eslint-plugin/src/rules/prefer-as-const.ts @@ -48,7 +48,7 @@ export default util.createRule({ { messageId: 'variableSuggest', fix: (fixer): TSESLint.RuleFix[] => [ - fixer.remove(typeNode.parent!), + fixer.remove(typeNode.parent), fixer.insertTextAfter(valueNode, ' as const'), ], }, diff --git a/packages/eslint-plugin/src/rules/prefer-return-this-type.ts b/packages/eslint-plugin/src/rules/prefer-return-this-type.ts index 0766d98efbd..da333aa5709 100644 --- a/packages/eslint-plugin/src/rules/prefer-return-this-type.ts +++ b/packages/eslint-plugin/src/rules/prefer-return-this-type.ts @@ -149,7 +149,7 @@ export default createRule({ return { 'ClassBody > MethodDefinition'(node: TSESTree.MethodDefinition): void { - checkFunction(node.value, node.parent!.parent as ClassLikeDeclaration); + checkFunction(node.value, node.parent.parent as ClassLikeDeclaration); }, 'ClassBody > PropertyDefinition'( node: TSESTree.PropertyDefinition, @@ -163,7 +163,7 @@ export default createRule({ return; } - checkFunction(node.value, node.parent!.parent as ClassLikeDeclaration); + checkFunction(node.value, node.parent.parent as ClassLikeDeclaration); }, }; }, diff --git a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts index 5354c87afa6..4c6f03594ee 100644 --- a/packages/eslint-plugin/src/rules/require-array-sort-compare.ts +++ b/packages/eslint-plugin/src/rules/require-array-sort-compare.ts @@ -75,7 +75,7 @@ export default util.createRule({ } if (util.isTypeArrayTypeOrUnionOfArrayTypes(calleeObjType, checker)) { - context.report({ node: callee.parent!, messageId: 'requireCompare' }); + context.report({ node: callee.parent, messageId: 'requireCompare' }); } }, }; diff --git a/packages/eslint-plugin/src/rules/space-before-function-paren.ts b/packages/eslint-plugin/src/rules/space-before-function-paren.ts index 2b34036b832..0cb6918eb78 100644 --- a/packages/eslint-plugin/src/rules/space-before-function-paren.ts +++ b/packages/eslint-plugin/src/rules/space-before-function-paren.ts @@ -77,7 +77,7 @@ export default util.createRule({ return true; } - const parent = node.parent!; + const parent = node.parent; return ( parent.type === AST_NODE_TYPES.MethodDefinition || diff --git a/packages/eslint-plugin/src/util/collectUnusedVariables.ts b/packages/eslint-plugin/src/util/collectUnusedVariables.ts index 6fe14b0d272..a9efa23962e 100644 --- a/packages/eslint-plugin/src/util/collectUnusedVariables.ts +++ b/packages/eslint-plugin/src/util/collectUnusedVariables.ts @@ -321,7 +321,7 @@ class UnusedVarsVisitor< protected TSModuleDeclaration(node: TSESTree.TSModuleDeclaration): void { // -- global augmentation can be in any file, and they do not need exports if (node.global === true) { - this.markVariableAsUsed('global', node.parent!); + this.markVariableAsUsed('global', node.parent); } } @@ -561,7 +561,7 @@ function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { } const id = ref.identifier; - const parent = id.parent!; + const parent = id.parent; const grandparent = parent.parent!; const refScope = ref.from.variableScope; const varScope = ref.resolved!.scope.variableScope; @@ -694,7 +694,7 @@ function isUsedVariable(variable: TSESLint.Scope.Variable): boolean { } const id = ref.identifier; - const parent = id.parent!; + const parent = id.parent; const grandparent = parent.parent!; return ( diff --git a/packages/eslint-plugin/src/util/getWrappingFixer.ts b/packages/eslint-plugin/src/util/getWrappingFixer.ts index af7885efc41..d74088076e1 100644 --- a/packages/eslint-plugin/src/util/getWrappingFixer.ts +++ b/packages/eslint-plugin/src/util/getWrappingFixer.ts @@ -140,7 +140,7 @@ function isMissingSemicolonBefore( const parent = node.parent!; if (parent.type === AST_NODE_TYPES.ExpressionStatement) { - const block = parent.parent!; + const block = parent.parent; if ( block.type === AST_NODE_TYPES.Program || block.type === AST_NODE_TYPES.BlockStatement From 3c9951b82e52e9d8298d75646fd02432a5871b51 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 25 Jun 2022 20:36:15 -0400 Subject: [PATCH 03/13] Also fixed up for non-null assertions now being redundant (again) --- packages/eslint-plugin/src/rules/strict-boolean-expressions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts index 5493e84d271..5a4584aa89c 100644 --- a/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts +++ b/packages/eslint-plugin/src/rules/strict-boolean-expressions.ts @@ -474,7 +474,7 @@ export default util.createRule({ if (is('number') || is('truthy number')) { if (!options.allowNumber) { if (isArrayLengthExpression(node, typeChecker, parserServices)) { - if (isLogicalNegationExpression(node.parent!)) { + if (isLogicalNegationExpression(node.parent)) { // if (!array.length) context.report({ node, From 4d55ee69cc27bf3aaccb74d30a9dafa70b2af369 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 25 Oct 2022 19:38:26 -0400 Subject: [PATCH 04/13] Fix lint: value type --- packages/website/src/components/ASTViewerESTree.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/website/src/components/ASTViewerESTree.tsx b/packages/website/src/components/ASTViewerESTree.tsx index 29b5550cd14..7f7f0cd2bb7 100644 --- a/packages/website/src/components/ASTViewerESTree.tsx +++ b/packages/website/src/components/ASTViewerESTree.tsx @@ -7,7 +7,7 @@ import { createESTreeSerializer } from './ast/serializer/serializerESTree'; import type { ASTViewerBaseProps } from './ast/types'; export interface ASTESTreeViewerProps extends ASTViewerBaseProps { - readonly value: TSESTree.BaseNode; + readonly value: TSESTree.BaseNode | TSESTree.Program; } export default function ASTViewerESTree({ From 0414e4d56e6386048170fd71b2255e6cc7c974f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Wed, 26 Oct 2022 01:59:38 +0200 Subject: [PATCH 05/13] fix(utils): removed `TRuleListener` generic from the `createRule` (#5036) * refactor(utils)!: removed `TRuleListener` generic from the `createRule` * refactor!: removed `TRuleListener` generic from the `CLIEngine` and `RuleCreateFunction` * chore: document and refactor 'extra' to 'parserSettings' (#5834) * chore(website): fix renamed Sponsorship docs link (#5882) * docs: Mention wide globs performance implications in monorepos docs and parser README (#5864) * docs: Mention wide globs performance implications in monorepos docs and parser readme * Update docs/linting/typed-linting/MONOREPOS.md Co-authored-by: Josh Goldberg Co-authored-by: Josh Goldberg Co-authored-by: Adnan Hashmi <56730784+adnanhashmi09@users.noreply.github.com> --- docs/linting/typed-linting/MONOREPOS.md | 27 + packages/parser/README.md | 2 + .../typescript-estree/src/ast-converter.ts | 18 +- .../create-program/createDefaultProgram.ts | 35 +- .../create-program/createIsolatedProgram.ts | 22 +- .../create-program/createProjectProgram.ts | 38 +- .../src/create-program/createSourceFile.ts | 14 +- .../src/create-program/createWatchProgram.ts | 62 +-- .../src/create-program/shared.ts | 17 +- .../src/create-program/useProvidedPrograms.ts | 12 +- .../src/parseSettings/createParseSettings.ts | 201 ++++++++ .../src/parseSettings/index.ts | 124 +++++ .../src/parseSettings/inferSingleRun.ts | 46 ++ .../src/parseSettings/warnAboutTSVersion.ts | 45 ++ .../typescript-estree/src/parser-options.ts | 28 - packages/typescript-estree/src/parser.ts | 487 ++---------------- .../utils/src/eslint-utils/RuleCreator.ts | 27 +- packages/utils/src/ts-eslint/CLIEngine.ts | 6 +- packages/utils/src/ts-eslint/Rule.ts | 4 +- .../FinancialContributors/index.tsx | 2 +- .../src/components/linter/WebLinter.ts | 4 +- .../website/src/components/linter/config.ts | 7 +- 22 files changed, 616 insertions(+), 612 deletions(-) create mode 100644 packages/typescript-estree/src/parseSettings/createParseSettings.ts create mode 100644 packages/typescript-estree/src/parseSettings/index.ts create mode 100644 packages/typescript-estree/src/parseSettings/inferSingleRun.ts create mode 100644 packages/typescript-estree/src/parseSettings/warnAboutTSVersion.ts diff --git a/docs/linting/typed-linting/MONOREPOS.md b/docs/linting/typed-linting/MONOREPOS.md index 1bb057718c6..5163e997ec8 100644 --- a/docs/linting/typed-linting/MONOREPOS.md +++ b/docs/linting/typed-linting/MONOREPOS.md @@ -62,6 +62,33 @@ module.exports = { }; ``` +### Wide globs in `parserOptions.project` + +Using wide globs `**` in your `parserOptions.project` may degrade linting performance. +Instead of globs that use `**` to recursively check all folders, prefer paths that use a single `*` at a time. + +```js title=".eslintrc.js" +module.exports = { + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + tsconfigRootDir: __dirname, + // Remove this line + project: ['./tsconfig.eslint.json', './**/tsconfig.json'], + // Add this line + project: ['./tsconfig.eslint.json', './packages/*/tsconfig.json'], + }, + plugins: ['@typescript-eslint'], + root: true, +}; +``` + +See [Glob pattern in parser's option "project" slows down linting](https://github.com/typescript-eslint/typescript-eslint/issues/2611) for more details. + ### Important note regarding large (> 10) multi-package monorepos We've had reports that for sufficiently large and/or interdependent projects, you may run into OOMs using this approach. diff --git a/packages/parser/README.md b/packages/parser/README.md index efba4854413..b32ed30beab 100644 --- a/packages/parser/README.md +++ b/packages/parser/README.md @@ -164,6 +164,8 @@ This option allows you to provide a path to your project's `tsconfig.json`. **Th - If you use project references, TypeScript will not automatically use project references to resolve files. This means that you will have to add each referenced tsconfig to the `project` field either separately, or via a glob. +- Note that using wide globs `**` in your `parserOptions.project` may cause performance implications. Instead of globs that use `**` to recursively check all folders, prefer paths that use a single `*` at a time. For more info see [#2611](https://github.com/typescript-eslint/typescript-eslint/issues/2611). + - TypeScript will ignore files with duplicate filenames in the same folder (for example, `src/file.ts` and `src/file.js`). TypeScript purposely ignore all but one of the files, only keeping the one file with the highest priority extension (the extension priority order (from highest to lowest) is `.ts`, `.tsx`, `.d.ts`, `.js`, `.jsx`). For more info see #955. - Note that if this setting is specified and `createDefaultProgram` is not, you must only lint files that are included in the projects as defined by the provided `tsconfig.json` files. If your existing configuration does not include all of the files you would like to lint, you can create a separate `tsconfig.eslint.json` as follows: diff --git a/packages/typescript-estree/src/ast-converter.ts b/packages/typescript-estree/src/ast-converter.ts index 86a1970aef7..b9be864f529 100644 --- a/packages/typescript-estree/src/ast-converter.ts +++ b/packages/typescript-estree/src/ast-converter.ts @@ -4,13 +4,13 @@ import type { ASTMaps } from './convert'; import { Converter, convertError } from './convert'; import { convertComments } from './convert-comments'; import { convertTokens } from './node-utils'; -import type { Extra } from './parser-options'; +import type { ParseSettings } from './parseSettings'; import { simpleTraverse } from './simple-traverse'; import type { TSESTree } from './ts-estree'; export function astConverter( ast: SourceFile, - extra: Extra, + parseSettings: ParseSettings, shouldPreserveNodeMaps: boolean, ): { estree: TSESTree.Program; astMaps: ASTMaps } { /** @@ -26,7 +26,7 @@ export function astConverter( * Recursively convert the TypeScript AST into an ESTree-compatible AST */ const instance = new Converter(ast, { - errorOnUnknownASTType: extra.errorOnUnknownASTType || false, + errorOnUnknownASTType: parseSettings.errorOnUnknownASTType || false, shouldPreserveNodeMaps, }); @@ -35,15 +35,15 @@ export function astConverter( /** * Optionally remove range and loc if specified */ - if (!extra.range || !extra.loc) { + if (!parseSettings.range || !parseSettings.loc) { simpleTraverse(estree, { enter: node => { - if (!extra.range) { + if (!parseSettings.range) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment -- TS 4.0 made this an error because the types aren't optional // @ts-expect-error delete node.range; } - if (!extra.loc) { + if (!parseSettings.loc) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment -- TS 4.0 made this an error because the types aren't optional // @ts-expect-error delete node.loc; @@ -55,15 +55,15 @@ export function astConverter( /** * Optionally convert and include all tokens in the AST */ - if (extra.tokens) { + if (parseSettings.tokens) { estree.tokens = convertTokens(ast); } /** * Optionally convert and include all comments in the AST */ - if (extra.comment) { - estree.comments = convertComments(ast, extra.code); + if (parseSettings.comment) { + estree.comments = convertComments(ast, parseSettings.code); } const astMaps = instance.getASTMaps(); diff --git a/packages/typescript-estree/src/create-program/createDefaultProgram.ts b/packages/typescript-estree/src/create-program/createDefaultProgram.ts index bebb194aef5..a2de81399d2 100644 --- a/packages/typescript-estree/src/create-program/createDefaultProgram.ts +++ b/packages/typescript-estree/src/create-program/createDefaultProgram.ts @@ -2,8 +2,8 @@ import debug from 'debug'; import path from 'path'; import * as ts from 'typescript'; -import type { Extra } from '../parser-options'; -import type { ASTAndProgram, CanonicalPath } from './shared'; +import type { ParseSettings } from '../parseSettings'; +import type { ASTAndProgram } from './shared'; import { createDefaultCompilerOptionsFromExtra, getModuleResolver, @@ -12,27 +12,26 @@ import { const log = debug('typescript-eslint:typescript-estree:createDefaultProgram'); /** - * @param code The code of the file being linted - * @param extra The config object - * @param extra.tsconfigRootDir The root directory for relative tsconfig paths - * @param extra.projects Provided tsconfig paths + * @param parseSettings Internal settings for parsing the file * @returns If found, returns the source file corresponding to the code and the containing program */ function createDefaultProgram( - code: string, - extra: Extra, + parseSettings: ParseSettings, ): ASTAndProgram | undefined { - log('Getting default program for: %s', extra.filePath || 'unnamed file'); + log( + 'Getting default program for: %s', + parseSettings.filePath || 'unnamed file', + ); - if (!extra.projects || extra.projects.length !== 1) { + if (parseSettings.projects?.length !== 1) { return undefined; } - const tsconfigPath: CanonicalPath = extra.projects[0]; + const tsconfigPath = parseSettings.projects[0]; const commandLine = ts.getParsedCommandLineOfConfigFile( tsconfigPath, - createDefaultCompilerOptionsFromExtra(extra), + createDefaultCompilerOptionsFromExtra(parseSettings), { ...ts.sys, onUnRecoverableConfigFileDiagnostic: () => {} }, ); @@ -45,24 +44,24 @@ function createDefaultProgram( /* setParentNodes */ true, ); - if (extra.moduleResolver) { + if (parseSettings.moduleResolver) { compilerHost.resolveModuleNames = getModuleResolver( - extra.moduleResolver, + parseSettings.moduleResolver, ).resolveModuleNames; } const oldReadFile = compilerHost.readFile; compilerHost.readFile = (fileName: string): string | undefined => - path.normalize(fileName) === path.normalize(extra.filePath) - ? code + path.normalize(fileName) === path.normalize(parseSettings.filePath) + ? parseSettings.code : oldReadFile(fileName); const program = ts.createProgram( - [extra.filePath], + [parseSettings.filePath], commandLine.options, compilerHost, ); - const ast = program.getSourceFile(extra.filePath); + const ast = program.getSourceFile(parseSettings.filePath); return ast && { ast, program }; } diff --git a/packages/typescript-estree/src/create-program/createIsolatedProgram.ts b/packages/typescript-estree/src/create-program/createIsolatedProgram.ts index ba19b843aeb..5ec1c8e0fe7 100644 --- a/packages/typescript-estree/src/create-program/createIsolatedProgram.ts +++ b/packages/typescript-estree/src/create-program/createIsolatedProgram.ts @@ -1,7 +1,7 @@ import debug from 'debug'; import * as ts from 'typescript'; -import type { Extra } from '../parser-options'; +import type { ParseSettings } from '../parseSettings'; import { getScriptKind } from './getScriptKind'; import type { ASTAndProgram } from './shared'; import { createDefaultCompilerOptionsFromExtra } from './shared'; @@ -12,11 +12,11 @@ const log = debug('typescript-eslint:typescript-estree:createIsolatedProgram'); * @param code The code of the file being linted * @returns Returns a new source file and program corresponding to the linted code */ -function createIsolatedProgram(code: string, extra: Extra): ASTAndProgram { +function createIsolatedProgram(parseSettings: ParseSettings): ASTAndProgram { log( 'Getting isolated program in %s mode for: %s', - extra.jsx ? 'TSX' : 'TS', - extra.filePath, + parseSettings.jsx ? 'TSX' : 'TS', + parseSettings.filePath, ); const compilerHost: ts.CompilerHost = { @@ -24,7 +24,7 @@ function createIsolatedProgram(code: string, extra: Extra): ASTAndProgram { return true; }, getCanonicalFileName() { - return extra.filePath; + return parseSettings.filePath; }, getCurrentDirectory() { return ''; @@ -43,10 +43,10 @@ function createIsolatedProgram(code: string, extra: Extra): ASTAndProgram { getSourceFile(filename: string) { return ts.createSourceFile( filename, - code, + parseSettings.code, ts.ScriptTarget.Latest, /* setParentNodes */ true, - getScriptKind(extra.filePath, extra.jsx), + getScriptKind(parseSettings.filePath, parseSettings.jsx), ); }, readFile() { @@ -61,17 +61,17 @@ function createIsolatedProgram(code: string, extra: Extra): ASTAndProgram { }; const program = ts.createProgram( - [extra.filePath], + [parseSettings.filePath], { noResolve: true, target: ts.ScriptTarget.Latest, - jsx: extra.jsx ? ts.JsxEmit.Preserve : undefined, - ...createDefaultCompilerOptionsFromExtra(extra), + jsx: parseSettings.jsx ? ts.JsxEmit.Preserve : undefined, + ...createDefaultCompilerOptionsFromExtra(parseSettings), }, compilerHost, ); - const ast = program.getSourceFile(extra.filePath); + const ast = program.getSourceFile(parseSettings.filePath); if (!ast) { throw new Error( 'Expected an ast to be returned for the single-file isolated program.', diff --git a/packages/typescript-estree/src/create-program/createProjectProgram.ts b/packages/typescript-estree/src/create-program/createProjectProgram.ts index 9ece5e4a827..326100d3578 100644 --- a/packages/typescript-estree/src/create-program/createProjectProgram.ts +++ b/packages/typescript-estree/src/create-program/createProjectProgram.ts @@ -3,7 +3,7 @@ import path from 'path'; import * as ts from 'typescript'; import { firstDefined } from '../node-utils'; -import type { Extra } from '../parser-options'; +import type { ParseSettings } from '../parseSettings'; import { getProgramsForProjects } from './createWatchProgram'; import type { ASTAndProgram } from './shared'; import { getAstFromProgram } from './shared'; @@ -22,45 +22,37 @@ const DEFAULT_EXTRA_FILE_EXTENSIONS = [ ] as readonly string[]; /** - * @param code The code of the file being linted - * @param createDefaultProgram True if the default program should be created - * @param extra The config object - * @returns If found, returns the source file corresponding to the code and the containing program + * @param parseSettings Internal settings for parsing the file + * @returns If found, the source file corresponding to the code and the containing program */ function createProjectProgram( - code: string, - createDefaultProgram: boolean, - extra: Extra, + parseSettings: ParseSettings, ): ASTAndProgram | undefined { - log('Creating project program for: %s', extra.filePath); + log('Creating project program for: %s', parseSettings.filePath); - const programsForProjects = getProgramsForProjects( - code, - extra.filePath, - extra, - ); + const programsForProjects = getProgramsForProjects(parseSettings); const astAndProgram = firstDefined(programsForProjects, currentProgram => - getAstFromProgram(currentProgram, extra), + getAstFromProgram(currentProgram, parseSettings), ); // The file was either matched within the tsconfig, or we allow creating a default program - if (astAndProgram || createDefaultProgram) { + if (astAndProgram || parseSettings.createDefaultProgram) { return astAndProgram; } const describeFilePath = (filePath: string): string => { const relative = path.relative( - extra.tsconfigRootDir || process.cwd(), + parseSettings.tsconfigRootDir || process.cwd(), filePath, ); - if (extra.tsconfigRootDir) { + if (parseSettings.tsconfigRootDir) { return `/${relative}`; } return `/${relative}`; }; - const describedFilePath = describeFilePath(extra.filePath); - const relativeProjects = extra.projects.map(describeFilePath); + const describedFilePath = describeFilePath(parseSettings.filePath); + const relativeProjects = parseSettings.projects.map(describeFilePath); const describedPrograms = relativeProjects.length === 1 ? relativeProjects[0] @@ -70,7 +62,7 @@ function createProjectProgram( ]; let hasMatchedAnError = false; - const extraFileExtensions = extra.extraFileExtensions || []; + const extraFileExtensions = parseSettings.extraFileExtensions || []; extraFileExtensions.forEach(extraExtension => { if (!extraExtension.startsWith('.')) { @@ -85,7 +77,7 @@ function createProjectProgram( } }); - const fileExtension = path.extname(extra.filePath); + const fileExtension = path.extname(parseSettings.filePath); if (!DEFAULT_EXTRA_FILE_EXTENSIONS.includes(fileExtension)) { const nonStandardExt = `The extension for the file (\`${fileExtension}\`) is non-standard`; if (extraFileExtensions.length > 0) { @@ -105,7 +97,7 @@ function createProjectProgram( if (!hasMatchedAnError) { const [describedInclusions, describedSpecifiers] = - extra.projects.length === 1 + parseSettings.projects.length === 1 ? ['that TSConfig does not', 'that TSConfig'] : ['none of those TSConfigs', 'one of those TSConfigs']; errorLines.push( diff --git a/packages/typescript-estree/src/create-program/createSourceFile.ts b/packages/typescript-estree/src/create-program/createSourceFile.ts index 107f027e46d..806e503f0e4 100644 --- a/packages/typescript-estree/src/create-program/createSourceFile.ts +++ b/packages/typescript-estree/src/create-program/createSourceFile.ts @@ -1,24 +1,24 @@ import debug from 'debug'; import * as ts from 'typescript'; -import type { Extra } from '../parser-options'; +import type { ParseSettings } from '../parseSettings'; import { getScriptKind } from './getScriptKind'; const log = debug('typescript-eslint:typescript-estree:createSourceFile'); -function createSourceFile(code: string, extra: Extra): ts.SourceFile { +function createSourceFile(parseSettings: ParseSettings): ts.SourceFile { log( 'Getting AST without type information in %s mode for: %s', - extra.jsx ? 'TSX' : 'TS', - extra.filePath, + parseSettings.jsx ? 'TSX' : 'TS', + parseSettings.filePath, ); return ts.createSourceFile( - extra.filePath, - code, + parseSettings.filePath, + parseSettings.code, ts.ScriptTarget.Latest, /* setParentNodes */ true, - getScriptKind(extra.filePath, extra.jsx), + getScriptKind(parseSettings.filePath, parseSettings.jsx), ); } diff --git a/packages/typescript-estree/src/create-program/createWatchProgram.ts b/packages/typescript-estree/src/create-program/createWatchProgram.ts index 1511023a97e..0e32f8ec1e5 100644 --- a/packages/typescript-estree/src/create-program/createWatchProgram.ts +++ b/packages/typescript-estree/src/create-program/createWatchProgram.ts @@ -3,7 +3,7 @@ import fs from 'fs'; import semver from 'semver'; import * as ts from 'typescript'; -import type { Extra } from '../parser-options'; +import type { ParseSettings } from '../parseSettings'; import type { CanonicalPath } from './shared'; import { canonicalDirname, @@ -121,40 +121,34 @@ function createHash(content: string): string { function updateCachedFileList( tsconfigPath: CanonicalPath, program: ts.Program, - extra: Extra, + parseSettings: ParseSettings, ): Set { - const fileList = extra.EXPERIMENTAL_useSourceOfProjectReferenceRedirect - ? new Set( - program.getSourceFiles().map(sf => getCanonicalFileName(sf.fileName)), - ) - : new Set(program.getRootFileNames().map(f => getCanonicalFileName(f))); + const fileList = + parseSettings.EXPERIMENTAL_useSourceOfProjectReferenceRedirect + ? new Set( + program.getSourceFiles().map(sf => getCanonicalFileName(sf.fileName)), + ) + : new Set(program.getRootFileNames().map(f => getCanonicalFileName(f))); programFileListCache.set(tsconfigPath, fileList); return fileList; } /** * Calculate project environments using options provided by consumer and paths from config - * @param code The code being linted - * @param filePathIn The path of the file being parsed - * @param extra.tsconfigRootDir The root directory for relative tsconfig paths - * @param extra.projects Provided tsconfig paths + * @param parseSettings Internal settings for parsing the file * @returns The programs corresponding to the supplied tsconfig paths */ -function getProgramsForProjects( - code: string, - filePathIn: string, - extra: Extra, -): ts.Program[] { - const filePath = getCanonicalFileName(filePathIn); +function getProgramsForProjects(parseSettings: ParseSettings): ts.Program[] { + const filePath = getCanonicalFileName(parseSettings.filePath); const results = []; // preserve reference to code and file being linted - currentLintOperationState.code = code; + currentLintOperationState.code = parseSettings.code; currentLintOperationState.filePath = filePath; // Update file version if necessary const fileWatchCallbacks = fileWatchCallbackTrackingMap.get(filePath); - const codeHash = createHash(code); + const codeHash = createHash(parseSettings.code); if ( parsedFilesSeenHash.get(filePath) !== codeHash && fileWatchCallbacks && @@ -174,7 +168,11 @@ function getProgramsForProjects( let updatedProgram: ts.Program | null = null; if (!fileList) { updatedProgram = existingWatch.getProgram().getProgram(); - fileList = updateCachedFileList(tsconfigPath, updatedProgram, extra); + fileList = updateCachedFileList( + tsconfigPath, + updatedProgram, + parseSettings, + ); } if (fileList.has(filePath)) { @@ -198,7 +196,7 @@ function getProgramsForProjects( * - the required program hasn't been created yet, or * - the file is new/renamed, and the program hasn't been updated. */ - for (const tsconfigPath of extra.projects) { + for (const tsconfigPath of parseSettings.projects) { const existingWatch = knownWatchProgramMap.get(tsconfigPath); if (existingWatch) { @@ -218,7 +216,7 @@ function getProgramsForProjects( const fileList = updateCachedFileList( tsconfigPath, updatedProgram, - extra, + parseSettings, ); if (fileList.has(filePath)) { log('Found updated program for file. %s', filePath); @@ -230,7 +228,7 @@ function getProgramsForProjects( continue; } - const programWatch = createWatchProgram(tsconfigPath, extra); + const programWatch = createWatchProgram(tsconfigPath, parseSettings); knownWatchProgramMap.set(tsconfigPath, programWatch); const program = programWatch.getProgram().getProgram(); @@ -238,7 +236,7 @@ function getProgramsForProjects( program.getTypeChecker(); // cache and check the file list - const fileList = updateCachedFileList(tsconfigPath, program, extra); + const fileList = updateCachedFileList(tsconfigPath, program, parseSettings); if (fileList.has(filePath)) { log('Found program for file. %s', filePath); // we can return early because we know this program contains the file @@ -257,23 +255,23 @@ const isRunningNoTimeoutFix = semver.satisfies(ts.version, '>=3.9.0-beta', { function createWatchProgram( tsconfigPath: string, - extra: Extra, + parseSettings: ParseSettings, ): ts.WatchOfConfigFile { log('Creating watch program for %s.', tsconfigPath); // create compiler host const watchCompilerHost = ts.createWatchCompilerHost( tsconfigPath, - createDefaultCompilerOptionsFromExtra(extra), + createDefaultCompilerOptionsFromExtra(parseSettings), ts.sys, ts.createAbstractBuilder, diagnosticReporter, /*reportWatchStatus*/ () => {}, ) as WatchCompilerHostOfConfigFile; - if (extra.moduleResolver) { + if (parseSettings.moduleResolver) { watchCompilerHost.resolveModuleNames = getModuleResolver( - extra.moduleResolver, + parseSettings.moduleResolver, ).resolveModuleNames; } @@ -337,7 +335,9 @@ function createWatchProgram( ): string[] => oldReadDirectory( path, - !extensions ? undefined : extensions.concat(extra.extraFileExtensions), + !extensions + ? undefined + : extensions.concat(parseSettings.extraFileExtensions), exclude, include, depth, @@ -345,7 +345,7 @@ function createWatchProgram( oldOnDirectoryStructureHostCreate(host); }; // This works only on 3.9 - watchCompilerHost.extraFileExtensions = extra.extraFileExtensions.map( + watchCompilerHost.extraFileExtensions = parseSettings.extraFileExtensions.map( extension => ({ extension, isMixedContent: true, @@ -359,7 +359,7 @@ function createWatchProgram( * See https://github.com/typescript-eslint/typescript-eslint/issues/2094 */ watchCompilerHost.useSourceOfProjectReferenceRedirect = (): boolean => - extra.EXPERIMENTAL_useSourceOfProjectReferenceRedirect; + parseSettings.EXPERIMENTAL_useSourceOfProjectReferenceRedirect; // Since we don't want to asynchronously update program we want to disable timeout methods // So any changes in the program will be delayed and updated when getProgram is called on watch diff --git a/packages/typescript-estree/src/create-program/shared.ts b/packages/typescript-estree/src/create-program/shared.ts index 63ba34a5f83..dd50f757dce 100644 --- a/packages/typescript-estree/src/create-program/shared.ts +++ b/packages/typescript-estree/src/create-program/shared.ts @@ -2,7 +2,8 @@ import path from 'path'; import type { Program } from 'typescript'; import * as ts from 'typescript'; -import type { Extra, ModuleResolver } from '../parser-options'; +import type { ModuleResolver } from '../parser-options'; +import type { ParseSettings } from '../parseSettings'; interface ASTAndProgram { ast: ts.SourceFile; @@ -33,9 +34,9 @@ const DEFAULT_COMPILER_OPTIONS: ts.CompilerOptions = { }; function createDefaultCompilerOptionsFromExtra( - extra: Extra, + parseSettings: ParseSettings, ): ts.CompilerOptions { - if (extra.debugLevel.has('typescript')) { + if (parseSettings.debugLevel.has('typescript')) { return { ...DEFAULT_COMPILER_OPTIONS, extendedDiagnostics: true, @@ -63,10 +64,10 @@ function getCanonicalFileName(filePath: string): CanonicalPath { return correctPathCasing(normalized) as CanonicalPath; } -function ensureAbsolutePath(p: string, extra: Extra): string { +function ensureAbsolutePath(p: string, tsconfigRootDir: string): string { return path.isAbsolute(p) ? p - : path.join(extra.tsconfigRootDir || process.cwd(), p); + : path.join(tsconfigRootDir || process.cwd(), p); } function canonicalDirname(p: CanonicalPath): CanonicalPath { @@ -92,12 +93,12 @@ function getExtension(fileName: string | undefined): string | null { function getAstFromProgram( currentProgram: Program, - extra: Extra, + parseSettings: ParseSettings, ): ASTAndProgram | undefined { - const ast = currentProgram.getSourceFile(extra.filePath); + const ast = currentProgram.getSourceFile(parseSettings.filePath); // working around https://github.com/typescript-eslint/typescript-eslint/issues/1573 - const expectedExt = getExtension(extra.filePath); + const expectedExt = getExtension(parseSettings.filePath); const returnedExt = getExtension(ast?.fileName); if (expectedExt !== returnedExt) { return undefined; diff --git a/packages/typescript-estree/src/create-program/useProvidedPrograms.ts b/packages/typescript-estree/src/create-program/useProvidedPrograms.ts index 0a8300f5ad2..fc99416faa5 100644 --- a/packages/typescript-estree/src/create-program/useProvidedPrograms.ts +++ b/packages/typescript-estree/src/create-program/useProvidedPrograms.ts @@ -3,7 +3,7 @@ import * as fs from 'fs'; import * as path from 'path'; import * as ts from 'typescript'; -import type { Extra } from '../parser-options'; +import type { ParseSettings } from '../parseSettings'; import type { ASTAndProgram } from './shared'; import { CORE_COMPILER_OPTIONS, getAstFromProgram } from './shared'; @@ -11,16 +11,16 @@ const log = debug('typescript-eslint:typescript-estree:useProvidedProgram'); function useProvidedPrograms( programInstances: Iterable, - extra: Extra, + parseSettings: ParseSettings, ): ASTAndProgram | undefined { log( 'Retrieving ast for %s from provided program instance(s)', - extra.filePath, + parseSettings.filePath, ); let astAndProgram: ASTAndProgram | undefined; for (const programInstance of programInstances) { - astAndProgram = getAstFromProgram(programInstance, extra); + astAndProgram = getAstFromProgram(programInstance, parseSettings); // Stop at the first applicable program instance if (astAndProgram) { break; @@ -29,8 +29,8 @@ function useProvidedPrograms( if (!astAndProgram) { const relativeFilePath = path.relative( - extra.tsconfigRootDir || process.cwd(), - extra.filePath, + parseSettings.tsconfigRootDir || process.cwd(), + parseSettings.filePath, ); const errorLines = [ '"parserOptions.programs" has been provided for @typescript-eslint/parser.', diff --git a/packages/typescript-estree/src/parseSettings/createParseSettings.ts b/packages/typescript-estree/src/parseSettings/createParseSettings.ts new file mode 100644 index 00000000000..b1cde9d4c9a --- /dev/null +++ b/packages/typescript-estree/src/parseSettings/createParseSettings.ts @@ -0,0 +1,201 @@ +import debug from 'debug'; +import { sync as globSync } from 'globby'; +import isGlob from 'is-glob'; + +import type { CanonicalPath } from '../create-program/shared'; +import { + ensureAbsolutePath, + getCanonicalFileName, +} from '../create-program/shared'; +import type { TSESTreeOptions } from '../parser-options'; +import type { MutableParseSettings } from './index'; +import { inferSingleRun } from './inferSingleRun'; +import { warnAboutTSVersion } from './warnAboutTSVersion'; + +const log = debug( + 'typescript-eslint:typescript-estree:parser:parseSettings:createParseSettings', +); + +export function createParseSettings( + code: string, + options: Partial = {}, +): MutableParseSettings { + const tsconfigRootDir = + typeof options.tsconfigRootDir === 'string' + ? options.tsconfigRootDir + : process.cwd(); + const parseSettings: MutableParseSettings = { + code: enforceString(code), + comment: options.comment === true, + comments: [], + createDefaultProgram: options.createDefaultProgram === true, + debugLevel: + options.debugLevel === true + ? new Set(['typescript-eslint']) + : Array.isArray(options.debugLevel) + ? new Set(options.debugLevel) + : new Set(), + errorOnTypeScriptSyntacticAndSemanticIssues: false, + errorOnUnknownASTType: options.errorOnUnknownASTType === true, + EXPERIMENTAL_useSourceOfProjectReferenceRedirect: + options.EXPERIMENTAL_useSourceOfProjectReferenceRedirect === true, + extraFileExtensions: + Array.isArray(options.extraFileExtensions) && + options.extraFileExtensions.every(ext => typeof ext === 'string') + ? options.extraFileExtensions + : [], + filePath: ensureAbsolutePath( + typeof options.filePath === 'string' && options.filePath !== '' + ? options.filePath + : getFileName(options.jsx), + tsconfigRootDir, + ), + jsx: options.jsx === true, + loc: options.loc === true, + log: + typeof options.loggerFn === 'function' + ? options.loggerFn + : options.loggerFn === false + ? (): void => {} + : console.log, // eslint-disable-line no-console + moduleResolver: options.moduleResolver ?? '', + preserveNodeMaps: options.preserveNodeMaps !== false, + programs: Array.isArray(options.programs) ? options.programs : null, + projects: [], + range: options.range === true, + singleRun: inferSingleRun(options), + tokens: options.tokens === true ? [] : null, + tsconfigRootDir, + }; + + // debug doesn't support multiple `enable` calls, so have to do it all at once + if (parseSettings.debugLevel.size > 0) { + const namespaces = []; + if (parseSettings.debugLevel.has('typescript-eslint')) { + namespaces.push('typescript-eslint:*'); + } + if ( + parseSettings.debugLevel.has('eslint') || + // make sure we don't turn off the eslint debug if it was enabled via --debug + debug.enabled('eslint:*,-eslint:code-path') + ) { + // https://github.com/eslint/eslint/blob/9dfc8501fb1956c90dc11e6377b4cb38a6bea65d/bin/eslint.js#L25 + namespaces.push('eslint:*,-eslint:code-path'); + } + debug.enable(namespaces.join(',')); + } + + if (Array.isArray(options.programs)) { + if (!options.programs.length) { + throw new Error( + `You have set parserOptions.programs to an empty array. This will cause all files to not be found in existing programs. Either provide one or more existing TypeScript Program instances in the array, or remove the parserOptions.programs setting.`, + ); + } + log( + 'parserOptions.programs was provided, so parserOptions.project will be ignored.', + ); + } + + // Providing a program overrides project resolution + if (!parseSettings.programs) { + const projectFolderIgnoreList = ( + options.projectFolderIgnoreList ?? ['**/node_modules/**'] + ) + .reduce((acc, folder) => { + if (typeof folder === 'string') { + acc.push(folder); + } + return acc; + }, []) + // prefix with a ! for not match glob + .map(folder => (folder.startsWith('!') ? folder : `!${folder}`)); + + parseSettings.projects = prepareAndTransformProjects( + tsconfigRootDir, + options.project, + projectFolderIgnoreList, + ); + } + + warnAboutTSVersion(parseSettings); + + return parseSettings; +} + +/** + * Ensures source code is a string. + */ +function enforceString(code: unknown): string { + if (typeof code !== 'string') { + return String(code); + } + + return code; +} + +/** + * Compute the filename based on the parser options. + * + * Even if jsx option is set in typescript compiler, filename still has to + * contain .tsx file extension. + * + * @param options Parser options + */ +function getFileName(jsx?: boolean): string { + return jsx ? 'estree.tsx' : 'estree.ts'; +} + +function getTsconfigPath( + tsconfigPath: string, + tsconfigRootDir: string, +): CanonicalPath { + return getCanonicalFileName( + ensureAbsolutePath(tsconfigPath, tsconfigRootDir), + ); +} + +/** + * Normalizes, sanitizes, resolves and filters the provided project paths + */ +function prepareAndTransformProjects( + tsconfigRootDir: string, + projectsInput: string | string[] | undefined, + ignoreListInput: string[], +): CanonicalPath[] { + const sanitizedProjects: string[] = []; + + // Normalize and sanitize the project paths + if (typeof projectsInput === 'string') { + sanitizedProjects.push(projectsInput); + } else if (Array.isArray(projectsInput)) { + for (const project of projectsInput) { + if (typeof project === 'string') { + sanitizedProjects.push(project); + } + } + } + + if (sanitizedProjects.length === 0) { + return []; + } + + // Transform glob patterns into paths + const nonGlobProjects = sanitizedProjects.filter(project => !isGlob(project)); + const globProjects = sanitizedProjects.filter(project => isGlob(project)); + const uniqueCanonicalProjectPaths = new Set( + nonGlobProjects + .concat( + globSync([...globProjects, ...ignoreListInput], { + cwd: tsconfigRootDir, + }), + ) + .map(project => getTsconfigPath(project, tsconfigRootDir)), + ); + + log( + 'parserOptions.project (excluding ignored) matched projects: %s', + uniqueCanonicalProjectPaths, + ); + + return Array.from(uniqueCanonicalProjectPaths); +} diff --git a/packages/typescript-estree/src/parseSettings/index.ts b/packages/typescript-estree/src/parseSettings/index.ts new file mode 100644 index 00000000000..0a9734d1b24 --- /dev/null +++ b/packages/typescript-estree/src/parseSettings/index.ts @@ -0,0 +1,124 @@ +import type * as ts from 'typescript'; + +import type { CanonicalPath } from '../create-program/shared'; +import type { TSESTree } from '../ts-estree'; + +type DebugModule = 'typescript-eslint' | 'eslint' | 'typescript'; + +/** + * Internal settings used by the parser to run on a file. + */ +export interface MutableParseSettings { + /** + * Code of the file being parsed. + */ + code: string; + + /** + * Whether the `comment` parse option is enabled. + */ + comment: boolean; + + /** + * If the `comment` parse option is enabled, retrieved comments. + */ + comments: TSESTree.Comment[]; + + /** + * Whether to create a TypeScript program if one is not provided. + */ + createDefaultProgram: boolean; + + /** + * Which debug areas should be logged. + */ + debugLevel: Set; + + /** + * Whether to error if TypeScript reports a semantic or syntactic error diagnostic. + */ + errorOnTypeScriptSyntacticAndSemanticIssues: boolean; + + /** + * Whether to error if an unknown AST node type is encountered. + */ + errorOnUnknownASTType: boolean; + + /** + * Whether TS should use the source files for referenced projects instead of the compiled .d.ts files. + * + * @remarks + * This feature is not yet optimized, and is likely to cause OOMs for medium to large projects. + * This flag REQUIRES at least TS v3.9, otherwise it does nothing. + */ + EXPERIMENTAL_useSourceOfProjectReferenceRedirect: boolean; + + /** + * Any non-standard file extensions which will be parsed. + */ + extraFileExtensions: string[]; + + /** + * Path of the file being parsed. + */ + filePath: string; + + /** + * Whether parsing of JSX is enabled. + * + * @remarks The applicable file extension is still required. + */ + jsx: boolean; + + /** + * Whether to add `loc` information to each node. + */ + loc: boolean; + + /** + * Log function, if not `console.log`. + */ + log: (message: string) => void; + + /** + * Path for a module resolver to use for the compiler host's `resolveModuleNames`. + */ + moduleResolver: string; + + /** + * Whether two-way AST node maps are preserved during the AST conversion process. + */ + preserveNodeMaps?: boolean; + + /** + * One or more instances of TypeScript Program objects to be used for type information. + */ + programs: null | Iterable; + + /** + * Normalized paths to provided project paths. + */ + projects: CanonicalPath[]; + + /** + * Whether to add the `range` property to AST nodes. + */ + range: boolean; + + /** + * Whether this is part of a single run, rather than a long-running process. + */ + singleRun: boolean; + + /** + * If the `tokens` parse option is enabled, retrieved tokens. + */ + tokens: null | TSESTree.Token[]; + + /** + * The absolute path to the root directory for all provided `project`s. + */ + tsconfigRootDir: string; +} + +export type ParseSettings = Readonly; diff --git a/packages/typescript-estree/src/parseSettings/inferSingleRun.ts b/packages/typescript-estree/src/parseSettings/inferSingleRun.ts new file mode 100644 index 00000000000..723f857ece9 --- /dev/null +++ b/packages/typescript-estree/src/parseSettings/inferSingleRun.ts @@ -0,0 +1,46 @@ +import { normalize } from 'path'; + +import type { TSESTreeOptions } from '../parser-options'; + +/** + * ESLint (and therefore typescript-eslint) is used in both "single run"/one-time contexts, + * such as an ESLint CLI invocation, and long-running sessions (such as continuous feedback + * on a file in an IDE). + * + * When typescript-eslint handles TypeScript Program management behind the scenes, this distinction + * is important because there is significant overhead to managing the so called Watch Programs + * needed for the long-running use-case. We therefore use the following logic to figure out which + * of these contexts applies to the current execution. + * + * @returns Whether this is part of a single run, rather than a long-running process. + */ +export function inferSingleRun(options: TSESTreeOptions | undefined): boolean { + // Allow users to explicitly inform us of their intent to perform a single run (or not) with TSESTREE_SINGLE_RUN + if (process.env.TSESTREE_SINGLE_RUN === 'false') { + return false; + } + if (process.env.TSESTREE_SINGLE_RUN === 'true') { + return true; + } + + // Currently behind a flag while we gather real-world feedback + if (options?.allowAutomaticSingleRunInference) { + if ( + // Default to single runs for CI processes. CI=true is set by most CI providers by default. + process.env.CI === 'true' || + // This will be true for invocations such as `npx eslint ...` and `./node_modules/.bin/eslint ...` + process.argv[1].endsWith(normalize('node_modules/.bin/eslint')) + ) { + return true; + } + } + + /** + * We default to assuming that this run could be part of a long-running session (e.g. in an IDE) + * and watch programs will therefore be required. + * + * Unless we can reliably infer otherwise, we default to assuming that this run could be part + * of a long-running session (e.g. in an IDE) and watch programs will therefore be required + */ + return false; +} diff --git a/packages/typescript-estree/src/parseSettings/warnAboutTSVersion.ts b/packages/typescript-estree/src/parseSettings/warnAboutTSVersion.ts new file mode 100644 index 00000000000..6bd890bdae9 --- /dev/null +++ b/packages/typescript-estree/src/parseSettings/warnAboutTSVersion.ts @@ -0,0 +1,45 @@ +import semver from 'semver'; +import * as ts from 'typescript'; + +import type { ParseSettings } from './index'; +/** + * This needs to be kept in sync with the top-level README.md in the + * typescript-eslint monorepo + */ +const SUPPORTED_TYPESCRIPT_VERSIONS = '>=3.3.1 <4.9.0'; + +/* + * The semver package will ignore prerelease ranges, and we don't want to explicitly document every one + * List them all separately here, so we can automatically create the full string + */ +const SUPPORTED_PRERELEASE_RANGES: string[] = []; +const ACTIVE_TYPESCRIPT_VERSION = ts.version; +const isRunningSupportedTypeScriptVersion = semver.satisfies( + ACTIVE_TYPESCRIPT_VERSION, + [SUPPORTED_TYPESCRIPT_VERSIONS] + .concat(SUPPORTED_PRERELEASE_RANGES) + .join(' || '), +); + +let warnedAboutTSVersion = false; + +export function warnAboutTSVersion(parseSettings: ParseSettings): void { + if (!isRunningSupportedTypeScriptVersion && !warnedAboutTSVersion) { + const isTTY = + typeof process === 'undefined' ? false : process.stdout?.isTTY; + if (isTTY) { + const border = '============='; + const versionWarning = [ + border, + 'WARNING: You are currently running a version of TypeScript which is not officially supported by @typescript-eslint/typescript-estree.', + 'You may find that it works just fine, or you may not.', + `SUPPORTED TYPESCRIPT VERSIONS: ${SUPPORTED_TYPESCRIPT_VERSIONS}`, + `YOUR TYPESCRIPT VERSION: ${ACTIVE_TYPESCRIPT_VERSION}`, + 'Please only submit bug reports when using the officially supported version.', + border, + ]; + parseSettings.log(versionWarning.join('\n\n')); + } + warnedAboutTSVersion = true; + } +} diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index cfe82b15f22..cec95c3b413 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -1,36 +1,8 @@ import type { DebugLevel } from '@typescript-eslint/types'; import type * as ts from 'typescript'; -import type { CanonicalPath } from './create-program/shared'; import type { TSESTree, TSESTreeToTSNode, TSNode, TSToken } from './ts-estree'; -type DebugModule = 'typescript-eslint' | 'eslint' | 'typescript'; - -export interface Extra { - code: string; - comment: boolean; - comments: TSESTree.Comment[]; - createDefaultProgram: boolean; - debugLevel: Set; - errorOnTypeScriptSyntacticAndSemanticIssues: boolean; - errorOnUnknownASTType: boolean; - EXPERIMENTAL_useSourceOfProjectReferenceRedirect: boolean; - extraFileExtensions: string[]; - filePath: string; - jsx: boolean; - loc: boolean; - singleRun: boolean; - log: (message: string) => void; - preserveNodeMaps?: boolean; - programs: null | Iterable; - projects: CanonicalPath[]; - range: boolean; - strict: boolean; - tokens: null | TSESTree.Token[]; - tsconfigRootDir: string; - moduleResolver: string; -} - //////////////////////////////////////////////////// // MAKE SURE THIS IS KEPT IN SYNC WITH THE README // //////////////////////////////////////////////////// diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index ae461a24dd5..c5504ba961a 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -1,9 +1,5 @@ import debug from 'debug'; -import { sync as globSync } from 'globby'; -import isGlob from 'is-glob'; -import { normalize } from 'path'; -import semver from 'semver'; -import * as ts from 'typescript'; +import type * as ts from 'typescript'; import { astConverter } from './ast-converter'; import { convertError } from './convert'; @@ -12,41 +8,18 @@ import { createIsolatedProgram } from './create-program/createIsolatedProgram'; import { createProjectProgram } from './create-program/createProjectProgram'; import { createSourceFile } from './create-program/createSourceFile'; import type { ASTAndProgram, CanonicalPath } from './create-program/shared'; -import { - ensureAbsolutePath, - getCanonicalFileName, -} from './create-program/shared'; import { createProgramFromConfigFile, useProvidedPrograms, } from './create-program/useProvidedPrograms'; -import type { Extra, ParserServices, TSESTreeOptions } from './parser-options'; +import type { ParserServices, TSESTreeOptions } from './parser-options'; +import type { ParseSettings } from './parseSettings'; +import { createParseSettings } from './parseSettings/createParseSettings'; import { getFirstSemanticOrSyntacticError } from './semantic-or-syntactic-errors'; import type { TSESTree } from './ts-estree'; const log = debug('typescript-eslint:typescript-estree:parser'); -/** - * This needs to be kept in sync with the top-level README.md in the - * typescript-eslint monorepo - */ -const SUPPORTED_TYPESCRIPT_VERSIONS = '>=3.3.1 <4.9.0'; -/* - * The semver package will ignore prerelease ranges, and we don't want to explicitly document every one - * List them all separately here, so we can automatically create the full string - */ -const SUPPORTED_PRERELEASE_RANGES: string[] = []; -const ACTIVE_TYPESCRIPT_VERSION = ts.version; -const isRunningSupportedTypeScriptVersion = semver.satisfies( - ACTIVE_TYPESCRIPT_VERSION, - [SUPPORTED_TYPESCRIPT_VERSIONS] - .concat(SUPPORTED_PRERELEASE_RANGES) - .join(' || '), -); - -let extra: Extra; -let warnedAboutTSVersion = false; - /** * Cache existing programs for the single run use-case. * @@ -57,348 +30,24 @@ function clearProgramCache(): void { existingPrograms.clear(); } -function enforceString(code: unknown): string { - /** - * Ensure the source code is a string - */ - if (typeof code !== 'string') { - return String(code); - } - - return code; -} - /** - * @param code The code of the file being linted - * @param programInstances One or more (potentially lazily constructed) existing programs to use + * @param parseSettings Internal settings for parsing the file * @param shouldProvideParserServices True if the program should be attempted to be calculated from provided tsconfig files - * @param shouldCreateDefaultProgram True if the program should be created from compiler host * @returns Returns a source file and program corresponding to the linted code */ function getProgramAndAST( - code: string, - programInstances: Iterable | null, + parseSettings: ParseSettings, shouldProvideParserServices: boolean, - shouldCreateDefaultProgram: boolean, ): ASTAndProgram { return ( - (programInstances && useProvidedPrograms(programInstances, extra)) || + (parseSettings.programs && + useProvidedPrograms(parseSettings.programs, parseSettings)) || + (shouldProvideParserServices && createProjectProgram(parseSettings)) || (shouldProvideParserServices && - createProjectProgram(code, shouldCreateDefaultProgram, extra)) || - (shouldProvideParserServices && - shouldCreateDefaultProgram && - createDefaultProgram(code, extra)) || - createIsolatedProgram(code, extra) - ); -} - -/** - * Compute the filename based on the parser options. - * - * Even if jsx option is set in typescript compiler, filename still has to - * contain .tsx file extension. - * - * @param options Parser options - */ -function getFileName({ jsx }: { jsx?: boolean } = {}): string { - return jsx ? 'estree.tsx' : 'estree.ts'; -} - -/** - * Resets the extra config object - */ -function resetExtra(): void { - extra = { - code: '', - comment: false, - comments: [], - createDefaultProgram: false, - debugLevel: new Set(), - errorOnTypeScriptSyntacticAndSemanticIssues: false, - errorOnUnknownASTType: false, - EXPERIMENTAL_useSourceOfProjectReferenceRedirect: false, - extraFileExtensions: [], - filePath: getFileName(), - jsx: false, - loc: false, - log: console.log, // eslint-disable-line no-console - preserveNodeMaps: true, - programs: null, - projects: [], - range: false, - strict: false, - tokens: null, - tsconfigRootDir: process.cwd(), - /** - * Unless we can reliably infer otherwise, we default to assuming that this run could be part - * of a long-running session (e.g. in an IDE) and watch programs will therefore be required - */ - singleRun: false, - moduleResolver: '', - }; -} - -function getTsconfigPath(tsconfigPath: string, extra: Extra): CanonicalPath { - return getCanonicalFileName(ensureAbsolutePath(tsconfigPath, extra)); -} - -/** - * Normalizes, sanitizes, resolves and filters the provided project paths - */ -function prepareAndTransformProjects( - projectsInput: string | string[] | undefined, - ignoreListInput: string[], -): CanonicalPath[] { - const sanitizedProjects: string[] = []; - - // Normalize and sanitize the project paths - if (typeof projectsInput === 'string') { - sanitizedProjects.push(projectsInput); - } else if (Array.isArray(projectsInput)) { - for (const project of projectsInput) { - if (typeof project === 'string') { - sanitizedProjects.push(project); - } - } - } - - if (sanitizedProjects.length === 0) { - return []; - } - - // Transform glob patterns into paths - const nonGlobProjects = sanitizedProjects.filter(project => !isGlob(project)); - const globProjects = sanitizedProjects.filter(project => isGlob(project)); - const uniqueCanonicalProjectPaths = new Set( - nonGlobProjects - .concat( - globSync([...globProjects, ...ignoreListInput], { - cwd: extra.tsconfigRootDir, - }), - ) - .map(project => getTsconfigPath(project, extra)), - ); - - log( - 'parserOptions.project (excluding ignored) matched projects: %s', - uniqueCanonicalProjectPaths, + parseSettings.createDefaultProgram && + createDefaultProgram(parseSettings)) || + createIsolatedProgram(parseSettings) ); - - return Array.from(uniqueCanonicalProjectPaths); -} - -function applyParserOptionsToExtra(options: TSESTreeOptions): void { - /** - * Configure Debug logging - */ - if (options.debugLevel === true) { - extra.debugLevel = new Set(['typescript-eslint']); - } else if (Array.isArray(options.debugLevel)) { - extra.debugLevel = new Set(options.debugLevel); - } - if (extra.debugLevel.size > 0) { - // debug doesn't support multiple `enable` calls, so have to do it all at once - const namespaces = []; - if (extra.debugLevel.has('typescript-eslint')) { - namespaces.push('typescript-eslint:*'); - } - if ( - extra.debugLevel.has('eslint') || - // make sure we don't turn off the eslint debug if it was enabled via --debug - debug.enabled('eslint:*,-eslint:code-path') - ) { - // https://github.com/eslint/eslint/blob/9dfc8501fb1956c90dc11e6377b4cb38a6bea65d/bin/eslint.js#L25 - namespaces.push('eslint:*,-eslint:code-path'); - } - debug.enable(namespaces.join(',')); - } - - /** - * Track range information in the AST - */ - extra.range = typeof options.range === 'boolean' && options.range; - extra.loc = typeof options.loc === 'boolean' && options.loc; - - /** - * Track tokens in the AST - */ - if (typeof options.tokens === 'boolean' && options.tokens) { - extra.tokens = []; - } - - /** - * Track comments in the AST - */ - if (typeof options.comment === 'boolean' && options.comment) { - extra.comment = true; - extra.comments = []; - } - - /** - * Enable JSX - note the applicable file extension is still required - */ - if (typeof options.jsx !== 'boolean') { - extra.jsx = false; - } else { - extra.jsx = options.jsx; - } - - /** - * Get the file path - */ - if (typeof options.filePath === 'string' && options.filePath !== '') { - extra.filePath = options.filePath; - } else { - extra.filePath = getFileName(extra); - } - - /** - * Allow the user to cause the parser to error if it encounters an unknown AST Node Type - * (used in testing) - */ - if ( - typeof options.errorOnUnknownASTType === 'boolean' && - options.errorOnUnknownASTType - ) { - extra.errorOnUnknownASTType = true; - } - - /** - * Allow the user to override the function used for logging - */ - if (typeof options.loggerFn === 'function') { - extra.log = options.loggerFn; - } else if (options.loggerFn === false) { - extra.log = (): void => {}; - } - - if (typeof options.tsconfigRootDir === 'string') { - extra.tsconfigRootDir = options.tsconfigRootDir; - } - - // NOTE - ensureAbsolutePath relies upon having the correct tsconfigRootDir in extra - extra.filePath = ensureAbsolutePath(extra.filePath, extra); - - if (Array.isArray(options.programs)) { - if (!options.programs.length) { - throw new Error( - `You have set parserOptions.programs to an empty array. This will cause all files to not be found in existing programs. Either provide one or more existing TypeScript Program instances in the array, or remove the parserOptions.programs setting.`, - ); - } - extra.programs = options.programs; - log( - 'parserOptions.programs was provided, so parserOptions.project will be ignored.', - ); - } - - if (!extra.programs) { - // providing a program overrides project resolution - const projectFolderIgnoreList = ( - options.projectFolderIgnoreList ?? ['**/node_modules/**'] - ) - .reduce((acc, folder) => { - if (typeof folder === 'string') { - acc.push(folder); - } - return acc; - }, []) - // prefix with a ! for not match glob - .map(folder => (folder.startsWith('!') ? folder : `!${folder}`)); - // NOTE - prepareAndTransformProjects relies upon having the correct tsconfigRootDir in extra - extra.projects = prepareAndTransformProjects( - options.project, - projectFolderIgnoreList, - ); - } - - if ( - Array.isArray(options.extraFileExtensions) && - options.extraFileExtensions.every(ext => typeof ext === 'string') - ) { - extra.extraFileExtensions = options.extraFileExtensions; - } - - /** - * Allow the user to enable or disable the preservation of the AST node maps - * during the conversion process. - */ - if (typeof options.preserveNodeMaps === 'boolean') { - extra.preserveNodeMaps = options.preserveNodeMaps; - } - - extra.createDefaultProgram = - typeof options.createDefaultProgram === 'boolean' && - options.createDefaultProgram; - - extra.EXPERIMENTAL_useSourceOfProjectReferenceRedirect = - typeof options.EXPERIMENTAL_useSourceOfProjectReferenceRedirect === - 'boolean' && options.EXPERIMENTAL_useSourceOfProjectReferenceRedirect; - - if (typeof options.moduleResolver === 'string') { - extra.moduleResolver = options.moduleResolver; - } -} - -function warnAboutTSVersion(): void { - if (!isRunningSupportedTypeScriptVersion && !warnedAboutTSVersion) { - const isTTY = - typeof process === 'undefined' ? false : process.stdout?.isTTY; - if (isTTY) { - const border = '============='; - const versionWarning = [ - border, - 'WARNING: You are currently running a version of TypeScript which is not officially supported by @typescript-eslint/typescript-estree.', - 'You may find that it works just fine, or you may not.', - `SUPPORTED TYPESCRIPT VERSIONS: ${SUPPORTED_TYPESCRIPT_VERSIONS}`, - `YOUR TYPESCRIPT VERSION: ${ACTIVE_TYPESCRIPT_VERSION}`, - 'Please only submit bug reports when using the officially supported version.', - border, - ]; - extra.log(versionWarning.join('\n\n')); - } - warnedAboutTSVersion = true; - } -} - -/** - * ESLint (and therefore typescript-eslint) is used in both "single run"/one-time contexts, - * such as an ESLint CLI invocation, and long-running sessions (such as continuous feedback - * on a file in an IDE). - * - * When typescript-eslint handles TypeScript Program management behind the scenes, this distinction - * is important because there is significant overhead to managing the so called Watch Programs - * needed for the long-running use-case. We therefore use the following logic to figure out which - * of these contexts applies to the current execution. - */ -function inferSingleRun(options: TSESTreeOptions | undefined): void { - // Allow users to explicitly inform us of their intent to perform a single run (or not) with TSESTREE_SINGLE_RUN - if (process.env.TSESTREE_SINGLE_RUN === 'false') { - extra.singleRun = false; - return; - } - if (process.env.TSESTREE_SINGLE_RUN === 'true') { - extra.singleRun = true; - return; - } - - // Currently behind a flag while we gather real-world feedback - if (options?.allowAutomaticSingleRunInference) { - if ( - // Default to single runs for CI processes. CI=true is set by most CI providers by default. - process.env.CI === 'true' || - // This will be true for invocations such as `npx eslint ...` and `./node_modules/.bin/eslint ...` - process.argv[1].endsWith(normalize('node_modules/.bin/eslint')) - ) { - extra.singleRun = true; - return; - } - } - - /** - * We default to assuming that this run could be part of a long-running session (e.g. in an IDE) - * and watch programs will therefore be required - */ - extra.singleRun = false; } // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -433,7 +82,7 @@ function parseWithNodeMapsInternal( /** * Reset the parse configuration */ - resetExtra(); + const parseSettings = createParseSettings(code, options); /** * Ensure users do not attempt to use parse() when they need parseAndGenerateServices() @@ -445,38 +94,18 @@ function parseWithNodeMapsInternal( } /** - * Ensure the source code is a string, and store a reference to it - */ - code = enforceString(code); - extra.code = code; - - /** - * Apply the given parser options - */ - if (typeof options !== 'undefined') { - applyParserOptionsToExtra(options); - } - - /** - * Warn if the user is using an unsupported version of TypeScript - */ - warnAboutTSVersion(); - - /** - * Figure out whether this is a single run or part of a long-running process + * Create a ts.SourceFile directly, no ts.Program is needed for a simple parse */ - inferSingleRun(options); - - /** - * Create a ts.SourceFile directly, no ts.Program is needed for a simple - * parse - */ - const ast = createSourceFile(code, extra); + const ast = createSourceFile(parseSettings); /** * Convert the TypeScript AST to an ESTree-compatible one */ - const { estree, astMaps } = astConverter(ast, extra, shouldPreserveNodeMaps); + const { estree, astMaps } = astConverter( + ast, + parseSettings, + shouldPreserveNodeMaps, + ); return { ast: estree as AST, @@ -505,47 +134,31 @@ function parseAndGenerateServices( /** * Reset the parse configuration */ - resetExtra(); + const parseSettings = createParseSettings(code, options); - /** - * Ensure the source code is a string, and store a reference to it - */ - code = enforceString(code); - extra.code = code; - - /** - * Apply the given parser options - */ if (typeof options !== 'undefined') { - applyParserOptionsToExtra(options); if ( typeof options.errorOnTypeScriptSyntacticAndSemanticIssues === 'boolean' && options.errorOnTypeScriptSyntacticAndSemanticIssues ) { - extra.errorOnTypeScriptSyntacticAndSemanticIssues = true; + parseSettings.errorOnTypeScriptSyntacticAndSemanticIssues = true; } } - /** - * Warn if the user is using an unsupported version of TypeScript - */ - warnAboutTSVersion(); - - /** - * Figure out whether this is a single run or part of a long-running process - */ - inferSingleRun(options); - /** * If this is a single run in which the user has not provided any existing programs but there * are programs which need to be created from the provided "project" option, * create an Iterable which will lazily create the programs as needed by the iteration logic */ - if (extra.singleRun && !extra.programs && extra.projects?.length > 0) { - extra.programs = { + if ( + parseSettings.singleRun && + !parseSettings.programs && + parseSettings.projects?.length > 0 + ) { + parseSettings.programs = { *[Symbol.iterator](): Iterator { - for (const configFile of extra.projects) { + for (const configFile of parseSettings.projects) { const existingProgram = existingPrograms.get(configFile); if (existingProgram) { yield existingProgram; @@ -567,7 +180,7 @@ function parseAndGenerateServices( * Generate a full ts.Program or offer provided instances in order to be able to provide parser services, such as type-checking */ const shouldProvideParserServices = - extra.programs != null || (extra.projects && extra.projects.length > 0); + parseSettings.programs != null || parseSettings.projects?.length > 0; /** * If we are in singleRun mode but the parseAndGenerateServices() function has been called more than once for the current file, @@ -577,46 +190,38 @@ function parseAndGenerateServices( * In this scenario we cannot rely upon the singleRun AOT compiled programs because the SourceFiles will not contain the source * with the latest fixes applied. Therefore we fallback to creating the quickest possible isolated program from the updated source. */ - let ast: ts.SourceFile; - let program: ts.Program; - - if (extra.singleRun && options.filePath) { + if (parseSettings.singleRun && options.filePath) { parseAndGenerateServicesCalls[options.filePath] = (parseAndGenerateServicesCalls[options.filePath] || 0) + 1; } - if ( - extra.singleRun && + const { ast, program } = + parseSettings.singleRun && options.filePath && parseAndGenerateServicesCalls[options.filePath] > 1 - ) { - const isolatedAstAndProgram = createIsolatedProgram(code, extra); - ast = isolatedAstAndProgram.ast; - program = isolatedAstAndProgram.program; - } else { - const astAndProgram = getProgramAndAST( - code, - extra.programs, - shouldProvideParserServices, - extra.createDefaultProgram, - )!; - ast = astAndProgram.ast; - program = astAndProgram.program; - } + ? createIsolatedProgram(parseSettings) + : getProgramAndAST(parseSettings, shouldProvideParserServices)!; /** * Convert the TypeScript AST to an ESTree-compatible one, and optionally preserve * mappings between converted and original AST nodes */ - const preserveNodeMaps = - typeof extra.preserveNodeMaps === 'boolean' ? extra.preserveNodeMaps : true; - const { estree, astMaps } = astConverter(ast, extra, preserveNodeMaps); + const shouldPreserveNodeMaps = + typeof parseSettings.preserveNodeMaps === 'boolean' + ? parseSettings.preserveNodeMaps + : true; + + const { estree, astMaps } = astConverter( + ast, + parseSettings, + shouldPreserveNodeMaps, + ); /** * Even if TypeScript parsed the source code ok, and we had no problems converting the AST, * there may be other syntactic or semantic issues in the code that we can optionally report on. */ - if (program && extra.errorOnTypeScriptSyntacticAndSemanticIssues) { + if (program && parseSettings.errorOnTypeScriptSyntacticAndSemanticIssues) { const error = getFirstSemanticOrSyntacticError(program, ast); if (error) { throw convertError(error); diff --git a/packages/utils/src/eslint-utils/RuleCreator.ts b/packages/utils/src/eslint-utils/RuleCreator.ts index dedf3043121..527ae36307b 100644 --- a/packages/utils/src/eslint-utils/RuleCreator.ts +++ b/packages/utils/src/eslint-utils/RuleCreator.ts @@ -16,28 +16,25 @@ export type NamedCreateRuleMeta = { export interface RuleCreateAndOptions< TOptions extends readonly unknown[], TMessageIds extends string, - TRuleListener extends RuleListener, > { create: ( context: Readonly>, optionsWithDefault: Readonly, - ) => TRuleListener; + ) => RuleListener; defaultOptions: Readonly; } export interface RuleWithMeta< TOptions extends readonly unknown[], TMessageIds extends string, - TRuleListener extends RuleListener, -> extends RuleCreateAndOptions { +> extends RuleCreateAndOptions { meta: RuleMetaData; } export interface RuleWithMetaAndName< TOptions extends readonly unknown[], TMessageIds extends string, - TRuleListener extends RuleListener, -> extends RuleCreateAndOptions { +> extends RuleCreateAndOptions { meta: NamedCreateRuleMeta; name: string; } @@ -54,15 +51,15 @@ export function RuleCreator(urlCreator: (ruleName: string) => string) { return function createNamedRule< TOptions extends readonly unknown[], TMessageIds extends string, - TRuleListener extends RuleListener = RuleListener, >({ name, meta, ...rule - }: Readonly< - RuleWithMetaAndName - >): RuleModule { - return createRule({ + }: Readonly>): RuleModule< + TMessageIds, + TOptions + > { + return createRule({ meta: { ...meta, docs: { @@ -84,20 +81,18 @@ export function RuleCreator(urlCreator: (ruleName: string) => string) { function createRule< TOptions extends readonly unknown[], TMessageIds extends string, - TRuleListener extends RuleListener = RuleListener, >({ create, defaultOptions, meta, -}: Readonly>): RuleModule< +}: Readonly>): RuleModule< TMessageIds, - TOptions, - TRuleListener + TOptions > { return { create( context: Readonly>, - ): TRuleListener { + ): RuleListener { const optionsWithDefault = applyDefault(defaultOptions, context.options); return create(context, optionsWithDefault); }, diff --git a/packages/utils/src/ts-eslint/CLIEngine.ts b/packages/utils/src/ts-eslint/CLIEngine.ts index 9ad4f5c76ed..65629fbbec7 100644 --- a/packages/utils/src/ts-eslint/CLIEngine.ts +++ b/packages/utils/src/ts-eslint/CLIEngine.ts @@ -4,7 +4,7 @@ import { CLIEngine as ESLintCLIEngine } from 'eslint'; import type { Linter } from './Linter'; -import type { RuleListener, RuleMetaData, RuleModule } from './Rule'; +import type { RuleMetaData, RuleModule } from './Rule'; declare class CLIEngineBase { /** @@ -72,9 +72,7 @@ declare class CLIEngineBase { getRules< TMessageIds extends string = string, TOptions extends readonly unknown[] = unknown[], - // for extending base rules - TRuleListener extends RuleListener = RuleListener, - >(): Map>; + >(): Map>; //////////////////// // static members // diff --git a/packages/utils/src/ts-eslint/Rule.ts b/packages/utils/src/ts-eslint/Rule.ts index ff305386321..7fe34067c95 100644 --- a/packages/utils/src/ts-eslint/Rule.ts +++ b/packages/utils/src/ts-eslint/Rule.ts @@ -453,9 +453,7 @@ interface RuleModule< type RuleCreateFunction< TMessageIds extends string = never, TOptions extends readonly unknown[] = unknown[], - // for extending base rules - TRuleListener extends RuleListener = RuleListener, -> = (context: Readonly>) => TRuleListener; +> = (context: Readonly>) => RuleListener; export { ReportDescriptor, diff --git a/packages/website/src/components/FinancialContributors/index.tsx b/packages/website/src/components/FinancialContributors/index.tsx index 3d5889ecede..609de140c9a 100644 --- a/packages/website/src/components/FinancialContributors/index.tsx +++ b/packages/website/src/components/FinancialContributors/index.tsx @@ -50,7 +50,7 @@ export function FinancialContributors(): JSX.Element { Docs diff --git a/packages/website/src/components/linter/WebLinter.ts b/packages/website/src/components/linter/WebLinter.ts index e3fa23cf0e0..378e34ed136 100644 --- a/packages/website/src/components/linter/WebLinter.ts +++ b/packages/website/src/components/linter/WebLinter.ts @@ -1,5 +1,5 @@ import { createVirtualCompilerHost } from '@site/src/components/linter/CompilerHost'; -import { extra } from '@site/src/components/linter/config'; +import { parseSettings } from '@site/src/components/linter/config'; import type { ParserOptions } from '@typescript-eslint/types'; import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; import type { LintUtils } from '@typescript-eslint/website-eslint'; @@ -106,7 +106,7 @@ export class WebLinter { const { estree: ast, astMaps } = this.lintUtils.astConverter( tsAst, - { ...extra, code, jsx: isJsx }, + { ...parseSettings, code, jsx: isJsx }, true, ); diff --git a/packages/website/src/components/linter/config.ts b/packages/website/src/components/linter/config.ts index cad38a3e28f..f077f3786ee 100644 --- a/packages/website/src/components/linter/config.ts +++ b/packages/website/src/components/linter/config.ts @@ -1,12 +1,11 @@ -import type { Extra } from '@typescript-eslint/typescript-estree/dist/parser-options'; +import type { ParseSettings } from '@typescript-eslint/typescript-estree/dist/parseSettings'; -export const extra: Extra = { +export const parseSettings: ParseSettings = { code: '', comment: true, comments: [], createDefaultProgram: false, debugLevel: new Set(), - errorOnTypeScriptSyntacticAndSemanticIssues: false, errorOnUnknownASTType: false, extraFileExtensions: [], filePath: '', @@ -17,9 +16,9 @@ export const extra: Extra = { preserveNodeMaps: true, projects: [], range: true, - strict: false, tokens: [], tsconfigRootDir: '/', + errorOnTypeScriptSyntacticAndSemanticIssues: false, EXPERIMENTAL_useSourceOfProjectReferenceRedirect: false, singleRun: false, programs: null, From 04488c2b081a0cce1b8b8ef66d34e96b3ed0ca60 Mon Sep 17 00:00:00 2001 From: Armano Date: Wed, 26 Oct 2022 02:42:06 +0200 Subject: [PATCH 06/13] feat: create TSTypeQuery node when TSImportType has isTypeOf (#3076) * feat: update TSImportType node * fix: update visitor keys * chore: document and refactor 'extra' to 'parserSettings' (#5834) * chore(website): fix renamed Sponsorship docs link (#5882) * docs: Mention wide globs performance implications in monorepos docs and parser README (#5864) * docs: Mention wide globs performance implications in monorepos docs and parser readme * Update docs/linting/typed-linting/MONOREPOS.md Co-authored-by: Josh Goldberg Co-authored-by: Josh Goldberg Co-authored-by: Adnan Hashmi <56730784+adnanhashmi09@users.noreply.github.com> --- .../ast-spec/src/type/TSImportType/spec.ts | 3 +- .../ast-spec/src/type/TSTypeQuery/spec.ts | 3 +- .../src/referencer/TypeVisitor.ts | 9 +- .../typescript/basics/type-import-type.src.ts | 2 +- packages/typescript-estree/src/convert.ts | 21 ++++- .../src/ts-estree/estree-to-ts-node-types.ts | 2 +- .../tests/ast-alignment/fixtures-to-test.ts | 5 - .../tests/ast-alignment/utils.ts | 22 +++++ ...e-parameters-in-type-reference.src.ts.shot | 23 +++-- .../basics/type-import-type.src.ts.shot | 91 +++++++++++-------- packages/visitor-keys/src/visitor-keys.ts | 2 +- 11 files changed, 117 insertions(+), 66 deletions(-) diff --git a/packages/ast-spec/src/type/TSImportType/spec.ts b/packages/ast-spec/src/type/TSImportType/spec.ts index b2eea1a78e0..fb2d33bfe11 100644 --- a/packages/ast-spec/src/type/TSImportType/spec.ts +++ b/packages/ast-spec/src/type/TSImportType/spec.ts @@ -6,8 +6,7 @@ import type { TypeNode } from '../../unions/TypeNode'; export interface TSImportType extends BaseNode { type: AST_NODE_TYPES.TSImportType; - isTypeOf: boolean; - parameter: TypeNode; + argument: TypeNode; qualifier: EntityName | null; typeParameters: TSTypeParameterInstantiation | null; } diff --git a/packages/ast-spec/src/type/TSTypeQuery/spec.ts b/packages/ast-spec/src/type/TSTypeQuery/spec.ts index 634c307ad2c..bc9cb7e68d0 100644 --- a/packages/ast-spec/src/type/TSTypeQuery/spec.ts +++ b/packages/ast-spec/src/type/TSTypeQuery/spec.ts @@ -2,9 +2,10 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; import type { TSTypeParameterInstantiation } from '../../special/spec'; import type { EntityName } from '../../unions/EntityName'; +import type { TSImportType } from '../TSImportType/spec'; export interface TSTypeQuery extends BaseNode { type: AST_NODE_TYPES.TSTypeQuery; - exprName: EntityName; + exprName: EntityName | TSImportType; typeParameters?: TSTypeParameterInstantiation; } diff --git a/packages/scope-manager/src/referencer/TypeVisitor.ts b/packages/scope-manager/src/referencer/TypeVisitor.ts index 70d4f86d55a..367fe3d3f23 100644 --- a/packages/scope-manager/src/referencer/TypeVisitor.ts +++ b/packages/scope-manager/src/referencer/TypeVisitor.ts @@ -260,7 +260,10 @@ class TypeVisitor extends Visitor { // a type query `typeof foo` is a special case that references a _non-type_ variable, protected TSTypeQuery(node: TSESTree.TSTypeQuery): void { - let entityName: TSESTree.Identifier | TSESTree.ThisExpression; + let entityName: + | TSESTree.Identifier + | TSESTree.ThisExpression + | TSESTree.TSImportType; if (node.exprName.type === AST_NODE_TYPES.TSQualifiedName) { let iter = node.exprName; while (iter.left.type === AST_NODE_TYPES.TSQualifiedName) { @@ -269,6 +272,10 @@ class TypeVisitor extends Visitor { entityName = iter.left; } else { entityName = node.exprName; + + if (node.exprName.type === AST_NODE_TYPES.TSImportType) { + this.visit(node.exprName); + } } if (entityName.type === AST_NODE_TYPES.Identifier) { this.#referencer.currentScope().referenceValue(entityName); diff --git a/packages/shared-fixtures/fixtures/typescript/basics/type-import-type.src.ts b/packages/shared-fixtures/fixtures/typescript/basics/type-import-type.src.ts index 00e3ba6afc1..5166c214216 100644 --- a/packages/shared-fixtures/fixtures/typescript/basics/type-import-type.src.ts +++ b/packages/shared-fixtures/fixtures/typescript/basics/type-import-type.src.ts @@ -1,2 +1,2 @@ type A = typeof import('A'); -type B = import("B").X; +type B = import('B').X; diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 30c33050d42..ecaeed7edd8 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -2730,11 +2730,15 @@ export class Converter { return result; } - case SyntaxKind.ImportType: - return this.createNode(node, { + case SyntaxKind.ImportType: { + const range = getRange(node, this.ast); + if (node.isTypeOf) { + const token = findNextToken(node.getFirstToken()!, node, this.ast)!; + range[0] = token.getStart(this.ast); + } + const result = this.createNode(node, { type: AST_NODE_TYPES.TSImportType, - isTypeOf: !!node.isTypeOf, - parameter: this.convertChild(node.argument), + argument: this.convertChild(node.argument), qualifier: this.convertChild(node.qualifier), typeParameters: node.typeArguments ? this.convertTypeArgumentsToTypeParameters( @@ -2742,7 +2746,16 @@ export class Converter { node, ) : null, + range: range, }); + if (node.isTypeOf) { + return this.createNode(node, { + type: AST_NODE_TYPES.TSTypeQuery, + exprName: result, + }); + } + return result; + } case SyntaxKind.EnumDeclaration: { const result = this.createNode(node, { diff --git a/packages/typescript-estree/src/ts-estree/estree-to-ts-node-types.ts b/packages/typescript-estree/src/ts-estree/estree-to-ts-node-types.ts index 3b80ab0a958..575a1e9a244 100644 --- a/packages/typescript-estree/src/ts-estree/estree-to-ts-node-types.ts +++ b/packages/typescript-estree/src/ts-estree/estree-to-ts-node-types.ts @@ -225,7 +225,7 @@ export interface EstreeToTsNodeTypes { | ts.CallExpression | ts.TypeQueryNode; [AST_NODE_TYPES.TSTypePredicate]: ts.TypePredicateNode; - [AST_NODE_TYPES.TSTypeQuery]: ts.TypeQueryNode; + [AST_NODE_TYPES.TSTypeQuery]: ts.TypeQueryNode | ts.ImportTypeNode; [AST_NODE_TYPES.TSTypeReference]: ts.TypeReferenceNode; [AST_NODE_TYPES.TSUnionType]: ts.UnionTypeNode; [AST_NODE_TYPES.UpdateExpression]: diff --git a/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts b/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts index 6abab590b77..ab3e9118268 100644 --- a/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts +++ b/packages/typescript-estree/tests/ast-alignment/fixtures-to-test.ts @@ -341,11 +341,6 @@ tester.addFixturePatternConfig('typescript/basics', { * @see https://github.com/babel/babel/issues/12884 */ 'interface-with-extends-member-expression', - /** - * @see https://github.com/typescript-eslint/typescript-eslint/issues/2998 - */ - 'type-import-type', - 'type-import-type-with-type-parameters-in-type-reference', /** * Not yet supported in Babel * Directive field is not added to module and namespace diff --git a/packages/typescript-estree/tests/ast-alignment/utils.ts b/packages/typescript-estree/tests/ast-alignment/utils.ts index 717a74837b5..d92e70541aa 100644 --- a/packages/typescript-estree/tests/ast-alignment/utils.ts +++ b/packages/typescript-estree/tests/ast-alignment/utils.ts @@ -292,6 +292,28 @@ export function preprocessBabylonAST(ast: File): any { delete node.loc.start.index; } }, + TSImportType(node: any) { + if (!node.typeParameters) { + node.typeParameters = null; + } + if (!node.qualifier) { + node.qualifier = null; + } + /** + * https://github.com/babel/babel/issues/12833 + */ + if (node.argument) { + node.argument = { + type: AST_NODE_TYPES.TSLiteralType, + literal: node.argument, + loc: { + start: { ...node.argument.loc.start }, + end: { ...node.argument.loc.end }, + }, + range: [...node.argument.range], + }; + } + }, TSTypePredicate(node: any) { if (node.loc?.start?.index) { delete node.loc.start.index; diff --git a/packages/typescript-estree/tests/snapshots/typescript/basics/type-import-type-with-type-parameters-in-type-reference.src.ts.shot b/packages/typescript-estree/tests/snapshots/typescript/basics/type-import-type-with-type-parameters-in-type-reference.src.ts.shot index 3d1549888bc..2e2104b59f3 100644 --- a/packages/typescript-estree/tests/snapshots/typescript/basics/type-import-type-with-type-parameters-in-type-reference.src.ts.shot +++ b/packages/typescript-estree/tests/snapshots/typescript/basics/type-import-type-with-type-parameters-in-type-reference.src.ts.shot @@ -84,18 +84,7 @@ Object { }, "params": Array [ Object { - "isTypeOf": false, - "loc": Object { - "end": Object { - "column": 28, - "line": 1, - }, - "start": Object { - "column": 11, - "line": 1, - }, - }, - "parameter": Object { + "argument": Object { "literal": Object { "loc": Object { "end": Object { @@ -131,6 +120,16 @@ Object { ], "type": "TSLiteralType", }, + "loc": Object { + "end": Object { + "column": 28, + "line": 1, + }, + "start": Object { + "column": 11, + "line": 1, + }, + }, "qualifier": Object { "loc": Object { "end": Object { diff --git a/packages/typescript-estree/tests/snapshots/typescript/basics/type-import-type.src.ts.shot b/packages/typescript-estree/tests/snapshots/typescript/basics/type-import-type.src.ts.shot index 1afe1ff0424..010427ef271 100644 --- a/packages/typescript-estree/tests/snapshots/typescript/basics/type-import-type.src.ts.shot +++ b/packages/typescript-estree/tests/snapshots/typescript/basics/type-import-type.src.ts.shot @@ -38,19 +38,27 @@ Object { ], "type": "TSTypeAliasDeclaration", "typeAnnotation": Object { - "isTypeOf": true, - "loc": Object { - "end": Object { - "column": 27, - "line": 1, - }, - "start": Object { - "column": 9, - "line": 1, - }, - }, - "parameter": Object { - "literal": Object { + "exprName": Object { + "argument": Object { + "literal": Object { + "loc": Object { + "end": Object { + "column": 26, + "line": 1, + }, + "start": Object { + "column": 23, + "line": 1, + }, + }, + "range": Array [ + 23, + 26, + ], + "raw": "'A'", + "type": "Literal", + "value": "A", + }, "loc": Object { "end": Object { "column": 26, @@ -65,33 +73,41 @@ Object { 23, 26, ], - "raw": "'A'", - "type": "Literal", - "value": "A", + "type": "TSLiteralType", }, "loc": Object { "end": Object { - "column": 26, + "column": 27, "line": 1, }, "start": Object { - "column": 23, + "column": 16, "line": 1, }, }, + "qualifier": null, "range": Array [ - 23, - 26, + 16, + 27, ], - "type": "TSLiteralType", + "type": "TSImportType", + "typeParameters": null, + }, + "loc": Object { + "end": Object { + "column": 27, + "line": 1, + }, + "start": Object { + "column": 9, + "line": 1, + }, }, - "qualifier": null, "range": Array [ 9, 27, ], - "type": "TSImportType", - "typeParameters": null, + "type": "TSTypeQuery", }, }, Object { @@ -129,18 +145,7 @@ Object { ], "type": "TSTypeAliasDeclaration", "typeAnnotation": Object { - "isTypeOf": false, - "loc": Object { - "end": Object { - "column": 25, - "line": 2, - }, - "start": Object { - "column": 9, - "line": 2, - }, - }, - "parameter": Object { + "argument": Object { "literal": Object { "loc": Object { "end": Object { @@ -156,7 +161,7 @@ Object { 45, 48, ], - "raw": "\\"B\\"", + "raw": "'B'", "type": "Literal", "value": "B", }, @@ -176,6 +181,16 @@ Object { ], "type": "TSLiteralType", }, + "loc": Object { + "end": Object { + "column": 25, + "line": 2, + }, + "start": Object { + "column": 9, + "line": 2, + }, + }, "qualifier": Object { "loc": Object { "end": Object { @@ -542,7 +557,7 @@ Object { 48, ], "type": "String", - "value": "\\"B\\"", + "value": "'B'", }, Object { "loc": Object { diff --git a/packages/visitor-keys/src/visitor-keys.ts b/packages/visitor-keys/src/visitor-keys.ts index a1898e92246..21648193b38 100644 --- a/packages/visitor-keys/src/visitor-keys.ts +++ b/packages/visitor-keys/src/visitor-keys.ts @@ -96,7 +96,7 @@ const additionalKeys: AdditionalKeys = { TSExternalModuleReference: ['expression'], TSFunctionType: ['typeParameters', 'params', 'returnType'], TSImportEqualsDeclaration: ['id', 'moduleReference'], - TSImportType: ['parameter', 'qualifier', 'typeParameters'], + TSImportType: ['argument', 'qualifier', 'typeParameters'], TSIndexedAccessType: ['indexType', 'objectType'], TSIndexSignature: ['parameters', 'typeAnnotation'], TSInferType: ['typeParameter'], From c44638654ff29dad29af938bc64443c0f0fa7dfd Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 25 Oct 2022 23:13:43 -0400 Subject: [PATCH 07/13] feat(scope-manager): ignore ECMA version (#5889) * feat(scope-manager): ignore ECMA version * chore: document and refactor 'extra' to 'parserSettings' (#5834) * chore(website): fix renamed Sponsorship docs link (#5882) * Remove much more * Fix WebLinter lint * docs: Mention wide globs performance implications in monorepos docs and parser README (#5864) * docs: Mention wide globs performance implications in monorepos docs and parser readme * Update docs/linting/typed-linting/MONOREPOS.md Co-authored-by: Josh Goldberg * chore: add auto-canary release for v6 (#5883) Co-authored-by: Adnan Hashmi <56730784+adnanhashmi09@users.noreply.github.com> --- .github/workflows/ci.yml | 26 ++++++++++ packages/parser/src/parser.ts | 1 - packages/parser/tests/lib/parser.ts | 8 --- packages/scope-manager/README.md | 12 +---- packages/scope-manager/src/ScopeManager.ts | 9 ++-- packages/scope-manager/src/analyze.ts | 35 ++----------- .../src/referencer/Referencer.ts | 17 ++----- .../get-declared-variables.test.ts | 1 - .../tests/eslint-scope/implied-strict.test.ts | 30 +---------- .../eslint-scope/map-ecma-version.test.ts | 51 ------------------- packages/scope-manager/tests/fixtures.test.ts | 1 - .../src/components/linter/WebLinter.ts | 4 -- 12 files changed, 44 insertions(+), 151 deletions(-) delete mode 100644 packages/scope-manager/tests/eslint-scope/map-ecma-version.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff735911dfa..1a1fd0e460b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - v6 pull_request: branches: - '**' @@ -249,3 +250,28 @@ jobs: run: npx lerna publish --loglevel=verbose --canary --exact --force-publish --yes env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + publish_canary_version_v6: + name: Publish the next major version code as a canary version + runs-on: ubuntu-latest + needs: [integration_tests, lint_with_build, lint_without_build, unit_tests] + if: github.ref == 'refs/heads/v${major}' + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install + uses: ./.github/actions/prepare-install + with: + node-version: ${{ env.PRIMARY_NODE_VERSION }} + registry-url: 'https://registry.npmjs.org' + - name: Build + uses: ./.github/actions/prepare-build + + # Fetch all history for all tags and branches in this job because lerna needs it + - run: | + git fetch --prune --unshallow + + - name: Publish all packages to npm + run: npx lerna publish premajor --loglevel=verbose --canary --exact --force-publish --yes --dist-tag rc-v${major} + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index 0e4b7780c17..e7223e93332 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -105,7 +105,6 @@ function parseForESLint( jsx: validateBoolean(options.ecmaFeatures.jsx), }); const analyzeOptions: AnalyzeOptions = { - ecmaVersion: options.ecmaVersion === 'latest' ? 1e8 : options.ecmaVersion, globalReturn: options.ecmaFeatures.globalReturn, jsxPragma: options.jsxPragma, jsxFragmentName: options.jsxFragmentName, diff --git a/packages/parser/tests/lib/parser.ts b/packages/parser/tests/lib/parser.ts index 7f2f193e11d..e554c4bfde7 100644 --- a/packages/parser/tests/lib/parser.ts +++ b/packages/parser/tests/lib/parser.ts @@ -19,11 +19,6 @@ describe('parser', () => { expect(() => parseForESLint(code, null)).not.toThrow(); }); - it("parseForESLint() should work if options.ecmaVersion is `'latest'`", () => { - const code = 'const valid = true;'; - expect(() => parseForESLint(code, { ecmaVersion: 'latest' })).not.toThrow(); - }); - it('parseAndGenerateServices() should be called with options', () => { const code = 'const valid = true;'; const spy = jest.spyOn(typescriptESTree, 'parseAndGenerateServices'); @@ -33,7 +28,6 @@ describe('parser', () => { range: false, tokens: false, sourceType: 'module' as const, - ecmaVersion: 2018, ecmaFeatures: { globalReturn: false, jsx: false, @@ -84,7 +78,6 @@ describe('parser', () => { range: false, tokens: false, sourceType: 'module' as const, - ecmaVersion: 2018, ecmaFeatures: { globalReturn: false, jsx: false, @@ -104,7 +97,6 @@ describe('parser', () => { parseForESLint(code, config); expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenLastCalledWith(expect.anything(), { - ecmaVersion: 2018, globalReturn: false, lib: ['dom.iterable'], jsxPragma: 'Foo', diff --git a/packages/scope-manager/README.md b/packages/scope-manager/README.md index b0a745f3fa5..3d9ef751032 100644 --- a/packages/scope-manager/README.md +++ b/packages/scope-manager/README.md @@ -36,13 +36,6 @@ interface AnalyzeOptions { */ childVisitorKeys?: Record | null; - /** - * Which ECMAScript version is considered. - * Defaults to `2018`. - * `'latest'` is converted to 1e8 at parser. - */ - ecmaVersion?: EcmaVersion | 1e8; - /** * Whether the whole script is executed under node.js environment. * When enabled, the scope manager adds a function scope immediately following the global scope. @@ -51,7 +44,7 @@ interface AnalyzeOptions { globalReturn?: boolean; /** - * Implied strict mode (if ecmaVersion >= 5). + * Implied strict mode. * Defaults to `false`. */ impliedStrict?: boolean; @@ -76,7 +69,7 @@ interface AnalyzeOptions { * This automatically defines a type variable for any types provided by the configured TS libs. * For more information, see https://www.typescriptlang.org/tsconfig#lib * - * Defaults to the lib for the provided `ecmaVersion`. + * Defaults to ['esnext']. */ lib?: Lib[]; @@ -105,7 +98,6 @@ const ast = parse(code, { range: true, }); const scope = analyze(ast, { - ecmaVersion: 2020, sourceType: 'module', }); ``` diff --git a/packages/scope-manager/src/ScopeManager.ts b/packages/scope-manager/src/ScopeManager.ts index 5368cca1dc3..d8beafba4aa 100644 --- a/packages/scope-manager/src/ScopeManager.ts +++ b/packages/scope-manager/src/ScopeManager.ts @@ -28,9 +28,11 @@ interface ScopeManagerOptions { globalReturn?: boolean; sourceType?: 'module' | 'script'; impliedStrict?: boolean; - ecmaVersion?: number; } +/** + * @see https://eslint.org/docs/latest/developer-guide/scope-manager-interface#scopemanager-interface + */ class ScopeManager { public currentScope: Scope | null; public readonly declaredVariables: WeakMap; @@ -77,12 +79,13 @@ class ScopeManager { public isImpliedStrict(): boolean { return this.#options.impliedStrict === true; } + public isStrictModeSupported(): boolean { - return this.#options.ecmaVersion != null && this.#options.ecmaVersion >= 5; + return true; } public isES6(): boolean { - return this.#options.ecmaVersion != null && this.#options.ecmaVersion >= 6; + return true; } /** diff --git a/packages/scope-manager/src/analyze.ts b/packages/scope-manager/src/analyze.ts index e227d1e45ad..1cb1d989209 100644 --- a/packages/scope-manager/src/analyze.ts +++ b/packages/scope-manager/src/analyze.ts @@ -1,7 +1,6 @@ -import type { EcmaVersion, Lib, TSESTree } from '@typescript-eslint/types'; +import type { Lib, TSESTree } from '@typescript-eslint/types'; import { visitorKeys } from '@typescript-eslint/visitor-keys'; -import { lib as TSLibraries } from './lib'; import type { ReferencerOptions } from './referencer'; import { Referencer } from './referencer'; import { ScopeManager } from './ScopeManager'; @@ -16,13 +15,6 @@ interface AnalyzeOptions { */ childVisitorKeys?: ReferencerOptions['childVisitorKeys']; - /** - * Which ECMAScript version is considered. - * Defaults to `2018`. - * `'latest'` is converted to 1e8 at parser. - */ - ecmaVersion?: EcmaVersion | 1e8; - /** * Whether the whole script is executed under node.js environment. * When enabled, the scope manager adds a function scope immediately following the global scope. @@ -31,7 +23,7 @@ interface AnalyzeOptions { globalReturn?: boolean; /** - * Implied strict mode (if ecmaVersion >= 5). + * Implied strict mode. * Defaults to `false`. */ impliedStrict?: boolean; @@ -54,7 +46,7 @@ interface AnalyzeOptions { /** * The lib used by the project. * This automatically defines a type variable for any types provided by the configured TS libs. - * Defaults to the lib for the provided `ecmaVersion`. + * Defaults to ['esnext']. * * https://www.typescriptlang.org/tsconfig#lib */ @@ -74,7 +66,6 @@ interface AnalyzeOptions { const DEFAULT_OPTIONS: Required = { childVisitorKeys: visitorKeys, - ecmaVersion: 2018, globalReturn: false, impliedStrict: false, jsxPragma: 'React', @@ -84,21 +75,6 @@ const DEFAULT_OPTIONS: Required = { emitDecoratorMetadata: false, }; -/** - * Convert ecmaVersion to lib. - * `'latest'` is converted to 1e8 at parser. - */ -function mapEcmaVersion(version: EcmaVersion | 1e8 | undefined): Lib { - if (version == null || version === 3 || version === 5) { - return 'es5'; - } - - const year = version > 2000 ? version : 2015 + (version - 6); - const lib = `es${year}`; - - return lib in TSLibraries ? (lib as Lib) : year > 2020 ? 'esnext' : 'es5'; -} - /** * Takes an AST and returns the analyzed scopes. */ @@ -106,12 +82,9 @@ function analyze( tree: TSESTree.Node, providedOptions?: AnalyzeOptions, ): ScopeManager { - const ecmaVersion = - providedOptions?.ecmaVersion ?? DEFAULT_OPTIONS.ecmaVersion; const options: Required = { childVisitorKeys: providedOptions?.childVisitorKeys ?? DEFAULT_OPTIONS.childVisitorKeys, - ecmaVersion, globalReturn: providedOptions?.globalReturn ?? DEFAULT_OPTIONS.globalReturn, impliedStrict: providedOptions?.impliedStrict ?? DEFAULT_OPTIONS.impliedStrict, @@ -122,7 +95,7 @@ function analyze( jsxFragmentName: providedOptions?.jsxFragmentName ?? DEFAULT_OPTIONS.jsxFragmentName, sourceType: providedOptions?.sourceType ?? DEFAULT_OPTIONS.sourceType, - lib: providedOptions?.lib ?? [mapEcmaVersion(ecmaVersion)], + lib: providedOptions?.lib ?? ['esnext'], emitDecoratorMetadata: providedOptions?.emitDecoratorMetadata ?? DEFAULT_OPTIONS.emitDecoratorMetadata, diff --git a/packages/scope-manager/src/referencer/Referencer.ts b/packages/scope-manager/src/referencer/Referencer.ts index a69209e86c6..ecadb7e9fd6 100644 --- a/packages/scope-manager/src/referencer/Referencer.ts +++ b/packages/scope-manager/src/referencer/Referencer.ts @@ -371,9 +371,7 @@ class Referencer extends Visitor { } protected BlockStatement(node: TSESTree.BlockStatement): void { - if (this.scopeManager.isES6()) { - this.scopeManager.nestBlockScope(node); - } + this.scopeManager.nestBlockScope(node); this.visitChildren(node); @@ -487,7 +485,7 @@ class Referencer extends Visitor { protected ImportDeclaration(node: TSESTree.ImportDeclaration): void { assert( - this.scopeManager.isES6() && this.scopeManager.isModule(), + this.scopeManager.isModule(), 'ImportDeclaration should appear when the mode is ES6 and in the module context.', ); @@ -579,14 +577,11 @@ class Referencer extends Visitor { this.scopeManager.nestFunctionScope(node, false); } - if (this.scopeManager.isES6() && this.scopeManager.isModule()) { + if (this.scopeManager.isModule()) { this.scopeManager.nestModuleScope(node); } - if ( - this.scopeManager.isStrictModeSupported() && - this.scopeManager.isImpliedStrict() - ) { + if (this.scopeManager.isImpliedStrict()) { this.currentScope().isStrict = true; } @@ -601,9 +596,7 @@ class Referencer extends Visitor { protected SwitchStatement(node: TSESTree.SwitchStatement): void { this.visit(node.discriminant); - if (this.scopeManager.isES6()) { - this.scopeManager.nestSwitchScope(node); - } + this.scopeManager.nestSwitchScope(node); for (const switchCase of node.cases) { this.visit(switchCase); diff --git a/packages/scope-manager/tests/eslint-scope/get-declared-variables.test.ts b/packages/scope-manager/tests/eslint-scope/get-declared-variables.test.ts index 23e02b9af0c..82611eb5147 100644 --- a/packages/scope-manager/tests/eslint-scope/get-declared-variables.test.ts +++ b/packages/scope-manager/tests/eslint-scope/get-declared-variables.test.ts @@ -12,7 +12,6 @@ describe('ScopeManager.prototype.getDeclaredVariables', () => { expectedNamesList: string[][], ): void { const scopeManager = analyze(ast, { - ecmaVersion: 6, sourceType: 'module', }); diff --git a/packages/scope-manager/tests/eslint-scope/implied-strict.test.ts b/packages/scope-manager/tests/eslint-scope/implied-strict.test.ts index 34151be8c3d..893da6048c2 100644 --- a/packages/scope-manager/tests/eslint-scope/implied-strict.test.ts +++ b/packages/scope-manager/tests/eslint-scope/implied-strict.test.ts @@ -8,7 +8,7 @@ import { } from '../util'; describe('impliedStrict option', () => { - it('ensures all user scopes are strict if ecmaVersion >= 5', () => { + it('ensures all user scopes are strict', () => { const { scopeManager } = parseAndAnalyze( ` function foo() { @@ -18,7 +18,6 @@ describe('impliedStrict option', () => { } `, { - ecmaVersion: 5, impliedStrict: true, }, ); @@ -42,38 +41,12 @@ describe('impliedStrict option', () => { expect(scope.isStrict).toBeTruthy(); }); - it('ensures impliedStrict option is only effective when ecmaVersion option >= 5', () => { - const { scopeManager } = parseAndAnalyze( - ` - function foo() {} - `, - { - ecmaVersion: 3, - impliedStrict: true, - }, - ); - - expect(scopeManager.scopes).toHaveLength(2); - - let scope = scopeManager.scopes[0]; - - expectToBeGlobalScope(scope); - expect(scope.block.type).toBe(AST_NODE_TYPES.Program); - expect(scope.isStrict).toBeFalsy(); - - scope = scopeManager.scopes[1]; - expectToBeFunctionScope(scope); - expect(scope.block.type).toBe(AST_NODE_TYPES.FunctionDeclaration); - expect(scope.isStrict).toBeFalsy(); - }); - it('omits a nodejs global scope when ensuring all user scopes are strict', () => { const { scopeManager } = parseAndAnalyze( ` function foo() {} `, { - ecmaVersion: 5, globalReturn: true, impliedStrict: true, }, @@ -100,7 +73,6 @@ describe('impliedStrict option', () => { it('omits a module global scope when ensuring all user scopes are strict', () => { const { scopeManager } = parseAndAnalyze('function foo() {}', { - ecmaVersion: 6, impliedStrict: true, sourceType: 'module', }); diff --git a/packages/scope-manager/tests/eslint-scope/map-ecma-version.test.ts b/packages/scope-manager/tests/eslint-scope/map-ecma-version.test.ts deleted file mode 100644 index becca474ff3..00000000000 --- a/packages/scope-manager/tests/eslint-scope/map-ecma-version.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { EcmaVersion, Lib, TSESTree } from '@typescript-eslint/types'; - -import { analyze } from '../../src/analyze'; -import { Referencer } from '../../src/referencer'; - -jest.mock('../../src/referencer'); -jest.mock('../../src/ScopeManager'); - -describe('ecma version mapping', () => { - it("should map to 'esnext' when unsuported and new", () => { - expectMapping(2042, 'esnext'); - expectMapping(42, 'esnext'); - }); - - it("should map to 'es5' when unsuported and old", () => { - expectMapping(2002, 'es5'); - expectMapping(2, 'es5'); - }); - - it("should map to 'es{year}' when supported and >= 6", () => { - expectMapping(2015, 'es2015'); - expectMapping(6, 'es2015'); - expectMapping(2020, 'es2020'); - expectMapping(11, 'es2020'); - }); - - it("should map to 'es5' when 5 or 3", () => { - expectMapping(5, 'es5'); - expectMapping(3, 'es5'); - }); - - it("should map to 'es2018' when undefined", () => { - expectMapping(undefined, 'es2018'); - }); - - it("should map to 'esnext' when 'latest'", () => { - // `'latest'` is converted to 1e8 at parser. - expectMapping(1e8, 'esnext'); - }); -}); - -const fakeNode = {} as unknown as TSESTree.Node; - -function expectMapping(ecmaVersion: number | undefined, lib: Lib): void { - (Referencer as jest.Mock).mockClear(); - analyze(fakeNode, { ecmaVersion: ecmaVersion as EcmaVersion }); - expect(Referencer).toHaveBeenCalledWith( - expect.objectContaining({ lib: [lib] }), - expect.any(Object), - ); -} diff --git a/packages/scope-manager/tests/fixtures.test.ts b/packages/scope-manager/tests/fixtures.test.ts index fa86c1544c7..ac6b39c860e 100644 --- a/packages/scope-manager/tests/fixtures.test.ts +++ b/packages/scope-manager/tests/fixtures.test.ts @@ -42,7 +42,6 @@ const ALLOWED_OPTIONS: Map = new Map< keyof AnalyzeOptions, ALLOWED_VALUE >([ - ['ecmaVersion', ['number']], ['globalReturn', ['boolean']], ['impliedStrict', ['boolean']], ['jsxPragma', ['string']], diff --git a/packages/website/src/components/linter/WebLinter.ts b/packages/website/src/components/linter/WebLinter.ts index 378e34ed136..acd3c569ff3 100644 --- a/packages/website/src/components/linter/WebLinter.ts +++ b/packages/website/src/components/linter/WebLinter.ts @@ -111,10 +111,6 @@ export class WebLinter { ); const scopeManager = this.lintUtils.analyze(ast, { - ecmaVersion: - eslintOptions.ecmaVersion === 'latest' - ? 1e8 - : eslintOptions.ecmaVersion, globalReturn: eslintOptions.ecmaFeatures?.globalReturn ?? false, sourceType: eslintOptions.sourceType ?? 'script', }); From cf2956faf30dd2ddf970e6e5697f8a58dd410b6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Garc=C3=ADa?= <82288753+juank1809@users.noreply.github.com> Date: Tue, 25 Oct 2022 22:29:28 -0500 Subject: [PATCH 08/13] feat: remove semantically invalid properties from TSEnumDeclaration, TSInterfaceDeclaration and TSModuleDeclaration (#4863) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: remove invalid properties from ast nodes * chore: remove invalid code in scope-manager and typescript-estree * chore: re-write snapshots that were using invalid properties * feat: remove modifiers union from ast types Co-authored-by: Juan GarcĂ­a Co-authored-by: Josh Goldberg --- .../src/declaration/TSEnumDeclaration/spec.ts | 3 - .../TSInterfaceDeclaration/spec.ts | 4 - .../declaration/TSModuleDeclaration/spec.ts | 3 - packages/ast-spec/src/index.ts | 1 - packages/ast-spec/src/unions/Modifier.ts | 16 --- .../src/referencer/TypeVisitor.ts | 1 - packages/typescript-estree/src/convert.ts | 85 ++---------- .../basics/abstract-interface.src.ts.shot | 1 - .../enum-with-keywords.src.ts.shot | 121 ------------------ .../interface-implements.src.ts.shot | 37 ------ 10 files changed, 12 insertions(+), 260 deletions(-) delete mode 100644 packages/ast-spec/src/unions/Modifier.ts diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts index 9a0df845c5d..1152c894f4a 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts @@ -2,7 +2,6 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; import type { TSEnumMember } from '../../element/TSEnumMember/spec'; import type { Identifier } from '../../expression/Identifier/spec'; -import type { Modifier } from '../../unions/Modifier'; export interface TSEnumDeclaration extends BaseNode { type: AST_NODE_TYPES.TSEnumDeclaration; @@ -30,6 +29,4 @@ export interface TSEnumDeclaration extends BaseNode { * The enum members. */ members: TSEnumMember[]; - // TODO(#4759) - breaking change remove this - modifiers?: Modifier[]; } diff --git a/packages/ast-spec/src/declaration/TSInterfaceDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSInterfaceDeclaration/spec.ts index a75c769daf9..18ad9a74155 100644 --- a/packages/ast-spec/src/declaration/TSInterfaceDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSInterfaceDeclaration/spec.ts @@ -7,8 +7,6 @@ import type { TSTypeParameterDeclaration } from '../../special/TSTypeParameterDe export interface TSInterfaceDeclaration extends BaseNode { type: AST_NODE_TYPES.TSInterfaceDeclaration; - // TODO(#4759) - breaking change remove this - abstract?: boolean; /** * The body of the interface */ @@ -26,8 +24,6 @@ export interface TSInterfaceDeclaration extends BaseNode { * The name of this interface */ id: Identifier; - // TODO(#4759) - breaking change remove this - implements?: TSInterfaceHeritage[]; /** * The generic type parameters declared for the interface. * This is `undefined` if there are no generic type parameters declared. diff --git a/packages/ast-spec/src/declaration/TSModuleDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSModuleDeclaration/spec.ts index e077d1648e1..79166ff38f9 100644 --- a/packages/ast-spec/src/declaration/TSModuleDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSModuleDeclaration/spec.ts @@ -3,7 +3,6 @@ import type { BaseNode } from '../../base/BaseNode'; import type { Identifier } from '../../expression/Identifier/spec'; import type { TSModuleBlock } from '../../special/TSModuleBlock/spec'; import type { Literal } from '../../unions/Literal'; -import type { Modifier } from '../../unions/Modifier'; export interface TSModuleDeclaration extends BaseNode { type: AST_NODE_TYPES.TSModuleDeclaration; @@ -49,6 +48,4 @@ export interface TSModuleDeclaration extends BaseNode { */ // TODO(#5020) - make this `false` if it is not `declare`d declare?: boolean; - // TODO(#4759) - breaking change remove this - modifiers?: Modifier[]; } diff --git a/packages/ast-spec/src/index.ts b/packages/ast-spec/src/index.ts index ba9ee680000..3994016c2e0 100644 --- a/packages/ast-spec/src/index.ts +++ b/packages/ast-spec/src/index.ts @@ -26,7 +26,6 @@ export * from './unions/JSXTagNameExpression'; export * from './unions/LeftHandSideExpression'; export * from './unions/Literal'; export * from './unions/LiteralExpression'; -export * from './unions/Modifier'; export * from './unions/Node'; export * from './unions/ObjectLiteralElement'; export * from './unions/Parameter'; diff --git a/packages/ast-spec/src/unions/Modifier.ts b/packages/ast-spec/src/unions/Modifier.ts deleted file mode 100644 index f2501e6585c..00000000000 --- a/packages/ast-spec/src/unions/Modifier.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { TSAbstractKeyword } from '../type/TSAbstractKeyword/spec'; -import type { TSAsyncKeyword } from '../type/TSAsyncKeyword/spec'; -import type { TSPrivateKeyword } from '../type/TSPrivateKeyword/spec'; -import type { TSProtectedKeyword } from '../type/TSProtectedKeyword/spec'; -import type { TSPublicKeyword } from '../type/TSPublicKeyword/spec'; -import type { TSReadonlyKeyword } from '../type/TSReadonlyKeyword/spec'; -import type { TSStaticKeyword } from '../type/TSStaticKeyword/spec'; - -export type Modifier = - | TSAbstractKeyword - | TSAsyncKeyword - | TSPrivateKeyword - | TSProtectedKeyword - | TSPublicKeyword - | TSReadonlyKeyword - | TSStaticKeyword; diff --git a/packages/scope-manager/src/referencer/TypeVisitor.ts b/packages/scope-manager/src/referencer/TypeVisitor.ts index 367fe3d3f23..899297ee12c 100644 --- a/packages/scope-manager/src/referencer/TypeVisitor.ts +++ b/packages/scope-manager/src/referencer/TypeVisitor.ts @@ -187,7 +187,6 @@ class TypeVisitor extends Visitor { } node.extends?.forEach(this.visit, this); - node.implements?.forEach(this.visit, this); this.visit(node.body); if (node.typeParameters) { diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index ecaeed7edd8..6c0e3f36dfa 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -696,62 +696,6 @@ export class Converter { : node.elements.map(element => this.convertChild(element)); } - /** - * Applies the given TS modifiers to the given result object. - * - * This method adds not standardized `modifiers` property in nodes - * - * @param result - * @param modifiers original ts.Nodes from the node.modifiers array - * @returns the current result object will be mutated - */ - private applyModifiersToResult( - result: TSESTree.TSEnumDeclaration | TSESTree.TSModuleDeclaration, - modifiers: Iterable | undefined, - ): void { - if (!modifiers) { - return; - } - - const remainingModifiers: TSESTree.Modifier[] = []; - /** - * Some modifiers are explicitly handled by applying them as - * boolean values on the result node. As well as adding them - * to the result, we remove them from the array, so that they - * are not handled twice. - */ - for (const modifier of modifiers) { - switch (modifier.kind) { - /** - * Ignore ExportKeyword and DefaultKeyword, they are handled - * via the fixExports utility function - */ - case SyntaxKind.ExportKeyword: - case SyntaxKind.DefaultKeyword: - break; - case SyntaxKind.ConstKeyword: - (result as any).const = true; - break; - case SyntaxKind.DeclareKeyword: - result.declare = true; - break; - default: - remainingModifiers.push( - this.convertChild(modifier) as TSESTree.Modifier, - ); - break; - } - } - /** - * If there are still valid modifiers available which have - * not been explicitly handled above, we just convert and - * add the modifiers array to the result node. - */ - if (remainingModifiers.length > 0) { - result.modifiers = remainingModifiers; - } - } - /** * Uses the provided range location to adjust the location data of the given Node * @param result The node that will have its location data mutated @@ -2674,7 +2618,6 @@ export class Converter { if (interfaceHeritageClauses.length > 0) { const interfaceExtends: TSESTree.TSInterfaceHeritage[] = []; - const interfaceImplements: TSESTree.TSInterfaceHeritage[] = []; for (const heritageClause of interfaceHeritageClauses) { if (heritageClause.token === SyntaxKind.ExtendsKeyword) { @@ -2683,27 +2626,14 @@ export class Converter { this.convertChild(n, node) as TSESTree.TSInterfaceHeritage, ); } - } else { - for (const n of heritageClause.types) { - interfaceImplements.push( - this.convertChild(n, node) as TSESTree.TSInterfaceHeritage, - ); - } } } - if (interfaceExtends.length) { + if (interfaceExtends.length > 0) { result.extends = interfaceExtends; } - - if (interfaceImplements.length) { - result.implements = interfaceImplements; - } } - if (hasModifier(SyntaxKind.AbstractKeyword, node)) { - result.abstract = true; - } if (hasModifier(SyntaxKind.DeclareKeyword, node)) { result.declare = true; } @@ -2764,7 +2694,14 @@ export class Converter { members: node.members.map(el => this.convertChild(el)), }); // apply modifiers first... - this.applyModifiersToResult(result, getModifiers(node)); + if (hasModifier(SyntaxKind.DeclareKeyword, node)) { + result.declare = true; + } + + if (hasModifier(SyntaxKind.ConstKeyword, node)) { + result.const = true; + } + // ...then check for exports return this.fixExports(node, result); } @@ -2792,7 +2729,9 @@ export class Converter { result.body = this.convertChild(node.body); } // apply modifiers first... - this.applyModifiersToResult(result, getModifiers(node)); + if (hasModifier(SyntaxKind.DeclareKeyword, node)) { + result.declare = true; + } if (node.flags & ts.NodeFlags.GlobalAugmentation) { result.global = true; } diff --git a/packages/typescript-estree/tests/snapshots/typescript/basics/abstract-interface.src.ts.shot b/packages/typescript-estree/tests/snapshots/typescript/basics/abstract-interface.src.ts.shot index 5d96c992f86..bc674bcb4d1 100644 --- a/packages/typescript-estree/tests/snapshots/typescript/basics/abstract-interface.src.ts.shot +++ b/packages/typescript-estree/tests/snapshots/typescript/basics/abstract-interface.src.ts.shot @@ -6,7 +6,6 @@ Object { Object { "assertions": Array [], "declaration": Object { - "abstract": true, "body": Object { "body": Array [], "loc": Object { diff --git a/packages/typescript-estree/tests/snapshots/typescript/errorRecovery/enum-with-keywords.src.ts.shot b/packages/typescript-estree/tests/snapshots/typescript/errorRecovery/enum-with-keywords.src.ts.shot index f21c928657d..5bb1a9402e3 100644 --- a/packages/typescript-estree/tests/snapshots/typescript/errorRecovery/enum-with-keywords.src.ts.shot +++ b/packages/typescript-estree/tests/snapshots/typescript/errorRecovery/enum-with-keywords.src.ts.shot @@ -35,127 +35,6 @@ Object { }, }, "members": Array [], - "modifiers": Array [ - Object { - "loc": Object { - "end": Object { - "column": 14, - "line": 1, - }, - "start": Object { - "column": 7, - "line": 1, - }, - }, - "range": Array [ - 7, - 14, - ], - "type": "TSPrivateKeyword", - }, - Object { - "loc": Object { - "end": Object { - "column": 21, - "line": 1, - }, - "start": Object { - "column": 15, - "line": 1, - }, - }, - "range": Array [ - 15, - 21, - ], - "type": "TSPublicKeyword", - }, - Object { - "loc": Object { - "end": Object { - "column": 31, - "line": 1, - }, - "start": Object { - "column": 22, - "line": 1, - }, - }, - "range": Array [ - 22, - 31, - ], - "type": "TSProtectedKeyword", - }, - Object { - "loc": Object { - "end": Object { - "column": 38, - "line": 1, - }, - "start": Object { - "column": 32, - "line": 1, - }, - }, - "range": Array [ - 32, - 38, - ], - "type": "TSStaticKeyword", - }, - Object { - "loc": Object { - "end": Object { - "column": 47, - "line": 1, - }, - "start": Object { - "column": 39, - "line": 1, - }, - }, - "range": Array [ - 39, - 47, - ], - "type": "TSReadonlyKeyword", - }, - Object { - "loc": Object { - "end": Object { - "column": 56, - "line": 1, - }, - "start": Object { - "column": 48, - "line": 1, - }, - }, - "range": Array [ - 48, - 56, - ], - "type": "TSAbstractKeyword", - }, - Object { - "loc": Object { - "end": Object { - "column": 62, - "line": 1, - }, - "start": Object { - "column": 57, - "line": 1, - }, - }, - "range": Array [ - 57, - 62, - ], - "type": "TSAsyncKeyword", - }, - ], "range": Array [ 7, 72, diff --git a/packages/typescript-estree/tests/snapshots/typescript/errorRecovery/interface-implements.src.ts.shot b/packages/typescript-estree/tests/snapshots/typescript/errorRecovery/interface-implements.src.ts.shot index 748c52a5a1f..65868666d64 100644 --- a/packages/typescript-estree/tests/snapshots/typescript/errorRecovery/interface-implements.src.ts.shot +++ b/packages/typescript-estree/tests/snapshots/typescript/errorRecovery/interface-implements.src.ts.shot @@ -40,43 +40,6 @@ Object { ], "type": "Identifier", }, - "implements": Array [ - Object { - "expression": Object { - "loc": Object { - "end": Object { - "column": 24, - "line": 1, - }, - "start": Object { - "column": 23, - "line": 1, - }, - }, - "name": "e", - "range": Array [ - 23, - 24, - ], - "type": "Identifier", - }, - "loc": Object { - "end": Object { - "column": 24, - "line": 1, - }, - "start": Object { - "column": 23, - "line": 1, - }, - }, - "range": Array [ - 23, - 24, - ], - "type": "TSInterfaceHeritage", - }, - ], "loc": Object { "end": Object { "column": 27, From b90e7c3289f916b6cd7fdf3defb267be4cd79316 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 26 Oct 2022 10:20:33 -0400 Subject: [PATCH 09/13] fix(eslint-plugin): remove valid-typeof disable in eslint-recommended (#5381) --- packages/eslint-plugin/src/configs/eslint-recommended.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/eslint-plugin/src/configs/eslint-recommended.ts b/packages/eslint-plugin/src/configs/eslint-recommended.ts index 71443e1f52e..4753d3fc729 100644 --- a/packages/eslint-plugin/src/configs/eslint-recommended.ts +++ b/packages/eslint-plugin/src/configs/eslint-recommended.ts @@ -28,7 +28,6 @@ export = { 'prefer-const': 'error', // ts provides better types with const 'prefer-rest-params': 'error', // ts provides better types with rest args over arguments 'prefer-spread': 'error', // ts transpiles spread to apply, so no need for manual apply - 'valid-typeof': 'off', // ts(2367) }, }, ], From fff0e296e12993f223b7059c03e6b9d389715a94 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 26 Oct 2022 10:43:12 -0400 Subject: [PATCH 10/13] feat(utils): remove (ts-)eslint-scope types (#5256) * chore(utils)\!: remove (ts-)eslint-scope types * Remove eslint-scope dep * More file deletions --- packages/utils/package.json | 1 - packages/utils/src/index.ts | 3 +- .../utils/src/ts-eslint-scope/Definition.ts | 41 ---- packages/utils/src/ts-eslint-scope/Options.ts | 21 -- .../src/ts-eslint-scope/PatternVisitor.ts | 39 ---- packages/utils/src/ts-eslint-scope/README.md | 3 - .../utils/src/ts-eslint-scope/Reference.ts | 43 ---- .../utils/src/ts-eslint-scope/Referencer.ts | 82 -------- packages/utils/src/ts-eslint-scope/Scope.ts | 197 ------------------ .../utils/src/ts-eslint-scope/ScopeManager.ts | 62 ------ .../utils/src/ts-eslint-scope/Variable.ts | 23 -- packages/utils/src/ts-eslint-scope/analyze.ts | 22 -- packages/utils/src/ts-eslint-scope/index.ts | 13 -- yarn.lock | 2 +- 14 files changed, 2 insertions(+), 550 deletions(-) delete mode 100644 packages/utils/src/ts-eslint-scope/Definition.ts delete mode 100644 packages/utils/src/ts-eslint-scope/Options.ts delete mode 100644 packages/utils/src/ts-eslint-scope/PatternVisitor.ts delete mode 100644 packages/utils/src/ts-eslint-scope/README.md delete mode 100644 packages/utils/src/ts-eslint-scope/Reference.ts delete mode 100644 packages/utils/src/ts-eslint-scope/Referencer.ts delete mode 100644 packages/utils/src/ts-eslint-scope/Scope.ts delete mode 100644 packages/utils/src/ts-eslint-scope/ScopeManager.ts delete mode 100644 packages/utils/src/ts-eslint-scope/Variable.ts delete mode 100644 packages/utils/src/ts-eslint-scope/analyze.ts delete mode 100644 packages/utils/src/ts-eslint-scope/index.ts diff --git a/packages/utils/package.json b/packages/utils/package.json index 21b80b4a9fa..423f9245662 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -44,7 +44,6 @@ "@typescript-eslint/scope-manager": "5.41.0", "@typescript-eslint/types": "5.41.0", "@typescript-eslint/typescript-estree": "5.41.0", - "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" }, diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 31328386269..c2c366379a0 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -2,7 +2,6 @@ import * as ASTUtils from './ast-utils'; import * as ESLintUtils from './eslint-utils'; import * as JSONSchema from './json-schema'; import * as TSESLint from './ts-eslint'; -import * as TSESLintScope from './ts-eslint-scope'; -export { ASTUtils, ESLintUtils, JSONSchema, TSESLint, TSESLintScope }; +export { ASTUtils, ESLintUtils, JSONSchema, TSESLint }; export * from './ts-estree'; diff --git a/packages/utils/src/ts-eslint-scope/Definition.ts b/packages/utils/src/ts-eslint-scope/Definition.ts deleted file mode 100644 index 3e35ecc9fc2..00000000000 --- a/packages/utils/src/ts-eslint-scope/Definition.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - Definition as ESLintDefinition, - ParameterDefinition as ESLintParameterDefinition, -} from 'eslint-scope/lib/definition'; - -import type { TSESTree } from '../ts-estree'; - -interface Definition { - type: string; - name: TSESTree.BindingName; - node: TSESTree.Node; - parent?: TSESTree.Node | null; - index?: number | null; - kind?: string | null; - rest?: boolean; -} -interface DefinitionConstructor { - new ( - type: string, - name: TSESTree.BindingName | TSESTree.PropertyName, - node: TSESTree.Node, - parent?: TSESTree.Node | null, - index?: number | null, - kind?: string | null, - ): Definition; -} -const Definition = ESLintDefinition as DefinitionConstructor; - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface ParameterDefinition extends Definition {} -const ParameterDefinition = - ESLintParameterDefinition as DefinitionConstructor & { - new ( - name: TSESTree.Node, - node: TSESTree.Node, - index?: number | null, - rest?: boolean, - ): ParameterDefinition; - }; - -export { Definition, ParameterDefinition }; diff --git a/packages/utils/src/ts-eslint-scope/Options.ts b/packages/utils/src/ts-eslint-scope/Options.ts deleted file mode 100644 index 0b1600ae1cb..00000000000 --- a/packages/utils/src/ts-eslint-scope/Options.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { TSESTree } from '../ts-estree'; - -type PatternVisitorCallback = ( - pattern: TSESTree.Identifier, - info: { - rest: boolean; - topLevel: boolean; - assignments: TSESTree.AssignmentPattern[]; - }, -) => void; - -interface PatternVisitorOptions { - processRightHandNodes?: boolean; -} - -interface Visitor { - visitChildren(node?: T): void; - visit(node?: T): void; -} - -export { PatternVisitorCallback, PatternVisitorOptions, Visitor }; diff --git a/packages/utils/src/ts-eslint-scope/PatternVisitor.ts b/packages/utils/src/ts-eslint-scope/PatternVisitor.ts deleted file mode 100644 index 75840211e28..00000000000 --- a/packages/utils/src/ts-eslint-scope/PatternVisitor.ts +++ /dev/null @@ -1,39 +0,0 @@ -import ESLintPatternVisitor from 'eslint-scope/lib/pattern-visitor'; - -import type { TSESTree } from '../ts-estree'; -import type { - PatternVisitorCallback, - PatternVisitorOptions, - Visitor, -} from './Options'; -import type { ScopeManager } from './ScopeManager'; - -interface PatternVisitor extends Visitor { - options: PatternVisitorOptions; - scopeManager: ScopeManager; - parent?: TSESTree.Node; - rightHandNodes: TSESTree.Node[]; - - Identifier(pattern: TSESTree.Node): void; - Property(property: TSESTree.Node): void; - ArrayPattern(pattern: TSESTree.Node): void; - AssignmentPattern(pattern: TSESTree.Node): void; - RestElement(pattern: TSESTree.Node): void; - MemberExpression(node: TSESTree.Node): void; - SpreadElement(node: TSESTree.Node): void; - ArrayExpression(node: TSESTree.Node): void; - AssignmentExpression(node: TSESTree.Node): void; - CallExpression(node: TSESTree.Node): void; -} -const PatternVisitor = ESLintPatternVisitor as { - new ( - options: PatternVisitorOptions, - rootPattern: TSESTree.BaseNode, - callback: PatternVisitorCallback, - ): PatternVisitor; - - // static methods - isPattern(node: TSESTree.Node): boolean; -}; - -export { PatternVisitor }; diff --git a/packages/utils/src/ts-eslint-scope/README.md b/packages/utils/src/ts-eslint-scope/README.md deleted file mode 100644 index 6cabba14fbf..00000000000 --- a/packages/utils/src/ts-eslint-scope/README.md +++ /dev/null @@ -1,3 +0,0 @@ -These will need to be removed in the next major as `eslint-scope` v6+ no longer export their internals. - -Issue: https://github.com/typescript-eslint/typescript-eslint/issues/4041 diff --git a/packages/utils/src/ts-eslint-scope/Reference.ts b/packages/utils/src/ts-eslint-scope/Reference.ts deleted file mode 100644 index d2dd6554977..00000000000 --- a/packages/utils/src/ts-eslint-scope/Reference.ts +++ /dev/null @@ -1,43 +0,0 @@ -import ESLintReference from 'eslint-scope/lib/reference'; - -import type { TSESTree } from '../ts-estree'; -import type { Scope } from './Scope'; -import type { Variable } from './Variable'; - -export type ReferenceFlag = 0x1 | 0x2 | 0x3; - -interface Reference { - identifier: TSESTree.Identifier; - from: Scope; - resolved: Variable | null; - writeExpr: TSESTree.Node | null; - init: boolean; - - partial: boolean; - __maybeImplicitGlobal: boolean; - tainted?: boolean; - typeMode?: boolean; - - isWrite(): boolean; - isRead(): boolean; - isWriteOnly(): boolean; - isReadOnly(): boolean; - isReadWrite(): boolean; -} -const Reference = ESLintReference as { - new ( - identifier: TSESTree.Identifier, - scope: Scope, - flag?: ReferenceFlag, - writeExpr?: TSESTree.Node | null, - maybeImplicitGlobal?: boolean, - partial?: boolean, - init?: boolean, - ): Reference; - - READ: 0x1; - WRITE: 0x2; - RW: 0x3; -}; - -export { Reference }; diff --git a/packages/utils/src/ts-eslint-scope/Referencer.ts b/packages/utils/src/ts-eslint-scope/Referencer.ts deleted file mode 100644 index 3bd6b27b20d..00000000000 --- a/packages/utils/src/ts-eslint-scope/Referencer.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import ESLintReferencer from 'eslint-scope/lib/referencer'; - -import type { TSESTree } from '../ts-estree'; -import type { - PatternVisitorCallback, - PatternVisitorOptions, - Visitor, -} from './Options'; -import type { Scope } from './Scope'; -import type { ScopeManager } from './ScopeManager'; - -interface Referencer extends Visitor { - isInnerMethodDefinition: boolean; - options: any; - scopeManager: SM; - parent?: TSESTree.Node; - - currentScope(): Scope; - close(node: TSESTree.Node): void; - pushInnerMethodDefinition(isInnerMethodDefinition: boolean): boolean; - popInnerMethodDefinition(isInnerMethodDefinition: boolean): void; - - referencingDefaultValue( - pattern: any, - assignments: any, - maybeImplicitGlobal: any, - init: boolean, - ): void; - visitPattern( - node: TSESTree.Node, - options: PatternVisitorOptions, - callback: PatternVisitorCallback, - ): void; - visitFunction(node: TSESTree.Node): void; - visitClass(node: TSESTree.Node): void; - visitProperty(node: TSESTree.Node): void; - visitForIn(node: TSESTree.Node): void; - visitVariableDeclaration( - variableTargetScope: any, - type: any, - node: TSESTree.Node, - index: any, - ): void; - - AssignmentExpression(node: TSESTree.Node): void; - CatchClause(node: TSESTree.Node): void; - Program(node: TSESTree.Program): void; - Identifier(node: TSESTree.Identifier): void; - UpdateExpression(node: TSESTree.Node): void; - MemberExpression(node: TSESTree.Node): void; - Property(node: TSESTree.Node): void; - MethodDefinition(node: TSESTree.Node): void; - BreakStatement(): void; - ContinueStatement(): void; - LabeledStatement(node: TSESTree.Node): void; - ForStatement(node: TSESTree.Node): void; - ClassExpression(node: TSESTree.Node): void; - ClassDeclaration(node: TSESTree.Node): void; - CallExpression(node: TSESTree.Node): void; - BlockStatement(node: TSESTree.Node): void; - ThisExpression(): void; - WithStatement(node: TSESTree.Node): void; - VariableDeclaration(node: TSESTree.Node): void; - SwitchStatement(node: TSESTree.Node): void; - FunctionDeclaration(node: TSESTree.Node): void; - FunctionExpression(node: TSESTree.Node): void; - ForOfStatement(node: TSESTree.Node): void; - ForInStatement(node: TSESTree.Node): void; - ArrowFunctionExpression(node: TSESTree.Node): void; - ImportDeclaration(node: TSESTree.Node): void; - visitExportDeclaration(node: TSESTree.Node): void; - ExportDeclaration(node: TSESTree.Node): void; - ExportNamedDeclaration(node: TSESTree.Node): void; - ExportSpecifier(node: TSESTree.Node): void; - MetaProperty(): void; -} -const Referencer = ESLintReferencer as { - new (options: any, scopeManager: SM): Referencer; -}; - -export { Referencer }; diff --git a/packages/utils/src/ts-eslint-scope/Scope.ts b/packages/utils/src/ts-eslint-scope/Scope.ts deleted file mode 100644 index e0f3320043a..00000000000 --- a/packages/utils/src/ts-eslint-scope/Scope.ts +++ /dev/null @@ -1,197 +0,0 @@ -/* eslint-disable @typescript-eslint/no-empty-interface, @typescript-eslint/no-explicit-any */ - -import { - BlockScope as ESLintBlockScope, - CatchScope as ESLintCatchScope, - ClassScope as ESLintClassScope, - ForScope as ESLintForScope, - FunctionExpressionNameScope as ESLintFunctionExpressionNameScope, - FunctionScope as ESLintFunctionScope, - GlobalScope as ESLintGlobalScope, - ModuleScope as ESLintModuleScope, - Scope as ESLintScope, - SwitchScope as ESLintSwitchScope, - WithScope as ESLintWithScope, -} from 'eslint-scope/lib/scope'; - -import type { TSESTree } from '../ts-estree'; -import type { Definition } from './Definition'; -import type { Reference, ReferenceFlag } from './Reference'; -import type { ScopeManager } from './ScopeManager'; -import type { Variable } from './Variable'; - -type ScopeType = - | 'block' - | 'catch' - | 'class' - | 'for' - | 'function' - | 'function-expression-name' - | 'global' - | 'module' - | 'switch' - | 'with' - | 'TDZ' - | 'enum' - | 'empty-function'; - -interface Scope { - type: ScopeType; - isStrict: boolean; - upper: Scope | null; - childScopes: Scope[]; - variableScope: Scope; - block: TSESTree.Node; - variables: Variable[]; - set: Map; - references: Reference[]; - through: Reference[]; - thisFound?: boolean; - taints: Map; - functionExpressionScope: boolean; - __left: Reference[]; - - __shouldStaticallyClose(scopeManager: ScopeManager): boolean; - __shouldStaticallyCloseForGlobal(ref: any): boolean; - __staticCloseRef(ref: any): void; - __dynamicCloseRef(ref: any): void; - __globalCloseRef(ref: any): void; - __close(scopeManager: ScopeManager): Scope; - __isValidResolution(ref: any, variable: any): variable is Variable; - __resolve(ref: Reference): boolean; - __delegateToUpperScope(ref: any): void; - __addDeclaredVariablesOfNode(variable: any, node: TSESTree.Node): void; - __defineGeneric( - name: string, - set: Map, - variables: Variable[], - node: TSESTree.Identifier, - def: Definition, - ): void; - - __define(node: TSESTree.Node, def: Definition): void; - - __referencing( - node: TSESTree.Node, - assign?: ReferenceFlag, - writeExpr?: TSESTree.Node, - maybeImplicitGlobal?: any, - partial?: any, - init?: any, - ): void; - - __detectEval(): void; - __detectThis(): void; - __isClosed(): boolean; - /** - * returns resolved {Reference} - * @method Scope#resolve - * @param {Espree.Identifier} ident - identifier to be resolved. - * @returns {Reference} reference - */ - resolve(ident: TSESTree.Node): Reference; - - /** - * returns this scope is static - * @method Scope#isStatic - * @returns {boolean} static - */ - isStatic(): boolean; - - /** - * returns this scope has materialized arguments - * @method Scope#isArgumentsMaterialized - * @returns {boolean} arguments materialized - */ - isArgumentsMaterialized(): boolean; - - /** - * returns this scope has materialized `this` reference - * @method Scope#isThisMaterialized - * @returns {boolean} this materialized - */ - isThisMaterialized(): boolean; - - isUsedName(name: any): boolean; -} -interface ScopeConstructor { - new ( - scopeManager: ScopeManager, - type: ScopeType, - upperScope: Scope | null, - block: TSESTree.Node | null, - isMethodDefinition: boolean, - ): Scope; -} -const Scope = ESLintScope as ScopeConstructor; - -interface ScopeChildConstructorWithUpperScope { - new ( - scopeManager: ScopeManager, - upperScope: Scope, - block: TSESTree.Node | null, - ): T; -} - -interface GlobalScope extends Scope {} -const GlobalScope = ESLintGlobalScope as ScopeConstructor & { - new (scopeManager: ScopeManager, block: TSESTree.Node | null): GlobalScope; -}; - -interface ModuleScope extends Scope {} -const ModuleScope = ESLintModuleScope as ScopeConstructor & - ScopeChildConstructorWithUpperScope; - -interface FunctionExpressionNameScope extends Scope {} -const FunctionExpressionNameScope = - ESLintFunctionExpressionNameScope as ScopeConstructor & - ScopeChildConstructorWithUpperScope; - -interface CatchScope extends Scope {} -const CatchScope = ESLintCatchScope as ScopeConstructor & - ScopeChildConstructorWithUpperScope; - -interface WithScope extends Scope {} -const WithScope = ESLintWithScope as ScopeConstructor & - ScopeChildConstructorWithUpperScope; - -interface BlockScope extends Scope {} -const BlockScope = ESLintBlockScope as ScopeConstructor & - ScopeChildConstructorWithUpperScope; - -interface SwitchScope extends Scope {} -const SwitchScope = ESLintSwitchScope as ScopeConstructor & - ScopeChildConstructorWithUpperScope; - -interface FunctionScope extends Scope {} -const FunctionScope = ESLintFunctionScope as ScopeConstructor & { - new ( - scopeManager: ScopeManager, - upperScope: Scope, - block: TSESTree.Node | null, - isMethodDefinition: boolean, - ): FunctionScope; -}; - -interface ForScope extends Scope {} -const ForScope = ESLintForScope as ScopeConstructor & - ScopeChildConstructorWithUpperScope; - -interface ClassScope extends Scope {} -const ClassScope = ESLintClassScope as ScopeConstructor & - ScopeChildConstructorWithUpperScope; - -export { - ScopeType, - Scope, - GlobalScope, - ModuleScope, - FunctionExpressionNameScope, - CatchScope, - WithScope, - BlockScope, - SwitchScope, - FunctionScope, - ForScope, - ClassScope, -}; diff --git a/packages/utils/src/ts-eslint-scope/ScopeManager.ts b/packages/utils/src/ts-eslint-scope/ScopeManager.ts deleted file mode 100644 index c7bbb2425c9..00000000000 --- a/packages/utils/src/ts-eslint-scope/ScopeManager.ts +++ /dev/null @@ -1,62 +0,0 @@ -import ESLintScopeManager from 'eslint-scope/lib/scope-manager'; - -import type { EcmaVersion } from '../ts-eslint'; -import type { TSESTree } from '../ts-estree'; -import type { Scope } from './Scope'; -import type { Variable } from './Variable'; - -interface ScopeManagerOptions { - directive?: boolean; - optimistic?: boolean; - ignoreEval?: boolean; - nodejsScope?: boolean; - sourceType?: 'module' | 'script'; - impliedStrict?: boolean; - ecmaVersion?: EcmaVersion; -} - -interface ScopeManager { - __options: ScopeManagerOptions; - __currentScope: Scope; - __nodeToScope: WeakMap; - __declaredVariables: WeakMap; - - scopes: Scope[]; - globalScope: Scope; - - __useDirective(): boolean; - __isOptimistic(): boolean; - __ignoreEval(): boolean; - __isNodejsScope(): boolean; - isModule(): boolean; - isImpliedStrict(): boolean; - isStrictModeSupported(): boolean; - - // Returns appropriate scope for this node. - __get(node: TSESTree.Node): Scope | undefined; - getDeclaredVariables(node: TSESTree.Node): Variable[]; - acquire(node: TSESTree.Node, inner?: boolean): Scope | null; - acquireAll(node: TSESTree.Node): Scope | null; - release(node: TSESTree.Node, inner?: boolean): Scope | null; - attach(): void; - detach(): void; - - __nestScope(scope: T): T; - __nestGlobalScope(node: TSESTree.Node): Scope; - __nestBlockScope(node: TSESTree.Node): Scope; - __nestFunctionScope(node: TSESTree.Node, isMethodDefinition: boolean): Scope; - __nestForScope(node: TSESTree.Node): Scope; - __nestCatchScope(node: TSESTree.Node): Scope; - __nestWithScope(node: TSESTree.Node): Scope; - __nestClassScope(node: TSESTree.Node): Scope; - __nestSwitchScope(node: TSESTree.Node): Scope; - __nestModuleScope(node: TSESTree.Node): Scope; - __nestFunctionExpressionNameScope(node: TSESTree.Node): Scope; - - __isES6(): boolean; -} -const ScopeManager = ESLintScopeManager as { - new (options: ScopeManagerOptions): ScopeManager; -}; - -export { ScopeManager, ScopeManagerOptions }; diff --git a/packages/utils/src/ts-eslint-scope/Variable.ts b/packages/utils/src/ts-eslint-scope/Variable.ts deleted file mode 100644 index 192c9f89550..00000000000 --- a/packages/utils/src/ts-eslint-scope/Variable.ts +++ /dev/null @@ -1,23 +0,0 @@ -import ESLintVariable from 'eslint-scope/lib/variable'; - -import type { TSESTree } from '../ts-estree'; -import type { Definition } from './Definition'; -import type { Reference } from './Reference'; -import type { Scope } from './Scope'; - -interface Variable { - name: string; - identifiers: TSESTree.Identifier[]; - references: Reference[]; - defs: Definition[]; - eslintUsed?: boolean; - stack?: unknown; - tainted?: boolean; - scope?: Scope; -} - -const Variable = ESLintVariable as { - new (): Variable; -}; - -export { Variable }; diff --git a/packages/utils/src/ts-eslint-scope/analyze.ts b/packages/utils/src/ts-eslint-scope/analyze.ts deleted file mode 100644 index 1543f93fa1a..00000000000 --- a/packages/utils/src/ts-eslint-scope/analyze.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { analyze as ESLintAnalyze } from 'eslint-scope'; - -import type { EcmaVersion } from '../ts-eslint'; -import type { TSESTree } from '../ts-estree'; -import type { ScopeManager } from './ScopeManager'; - -interface AnalysisOptions { - optimistic?: boolean; - directive?: boolean; - ignoreEval?: boolean; - nodejsScope?: boolean; - impliedStrict?: boolean; - fallback?: string | ((node: TSESTree.Node) => string[]); - sourceType?: 'script' | 'module'; - ecmaVersion?: EcmaVersion; -} -const analyze = ESLintAnalyze as ( - ast: TSESTree.Node, - options?: AnalysisOptions, -) => ScopeManager; - -export { analyze, AnalysisOptions }; diff --git a/packages/utils/src/ts-eslint-scope/index.ts b/packages/utils/src/ts-eslint-scope/index.ts deleted file mode 100644 index 870c34fce1b..00000000000 --- a/packages/utils/src/ts-eslint-scope/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { version as ESLintVersion } from 'eslint-scope'; - -export * from './analyze'; -export * from './Definition'; -export * from './Options'; -export * from './PatternVisitor'; -export * from './Reference'; -export * from './Referencer'; -export * from './Scope'; -export * from './ScopeManager'; -export * from './Variable'; - -export const version: string = ESLintVersion; diff --git a/yarn.lock b/yarn.lock index 1e719261af6..5c78be7104e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6969,7 +6969,7 @@ eslint-plugin-simple-import-sort@^8.0.0: resolved "https://registry.yarnpkg.com/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-8.0.0.tgz#9d9a2372b0606e999ea841b10458a370a6ccc160" integrity sha512-bXgJQ+lqhtQBCuWY/FUWdB27j4+lqcvXv5rUARkzbeWLwea+S5eBZEQrhnO+WgX3ZoJHVj0cn943iyXwByHHQw== -eslint-scope@5.1.1, eslint-scope@^5.1.1: +eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== From 5b9c3792a95613b6ec29b91e1612131b09697904 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 26 Oct 2022 11:15:24 -0400 Subject: [PATCH 11/13] fix(eslint-plugin): [explicit-module-boundary-types] remove shouldTrackReferences option from schema (#5399) --- .../src/rules/explicit-module-boundary-types.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts index e7552e0cbd8..b891cbec6cb 100644 --- a/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts +++ b/packages/eslint-plugin/src/rules/explicit-module-boundary-types.ts @@ -22,7 +22,6 @@ type Options = [ allowedNames?: string[]; allowHigherOrderFunctions?: boolean; allowTypedFunctionExpressions?: boolean; - shouldTrackReferences?: boolean; }, ]; type MessageIds = @@ -85,10 +84,6 @@ export default util.createRule({ 'Whether to ignore type annotations on the variable of a function expresion.', type: 'boolean', }, - // DEPRECATED - To be removed in next major - shouldTrackReferences: { - type: 'boolean', - }, }, additionalProperties: false, }, From d523f84b670c33f39c84804071050be9e22def8f Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Wed, 26 Oct 2022 11:17:50 -0400 Subject: [PATCH 12/13] One more post-merge cleanup --- packages/ast-spec/src/base/BaseNode.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/ast-spec/src/base/BaseNode.ts b/packages/ast-spec/src/base/BaseNode.ts index d27ae9a11b7..797b3d35132 100644 --- a/packages/ast-spec/src/base/BaseNode.ts +++ b/packages/ast-spec/src/base/BaseNode.ts @@ -3,8 +3,12 @@ import type { AST_NODE_TYPES } from '../ast-node-types'; import type { NodeOrTokenData } from './NodeOrTokenData'; export interface BaseNode extends NodeOrTokenData { - // The parent node of the current node - // (added in @typescript-eslint/types as ESLint adds it while traversing) + /** + * The parent node of the current node + * + * This is added in the @typescript-eslint/types package as ESLint adds it + * while traversing. + */ // parent?: Node; type: AST_NODE_TYPES; From 3d65e7424b99854e4429b290455c1db7abb8497d Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Fri, 25 Nov 2022 17:06:27 +1030 Subject: [PATCH 13/13] fix lint errors in new files after merge --- packages/eslint-plugin/src/rules/no-invalid-void-type.ts | 2 +- packages/eslint-plugin/src/rules/prefer-optional-chain.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts index f0dc5796258..ebfb1f53928 100644 --- a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts +++ b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts @@ -230,7 +230,7 @@ export default util.createRule<[Options], MessageIds>({ function getNotReturnOrGenericMessageId( node: TSESTree.TSVoidKeyword, ): MessageIds { - return node.parent!.type === AST_NODE_TYPES.TSUnionType + return node.parent.type === AST_NODE_TYPES.TSUnionType ? 'invalidVoidUnionConstituent' : 'invalidVoidNotReturnOrGeneric'; } diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain.ts index 73fbcc27af3..182b4c4ae68 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain.ts @@ -122,7 +122,7 @@ export default util.createRule({ ): void { // selector guarantees this cast const initialExpression = ( - initialIdentifierOrNotEqualsExpr.parent!.type === + initialIdentifierOrNotEqualsExpr.parent.type === AST_NODE_TYPES.ChainExpression ? initialIdentifierOrNotEqualsExpr.parent.parent : initialIdentifierOrNotEqualsExpr.parent