diff --git a/eslint.config.mjs b/eslint.config.mjs index cc1672c8799..26c7e212c24 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -613,6 +613,7 @@ export default tseslint.config( 'packages/scope-manager/{src,tests}/**/*.ts', 'packages/types/{src,tools}/**/*.ts', 'packages/typescript-eslint/{src,tests}/**/*.ts', + 'packages/typescript-estree/{src,tests,typings}/**/*.ts', 'packages/utils/src/**/*.ts', 'packages/visitor-keys/src/**/*.ts', 'packages/website*/src/**/*.ts', @@ -625,7 +626,7 @@ export default tseslint.config( 'perfectionist/sort-union-types': [ 'error', { - groups: ['unknown', 'keyword', 'nullish'], + groups: ['keyword', 'unknown', 'nullish'], type: 'natural', }, ], @@ -682,4 +683,19 @@ export default tseslint.config( ], }, }, + { + files: ['packages/typescript-estree/src/**/*.ts'], + rules: { + 'perfectionist/sort-objects': [ + 'error', + { + customGroups: { + first: ['type'], + second: ['loc', 'range'], + }, + groups: ['first', 'second'], + }, + ], + }, + }, ); diff --git a/packages/ast-spec/src/base/LiteralBase.ts b/packages/ast-spec/src/base/LiteralBase.ts index c878fe7b7aa..f697b5d8ace 100644 --- a/packages/ast-spec/src/base/LiteralBase.ts +++ b/packages/ast-spec/src/base/LiteralBase.ts @@ -4,5 +4,5 @@ import type { BaseNode } from './BaseNode'; export interface LiteralBase extends BaseNode { type: AST_NODE_TYPES.Literal; raw: string; - value: RegExp | bigint | boolean | number | string | null; + value: bigint | boolean | number | string | RegExp | null; } diff --git a/packages/ast-spec/src/type/TSMappedType/spec.ts b/packages/ast-spec/src/type/TSMappedType/spec.ts index c78d4ce5fe9..8ffed262dd5 100644 --- a/packages/ast-spec/src/type/TSMappedType/spec.ts +++ b/packages/ast-spec/src/type/TSMappedType/spec.ts @@ -9,8 +9,8 @@ export interface TSMappedType extends BaseNode { constraint: TypeNode; key: Identifier; nameType: TypeNode | null; - optional: '+' | '-' | boolean | undefined; - readonly: '+' | '-' | boolean | undefined; + optional: boolean | '+' | '-' | undefined; + readonly: boolean | '+' | '-' | undefined; typeAnnotation: TypeNode | undefined; /** @deprecated Use {@link `constraint`} and {@link `key`} instead. */ typeParameter: TSTypeParameter; diff --git a/packages/eslint-plugin/src/rules/ban-ts-comment.ts b/packages/eslint-plugin/src/rules/ban-ts-comment.ts index 3f1379b807f..61ded7aabf4 100644 --- a/packages/eslint-plugin/src/rules/ban-ts-comment.ts +++ b/packages/eslint-plugin/src/rules/ban-ts-comment.ts @@ -5,9 +5,9 @@ import { AST_TOKEN_TYPES } from '@typescript-eslint/utils'; import { createRule, getStringLength, nullThrows } from '../util'; type DirectiveConfig = + | boolean | 'allow-with-description' - | { descriptionFormat: string } - | boolean; + | { descriptionFormat: string }; interface Options { minimumDescriptionLength?: number; diff --git a/packages/eslint-plugin/src/rules/class-methods-use-this.ts b/packages/eslint-plugin/src/rules/class-methods-use-this.ts index f049781f7d6..9283de32630 100644 --- a/packages/eslint-plugin/src/rules/class-methods-use-this.ts +++ b/packages/eslint-plugin/src/rules/class-methods-use-this.ts @@ -13,7 +13,7 @@ type Options = [ { enforceForClassFields?: boolean; exceptMethods?: string[]; - ignoreClassesThatImplementAnInterface?: 'public-fields' | boolean; + ignoreClassesThatImplementAnInterface?: boolean | 'public-fields'; ignoreOverrideMethods?: boolean; }, ]; diff --git a/packages/eslint-plugin/src/rules/member-ordering.ts b/packages/eslint-plugin/src/rules/member-ordering.ts index f845f1f1ae7..83b29715226 100644 --- a/packages/eslint-plugin/src/rules/member-ordering.ts +++ b/packages/eslint-plugin/src/rules/member-ordering.ts @@ -995,7 +995,7 @@ export default createRule({ // Standardize config let order: Order | undefined; - let memberTypes: MemberType[] | string | undefined; + let memberTypes: string | MemberType[] | undefined; let optionalityOrder: OptionalityOrder | undefined; /** diff --git a/packages/eslint-plugin/src/rules/naming-convention-utils/types.ts b/packages/eslint-plugin/src/rules/naming-convention-utils/types.ts index 41df37e8cb1..ecf98d4d517 100644 --- a/packages/eslint-plugin/src/rules/naming-convention-utils/types.ts +++ b/packages/eslint-plugin/src/rules/naming-convention-utils/types.ts @@ -23,7 +23,7 @@ interface MatchRegex { interface Selector { custom?: MatchRegex; - filter?: MatchRegex | string; + filter?: string | MatchRegex; // format options format: PredefinedFormatsString[] | null; leadingUnderscore?: UnderscoreOptionsString; 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 af8ef4d79df..ce41636f9ff 100644 --- a/packages/eslint-plugin/src/rules/no-invalid-void-type.ts +++ b/packages/eslint-plugin/src/rules/no-invalid-void-type.ts @@ -6,7 +6,7 @@ import { createRule } from '../util'; interface Options { allowAsThisParameter?: boolean; - allowInGenericTypeArguments?: [string, ...string[]] | boolean; + allowInGenericTypeArguments?: boolean | [string, ...string[]]; } type MessageIds = diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index 6498b10d0f5..c80b253c650 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -18,7 +18,7 @@ type Options = [ { checksConditionals?: boolean; checksSpreads?: boolean; - checksVoidReturn?: ChecksVoidReturnOptions | boolean; + checksVoidReturn?: boolean | ChecksVoidReturnOptions; }, ]; @@ -43,7 +43,7 @@ type MessageId = | 'voidReturnVariable'; function parseChecksVoidReturn( - checksVoidReturn: ChecksVoidReturnOptions | boolean | undefined, + checksVoidReturn: boolean | ChecksVoidReturnOptions | undefined, ): ChecksVoidReturnOptions | false { switch (checksVoidReturn) { case false: diff --git a/packages/eslint-plugin/src/rules/no-restricted-types.ts b/packages/eslint-plugin/src/rules/no-restricted-types.ts index a6f22308919..6565b0eb2ab 100644 --- a/packages/eslint-plugin/src/rules/no-restricted-types.ts +++ b/packages/eslint-plugin/src/rules/no-restricted-types.ts @@ -6,13 +6,13 @@ import { createRule, objectReduceKey } from '../util'; type Types = Record< string, + | boolean + | string | { fixWith?: string; message: string; suggest?: readonly string[]; } - | boolean - | string | null >; @@ -37,7 +37,7 @@ function stringifyNode( } function getCustomMessage( - bannedType: { fixWith?: string; message?: string } | true | string | null, + bannedType: string | { fixWith?: string; message?: string } | true | null, ): string { if (!bannedType || bannedType === true) { return ''; 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 e89868efd69..712ccff6636 100644 --- a/packages/eslint-plugin/src/rules/no-use-before-define.ts +++ b/packages/eslint-plugin/src/rules/no-use-before-define.ts @@ -12,7 +12,7 @@ const SENTINEL_TYPE = /** * Parses a given value as options. */ -function parseOptions(options: Config | string | null): Required { +function parseOptions(options: string | Config | null): Required { let functions = true; let classes = true; let enums = true; diff --git a/packages/eslint-plugin/src/util/collectUnusedVariables.ts b/packages/eslint-plugin/src/util/collectUnusedVariables.ts index 2e74a5f6a66..065aac8e1f5 100644 --- a/packages/eslint-plugin/src/util/collectUnusedVariables.ts +++ b/packages/eslint-plugin/src/util/collectUnusedVariables.ts @@ -234,7 +234,7 @@ class UnusedVarsVisitor extends Visitor { private markVariableAsUsed(name: string, parent: TSESTree.Node): void; private markVariableAsUsed( - variableOrIdentifierOrName: ScopeVariable | TSESTree.Identifier | string, + variableOrIdentifierOrName: string | ScopeVariable | TSESTree.Identifier, parent?: TSESTree.Node, ): void { if ( diff --git a/packages/eslint-plugin/tests/configs.test.ts b/packages/eslint-plugin/tests/configs.test.ts index 3c62311271d..53382ced824 100644 --- a/packages/eslint-plugin/tests/configs.test.ts +++ b/packages/eslint-plugin/tests/configs.test.ts @@ -17,8 +17,8 @@ const EXTENSION_RULES = Object.entries(rules) ); function filterRules( - values: Record, -): [string, unknown[] | string][] { + values: Record, +): [string, string | unknown[]][] { return Object.entries(values).filter(([name]) => name.startsWith(RULE_NAME_PREFIX), ); @@ -84,7 +84,7 @@ function filterAndMapRuleConfigs({ } function itHasBaseRulesOverriden( - unfilteredConfigRules: Record, + unfilteredConfigRules: Record, ): void { it('has the base rules overriden by the appropriate extension rules', () => { const ruleNames = new Set(Object.keys(unfilteredConfigRules)); @@ -196,7 +196,7 @@ describe('recommended-type-checked-only.ts', () => { }); describe('strict.ts', () => { - const unfilteredConfigRules: Record = + const unfilteredConfigRules: Record = plugin.configs.strict.rules; it('contains all strict rules, excluding type checked ones', () => { @@ -217,7 +217,7 @@ describe('strict.ts', () => { }); describe('strict-type-checked.ts', () => { - const unfilteredConfigRules: Record = + const unfilteredConfigRules: Record = plugin.configs['strict-type-checked'].rules; it('contains all strict rules', () => { @@ -236,7 +236,7 @@ describe('strict-type-checked.ts', () => { }); describe('strict-type-checked-only.ts', () => { - const unfilteredConfigRules: Record = + const unfilteredConfigRules: Record = plugin.configs['strict-type-checked-only'].rules; it('contains only type-checked strict rules', () => { @@ -257,7 +257,7 @@ describe('strict-type-checked-only.ts', () => { }); describe('stylistic.ts', () => { - const unfilteredConfigRules: Record = + const unfilteredConfigRules: Record = plugin.configs.stylistic.rules; it('contains all stylistic rules, excluding deprecated or type checked ones', () => { diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index c94c88e9b93..a0f1d74dad3 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -435,7 +435,7 @@ describe('Validating rule docs', () => { function lintCodeBlock( token: mdast.Code, - shouldContainLintErrors: 'skip-check' | boolean, + shouldContainLintErrors: boolean | 'skip-check', ): void { const lang = token.lang?.trim(); if (!lang || !/^tsx?\b/i.test(lang)) { diff --git a/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts b/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts index bbd655fa5d9..85c2365739c 100644 --- a/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts @@ -30,15 +30,15 @@ const nullishTypes = ['null', 'undefined', 'null | undefined']; const ignorablePrimitiveTypes = ['string', 'number', 'boolean', 'bigint']; function typeValidTest( - cb: (type: string) => ValidTestCase | string, -): (ValidTestCase | string)[] { + cb: (type: string) => string | ValidTestCase, +): (string | ValidTestCase)[] { return types.map(type => cb(type)); } function nullishTypeTest< T extends + | string | InvalidTestCase - | ValidTestCase - | string, + | ValidTestCase, >(cb: (nullish: string, type: string) => T): T[] { return nullishTypes.flatMap(nullish => types.map(type => cb(nullish, type))); } diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index 0b39f36da45..557a6eece2f 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -256,11 +256,11 @@ declare module 'eslint/lib/rules/no-restricted-globals' { const rule: TSESLint.RuleModule< 'customMessage' | 'defaultMessage', ( + | string | { message?: string; name: string; } - | string )[], unknown, { @@ -539,6 +539,7 @@ declare module 'eslint/lib/rules/no-restricted-imports' { namespace rule { export type ArrayOfStringOrObject = ( + | string | { // extended allowTypeImports?: boolean; @@ -546,7 +547,6 @@ declare module 'eslint/lib/rules/no-restricted-imports' { message?: string; name: string; } - | string )[]; export type ArrayOfStringOrObjectPatterns = | { diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index 42f938e980a..2ca5df4304e 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -79,14 +79,14 @@ function getLib(compilerOptions: ts.CompilerOptions): Lib[] { } function parse( - code: ts.SourceFile | string, + code: string | ts.SourceFile, options?: ParserOptions, ): ParseForESLintResult['ast'] { return parseForESLint(code, options).ast; } function parseForESLint( - code: ts.SourceFile | string, + code: string | ts.SourceFile, parserOptions?: ParserOptions | null, ): ParseForESLintResult { if (!parserOptions || typeof parserOptions !== 'object') { diff --git a/packages/rule-tester/src/RuleTester.ts b/packages/rule-tester/src/RuleTester.ts index 0312abe18ac..2c9f91ddf7e 100644 --- a/packages/rule-tester/src/RuleTester.ts +++ b/packages/rule-tester/src/RuleTester.ts @@ -258,7 +258,7 @@ export class RuleTester extends TestFramework { * Adds the `only` property to a test to run it in isolation. */ static only( - item: ValidTestCase | string, + item: string | ValidTestCase, ): ValidTestCase; /** * Adds the `only` property to a test to run it in isolation. @@ -268,9 +268,9 @@ export class RuleTester extends TestFramework { ): InvalidTestCase; static only( item: + | string | InvalidTestCase - | ValidTestCase - | string, + | ValidTestCase, ): InvalidTestCase | ValidTestCase { if (typeof item === 'string') { return { code: item, only: true }; @@ -833,7 +833,7 @@ export class RuleTester extends TestFramework { >( ruleName: string, rule: RuleModule, - itemIn: ValidTestCase | string, + itemIn: string | ValidTestCase, seenValidTestCases: Set, ): void { const item: ValidTestCase = @@ -1374,7 +1374,7 @@ function checkDuplicateTestCase( * value is a regular expression, it is checked against the actual * value. */ -function assertMessageMatches(actual: string, expected: RegExp | string): void { +function assertMessageMatches(actual: string, expected: string | RegExp): void { if (expected instanceof RegExp) { // assert.js doesn't have a built-in RegExp match function assert.ok( diff --git a/packages/rule-tester/src/types/DependencyConstraint.ts b/packages/rule-tester/src/types/DependencyConstraint.ts index 2e9138cd401..c271e8bba3f 100644 --- a/packages/rule-tester/src/types/DependencyConstraint.ts +++ b/packages/rule-tester/src/types/DependencyConstraint.ts @@ -6,7 +6,7 @@ export interface RangeOptions { } export interface SemverVersionConstraint { - readonly options?: RangeOptions | boolean; + readonly options?: boolean | RangeOptions; readonly range: string; } export type AtLeastVersionConstraint = diff --git a/packages/rule-tester/src/types/InvalidTestCase.ts b/packages/rule-tester/src/types/InvalidTestCase.ts index bcd9439f554..73e8f48a677 100644 --- a/packages/rule-tester/src/types/InvalidTestCase.ts +++ b/packages/rule-tester/src/types/InvalidTestCase.ts @@ -76,5 +76,5 @@ export interface InvalidTestCase< /** * The expected code after autofixes are applied. If set to `null`, the test runner will assert that no autofix is suggested. */ - readonly output?: string[] | string | null; + readonly output?: string | string[] | null; } diff --git a/packages/rule-tester/src/types/index.ts b/packages/rule-tester/src/types/index.ts index 6d89f7808fd..becf16d2c5c 100644 --- a/packages/rule-tester/src/types/index.ts +++ b/packages/rule-tester/src/types/index.ts @@ -18,7 +18,7 @@ export interface RunTests< > { readonly invalid: readonly InvalidTestCase[]; // RuleTester.run also accepts strings for valid cases - readonly valid: readonly (ValidTestCase | string)[]; + readonly valid: readonly (string | ValidTestCase)[]; } export interface NormalizedRunTests< diff --git a/packages/rule-tester/src/utils/flat-config-schema.ts b/packages/rule-tester/src/utils/flat-config-schema.ts index d1fe7a89451..55730f8e6b1 100644 --- a/packages/rule-tester/src/utils/flat-config-schema.ts +++ b/packages/rule-tester/src/utils/flat-config-schema.ts @@ -10,8 +10,8 @@ import { normalizeSeverityToNumber } from './severity'; type PluginMemberName = `${string}/${string}`; interface ObjectPropertySchema { - merge: ((a: T, b: T) => T) | string; - validate: ((value: unknown) => asserts value is T) | string; + merge: string | ((a: T, b: T) => T); + validate: string | ((value: unknown) => asserts value is T); } const ruleSeverities = new Map([ @@ -279,8 +279,8 @@ const ALLOWED_SEVERITIES = new Set([0, 1, 2, 'error', 'off', 'warn']); const disableDirectiveSeveritySchema: ObjectPropertySchema = { merge( - first: SharedConfig.RuleLevel | boolean | undefined, - second: SharedConfig.RuleLevel | boolean | undefined, + first: boolean | SharedConfig.RuleLevel | undefined, + second: boolean | SharedConfig.RuleLevel | undefined, ): SharedConfig.RuleLevel { const value = second ?? first; diff --git a/packages/scope-manager/src/scope/ScopeBase.ts b/packages/scope-manager/src/scope/ScopeBase.ts index 2ffb0a7b958..bd86ee66a26 100644 --- a/packages/scope-manager/src/scope/ScopeBase.ts +++ b/packages/scope-manager/src/scope/ScopeBase.ts @@ -374,7 +374,7 @@ abstract class ScopeBase< * References in default parameters isn't resolved to variables which are in their function body. */ protected defineVariable( - nameOrVariable: Variable | string, + nameOrVariable: string | Variable, set: Map, variables: Variable[], node: TSESTree.Identifier | null, diff --git a/packages/scope-manager/tests/test-utils/getSpecificNode.ts b/packages/scope-manager/tests/test-utils/getSpecificNode.ts index a5b6b68b00e..0272aca3d37 100644 --- a/packages/scope-manager/tests/test-utils/getSpecificNode.ts +++ b/packages/scope-manager/tests/test-utils/getSpecificNode.ts @@ -24,7 +24,7 @@ function getSpecificNode< function getSpecificNode( ast: TSESTree.Node, selector: AST_NODE_TYPES, - cb?: (node: TSESTree.Node) => TSESTree.Node | boolean | null | undefined, + cb?: (node: TSESTree.Node) => boolean | TSESTree.Node | null | undefined, ): TSESTree.Node { let node: TSESTree.Node | null | undefined = null; simpleTraverse( diff --git a/packages/types/src/parser-options.ts b/packages/types/src/parser-options.ts index e6f3b404c96..b2257ebb91a 100644 --- a/packages/types/src/parser-options.ts +++ b/packages/types/src/parser-options.ts @@ -2,8 +2,8 @@ import type { Program } from 'typescript'; import type { Lib } from './lib'; -type DebugLevel = ('eslint' | 'typescript' | 'typescript-eslint')[] | boolean; -type CacheDurationSeconds = 'Infinity' | number; +type DebugLevel = boolean | ('eslint' | 'typescript' | 'typescript-eslint')[]; +type CacheDurationSeconds = number | 'Infinity'; type EcmaVersion = | 'latest' @@ -102,9 +102,9 @@ interface ParserOptions { jsxPragma?: string | null; lib?: Lib[]; programs?: Program[] | null; - project?: string[] | boolean | string | null; + project?: boolean | string | string[] | null; projectFolderIgnoreList?: string[]; - projectService?: ProjectServiceOptions | boolean; + projectService?: boolean | ProjectServiceOptions; range?: boolean; sourceType?: SourceType | undefined; tokens?: boolean; diff --git a/packages/typescript-estree/src/ast-converter.ts b/packages/typescript-estree/src/ast-converter.ts index a4cb0a4b7dd..65b993cbefa 100644 --- a/packages/typescript-estree/src/ast-converter.ts +++ b/packages/typescript-estree/src/ast-converter.ts @@ -1,18 +1,19 @@ import type { SourceFile } from 'typescript'; import type { ASTMaps } from './convert'; +import type { ParseSettings } from './parseSettings'; +import type { TSESTree } from './ts-estree'; + import { Converter, convertError } from './convert'; import { convertComments } from './convert-comments'; import { convertTokens } from './node-utils'; -import type { ParseSettings } from './parseSettings'; import { simpleTraverse } from './simple-traverse'; -import type { TSESTree } from './ts-estree'; export function astConverter( ast: SourceFile, parseSettings: ParseSettings, shouldPreserveNodeMaps: boolean, -): { estree: TSESTree.Program; astMaps: ASTMaps } { +): { astMaps: ASTMaps; estree: TSESTree.Program } { /** * The TypeScript compiler produced fundamental parse errors when parsing the * source. @@ -71,5 +72,5 @@ export function astConverter( const astMaps = instance.getASTMaps(); - return { estree, astMaps }; + return { astMaps, estree }; } diff --git a/packages/typescript-estree/src/convert-comments.ts b/packages/typescript-estree/src/convert-comments.ts index db1278baa4c..234f62ec802 100644 --- a/packages/typescript-estree/src/convert-comments.ts +++ b/packages/typescript-estree/src/convert-comments.ts @@ -1,8 +1,9 @@ import * as tsutils from 'ts-api-utils'; import * as ts from 'typescript'; -import { getLocFor } from './node-utils'; import type { TSESTree } from './ts-estree'; + +import { getLocFor } from './node-utils'; import { AST_TOKEN_TYPES } from './ts-estree'; /** @@ -38,9 +39,9 @@ export function convertComments( range[1] - textStart - 2; comments.push({ type, - value: code.slice(textStart, textStart + textEnd), - range, loc, + range, + value: code.slice(textStart, textStart + textEnd), }); }, ast, diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 5cb94c19e77..b014d885bf3 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -2,8 +2,15 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unnecessary-condition, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access */ import * as ts from 'typescript'; -import { getDecorators, getModifiers } from './getModifiers'; import type { TSError } from './node-utils'; +import type { + ParserWeakMap, + ParserWeakMapESTreeToTSNode, +} from './parser-options'; +import type { SemanticOrSyntacticError } from './semantic-or-syntactic-errors'; +import type { TSESTree, TSESTreeToTSNode, TSNode } from './ts-estree'; + +import { getDecorators, getModifiers } from './getModifiers'; import { canContainDirective, createError, @@ -32,12 +39,6 @@ import { nodeIsPresent, unescapeStringLiteralText, } from './node-utils'; -import type { - ParserWeakMap, - ParserWeakMapESTreeToTSNode, -} from './parser-options'; -import type { SemanticOrSyntacticError } from './semantic-or-syntactic-errors'; -import type { TSESTree, TSESTreeToTSNode, TSNode } from './ts-estree'; import { AST_NODE_TYPES } from './ts-estree'; const SyntaxKind = ts.SyntaxKind; @@ -70,13 +71,12 @@ export interface ASTMaps { } export class Converter { + private allowPattern = false; private readonly ast: ts.SourceFile; - private readonly options: ConverterOptions; private readonly esTreeNodeToTSNodeMap = new WeakMap(); + private readonly options: ConverterOptions; private readonly tsNodeToESTreeNodeMap = new WeakMap(); - private allowPattern = false; - /** * Converts a TypeScript node into an ESTree node * @param ast the full TypeScript AST @@ -88,153 +88,525 @@ export class Converter { this.options = { ...options }; } - getASTMaps(): ASTMaps { - return { - esTreeNodeToTSNodeMap: this.esTreeNodeToTSNodeMap, - tsNodeToESTreeNodeMap: this.tsNodeToESTreeNodeMap, - }; - } - - convertProgram(): TSESTree.Program { - return this.converter(this.ast) as TSESTree.Program; + #checkForStatementDeclaration( + initializer: ts.ForInitializer, + kind: ts.SyntaxKind.ForInStatement | ts.SyntaxKind.ForOfStatement, + ): void { + const loop = + kind === ts.SyntaxKind.ForInStatement ? 'for...in' : 'for...of'; + if (ts.isVariableDeclarationList(initializer)) { + if (initializer.declarations.length !== 1) { + this.#throwError( + initializer, + `Only a single variable declaration is allowed in a '${loop}' statement.`, + ); + } + const declaration = initializer.declarations[0]; + if (declaration.initializer) { + this.#throwError( + declaration, + `The variable declaration of a '${loop}' statement cannot have an initializer.`, + ); + } else if (declaration.type) { + this.#throwError( + declaration, + `The variable declaration of a '${loop}' statement cannot have a type annotation.`, + ); + } + if ( + kind === ts.SyntaxKind.ForInStatement && + initializer.flags & ts.NodeFlags.Using + ) { + this.#throwError( + initializer, + "The left-hand side of a 'for...in' statement cannot be a 'using' declaration.", + ); + } + } else if ( + !isValidAssignmentTarget(initializer) && + initializer.kind !== ts.SyntaxKind.ObjectLiteralExpression && + initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression + ) { + this.#throwError( + initializer, + `The left-hand side of a '${loop}' statement must be a variable or a property access.`, + ); + } } - /** - * Converts a TypeScript node into an ESTree node. - * @param node the child ts.Node - * @param parent parentNode - * @param allowPattern flag to determine if patterns are allowed - * @returns the converted ESTree node - */ - private converter( - node?: ts.Node, - parent?: ts.Node, - allowPattern?: boolean, - ): any { - /** - * Exit early for null and undefined - */ - if (!node) { - return null; + #checkModifiers(node: ts.Node): void { + if (this.options.allowInvalidAST) { + return; } - this.#checkModifiers(node); - - const pattern = this.allowPattern; - if (allowPattern !== undefined) { - this.allowPattern = allowPattern; + // typescript<5.0.0 + if (nodeHasIllegalDecorators(node)) { + this.#throwError( + node.illegalDecorators[0], + 'Decorators are not valid here.', + ); } - const result = this.convertNode( - node as TSNode, - (parent ?? node.parent) as TSNode, - ); - - this.registerTSNodeInNodeMap(node, result); - - this.allowPattern = pattern; - return result; - } - - /** - * Fixes the exports of the given ts.Node - * @returns the ESTreeNode with fixed exports - */ - private fixExports< - T extends - | TSESTree.DefaultExportDeclarations - | TSESTree.NamedExportDeclarations, - >( - node: - | ts.ClassDeclaration - | ts.ClassExpression - | ts.EnumDeclaration - | ts.FunctionDeclaration - | ts.ImportEqualsDeclaration - | ts.InterfaceDeclaration - | ts.ModuleDeclaration - | ts.TypeAliasDeclaration - | ts.VariableStatement, - result: T, - ): T | TSESTree.ExportDefaultDeclaration | TSESTree.ExportNamedDeclaration { - const isNamespaceNode = - ts.isModuleDeclaration(node) && - Boolean(node.flags & ts.NodeFlags.Namespace); - - const modifiers = isNamespaceNode - ? getNamespaceModifiers(node) - : getModifiers(node); + for (const decorator of getDecorators( + node, + /* includeIllegalDecorators */ true, + ) ?? []) { + // `checkGrammarModifiers` function in typescript + if (!nodeCanBeDecorated(node as TSNode)) { + if (ts.isMethodDeclaration(node) && !nodeIsPresent(node.body)) { + this.#throwError( + decorator, + 'A decorator can only decorate a method implementation, not an overload.', + ); + } else { + this.#throwError(decorator, 'Decorators are not valid here.'); + } + } + } - if (modifiers?.[0].kind === SyntaxKind.ExportKeyword) { - /** - * Make sure that original node is registered instead of export - */ - this.registerTSNodeInNodeMap(node, result); + for (const modifier of getModifiers( + node, + /* includeIllegalModifiers */ true, + ) ?? []) { + if (modifier.kind !== SyntaxKind.ReadonlyKeyword) { + if ( + node.kind === SyntaxKind.PropertySignature || + node.kind === SyntaxKind.MethodSignature + ) { + this.#throwError( + modifier, + `'${ts.tokenToString( + modifier.kind, + )}' modifier cannot appear on a type member`, + ); + } - const exportKeyword = modifiers[0]; - const nextModifier = modifiers[1]; - const declarationIsDefault = - nextModifier?.kind === SyntaxKind.DefaultKeyword; + if ( + node.kind === SyntaxKind.IndexSignature && + (modifier.kind !== SyntaxKind.StaticKeyword || + !ts.isClassLike(node.parent)) + ) { + this.#throwError( + modifier, + `'${ts.tokenToString( + modifier.kind, + )}' modifier cannot appear on an index signature`, + ); + } + } - const varToken = declarationIsDefault - ? findNextToken(nextModifier, this.ast, this.ast) - : findNextToken(exportKeyword, this.ast, this.ast); + if ( + modifier.kind !== SyntaxKind.InKeyword && + modifier.kind !== SyntaxKind.OutKeyword && + modifier.kind !== SyntaxKind.ConstKeyword && + node.kind === SyntaxKind.TypeParameter + ) { + this.#throwError( + modifier, + `'${ts.tokenToString( + modifier.kind, + )}' modifier cannot appear on a type parameter`, + ); + } - result.range[0] = varToken!.getStart(this.ast); - result.loc = getLocFor(result.range, this.ast); + if ( + (modifier.kind === SyntaxKind.InKeyword || + modifier.kind === SyntaxKind.OutKeyword) && + (node.kind !== SyntaxKind.TypeParameter || + !( + ts.isInterfaceDeclaration(node.parent) || + ts.isClassLike(node.parent) || + ts.isTypeAliasDeclaration(node.parent) + )) + ) { + this.#throwError( + modifier, + `'${ts.tokenToString( + modifier.kind, + )}' modifier can only appear on a type parameter of a class, interface or type alias`, + ); + } - if (declarationIsDefault) { - return this.createNode( - node as Exclude, - { - type: AST_NODE_TYPES.ExportDefaultDeclaration, - declaration: result as TSESTree.DefaultExportDeclarations, - range: [exportKeyword.getStart(this.ast), result.range[1]], - exportKind: 'value', - }, + if ( + modifier.kind === SyntaxKind.ReadonlyKeyword && + node.kind !== SyntaxKind.PropertyDeclaration && + node.kind !== SyntaxKind.PropertySignature && + node.kind !== SyntaxKind.IndexSignature && + node.kind !== SyntaxKind.Parameter + ) { + this.#throwError( + modifier, + "'readonly' modifier can only appear on a property declaration or index signature.", ); } - const isType = - result.type === AST_NODE_TYPES.TSInterfaceDeclaration || - result.type === AST_NODE_TYPES.TSTypeAliasDeclaration; - const isDeclare = 'declare' in result && result.declare; - return this.createNode( - node, - // @ts-expect-error - TODO, narrow the types here - this.#withDeprecatedAliasGetter( - { - type: AST_NODE_TYPES.ExportNamedDeclaration, - declaration: result, - specifiers: [], - source: null, - exportKind: isType || isDeclare ? 'type' : 'value', - range: [exportKeyword.getStart(this.ast), result.range[1]], - attributes: [], + + if ( + modifier.kind === SyntaxKind.DeclareKeyword && + ts.isClassLike(node.parent) && + !ts.isPropertyDeclaration(node) + ) { + this.#throwError( + modifier, + `'${ts.tokenToString( + modifier.kind, + )}' modifier cannot appear on class elements of this kind.`, + ); + } + + if ( + modifier.kind === SyntaxKind.DeclareKeyword && + ts.isVariableStatement(node) + ) { + const declarationKind = getDeclarationKind(node.declarationList); + if (declarationKind === 'using' || declarationKind === 'await using') { + this.#throwError( + modifier, + `'declare' modifier cannot appear on a '${declarationKind}' declaration.`, + ); + } + } + + if ( + modifier.kind === SyntaxKind.AbstractKeyword && + node.kind !== SyntaxKind.ClassDeclaration && + node.kind !== SyntaxKind.ConstructorType && + node.kind !== SyntaxKind.MethodDeclaration && + node.kind !== SyntaxKind.PropertyDeclaration && + node.kind !== SyntaxKind.GetAccessor && + node.kind !== SyntaxKind.SetAccessor + ) { + this.#throwError( + modifier, + `'${ts.tokenToString( + modifier.kind, + )}' modifier can only appear on a class, method, or property declaration.`, + ); + } + + if ( + (modifier.kind === SyntaxKind.StaticKeyword || + modifier.kind === SyntaxKind.PublicKeyword || + modifier.kind === SyntaxKind.ProtectedKeyword || + modifier.kind === SyntaxKind.PrivateKeyword) && + (node.parent.kind === SyntaxKind.ModuleBlock || + node.parent.kind === SyntaxKind.SourceFile) + ) { + this.#throwError( + modifier, + `'${ts.tokenToString( + modifier.kind, + )}' modifier cannot appear on a module or namespace element.`, + ); + } + + if ( + modifier.kind === SyntaxKind.AccessorKeyword && + node.kind !== SyntaxKind.PropertyDeclaration + ) { + this.#throwError( + modifier, + "'accessor' modifier can only appear on a property declaration.", + ); + } + + // `checkGrammarAsyncModifier` function in `typescript` + if ( + modifier.kind === SyntaxKind.AsyncKeyword && + node.kind !== SyntaxKind.MethodDeclaration && + node.kind !== SyntaxKind.FunctionDeclaration && + node.kind !== SyntaxKind.FunctionExpression && + node.kind !== SyntaxKind.ArrowFunction + ) { + this.#throwError(modifier, "'async' modifier cannot be used here."); + } + + // `checkGrammarModifiers` function in `typescript` + if ( + node.kind === SyntaxKind.Parameter && + (modifier.kind === SyntaxKind.StaticKeyword || + modifier.kind === SyntaxKind.ExportKeyword || + modifier.kind === SyntaxKind.DeclareKeyword || + modifier.kind === SyntaxKind.AsyncKeyword) + ) { + this.#throwError( + modifier, + `'${ts.tokenToString( + modifier.kind, + )}' modifier cannot appear on a parameter.`, + ); + } + + // `checkGrammarModifiers` function in `typescript` + if ( + modifier.kind === SyntaxKind.PublicKeyword || + modifier.kind === SyntaxKind.ProtectedKeyword || + modifier.kind === SyntaxKind.PrivateKeyword + ) { + for (const anotherModifier of getModifiers(node) ?? []) { + if ( + anotherModifier !== modifier && + (anotherModifier.kind === SyntaxKind.PublicKeyword || + anotherModifier.kind === SyntaxKind.ProtectedKeyword || + anotherModifier.kind === SyntaxKind.PrivateKeyword) + ) { + this.#throwError( + anotherModifier, + `Accessibility modifier already seen.`, + ); + } + } + } + + // `checkParameter` function in `typescript` + if ( + node.kind === SyntaxKind.Parameter && + // In `typescript` package, it's `ts.hasSyntacticModifier(node, ts.ModifierFlags.ParameterPropertyModifier)` + // https://github.com/typescript-eslint/typescript-eslint/pull/6615#discussion_r1136489935 + (modifier.kind === SyntaxKind.PublicKeyword || + modifier.kind === SyntaxKind.PrivateKeyword || + modifier.kind === SyntaxKind.ProtectedKeyword || + modifier.kind === SyntaxKind.ReadonlyKeyword || + modifier.kind === SyntaxKind.OverrideKeyword) + ) { + const func = getContainingFunction(node)!; + + if ( + !(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body)) + ) { + this.#throwError( + modifier, + 'A parameter property is only allowed in a constructor implementation.', + ); + } + } + } + } + + #throwError(node: number | ts.Node, message: string): asserts node is never { + let start; + let end; + if (typeof node === 'number') { + start = end = node; + } else { + start = node.getStart(this.ast); + end = node.getEnd(); + } + + throw createError(message, this.ast, start, end); + } + + #throwUnlessAllowInvalidAST( + node: number | ts.Node, + message: string, + ): asserts node is never { + if (!this.options.allowInvalidAST) { + this.#throwError(node, message); + } + } + + /** + * Creates a getter for a property under aliasKey that returns the value under + * valueKey. If suppressDeprecatedPropertyWarnings is not enabled, the + * getter also console warns about the deprecation. + * + * @see https://github.com/typescript-eslint/typescript-eslint/issues/6469 + */ + #withDeprecatedAliasGetter< + Properties extends { type: string }, + AliasKey extends string, + ValueKey extends keyof Properties & string, + >( + node: Properties, + aliasKey: AliasKey, + valueKey: ValueKey, + suppressWarnings = false, + ): Properties & Record { + let warned = suppressWarnings; + + Object.defineProperty(node, aliasKey, { + configurable: true, + get: this.options.suppressDeprecatedPropertyWarnings + ? (): Properties[typeof valueKey] => node[valueKey] + : (): Properties[typeof valueKey] => { + if (!warned) { + process.emitWarning( + `The '${aliasKey}' property is deprecated on ${node.type} nodes. Use '${valueKey}' instead. See https://typescript-eslint.io/troubleshooting/faqs/general#the-key-property-is-deprecated-on-type-nodes-use-key-instead-warnings.`, + 'DeprecationWarning', + ); + warned = true; + } + + return node[valueKey]; }, - 'assertions', - 'attributes', - true, - ), + set(value): void { + Object.defineProperty(node, aliasKey, { + enumerable: true, + value, + writable: true, + }); + }, + }); + + return node as Properties & Record; + } + + #withDeprecatedGetter< + Properties extends { type: string }, + Key extends string, + Value, + >( + node: Properties, + deprecatedKey: Key, + preferredKey: string, + value: Value, + ): Properties & Record { + let warned = false; + + Object.defineProperty(node, deprecatedKey, { + configurable: true, + get: this.options.suppressDeprecatedPropertyWarnings + ? (): Value => value + : (): Value => { + if (!warned) { + process.emitWarning( + `The '${deprecatedKey}' property is deprecated on ${node.type} nodes. Use ${preferredKey} instead. See https://typescript-eslint.io/troubleshooting/faqs/general#the-key-property-is-deprecated-on-type-nodes-use-key-instead-warnings.`, + 'DeprecationWarning', + ); + warned = true; + } + + return value; + }, + set(value): void { + Object.defineProperty(node, deprecatedKey, { + enumerable: true, + value, + writable: true, + }); + }, + }); + + return node as Properties & Record; + } + + private assertModuleSpecifier( + node: ts.ExportDeclaration | ts.ImportDeclaration, + allowNull: boolean, + ): void { + if (!allowNull && node.moduleSpecifier == null) { + this.#throwUnlessAllowInvalidAST( + node, + 'Module specifier must be a string literal.', + ); + } + + if ( + node.moduleSpecifier && + node.moduleSpecifier?.kind !== SyntaxKind.StringLiteral + ) { + this.#throwUnlessAllowInvalidAST( + node.moduleSpecifier, + 'Module specifier must be a string literal.', ); } + } + + private convertBindingNameWithTypeAnnotation( + name: ts.BindingName, + tsType: ts.TypeNode | undefined, + parent?: ts.Node, + ): TSESTree.BindingName { + const id = this.convertPattern(name) as TSESTree.BindingName; + + if (tsType) { + id.typeAnnotation = this.convertTypeAnnotation(tsType, parent); + this.fixParentLocation(id, id.typeAnnotation.range); + } + + return id; + } + + /** + * Coverts body Nodes and add a directive field to StringLiterals + * @param nodes of ts.Node + * @param parent parentNode + * @returns Array of body statements + */ + private convertBodyExpressions( + nodes: ts.NodeArray, + parent: + | ts.Block + | ts.ClassStaticBlockDeclaration + | ts.ModuleBlock + | ts.SourceFile, + ): TSESTree.Statement[] { + let allowDirectives = canContainDirective(parent); + + return ( + nodes + .map(statement => { + const child = this.convertChild(statement); + if (allowDirectives) { + if ( + child?.expression && + ts.isExpressionStatement(statement) && + ts.isStringLiteral(statement.expression) + ) { + const raw = child.expression.raw; + child.directive = raw.slice(1, -1); + return child; // child can be null, but it's filtered below + } + allowDirectives = false; + } + return child; // child can be null, but it's filtered below + }) + // filter out unknown nodes for now + .filter(statement => statement) + ); + } + + private convertChainExpression( + node: TSESTree.ChainElement, + tsNode: + | ts.CallExpression + | ts.ElementAccessExpression + | ts.NonNullExpression + | ts.PropertyAccessExpression, + ): TSESTree.ChainElement | TSESTree.ChainExpression { + const { child, isOptional } = ((): { + child: TSESTree.Node; + isOptional: boolean; + } => { + if (node.type === AST_NODE_TYPES.MemberExpression) { + return { child: node.object, isOptional: node.optional }; + } + if (node.type === AST_NODE_TYPES.CallExpression) { + return { child: node.callee, isOptional: node.optional }; + } + return { child: node.expression, isOptional: false }; + })(); + const isChildUnwrappable = isChildUnwrappableOptionalChain(tsNode, child); - return result; - } + if (!isChildUnwrappable && !isOptional) { + return node; + } - /** - * Register specific TypeScript node into map with first ESTree node provided - */ - private registerTSNodeInNodeMap( - node: ts.Node, - result: TSESTree.Node | null, - ): void { - if ( - result && - this.options.shouldPreserveNodeMaps && - !this.tsNodeToESTreeNodeMap.has(node) - ) { - this.tsNodeToESTreeNodeMap.set(node, result); + if (isChildUnwrappable && isChainExpression(child)) { + // unwrap the chain expression child + const newChild = child.expression; + if (node.type === AST_NODE_TYPES.MemberExpression) { + node.object = newChild; + } else if (node.type === AST_NODE_TYPES.CallExpression) { + node.callee = newChild; + } else { + node.expression = newChild; + } } + + return this.createNode(tsNode, { + type: AST_NODE_TYPES.ChainExpression, + expression: node, + }); } /** @@ -243,8 +615,8 @@ export class Converter { * @param parent parentNode * @returns the converted ESTree node */ - private convertPattern(child?: ts.Node, parent?: ts.Node): any { - return this.converter(child, parent, true); + private convertChild(child?: ts.Node, parent?: ts.Node): any { + return this.converter(child, parent, false); } /** @@ -253,38 +625,8 @@ export class Converter { * @param parent parentNode * @returns the converted ESTree node */ - private convertChild(child?: ts.Node, parent?: ts.Node): any { - return this.converter(child, parent, false); - } - - private createNode( - // The 'parent' property will be added later if specified - node: Omit, 'parent'>, - data: Omit, 'parent'>, - ): T { - const result = data; - result.range ??= getRange(node, this.ast); - result.loc ??= getLocFor(result.range, this.ast); - - if (result && this.options.shouldPreserveNodeMaps) { - this.esTreeNodeToTSNodeMap.set(result, node); - } - return result as T; - } - - private convertBindingNameWithTypeAnnotation( - name: ts.BindingName, - tsType: ts.TypeNode | undefined, - parent?: ts.Node, - ): TSESTree.BindingName { - const id = this.convertPattern(name) as TSESTree.BindingName; - - if (tsType) { - id.typeAnnotation = this.convertTypeAnnotation(tsType, parent); - this.fixParentLocation(id, id.typeAnnotation.range); - } - - return id; + private convertPattern(child?: ts.Node, parent?: ts.Node): any { + return this.converter(child, parent, true); } /** @@ -316,45 +658,6 @@ export class Converter { } as TSESTree.TSTypeAnnotation; } - /** - * Coverts body Nodes and add a directive field to StringLiterals - * @param nodes of ts.Node - * @param parent parentNode - * @returns Array of body statements - */ - private convertBodyExpressions( - nodes: ts.NodeArray, - parent: - | ts.Block - | ts.ClassStaticBlockDeclaration - | ts.ModuleBlock - | ts.SourceFile, - ): TSESTree.Statement[] { - let allowDirectives = canContainDirective(parent); - - return ( - nodes - .map(statement => { - const child = this.convertChild(statement); - if (allowDirectives) { - if ( - child?.expression && - ts.isExpressionStatement(statement) && - ts.isStringLiteral(statement.expression) - ) { - const raw = child.expression.raw; - child.directive = raw.slice(1, -1); - return child; // child can be null, but it's filtered below - } - allowDirectives = false; - } - return child; // child can be null, but it's filtered below - }) - // filter out unknown nodes for now - .filter(statement => statement) - ); - } - /** * Converts a ts.Node's typeArguments to TSTypeParameterInstantiation node * @param typeArguments ts.NodeArray typeArguments @@ -392,8 +695,8 @@ export class Converter { return { type: AST_NODE_TYPES.TSTypeParameterDeclaration, - range, loc: getLocFor(range, this.ast), + range, params: typeParameters.map(typeParameter => this.convertChild(typeParameter), ), @@ -421,145 +724,51 @@ export class Converter { }); } - private convertChainExpression( - node: TSESTree.ChainElement, - tsNode: - | ts.CallExpression - | ts.ElementAccessExpression - | ts.NonNullExpression - | ts.PropertyAccessExpression, - ): TSESTree.ChainElement | TSESTree.ChainExpression { - const { child, isOptional } = ((): { - child: TSESTree.Node; - isOptional: boolean; - } => { - if (node.type === AST_NODE_TYPES.MemberExpression) { - return { child: node.object, isOptional: node.optional }; - } - if (node.type === AST_NODE_TYPES.CallExpression) { - return { child: node.callee, isOptional: node.optional }; - } - return { child: node.expression, isOptional: false }; - })(); - const isChildUnwrappable = isChildUnwrappableOptionalChain(tsNode, child); - - if (!isChildUnwrappable && !isOptional) { - return node; - } - - if (isChildUnwrappable && isChainExpression(child)) { - // unwrap the chain expression child - const newChild = child.expression; - if (node.type === AST_NODE_TYPES.MemberExpression) { - node.object = newChild; - } else if (node.type === AST_NODE_TYPES.CallExpression) { - node.callee = newChild; - } else { - node.expression = newChild; - } - } - - return this.createNode(tsNode, { - type: AST_NODE_TYPES.ChainExpression, - expression: node, - }); - } - /** - * For nodes that are copied directly from the TypeScript AST into - * ESTree mostly as-is. The only difference is the addition of a type - * property instead of a kind property. Recursively copies all children. + * Converts a TypeScript node into an ESTree node. + * @param node the child ts.Node + * @param parent parentNode + * @param allowPattern flag to determine if patterns are allowed + * @returns the converted ESTree node */ - private deeplyCopy(node: TSNode): any { - if (node.kind === ts.SyntaxKind.JSDocFunctionType) { - this.#throwError( - node, - 'JSDoc types can only be used inside documentation comments.', - ); - } - - const customType = `TS${SyntaxKind[node.kind]}` as AST_NODE_TYPES; - + private converter( + node?: ts.Node, + parent?: ts.Node, + allowPattern?: boolean, + ): any { /** - * If the "errorOnUnknownASTType" option is set to true, throw an error, - * otherwise fallback to just including the unknown type as-is. + * Exit early for null and undefined */ - if (this.options.errorOnUnknownASTType && !AST_NODE_TYPES[customType]) { - throw new Error(`Unknown AST_NODE_TYPE: "${customType}"`); + if (!node) { + return null; } - const result = this.createNode(node, { - type: customType, - }); + this.#checkModifiers(node); - if ('type' in node) { - result.typeAnnotation = - node.type && 'kind' in node.type && ts.isTypeNode(node.type) - ? this.convertTypeAnnotation(node.type, node) - : null; - } - if ('typeArguments' in node) { - result.typeArguments = - node.typeArguments && 'pos' in node.typeArguments - ? this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ) - : null; - } - if ('typeParameters' in node) { - result.typeParameters = - node.typeParameters && 'pos' in node.typeParameters - ? this.convertTSTypeParametersToTypeParametersDeclaration( - node.typeParameters, - ) - : null; - } - const decorators = getDecorators(node); - if (decorators?.length) { - result.decorators = decorators.map(el => this.convertChild(el)); + const pattern = this.allowPattern; + if (allowPattern !== undefined) { + this.allowPattern = allowPattern; } - // keys we never want to clone from the base typescript node as they - // introduce garbage into our AST - const KEYS_TO_NOT_COPY = new Set([ - '_children', - 'decorators', - 'end', - 'flags', - 'illegalDecorators', - 'heritageClauses', - 'locals', - 'localSymbol', - 'jsDoc', - 'kind', - 'modifierFlagsCache', - 'modifiers', - 'nextContainer', - 'parent', - 'pos', - 'symbol', - 'transformFlags', - 'type', - 'typeArguments', - 'typeParameters', - ]); + const result = this.convertNode( + node as TSNode, + (parent ?? node.parent) as TSNode, + ); - Object.entries(node) - .filter(([key]) => !KEYS_TO_NOT_COPY.has(key)) - .forEach(([key, value]) => { - if (Array.isArray(value)) { - result[key] = value.map(el => this.convertChild(el as TSNode)); - } else if (value && typeof value === 'object' && value.kind) { - // need to check node[key].kind to ensure we don't try to convert a symbol - result[key] = this.convertChild(value as TSNode); - } else { - result[key] = value; - } - }); + this.registerTSNodeInNodeMap(node, result); + + this.allowPattern = pattern; return result; } + private convertImportAttributes( + node: ts.ImportAttributes | undefined, + ): TSESTree.ImportAttribute[] { + return node === undefined + ? [] + : node.elements.map(element => this.convertChild(element)); + } + private convertJSXIdentifier( node: ts.Identifier | ts.ThisExpression, ): TSESTree.JSXIdentifier { @@ -572,21 +781,21 @@ export class Converter { } private convertJSXNamespaceOrIdentifier( - node: ts.JsxNamespacedName | ts.Identifier | ts.ThisExpression, + node: ts.Identifier | ts.JsxNamespacedName | ts.ThisExpression, ): TSESTree.JSXIdentifier | TSESTree.JSXNamespacedName { // TypeScript@5.1 added in ts.JsxNamespacedName directly // We prefer using that if it's relevant for this node type if (node.kind === ts.SyntaxKind.JsxNamespacedName) { const result = this.createNode(node, { type: AST_NODE_TYPES.JSXNamespacedName, - namespace: this.createNode(node.namespace, { - type: AST_NODE_TYPES.JSXIdentifier, - name: node.namespace.text, - }), name: this.createNode(node.name, { type: AST_NODE_TYPES.JSXIdentifier, name: node.name.text, }), + namespace: this.createNode(node.namespace, { + type: AST_NODE_TYPES.JSXIdentifier, + name: node.namespace.text, + }), }); this.registerTSNodeInNodeMap(node, result); return result; @@ -601,17 +810,17 @@ export class Converter { // @ts-expect-error -- TypeScript@<5.1 doesn't have ts.JsxNamespacedName const result = this.createNode(node, { type: AST_NODE_TYPES.JSXNamespacedName, - namespace: this.createNode(node, { - type: AST_NODE_TYPES.JSXIdentifier, - name: text.slice(0, colonIndex), - range: [range[0], range[0] + colonIndex], - }), + range, name: this.createNode(node, { type: AST_NODE_TYPES.JSXIdentifier, - name: text.slice(colonIndex + 1), range: [range[0] + colonIndex + 1, range[1]], + name: text.slice(colonIndex + 1), + }), + namespace: this.createNode(node, { + type: AST_NODE_TYPES.JSXIdentifier, + range: [range[0], range[0] + colonIndex], + name: text.slice(0, colonIndex), }), - range, }); this.registerTSNodeInNodeMap(node, result); return result; @@ -680,8 +889,8 @@ export class Converter { })(), optional: isOptional(node), params: this.convertParameters(node.parameters), - returnType: node.type && this.convertTypeAnnotation(node.type, node), readonly: hasModifier(SyntaxKind.ReadonlyKeyword, node), + returnType: node.type && this.convertTypeAnnotation(node.type, node), static: hasModifier(SyntaxKind.StaticKeyword, node), typeParameters: node.typeParameters && @@ -691,14 +900,6 @@ export class Converter { }); } - private convertImportAttributes( - node: ts.ImportAttributes | undefined, - ): TSESTree.ImportAttribute[] { - return node === undefined - ? [] - : node.elements.map(element => this.convertChild(element)); - } - /** * 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 @@ -718,28 +919,6 @@ export class Converter { } } - private assertModuleSpecifier( - node: ts.ExportDeclaration | ts.ImportDeclaration, - allowNull: boolean, - ): void { - if (!allowNull && node.moduleSpecifier == null) { - this.#throwUnlessAllowInvalidAST( - node, - 'Module specifier must be a string literal.', - ); - } - - if ( - node.moduleSpecifier && - node.moduleSpecifier?.kind !== SyntaxKind.StringLiteral - ) { - this.#throwUnlessAllowInvalidAST( - node.moduleSpecifier, - 'Module specifier must be a string literal.', - ); - } - } - /** * Converts a TypeScript node into an ESTree node. * The core of the conversion logic: @@ -751,9 +930,9 @@ export class Converter { case SyntaxKind.SourceFile: { return this.createNode(node, { type: AST_NODE_TYPES.Program, + range: [node.getStart(this.ast), node.endOfFileToken.end], body: this.convertBodyExpressions(node.statements, node), comments: undefined, - range: [node.getStart(this.ast), node.endOfFileToken.end], sourceType: node.externalModuleIndicator ? 'module' : 'script', tokens: undefined, }); @@ -794,8 +973,8 @@ export class Converter { case SyntaxKind.WithStatement: return this.createNode(node, { type: AST_NODE_TYPES.WithStatement, - object: this.convertChild(node.expression), body: this.convertChild(node.statement), + object: this.convertChild(node.expression), }); // Control Flow @@ -809,8 +988,8 @@ export class Converter { case SyntaxKind.LabeledStatement: return this.createNode(node, { type: AST_NODE_TYPES.LabeledStatement, - label: this.convertChild(node.label), body: this.convertChild(node.statement), + label: this.convertChild(node.label), }); case SyntaxKind.ContinueStatement: @@ -830,9 +1009,9 @@ export class Converter { case SyntaxKind.IfStatement: return this.createNode(node, { type: AST_NODE_TYPES.IfStatement, - test: this.convertChild(node.expression), - consequent: this.convertChild(node.thenStatement), alternate: this.convertChild(node.elseStatement), + consequent: this.convertChild(node.thenStatement), + test: this.convertChild(node.expression), }); case SyntaxKind.SwitchStatement: @@ -849,8 +1028,8 @@ export class Converter { return this.createNode(node, { type: AST_NODE_TYPES.SwitchStatement, - discriminant: this.convertChild(node.expression), cases: node.caseBlock.clauses.map(el => this.convertChild(el)), + discriminant: this.convertChild(node.expression), }); case SyntaxKind.CaseClause: @@ -858,11 +1037,11 @@ export class Converter { return this.createNode(node, { type: AST_NODE_TYPES.SwitchCase, // expression is present in case only + consequent: node.statements.map(el => this.convertChild(el)), test: node.kind === SyntaxKind.CaseClause ? this.convertChild(node.expression) : null, - consequent: node.statements.map(el => this.convertChild(el)), }); // Exceptions @@ -884,8 +1063,8 @@ export class Converter { return this.createNode(node, { type: AST_NODE_TYPES.TryStatement, block: this.convertChild(node.tryBlock), - handler: this.convertChild(node.catchClause), finalizer: this.convertChild(node.finallyBlock), + handler: this.convertChild(node.catchClause), }); case SyntaxKind.CatchClause: @@ -897,13 +1076,13 @@ export class Converter { } return this.createNode(node, { type: AST_NODE_TYPES.CatchClause, + body: this.convertChild(node.block), param: node.variableDeclaration ? this.convertBindingNameWithTypeAnnotation( node.variableDeclaration.name, node.variableDeclaration.type, ) : null, - body: this.convertChild(node.block), }); // Loops @@ -911,8 +1090,8 @@ export class Converter { case SyntaxKind.WhileStatement: return this.createNode(node, { type: AST_NODE_TYPES.WhileStatement, - test: this.convertChild(node.expression), body: this.convertChild(node.statement), + test: this.convertChild(node.expression), }); /** @@ -922,39 +1101,39 @@ export class Converter { case SyntaxKind.DoStatement: return this.createNode(node, { type: AST_NODE_TYPES.DoWhileStatement, - test: this.convertChild(node.expression), body: this.convertChild(node.statement), + test: this.convertChild(node.expression), }); case SyntaxKind.ForStatement: return this.createNode(node, { type: AST_NODE_TYPES.ForStatement, + body: this.convertChild(node.statement), init: this.convertChild(node.initializer), test: this.convertChild(node.condition), update: this.convertChild(node.incrementor), - body: this.convertChild(node.statement), }); case SyntaxKind.ForInStatement: this.#checkForStatementDeclaration(node.initializer, node.kind); return this.createNode(node, { type: AST_NODE_TYPES.ForInStatement, + body: this.convertChild(node.statement), left: this.convertPattern(node.initializer), right: this.convertChild(node.expression), - body: this.convertChild(node.statement), }); case SyntaxKind.ForOfStatement: { this.#checkForStatementDeclaration(node.initializer, node.kind); return this.createNode(node, { type: AST_NODE_TYPES.ForOfStatement, - left: this.convertPattern(node.initializer), - right: this.convertChild(node.expression), - body: this.convertChild(node.statement), await: Boolean( node.awaitModifier && node.awaitModifier.kind === SyntaxKind.AwaitKeyword, ), + body: this.convertChild(node.statement), + left: this.convertPattern(node.initializer), + right: this.convertChild(node.expression), }); } @@ -1080,7 +1259,7 @@ export class Converter { // Definite assignment only allowed for non-declare let and var if ( result.declare || - ['using', 'await using', 'const'].includes(result.kind) + ['await using', 'const', 'using'].includes(result.kind) ) { node.declarationList.declarations.forEach((declaration, i) => { if (result.declarations[i].definite) { @@ -1216,7 +1395,7 @@ export class Converter { case SyntaxKind.PropertyAssignment: { // eslint-disable-next-line @typescript-eslint/no-deprecated - const { questionToken, exclamationToken } = node; + const { exclamationToken, questionToken } = node; if (questionToken) { this.#throwError( @@ -1234,19 +1413,19 @@ export class Converter { return this.createNode(node, { type: AST_NODE_TYPES.Property, - key: this.convertChild(node.name), - value: this.converter(node.initializer, node, this.allowPattern), computed: isComputedProperty(node.name), + key: this.convertChild(node.name), + kind: 'init', method: false, optional: false, shorthand: false, - kind: 'init', + value: this.converter(node.initializer, node, this.allowPattern), }); } case SyntaxKind.ShorthandPropertyAssignment: { // eslint-disable-next-line @typescript-eslint/no-deprecated - const { modifiers, questionToken, exclamationToken } = node; + const { exclamationToken, modifiers, questionToken } = node; if (modifiers) { this.#throwError( @@ -1272,7 +1451,12 @@ export class Converter { if (node.objectAssignmentInitializer) { return this.createNode(node, { type: AST_NODE_TYPES.Property, + computed: false, key: this.convertChild(node.name), + kind: 'init', + method: false, + optional: false, + shorthand: true, value: this.createNode(node, { type: AST_NODE_TYPES.AssignmentPattern, decorators: [], @@ -1281,11 +1465,6 @@ export class Converter { right: this.convertChild(node.objectAssignmentInitializer), typeAnnotation: undefined, }), - computed: false, - method: false, - optional: false, - shorthand: true, - kind: 'init', }); } return this.createNode(node, { @@ -1337,26 +1516,26 @@ export class Converter { | TSESTree.TSAbstractPropertyDefinition >(node, { type, - key, accessibility: getTSNodeAccessibility(node), - value: isAbstract ? null : this.convertChild(node.initializer), computed: isComputedProperty(node.name), - static: hasModifier(SyntaxKind.StaticKeyword, node), - readonly: hasModifier(SyntaxKind.ReadonlyKeyword, node), + declare: hasModifier(SyntaxKind.DeclareKeyword, node), decorators: getDecorators(node)?.map(el => this.convertChild(el)) ?? [], - - declare: hasModifier(SyntaxKind.DeclareKeyword, node), - override: hasModifier(SyntaxKind.OverrideKeyword, node), - typeAnnotation: - node.type && this.convertTypeAnnotation(node.type, node), + definite: !!node.exclamationToken, + key, optional: (key.type === AST_NODE_TYPES.Literal || node.name.kind === SyntaxKind.Identifier || node.name.kind === SyntaxKind.ComputedPropertyName || node.name.kind === SyntaxKind.PrivateIdentifier) && !!node.questionToken, - definite: !!node.exclamationToken, + + override: hasModifier(SyntaxKind.OverrideKeyword, node), + readonly: hasModifier(SyntaxKind.ReadonlyKeyword, node), + static: hasModifier(SyntaxKind.StaticKeyword, node), + typeAnnotation: + node.type && this.convertTypeAnnotation(node.type, node), + value: isAbstract ? null : this.convertChild(node.initializer), }); } @@ -1377,13 +1556,13 @@ export class Converter { type: !node.body ? AST_NODE_TYPES.TSEmptyBodyFunctionExpression : AST_NODE_TYPES.FunctionExpression, - id: null, - generator: !!node.asteriskToken, - expression: false, // ESTreeNode as ESTreeNode here + range: [node.parameters.pos - 1, node.end], async: hasModifier(SyntaxKind.AsyncKeyword, node), body: this.convertChild(node.body), declare: false, - range: [node.parameters.pos - 1, node.end], + expression: false, // ESTreeNode as ESTreeNode here + generator: !!node.asteriskToken, + id: null, params: [], returnType: node.type && this.convertTypeAnnotation(node.type, node), typeParameters: @@ -1407,13 +1586,13 @@ export class Converter { result = this.createNode(node, { type: AST_NODE_TYPES.Property, - key: this.convertChild(node.name), - value: method, computed: isComputedProperty(node.name), - optional: !!node.questionToken, + key: this.convertChild(node.name), + kind: 'init', method: node.kind === SyntaxKind.MethodDeclaration, + optional: !!node.questionToken, shorthand: false, - kind: 'init', + value: method, }); } else { // class @@ -1478,6 +1657,7 @@ export class Converter { type: !node.body ? AST_NODE_TYPES.TSEmptyBodyFunctionExpression : AST_NODE_TYPES.FunctionExpression, + range: [node.parameters.pos - 1, node.end], async: false, body: this.convertChild(node.body), declare: false, @@ -1485,7 +1665,6 @@ export class Converter { generator: false, id: null, params: this.convertParameters(node.parameters), - range: [node.parameters.pos - 1, node.end], returnType: node.type && this.convertTypeAnnotation(node.type, node), typeParameters: node.typeParameters && @@ -1500,10 +1679,10 @@ export class Converter { const constructorKey = this.createNode(node, { type: AST_NODE_TYPES.Identifier, + range: [constructorToken.getStart(this.ast), constructorToken.end], decorators: [], name: 'constructor', optional: false, - range: [constructorToken.getStart(this.ast), constructorToken.end], typeAnnotation: undefined, }); @@ -1518,9 +1697,9 @@ export class Converter { accessibility: getTSNodeAccessibility(node), computed: false, decorators: [], - optional: false, key: constructorKey, kind: isStatic ? 'method' : 'constructor', + optional: false, override: false, static: isStatic, value: constructor, @@ -1611,26 +1790,26 @@ export class Converter { } else { result = this.createNode(node, { type: AST_NODE_TYPES.Property, - key: this.convertChild(node.propertyName ?? node.name), - value: this.convertChild(node.name), computed: Boolean( node.propertyName && node.propertyName.kind === SyntaxKind.ComputedPropertyName, ), + key: this.convertChild(node.propertyName ?? node.name), + kind: 'init', method: false, optional: false, shorthand: !node.propertyName, - kind: 'init', + value: this.convertChild(node.name), }); } if (node.initializer) { result.value = this.createNode(node, { type: AST_NODE_TYPES.AssignmentPattern, + range: [node.name.getStart(this.ast), node.initializer.end], decorators: [], left: this.convertChild(node.name), optional: false, - range: [node.name.getStart(this.ast), node.initializer.end], right: this.convertChild(node.initializer), typeAnnotation: undefined, }); @@ -1641,12 +1820,12 @@ export class Converter { case SyntaxKind.ArrowFunction: { return this.createNode(node, { type: AST_NODE_TYPES.ArrowFunctionExpression, + async: hasModifier(SyntaxKind.AsyncKeyword, node), + body: this.convertChild(node.body), + expression: node.body.kind !== SyntaxKind.Block, generator: false, id: null, params: this.convertParameters(node.parameters), - body: this.convertChild(node.body), - async: hasModifier(SyntaxKind.AsyncKeyword, node), - expression: node.body.kind !== SyntaxKind.Block, returnType: node.type && this.convertTypeAnnotation(node.type, node), typeParameters: node.typeParameters && @@ -1659,8 +1838,8 @@ export class Converter { case SyntaxKind.YieldExpression: return this.createNode(node, { type: AST_NODE_TYPES.YieldExpression, - delegate: !!node.asteriskToken, argument: this.convertChild(node.expression), + delegate: !!node.asteriskToken, }); case SyntaxKind.AwaitExpression: @@ -1674,27 +1853,27 @@ export class Converter { case SyntaxKind.NoSubstitutionTemplateLiteral: return this.createNode(node, { type: AST_NODE_TYPES.TemplateLiteral, + expressions: [], quasis: [ this.createNode(node, { type: AST_NODE_TYPES.TemplateElement, + tail: true, value: { + cooked: node.text, raw: this.ast.text.slice( node.getStart(this.ast) + 1, node.end - 1, ), - cooked: node.text, }, - tail: true, }), ], - expressions: [], }); case SyntaxKind.TemplateExpression: { const result = this.createNode(node, { type: AST_NODE_TYPES.TemplateLiteral, - quasis: [this.convertChild(node.head)], expressions: [], + quasis: [this.convertChild(node.head)], }); node.templateSpans.forEach(templateSpan => { @@ -1711,14 +1890,14 @@ export class Converter { case SyntaxKind.TaggedTemplateExpression: return this.createNode(node, { type: AST_NODE_TYPES.TaggedTemplateExpression, + quasi: this.convertChild(node.template), + tag: this.convertChild(node.tag), typeArguments: node.typeArguments && this.convertTypeArgumentsToTypeParameterInstantiation( node.typeArguments, node, ), - tag: this.convertChild(node.tag), - quasi: this.convertChild(node.template), }); case SyntaxKind.TemplateHead: @@ -1727,14 +1906,14 @@ export class Converter { const tail = node.kind === SyntaxKind.TemplateTail; return this.createNode(node, { type: AST_NODE_TYPES.TemplateElement, + tail, value: { + cooked: node.text, raw: this.ast.text.slice( node.getStart(this.ast) + 1, node.end - (tail ? 1 : 2), ), - cooked: node.text, }, - tail, }); } @@ -1901,10 +2080,10 @@ export class Converter { abstract: hasModifier(SyntaxKind.AbstractKeyword, node), body: this.createNode(node, { type: AST_NODE_TYPES.ClassBody, + range: [node.members.pos - 1, node.end], body: node.members .filter(isESTreeClassMember) .map(el => this.convertChild(el)), - range: [node.members.pos - 1, node.end], }), declare: hasModifier(SyntaxKind.DeclareKeyword, node), decorators: @@ -1949,13 +2128,13 @@ export class Converter { this.#withDeprecatedAliasGetter( { type: AST_NODE_TYPES.ImportDeclaration, - source: this.convertChild(node.moduleSpecifier), - specifiers: [], - importKind: 'value', attributes: this.convertImportAttributes( // eslint-disable-next-line @typescript-eslint/no-deprecated node.attributes ?? node.assertClause, ), + importKind: 'value', + source: this.convertChild(node.moduleSpecifier), + specifiers: [], }, 'assertions', 'attributes', @@ -2005,17 +2184,17 @@ export class Converter { case SyntaxKind.ImportSpecifier: return this.createNode(node, { type: AST_NODE_TYPES.ImportSpecifier, - local: this.convertChild(node.name), imported: this.convertChild(node.propertyName ?? node.name), importKind: node.isTypeOnly ? 'type' : 'value', + local: this.convertChild(node.name), }); case SyntaxKind.ImportClause: { const local = this.convertChild(node.name); return this.createNode(node, { type: AST_NODE_TYPES.ImportDefaultSpecifier, - local, range: local.range, + local, }); } @@ -2027,16 +2206,16 @@ export class Converter { this.#withDeprecatedAliasGetter( { type: AST_NODE_TYPES.ExportNamedDeclaration, - source: this.convertChild(node.moduleSpecifier), - specifiers: node.exportClause.elements.map(el => - this.convertChild(el, node), - ), - exportKind: node.isTypeOnly ? 'type' : 'value', - declaration: null, attributes: this.convertImportAttributes( // eslint-disable-next-line @typescript-eslint/no-deprecated node.attributes ?? node.assertClause, ), + declaration: null, + exportKind: node.isTypeOnly ? 'type' : 'value', + source: this.convertChild(node.moduleSpecifier), + specifiers: node.exportClause.elements.map(el => + this.convertChild(el, node), + ), }, 'assertions', 'attributes', @@ -2050,16 +2229,16 @@ export class Converter { this.#withDeprecatedAliasGetter( { type: AST_NODE_TYPES.ExportAllDeclaration, - source: this.convertChild(node.moduleSpecifier), - exportKind: node.isTypeOnly ? 'type' : 'value', - exported: - node.exportClause?.kind === SyntaxKind.NamespaceExport - ? this.convertChild(node.exportClause.name) - : null, attributes: this.convertImportAttributes( // eslint-disable-next-line @typescript-eslint/no-deprecated node.attributes ?? node.assertClause, ), + exported: + node.exportClause?.kind === SyntaxKind.NamespaceExport + ? this.convertChild(node.exportClause.name) + : null, + exportKind: node.isTypeOnly ? 'type' : 'value', + source: this.convertChild(node.moduleSpecifier), }, 'assertions', 'attributes', @@ -2082,9 +2261,9 @@ export class Converter { } return this.createNode(node, { type: AST_NODE_TYPES.ExportSpecifier, - local: this.convertChild(local), exported: this.convertChild(node.name), exportKind: node.isTypeOnly ? 'type' : 'value', + local: this.convertChild(local), }); } @@ -2118,41 +2297,41 @@ export class Converter { } return this.createNode(node, { type: AST_NODE_TYPES.UpdateExpression, + argument: this.convertChild(node.operand), operator, prefix: node.kind === SyntaxKind.PrefixUnaryExpression, - argument: this.convertChild(node.operand), }); } return this.createNode(node, { type: AST_NODE_TYPES.UnaryExpression, + argument: this.convertChild(node.operand), operator, prefix: node.kind === SyntaxKind.PrefixUnaryExpression, - argument: this.convertChild(node.operand), }); } case SyntaxKind.DeleteExpression: return this.createNode(node, { type: AST_NODE_TYPES.UnaryExpression, + argument: this.convertChild(node.expression), operator: 'delete', prefix: true, - argument: this.convertChild(node.expression), }); case SyntaxKind.VoidExpression: return this.createNode(node, { type: AST_NODE_TYPES.UnaryExpression, + argument: this.convertChild(node.expression), operator: 'void', prefix: true, - argument: this.convertChild(node.expression), }); case SyntaxKind.TypeOfExpression: return this.createNode(node, { type: AST_NODE_TYPES.UnaryExpression, + argument: this.convertChild(node.expression), operator: 'typeof', prefix: true, - argument: this.convertChild(node.expression), }); case SyntaxKind.TypeOperator: @@ -2223,10 +2402,10 @@ export class Converter { const result = this.createNode(node, { type: AST_NODE_TYPES.MemberExpression, - object, - property, computed, + object, optional: node.questionDotToken !== undefined, + property, }); return this.convertChainExpression(result, node); @@ -2239,10 +2418,10 @@ export class Converter { const result = this.createNode(node, { type: AST_NODE_TYPES.MemberExpression, - object, - property, computed, + object, optional: node.questionDotToken !== undefined, + property, }); return this.convertChainExpression(result, node); @@ -2258,10 +2437,10 @@ export class Converter { } return this.createNode(node, { type: AST_NODE_TYPES.ImportExpression, - source: this.convertChild(node.arguments[0]), attributes: node.arguments[1] ? this.convertChild(node.arguments[1]) : null, + source: this.convertChild(node.arguments[0]), }); } @@ -2276,8 +2455,8 @@ export class Converter { const result = this.createNode(node, { type: AST_NODE_TYPES.CallExpression, - callee, arguments: args, + callee, optional: node.questionDotToken !== undefined, typeArguments, }); @@ -2307,9 +2486,9 @@ export class Converter { case SyntaxKind.ConditionalExpression: return this.createNode(node, { type: AST_NODE_TYPES.ConditionalExpression, - test: this.convertChild(node.condition), - consequent: this.convertChild(node.whenTrue), alternate: this.convertChild(node.whenFalse), + consequent: this.convertChild(node.whenTrue), + test: this.convertChild(node.condition), }); case SyntaxKind.MetaProperty: { @@ -2342,19 +2521,19 @@ export class Converter { case SyntaxKind.StringLiteral: { return this.createNode(node, { type: AST_NODE_TYPES.Literal, + raw: node.getText(), value: parent.kind === SyntaxKind.JsxAttribute ? unescapeStringLiteralText(node.text) : node.text, - raw: node.getText(), }); } case SyntaxKind.NumericLiteral: { return this.createNode(node, { type: AST_NODE_TYPES.Literal, - value: Number(node.text), raw: node.getText(), + value: Number(node.text), }); } @@ -2370,10 +2549,10 @@ export class Converter { const value = typeof BigInt !== 'undefined' ? BigInt(bigint) : null; return this.createNode(node, { type: AST_NODE_TYPES.Literal, + range, + bigint: value == null ? bigint : String(value), raw: rawValue, value, - bigint: value == null ? bigint : String(value), - range, }); } @@ -2390,34 +2569,34 @@ export class Converter { return this.createNode(node, { type: AST_NODE_TYPES.Literal, - value: regex, raw: node.text, regex: { - pattern, flags, + pattern, }, + value: regex, }); } case SyntaxKind.TrueKeyword: return this.createNode(node, { type: AST_NODE_TYPES.Literal, - value: true, raw: 'true', + value: true, }); case SyntaxKind.FalseKeyword: return this.createNode(node, { type: AST_NODE_TYPES.Literal, - value: false, raw: 'false', + value: false, }); case SyntaxKind.NullKeyword: { return this.createNode(node, { type: AST_NODE_TYPES.Literal, - value: null, raw: 'null', + value: null, }); } @@ -2436,17 +2615,17 @@ export class Converter { case SyntaxKind.JsxElement: return this.createNode(node, { type: AST_NODE_TYPES.JSXElement, - openingElement: this.convertChild(node.openingElement), - closingElement: this.convertChild(node.closingElement), children: node.children.map(el => this.convertChild(el)), + closingElement: this.convertChild(node.closingElement), + openingElement: this.convertChild(node.openingElement), }); case SyntaxKind.JsxFragment: return this.createNode(node, { type: AST_NODE_TYPES.JSXFragment, - openingFragment: this.convertChild(node.openingFragment), - closingFragment: this.convertChild(node.closingFragment), children: node.children.map(el => this.convertChild(el)), + closingFragment: this.convertChild(node.closingFragment), + openingFragment: this.convertChild(node.openingFragment), }); case SyntaxKind.JsxSelfClosingElement: { @@ -2456,40 +2635,40 @@ export class Converter { * Convert SyntaxKind.JsxSelfClosingElement to SyntaxKind.JsxOpeningElement, * TypeScript does not seem to have the idea of openingElement when tag is self-closing */ + children: [], + closingElement: null, openingElement: this.createNode(node, { type: AST_NODE_TYPES.JSXOpeningElement, + range: getRange(node, this.ast), + attributes: node.attributes.properties.map(el => + this.convertChild(el), + ), + name: this.convertJSXTagName(node.tagName, node), + selfClosing: true, typeArguments: node.typeArguments ? this.convertTypeArgumentsToTypeParameterInstantiation( node.typeArguments, node, ) : undefined, - selfClosing: true, - name: this.convertJSXTagName(node.tagName, node), - attributes: node.attributes.properties.map(el => - this.convertChild(el), - ), - range: getRange(node, this.ast), }), - closingElement: null, - children: [], }); } case SyntaxKind.JsxOpeningElement: { return this.createNode(node, { type: AST_NODE_TYPES.JSXOpeningElement, + attributes: node.attributes.properties.map(el => + this.convertChild(el), + ), + name: this.convertJSXTagName(node.tagName, node), + selfClosing: false, typeArguments: node.typeArguments && this.convertTypeArgumentsToTypeParameterInstantiation( node.typeArguments, node, ), - selfClosing: false, - name: this.convertJSXTagName(node.tagName, node), - attributes: node.attributes.properties.map(el => - this.convertChild(el), - ), }); } @@ -2544,9 +2723,9 @@ export class Converter { return this.createNode(node, { type: AST_NODE_TYPES.JSXText, - value: unescapeStringLiteralText(text), - raw: text, range: [start, end], + raw: text, + value: unescapeStringLiteralText(text), }); } @@ -2569,24 +2748,24 @@ export class Converter { case SyntaxKind.TypeReference: return this.createNode(node, { type: AST_NODE_TYPES.TSTypeReference, - typeName: this.convertChild(node.typeName), typeArguments: node.typeArguments && this.convertTypeArgumentsToTypeParameterInstantiation( node.typeArguments, node, ), + typeName: this.convertChild(node.typeName), }); case SyntaxKind.TypeParameter: { return this.createNode(node, { type: AST_NODE_TYPES.TSTypeParameter, - name: this.convertChild(node.name), + const: hasModifier(SyntaxKind.ConstKeyword, node), constraint: node.constraint && this.convertChild(node.constraint), default: node.default ? this.convertChild(node.default) : undefined, in: hasModifier(SyntaxKind.InKeyword, node), + name: this.convertChild(node.name), out: hasModifier(SyntaxKind.OutKeyword, node), - const: hasModifier(SyntaxKind.ConstKeyword, node), }); } @@ -2638,8 +2817,8 @@ export class Converter { case SyntaxKind.IndexedAccessType: { return this.createNode(node, { type: AST_NODE_TYPES.TSIndexedAccessType, - objectType: this.convertChild(node.objectType), indexType: this.convertChild(node.indexType), + objectType: this.convertChild(node.objectType), }); } @@ -2648,8 +2827,8 @@ export class Converter { type: AST_NODE_TYPES.TSConditionalType, checkType: this.convertChild(node.checkType), extendsType: this.convertChild(node.extendsType), - trueType: this.convertChild(node.trueType), falseType: this.convertChild(node.falseType), + trueType: this.convertChild(node.trueType), }); } @@ -2859,8 +3038,8 @@ export class Converter { type: AST_NODE_TYPES.TSInterfaceDeclaration, body: this.createNode(node, { type: AST_NODE_TYPES.TSInterfaceBody, - body: node.members.map(member => this.convertChild(member)), range: [node.members.pos - 1, node.end], + body: node.members.map(member => this.convertChild(member)), }), declare: hasModifier(SyntaxKind.DeclareKeyword, node), extends: interfaceExtends, @@ -2902,6 +3081,7 @@ export class Converter { } const result = this.createNode(node, { type: AST_NODE_TYPES.TSImportType, + range, argument: this.convertChild(node.argument), qualifier: this.convertChild(node.qualifier), typeArguments: node.typeArguments @@ -2910,7 +3090,6 @@ export class Converter { node, ) : null, - range, }); if (node.isTypeOf) { @@ -2932,8 +3111,8 @@ export class Converter { type: AST_NODE_TYPES.TSEnumDeclaration, body: this.createNode(node, { type: AST_NODE_TYPES.TSEnumBody, - members, range: [node.members.pos - 1, node.end], + members, }), const: hasModifier(SyntaxKind.ConstKeyword, node), declare: hasModifier(SyntaxKind.DeclareKeyword, node), @@ -2992,11 +3171,11 @@ export class Converter { ); } return { - kind: 'global', body: body as TSESTree.TSModuleBlock, declare: false, global: false, id, + kind: 'global', }; } @@ -3029,11 +3208,11 @@ export class Converter { let name: TSESTree.Identifier | TSESTree.TSQualifiedName = this.createNode(node.name, { + type: AST_NODE_TYPES.Identifier, + range: [node.name.getStart(this.ast), node.name.getEnd()], decorators: [], name: node.name.text, optional: false, - range: [node.name.getStart(this.ast), node.name.getEnd()], - type: AST_NODE_TYPES.Identifier, typeAnnotation: undefined, }); @@ -3048,28 +3227,28 @@ export class Converter { const nextName = node.name as ts.Identifier; const right = this.createNode(nextName, { + type: AST_NODE_TYPES.Identifier, + range: [nextName.getStart(this.ast), nextName.getEnd()], decorators: [], name: nextName.text, optional: false, - range: [nextName.getStart(this.ast), nextName.getEnd()], - type: AST_NODE_TYPES.Identifier, typeAnnotation: undefined, }); name = this.createNode(nextName, { + type: AST_NODE_TYPES.TSQualifiedName, + range: [name.range[0], right.range[1]], left: name, right, - range: [name.range[0], right.range[1]], - type: AST_NODE_TYPES.TSQualifiedName, }); } return { - kind: 'namespace', body: this.convertChild(node.body), declare: false, global: false, id: name, + kind: 'namespace', }; })(), }); @@ -3133,8 +3312,8 @@ export class Converter { case SyntaxKind.TypeAssertionExpression: { return this.createNode(node, { type: AST_NODE_TYPES.TSTypeAssertion, - typeAnnotation: this.convertChild(node.type), expression: this.convertChild(node.expression), + typeAnnotation: this.convertChild(node.type), }); } case SyntaxKind.ImportEqualsDeclaration: { @@ -3196,467 +3375,289 @@ export class Converter { }); } - return member; - } - case SyntaxKind.OptionalType: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSOptionalType, - typeAnnotation: this.convertChild(node.type), - }); - } - case SyntaxKind.RestType: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSRestType, - typeAnnotation: this.convertChild(node.type), - }); - } - - // Template Literal Types - case SyntaxKind.TemplateLiteralType: { - const result = this.createNode(node, { - type: AST_NODE_TYPES.TSTemplateLiteralType, - quasis: [this.convertChild(node.head)], - types: [], - }); - - node.templateSpans.forEach(templateSpan => { - result.types.push( - this.convertChild(templateSpan.type) as TSESTree.TypeNode, - ); - result.quasis.push( - this.convertChild(templateSpan.literal) as TSESTree.TemplateElement, - ); - }); - return result; - } - - case SyntaxKind.ClassStaticBlockDeclaration: { - return this.createNode(node, { - type: AST_NODE_TYPES.StaticBlock, - body: this.convertBodyExpressions(node.body.statements, node), - }); - } - - // eslint-disable-next-line @typescript-eslint/no-deprecated - case SyntaxKind.AssertEntry: - case SyntaxKind.ImportAttribute: { - return this.createNode(node, { - type: AST_NODE_TYPES.ImportAttribute, - key: this.convertChild(node.name), - value: this.convertChild(node.value), - }); - } - - case SyntaxKind.SatisfiesExpression: { - return this.createNode(node, { - type: AST_NODE_TYPES.TSSatisfiesExpression, - expression: this.convertChild(node.expression), - typeAnnotation: this.convertChild(node.type), - }); - } - - default: - return this.deeplyCopy(node); - } - } - - #checkModifiers(node: ts.Node): void { - if (this.options.allowInvalidAST) { - return; - } - - // typescript<5.0.0 - if (nodeHasIllegalDecorators(node)) { - this.#throwError( - node.illegalDecorators[0], - 'Decorators are not valid here.', - ); - } - - for (const decorator of getDecorators( - node, - /* includeIllegalDecorators */ true, - ) ?? []) { - // `checkGrammarModifiers` function in typescript - if (!nodeCanBeDecorated(node as TSNode)) { - if (ts.isMethodDeclaration(node) && !nodeIsPresent(node.body)) { - this.#throwError( - decorator, - 'A decorator can only decorate a method implementation, not an overload.', - ); - } else { - this.#throwError(decorator, 'Decorators are not valid here.'); - } - } - } - - for (const modifier of getModifiers( - node, - /* includeIllegalModifiers */ true, - ) ?? []) { - if (modifier.kind !== SyntaxKind.ReadonlyKeyword) { - if ( - node.kind === SyntaxKind.PropertySignature || - node.kind === SyntaxKind.MethodSignature - ) { - this.#throwError( - modifier, - `'${ts.tokenToString( - modifier.kind, - )}' modifier cannot appear on a type member`, - ); - } - - if ( - node.kind === SyntaxKind.IndexSignature && - (modifier.kind !== SyntaxKind.StaticKeyword || - !ts.isClassLike(node.parent)) - ) { - this.#throwError( - modifier, - `'${ts.tokenToString( - modifier.kind, - )}' modifier cannot appear on an index signature`, - ); - } - } - - if ( - modifier.kind !== SyntaxKind.InKeyword && - modifier.kind !== SyntaxKind.OutKeyword && - modifier.kind !== SyntaxKind.ConstKeyword && - node.kind === SyntaxKind.TypeParameter - ) { - this.#throwError( - modifier, - `'${ts.tokenToString( - modifier.kind, - )}' modifier cannot appear on a type parameter`, - ); - } - - if ( - (modifier.kind === SyntaxKind.InKeyword || - modifier.kind === SyntaxKind.OutKeyword) && - (node.kind !== SyntaxKind.TypeParameter || - !( - ts.isInterfaceDeclaration(node.parent) || - ts.isClassLike(node.parent) || - ts.isTypeAliasDeclaration(node.parent) - )) - ) { - this.#throwError( - modifier, - `'${ts.tokenToString( - modifier.kind, - )}' modifier can only appear on a type parameter of a class, interface or type alias`, - ); - } - - if ( - modifier.kind === SyntaxKind.ReadonlyKeyword && - node.kind !== SyntaxKind.PropertyDeclaration && - node.kind !== SyntaxKind.PropertySignature && - node.kind !== SyntaxKind.IndexSignature && - node.kind !== SyntaxKind.Parameter - ) { - this.#throwError( - modifier, - "'readonly' modifier can only appear on a property declaration or index signature.", - ); + return member; } - - if ( - modifier.kind === SyntaxKind.DeclareKeyword && - ts.isClassLike(node.parent) && - !ts.isPropertyDeclaration(node) - ) { - this.#throwError( - modifier, - `'${ts.tokenToString( - modifier.kind, - )}' modifier cannot appear on class elements of this kind.`, - ); + case SyntaxKind.OptionalType: { + return this.createNode(node, { + type: AST_NODE_TYPES.TSOptionalType, + typeAnnotation: this.convertChild(node.type), + }); + } + case SyntaxKind.RestType: { + return this.createNode(node, { + type: AST_NODE_TYPES.TSRestType, + typeAnnotation: this.convertChild(node.type), + }); } - if ( - modifier.kind === SyntaxKind.DeclareKeyword && - ts.isVariableStatement(node) - ) { - const declarationKind = getDeclarationKind(node.declarationList); - if (declarationKind === 'using' || declarationKind === 'await using') { - this.#throwError( - modifier, - `'declare' modifier cannot appear on a '${declarationKind}' declaration.`, + // Template Literal Types + case SyntaxKind.TemplateLiteralType: { + const result = this.createNode(node, { + type: AST_NODE_TYPES.TSTemplateLiteralType, + quasis: [this.convertChild(node.head)], + types: [], + }); + + node.templateSpans.forEach(templateSpan => { + result.types.push( + this.convertChild(templateSpan.type) as TSESTree.TypeNode, ); - } + result.quasis.push( + this.convertChild(templateSpan.literal) as TSESTree.TemplateElement, + ); + }); + return result; } - if ( - modifier.kind === SyntaxKind.AbstractKeyword && - node.kind !== SyntaxKind.ClassDeclaration && - node.kind !== SyntaxKind.ConstructorType && - node.kind !== SyntaxKind.MethodDeclaration && - node.kind !== SyntaxKind.PropertyDeclaration && - node.kind !== SyntaxKind.GetAccessor && - node.kind !== SyntaxKind.SetAccessor - ) { - this.#throwError( - modifier, - `'${ts.tokenToString( - modifier.kind, - )}' modifier can only appear on a class, method, or property declaration.`, - ); + case SyntaxKind.ClassStaticBlockDeclaration: { + return this.createNode(node, { + type: AST_NODE_TYPES.StaticBlock, + body: this.convertBodyExpressions(node.body.statements, node), + }); } - if ( - (modifier.kind === SyntaxKind.StaticKeyword || - modifier.kind === SyntaxKind.PublicKeyword || - modifier.kind === SyntaxKind.ProtectedKeyword || - modifier.kind === SyntaxKind.PrivateKeyword) && - (node.parent.kind === SyntaxKind.ModuleBlock || - node.parent.kind === SyntaxKind.SourceFile) - ) { - this.#throwError( - modifier, - `'${ts.tokenToString( - modifier.kind, - )}' modifier cannot appear on a module or namespace element.`, - ); + // eslint-disable-next-line @typescript-eslint/no-deprecated + case SyntaxKind.AssertEntry: + case SyntaxKind.ImportAttribute: { + return this.createNode(node, { + type: AST_NODE_TYPES.ImportAttribute, + key: this.convertChild(node.name), + value: this.convertChild(node.value), + }); } - if ( - modifier.kind === SyntaxKind.AccessorKeyword && - node.kind !== SyntaxKind.PropertyDeclaration - ) { - this.#throwError( - modifier, - "'accessor' modifier can only appear on a property declaration.", - ); + case SyntaxKind.SatisfiesExpression: { + return this.createNode(node, { + type: AST_NODE_TYPES.TSSatisfiesExpression, + expression: this.convertChild(node.expression), + typeAnnotation: this.convertChild(node.type), + }); } - // `checkGrammarAsyncModifier` function in `typescript` - if ( - modifier.kind === SyntaxKind.AsyncKeyword && - node.kind !== SyntaxKind.MethodDeclaration && - node.kind !== SyntaxKind.FunctionDeclaration && - node.kind !== SyntaxKind.FunctionExpression && - node.kind !== SyntaxKind.ArrowFunction - ) { - this.#throwError(modifier, "'async' modifier cannot be used here."); - } + default: + return this.deeplyCopy(node); + } + } - // `checkGrammarModifiers` function in `typescript` - if ( - node.kind === SyntaxKind.Parameter && - (modifier.kind === SyntaxKind.StaticKeyword || - modifier.kind === SyntaxKind.ExportKeyword || - modifier.kind === SyntaxKind.DeclareKeyword || - modifier.kind === SyntaxKind.AsyncKeyword) - ) { - this.#throwError( - modifier, - `'${ts.tokenToString( - modifier.kind, - )}' modifier cannot appear on a parameter.`, - ); - } + private createNode( + // The 'parent' property will be added later if specified + node: Omit, 'parent'>, + data: Omit, 'parent'>, + ): T { + const result = data; + result.range ??= getRange(node, this.ast); + result.loc ??= getLocFor(result.range, this.ast); - // `checkGrammarModifiers` function in `typescript` - if ( - modifier.kind === SyntaxKind.PublicKeyword || - modifier.kind === SyntaxKind.ProtectedKeyword || - modifier.kind === SyntaxKind.PrivateKeyword - ) { - for (const anotherModifier of getModifiers(node) ?? []) { - if ( - anotherModifier !== modifier && - (anotherModifier.kind === SyntaxKind.PublicKeyword || - anotherModifier.kind === SyntaxKind.ProtectedKeyword || - anotherModifier.kind === SyntaxKind.PrivateKeyword) - ) { - this.#throwError( - anotherModifier, - `Accessibility modifier already seen.`, - ); - } - } - } + if (result && this.options.shouldPreserveNodeMaps) { + this.esTreeNodeToTSNodeMap.set(result, node); + } + return result as T; + } - // `checkParameter` function in `typescript` - if ( - node.kind === SyntaxKind.Parameter && - // In `typescript` package, it's `ts.hasSyntacticModifier(node, ts.ModifierFlags.ParameterPropertyModifier)` - // https://github.com/typescript-eslint/typescript-eslint/pull/6615#discussion_r1136489935 - (modifier.kind === SyntaxKind.PublicKeyword || - modifier.kind === SyntaxKind.PrivateKeyword || - modifier.kind === SyntaxKind.ProtectedKeyword || - modifier.kind === SyntaxKind.ReadonlyKeyword || - modifier.kind === SyntaxKind.OverrideKeyword) - ) { - const func = getContainingFunction(node)!; + convertProgram(): TSESTree.Program { + return this.converter(this.ast) as TSESTree.Program; + } - if ( - !(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body)) - ) { - this.#throwError( - modifier, - 'A parameter property is only allowed in a constructor implementation.', - ); - } - } + /** + * For nodes that are copied directly from the TypeScript AST into + * ESTree mostly as-is. The only difference is the addition of a type + * property instead of a kind property. Recursively copies all children. + */ + private deeplyCopy(node: TSNode): any { + if (node.kind === ts.SyntaxKind.JSDocFunctionType) { + this.#throwError( + node, + 'JSDoc types can only be used inside documentation comments.', + ); + } + + const customType = `TS${SyntaxKind[node.kind]}` as AST_NODE_TYPES; + + /** + * If the "errorOnUnknownASTType" option is set to true, throw an error, + * otherwise fallback to just including the unknown type as-is. + */ + if (this.options.errorOnUnknownASTType && !AST_NODE_TYPES[customType]) { + throw new Error(`Unknown AST_NODE_TYPE: "${customType}"`); + } + + const result = this.createNode(node, { + type: customType, + }); + + if ('type' in node) { + result.typeAnnotation = + node.type && 'kind' in node.type && ts.isTypeNode(node.type) + ? this.convertTypeAnnotation(node.type, node) + : null; + } + if ('typeArguments' in node) { + result.typeArguments = + node.typeArguments && 'pos' in node.typeArguments + ? this.convertTypeArgumentsToTypeParameterInstantiation( + node.typeArguments, + node, + ) + : null; + } + if ('typeParameters' in node) { + result.typeParameters = + node.typeParameters && 'pos' in node.typeParameters + ? this.convertTSTypeParametersToTypeParametersDeclaration( + node.typeParameters, + ) + : null; + } + const decorators = getDecorators(node); + if (decorators?.length) { + result.decorators = decorators.map(el => this.convertChild(el)); } - } - #throwUnlessAllowInvalidAST( - node: ts.Node | number, - message: string, - ): asserts node is never { - if (!this.options.allowInvalidAST) { - this.#throwError(node, message); - } + // keys we never want to clone from the base typescript node as they + // introduce garbage into our AST + const KEYS_TO_NOT_COPY = new Set([ + '_children', + 'decorators', + 'end', + 'flags', + 'heritageClauses', + 'illegalDecorators', + 'jsDoc', + 'kind', + 'locals', + 'localSymbol', + 'modifierFlagsCache', + 'modifiers', + 'nextContainer', + 'parent', + 'pos', + 'symbol', + 'transformFlags', + 'type', + 'typeArguments', + 'typeParameters', + ]); + + Object.entries(node) + .filter(([key]) => !KEYS_TO_NOT_COPY.has(key)) + .forEach(([key, value]) => { + if (Array.isArray(value)) { + result[key] = value.map(el => this.convertChild(el as TSNode)); + } else if (value && typeof value === 'object' && value.kind) { + // need to check node[key].kind to ensure we don't try to convert a symbol + result[key] = this.convertChild(value as TSNode); + } else { + result[key] = value; + } + }); + return result; } /** - * Creates a getter for a property under aliasKey that returns the value under - * valueKey. If suppressDeprecatedPropertyWarnings is not enabled, the - * getter also console warns about the deprecation. - * - * @see https://github.com/typescript-eslint/typescript-eslint/issues/6469 + * Fixes the exports of the given ts.Node + * @returns the ESTreeNode with fixed exports */ - #withDeprecatedAliasGetter< - Properties extends { type: string }, - AliasKey extends string, - ValueKey extends string & keyof Properties, + private fixExports< + T extends + | TSESTree.DefaultExportDeclarations + | TSESTree.NamedExportDeclarations, >( - node: Properties, - aliasKey: AliasKey, - valueKey: ValueKey, - suppressWarnings = false, - ): Properties & Record { - let warned = suppressWarnings; + node: + | ts.ClassDeclaration + | ts.ClassExpression + | ts.EnumDeclaration + | ts.FunctionDeclaration + | ts.ImportEqualsDeclaration + | ts.InterfaceDeclaration + | ts.ModuleDeclaration + | ts.TypeAliasDeclaration + | ts.VariableStatement, + result: T, + ): T | TSESTree.ExportDefaultDeclaration | TSESTree.ExportNamedDeclaration { + const isNamespaceNode = + ts.isModuleDeclaration(node) && + Boolean(node.flags & ts.NodeFlags.Namespace); - Object.defineProperty(node, aliasKey, { - configurable: true, - get: this.options.suppressDeprecatedPropertyWarnings - ? (): Properties[typeof valueKey] => node[valueKey] - : (): Properties[typeof valueKey] => { - if (!warned) { - process.emitWarning( - `The '${aliasKey}' property is deprecated on ${node.type} nodes. Use '${valueKey}' instead. See https://typescript-eslint.io/troubleshooting/faqs/general#the-key-property-is-deprecated-on-type-nodes-use-key-instead-warnings.`, - 'DeprecationWarning', - ); - warned = true; - } + const modifiers = isNamespaceNode + ? getNamespaceModifiers(node) + : getModifiers(node); - return node[valueKey]; - }, - set(value): void { - Object.defineProperty(node, aliasKey, { - enumerable: true, - writable: true, - value, - }); - }, - }); + if (modifiers?.[0].kind === SyntaxKind.ExportKeyword) { + /** + * Make sure that original node is registered instead of export + */ + this.registerTSNodeInNodeMap(node, result); - return node as Properties & Record; - } + const exportKeyword = modifiers[0]; + const nextModifier = modifiers[1]; + const declarationIsDefault = + nextModifier?.kind === SyntaxKind.DefaultKeyword; - #withDeprecatedGetter< - Properties extends { type: string }, - Key extends string, - Value, - >( - node: Properties, - deprecatedKey: Key, - preferredKey: string, - value: Value, - ): Properties & Record { - let warned = false; + const varToken = declarationIsDefault + ? findNextToken(nextModifier, this.ast, this.ast) + : findNextToken(exportKeyword, this.ast, this.ast); - Object.defineProperty(node, deprecatedKey, { - configurable: true, - get: this.options.suppressDeprecatedPropertyWarnings - ? (): Value => value - : (): Value => { - if (!warned) { - process.emitWarning( - `The '${deprecatedKey}' property is deprecated on ${node.type} nodes. Use ${preferredKey} instead. See https://typescript-eslint.io/troubleshooting/faqs/general#the-key-property-is-deprecated-on-type-nodes-use-key-instead-warnings.`, - 'DeprecationWarning', - ); - warned = true; - } + result.range[0] = varToken!.getStart(this.ast); + result.loc = getLocFor(result.range, this.ast); - return value; + if (declarationIsDefault) { + return this.createNode( + node as Exclude, + { + type: AST_NODE_TYPES.ExportDefaultDeclaration, + range: [exportKeyword.getStart(this.ast), result.range[1]], + declaration: result as TSESTree.DefaultExportDeclarations, + exportKind: 'value', }, - set(value): void { - Object.defineProperty(node, deprecatedKey, { - enumerable: true, - writable: true, - value, - }); - }, - }); + ); + } + const isType = + result.type === AST_NODE_TYPES.TSInterfaceDeclaration || + result.type === AST_NODE_TYPES.TSTypeAliasDeclaration; + const isDeclare = 'declare' in result && result.declare; + return this.createNode( + node, + // @ts-expect-error - TODO, narrow the types here + this.#withDeprecatedAliasGetter( + { + type: AST_NODE_TYPES.ExportNamedDeclaration, + range: [exportKeyword.getStart(this.ast), result.range[1]], + attributes: [], + declaration: result, + exportKind: isType || isDeclare ? 'type' : 'value', + source: null, + specifiers: [], + }, + 'assertions', + 'attributes', + true, + ), + ); + } - return node as Properties & Record; + return result; } - #throwError(node: ts.Node | number, message: string): asserts node is never { - let start; - let end; - if (typeof node === 'number') { - start = end = node; - } else { - start = node.getStart(this.ast); - end = node.getEnd(); - } - - throw createError(message, this.ast, start, end); + getASTMaps(): ASTMaps { + return { + esTreeNodeToTSNodeMap: this.esTreeNodeToTSNodeMap, + tsNodeToESTreeNodeMap: this.tsNodeToESTreeNodeMap, + }; } - #checkForStatementDeclaration( - initializer: ts.ForInitializer, - kind: ts.SyntaxKind.ForInStatement | ts.SyntaxKind.ForOfStatement, + + /** + * Register specific TypeScript node into map with first ESTree node provided + */ + private registerTSNodeInNodeMap( + node: ts.Node, + result: TSESTree.Node | null, ): void { - const loop = - kind === ts.SyntaxKind.ForInStatement ? 'for...in' : 'for...of'; - if (ts.isVariableDeclarationList(initializer)) { - if (initializer.declarations.length !== 1) { - this.#throwError( - initializer, - `Only a single variable declaration is allowed in a '${loop}' statement.`, - ); - } - const declaration = initializer.declarations[0]; - if (declaration.initializer) { - this.#throwError( - declaration, - `The variable declaration of a '${loop}' statement cannot have an initializer.`, - ); - } else if (declaration.type) { - this.#throwError( - declaration, - `The variable declaration of a '${loop}' statement cannot have a type annotation.`, - ); - } - if ( - kind === ts.SyntaxKind.ForInStatement && - initializer.flags & ts.NodeFlags.Using - ) { - this.#throwError( - initializer, - "The left-hand side of a 'for...in' statement cannot be a 'using' declaration.", - ); - } - } else if ( - !isValidAssignmentTarget(initializer) && - initializer.kind !== ts.SyntaxKind.ObjectLiteralExpression && - initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression + if ( + result && + this.options.shouldPreserveNodeMaps && + !this.tsNodeToESTreeNodeMap.has(node) ) { - this.#throwError( - initializer, - `The left-hand side of a '${loop}' statement must be a variable or a property access.`, - ); + this.tsNodeToESTreeNodeMap.set(node, result); } } } diff --git a/packages/typescript-estree/src/create-program/WatchCompilerHostOfConfigFile.ts b/packages/typescript-estree/src/create-program/WatchCompilerHostOfConfigFile.ts index 21d0c24516f..1925d3edeaa 100644 --- a/packages/typescript-estree/src/create-program/WatchCompilerHostOfConfigFile.ts +++ b/packages/typescript-estree/src/create-program/WatchCompilerHostOfConfigFile.ts @@ -29,10 +29,10 @@ interface CachedDirectoryStructureHost extends DirectoryStructureHost { // https://github.com/microsoft/TypeScript/blob/5d36aab06f12b0a3ba6197eb14e98204ec0195fb/src/compiler/watch.ts#L548-L554 interface WatchCompilerHostOfConfigFile extends ts.WatchCompilerHostOfConfigFile { + extraFileExtensions?: readonly ts.FileExtensionInfo[]; onCachedDirectoryStructureHostCreate( host: CachedDirectoryStructureHost, ): void; - extraFileExtensions?: readonly ts.FileExtensionInfo[]; } export type { WatchCompilerHostOfConfigFile }; diff --git a/packages/typescript-estree/src/create-program/createIsolatedProgram.ts b/packages/typescript-estree/src/create-program/createIsolatedProgram.ts index 4c15f4a25bb..ab9144326b2 100644 --- a/packages/typescript-estree/src/create-program/createIsolatedProgram.ts +++ b/packages/typescript-estree/src/create-program/createIsolatedProgram.ts @@ -2,8 +2,9 @@ import debug from 'debug'; import * as ts from 'typescript'; import type { ParseSettings } from '../parseSettings'; -import { getScriptKind } from './getScriptKind'; import type { ASTAndDefiniteProgram } from './shared'; + +import { getScriptKind } from './getScriptKind'; import { createDefaultCompilerOptionsFromExtra } from './shared'; const log = debug('typescript-eslint:typescript-estree:createIsolatedProgram'); @@ -30,12 +31,12 @@ function createIsolatedProgram( getCurrentDirectory() { return ''; }, - getDirectories() { - return []; - }, getDefaultLibFileName() { return 'lib.d.ts'; }, + getDirectories() { + return []; + }, // TODO: Support Windows CRLF getNewLine() { @@ -65,9 +66,9 @@ function createIsolatedProgram( [parseSettings.filePath], { jsDocParsingMode: parseSettings.jsDocParsingMode, + jsx: parseSettings.jsx ? ts.JsxEmit.Preserve : undefined, noResolve: true, target: ts.ScriptTarget.Latest, - jsx: parseSettings.jsx ? ts.JsxEmit.Preserve : undefined, ...createDefaultCompilerOptionsFromExtra(parseSettings), }, compilerHost, diff --git a/packages/typescript-estree/src/create-program/createProjectProgram.ts b/packages/typescript-estree/src/create-program/createProjectProgram.ts index 0d678866da1..e02c2c83438 100644 --- a/packages/typescript-estree/src/create-program/createProjectProgram.ts +++ b/packages/typescript-estree/src/create-program/createProjectProgram.ts @@ -1,10 +1,12 @@ -import debug from 'debug'; import type * as ts from 'typescript'; -import { firstDefined } from '../node-utils'; +import debug from 'debug'; + import type { ParseSettings } from '../parseSettings'; -import { createProjectProgramError } from './createProjectProgramError'; import type { ASTAndDefiniteProgram } from './shared'; + +import { firstDefined } from '../node-utils'; +import { createProjectProgramError } from './createProjectProgramError'; import { getAstFromProgram } from './shared'; const log = debug('typescript-eslint:typescript-estree:createProjectProgram'); diff --git a/packages/typescript-estree/src/create-program/createProjectProgramError.ts b/packages/typescript-estree/src/create-program/createProjectProgramError.ts index 26a0c282d31..f1474d344bf 100644 --- a/packages/typescript-estree/src/create-program/createProjectProgramError.ts +++ b/packages/typescript-estree/src/create-program/createProjectProgramError.ts @@ -1,8 +1,9 @@ -import path from 'node:path'; - import type * as ts from 'typescript'; +import path from 'node:path'; + import type { ParseSettings } from '../parseSettings'; + import { describeFilePath } from './describeFilePath'; import { DEFAULT_EXTRA_FILE_EXTENSIONS } from './shared'; diff --git a/packages/typescript-estree/src/create-program/createProjectService.ts b/packages/typescript-estree/src/create-program/createProjectService.ts index 89158d15e06..dfd75380323 100644 --- a/packages/typescript-estree/src/create-program/createProjectService.ts +++ b/packages/typescript-estree/src/create-program/createProjectService.ts @@ -1,8 +1,10 @@ /* eslint-disable @typescript-eslint/no-empty-function -- for TypeScript APIs*/ -import debug from 'debug'; import type * as ts from 'typescript/lib/tsserverlibrary'; +import debug from 'debug'; + import type { ProjectServiceOptions } from '../parser-options'; + import { getParsedConfigFile } from './getParsedConfigFile'; import { validateDefaultProjectForFilesGlob } from './validateDefaultProjectForFilesGlob'; @@ -71,11 +73,11 @@ export function createProjectService( // See https://github.com/typescript-eslint/typescript-eslint/issues/9905 ...(!options.loadTypeScriptPlugins && { require: () => ({ - module: undefined, error: { message: 'TypeScript plugins are not required when using parserOptions.projectService.', }, + module: undefined, }), }), }; @@ -118,18 +120,18 @@ export function createProjectService( log('Creating project service with: %o', options); const service = new tsserver.server.ProjectService({ - host: system, cancellationToken: { isCancellationRequested: (): boolean => false }, - useSingleInferredProject: false, - useInferredProjectPerProjectRoot: false, - logger, eventHandler: logTsserverEvent.enabled ? (e): void => { logTsserverEvent(e); } : undefined, - session: undefined, + host: system, jsDocParsingMode, + logger, + session: undefined, + useInferredProjectPerProjectRoot: false, + useSingleInferredProject: false, }); service.setHostConfiguration({ diff --git a/packages/typescript-estree/src/create-program/createSourceFile.ts b/packages/typescript-estree/src/create-program/createSourceFile.ts index 096264c5844..618e78645eb 100644 --- a/packages/typescript-estree/src/create-program/createSourceFile.ts +++ b/packages/typescript-estree/src/create-program/createSourceFile.ts @@ -2,9 +2,10 @@ import debug from 'debug'; import * as ts from 'typescript'; import type { ParseSettings } from '../parseSettings'; +import type { ASTAndNoProgram } from './shared'; + import { isSourceFile } from '../source-files'; import { getScriptKind } from './getScriptKind'; -import type { ASTAndNoProgram } from './shared'; const log = debug('typescript-eslint:typescript-estree:createSourceFile'); @@ -21,8 +22,8 @@ function createSourceFile(parseSettings: ParseSettings): ts.SourceFile { parseSettings.filePath, parseSettings.codeFullText, { - languageVersion: ts.ScriptTarget.Latest, jsDocParsingMode: parseSettings.jsDocParsingMode, + languageVersion: ts.ScriptTarget.Latest, setExternalModuleIndicator: parseSettings.setExternalModuleIndicator, }, /* setParentNodes */ true, @@ -37,4 +38,4 @@ function createNoProgram(parseSettings: ParseSettings): ASTAndNoProgram { }; } -export { createSourceFile, createNoProgram }; +export { createNoProgram, createSourceFile }; diff --git a/packages/typescript-estree/src/create-program/getParsedConfigFile.ts b/packages/typescript-estree/src/create-program/getParsedConfigFile.ts index 6429bb87ed7..0392c02a49a 100644 --- a/packages/typescript-estree/src/create-program/getParsedConfigFile.ts +++ b/packages/typescript-estree/src/create-program/getParsedConfigFile.ts @@ -1,8 +1,8 @@ +import type * as ts from 'typescript/lib/tsserverlibrary'; + import * as fs from 'node:fs'; import * as path from 'node:path'; -import type * as ts from 'typescript/lib/tsserverlibrary'; - import { CORE_COMPILER_OPTIONS } from './shared'; /** @@ -27,11 +27,11 @@ function getParsedConfigFile( configFile, CORE_COMPILER_OPTIONS, { + fileExists: fs.existsSync, + getCurrentDirectory, onUnRecoverableConfigFileDiagnostic: diag => { throw new Error(formatDiagnostics([diag])); // ensures that `parsed` is defined. }, - fileExists: fs.existsSync, - getCurrentDirectory, readDirectory: tsserver.sys.readDirectory, readFile: file => fs.readFileSync( diff --git a/packages/typescript-estree/src/create-program/getScriptKind.ts b/packages/typescript-estree/src/create-program/getScriptKind.ts index 619a34cf0a5..64250186613 100644 --- a/packages/typescript-estree/src/create-program/getScriptKind.ts +++ b/packages/typescript-estree/src/create-program/getScriptKind.ts @@ -1,5 +1,4 @@ import path from 'node:path'; - import * as ts from 'typescript'; function getScriptKind(filePath: string, jsx: boolean): ts.ScriptKind { @@ -9,25 +8,25 @@ function getScriptKind(filePath: string, jsx: boolean): ts.ScriptKind { // weird errors due to a mismatch. // https://github.com/microsoft/TypeScript/blob/da00ba67ed1182ad334f7c713b8254fba174aeba/src/compiler/utilities.ts#L6948-L6968 switch (extension) { - case ts.Extension.Js: case ts.Extension.Cjs: + case ts.Extension.Js: case ts.Extension.Mjs: return ts.ScriptKind.JS; - case ts.Extension.Jsx: - return ts.ScriptKind.JSX; - - case ts.Extension.Ts: case ts.Extension.Cts: case ts.Extension.Mts: + case ts.Extension.Ts: return ts.ScriptKind.TS; - case ts.Extension.Tsx: - return ts.ScriptKind.TSX; - case ts.Extension.Json: return ts.ScriptKind.JSON; + case ts.Extension.Jsx: + return ts.ScriptKind.JSX; + + case ts.Extension.Tsx: + return ts.ScriptKind.TSX; + default: // unknown extension, force typescript to ignore the file extension, and respect the user's setting return jsx ? ts.ScriptKind.TSX : ts.ScriptKind.TS; @@ -37,10 +36,10 @@ function getScriptKind(filePath: string, jsx: boolean): ts.ScriptKind { function getLanguageVariant(scriptKind: ts.ScriptKind): ts.LanguageVariant { // https://github.com/microsoft/TypeScript/blob/d6e483b8dabd8fd37c00954c3f2184bb7f1eb90c/src/compiler/utilities.ts#L6281-L6285 switch (scriptKind) { - case ts.ScriptKind.TSX: - case ts.ScriptKind.JSX: case ts.ScriptKind.JS: case ts.ScriptKind.JSON: + case ts.ScriptKind.JSX: + case ts.ScriptKind.TSX: return ts.LanguageVariant.JSX; default: @@ -48,4 +47,4 @@ function getLanguageVariant(scriptKind: ts.ScriptKind): ts.LanguageVariant { } } -export { getScriptKind, getLanguageVariant }; +export { getLanguageVariant, getScriptKind }; diff --git a/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts b/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts index 473e73f2ae7..dd93452b482 100644 --- a/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts +++ b/packages/typescript-estree/src/create-program/getWatchProgramsForProjects.ts @@ -1,18 +1,18 @@ -import fs from 'node:fs'; - import debug from 'debug'; +import fs from 'node:fs'; import * as ts from 'typescript'; import type { ParseSettings } from '../parseSettings'; -import { getCodeText } from '../source-files'; import type { CanonicalPath } from './shared'; +import type { WatchCompilerHostOfConfigFile } from './WatchCompilerHostOfConfigFile'; + +import { getCodeText } from '../source-files'; import { canonicalDirname, createDefaultCompilerOptionsFromExtra, createHash, getCanonicalFileName, } from './shared'; -import type { WatchCompilerHostOfConfigFile } from './WatchCompilerHostOfConfigFile'; const log = debug('typescript-eslint:typescript-estree:createWatchProgram'); @@ -92,7 +92,7 @@ function saveWatchCallback( * Holds information about the file currently being linted */ const currentLintOperationState: { - code: ts.SourceFile | string; + code: string | ts.SourceFile; filePath: CanonicalPath; } = { code: '', diff --git a/packages/typescript-estree/src/create-program/shared.ts b/packages/typescript-estree/src/create-program/shared.ts index f2014d85303..f8d9117de11 100644 --- a/packages/typescript-estree/src/create-program/shared.ts +++ b/packages/typescript-estree/src/create-program/shared.ts @@ -1,6 +1,6 @@ -import path from 'node:path'; - import type { Program } from 'typescript'; + +import path from 'node:path'; import * as ts from 'typescript'; import type { ParseSettings } from '../parseSettings'; @@ -33,20 +33,20 @@ const CORE_COMPILER_OPTIONS: ts.CompilerOptions = { */ const DEFAULT_COMPILER_OPTIONS: ts.CompilerOptions = { ...CORE_COMPILER_OPTIONS, - allowNonTsExtensions: true, allowJs: true, + allowNonTsExtensions: true, checkJs: true, }; const DEFAULT_EXTRA_FILE_EXTENSIONS = new Set([ - ts.Extension.Ts, - ts.Extension.Tsx, + ts.Extension.Cjs, + ts.Extension.Cts, ts.Extension.Js, ts.Extension.Jsx, ts.Extension.Mjs, ts.Extension.Mts, - ts.Extension.Cjs, - ts.Extension.Cts, + ts.Extension.Ts, + ts.Extension.Tsx, ]); function createDefaultCompilerOptionsFromExtra( @@ -63,7 +63,7 @@ function createDefaultCompilerOptionsFromExtra( } // This narrows the type so we can be sure we're passing canonical names in the correct places -type CanonicalPath = string & { __brand: unknown }; +type CanonicalPath = { __brand: unknown } & string; // typescript doesn't provide a ts.sys implementation for browser environments const useCaseSensitiveFileNames = @@ -142,13 +142,13 @@ export { type ASTAndDefiniteProgram, type ASTAndNoProgram, type ASTAndProgram, - CORE_COMPILER_OPTIONS, canonicalDirname, type CanonicalPath, + CORE_COMPILER_OPTIONS, createDefaultCompilerOptionsFromExtra, createHash, + DEFAULT_EXTRA_FILE_EXTENSIONS, ensureAbsolutePath, - getCanonicalFileName, getAstFromProgram, - DEFAULT_EXTRA_FILE_EXTENSIONS, + getCanonicalFileName, }; diff --git a/packages/typescript-estree/src/create-program/useProvidedPrograms.ts b/packages/typescript-estree/src/create-program/useProvidedPrograms.ts index 9baa3bf7ba2..660e82ac4d7 100644 --- a/packages/typescript-estree/src/create-program/useProvidedPrograms.ts +++ b/packages/typescript-estree/src/create-program/useProvidedPrograms.ts @@ -1,11 +1,11 @@ -import * as path from 'node:path'; - import debug from 'debug'; +import * as path from 'node:path'; import * as ts from 'typescript'; import type { ParseSettings } from '../parseSettings'; -import { getParsedConfigFile } from './getParsedConfigFile'; import type { ASTAndDefiniteProgram } from './shared'; + +import { getParsedConfigFile } from './getParsedConfigFile'; import { getAstFromProgram } from './shared'; const log = debug('typescript-eslint:typescript-estree:useProvidedProgram'); @@ -66,4 +66,4 @@ function createProgramFromConfigFile( return ts.createProgram(parsed.fileNames, parsed.options, host); } -export { useProvidedPrograms, createProgramFromConfigFile }; +export { createProgramFromConfigFile, useProvidedPrograms }; diff --git a/packages/typescript-estree/src/createParserServices.ts b/packages/typescript-estree/src/createParserServices.ts index de3070c2fe7..b9a23477c23 100644 --- a/packages/typescript-estree/src/createParserServices.ts +++ b/packages/typescript-estree/src/createParserServices.ts @@ -9,9 +9,9 @@ export function createParserServices( ): ParserServices { if (!program) { return { - program, emitDecoratorMetadata: undefined, experimentalDecorators: undefined, + program, // we always return the node maps because // (a) they don't require type info and // (b) they can be useful when using some of TS's internal non-type-aware AST utils diff --git a/packages/typescript-estree/src/index.ts b/packages/typescript-estree/src/index.ts index 757b50074d1..84f85ea4b35 100644 --- a/packages/typescript-estree/src/index.ts +++ b/packages/typescript-estree/src/index.ts @@ -1,3 +1,9 @@ +export * from './clear-caches'; +export * from './create-program/getScriptKind'; +export { getCanonicalFileName } from './create-program/shared'; +export { createProgramFromConfigFile as createProgram } from './create-program/useProvidedPrograms'; +export * from './getModifiers'; +export { TSError } from './node-utils'; export { type AST, parse, @@ -6,19 +12,13 @@ export { } from './parser'; export type { ParserServices, - ParserServicesWithTypeInformation, ParserServicesWithoutTypeInformation, + ParserServicesWithTypeInformation, TSESTreeOptions, } from './parser-options'; export { simpleTraverse } from './simple-traverse'; export * from './ts-estree'; -export { createProgramFromConfigFile as createProgram } from './create-program/useProvidedPrograms'; -export * from './create-program/getScriptKind'; -export { getCanonicalFileName } from './create-program/shared'; export { typescriptVersionIsAtLeast } from './version-check'; -export * from './getModifiers'; -export { TSError } from './node-utils'; -export * from './clear-caches'; export { withoutProjectParserOptions } from './withoutProjectParserOptions'; // note - cannot migrate this to an import statement because it will make TSC copy the package.json to the dist folder diff --git a/packages/typescript-estree/src/jsx/xhtml-entities.ts b/packages/typescript-estree/src/jsx/xhtml-entities.ts index 2c3e788f0ed..beb65f67cc9 100644 --- a/packages/typescript-estree/src/jsx/xhtml-entities.ts +++ b/packages/typescript-estree/src/jsx/xhtml-entities.ts @@ -1,255 +1,255 @@ export const xhtmlEntities: Record = { - quot: '\u0022', - amp: '&', - apos: '\u0027', - lt: '<', - gt: '>', - nbsp: '\u00A0', - iexcl: '\u00A1', - cent: '\u00A2', - pound: '\u00A3', - curren: '\u00A4', - yen: '\u00A5', - brvbar: '\u00A6', - sect: '\u00A7', - uml: '\u00A8', - copy: '\u00A9', - ordf: '\u00AA', - laquo: '\u00AB', - not: '\u00AC', - shy: '\u00AD', - reg: '\u00AE', - macr: '\u00AF', - deg: '\u00B0', - plusmn: '\u00B1', - sup2: '\u00B2', - sup3: '\u00B3', - acute: '\u00B4', - micro: '\u00B5', - para: '\u00B6', - middot: '\u00B7', - cedil: '\u00B8', - sup1: '\u00B9', - ordm: '\u00BA', - raquo: '\u00BB', - frac14: '\u00BC', - frac12: '\u00BD', - frac34: '\u00BE', - iquest: '\u00BF', - Agrave: '\u00C0', Aacute: '\u00C1', + aacute: '\u00E1', Acirc: '\u00C2', - Atilde: '\u00C3', - Auml: '\u00C4', - Aring: '\u00C5', + acirc: '\u00E2', + acute: '\u00B4', AElig: '\u00C6', - Ccedil: '\u00C7', - Egrave: '\u00C8', - Eacute: '\u00C9', - Ecirc: '\u00CA', - Euml: '\u00CB', - Igrave: '\u00CC', - Iacute: '\u00CD', - Icirc: '\u00CE', - Iuml: '\u00CF', - ETH: '\u00D0', - Ntilde: '\u00D1', - Ograve: '\u00D2', - Oacute: '\u00D3', - Ocirc: '\u00D4', - Otilde: '\u00D5', - Ouml: '\u00D6', - times: '\u00D7', - Oslash: '\u00D8', - Ugrave: '\u00D9', - Uacute: '\u00DA', - Ucirc: '\u00DB', - Uuml: '\u00DC', - Yacute: '\u00DD', - THORN: '\u00DE', - szlig: '\u00DF', + aelig: '\u00E6', + Agrave: '\u00C0', agrave: '\u00E0', - aacute: '\u00E1', - acirc: '\u00E2', + alefsym: '\u2135', + Alpha: '\u0391', + alpha: '\u03B1', + amp: '&', + and: '\u2227', + ang: '\u2220', + apos: '\u0027', + Aring: '\u00C5', + aring: '\u00E5', + asymp: '\u2248', + Atilde: '\u00C3', atilde: '\u00E3', + Auml: '\u00C4', auml: '\u00E4', - aring: '\u00E5', - aelig: '\u00E6', + bdquo: '\u201E', + Beta: '\u0392', + beta: '\u03B2', + brvbar: '\u00A6', + bull: '\u2022', + cap: '\u2229', + Ccedil: '\u00C7', ccedil: '\u00E7', - egrave: '\u00E8', + cedil: '\u00B8', + cent: '\u00A2', + Chi: '\u03A7', + chi: '\u03C7', + circ: '\u02C6', + clubs: '\u2663', + cong: '\u2245', + copy: '\u00A9', + crarr: '\u21B5', + cup: '\u222A', + curren: '\u00A4', + dagger: '\u2020', + Dagger: '\u2021', + darr: '\u2193', + dArr: '\u21D3', + deg: '\u00B0', + Delta: '\u0394', + delta: '\u03B4', + diams: '\u2666', + divide: '\u00F7', + Eacute: '\u00C9', eacute: '\u00E9', + Ecirc: '\u00CA', ecirc: '\u00EA', + Egrave: '\u00C8', + egrave: '\u00E8', + empty: '\u2205', + emsp: '\u2003', + ensp: '\u2002', + Epsilon: '\u0395', + epsilon: '\u03B5', + equiv: '\u2261', + Eta: '\u0397', + eta: '\u03B7', + ETH: '\u00D0', + eth: '\u00F0', + Euml: '\u00CB', euml: '\u00EB', - igrave: '\u00EC', + euro: '\u20AC', + exist: '\u2203', + fnof: '\u0192', + forall: '\u2200', + frac12: '\u00BD', + frac14: '\u00BC', + frac34: '\u00BE', + frasl: '\u2044', + Gamma: '\u0393', + gamma: '\u03B3', + ge: '\u2265', + gt: '>', + harr: '\u2194', + hArr: '\u21D4', + hearts: '\u2665', + hellip: '\u2026', + Iacute: '\u00CD', iacute: '\u00ED', + Icirc: '\u00CE', icirc: '\u00EE', + iexcl: '\u00A1', + Igrave: '\u00CC', + igrave: '\u00EC', + image: '\u2111', + infin: '\u221E', + int: '\u222B', + Iota: '\u0399', + iota: '\u03B9', + iquest: '\u00BF', + isin: '\u2208', + Iuml: '\u00CF', iuml: '\u00EF', - eth: '\u00F0', + Kappa: '\u039A', + kappa: '\u03BA', + Lambda: '\u039B', + lambda: '\u03BB', + lang: '\u2329', + laquo: '\u00AB', + larr: '\u2190', + lArr: '\u21D0', + lceil: '\u2308', + ldquo: '\u201C', + le: '\u2264', + lfloor: '\u230A', + lowast: '\u2217', + loz: '\u25CA', + lrm: '\u200E', + lsaquo: '\u2039', + lsquo: '\u2018', + lt: '<', + macr: '\u00AF', + mdash: '\u2014', + micro: '\u00B5', + middot: '\u00B7', + minus: '\u2212', + Mu: '\u039C', + mu: '\u03BC', + nabla: '\u2207', + nbsp: '\u00A0', + ndash: '\u2013', + ne: '\u2260', + ni: '\u220B', + not: '\u00AC', + notin: '\u2209', + nsub: '\u2284', + Ntilde: '\u00D1', ntilde: '\u00F1', - ograve: '\u00F2', + Nu: '\u039D', + nu: '\u03BD', + Oacute: '\u00D3', oacute: '\u00F3', + Ocirc: '\u00D4', ocirc: '\u00F4', - otilde: '\u00F5', - ouml: '\u00F6', - divide: '\u00F7', - oslash: '\u00F8', - ugrave: '\u00F9', - uacute: '\u00FA', - ucirc: '\u00FB', - uuml: '\u00FC', - yacute: '\u00FD', - thorn: '\u00FE', - yuml: '\u00FF', OElig: '\u0152', oelig: '\u0153', - Scaron: '\u0160', - scaron: '\u0161', - Yuml: '\u0178', - fnof: '\u0192', - circ: '\u02C6', - tilde: '\u02DC', - Alpha: '\u0391', - Beta: '\u0392', - Gamma: '\u0393', - Delta: '\u0394', - Epsilon: '\u0395', - Zeta: '\u0396', - Eta: '\u0397', - Theta: '\u0398', - Iota: '\u0399', - Kappa: '\u039A', - Lambda: '\u039B', - Mu: '\u039C', - Nu: '\u039D', - Xi: '\u039E', - Omicron: '\u039F', - Pi: '\u03A0', - Rho: '\u03A1', - Sigma: '\u03A3', - Tau: '\u03A4', - Upsilon: '\u03A5', - Phi: '\u03A6', - Chi: '\u03A7', - Psi: '\u03A8', + Ograve: '\u00D2', + ograve: '\u00F2', + oline: '\u203E', Omega: '\u03A9', - alpha: '\u03B1', - beta: '\u03B2', - gamma: '\u03B3', - delta: '\u03B4', - epsilon: '\u03B5', - zeta: '\u03B6', - eta: '\u03B7', - theta: '\u03B8', - iota: '\u03B9', - kappa: '\u03BA', - lambda: '\u03BB', - mu: '\u03BC', - nu: '\u03BD', - xi: '\u03BE', + omega: '\u03C9', + Omicron: '\u039F', omicron: '\u03BF', - pi: '\u03C0', - rho: '\u03C1', - sigmaf: '\u03C2', - sigma: '\u03C3', - tau: '\u03C4', - upsilon: '\u03C5', + oplus: '\u2295', + or: '\u2228', + ordf: '\u00AA', + ordm: '\u00BA', + Oslash: '\u00D8', + oslash: '\u00F8', + Otilde: '\u00D5', + otilde: '\u00F5', + otimes: '\u2297', + Ouml: '\u00D6', + ouml: '\u00F6', + para: '\u00B6', + part: '\u2202', + permil: '\u2030', + perp: '\u22A5', + Phi: '\u03A6', phi: '\u03C6', - chi: '\u03C7', - psi: '\u03C8', - omega: '\u03C9', - thetasym: '\u03D1', - upsih: '\u03D2', + Pi: '\u03A0', + pi: '\u03C0', piv: '\u03D6', - ensp: '\u2002', - emsp: '\u2003', - thinsp: '\u2009', - zwnj: '\u200C', - zwj: '\u200D', - lrm: '\u200E', - rlm: '\u200F', - ndash: '\u2013', - mdash: '\u2014', - lsquo: '\u2018', - rsquo: '\u2019', - sbquo: '\u201A', - ldquo: '\u201C', - rdquo: '\u201D', - bdquo: '\u201E', - dagger: '\u2020', - Dagger: '\u2021', - bull: '\u2022', - hellip: '\u2026', - permil: '\u2030', + plusmn: '\u00B1', + pound: '\u00A3', prime: '\u2032', Prime: '\u2033', - lsaquo: '\u2039', - rsaquo: '\u203A', - oline: '\u203E', - frasl: '\u2044', - euro: '\u20AC', - image: '\u2111', - weierp: '\u2118', - real: '\u211C', - trade: '\u2122', - alefsym: '\u2135', - larr: '\u2190', - uarr: '\u2191', - rarr: '\u2192', - darr: '\u2193', - harr: '\u2194', - crarr: '\u21B5', - lArr: '\u21D0', - uArr: '\u21D1', - rArr: '\u21D2', - dArr: '\u21D3', - hArr: '\u21D4', - forall: '\u2200', - part: '\u2202', - exist: '\u2203', - empty: '\u2205', - nabla: '\u2207', - isin: '\u2208', - notin: '\u2209', - ni: '\u220B', prod: '\u220F', - sum: '\u2211', - minus: '\u2212', - lowast: '\u2217', - radic: '\u221A', prop: '\u221D', - infin: '\u221E', - ang: '\u2220', - and: '\u2227', - or: '\u2228', - cap: '\u2229', - cup: '\u222A', - int: '\u222B', - there4: '\u2234', + Psi: '\u03A8', + psi: '\u03C8', + quot: '\u0022', + radic: '\u221A', + rang: '\u232A', + raquo: '\u00BB', + rarr: '\u2192', + rArr: '\u21D2', + rceil: '\u2309', + rdquo: '\u201D', + real: '\u211C', + reg: '\u00AE', + rfloor: '\u230B', + Rho: '\u03A1', + rho: '\u03C1', + rlm: '\u200F', + rsaquo: '\u203A', + rsquo: '\u2019', + sbquo: '\u201A', + Scaron: '\u0160', + scaron: '\u0161', + sdot: '\u22C5', + sect: '\u00A7', + shy: '\u00AD', + Sigma: '\u03A3', + sigma: '\u03C3', + sigmaf: '\u03C2', sim: '\u223C', - cong: '\u2245', - asymp: '\u2248', - ne: '\u2260', - equiv: '\u2261', - le: '\u2264', - ge: '\u2265', + spades: '\u2660', sub: '\u2282', - sup: '\u2283', - nsub: '\u2284', sube: '\u2286', + sum: '\u2211', + sup: '\u2283', + sup1: '\u00B9', + sup2: '\u00B2', + sup3: '\u00B3', supe: '\u2287', - oplus: '\u2295', - otimes: '\u2297', - perp: '\u22A5', - sdot: '\u22C5', - lceil: '\u2308', - rceil: '\u2309', - lfloor: '\u230A', - rfloor: '\u230B', - lang: '\u2329', - rang: '\u232A', - loz: '\u25CA', - spades: '\u2660', - clubs: '\u2663', - hearts: '\u2665', - diams: '\u2666', + szlig: '\u00DF', + Tau: '\u03A4', + tau: '\u03C4', + there4: '\u2234', + Theta: '\u0398', + theta: '\u03B8', + thetasym: '\u03D1', + thinsp: '\u2009', + THORN: '\u00DE', + thorn: '\u00FE', + tilde: '\u02DC', + times: '\u00D7', + trade: '\u2122', + Uacute: '\u00DA', + uacute: '\u00FA', + uarr: '\u2191', + uArr: '\u21D1', + Ucirc: '\u00DB', + ucirc: '\u00FB', + Ugrave: '\u00D9', + ugrave: '\u00F9', + uml: '\u00A8', + upsih: '\u03D2', + Upsilon: '\u03A5', + upsilon: '\u03C5', + Uuml: '\u00DC', + uuml: '\u00FC', + weierp: '\u2118', + Xi: '\u039E', + xi: '\u03BE', + Yacute: '\u00DD', + yacute: '\u00FD', + yen: '\u00A5', + yuml: '\u00FF', + Yuml: '\u0178', + Zeta: '\u0396', + zeta: '\u03B6', + zwj: '\u200D', + zwnj: '\u200C', }; diff --git a/packages/typescript-estree/src/node-utils.ts b/packages/typescript-estree/src/node-utils.ts index 6d16729b070..46922d1058d 100644 --- a/packages/typescript-estree/src/node-utils.ts +++ b/packages/typescript-estree/src/node-utils.ts @@ -1,8 +1,9 @@ import * as ts from 'typescript'; +import type { TSESTree, TSNode } from './ts-estree'; + import { getModifiers } from './getModifiers'; import { xhtmlEntities } from './jsx/xhtml-entities'; -import type { TSESTree, TSNode } from './ts-estree'; import { AST_NODE_TYPES, AST_TOKEN_TYPES } from './ts-estree'; import { typescriptVersionIsAtLeast } from './version-check'; @@ -15,8 +16,8 @@ type LogicalOperatorKind = | ts.SyntaxKind.BarBarToken | ts.SyntaxKind.QuestionQuestionToken; const LOGICAL_OPERATORS: ReadonlySet = new Set([ - SyntaxKind.BarBarToken, SyntaxKind.AmpersandAmpersandToken, + SyntaxKind.BarBarToken, SyntaxKind.QuestionQuestionToken, ]); @@ -24,58 +25,58 @@ interface TokenToText extends TSESTree.PunctuatorTokenToText, TSESTree.BinaryOperatorToText { [SyntaxKind.ImportKeyword]: 'import'; - [SyntaxKind.NewKeyword]: 'new'; [SyntaxKind.KeyOfKeyword]: 'keyof'; + [SyntaxKind.NewKeyword]: 'new'; [SyntaxKind.ReadonlyKeyword]: 'readonly'; [SyntaxKind.UniqueKeyword]: 'unique'; } type AssignmentOperatorKind = keyof TSESTree.AssignmentOperatorToText; const ASSIGNMENT_OPERATORS: ReadonlySet = new Set([ - ts.SyntaxKind.EqualsToken, - ts.SyntaxKind.PlusEqualsToken, - ts.SyntaxKind.MinusEqualsToken, - ts.SyntaxKind.AsteriskEqualsToken, + ts.SyntaxKind.AmpersandAmpersandEqualsToken, + ts.SyntaxKind.AmpersandEqualsToken, ts.SyntaxKind.AsteriskAsteriskEqualsToken, - ts.SyntaxKind.SlashEqualsToken, - ts.SyntaxKind.PercentEqualsToken, - ts.SyntaxKind.LessThanLessThanEqualsToken, + ts.SyntaxKind.AsteriskEqualsToken, + ts.SyntaxKind.BarBarEqualsToken, + ts.SyntaxKind.BarEqualsToken, + ts.SyntaxKind.CaretEqualsToken, + ts.SyntaxKind.EqualsToken, ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, - ts.SyntaxKind.AmpersandEqualsToken, - ts.SyntaxKind.BarEqualsToken, - ts.SyntaxKind.BarBarEqualsToken, - ts.SyntaxKind.AmpersandAmpersandEqualsToken, + ts.SyntaxKind.LessThanLessThanEqualsToken, + ts.SyntaxKind.MinusEqualsToken, + ts.SyntaxKind.PercentEqualsToken, + ts.SyntaxKind.PlusEqualsToken, ts.SyntaxKind.QuestionQuestionEqualsToken, - ts.SyntaxKind.CaretEqualsToken, + ts.SyntaxKind.SlashEqualsToken, ]); type BinaryOperatorKind = keyof TSESTree.BinaryOperatorToText; const BINARY_OPERATORS: ReadonlySet = new Set([ - SyntaxKind.InstanceOfKeyword, - SyntaxKind.InKeyword, + SyntaxKind.AmpersandAmpersandToken, + SyntaxKind.AmpersandToken, SyntaxKind.AsteriskAsteriskToken, SyntaxKind.AsteriskToken, - SyntaxKind.SlashToken, - SyntaxKind.PercentToken, - SyntaxKind.PlusToken, - SyntaxKind.MinusToken, - SyntaxKind.AmpersandToken, + SyntaxKind.BarBarToken, SyntaxKind.BarToken, SyntaxKind.CaretToken, - SyntaxKind.LessThanLessThanToken, - SyntaxKind.GreaterThanGreaterThanToken, - SyntaxKind.GreaterThanGreaterThanGreaterThanToken, - SyntaxKind.AmpersandAmpersandToken, - SyntaxKind.BarBarToken, - SyntaxKind.LessThanToken, - SyntaxKind.LessThanEqualsToken, - SyntaxKind.GreaterThanToken, - SyntaxKind.GreaterThanEqualsToken, - SyntaxKind.EqualsEqualsToken, SyntaxKind.EqualsEqualsEqualsToken, + SyntaxKind.EqualsEqualsToken, SyntaxKind.ExclamationEqualsEqualsToken, SyntaxKind.ExclamationEqualsToken, + SyntaxKind.GreaterThanEqualsToken, + SyntaxKind.GreaterThanGreaterThanGreaterThanToken, + SyntaxKind.GreaterThanGreaterThanToken, + SyntaxKind.GreaterThanToken, + SyntaxKind.InKeyword, + SyntaxKind.InstanceOfKeyword, + SyntaxKind.LessThanEqualsToken, + SyntaxKind.LessThanLessThanToken, + SyntaxKind.LessThanToken, + SyntaxKind.MinusToken, + SyntaxKind.PercentToken, + SyntaxKind.PlusToken, + SyntaxKind.SlashToken, ]); type DeclarationKind = TSESTree.VariableDeclaration['kind']; @@ -200,16 +201,16 @@ function isJSDocComment(node: ts.Node): node is ts.JSDoc { */ export function getBinaryExpressionType(operator: ts.BinaryOperatorToken): | { - type: AST_NODE_TYPES.AssignmentExpression; operator: TokenForTokenKind; + type: AST_NODE_TYPES.AssignmentExpression; } | { - type: AST_NODE_TYPES.BinaryExpression; operator: TokenForTokenKind; + type: AST_NODE_TYPES.BinaryExpression; } | { - type: AST_NODE_TYPES.LogicalExpression; operator: TokenForTokenKind; + type: AST_NODE_TYPES.LogicalExpression; } { if (isAssignmentOperator(operator)) { return { @@ -245,8 +246,8 @@ export function getLineAndCharacterFor( ): TSESTree.Position { const loc = ast.getLineAndCharacterOfPosition(pos); return { - line: loc.line + 1, column: loc.character, + line: loc.line + 1, }; } @@ -262,7 +263,7 @@ export function getLocFor( ast: ts.SourceFile, ): TSESTree.SourceLocation { const [start, end] = range.map(pos => getLineAndCharacterFor(pos, ast)); - return { start, end }; + return { end, start }; } /** @@ -637,22 +638,22 @@ export function convertToken( if (tokenType === AST_TOKEN_TYPES.RegularExpression) { return { type: tokenType, - value, - range, loc, + range, regex: { - pattern: value.slice(1, value.lastIndexOf('/')), flags: value.slice(value.lastIndexOf('/') + 1), + pattern: value.slice(1, value.lastIndexOf('/')), }, + value, }; } // @ts-expect-error TS is complaining about `value` not being the correct // type but it is return { type: tokenType, - value, - range, loc, + range, + value, }; } @@ -688,23 +689,23 @@ export class TSError extends Error { message: string, public readonly fileName: string, public readonly location: { - start: { - line: number; + end: { column: number; + line: number; offset: number; }; - end: { - line: number; + start: { column: number; + line: number; offset: number; }; }, ) { super(message); Object.defineProperty(this, 'name', { - value: new.target.name, - enumerable: false, configurable: true, + enumerable: false, + value: new.target.name, }); } @@ -738,16 +739,16 @@ export function createError( endIndex: number = startIndex, ): TSError { const [start, end] = [startIndex, endIndex].map(offset => { - const { line, character: column } = + const { character: column, line } = ast.getLineAndCharacterOfPosition(offset); - return { line: line + 1, column, offset }; + return { column, line: line + 1, offset }; }); - return new TSError(message, ast.fileName, { start, end }); + return new TSError(message, ast.fileName, { end, start }); } export function nodeHasIllegalDecorators( node: ts.Node, -): node is ts.Node & { illegalDecorators: ts.Node[] } { +): node is { illegalDecorators: ts.Node[] } & ts.Node { return !!( 'illegalDecorators' in node && (node.illegalDecorators as unknown[] | undefined)?.length @@ -943,11 +944,11 @@ export function isValidAssignmentTarget(node: ts.Node): boolean { return isValidAssignmentTarget( ( node as - | ts.ParenthesizedExpression | ts.AssertionExpression - | ts.SatisfiesExpression | ts.ExpressionWithTypeArguments | ts.NonNullExpression + | ts.ParenthesizedExpression + | ts.SatisfiesExpression ).expression, ); default: diff --git a/packages/typescript-estree/src/parseSettings/ExpiringCache.ts b/packages/typescript-estree/src/parseSettings/ExpiringCache.ts index d02bdfb0b1f..837612c026b 100644 --- a/packages/typescript-estree/src/parseSettings/ExpiringCache.ts +++ b/packages/typescript-estree/src/parseSettings/ExpiringCache.ts @@ -17,8 +17,8 @@ export class ExpiringCache implements CacheLike { readonly #map = new Map< Key, Readonly<{ - value: Value; lastSeen: [number, number]; + value: Value; }> >(); @@ -26,16 +26,8 @@ export class ExpiringCache implements CacheLike { this.#cacheDurationSeconds = cacheDurationSeconds; } - set(key: Key, value: Value): this { - this.#map.set(key, { - value, - lastSeen: - this.#cacheDurationSeconds === 'Infinity' - ? // no need to waste time calculating the hrtime in infinity mode as there's no expiry - ZERO_HR_TIME - : process.hrtime(), - }); - return this; + clear(): void { + this.#map.clear(); } get(key: Key): Value | undefined { @@ -57,7 +49,15 @@ export class ExpiringCache implements CacheLike { return undefined; } - clear(): void { - this.#map.clear(); + set(key: Key, value: Value): this { + this.#map.set(key, { + lastSeen: + this.#cacheDurationSeconds === 'Infinity' + ? // no need to waste time calculating the hrtime in infinity mode as there's no expiry + ZERO_HR_TIME + : process.hrtime(), + value, + }); + return this; } } diff --git a/packages/typescript-estree/src/parseSettings/createParseSettings.ts b/packages/typescript-estree/src/parseSettings/createParseSettings.ts index 35783e0acbe..999107b6d4a 100644 --- a/packages/typescript-estree/src/parseSettings/createParseSettings.ts +++ b/packages/typescript-estree/src/parseSettings/createParseSettings.ts @@ -1,19 +1,19 @@ -import path from 'node:path'; - import debug from 'debug'; +import path from 'node:path'; import * as ts from 'typescript'; import type { ProjectServiceSettings } from '../create-program/createProjectService'; +import type { TSESTreeOptions } from '../parser-options'; +import type { MutableParseSettings } from './index'; + import { createProjectService } from '../create-program/createProjectService'; import { ensureAbsolutePath } from '../create-program/shared'; -import type { TSESTreeOptions } from '../parser-options'; import { isSourceFile } from '../source-files'; import { DEFAULT_TSCONFIG_CACHE_DURATION_SECONDS, ExpiringCache, } from './ExpiringCache'; import { getProjectConfigFiles } from './getProjectConfigFiles'; -import type { MutableParseSettings } from './index'; import { inferSingleRun } from './inferSingleRun'; import { resolveProjectList } from './resolveProjectList'; import { warnAboutTSVersion } from './warnAboutTSVersion'; @@ -31,14 +31,14 @@ let TSSERVER_PROJECT_SERVICE: ProjectServiceSettings | null = null; /* eslint-disable @typescript-eslint/no-unnecessary-condition */ const JSDocParsingMode = { ParseAll: ts.JSDocParsingMode?.ParseAll, - ParseNone: ts.JSDocParsingMode?.ParseNone, ParseForTypeErrors: ts.JSDocParsingMode?.ParseForTypeErrors, ParseForTypeInfo: ts.JSDocParsingMode?.ParseForTypeInfo, + ParseNone: ts.JSDocParsingMode?.ParseNone, } as const; /* eslint-enable @typescript-eslint/no-unnecessary-condition */ export function createParseSettings( - code: ts.SourceFile | string, + code: string | ts.SourceFile, tsestreeOptions: Partial = {}, ): MutableParseSettings { const codeFullText = enforceCodeString(code); @@ -73,6 +73,8 @@ export function createParseSettings( })(); const parseSettings: MutableParseSettings = { + loc: tsestreeOptions.loc === true, + range: tsestreeOptions.range === true, allowInvalidAST: tsestreeOptions.allowInvalidAST === true, code, codeFullText, @@ -92,19 +94,8 @@ export function createParseSettings( ? tsestreeOptions.extraFileExtensions : [], filePath, - setExternalModuleIndicator: - tsestreeOptions.sourceType === 'module' || - (tsestreeOptions.sourceType === undefined && - extension === ts.Extension.Mjs) || - (tsestreeOptions.sourceType === undefined && - extension === ts.Extension.Mts) - ? (file): void => { - file.externalModuleIndicator = true; - } - : undefined, jsDocParsingMode, jsx: tsestreeOptions.jsx === true, - loc: tsestreeOptions.loc === true, log: typeof tsestreeOptions.loggerFn === 'function' ? tsestreeOptions.loggerFn @@ -127,7 +118,16 @@ export function createParseSettings( tsconfigRootDir, )) : undefined, - range: tsestreeOptions.range === true, + setExternalModuleIndicator: + tsestreeOptions.sourceType === 'module' || + (tsestreeOptions.sourceType === undefined && + extension === ts.Extension.Mjs) || + (tsestreeOptions.sourceType === undefined && + extension === ts.Extension.Mts) + ? (file): void => { + file.externalModuleIndicator = true; + } + : undefined, singleRun, suppressDeprecatedPropertyWarnings: tsestreeOptions.suppressDeprecatedPropertyWarnings ?? diff --git a/packages/typescript-estree/src/parseSettings/getProjectConfigFiles.ts b/packages/typescript-estree/src/parseSettings/getProjectConfigFiles.ts index 9744eec4146..6c79d0619f8 100644 --- a/packages/typescript-estree/src/parseSettings/getProjectConfigFiles.ts +++ b/packages/typescript-estree/src/parseSettings/getProjectConfigFiles.ts @@ -1,8 +1,7 @@ +import debug from 'debug'; import * as fs from 'node:fs'; import * as path from 'node:path'; -import debug from 'debug'; - import type { TSESTreeOptions } from '../parser-options'; import type { ParseSettings } from './index'; diff --git a/packages/typescript-estree/src/parseSettings/index.ts b/packages/typescript-estree/src/parseSettings/index.ts index 2e9b341d3dc..1ea208210e0 100644 --- a/packages/typescript-estree/src/parseSettings/index.ts +++ b/packages/typescript-estree/src/parseSettings/index.ts @@ -5,7 +5,7 @@ import type { CanonicalPath } from '../create-program/shared'; import type { TSESTree } from '../ts-estree'; import type { CacheLike } from './ExpiringCache'; -type DebugModule = 'eslint' | 'typescript-eslint' | 'typescript'; +type DebugModule = 'eslint' | 'typescript' | 'typescript-eslint'; // Workaround to support new TS version features for consumers on old TS versions declare module 'typescript' { @@ -30,7 +30,7 @@ export interface MutableParseSettings { /** * Code of the file being parsed, or raw source file containing it. */ - code: ts.SourceFile | string; + code: string | ts.SourceFile; /** * Full text of the file being parsed. diff --git a/packages/typescript-estree/src/parseSettings/resolveProjectList.ts b/packages/typescript-estree/src/parseSettings/resolveProjectList.ts index 4a09b55775e..7634387df9c 100644 --- a/packages/typescript-estree/src/parseSettings/resolveProjectList.ts +++ b/packages/typescript-estree/src/parseSettings/resolveProjectList.ts @@ -3,12 +3,13 @@ import { sync as globSync } from 'fast-glob'; import isGlob from 'is-glob'; import type { CanonicalPath } from '../create-program/shared'; +import type { TSESTreeOptions } from '../parser-options'; + import { createHash, ensureAbsolutePath, getCanonicalFileName, } from '../create-program/shared'; -import type { TSESTreeOptions } from '../parser-options'; import { DEFAULT_TSCONFIG_CACHE_DURATION_SECONDS, ExpiringCache, diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index 44af134611c..48cbf0b8da1 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -91,7 +91,7 @@ interface ParseOptions { * When value is `false`, no logging will occur. * When value is not provided, `console.log()` will be used. */ - loggerFn?: false | ((message: string) => void); + loggerFn?: ((message: string) => void) | false; /** * Controls whether the `range` property is included on AST nodes. @@ -190,7 +190,7 @@ interface ParseAndGenerateServicesOptions extends ParseOptions { * * Note that {@link projectService} is now preferred. */ - project?: string[] | string | boolean | null; + project?: boolean | string | string[] | null; /** * If you provide a glob (or globs) to the project option, you can use this option to ignore certain folders from @@ -248,9 +248,9 @@ export interface ParserServicesNodeMaps { export interface ParserServicesWithTypeInformation extends ParserServicesNodeMaps, ParserServicesBase { - program: ts.Program; getSymbolAtLocation: (node: TSESTree.Node) => ts.Symbol | undefined; getTypeAtLocation: (node: TSESTree.Node) => ts.Type; + program: ts.Program; } export interface ParserServicesWithoutTypeInformation extends ParserServicesNodeMaps, diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index 3f37aeed5e6..340bee1c15a 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -1,6 +1,16 @@ -import debug from 'debug'; import type * as ts from 'typescript'; +import debug from 'debug'; + +import type { ASTAndProgram, CanonicalPath } from './create-program/shared'; +import type { + ParserServices, + ParserServicesNodeMaps, + TSESTreeOptions, +} from './parser-options'; +import type { ParseSettings } from './parseSettings'; +import type { TSESTree } from './ts-estree'; + import { astConverter } from './ast-converter'; import { convertError } from './convert'; import { createIsolatedProgram } from './create-program/createIsolatedProgram'; @@ -10,21 +20,13 @@ import { createSourceFile, } from './create-program/createSourceFile'; import { getWatchProgramsForProjects } from './create-program/getWatchProgramsForProjects'; -import type { ASTAndProgram, CanonicalPath } from './create-program/shared'; import { createProgramFromConfigFile, useProvidedPrograms, } from './create-program/useProvidedPrograms'; import { createParserServices } from './createParserServices'; -import type { - ParserServices, - ParserServicesNodeMaps, - 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'; import { useProgramFromProjectService } from './useProgramFromProjectService'; const log = debug('typescript-eslint:typescript-estree:parser'); @@ -88,9 +90,11 @@ function getProgramAndAST( } /* eslint-disable @typescript-eslint/no-empty-object-type */ -type AST = TSESTree.Program & - (T['comment'] extends true ? { comments: TSESTree.Comment[] } : {}) & - (T['tokens'] extends true ? { tokens: TSESTree.Token[] } : {}); +type AST = (T['comment'] extends true + ? { comments: TSESTree.Comment[] } + : {}) & + (T['tokens'] extends true ? { tokens: TSESTree.Token[] } : {}) & + TSESTree.Program; /* eslint-enable @typescript-eslint/no-empty-object-type */ interface ParseAndGenerateServicesResult { @@ -111,7 +115,7 @@ function parse( } function parseWithNodeMapsInternal( - code: ts.SourceFile | string, + code: string | ts.SourceFile, options: T | undefined, shouldPreserveNodeMaps: boolean, ): ParseWithNodeMapsResult { @@ -137,7 +141,7 @@ function parseWithNodeMapsInternal( /** * Convert the TypeScript AST to an ESTree-compatible one */ - const { estree, astMaps } = astConverter( + const { astMaps, estree } = astConverter( ast, parseSettings, shouldPreserveNodeMaps, @@ -157,7 +161,7 @@ function clearParseAndGenerateServicesCalls(): void { } function parseAndGenerateServices( - code: ts.SourceFile | string, + code: string | ts.SourceFile, tsestreeOptions: T, ): ParseAndGenerateServicesResult { /** @@ -246,7 +250,7 @@ function parseAndGenerateServices( ? parseSettings.preserveNodeMaps : true; - const { estree, astMaps } = astConverter( + const { astMaps, estree } = astConverter( ast, parseSettings, shouldPreserveNodeMaps, @@ -274,10 +278,10 @@ function parseAndGenerateServices( export { type AST, + clearDefaultProjectMatchedFiles, + clearParseAndGenerateServicesCalls, + clearProgramCache, parse, parseAndGenerateServices, type ParseAndGenerateServicesResult, - clearDefaultProjectMatchedFiles, - clearProgramCache, - clearParseAndGenerateServicesCalls, }; diff --git a/packages/typescript-estree/src/semantic-or-syntactic-errors.ts b/packages/typescript-estree/src/semantic-or-syntactic-errors.ts index 584e47cb51c..a88a3615d9f 100644 --- a/packages/typescript-estree/src/semantic-or-syntactic-errors.ts +++ b/packages/typescript-estree/src/semantic-or-syntactic-errors.ts @@ -4,6 +4,7 @@ import type { Program, SourceFile, } from 'typescript'; + import { flattenDiagnosticMessageText, sys } from 'typescript'; export interface SemanticOrSyntacticError extends Diagnostic { diff --git a/packages/typescript-estree/src/simple-traverse.ts b/packages/typescript-estree/src/simple-traverse.ts index b02189678a4..aa0d7ed4d07 100644 --- a/packages/typescript-estree/src/simple-traverse.ts +++ b/packages/typescript-estree/src/simple-traverse.ts @@ -1,4 +1,5 @@ import type { VisitorKeys } from '@typescript-eslint/visitor-keys'; + import { visitorKeys } from '@typescript-eslint/visitor-keys'; import type { TSESTree } from './ts-estree'; @@ -22,8 +23,8 @@ function getVisitorKeysForNode( type SimpleTraverseOptions = Readonly< | { - visitorKeys?: Readonly; enter: (node: TSESTree.Node, parent: TSESTree.Node | undefined) => void; + visitorKeys?: Readonly; } | { visitorKeys?: Readonly; diff --git a/packages/typescript-estree/src/source-files.ts b/packages/typescript-estree/src/source-files.ts index 8c4ae0fc87e..18cd4567090 100644 --- a/packages/typescript-estree/src/source-files.ts +++ b/packages/typescript-estree/src/source-files.ts @@ -12,6 +12,6 @@ export function isSourceFile(code: unknown): code is ts.SourceFile { ); } -export function getCodeText(code: ts.SourceFile | string): string { +export function getCodeText(code: string | ts.SourceFile): string { return isSourceFile(code) ? code.getFullText(code) : code; } 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 f2ec49c70cf..4edb03880ef 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 @@ -30,7 +30,6 @@ export interface EstreeToTsNodeTypes { [AST_NODE_TYPES.ClassBody]: ts.ClassDeclaration | ts.ClassExpression; [AST_NODE_TYPES.ClassDeclaration]: ts.ClassDeclaration; [AST_NODE_TYPES.ClassExpression]: ts.ClassExpression; - [AST_NODE_TYPES.PropertyDefinition]: ts.PropertyDeclaration; [AST_NODE_TYPES.ConditionalExpression]: ts.ConditionalExpression; [AST_NODE_TYPES.ContinueStatement]: ts.ContinueStatement; [AST_NODE_TYPES.DebuggerStatement]: ts.DebuggerStatement; @@ -75,8 +74,9 @@ export interface EstreeToTsNodeTypes { | ts.ConstructorDeclaration | ts.Identifier | ts.Token; - [AST_NODE_TYPES.PrivateIdentifier]: ts.PrivateIdentifier; [AST_NODE_TYPES.IfStatement]: ts.IfStatement; + [AST_NODE_TYPES.PrivateIdentifier]: ts.PrivateIdentifier; + [AST_NODE_TYPES.PropertyDefinition]: ts.PropertyDeclaration; // eslint-disable-next-line @typescript-eslint/internal/prefer-ast-types-enum [AST_NODE_TYPES.ImportAttribute]: 'ImportAttribute' extends keyof typeof ts ? ts.ImportAttribute @@ -95,14 +95,14 @@ export interface EstreeToTsNodeTypes { [AST_NODE_TYPES.JSXExpressionContainer]: ts.JsxExpression; [AST_NODE_TYPES.JSXFragment]: ts.JsxFragment; [AST_NODE_TYPES.JSXIdentifier]: ts.Identifier | ts.ThisExpression; + [AST_NODE_TYPES.JSXMemberExpression]: ts.PropertyAccessExpression; + [AST_NODE_TYPES.JSXNamespacedName]: ts.JsxNamespacedName; [AST_NODE_TYPES.JSXOpeningElement]: | ts.JsxOpeningElement | ts.JsxSelfClosingElement; [AST_NODE_TYPES.JSXOpeningFragment]: ts.JsxOpeningFragment; [AST_NODE_TYPES.JSXSpreadAttribute]: ts.JsxSpreadAttribute; [AST_NODE_TYPES.JSXSpreadChild]: ts.JsxExpression; - [AST_NODE_TYPES.JSXMemberExpression]: ts.PropertyAccessExpression; - [AST_NODE_TYPES.JSXNamespacedName]: ts.JsxNamespacedName; [AST_NODE_TYPES.JSXText]: ts.JsxText; [AST_NODE_TYPES.LabeledStatement]: ts.LabeledStatement; [AST_NODE_TYPES.Literal]: @@ -163,12 +163,12 @@ export interface EstreeToTsNodeTypes { [AST_NODE_TYPES.ThrowStatement]: ts.ThrowStatement; [AST_NODE_TYPES.TryStatement]: ts.TryStatement; [AST_NODE_TYPES.TSAbstractAccessorProperty]: ts.PropertyDeclaration; - [AST_NODE_TYPES.TSAbstractPropertyDefinition]: ts.PropertyDeclaration; [AST_NODE_TYPES.TSAbstractMethodDefinition]: | ts.ConstructorDeclaration | ts.GetAccessorDeclaration | ts.MethodDeclaration | ts.SetAccessorDeclaration; + [AST_NODE_TYPES.TSAbstractPropertyDefinition]: ts.PropertyDeclaration; [AST_NODE_TYPES.TSArrayType]: ts.ArrayTypeNode; [AST_NODE_TYPES.TSAsExpression]: ts.AsExpression; [AST_NODE_TYPES.TSCallSignatureDeclaration]: ts.CallSignatureDeclaration; @@ -188,12 +188,11 @@ export interface EstreeToTsNodeTypes { [AST_NODE_TYPES.TSIndexedAccessType]: ts.IndexedAccessTypeNode; [AST_NODE_TYPES.TSIndexSignature]: ts.IndexSignatureDeclaration; [AST_NODE_TYPES.TSInferType]: ts.InferTypeNode; - [AST_NODE_TYPES.TSInterfaceDeclaration]: ts.InterfaceDeclaration; + [AST_NODE_TYPES.TSInstantiationExpression]: ts.ExpressionWithTypeArguments; [AST_NODE_TYPES.TSInterfaceBody]: ts.InterfaceDeclaration; + [AST_NODE_TYPES.TSInterfaceDeclaration]: ts.InterfaceDeclaration; [AST_NODE_TYPES.TSInterfaceHeritage]: ts.ExpressionWithTypeArguments; [AST_NODE_TYPES.TSIntersectionType]: ts.IntersectionTypeNode; - [AST_NODE_TYPES.TSInstantiationExpression]: ts.ExpressionWithTypeArguments; - [AST_NODE_TYPES.TSSatisfiesExpression]: ts.SatisfiesExpression; [AST_NODE_TYPES.TSLiteralType]: ts.LiteralTypeNode; [AST_NODE_TYPES.TSMappedType]: ts.MappedTypeNode; [AST_NODE_TYPES.TSMethodSignature]: @@ -212,9 +211,10 @@ export interface EstreeToTsNodeTypes { [AST_NODE_TYPES.TSRestType]: | ts.NamedTupleMember // for consistency and following babel's choices, a named tuple member with a rest gets converted to a TSRestType | ts.RestTypeNode; + [AST_NODE_TYPES.TSSatisfiesExpression]: ts.SatisfiesExpression; + [AST_NODE_TYPES.TSTemplateLiteralType]: ts.TemplateLiteralTypeNode; [AST_NODE_TYPES.TSThisType]: ts.ThisTypeNode; [AST_NODE_TYPES.TSTupleType]: ts.TupleTypeNode; - [AST_NODE_TYPES.TSTemplateLiteralType]: ts.TemplateLiteralTypeNode; [AST_NODE_TYPES.TSTypeAliasDeclaration]: ts.TypeAliasDeclaration; [AST_NODE_TYPES.TSTypeAnnotation]: undefined; [AST_NODE_TYPES.TSTypeAssertion]: ts.TypeAssertion; @@ -236,15 +236,15 @@ export interface EstreeToTsNodeTypes { [AST_NODE_TYPES.TSTypeQuery]: ts.ImportTypeNode | ts.TypeQueryNode; [AST_NODE_TYPES.TSTypeReference]: ts.TypeReferenceNode; [AST_NODE_TYPES.TSUnionType]: ts.UnionTypeNode; - [AST_NODE_TYPES.UpdateExpression]: - | ts.PostfixUnaryExpression - | ts.PrefixUnaryExpression; [AST_NODE_TYPES.UnaryExpression]: | ts.DeleteExpression | ts.PostfixUnaryExpression | ts.PrefixUnaryExpression | ts.TypeOfExpression | ts.VoidExpression; + [AST_NODE_TYPES.UpdateExpression]: + | ts.PostfixUnaryExpression + | ts.PrefixUnaryExpression; [AST_NODE_TYPES.VariableDeclaration]: | ts.VariableDeclarationList | ts.VariableStatement; @@ -264,30 +264,30 @@ export interface EstreeToTsNodeTypes { // Keywords [AST_NODE_TYPES.TSAbstractKeyword]: ts.Token; - [AST_NODE_TYPES.TSNullKeyword]: ts.KeywordTypeNode | ts.NullLiteral; - [AST_NODE_TYPES.TSAnyKeyword]: ts.KeywordTypeNode; + [AST_NODE_TYPES.TSBigIntKeyword]: ts.KeywordTypeNode; [AST_NODE_TYPES.TSBooleanKeyword]: ts.KeywordTypeNode; [AST_NODE_TYPES.TSIntrinsicKeyword]: ts.KeywordTypeNode; [AST_NODE_TYPES.TSNeverKeyword]: ts.KeywordTypeNode; + [AST_NODE_TYPES.TSNullKeyword]: ts.KeywordTypeNode | ts.NullLiteral; [AST_NODE_TYPES.TSNumberKeyword]: ts.KeywordTypeNode; [AST_NODE_TYPES.TSObjectKeyword]: ts.KeywordTypeNode; [AST_NODE_TYPES.TSStringKeyword]: ts.KeywordTypeNode; [AST_NODE_TYPES.TSSymbolKeyword]: ts.KeywordTypeNode; + [AST_NODE_TYPES.TSUndefinedKeyword]: ts.KeywordTypeNode; [AST_NODE_TYPES.TSUnknownKeyword]: ts.KeywordTypeNode; [AST_NODE_TYPES.TSVoidKeyword]: ts.KeywordTypeNode; - [AST_NODE_TYPES.TSUndefinedKeyword]: ts.KeywordTypeNode; // Unused [AST_NODE_TYPES.TSAsyncKeyword]: ts.Token; [AST_NODE_TYPES.TSDeclareKeyword]: ts.Token; [AST_NODE_TYPES.TSExportKeyword]: ts.Token; - [AST_NODE_TYPES.TSStaticKeyword]: ts.Token; - [AST_NODE_TYPES.TSPublicKeyword]: ts.Token; [AST_NODE_TYPES.TSPrivateKeyword]: ts.Token; [AST_NODE_TYPES.TSProtectedKeyword]: ts.Token; + [AST_NODE_TYPES.TSPublicKeyword]: ts.Token; [AST_NODE_TYPES.TSReadonlyKeyword]: ts.Token; + [AST_NODE_TYPES.TSStaticKeyword]: ts.Token; } /** diff --git a/packages/typescript-estree/src/ts-estree/index.ts b/packages/typescript-estree/src/ts-estree/index.ts index a7f64d91ce6..833ec57508b 100644 --- a/packages/typescript-estree/src/ts-estree/index.ts +++ b/packages/typescript-estree/src/ts-estree/index.ts @@ -1,8 +1,8 @@ // for simplicity and backwards-compatibility +export * from './estree-to-ts-node-types'; +export * from './ts-nodes'; export { AST_NODE_TYPES, AST_TOKEN_TYPES, TSESTree, } from '@typescript-eslint/types'; -export * from './ts-nodes'; -export * from './estree-to-ts-node-types'; diff --git a/packages/typescript-estree/src/ts-estree/ts-nodes.ts b/packages/typescript-estree/src/ts-estree/ts-nodes.ts index 11f48becebb..70a4ad99439 100644 --- a/packages/typescript-estree/src/ts-estree/ts-nodes.ts +++ b/packages/typescript-estree/src/ts-estree/ts-nodes.ts @@ -20,197 +20,197 @@ declare module 'typescript' { export type TSToken = ts.Token; export type TSNode = - | ts.Modifier - | ts.Identifier - | ts.ImportAttribute - | ts.ImportAttributes + | ts.ArrayBindingPattern + | ts.ArrayLiteralExpression + | ts.ArrayTypeNode + | ts.ArrowFunction + | ts.AsExpression /* eslint-disable-next-line @typescript-eslint/no-deprecated -- intentional for old TS versions */ | ts.AssertClause /* eslint-disable-next-line @typescript-eslint/no-deprecated -- intentional for old TS versions */ | ts.AssertEntry - | ts.PrivateIdentifier - | ts.QualifiedName - | ts.ComputedPropertyName - | ts.Decorator - | ts.TypeParameterDeclaration - // | ts.SignatureDeclarationBase -> CallSignatureDeclaration, ConstructSignatureDeclaration - | ts.CallSignatureDeclaration - | ts.ConstructSignatureDeclaration - | ts.VariableDeclaration - | ts.VariableDeclarationList - | ts.ParameterDeclaration - | ts.BindingElement - | ts.PropertySignature - | ts.PropertyDeclaration - | ts.PropertyAssignment - | ts.ShorthandPropertyAssignment - | ts.SpreadAssignment - | ts.ObjectBindingPattern - | ts.ArrayBindingPattern - | ts.FunctionDeclaration - | ts.MethodSignature - | ts.MethodDeclaration - | ts.ConstructorDeclaration - | ts.SemicolonClassElement - | ts.GetAccessorDeclaration - | ts.SetAccessorDeclaration - | ts.IndexSignatureDeclaration - | ts.KeywordTypeNode // TODO: This node is bad, maybe we should report this - | ts.ImportTypeNode - | ts.ThisTypeNode - | ts.ClassStaticBlockDeclaration - // | ts.FunctionOrConstructorTypeNodeBase -> FunctionTypeNode, ConstructorTypeNode - | ts.ConstructorTypeNode - | ts.FunctionTypeNode - | ts.TypeReferenceNode - | ts.TypePredicateNode - | ts.TypeQueryNode - | ts.TypeLiteralNode - | ts.ArrayTypeNode - | ts.NamedTupleMember - | ts.TupleTypeNode - | ts.OptionalTypeNode - | ts.RestTypeNode - | ts.UnionTypeNode - | ts.IntersectionTypeNode - | ts.ConditionalTypeNode - | ts.InferTypeNode - | ts.ParenthesizedTypeNode - | ts.TypeOperatorNode - | ts.IndexedAccessTypeNode - | ts.MappedTypeNode - | ts.LiteralTypeNode - | ts.StringLiteral - | ts.OmittedExpression - | ts.PartiallyEmittedExpression - | ts.PrefixUnaryExpression - | ts.PostfixUnaryExpression - | ts.NullLiteral - | ts.BooleanLiteral - | ts.ThisExpression - | ts.SuperExpression - | ts.ImportExpression - | ts.DeleteExpression - | ts.TypeOfExpression - | ts.VoidExpression | ts.AwaitExpression - | ts.YieldExpression - | ts.SyntheticExpression - | ts.BinaryExpression - | ts.ConditionalExpression - | ts.FunctionExpression - | ts.ArrowFunction - | ts.RegularExpressionLiteral - | ts.NoSubstitutionTemplateLiteral - | ts.NumericLiteral | ts.BigIntLiteral - | ts.TemplateHead - | ts.TemplateMiddle - | ts.TemplateTail - | ts.TemplateExpression - | ts.TemplateSpan - | ts.ParenthesizedExpression - | ts.ArrayLiteralExpression - | ts.SpreadElement - | ts.ObjectLiteralExpression - | ts.PropertyAccessExpression - | ts.ElementAccessExpression - | ts.CallExpression - | ts.ExpressionWithTypeArguments - | ts.NewExpression - | ts.TaggedTemplateExpression - | ts.AsExpression - | ts.TypeAssertion - | ts.NonNullExpression - | ts.MetaProperty - | ts.JsxElement - | ts.JsxOpeningElement - | ts.JsxSelfClosingElement - | ts.JsxFragment - | ts.JsxOpeningFragment - | ts.JsxClosingFragment - | ts.JsxAttribute - | ts.JsxSpreadAttribute - | ts.JsxClosingElement - | ts.JsxExpression - | ts.JsxNamespacedName - | ts.JsxText - | ts.NotEmittedStatement - | ts.CommaListExpression - | ts.EmptyStatement - | ts.DebuggerStatement - | ts.MissingDeclaration + | ts.BinaryExpression + | ts.BindingElement | ts.Block - | ts.VariableStatement - | ts.ExpressionStatement - | ts.IfStatement - | ts.DoStatement - | ts.WhileStatement - | ts.ForStatement - | ts.ForInStatement - | ts.ForOfStatement + | ts.BooleanLiteral | ts.BreakStatement - | ts.ContinueStatement - | ts.ReturnStatement - | ts.WithStatement - | ts.SwitchStatement + | ts.Bundle + | ts.CallExpression + | ts.CallSignatureDeclaration | ts.CaseBlock | ts.CaseClause - | ts.DefaultClause - | ts.LabeledStatement - | ts.ThrowStatement - | ts.TryStatement | ts.CatchClause - // | ts.ClassLikeDeclarationBase -> ClassDeclaration | ClassExpression | ts.ClassDeclaration | ts.ClassExpression - | ts.InterfaceDeclaration - | ts.HeritageClause - | ts.TypeAliasDeclaration - | ts.EnumMember + // | ts.ClassLikeDeclarationBase -> ClassDeclaration | ClassExpression + | ts.ClassStaticBlockDeclaration + | ts.CommaListExpression + | ts.ComputedPropertyName + | ts.ConditionalExpression + | ts.ConditionalTypeNode + | ts.ConstructorDeclaration + | ts.ConstructorTypeNode + | ts.ConstructSignatureDeclaration + | ts.ContinueStatement + | ts.DebuggerStatement + | ts.Decorator + | ts.DefaultClause + | ts.DeleteExpression + | ts.DoStatement + | ts.ElementAccessExpression + | ts.EmptyStatement | ts.EnumDeclaration - | ts.ModuleDeclaration - | ts.ModuleBlock - | ts.ImportEqualsDeclaration + | ts.EnumMember + | ts.ExportAssignment + | ts.ExportDeclaration + | ts.ExportSpecifier + | ts.ExpressionStatement + | ts.ExpressionWithTypeArguments | ts.ExternalModuleReference - | ts.ImportDeclaration + | ts.ForInStatement + | ts.ForOfStatement + | ts.ForStatement + | ts.FunctionDeclaration + | ts.FunctionExpression + // | ts.FunctionOrConstructorTypeNodeBase -> FunctionTypeNode, ConstructorTypeNode + | ts.FunctionTypeNode + | ts.GetAccessorDeclaration + | ts.HeritageClause + | ts.Identifier + | ts.IfStatement + | ts.ImportAttribute + | ts.ImportAttributes | ts.ImportClause - | ts.NamespaceImport - | ts.NamespaceExportDeclaration - | ts.ExportDeclaration - | ts.NamedImports - | ts.NamedExports + | ts.ImportDeclaration + | ts.ImportEqualsDeclaration + | ts.ImportExpression | ts.ImportSpecifier - | ts.ExportSpecifier - | ts.ExportAssignment - | ts.SourceFile - | ts.Bundle - | ts.JsonMinusNumericLiteral - | ts.TemplateLiteralTypeNode - | ts.SatisfiesExpression - - // JSDoc: Unsupported + | ts.ImportTypeNode + | ts.IndexedAccessTypeNode + | ts.IndexSignatureDeclaration + | ts.InferTypeNode + | ts.InterfaceDeclaration + | ts.IntersectionTypeNode | ts.JSDoc - | ts.JSDocTypeExpression - | ts.JSDocUnknownTag + | ts.JSDocAllType | ts.JSDocAugmentsTag + | ts.JSDocAuthorTag + | ts.JSDocCallbackTag | ts.JSDocClassTag | ts.JSDocEnumTag - | ts.JSDocThisTag - | ts.JSDocTemplateTag + | ts.JSDocFunctionType + | ts.JSDocNonNullableType + | ts.JSDocNullableType + | ts.JSDocOptionalType + | ts.JSDocParameterTag + | ts.JSDocPropertyTag | ts.JSDocReturnTag - | ts.JSDocTypeTag - | ts.JSDocTypedefTag - | ts.JSDocCallbackTag | ts.JSDocSignature - | ts.JSDocPropertyTag - | ts.JSDocParameterTag + | ts.JSDocTemplateTag + | ts.JSDocThisTag + | ts.JSDocTypedefTag + | ts.JSDocTypeExpression | ts.JSDocTypeLiteral - | ts.JSDocFunctionType - | ts.JSDocAllType + | ts.JSDocTypeTag + | ts.JSDocUnknownTag | ts.JSDocUnknownType - | ts.JSDocNullableType - | ts.JSDocNonNullableType - | ts.JSDocOptionalType | ts.JSDocVariadicType - | ts.JSDocAuthorTag; + | ts.JsonMinusNumericLiteral + | ts.JsxAttribute + | ts.JsxClosingElement + | ts.JsxClosingFragment + | ts.JsxElement + | ts.JsxExpression + | ts.JsxFragment + | ts.JsxNamespacedName + | ts.JsxOpeningElement + | ts.JsxOpeningFragment + | ts.JsxSelfClosingElement + | ts.JsxSpreadAttribute + | ts.JsxText + | ts.KeywordTypeNode // TODO: This node is bad, maybe we should report this + | ts.LabeledStatement + | ts.LiteralTypeNode + | ts.MappedTypeNode + | ts.MetaProperty + | ts.MethodDeclaration + | ts.MethodSignature + | ts.MissingDeclaration + | ts.Modifier + | ts.ModuleBlock + | ts.ModuleDeclaration + | ts.NamedExports + | ts.NamedImports + | ts.NamedTupleMember + | ts.NamespaceExportDeclaration + | ts.NamespaceImport + | ts.NewExpression + | ts.NonNullExpression + | ts.NoSubstitutionTemplateLiteral + | ts.NotEmittedStatement + | ts.NullLiteral + | ts.NumericLiteral + | ts.ObjectBindingPattern + | ts.ObjectLiteralExpression + | ts.OmittedExpression + | ts.OptionalTypeNode + | ts.ParameterDeclaration + | ts.ParenthesizedExpression + | ts.ParenthesizedTypeNode + | ts.PartiallyEmittedExpression + | ts.PostfixUnaryExpression + | ts.PrefixUnaryExpression + | ts.PrivateIdentifier + | ts.PropertyAccessExpression + | ts.PropertyAssignment + | ts.PropertyDeclaration + | ts.PropertySignature + | ts.QualifiedName + | ts.RegularExpressionLiteral + | ts.RestTypeNode + | ts.ReturnStatement + | ts.SatisfiesExpression + | ts.SemicolonClassElement + | ts.SetAccessorDeclaration + // | ts.SignatureDeclarationBase -> CallSignatureDeclaration, ConstructSignatureDeclaration + | ts.ShorthandPropertyAssignment + | ts.SourceFile + | ts.SpreadAssignment + | ts.SpreadElement + | ts.StringLiteral + | ts.SuperExpression + | ts.SwitchStatement + | ts.SyntheticExpression + | ts.TaggedTemplateExpression + | ts.TemplateExpression + | ts.TemplateHead + | ts.TemplateLiteralTypeNode + | ts.TemplateMiddle + + // JSDoc: Unsupported + | ts.TemplateSpan + | ts.TemplateTail + | ts.ThisExpression + | ts.ThisTypeNode + | ts.ThrowStatement + | ts.TryStatement + | ts.TupleTypeNode + | ts.TypeAliasDeclaration + | ts.TypeAssertion + | ts.TypeLiteralNode + | ts.TypeOfExpression + | ts.TypeOperatorNode + | ts.TypeParameterDeclaration + | ts.TypePredicateNode + | ts.TypeQueryNode + | ts.TypeReferenceNode + | ts.UnionTypeNode + | ts.VariableDeclaration + | ts.VariableDeclarationList + | ts.VariableStatement + | ts.VoidExpression + | ts.WhileStatement + | ts.WithStatement + | ts.YieldExpression; diff --git a/packages/typescript-estree/src/use-at-your-own-risk.ts b/packages/typescript-estree/src/use-at-your-own-risk.ts index 951d68d649a..06cc89e1a5a 100644 --- a/packages/typescript-estree/src/use-at-your-own-risk.ts +++ b/packages/typescript-estree/src/use-at-your-own-risk.ts @@ -1,6 +1,6 @@ // required by website -export * from './create-program/getScriptKind'; export * from './ast-converter'; +export * from './create-program/getScriptKind'; export type { ParseSettings } from './parseSettings'; // required by packages/utils/src/ts-estree.ts diff --git a/packages/typescript-estree/src/useProgramFromProjectService.ts b/packages/typescript-estree/src/useProgramFromProjectService.ts index 3a6b04190ad..cf98e552392 100644 --- a/packages/typescript-estree/src/useProgramFromProjectService.ts +++ b/packages/typescript-estree/src/useProgramFromProjectService.ts @@ -1,21 +1,21 @@ -import path from 'node:path'; -import util from 'node:util'; - import debug from 'debug'; import { minimatch } from 'minimatch'; +import path from 'node:path'; +import util from 'node:util'; import * as ts from 'typescript'; -import { createProjectProgram } from './create-program/createProjectProgram'; import type { ProjectServiceSettings } from './create-program/createProjectService'; -import { createNoProgram } from './create-program/createSourceFile'; import type { ASTAndDefiniteProgram, ASTAndNoProgram, ASTAndProgram, } from './create-program/shared'; +import type { MutableParseSettings } from './parseSettings'; + +import { createProjectProgram } from './create-program/createProjectProgram'; +import { createNoProgram } from './create-program/createSourceFile'; import { DEFAULT_EXTRA_FILE_EXTENSIONS } from './create-program/shared'; import { DEFAULT_PROJECT_FILES_ERROR_EXPLANATION } from './create-program/validateDefaultProjectForFilesGlob'; -import type { MutableParseSettings } from './parseSettings'; const RELOAD_THROTTLE_MS = 250; diff --git a/packages/typescript-estree/tests/lib/convert.test.ts b/packages/typescript-estree/tests/lib/convert.test.ts index 35a49848e1b..2d56d9fe362 100644 --- a/packages/typescript-estree/tests/lib/convert.test.ts +++ b/packages/typescript-estree/tests/lib/convert.test.ts @@ -1,9 +1,11 @@ import type { TSESTree } from '@typescript-eslint/types'; + import { AST_NODE_TYPES } from '@typescript-eslint/types'; import * as ts from 'typescript'; import type { TSNode } from '../../src'; import type { ConverterOptions } from '../../src/convert'; + import { Converter } from '../../src/convert'; describe('convert', () => { @@ -205,32 +207,32 @@ describe('convert', () => { pos: 0, }; const convertedNode = instance['createNode'](tsNode, { - type: AST_NODE_TYPES.TSAbstractKeyword, - range: [0, 20], loc: { - start: { - line: 10, - column: 20, - }, end: { - line: 15, column: 25, + line: 15, + }, + start: { + column: 20, + line: 10, }, }, + range: [0, 20], + type: AST_NODE_TYPES.TSAbstractKeyword, }); expect(convertedNode).toEqual({ - type: AST_NODE_TYPES.TSAbstractKeyword, - range: [0, 20], loc: { - start: { - line: 10, - column: 20, - }, end: { - line: 15, column: 25, + line: 15, + }, + start: { + column: 20, + line: 10, }, }, + range: [0, 20], + type: AST_NODE_TYPES.TSAbstractKeyword, }); }); }); diff --git a/packages/typescript-estree/tests/lib/createParseSettings.test.ts b/packages/typescript-estree/tests/lib/createParseSettings.test.ts index d4de22b92a0..368a2f40d50 100644 --- a/packages/typescript-estree/tests/lib/createParseSettings.test.ts +++ b/packages/typescript-estree/tests/lib/createParseSettings.test.ts @@ -22,8 +22,8 @@ describe('createParseSettings', () => { process.env.TYPESCRIPT_ESLINT_PROJECT_SERVICE = 'true'; const parseSettings = createParseSettings('', { - projectService: undefined, project: true, + projectService: undefined, }); expect(parseSettings.projectService).toBe(projectService); @@ -43,8 +43,8 @@ describe('createParseSettings', () => { process.env.TYPESCRIPT_ESLINT_PROJECT_SERVICE = 'true'; const parseSettings = createParseSettings('', { - projectService: false, project: true, + projectService: false, }); expect(parseSettings.projectService).toBeUndefined(); diff --git a/packages/typescript-estree/tests/lib/createProjectService.test.ts b/packages/typescript-estree/tests/lib/createProjectService.test.ts index d75b8b5d9a8..fa9684e6237 100644 --- a/packages/typescript-estree/tests/lib/createProjectService.test.ts +++ b/packages/typescript-estree/tests/lib/createProjectService.test.ts @@ -17,6 +17,9 @@ jest.mock('typescript/lib/tsserverlibrary', () => ({ eventHandler: ts.server.ProjectServiceEventHandler | undefined; host: ts.server.ServerHost; logger: ts.server.Logger; + setCompilerOptionsForInferredProjects = + mockSetCompilerOptionsForInferredProjects; + setHostConfiguration = mockSetHostConfiguration; constructor( ...args: ConstructorParameters ) { @@ -29,9 +32,6 @@ jest.mock('typescript/lib/tsserverlibrary', () => ({ } as ts.server.ProjectLoadingStartEvent); } } - setCompilerOptionsForInferredProjects = - mockSetCompilerOptionsForInferredProjects; - setHostConfiguration = mockSetHostConfiguration; }, }, })); @@ -318,11 +318,11 @@ describe('createProjectService', () => { const required = service.host.require(); expect(required).toEqual({ - module: undefined, error: { message: 'TypeScript plugins are not required when using parserOptions.projectService.', }, + module: undefined, }); }); diff --git a/packages/typescript-estree/tests/lib/getParsedConfigFile.test.ts b/packages/typescript-estree/tests/lib/getParsedConfigFile.test.ts index a1aa1a2c04e..ca41058de14 100644 --- a/packages/typescript-estree/tests/lib/getParsedConfigFile.test.ts +++ b/packages/typescript-estree/tests/lib/getParsedConfigFile.test.ts @@ -1,5 +1,4 @@ import path from 'node:path'; - import * as ts from 'typescript'; import { getParsedConfigFile } from '../../src/create-program/getParsedConfigFile'; @@ -7,9 +6,9 @@ import { getParsedConfigFile } from '../../src/create-program/getParsedConfigFil const mockGetParsedCommandLineOfConfigFile = jest.fn(); const mockTsserver: typeof ts = { - sys: {} as ts.System, formatDiagnostics: ts.formatDiagnostics, getParsedCommandLineOfConfigFile: mockGetParsedCommandLineOfConfigFile, + sys: {} as ts.System, // eslint-disable-next-line @typescript-eslint/no-explicit-any } as any; @@ -63,9 +62,9 @@ describe('getParsedConfigFile', () => { file: ts.createSourceFile('./tsconfig.json', '', { languageVersion: ts.ScriptTarget.Latest, }), - start: 0, length: 0, messageText: 'Oh no!', + start: 0, }, ] satisfies ts.Diagnostic[], }); @@ -87,9 +86,9 @@ describe('getParsedConfigFile', () => { file: ts.createSourceFile('./tsconfig.json', '', { languageVersion: ts.ScriptTarget.Latest, }), - start: 0, length: 0, messageText: 'Oh no!', + start: 0, } satisfies ts.Diagnostic); }, ); @@ -100,9 +99,9 @@ describe('getParsedConfigFile', () => { it('uses compiler options when parsing a config file succeeds', () => { const parsedConfigFile = { + errors: [], options: { strict: true }, raw: { compilerOptions: { strict: true } }, - errors: [], }; mockGetParsedCommandLineOfConfigFile.mockReturnValue(parsedConfigFile); expect(getParsedConfigFile(mockTsserver, 'tsconfig.json')).toEqual( diff --git a/packages/typescript-estree/tests/lib/getProjectConfigFiles.test.ts b/packages/typescript-estree/tests/lib/getProjectConfigFiles.test.ts index 4d9bd87fcee..8da31477fdf 100644 --- a/packages/typescript-estree/tests/lib/getProjectConfigFiles.test.ts +++ b/packages/typescript-estree/tests/lib/getProjectConfigFiles.test.ts @@ -72,8 +72,8 @@ describe('getProjectConfigFiles', () => { getProjectConfigFiles( { filePath: './a/b/c/d.ts', - tsconfigRootDir: './a', tsconfigMatchCache, + tsconfigRootDir: './a', }, true, ); @@ -83,8 +83,8 @@ describe('getProjectConfigFiles', () => { const actual = getProjectConfigFiles( { filePath: './a/b/c/e/f.ts', - tsconfigRootDir: './a', tsconfigMatchCache, + tsconfigRootDir: './a', }, true, ); @@ -104,8 +104,8 @@ describe('getProjectConfigFiles', () => { getProjectConfigFiles( { filePath: './a/b/c/d/e.ts', - tsconfigRootDir: './a', tsconfigMatchCache, + tsconfigRootDir: './a', }, true, ); @@ -115,8 +115,8 @@ describe('getProjectConfigFiles', () => { const actual = getProjectConfigFiles( { filePath: './a/b/f/g/h.ts', - tsconfigRootDir: './a', tsconfigMatchCache, + tsconfigRootDir: './a', }, true, ); diff --git a/packages/typescript-estree/tests/lib/parse.project-true.test.ts b/packages/typescript-estree/tests/lib/parse.project-true.test.ts index 991d2b618c3..35abf0baf92 100644 --- a/packages/typescript-estree/tests/lib/parse.project-true.test.ts +++ b/packages/typescript-estree/tests/lib/parse.project-true.test.ts @@ -5,8 +5,8 @@ import * as parser from '../../src'; const PROJECT_DIR = join(__dirname, '../fixtures/projectTrue'); const config = { - tsconfigRootDir: PROJECT_DIR, project: true, + tsconfigRootDir: PROJECT_DIR, } satisfies Partial; describe('parseAndGenerateServices', () => { diff --git a/packages/typescript-estree/tests/lib/parse.test.ts b/packages/typescript-estree/tests/lib/parse.test.ts index 69b60082584..b701bf35d21 100644 --- a/packages/typescript-estree/tests/lib/parse.test.ts +++ b/packages/typescript-estree/tests/lib/parse.test.ts @@ -1,13 +1,14 @@ -import { join, resolve } from 'node:path'; - import type { CacheDurationSeconds } from '@typescript-eslint/types'; +import type * as typescriptModule from 'typescript'; + import debug from 'debug'; import * as fastGlobModule from 'fast-glob'; -import type * as typescriptModule from 'typescript'; +import { join, resolve } from 'node:path'; + +import type { TSESTreeOptions } from '../../src/parser-options'; import * as parser from '../../src'; import * as sharedParserUtilsModule from '../../src/create-program/shared'; -import type { TSESTreeOptions } from '../../src/parser-options'; import { clearGlobResolutionCache } from '../../src/parseSettings/resolveProjectList'; const FIXTURES_DIR = join(__dirname, '../fixtures/simpleProject'); @@ -72,15 +73,15 @@ describe('parseAndGenerateServices', () => { const code = 'var a = true'; const baseConfig: TSESTreeOptions = { comment: true, - tokens: true, - range: true, - loc: true, filePath: 'file.ts', + loc: true, + range: true, + tokens: true, }; const projectConfig: TSESTreeOptions = { ...baseConfig, - tsconfigRootDir: FIXTURES_DIR, project: './tsconfig.json', + tsconfigRootDir: FIXTURES_DIR, }; it('should not impact the use of parse()', () => { @@ -201,8 +202,8 @@ describe('parseAndGenerateServices', () => { const exp = expect(() => { result = parser.parseAndGenerateServices(code, { ...config, - jsx: jsxSetting, filePath: join(FIXTURES_DIR, `file${ext}`), + jsx: jsxSetting, }); }); if (!shouldThrow) { @@ -345,27 +346,27 @@ describe('parseAndGenerateServices', () => { describe('ESM parsing', () => { describe('TLA(Top Level Await)', () => { const config: TSESTreeOptions = { - projectService: false, comment: true, - tokens: true, - range: true, loc: true, + projectService: false, + range: true, + tokens: true, }; const code = 'await(1)'; const testParse = ({ - sourceType, ext, shouldAllowTLA = false, + sourceType, }: { - sourceType?: 'module' | 'script'; - ext: '.js' | '.ts' | '.mjs' | '.mts'; + ext: '.js' | '.mjs' | '.mts' | '.ts'; shouldAllowTLA?: boolean; + sourceType?: 'module' | 'script'; }): void => { const ast = parser.parse(code, { ...config, - sourceType, filePath: `file${ext}`, + sourceType, }); const expressionType = ( ast.body[0] as parser.TSESTree.ExpressionStatement @@ -382,18 +383,18 @@ describe('parseAndGenerateServices', () => { }); }; const testParseAndGenerateServices = ({ - sourceType, ext, shouldAllowTLA = false, + sourceType, }: { - sourceType?: 'module' | 'script'; - ext: '.js' | '.ts' | '.mjs' | '.mts'; + ext: '.js' | '.mjs' | '.mts' | '.ts'; shouldAllowTLA?: boolean; + sourceType?: 'module' | 'script'; }): void => { const result = parser.parseAndGenerateServices(code, { ...config, - sourceType, filePath: `file${ext}`, + sourceType, }); const expressionType = ( result.ast.body[0] as parser.TSESTree.ExpressionStatement @@ -415,15 +416,15 @@ describe('parseAndGenerateServices', () => { testParse({ ext: '.mjs', shouldAllowTLA: true }); testParse({ ext: '.mts', shouldAllowTLA: true }); - testParse({ sourceType: 'module', ext: '.js', shouldAllowTLA: true }); - testParse({ sourceType: 'module', ext: '.ts', shouldAllowTLA: true }); - testParse({ sourceType: 'module', ext: '.mjs', shouldAllowTLA: true }); - testParse({ sourceType: 'module', ext: '.mts', shouldAllowTLA: true }); + testParse({ ext: '.js', shouldAllowTLA: true, sourceType: 'module' }); + testParse({ ext: '.ts', shouldAllowTLA: true, sourceType: 'module' }); + testParse({ ext: '.mjs', shouldAllowTLA: true, sourceType: 'module' }); + testParse({ ext: '.mts', shouldAllowTLA: true, sourceType: 'module' }); - testParse({ sourceType: 'script', ext: '.js' }); - testParse({ sourceType: 'script', ext: '.ts' }); - testParse({ sourceType: 'script', ext: '.mjs' }); - testParse({ sourceType: 'script', ext: '.mts' }); + testParse({ ext: '.js', sourceType: 'script' }); + testParse({ ext: '.ts', sourceType: 'script' }); + testParse({ ext: '.mjs', sourceType: 'script' }); + testParse({ ext: '.mts', sourceType: 'script' }); testParseAndGenerateServices({ ext: '.js' }); testParseAndGenerateServices({ ext: '.ts' }); @@ -431,41 +432,41 @@ describe('parseAndGenerateServices', () => { testParseAndGenerateServices({ ext: '.mts', shouldAllowTLA: true }); testParseAndGenerateServices({ - sourceType: 'module', ext: '.js', shouldAllowTLA: true, + sourceType: 'module', }); testParseAndGenerateServices({ - sourceType: 'module', ext: '.ts', shouldAllowTLA: true, + sourceType: 'module', }); testParseAndGenerateServices({ - sourceType: 'module', ext: '.mjs', shouldAllowTLA: true, + sourceType: 'module', }); testParseAndGenerateServices({ - sourceType: 'module', ext: '.mts', shouldAllowTLA: true, + sourceType: 'module', }); testParseAndGenerateServices({ - sourceType: 'script', ext: '.js', + sourceType: 'script', }); testParseAndGenerateServices({ - sourceType: 'script', ext: '.ts', + sourceType: 'script', }); testParseAndGenerateServices({ - sourceType: 'script', ext: '.mjs', + sourceType: 'script', }); testParseAndGenerateServices({ - sourceType: 'script', ext: '.mts', + sourceType: 'script', }); }); }); @@ -477,9 +478,9 @@ describe('parseAndGenerateServices', () => { const config: TSESTreeOptions = { comment: true, disallowAutomaticSingleRunInference: true, - tokens: true, - range: true, loc: true, + range: true, + tokens: true, tsconfigRootDir: PROJECT_DIR, }; const testParse = @@ -667,11 +668,11 @@ describe('parseAndGenerateServices', () => { const config: TSESTreeOptions = { comment: true, disallowAutomaticSingleRunInference: true, - tokens: true, - range: true, loc: true, - tsconfigRootDir: PROJECT_DIR, project: ['./**/tsconfig.json', './**/tsconfig.extra.json'], + range: true, + tokens: true, + tsconfigRootDir: PROJECT_DIR, }; const testParse = (filePath: string) => (): void => { try { @@ -777,11 +778,11 @@ describe('parseAndGenerateServices', () => { const config: TSESTreeOptions = { comment: true, disallowAutomaticSingleRunInference: true, - tokens: true, - range: true, loc: true, - tsconfigRootDir: PROJECT_DIR, project: './**/tsconfig.json', + range: true, + tokens: true, + tsconfigRootDir: PROJECT_DIR, }; const testParse = @@ -792,8 +793,8 @@ describe('parseAndGenerateServices', () => { (): void => { parser.parseAndGenerateServices(code, { ...config, - projectFolderIgnoreList, filePath: join(PROJECT_DIR, filePath, './file.ts'), + projectFolderIgnoreList, }); }; @@ -826,8 +827,8 @@ describe('parseAndGenerateServices', () => { }, disallowAutomaticSingleRunInference: true, filePath: join(FIXTURES_DIR, 'file.ts'), - tsconfigRootDir: FIXTURES_DIR, project, + tsconfigRootDir: FIXTURES_DIR, }); } diff --git a/packages/typescript-estree/tests/lib/persistentParse.test.ts b/packages/typescript-estree/tests/lib/persistentParse.test.ts index 82bc44abdf9..6a3338cf9be 100644 --- a/packages/typescript-estree/tests/lib/persistentParse.test.ts +++ b/packages/typescript-estree/tests/lib/persistentParse.test.ts @@ -1,6 +1,5 @@ import fs from 'node:fs'; import path from 'node:path'; - import tmp from 'tmp'; import { clearCaches } from '../../src/clear-caches'; @@ -11,10 +10,10 @@ import { } from '../../src/parser'; const CONTENTS = { - foo: 'console.log("foo")', bar: 'console.log("bar")', - 'baz/bar': 'console.log("baz bar")', 'bat/baz/bar': 'console.log("bat/baz/bar")', + 'baz/bar': 'console.log("baz bar")', + foo: 'console.log("foo")', number: 'const foo = 1;', object: '(() => { })();', string: 'let a: "a" | "b";', @@ -81,11 +80,11 @@ function parseFile( ): void { parseAndGenerateServices(CONTENTS[filename], { disallowAutomaticSingleRunInference: true, - project: './tsconfig.json', - tsconfigRootDir: ignoreTsconfigRootDir ? undefined : tmpDir, filePath: relative ? path.join('src', `${filename}.ts`) : path.join(tmpDir, 'src', `${filename}.ts`), + project: './tsconfig.json', + tsconfigRootDir: ignoreTsconfigRootDir ? undefined : tmpDir, }); } @@ -243,12 +242,12 @@ function baseTests( describe('persistent parse', () => { describe('includes not ending in a slash', () => { const tsConfigExcludeBar = { - include: ['src'], exclude: ['./src/bar.ts'], + include: ['src'], }; const tsConfigIncludeAll = { - include: ['src'], exclude: [], + include: ['src'], }; baseTests(tsConfigExcludeBar, tsConfigIncludeAll); @@ -260,12 +259,12 @@ describe('persistent parse', () => { */ describe('includes ending in a slash', () => { const tsConfigExcludeBar = { - include: ['src/'], exclude: ['./src/bar.ts'], + include: ['src/'], }; const tsConfigIncludeAll = { - include: ['src/'], exclude: [], + include: ['src/'], }; baseTests(tsConfigExcludeBar, tsConfigIncludeAll); @@ -322,8 +321,8 @@ describe('persistent parse', () => { */ describe('tsconfig with overlapping globs', () => { const tsConfigExcludeBar = { - include: ['./*', './**/*', './src/**/*'], exclude: ['./src/bar.ts'], + include: ['./*', './**/*', './src/**/*'], }; const tsConfigIncludeAll = { include: ['./*', './**/*', './src/**/*'], diff --git a/packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts b/packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts index ea455ab20ac..0df2e12b6a9 100644 --- a/packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts +++ b/packages/typescript-estree/tests/lib/semanticInfo-singleRun.test.ts @@ -1,6 +1,5 @@ -import * as path from 'node:path'; - import * as glob from 'glob'; +import * as path from 'node:path'; import { createProgramFromConfigFile as createProgramFromConfigFileOriginal } from '../../src/create-program/useProvidedPrograms'; import { @@ -10,21 +9,21 @@ import { } from '../../src/parser'; const mockProgram = { + getCompilerOptions(): unknown { + return {}; + }, getSourceFile(): void { return; }, getTypeChecker(): void { return; }, - getCompilerOptions(): unknown { - return {}; - }, }; jest.mock('../../src/ast-converter', () => { return { astConverter(): unknown { - return { estree: {}, astMaps: {} }; + return { astMaps: {}, estree: {} }; }, }; }); @@ -86,11 +85,11 @@ const code = 'const foo = 5;'; // File will not be found in the first Program, but will be in the second const tsconfigs = ['./non-matching-tsconfig.json', './tsconfig.json']; const options = { + allowAutomaticSingleRunInference: true, filePath: testFiles[0], - tsconfigRootDir: path.join(process.cwd(), FIXTURES_DIR), loggerFn: false, project: tsconfigs, - allowAutomaticSingleRunInference: true, + tsconfigRootDir: path.join(process.cwd(), FIXTURES_DIR), } as const; const resolvedProject = (p: string): string => diff --git a/packages/typescript-estree/tests/lib/semanticInfo.test.ts b/packages/typescript-estree/tests/lib/semanticInfo.test.ts index a5378e52bc9..135b4b9afc0 100644 --- a/packages/typescript-estree/tests/lib/semanticInfo.test.ts +++ b/packages/typescript-estree/tests/lib/semanticInfo.test.ts @@ -1,15 +1,15 @@ +import * as glob from 'glob'; import * as fs from 'node:fs'; import * as path from 'node:path'; - -import * as glob from 'glob'; import * as ts from 'typescript'; -import { clearCaches } from '../../src'; -import { createProgramFromConfigFile as createProgram } from '../../src/create-program/useProvidedPrograms'; import type { ParseAndGenerateServicesResult } from '../../src/parser'; -import { parseAndGenerateServices } from '../../src/parser'; import type { TSESTreeOptions } from '../../src/parser-options'; import type { TSESTree } from '../../src/ts-estree'; + +import { clearCaches } from '../../src'; +import { createProgramFromConfigFile as createProgram } from '../../src/create-program/useProvidedPrograms'; +import { parseAndGenerateServices } from '../../src/parser'; import { expectToHaveParserServices } from '../test-utils/expectToHaveParserServices'; import { createSnapshotTestBlock, @@ -22,19 +22,19 @@ const testFiles = glob.sync(`**/*.src.ts`, { cwd: FIXTURES_DIR, }); -function createOptions(fileName: string): TSESTreeOptions & { cwd?: string } { +function createOptions(fileName: string): { cwd?: string } & TSESTreeOptions { return { + comment: true, disallowAutomaticSingleRunInference: true, + errorOnUnknownASTType: true, + filePath: fileName, + jsx: false, loc: true, + loggerFn: false, + project: `./tsconfig.json`, range: true, tokens: true, - comment: true, - jsx: false, - errorOnUnknownASTType: true, - filePath: fileName, tsconfigRootDir: path.join(process.cwd(), FIXTURES_DIR), - project: `./tsconfig.json`, - loggerFn: false, }; } @@ -223,8 +223,8 @@ describe('semanticInfo', () => { `const x = [parseInt("5")];`, { ...createOptions(''), - project: undefined, preserveNodeMaps: true, + project: undefined, }, ); @@ -338,8 +338,8 @@ describe('semanticInfo', () => { const options = createOptions(filename); const optionsWithProjectTrue = { ...options, - project: true, programs: undefined, + project: true, }; expect(() => parseAndGenerateServices('const foo = 5;', optionsWithProjectTrue), diff --git a/packages/typescript-estree/tests/lib/useProgramFromProjectService.test.ts b/packages/typescript-estree/tests/lib/useProgramFromProjectService.test.ts index 8ff667fac58..1b0ffd3ed40 100644 --- a/packages/typescript-estree/tests/lib/useProgramFromProjectService.test.ts +++ b/packages/typescript-estree/tests/lib/useProgramFromProjectService.test.ts @@ -1,6 +1,5 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type -- Fancy mocks */ import path from 'node:path'; - import * as ts from 'typescript'; import type { @@ -8,6 +7,7 @@ import type { TypeScriptProjectService, } from '../../src/create-program/createProjectService'; import type { ParseSettings } from '../../src/parseSettings'; + import { useProgramFromProjectService } from '../../src/useProgramFromProjectService'; const mockCreateNoProgram = jest.fn(); @@ -50,17 +50,17 @@ function createMockProjectService() { }; return { - service: service as typeof service & TypeScriptProjectService, openClientFile, reloadProjects, + service: service as typeof service & TypeScriptProjectService, }; } const mockFileName = 'camelCaseFile.ts'; const mockParseSettings = { - filePath: `path/PascalCaseDirectory/${mockFileName}`, extraFileExtensions: [] as readonly string[], + filePath: `path/PascalCaseDirectory/${mockFileName}`, singleRun: false, tsconfigRootDir: currentDirectory, } as ParseSettings; @@ -70,8 +70,8 @@ const createProjectServiceSettings = < >( settings: T, ) => ({ - maximumDefaultProjectFileMatchCount: 8, lastReloadTimestamp: 0, + maximumDefaultProjectFileMatchCount: 8, ...settings, }); @@ -615,8 +615,8 @@ If you absolutely need more files included, set parserOptions.projectService.max }), { ...mockParseSettings, - filePath, extraFileExtensions: ['.svelte'], + filePath, }, true, new Set(), diff --git a/packages/typescript-estree/tests/lib/withoutProjectParserOptions.test.ts b/packages/typescript-estree/tests/lib/withoutProjectParserOptions.test.ts index bf1b111544d..2b4a16fa78d 100644 --- a/packages/typescript-estree/tests/lib/withoutProjectParserOptions.test.ts +++ b/packages/typescript-estree/tests/lib/withoutProjectParserOptions.test.ts @@ -1,4 +1,5 @@ import type { TSESTreeOptions } from '../../src'; + import { withoutProjectParserOptions } from '../../src'; describe('withoutProjectParserOptions', () => { @@ -20,9 +21,9 @@ describe('withoutProjectParserOptions', () => { it('allows an alternate type extending from TSESTreeOptions', () => { const without = withoutProjectParserOptions({ comment: true, + other: true, project: true, projectService: true, - other: true, }); expect(without).toEqual({ diff --git a/packages/typescript-estree/tests/test-utils/test-utils.ts b/packages/typescript-estree/tests/test-utils/test-utils.ts index b307e755e88..41951dc07ff 100644 --- a/packages/typescript-estree/tests/test-utils/test-utils.ts +++ b/packages/typescript-estree/tests/test-utils/test-utils.ts @@ -3,7 +3,8 @@ import type { TSESTree, TSESTreeOptions, } from '../../src'; -import { parse as parserParse, parseAndGenerateServices } from '../../src'; + +import { parseAndGenerateServices, parse as parserParse } from '../../src'; export function parseCodeAndGenerateServices( code: string, diff --git a/packages/typescript-estree/typings/typescript.d.ts b/packages/typescript-estree/typings/typescript.d.ts index 039c9a06259..7a665b57abb 100644 --- a/packages/typescript-estree/typings/typescript.d.ts +++ b/packages/typescript-estree/typings/typescript.d.ts @@ -20,18 +20,18 @@ declare module 'typescript' { readonly initializer?: Expression | undefined; } interface PropertyAssignment { - /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ - readonly questionToken?: QuestionToken | undefined; /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ readonly exclamationToken?: ExclamationToken | undefined; + /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ + readonly questionToken?: QuestionToken | undefined; } interface ShorthandPropertyAssignment { + /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ + readonly exclamationToken?: ExclamationToken | undefined; /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ readonly modifiers?: NodeArray | undefined; /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ readonly questionToken?: QuestionToken | undefined; - /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ - readonly exclamationToken?: ExclamationToken | undefined; } interface FunctionTypeNode { /** @deprecated removed in 5.0 but we want to keep it for backwards compatibility checks! */ diff --git a/packages/utils/src/ast-utils/eslint-utils/PatternMatcher.ts b/packages/utils/src/ast-utils/eslint-utils/PatternMatcher.ts index 34431ded315..3f5f4c841a5 100644 --- a/packages/utils/src/ast-utils/eslint-utils/PatternMatcher.ts +++ b/packages/utils/src/ast-utils/eslint-utils/PatternMatcher.ts @@ -25,7 +25,7 @@ interface PatternMatcher { */ [Symbol.replace]( str: string, - replacer: ((...strs: string[]) => string) | string, + replacer: string | ((...strs: string[]) => string), ): string; /** diff --git a/packages/utils/src/ast-utils/eslint-utils/scopeAnalysis.ts b/packages/utils/src/ast-utils/eslint-utils/scopeAnalysis.ts index 43199ce0553..563c51c7782 100644 --- a/packages/utils/src/ast-utils/eslint-utils/scopeAnalysis.ts +++ b/packages/utils/src/ast-utils/eslint-utils/scopeAnalysis.ts @@ -10,7 +10,7 @@ import type { TSESTree } from '../../ts-estree'; */ const findVariable = eslintUtils.findVariable as ( initialScope: TSESLint.Scope.Scope, - nameOrNode: TSESTree.Identifier | string, + nameOrNode: string | TSESTree.Identifier, ) => TSESLint.Scope.Variable | null; /** diff --git a/packages/utils/src/json-schema.ts b/packages/utils/src/json-schema.ts index 83ba0fd3679..1c94883578c 100644 --- a/packages/utils/src/json-schema.ts +++ b/packages/utils/src/json-schema.ts @@ -146,7 +146,7 @@ interface JSONSchema4Base { * * @see https://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.26 */ - extends?: string[] | string | undefined; + extends?: string | string[] | undefined; id?: string | undefined; @@ -167,7 +167,7 @@ interface JSONSchema4Base { * * @see https://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.7 */ - required?: string[] | boolean | undefined; + required?: boolean | string[] | undefined; /** * This attribute is a string that provides a short description of the @@ -238,7 +238,7 @@ export interface JSONSchema4ObjectSchema extends JSONSchema4Base { * * @see https://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.4 */ - additionalProperties?: JSONSchema4 | boolean | undefined; + additionalProperties?: boolean | JSONSchema4 | undefined; /** * The `dependencies` keyword conditionally applies a sub-schema when a given @@ -302,7 +302,7 @@ export interface JSONSchema4ArraySchema extends JSONSchema4Base { * * @see https://tools.ietf.org/html/draft-zyp-json-schema-03#section-5.6 */ - additionalItems?: JSONSchema4 | boolean | undefined; + additionalItems?: boolean | JSONSchema4 | undefined; /** * This attribute defines the allowed items in an instance array, and diff --git a/packages/utils/src/ts-eslint/Config.ts b/packages/utils/src/ts-eslint/Config.ts index 924214bf86c..dad591e03ef 100644 --- a/packages/utils/src/ts-eslint/Config.ts +++ b/packages/utils/src/ts-eslint/Config.ts @@ -73,7 +73,7 @@ export namespace ClassicConfig { /** * The path to other config files or the package name of shareable configs. */ - extends?: string[] | string; + extends?: string | string[]; /** * The global variable settings. */ @@ -117,15 +117,15 @@ export namespace ClassicConfig { } export interface ConfigOverride extends BaseConfig { - excludedFiles?: string[] | string; - files: string[] | string; + excludedFiles?: string | string[]; + files: string | string[]; } export interface Config extends BaseConfig { /** * The glob patterns that ignore to lint. */ - ignorePatterns?: string[] | string; + ignorePatterns?: string | string[]; /** * The root flag. */ @@ -197,9 +197,9 @@ export namespace FlatConfig { * @default "off" */ reportUnusedDisableDirectives?: + | boolean | SharedConfig.Severity - | SharedConfig.SeverityString - | boolean; + | SharedConfig.SeverityString; } export interface LanguageOptions { @@ -250,8 +250,8 @@ export namespace FlatConfig { * If not specified, the configuration object applies to all files matched by any other configuration object. */ files?: ( - | string[] // yes, a single layer of array nesting is supported | string + | string[] // yes, a single layer of array nesting is supported )[]; /** * An array of glob patterns indicating the files that the configuration object should not apply to. @@ -280,7 +280,7 @@ export namespace FlatConfig { * a string indicating the name of a processor inside of a plugin * (i.e., `"pluginName/processorName"`). */ - processor?: Processor | string; + processor?: string | Processor; /** * An object containing the configured rules. * When `files` or `ignores` are specified, these rule configurations are only available to the matching files. diff --git a/packages/utils/src/ts-eslint/Linter.ts b/packages/utils/src/ts-eslint/Linter.ts index b9bee0ee5d9..df67ddb714d 100644 --- a/packages/utils/src/ts-eslint/Linter.ts +++ b/packages/utils/src/ts-eslint/Linter.ts @@ -84,9 +84,9 @@ declare class LinterBase { * @returns The results as an array of messages or an empty array if no messages. */ verify( - textOrSourceCode: SourceCode | string, + textOrSourceCode: string | SourceCode, config: Linter.ConfigType, - filenameOrOptions?: Linter.VerifyOptions | string, + filenameOrOptions?: string | Linter.VerifyOptions, ): Linter.LintMessage[]; //////////////////// @@ -183,7 +183,7 @@ namespace Linter { /** * Adds reported errors for unused `eslint-disable` directives. */ - reportUnusedDisableDirectives?: SeverityString | boolean; + reportUnusedDisableDirectives?: boolean | SeverityString; } export interface FixOptions extends VerifyOptions { diff --git a/packages/utils/src/ts-eslint/Processor.ts b/packages/utils/src/ts-eslint/Processor.ts index 5af746af400..631e575690d 100644 --- a/packages/utils/src/ts-eslint/Processor.ts +++ b/packages/utils/src/ts-eslint/Processor.ts @@ -17,7 +17,7 @@ export namespace Processor { export type PreProcess = ( text: string, filename: string, - ) => ({ filename: string; text: string } | string)[]; + ) => (string | { filename: string; text: string })[]; export type PostProcess = ( messagesList: Linter.LintMessage[][], diff --git a/packages/utils/src/ts-eslint/RuleTester.ts b/packages/utils/src/ts-eslint/RuleTester.ts index a9887ad835d..4d0a8ca2b12 100644 --- a/packages/utils/src/ts-eslint/RuleTester.ts +++ b/packages/utils/src/ts-eslint/RuleTester.ts @@ -94,7 +94,7 @@ interface InvalidTestCase< /** * The expected code after autofixes are applied. If set to `null`, the test runner will assert that no autofix is suggested. */ - readonly output?: string[] | string | null; + readonly output?: string | string[] | null; } /** @@ -156,7 +156,7 @@ interface RunTests< > { // RuleTester.run also accepts strings for valid cases readonly invalid: readonly InvalidTestCase[]; - readonly valid: readonly (ValidTestCase | string)[]; + readonly valid: readonly (string | ValidTestCase)[]; } /** diff --git a/packages/utils/src/ts-eslint/SourceCode.ts b/packages/utils/src/ts-eslint/SourceCode.ts index b5caea851b9..eefc36696be 100644 --- a/packages/utils/src/ts-eslint/SourceCode.ts +++ b/packages/utils/src/ts-eslint/SourceCode.ts @@ -184,7 +184,7 @@ declare class TokenStore { */ getTokensAfter( node: TSESTree.Node | TSESTree.Token, - options?: T | number, + options?: number | T, ): SourceCode.ReturnTypeFromOptions[]; /** * Gets the `count` tokens that precedes a given node or token. @@ -193,7 +193,7 @@ declare class TokenStore { */ getTokensBefore( node: TSESTree.Node | TSESTree.Token, - options?: T | number, + options?: number | T, ): SourceCode.ReturnTypeFromOptions[]; /** * Gets all of the tokens between two non-overlapping nodes. @@ -205,7 +205,7 @@ declare class TokenStore { getTokensBetween( left: TSESTree.Node | TSESTree.Token, right: TSESTree.Node | TSESTree.Token, - options?: T | number, + options?: number | T, ): SourceCode.ReturnTypeFromOptions[]; } @@ -415,6 +415,7 @@ namespace SourceCode { >; export type CursorWithSkipOptions = + | number | { /** * The predicate function to choose tokens. @@ -429,10 +430,10 @@ namespace SourceCode { */ skip?: number; } - | FilterPredicate - | number; + | FilterPredicate; export type CursorWithCountOptions = + | number | { /** * The maximum count of tokens the cursor iterates. @@ -447,8 +448,7 @@ namespace SourceCode { */ includeComments?: boolean; } - | FilterPredicate - | number; + | FilterPredicate; } class SourceCode extends (ESLintSourceCode as typeof SourceCodeBase) {} diff --git a/packages/utils/src/ts-eslint/eslint/ESLintShared.ts b/packages/utils/src/ts-eslint/eslint/ESLintShared.ts index aad9b71e558..db6e5b4de20 100644 --- a/packages/utils/src/ts-eslint/eslint/ESLintShared.ts +++ b/packages/utils/src/ts-eslint/eslint/ESLintShared.ts @@ -42,7 +42,7 @@ export declare class ESLintBase< * @param patterns The lint target files. This can contain any of file paths, directory paths, and glob patterns. * @returns The promise that will be fulfilled with an array of LintResult objects. */ - lintFiles(patterns: string[] | string): Promise; + lintFiles(patterns: string | string[]): Promise; /** * This method lints the given source code text and then returns the results. @@ -150,7 +150,7 @@ export interface ESLintOptions { * lint messages for which the function returned true. * @default false */ - fix?: ((message: LintMessage) => boolean) | boolean; + fix?: boolean | ((message: LintMessage) => boolean); /** * The types of the rules that the `eslint.lintFiles()` and `eslint.lintText()` methods use for autofix. * @default null @@ -404,5 +404,5 @@ export interface Formatter { * The method to convert the LintResult objects to text. * Promise return supported since 8.4.0 */ - format(results: LintResult[]): Promise | string; + format(results: LintResult[]): string | Promise; } diff --git a/packages/website/src/hooks/useBool.ts b/packages/website/src/hooks/useBool.ts index 8ef2e086f42..39bec4e5fd4 100644 --- a/packages/website/src/hooks/useBool.ts +++ b/packages/website/src/hooks/useBool.ts @@ -3,7 +3,7 @@ import type { Dispatch, SetStateAction } from 'react'; import { useCallback, useState } from 'react'; export function useBool( - initialState: (() => boolean) | boolean, + initialState: boolean | (() => boolean), ): [boolean, () => void, Dispatch>] { const [value, setValue] = useState(initialState);