From 6464df29e63c22013fe9028a8a61157af1c905a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Mon, 15 Apr 2024 10:56:27 -0400 Subject: [PATCH 01/53] feat(typescript-estree): remove slow deprecated and isolated programs (#8834) * feat(typescript-estree): remove slow deprecated and isolated programs * Update packages/typescript-estree/src/create-program/createProjectProgram.ts Co-authored-by: Brad Zacher --------- Co-authored-by: Brad Zacher --- docs/packages/TypeScript_ESTree.mdx | 6 -- .../create-program/createDefaultProgram.ts | 72 ------------------- .../create-program/createProjectProgram.ts | 7 +- .../src/parseSettings/createParseSettings.ts | 3 - .../src/parseSettings/index.ts | 7 -- .../typescript-estree/src/parser-options.ts | 6 -- packages/typescript-estree/src/parser.ts | 17 +---- .../tests/lib/semanticInfo.test.ts | 9 --- .../website/src/components/linter/config.ts | 1 - 9 files changed, 4 insertions(+), 124 deletions(-) delete mode 100644 packages/typescript-estree/src/create-program/createDefaultProgram.ts diff --git a/docs/packages/TypeScript_ESTree.mdx b/docs/packages/TypeScript_ESTree.mdx index 6454920b23c3..a23421b248aa 100644 --- a/docs/packages/TypeScript_ESTree.mdx +++ b/docs/packages/TypeScript_ESTree.mdx @@ -235,12 +235,6 @@ interface ParseAndGenerateServicesOptions extends ParseOptions { */ programs?: Program[]; - /** - * @deprecated - this flag will be removed in the next major. - * Do not rely on the behavior provided by this flag. - */ - DEPRECATED__createDefaultProgram?: boolean; - /** * ESLint (and therefore typescript-eslint) is used in both "single run"/one-time contexts, * such as an ESLint CLI invocation, and long-running sessions (such as continuous feedback diff --git a/packages/typescript-estree/src/create-program/createDefaultProgram.ts b/packages/typescript-estree/src/create-program/createDefaultProgram.ts deleted file mode 100644 index b42ec76c678b..000000000000 --- a/packages/typescript-estree/src/create-program/createDefaultProgram.ts +++ /dev/null @@ -1,72 +0,0 @@ -import debug from 'debug'; -import path from 'path'; -import * as ts from 'typescript'; - -import type { ParseSettings } from '../parseSettings'; -import type { ASTAndDefiniteProgram } from './shared'; -import { createDefaultCompilerOptionsFromExtra } from './shared'; - -const log = debug('typescript-eslint:typescript-estree:createDefaultProgram'); - -/** - * @param parseSettings Internal settings for parsing the file - * @returns If found, returns the source file corresponding to the code and the containing program - * @deprecated - * This is a legacy option that comes with severe performance penalties. - * Please do not use it. - */ -function createDefaultProgram( - parseSettings: ParseSettings, -): ASTAndDefiniteProgram | undefined { - log( - 'Getting default program for: %s', - parseSettings.filePath || 'unnamed file', - ); - - if (parseSettings.projects.length !== 1) { - return undefined; - } - - const tsconfigPath = parseSettings.projects[0]; - - const commandLine = ts.getParsedCommandLineOfConfigFile( - tsconfigPath, - createDefaultCompilerOptionsFromExtra(parseSettings), - { - ...ts.sys, - // TODO: file issue on TypeScript to suggest making optional? - // eslint-disable-next-line @typescript-eslint/no-empty-function - onUnRecoverableConfigFileDiagnostic: () => {}, - }, - ); - - if (!commandLine) { - return undefined; - } - - const compilerHost = ts.createCompilerHost( - commandLine.options, - /* setParentNodes */ true, - ); - - const oldReadFile = compilerHost.readFile; - compilerHost.readFile = (fileName: string): string | undefined => - path.normalize(fileName) === path.normalize(parseSettings.filePath) - ? parseSettings.codeFullText - : oldReadFile(fileName); - - const program = ts.createProgram( - [parseSettings.filePath], - { - ...commandLine.options, - jsDocParsingMode: parseSettings.jsDocParsingMode, - }, - compilerHost, - ); - const ast = program.getSourceFile(parseSettings.filePath); - - return ast && { ast, program }; -} - -// eslint-disable-next-line deprecation/deprecation -- will be cleaned up with the next major -export { createDefaultProgram }; diff --git a/packages/typescript-estree/src/create-program/createProjectProgram.ts b/packages/typescript-estree/src/create-program/createProjectProgram.ts index a58097e3cd73..280e48daff2e 100644 --- a/packages/typescript-estree/src/create-program/createProjectProgram.ts +++ b/packages/typescript-estree/src/create-program/createProjectProgram.ts @@ -28,16 +28,15 @@ const DEFAULT_EXTRA_FILE_EXTENSIONS = [ function createProjectProgram( parseSettings: ParseSettings, programsForProjects: readonly ts.Program[], -): ASTAndDefiniteProgram | undefined { +): ASTAndDefiniteProgram { log('Creating project program for: %s', parseSettings.filePath); const astAndProgram = firstDefined(programsForProjects, currentProgram => getAstFromProgram(currentProgram, parseSettings.filePath), ); - // The file was either matched within the tsconfig, or we allow creating a default program - // eslint-disable-next-line deprecation/deprecation -- will be cleaned up with the next major - if (astAndProgram || parseSettings.DEPRECATED__createDefaultProgram) { + // The file was matched within the tsconfig + if (astAndProgram) { return astAndProgram; } diff --git a/packages/typescript-estree/src/parseSettings/createParseSettings.ts b/packages/typescript-estree/src/parseSettings/createParseSettings.ts index 49d048391d8f..210da9bcb042 100644 --- a/packages/typescript-estree/src/parseSettings/createParseSettings.ts +++ b/packages/typescript-estree/src/parseSettings/createParseSettings.ts @@ -68,9 +68,6 @@ export function createParseSettings( codeFullText, comment: options.comment === true, comments: [], - DEPRECATED__createDefaultProgram: - // eslint-disable-next-line deprecation/deprecation -- will be cleaned up with the next major - options.DEPRECATED__createDefaultProgram === true, debugLevel: options.debugLevel === true ? new Set(['typescript-eslint']) diff --git a/packages/typescript-estree/src/parseSettings/index.ts b/packages/typescript-estree/src/parseSettings/index.ts index 1df275901066..65e197ca70af 100644 --- a/packages/typescript-estree/src/parseSettings/index.ts +++ b/packages/typescript-estree/src/parseSettings/index.ts @@ -47,13 +47,6 @@ export interface MutableParseSettings { */ comments: TSESTree.Comment[]; - /** - * @deprecated - * This is a legacy option that comes with severe performance penalties. - * Please do not use it. - */ - DEPRECATED__createDefaultProgram: boolean; - /** * Which debug areas should be logged. */ diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index b345dd72f030..b301b5ffbd37 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -197,12 +197,6 @@ interface ParseAndGenerateServicesOptions extends ParseOptions { */ programs?: ts.Program[] | null; - /** - * @deprecated - this flag will be removed in the next major. - * Do not rely on the behavior provided by this flag. - */ - DEPRECATED__createDefaultProgram?: boolean; - /** * ESLint (and therefore typescript-eslint) is used in both "single run"/one-time contexts, * such as an ESLint CLI invocation, and long-running sessions (such as continuous feedback diff --git a/packages/typescript-estree/src/parser.ts b/packages/typescript-estree/src/parser.ts index ffa0c4212295..4c4b7f61a848 100644 --- a/packages/typescript-estree/src/parser.ts +++ b/packages/typescript-estree/src/parser.ts @@ -3,7 +3,6 @@ import type * as ts from 'typescript'; import { astConverter } from './ast-converter'; import { convertError } from './convert'; -import { createDefaultProgram } from './create-program/createDefaultProgram'; import { createIsolatedProgram } from './create-program/createIsolatedProgram'; import { createProjectProgram } from './create-program/createProjectProgram'; import { @@ -76,24 +75,10 @@ function getProgramAndAST( return createNoProgram(parseSettings); } - const fromProjectProgram = createProjectProgram( + return createProjectProgram( parseSettings, getWatchProgramsForProjects(parseSettings), ); - if (fromProjectProgram) { - return fromProjectProgram; - } - - // eslint-disable-next-line deprecation/deprecation -- will be cleaned up with the next major - if (parseSettings.DEPRECATED__createDefaultProgram) { - // eslint-disable-next-line deprecation/deprecation -- will be cleaned up with the next major - const fromDefaultProgram = createDefaultProgram(parseSettings); - if (fromDefaultProgram) { - return fromDefaultProgram; - } - } - - return createIsolatedProgram(parseSettings); } // eslint-disable-next-line @typescript-eslint/no-empty-interface diff --git a/packages/typescript-estree/tests/lib/semanticInfo.test.ts b/packages/typescript-estree/tests/lib/semanticInfo.test.ts index f5f78934a880..eac23a5deeab 100644 --- a/packages/typescript-estree/tests/lib/semanticInfo.test.ts +++ b/packages/typescript-estree/tests/lib/semanticInfo.test.ts @@ -302,15 +302,6 @@ describe('semanticInfo', () => { }); } - it('default program produced with option', () => { - const parseResult = parseCodeAndGenerateServices('var foo = 5;', { - ...createOptions(''), - DEPRECATED__createDefaultProgram: true, - }); - - expectToHaveParserServices(parseResult.services); - }); - it('empty programs array should throw', () => { const fileName = path.resolve(FIXTURES_DIR, 'isolated-file.src.ts'); const badConfig = createOptions(fileName); diff --git a/packages/website/src/components/linter/config.ts b/packages/website/src/components/linter/config.ts index 8262487369c1..fea3cf897b16 100644 --- a/packages/website/src/components/linter/config.ts +++ b/packages/website/src/components/linter/config.ts @@ -10,7 +10,6 @@ export const defaultParseSettings: ParseSettings = { comment: true, comments: [], debugLevel: new Set(), - DEPRECATED__createDefaultProgram: false, errorOnTypeScriptSyntacticAndSemanticIssues: false, errorOnUnknownASTType: false, EXPERIMENTAL_projectService: undefined, From b8e885326c95f7c8f81d0e686a3364ab8ebc0a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Mon, 15 Apr 2024 11:20:16 -0400 Subject: [PATCH 02/53] fix(typescript-estree): add TSEnumBody node for TSEnumDeclaration body (#8920) * fix(typescript-estree): add TSEnumBody node for TSEnumDeclaration body * Fixed up tests and their snapshots * More about enums * Indent too * Update packages/ast-spec/src/special/TSEnumBody/spec.ts Co-authored-by: Brad Zacher * parent types touchups * Update packages/visitor-keys/src/visitor-keys.ts Co-authored-by: Brad Zacher --------- Co-authored-by: Brad Zacher --- packages/ast-spec/src/ast-node-types.ts | 1 + .../enum/snapshots/1-TSESTree-AST.shot | 11 +- .../enum/snapshots/5-AST-Alignment-AST.shot | 12 +- .../const/snapshots/1-TSESTree-AST.shot | 11 +- .../const/snapshots/5-AST-Alignment-AST.shot | 12 +- .../declare/snapshots/1-TSESTree-AST.shot | 11 +- .../snapshots/5-AST-Alignment-AST.shot | 12 +- .../empty/snapshots/1-TSESTree-AST.shot | 11 +- .../empty/snapshots/5-AST-Alignment-AST.shot | 12 +- .../snapshots/1-TSESTree-AST.shot | 57 ++--- .../snapshots/5-AST-Alignment-AST.shot | 86 +++++--- .../src/declaration/TSEnumDeclaration/spec.ts | 6 + .../ast-spec/src/element/TSEnumMember/spec.ts | 2 + .../const-enum/snapshots/1-TSESTree-AST.shot | 115 +++++----- .../snapshots/5-AST-Alignment-AST.shot | 159 +++++++++----- .../snapshots/1-TSESTree-AST.shot | 115 +++++----- .../snapshots/5-AST-Alignment-AST.shot | 160 +++++++++----- .../snapshots/1-TSESTree-AST.shot | 115 +++++----- .../snapshots/5-AST-Alignment-AST.shot | 159 +++++++++----- .../snapshots/1-TSESTree-AST.shot | 115 +++++----- .../snapshots/5-AST-Alignment-AST.shot | 158 +++++++++----- .../enum/snapshots/1-TSESTree-AST.shot | 93 ++++---- .../enum/snapshots/5-AST-Alignment-AST.shot | 126 +++++++---- .../ast-spec/src/special/TSEnumBody/spec.ts | 10 + packages/ast-spec/src/special/spec.ts | 1 + packages/ast-spec/src/unions/Node.ts | 2 + .../src/rules/prefer-ast-types-enum.ts | 3 +- .../eslint-plugin/src/rules/brace-style.ts | 2 +- .../eslint-plugin/src/rules/comma-dangle.ts | 2 +- packages/eslint-plugin/src/rules/indent.ts | 5 +- .../src/rules/no-duplicate-enum-values.ts | 2 +- .../eslint-plugin/src/rules/no-mixed-enums.ts | 10 +- .../src/rules/prefer-enum-initializers.ts | 2 +- .../tests/rules/indent/indent.test.ts | 101 --------- .../tests/rules/lines-around-comment.test.ts | 202 ------------------ .../src/referencer/Referencer.ts | 2 +- packages/typescript-estree/src/convert.ts | 66 +++++- .../src/ts-estree/estree-to-ts-node-types.ts | 1 + packages/visitor-keys/src/visitor-keys.ts | 3 +- 39 files changed, 1080 insertions(+), 893 deletions(-) create mode 100644 packages/ast-spec/src/special/TSEnumBody/spec.ts diff --git a/packages/ast-spec/src/ast-node-types.ts b/packages/ast-spec/src/ast-node-types.ts index 7a637489eb0d..c1f8d7b4cc36 100644 --- a/packages/ast-spec/src/ast-node-types.ts +++ b/packages/ast-spec/src/ast-node-types.ts @@ -110,6 +110,7 @@ export enum AST_NODE_TYPES { TSDeclareKeyword = 'TSDeclareKeyword', TSEmptyBodyFunctionExpression = 'TSEmptyBodyFunctionExpression', TSEnumDeclaration = 'TSEnumDeclaration', + TSEnumBody = 'TSEnumBody', TSEnumMember = 'TSEnumMember', TSExportAssignment = 'TSExportAssignment', TSExportKeyword = 'TSExportKeyword', diff --git a/packages/ast-spec/src/declaration/ExportNamedDeclaration/fixtures/enum/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/declaration/ExportNamedDeclaration/fixtures/enum/snapshots/1-TSESTree-AST.shot index f3dff0f70ee9..f8860c0534d2 100644 --- a/packages/ast-spec/src/declaration/ExportNamedDeclaration/fixtures/enum/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/declaration/ExportNamedDeclaration/fixtures/enum/snapshots/1-TSESTree-AST.shot @@ -9,6 +9,16 @@ Program { attributes: [], declaration: TSEnumDeclaration { type: "TSEnumDeclaration", + body: TSEnumBody { + type: "TSEnumBody", + members: [], + + range: [16, 18], + loc: { + start: { column: 16, line: 1 }, + end: { column: 18, line: 1 }, + }, + }, const: false, declare: false, id: Identifier { @@ -23,7 +33,6 @@ Program { end: { column: 15, line: 1 }, }, }, - members: [], range: [7, 18], loc: { diff --git a/packages/ast-spec/src/declaration/ExportNamedDeclaration/fixtures/enum/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/declaration/ExportNamedDeclaration/fixtures/enum/snapshots/5-AST-Alignment-AST.shot index 0f83a6c9f506..9aa4daf97447 100644 --- a/packages/ast-spec/src/declaration/ExportNamedDeclaration/fixtures/enum/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/declaration/ExportNamedDeclaration/fixtures/enum/snapshots/5-AST-Alignment-AST.shot @@ -14,6 +14,16 @@ exports[`AST Fixtures declaration ExportNamedDeclaration enum AST Alignment - AS + assertions: Array [], declaration: TSEnumDeclaration { type: 'TSEnumDeclaration', +- body: TSEnumBody { +- type: 'TSEnumBody', +- members: Array [], +- +- range: [16, 18], +- loc: { +- start: { column: 16, line: 1 }, +- end: { column: 18, line: 1 }, +- }, +- }, - const: false, - declare: false, id: Identifier { @@ -28,7 +38,7 @@ exports[`AST Fixtures declaration ExportNamedDeclaration enum AST Alignment - AS end: { column: 15, line: 1 }, }, }, - members: Array [], ++ members: Array [], range: [7, 18], loc: { diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/const/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/const/snapshots/1-TSESTree-AST.shot index 45c1e395be04..30fdcfbc6815 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/const/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/const/snapshots/1-TSESTree-AST.shot @@ -6,6 +6,16 @@ Program { body: [ TSEnumDeclaration { type: "TSEnumDeclaration", + body: TSEnumBody { + type: "TSEnumBody", + members: [], + + range: [15, 17], + loc: { + start: { column: 15, line: 1 }, + end: { column: 17, line: 1 }, + }, + }, const: true, declare: false, id: Identifier { @@ -20,7 +30,6 @@ Program { end: { column: 14, line: 1 }, }, }, - members: [], range: [0, 17], loc: { diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/const/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/const/snapshots/5-AST-Alignment-AST.shot index c9480e42c3a4..a0fe24cb4d5c 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/const/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/const/snapshots/5-AST-Alignment-AST.shot @@ -10,6 +10,16 @@ exports[`AST Fixtures declaration TSEnumDeclaration const AST Alignment - AST 1` body: Array [ TSEnumDeclaration { type: 'TSEnumDeclaration', +- body: TSEnumBody { +- type: 'TSEnumBody', +- members: Array [], +- +- range: [15, 17], +- loc: { +- start: { column: 15, line: 1 }, +- end: { column: 17, line: 1 }, +- }, +- }, const: true, - declare: false, id: Identifier { @@ -24,7 +34,7 @@ exports[`AST Fixtures declaration TSEnumDeclaration const AST Alignment - AST 1` end: { column: 14, line: 1 }, }, }, - members: Array [], ++ members: Array [], range: [0, 17], loc: { diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/declare/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/declare/snapshots/1-TSESTree-AST.shot index b4e7d92295e5..e1f611ebd1ba 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/declare/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/declare/snapshots/1-TSESTree-AST.shot @@ -6,6 +6,16 @@ Program { body: [ TSEnumDeclaration { type: "TSEnumDeclaration", + body: TSEnumBody { + type: "TSEnumBody", + members: [], + + range: [17, 19], + loc: { + start: { column: 17, line: 1 }, + end: { column: 19, line: 1 }, + }, + }, const: false, declare: true, id: Identifier { @@ -20,7 +30,6 @@ Program { end: { column: 16, line: 1 }, }, }, - members: [], range: [0, 19], loc: { diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/declare/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/declare/snapshots/5-AST-Alignment-AST.shot index 3926e3f88cc8..c942393ad72d 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/declare/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/declare/snapshots/5-AST-Alignment-AST.shot @@ -10,6 +10,16 @@ exports[`AST Fixtures declaration TSEnumDeclaration declare AST Alignment - AST body: Array [ TSEnumDeclaration { type: 'TSEnumDeclaration', +- body: TSEnumBody { +- type: 'TSEnumBody', +- members: Array [], +- +- range: [17, 19], +- loc: { +- start: { column: 17, line: 1 }, +- end: { column: 19, line: 1 }, +- }, +- }, - const: false, declare: true, id: Identifier { @@ -24,7 +34,7 @@ exports[`AST Fixtures declaration TSEnumDeclaration declare AST Alignment - AST end: { column: 16, line: 1 }, }, }, - members: Array [], ++ members: Array [], range: [0, 19], loc: { diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/empty/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/empty/snapshots/1-TSESTree-AST.shot index 2c454bcfcd82..dd8b9928297b 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/empty/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/empty/snapshots/1-TSESTree-AST.shot @@ -6,6 +6,16 @@ Program { body: [ TSEnumDeclaration { type: "TSEnumDeclaration", + body: TSEnumBody { + type: "TSEnumBody", + members: [], + + range: [9, 11], + loc: { + start: { column: 9, line: 1 }, + end: { column: 11, line: 1 }, + }, + }, const: false, declare: false, id: Identifier { @@ -20,7 +30,6 @@ Program { end: { column: 8, line: 1 }, }, }, - members: [], range: [0, 11], loc: { diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/empty/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/empty/snapshots/5-AST-Alignment-AST.shot index 86d2f4844f4a..f6fd91b6e44c 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/empty/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/empty/snapshots/5-AST-Alignment-AST.shot @@ -10,6 +10,16 @@ exports[`AST Fixtures declaration TSEnumDeclaration empty AST Alignment - AST 1` body: Array [ TSEnumDeclaration { type: 'TSEnumDeclaration', +- body: TSEnumBody { +- type: 'TSEnumBody', +- members: Array [], +- +- range: [9, 11], +- loc: { +- start: { column: 9, line: 1 }, +- end: { column: 11, line: 1 }, +- }, +- }, - const: false, - declare: false, id: Identifier { @@ -24,7 +34,7 @@ exports[`AST Fixtures declaration TSEnumDeclaration empty AST Alignment - AST 1` end: { column: 8, line: 1 }, }, }, - members: Array [], ++ members: Array [], range: [0, 11], loc: { diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/with-member-one/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/with-member-one/snapshots/1-TSESTree-AST.shot index 29f98e4e129e..4d9bc63c978a 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/with-member-one/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/with-member-one/snapshots/1-TSESTree-AST.shot @@ -6,6 +6,39 @@ Program { body: [ TSEnumDeclaration { type: "TSEnumDeclaration", + body: TSEnumBody { + type: "TSEnumBody", + members: [ + TSEnumMember { + type: "TSEnumMember", + computed: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "A", + optional: false, + + range: [13, 14], + loc: { + start: { column: 2, line: 2 }, + end: { column: 3, line: 2 }, + }, + }, + + range: [13, 14], + loc: { + start: { column: 2, line: 2 }, + end: { column: 3, line: 2 }, + }, + }, + ], + + range: [9, 17], + loc: { + start: { column: 9, line: 1 }, + end: { column: 1, line: 3 }, + }, + }, const: false, declare: false, id: Identifier { @@ -20,30 +53,6 @@ Program { end: { column: 8, line: 1 }, }, }, - members: [ - TSEnumMember { - type: "TSEnumMember", - computed: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "A", - optional: false, - - range: [13, 14], - loc: { - start: { column: 2, line: 2 }, - end: { column: 3, line: 2 }, - }, - }, - - range: [13, 14], - loc: { - start: { column: 2, line: 2 }, - end: { column: 3, line: 2 }, - }, - }, - ], range: [0, 17], loc: { diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/with-member-one/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/with-member-one/snapshots/5-AST-Alignment-AST.shot index 7137394a11bb..85d1319d1313 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/with-member-one/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/fixtures/with-member-one/snapshots/5-AST-Alignment-AST.shot @@ -10,29 +10,39 @@ exports[`AST Fixtures declaration TSEnumDeclaration with-member-one AST Alignmen body: Array [ TSEnumDeclaration { type: 'TSEnumDeclaration', -- const: false, -- declare: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'Foo', -- optional: false, +- body: TSEnumBody { +- type: 'TSEnumBody', +- members: Array [ +- TSEnumMember { +- type: 'TSEnumMember', +- computed: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'A', +- optional: false, ++ id: Identifier { ++ type: 'Identifier', ++ name: 'Foo', - range: [5, 8], - loc: { - start: { column: 5, line: 1 }, - end: { column: 8, line: 1 }, - }, - }, - members: Array [ - TSEnumMember { - type: 'TSEnumMember', -- computed: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'A', -- optional: false, +- range: [13, 14], +- loc: { +- start: { column: 2, line: 2 }, +- end: { column: 3, line: 2 }, +- }, +- }, ++ range: [5, 8], ++ loc: { ++ start: { column: 5, line: 1 }, ++ end: { column: 8, line: 1 }, ++ }, ++ }, ++ members: Array [ ++ TSEnumMember { ++ type: 'TSEnumMember', ++ id: Identifier { ++ type: 'Identifier', ++ name: 'A', range: [13, 14], loc: { @@ -40,14 +50,34 @@ exports[`AST Fixtures declaration TSEnumDeclaration with-member-one AST Alignmen end: { column: 3, line: 2 }, }, }, +- ], - range: [13, 14], - loc: { - start: { column: 2, line: 2 }, - end: { column: 3, line: 2 }, - }, +- range: [9, 17], +- loc: { +- start: { column: 9, line: 1 }, +- end: { column: 1, line: 3 }, ++ range: [13, 14], ++ loc: { ++ start: { column: 2, line: 2 }, ++ end: { column: 3, line: 2 }, ++ }, }, - ], +- }, +- const: false, +- declare: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'Foo', +- optional: false, +- +- range: [5, 8], +- loc: { +- start: { column: 5, line: 1 }, +- end: { column: 8, line: 1 }, +- }, +- }, ++ ], range: [0, 17], loc: { diff --git a/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts b/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts index 1625063426c8..9d51d654c47b 100644 --- a/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts +++ b/packages/ast-spec/src/declaration/TSEnumDeclaration/spec.ts @@ -2,9 +2,14 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; import type { TSEnumMember } from '../../element/TSEnumMember/spec'; import type { Identifier } from '../../expression/Identifier/spec'; +import type { TSEnumBody } from '../../special/TSEnumBody/spec'; export interface TSEnumDeclaration extends BaseNode { type: AST_NODE_TYPES.TSEnumDeclaration; + /** + * The body of the enum. + */ + body: TSEnumBody; /** * Whether this is a `const` enum. * ``` @@ -25,6 +30,7 @@ export interface TSEnumDeclaration extends BaseNode { id: Identifier; /** * The enum members. + * @deprecated Use {@link body} instead. */ members: TSEnumMember[]; } diff --git a/packages/ast-spec/src/element/TSEnumMember/spec.ts b/packages/ast-spec/src/element/TSEnumMember/spec.ts index 9dd1ddeee696..3e506809dab9 100644 --- a/packages/ast-spec/src/element/TSEnumMember/spec.ts +++ b/packages/ast-spec/src/element/TSEnumMember/spec.ts @@ -1,5 +1,6 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; +import type { TSEnumBody } from '../../special/spec'; import type { Expression } from '../../unions/Expression'; import type { PropertyNameComputed, @@ -13,6 +14,7 @@ interface TSEnumMemberBase extends BaseNode { | PropertyNameNonComputed; initializer: Expression | undefined; computed: boolean; + parent: TSEnumBody; } /** diff --git a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/const-enum/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/const-enum/snapshots/1-TSESTree-AST.shot index 6afdeed4fd39..e6eeb71a2399 100644 --- a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/const-enum/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/const-enum/snapshots/1-TSESTree-AST.shot @@ -6,62 +6,57 @@ Program { body: [ TSEnumDeclaration { type: "TSEnumDeclaration", - const: true, - declare: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "Foo", - optional: false, + body: TSEnumBody { + type: "TSEnumBody", + members: [ + TSEnumMember { + type: "TSEnumMember", + computed: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "foo", + optional: false, - range: [84, 87], - loc: { - start: { column: 11, line: 3 }, - end: { column: 14, line: 3 }, - }, - }, - members: [ - TSEnumMember { - type: "TSEnumMember", - computed: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "foo", - optional: false, + range: [92, 95], + loc: { + start: { column: 2, line: 4 }, + end: { column: 5, line: 4 }, + }, + }, + initializer: Literal { + type: "Literal", + raw: "1", + value: 1, - range: [92, 95], - loc: { - start: { column: 2, line: 4 }, - end: { column: 5, line: 4 }, + range: [98, 99], + loc: { + start: { column: 8, line: 4 }, + end: { column: 9, line: 4 }, + }, }, - }, - initializer: Literal { - type: "Literal", - raw: "1", - value: 1, - range: [98, 99], + range: [92, 99], loc: { - start: { column: 8, line: 4 }, + start: { column: 2, line: 4 }, end: { column: 9, line: 4 }, }, }, + TSEnumMember { + type: "TSEnumMember", + computed: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "bar", + optional: false, - range: [92, 99], - loc: { - start: { column: 2, line: 4 }, - end: { column: 9, line: 4 }, - }, - }, - TSEnumMember { - type: "TSEnumMember", - computed: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "bar", - optional: false, + range: [103, 106], + loc: { + start: { column: 2, line: 5 }, + end: { column: 5, line: 5 }, + }, + }, range: [103, 106], loc: { @@ -69,14 +64,28 @@ Program { end: { column: 5, line: 5 }, }, }, + ], - range: [103, 106], - loc: { - start: { column: 2, line: 5 }, - end: { column: 5, line: 5 }, - }, + range: [88, 109], + loc: { + start: { column: 15, line: 3 }, + end: { column: 1, line: 6 }, }, - ], + }, + const: true, + declare: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "Foo", + optional: false, + + range: [84, 87], + loc: { + start: { column: 11, line: 3 }, + end: { column: 14, line: 3 }, + }, + }, range: [73, 109], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/const-enum/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/const-enum/snapshots/5-AST-Alignment-AST.shot index 3ca65220e227..edeb8e60fafa 100644 --- a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/const-enum/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/const-enum/snapshots/5-AST-Alignment-AST.shot @@ -10,62 +10,95 @@ exports[`AST Fixtures legacy-fixtures basics const-enum AST Alignment - AST 1`] body: Array [ TSEnumDeclaration { type: 'TSEnumDeclaration', - const: true, -- declare: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'Foo', -- optional: false, +- body: TSEnumBody { +- type: 'TSEnumBody', +- members: Array [ +- TSEnumMember { +- type: 'TSEnumMember', +- computed: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'foo', +- optional: false, ++ const: true, ++ id: Identifier { ++ type: 'Identifier', ++ name: 'Foo', - range: [84, 87], - loc: { - start: { column: 11, line: 3 }, - end: { column: 14, line: 3 }, - }, - }, - members: Array [ - TSEnumMember { - type: 'TSEnumMember', -- computed: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'foo', -- optional: false, +- range: [92, 95], +- loc: { +- start: { column: 2, line: 4 }, +- end: { column: 5, line: 4 }, +- }, +- }, +- initializer: Literal { +- type: 'Literal', +- raw: '1', +- value: 1, ++ range: [84, 87], ++ loc: { ++ start: { column: 11, line: 3 }, ++ end: { column: 14, line: 3 }, ++ }, ++ }, ++ members: Array [ ++ TSEnumMember { ++ type: 'TSEnumMember', ++ id: Identifier { ++ type: 'Identifier', ++ name: 'foo', - range: [92, 95], - loc: { - start: { column: 2, line: 4 }, - end: { column: 5, line: 4 }, +- range: [98, 99], +- loc: { +- start: { column: 8, line: 4 }, +- end: { column: 9, line: 4 }, +- }, ++ range: [92, 95], ++ loc: { ++ start: { column: 2, line: 4 }, ++ end: { column: 5, line: 4 }, }, - }, - initializer: Literal { - type: 'Literal', - raw: '1', - value: 1, ++ }, ++ initializer: Literal { ++ type: 'Literal', ++ raw: '1', ++ value: 1, - range: [98, 99], +- range: [92, 99], ++ range: [98, 99], loc: { - start: { column: 8, line: 4 }, +- start: { column: 2, line: 4 }, ++ start: { column: 8, line: 4 }, end: { column: 9, line: 4 }, }, }, +- TSEnumMember { +- type: 'TSEnumMember', +- computed: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'bar', +- optional: false, - range: [92, 99], - loc: { - start: { column: 2, line: 4 }, - end: { column: 9, line: 4 }, - }, - }, - TSEnumMember { - type: 'TSEnumMember', -- computed: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'bar', -- optional: false, +- range: [103, 106], +- loc: { +- start: { column: 2, line: 5 }, +- end: { column: 5, line: 5 }, +- }, +- }, ++ range: [92, 99], ++ loc: { ++ start: { column: 2, line: 4 }, ++ end: { column: 9, line: 4 }, ++ }, ++ }, ++ TSEnumMember { ++ type: 'TSEnumMember', ++ id: Identifier { ++ type: 'Identifier', ++ name: 'bar', range: [103, 106], loc: { @@ -73,14 +106,34 @@ exports[`AST Fixtures legacy-fixtures basics const-enum AST Alignment - AST 1`] end: { column: 5, line: 5 }, }, }, +- ], - range: [103, 106], - loc: { - start: { column: 2, line: 5 }, - end: { column: 5, line: 5 }, - }, +- range: [88, 109], +- loc: { +- start: { column: 15, line: 3 }, +- end: { column: 1, line: 6 }, +- }, +- }, +- const: true, +- declare: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'Foo', +- optional: false, +- +- range: [84, 87], +- loc: { +- start: { column: 11, line: 3 }, +- end: { column: 14, line: 3 }, ++ range: [103, 106], ++ loc: { ++ start: { column: 2, line: 5 }, ++ end: { column: 5, line: 5 }, ++ }, }, - ], +- }, ++ ], range: [73, 109], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-const-named-enum/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-const-named-enum/snapshots/1-TSESTree-AST.shot index edf76ad24aee..39a9d91b204a 100644 --- a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-const-named-enum/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-const-named-enum/snapshots/1-TSESTree-AST.shot @@ -9,62 +9,57 @@ Program { attributes: [], declaration: TSEnumDeclaration { type: "TSEnumDeclaration", - const: true, - declare: true, - id: Identifier { - type: "Identifier", - decorators: [], - name: "Foo", - optional: false, + body: TSEnumBody { + type: "TSEnumBody", + members: [ + TSEnumMember { + type: "TSEnumMember", + computed: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "foo", + optional: false, - range: [99, 102], - loc: { - start: { column: 26, line: 3 }, - end: { column: 29, line: 3 }, - }, - }, - members: [ - TSEnumMember { - type: "TSEnumMember", - computed: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "foo", - optional: false, + range: [107, 110], + loc: { + start: { column: 2, line: 4 }, + end: { column: 5, line: 4 }, + }, + }, + initializer: Literal { + type: "Literal", + raw: "1", + value: 1, - range: [107, 110], - loc: { - start: { column: 2, line: 4 }, - end: { column: 5, line: 4 }, + range: [113, 114], + loc: { + start: { column: 8, line: 4 }, + end: { column: 9, line: 4 }, + }, }, - }, - initializer: Literal { - type: "Literal", - raw: "1", - value: 1, - range: [113, 114], + range: [107, 114], loc: { - start: { column: 8, line: 4 }, + start: { column: 2, line: 4 }, end: { column: 9, line: 4 }, }, }, + TSEnumMember { + type: "TSEnumMember", + computed: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "bar", + optional: false, - range: [107, 114], - loc: { - start: { column: 2, line: 4 }, - end: { column: 9, line: 4 }, - }, - }, - TSEnumMember { - type: "TSEnumMember", - computed: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "bar", - optional: false, + range: [118, 121], + loc: { + start: { column: 2, line: 5 }, + end: { column: 5, line: 5 }, + }, + }, range: [118, 121], loc: { @@ -72,14 +67,28 @@ Program { end: { column: 5, line: 5 }, }, }, + ], - range: [118, 121], - loc: { - start: { column: 2, line: 5 }, - end: { column: 5, line: 5 }, - }, + range: [103, 124], + loc: { + start: { column: 30, line: 3 }, + end: { column: 1, line: 6 }, }, - ], + }, + const: true, + declare: true, + id: Identifier { + type: "Identifier", + decorators: [], + name: "Foo", + optional: false, + + range: [99, 102], + loc: { + start: { column: 26, line: 3 }, + end: { column: 29, line: 3 }, + }, + }, range: [80, 124], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-const-named-enum/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-const-named-enum/snapshots/5-AST-Alignment-AST.shot index 1795c3300259..7d3830711a9c 100644 --- a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-const-named-enum/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-const-named-enum/snapshots/5-AST-Alignment-AST.shot @@ -14,62 +14,96 @@ exports[`AST Fixtures legacy-fixtures basics export-declare-const-named-enum AST + assertions: Array [], declaration: TSEnumDeclaration { type: 'TSEnumDeclaration', - const: true, - declare: true, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'Foo', -- optional: false, +- body: TSEnumBody { +- type: 'TSEnumBody', +- members: Array [ +- TSEnumMember { +- type: 'TSEnumMember', +- computed: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'foo', +- optional: false, ++ const: true, ++ declare: true, ++ id: Identifier { ++ type: 'Identifier', ++ name: 'Foo', - range: [99, 102], - loc: { - start: { column: 26, line: 3 }, - end: { column: 29, line: 3 }, - }, - }, - members: Array [ - TSEnumMember { - type: 'TSEnumMember', -- computed: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'foo', -- optional: false, +- range: [107, 110], +- loc: { +- start: { column: 2, line: 4 }, +- end: { column: 5, line: 4 }, +- }, +- }, +- initializer: Literal { +- type: 'Literal', +- raw: '1', +- value: 1, ++ range: [99, 102], ++ loc: { ++ start: { column: 26, line: 3 }, ++ end: { column: 29, line: 3 }, ++ }, ++ }, ++ members: Array [ ++ TSEnumMember { ++ type: 'TSEnumMember', ++ id: Identifier { ++ type: 'Identifier', ++ name: 'foo', - range: [107, 110], - loc: { - start: { column: 2, line: 4 }, - end: { column: 5, line: 4 }, +- range: [113, 114], +- loc: { +- start: { column: 8, line: 4 }, +- end: { column: 9, line: 4 }, +- }, ++ range: [107, 110], ++ loc: { ++ start: { column: 2, line: 4 }, ++ end: { column: 5, line: 4 }, }, - }, - initializer: Literal { - type: 'Literal', - raw: '1', - value: 1, ++ }, ++ initializer: Literal { ++ type: 'Literal', ++ raw: '1', ++ value: 1, - range: [113, 114], +- range: [107, 114], ++ range: [113, 114], loc: { - start: { column: 8, line: 4 }, +- start: { column: 2, line: 4 }, ++ start: { column: 8, line: 4 }, end: { column: 9, line: 4 }, }, }, +- TSEnumMember { +- type: 'TSEnumMember', +- computed: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'bar', +- optional: false, - range: [107, 114], - loc: { - start: { column: 2, line: 4 }, - end: { column: 9, line: 4 }, - }, - }, - TSEnumMember { - type: 'TSEnumMember', -- computed: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'bar', -- optional: false, +- range: [118, 121], +- loc: { +- start: { column: 2, line: 5 }, +- end: { column: 5, line: 5 }, +- }, +- }, ++ range: [107, 114], ++ loc: { ++ start: { column: 2, line: 4 }, ++ end: { column: 9, line: 4 }, ++ }, ++ }, ++ TSEnumMember { ++ type: 'TSEnumMember', ++ id: Identifier { ++ type: 'Identifier', ++ name: 'bar', range: [118, 121], loc: { @@ -77,14 +111,34 @@ exports[`AST Fixtures legacy-fixtures basics export-declare-const-named-enum AST end: { column: 5, line: 5 }, }, }, +- ], - range: [118, 121], - loc: { - start: { column: 2, line: 5 }, - end: { column: 5, line: 5 }, - }, +- range: [103, 124], +- loc: { +- start: { column: 30, line: 3 }, +- end: { column: 1, line: 6 }, +- }, +- }, +- const: true, +- declare: true, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'Foo', +- optional: false, +- +- range: [99, 102], +- loc: { +- start: { column: 26, line: 3 }, +- end: { column: 29, line: 3 }, ++ range: [118, 121], ++ loc: { ++ start: { column: 2, line: 5 }, ++ end: { column: 5, line: 5 }, ++ }, }, - ], +- }, ++ ], range: [80, 124], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-named-enum/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-named-enum/snapshots/1-TSESTree-AST.shot index 14b710bbca7d..80bdeff4c86d 100644 --- a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-named-enum/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-named-enum/snapshots/1-TSESTree-AST.shot @@ -9,62 +9,57 @@ Program { attributes: [], declaration: TSEnumDeclaration { type: "TSEnumDeclaration", - const: false, - declare: true, - id: Identifier { - type: "Identifier", - decorators: [], - name: "Foo", - optional: false, + body: TSEnumBody { + type: "TSEnumBody", + members: [ + TSEnumMember { + type: "TSEnumMember", + computed: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "foo", + optional: false, - range: [93, 96], - loc: { - start: { column: 20, line: 3 }, - end: { column: 23, line: 3 }, - }, - }, - members: [ - TSEnumMember { - type: "TSEnumMember", - computed: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "foo", - optional: false, + range: [101, 104], + loc: { + start: { column: 2, line: 4 }, + end: { column: 5, line: 4 }, + }, + }, + initializer: Literal { + type: "Literal", + raw: "1", + value: 1, - range: [101, 104], - loc: { - start: { column: 2, line: 4 }, - end: { column: 5, line: 4 }, + range: [107, 108], + loc: { + start: { column: 8, line: 4 }, + end: { column: 9, line: 4 }, + }, }, - }, - initializer: Literal { - type: "Literal", - raw: "1", - value: 1, - range: [107, 108], + range: [101, 108], loc: { - start: { column: 8, line: 4 }, + start: { column: 2, line: 4 }, end: { column: 9, line: 4 }, }, }, + TSEnumMember { + type: "TSEnumMember", + computed: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "bar", + optional: false, - range: [101, 108], - loc: { - start: { column: 2, line: 4 }, - end: { column: 9, line: 4 }, - }, - }, - TSEnumMember { - type: "TSEnumMember", - computed: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "bar", - optional: false, + range: [112, 115], + loc: { + start: { column: 2, line: 5 }, + end: { column: 5, line: 5 }, + }, + }, range: [112, 115], loc: { @@ -72,14 +67,28 @@ Program { end: { column: 5, line: 5 }, }, }, + ], - range: [112, 115], - loc: { - start: { column: 2, line: 5 }, - end: { column: 5, line: 5 }, - }, + range: [97, 118], + loc: { + start: { column: 24, line: 3 }, + end: { column: 1, line: 6 }, }, - ], + }, + const: false, + declare: true, + id: Identifier { + type: "Identifier", + decorators: [], + name: "Foo", + optional: false, + + range: [93, 96], + loc: { + start: { column: 20, line: 3 }, + end: { column: 23, line: 3 }, + }, + }, range: [80, 118], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-named-enum/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-named-enum/snapshots/5-AST-Alignment-AST.shot index 90b208e7ad14..913dcb738db1 100644 --- a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-named-enum/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-declare-named-enum/snapshots/5-AST-Alignment-AST.shot @@ -14,62 +14,95 @@ exports[`AST Fixtures legacy-fixtures basics export-declare-named-enum AST Align + assertions: Array [], declaration: TSEnumDeclaration { type: 'TSEnumDeclaration', -- const: false, - declare: true, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'Foo', -- optional: false, +- body: TSEnumBody { +- type: 'TSEnumBody', +- members: Array [ +- TSEnumMember { +- type: 'TSEnumMember', +- computed: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'foo', +- optional: false, ++ declare: true, ++ id: Identifier { ++ type: 'Identifier', ++ name: 'Foo', - range: [93, 96], - loc: { - start: { column: 20, line: 3 }, - end: { column: 23, line: 3 }, - }, - }, - members: Array [ - TSEnumMember { - type: 'TSEnumMember', -- computed: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'foo', -- optional: false, +- range: [101, 104], +- loc: { +- start: { column: 2, line: 4 }, +- end: { column: 5, line: 4 }, +- }, +- }, +- initializer: Literal { +- type: 'Literal', +- raw: '1', +- value: 1, ++ range: [93, 96], ++ loc: { ++ start: { column: 20, line: 3 }, ++ end: { column: 23, line: 3 }, ++ }, ++ }, ++ members: Array [ ++ TSEnumMember { ++ type: 'TSEnumMember', ++ id: Identifier { ++ type: 'Identifier', ++ name: 'foo', - range: [101, 104], - loc: { - start: { column: 2, line: 4 }, - end: { column: 5, line: 4 }, +- range: [107, 108], +- loc: { +- start: { column: 8, line: 4 }, +- end: { column: 9, line: 4 }, +- }, ++ range: [101, 104], ++ loc: { ++ start: { column: 2, line: 4 }, ++ end: { column: 5, line: 4 }, }, - }, - initializer: Literal { - type: 'Literal', - raw: '1', - value: 1, ++ }, ++ initializer: Literal { ++ type: 'Literal', ++ raw: '1', ++ value: 1, - range: [107, 108], +- range: [101, 108], ++ range: [107, 108], loc: { - start: { column: 8, line: 4 }, +- start: { column: 2, line: 4 }, ++ start: { column: 8, line: 4 }, end: { column: 9, line: 4 }, }, }, +- TSEnumMember { +- type: 'TSEnumMember', +- computed: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'bar', +- optional: false, - range: [101, 108], - loc: { - start: { column: 2, line: 4 }, - end: { column: 9, line: 4 }, - }, - }, - TSEnumMember { - type: 'TSEnumMember', -- computed: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'bar', -- optional: false, +- range: [112, 115], +- loc: { +- start: { column: 2, line: 5 }, +- end: { column: 5, line: 5 }, +- }, +- }, ++ range: [101, 108], ++ loc: { ++ start: { column: 2, line: 4 }, ++ end: { column: 9, line: 4 }, ++ }, ++ }, ++ TSEnumMember { ++ type: 'TSEnumMember', ++ id: Identifier { ++ type: 'Identifier', ++ name: 'bar', range: [112, 115], loc: { @@ -77,14 +110,34 @@ exports[`AST Fixtures legacy-fixtures basics export-declare-named-enum AST Align end: { column: 5, line: 5 }, }, }, +- ], +- +- range: [97, 118], +- loc: { +- start: { column: 24, line: 3 }, +- end: { column: 1, line: 6 }, +- }, +- }, +- const: false, +- declare: true, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'Foo', +- optional: false, - range: [112, 115], - loc: { - start: { column: 2, line: 5 }, - end: { column: 5, line: 5 }, - }, +- range: [93, 96], +- loc: { +- start: { column: 20, line: 3 }, +- end: { column: 23, line: 3 }, ++ range: [112, 115], ++ loc: { ++ start: { column: 2, line: 5 }, ++ end: { column: 5, line: 5 }, ++ }, }, - ], +- }, ++ ], range: [80, 118], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-named-enum/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-named-enum/snapshots/1-TSESTree-AST.shot index 7126562753f5..d0fa7b0eb7ba 100644 --- a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-named-enum/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-named-enum/snapshots/1-TSESTree-AST.shot @@ -9,62 +9,57 @@ Program { attributes: [], declaration: TSEnumDeclaration { type: "TSEnumDeclaration", - const: false, - declare: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "Foo", - optional: false, + body: TSEnumBody { + type: "TSEnumBody", + members: [ + TSEnumMember { + type: "TSEnumMember", + computed: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "foo", + optional: false, - range: [85, 88], - loc: { - start: { column: 12, line: 3 }, - end: { column: 15, line: 3 }, - }, - }, - members: [ - TSEnumMember { - type: "TSEnumMember", - computed: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "foo", - optional: false, + range: [93, 96], + loc: { + start: { column: 2, line: 4 }, + end: { column: 5, line: 4 }, + }, + }, + initializer: Literal { + type: "Literal", + raw: "1", + value: 1, - range: [93, 96], - loc: { - start: { column: 2, line: 4 }, - end: { column: 5, line: 4 }, + range: [99, 100], + loc: { + start: { column: 8, line: 4 }, + end: { column: 9, line: 4 }, + }, }, - }, - initializer: Literal { - type: "Literal", - raw: "1", - value: 1, - range: [99, 100], + range: [93, 100], loc: { - start: { column: 8, line: 4 }, + start: { column: 2, line: 4 }, end: { column: 9, line: 4 }, }, }, + TSEnumMember { + type: "TSEnumMember", + computed: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "bar", + optional: false, - range: [93, 100], - loc: { - start: { column: 2, line: 4 }, - end: { column: 9, line: 4 }, - }, - }, - TSEnumMember { - type: "TSEnumMember", - computed: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "bar", - optional: false, + range: [104, 107], + loc: { + start: { column: 2, line: 5 }, + end: { column: 5, line: 5 }, + }, + }, range: [104, 107], loc: { @@ -72,14 +67,28 @@ Program { end: { column: 5, line: 5 }, }, }, + ], - range: [104, 107], - loc: { - start: { column: 2, line: 5 }, - end: { column: 5, line: 5 }, - }, + range: [89, 110], + loc: { + start: { column: 16, line: 3 }, + end: { column: 1, line: 6 }, }, - ], + }, + const: false, + declare: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "Foo", + optional: false, + + range: [85, 88], + loc: { + start: { column: 12, line: 3 }, + end: { column: 15, line: 3 }, + }, + }, range: [80, 110], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-named-enum/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-named-enum/snapshots/5-AST-Alignment-AST.shot index 020aebc7f3d0..3b1b5eeb9c1a 100644 --- a/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-named-enum/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/basics/fixtures/export-named-enum/snapshots/5-AST-Alignment-AST.shot @@ -14,62 +14,94 @@ exports[`AST Fixtures legacy-fixtures basics export-named-enum AST Alignment - A + assertions: Array [], declaration: TSEnumDeclaration { type: 'TSEnumDeclaration', -- const: false, -- declare: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'Foo', -- optional: false, +- body: TSEnumBody { +- type: 'TSEnumBody', +- members: Array [ +- TSEnumMember { +- type: 'TSEnumMember', +- computed: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'foo', +- optional: false, ++ id: Identifier { ++ type: 'Identifier', ++ name: 'Foo', - range: [85, 88], - loc: { - start: { column: 12, line: 3 }, - end: { column: 15, line: 3 }, - }, - }, - members: Array [ - TSEnumMember { - type: 'TSEnumMember', -- computed: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'foo', -- optional: false, +- range: [93, 96], +- loc: { +- start: { column: 2, line: 4 }, +- end: { column: 5, line: 4 }, +- }, +- }, +- initializer: Literal { +- type: 'Literal', +- raw: '1', +- value: 1, ++ range: [85, 88], ++ loc: { ++ start: { column: 12, line: 3 }, ++ end: { column: 15, line: 3 }, ++ }, ++ }, ++ members: Array [ ++ TSEnumMember { ++ type: 'TSEnumMember', ++ id: Identifier { ++ type: 'Identifier', ++ name: 'foo', - range: [93, 96], - loc: { - start: { column: 2, line: 4 }, - end: { column: 5, line: 4 }, +- range: [99, 100], +- loc: { +- start: { column: 8, line: 4 }, +- end: { column: 9, line: 4 }, +- }, ++ range: [93, 96], ++ loc: { ++ start: { column: 2, line: 4 }, ++ end: { column: 5, line: 4 }, }, - }, - initializer: Literal { - type: 'Literal', - raw: '1', - value: 1, ++ }, ++ initializer: Literal { ++ type: 'Literal', ++ raw: '1', ++ value: 1, - range: [99, 100], +- range: [93, 100], ++ range: [99, 100], loc: { - start: { column: 8, line: 4 }, +- start: { column: 2, line: 4 }, ++ start: { column: 8, line: 4 }, end: { column: 9, line: 4 }, }, }, +- TSEnumMember { +- type: 'TSEnumMember', +- computed: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'bar', +- optional: false, - range: [93, 100], - loc: { - start: { column: 2, line: 4 }, - end: { column: 9, line: 4 }, - }, - }, - TSEnumMember { - type: 'TSEnumMember', -- computed: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'bar', -- optional: false, +- range: [104, 107], +- loc: { +- start: { column: 2, line: 5 }, +- end: { column: 5, line: 5 }, +- }, +- }, ++ range: [93, 100], ++ loc: { ++ start: { column: 2, line: 4 }, ++ end: { column: 9, line: 4 }, ++ }, ++ }, ++ TSEnumMember { ++ type: 'TSEnumMember', ++ id: Identifier { ++ type: 'Identifier', ++ name: 'bar', range: [104, 107], loc: { @@ -77,14 +109,34 @@ exports[`AST Fixtures legacy-fixtures basics export-named-enum AST Alignment - A end: { column: 5, line: 5 }, }, }, +- ], - range: [104, 107], - loc: { - start: { column: 2, line: 5 }, - end: { column: 5, line: 5 }, - }, +- range: [89, 110], +- loc: { +- start: { column: 16, line: 3 }, +- end: { column: 1, line: 6 }, ++ range: [104, 107], ++ loc: { ++ start: { column: 2, line: 5 }, ++ end: { column: 5, line: 5 }, ++ }, }, - ], +- }, +- const: false, +- declare: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'Foo', +- optional: false, +- +- range: [85, 88], +- loc: { +- start: { column: 12, line: 3 }, +- end: { column: 15, line: 3 }, +- }, +- }, ++ ], range: [80, 110], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/declare/fixtures/enum/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/declare/fixtures/enum/snapshots/1-TSESTree-AST.shot index 956fec5e84a7..e6515601902c 100644 --- a/packages/ast-spec/src/legacy-fixtures/declare/fixtures/enum/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/declare/fixtures/enum/snapshots/1-TSESTree-AST.shot @@ -6,29 +6,24 @@ Program { body: [ TSEnumDeclaration { type: "TSEnumDeclaration", - const: false, - declare: true, - id: Identifier { - type: "Identifier", - decorators: [], - name: "Foo", - optional: false, + body: TSEnumBody { + type: "TSEnumBody", + members: [ + TSEnumMember { + type: "TSEnumMember", + computed: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "Bar", + optional: false, - range: [86, 89], - loc: { - start: { column: 13, line: 3 }, - end: { column: 16, line: 3 }, - }, - }, - members: [ - TSEnumMember { - type: "TSEnumMember", - computed: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "Bar", - optional: false, + range: [94, 97], + loc: { + start: { column: 2, line: 4 }, + end: { column: 5, line: 4 }, + }, + }, range: [94, 97], loc: { @@ -36,21 +31,21 @@ Program { end: { column: 5, line: 4 }, }, }, + TSEnumMember { + type: "TSEnumMember", + computed: false, + id: Identifier { + type: "Identifier", + decorators: [], + name: "Baz", + optional: false, - range: [94, 97], - loc: { - start: { column: 2, line: 4 }, - end: { column: 5, line: 4 }, - }, - }, - TSEnumMember { - type: "TSEnumMember", - computed: false, - id: Identifier { - type: "Identifier", - decorators: [], - name: "Baz", - optional: false, + range: [101, 104], + loc: { + start: { column: 2, line: 5 }, + end: { column: 5, line: 5 }, + }, + }, range: [101, 104], loc: { @@ -58,14 +53,28 @@ Program { end: { column: 5, line: 5 }, }, }, + ], - range: [101, 104], - loc: { - start: { column: 2, line: 5 }, - end: { column: 5, line: 5 }, - }, + range: [90, 107], + loc: { + start: { column: 17, line: 3 }, + end: { column: 1, line: 6 }, }, - ], + }, + const: false, + declare: true, + id: Identifier { + type: "Identifier", + decorators: [], + name: "Foo", + optional: false, + + range: [86, 89], + loc: { + start: { column: 13, line: 3 }, + end: { column: 16, line: 3 }, + }, + }, range: [73, 107], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/declare/fixtures/enum/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/declare/fixtures/enum/snapshots/5-AST-Alignment-AST.shot index 1ef1a9d0f0b7..baf9d0365b3a 100644 --- a/packages/ast-spec/src/legacy-fixtures/declare/fixtures/enum/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/declare/fixtures/enum/snapshots/5-AST-Alignment-AST.shot @@ -10,29 +10,40 @@ exports[`AST Fixtures legacy-fixtures declare enum AST Alignment - AST 1`] = ` body: Array [ TSEnumDeclaration { type: 'TSEnumDeclaration', -- const: false, - declare: true, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'Foo', -- optional: false, +- body: TSEnumBody { +- type: 'TSEnumBody', +- members: Array [ +- TSEnumMember { +- type: 'TSEnumMember', +- computed: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'Bar', +- optional: false, ++ declare: true, ++ id: Identifier { ++ type: 'Identifier', ++ name: 'Foo', - range: [86, 89], - loc: { - start: { column: 13, line: 3 }, - end: { column: 16, line: 3 }, - }, - }, - members: Array [ - TSEnumMember { - type: 'TSEnumMember', -- computed: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'Bar', -- optional: false, +- range: [94, 97], +- loc: { +- start: { column: 2, line: 4 }, +- end: { column: 5, line: 4 }, +- }, +- }, ++ range: [86, 89], ++ loc: { ++ start: { column: 13, line: 3 }, ++ end: { column: 16, line: 3 }, ++ }, ++ }, ++ members: Array [ ++ TSEnumMember { ++ type: 'TSEnumMember', ++ id: Identifier { ++ type: 'Identifier', ++ name: 'Bar', range: [94, 97], loc: { @@ -40,21 +51,32 @@ exports[`AST Fixtures legacy-fixtures declare enum AST Alignment - AST 1`] = ` end: { column: 5, line: 4 }, }, }, +- TSEnumMember { +- type: 'TSEnumMember', +- computed: false, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'Baz', +- optional: false, - range: [94, 97], - loc: { - start: { column: 2, line: 4 }, - end: { column: 5, line: 4 }, - }, - }, - TSEnumMember { - type: 'TSEnumMember', -- computed: false, - id: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'Baz', -- optional: false, +- range: [101, 104], +- loc: { +- start: { column: 2, line: 5 }, +- end: { column: 5, line: 5 }, +- }, +- }, ++ range: [94, 97], ++ loc: { ++ start: { column: 2, line: 4 }, ++ end: { column: 5, line: 4 }, ++ }, ++ }, ++ TSEnumMember { ++ type: 'TSEnumMember', ++ id: Identifier { ++ type: 'Identifier', ++ name: 'Baz', range: [101, 104], loc: { @@ -62,14 +84,34 @@ exports[`AST Fixtures legacy-fixtures declare enum AST Alignment - AST 1`] = ` end: { column: 5, line: 5 }, }, }, +- ], - range: [101, 104], - loc: { - start: { column: 2, line: 5 }, - end: { column: 5, line: 5 }, - }, +- range: [90, 107], +- loc: { +- start: { column: 17, line: 3 }, +- end: { column: 1, line: 6 }, +- }, +- }, +- const: false, +- declare: true, +- id: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'Foo', +- optional: false, +- +- range: [86, 89], +- loc: { +- start: { column: 13, line: 3 }, +- end: { column: 16, line: 3 }, ++ range: [101, 104], ++ loc: { ++ start: { column: 2, line: 5 }, ++ end: { column: 5, line: 5 }, ++ }, }, - ], +- }, ++ ], range: [73, 107], loc: { diff --git a/packages/ast-spec/src/special/TSEnumBody/spec.ts b/packages/ast-spec/src/special/TSEnumBody/spec.ts new file mode 100644 index 000000000000..f6eef26a1afe --- /dev/null +++ b/packages/ast-spec/src/special/TSEnumBody/spec.ts @@ -0,0 +1,10 @@ +import type { AST_NODE_TYPES } from '../../ast-node-types'; +import type { BaseNode } from '../../base/BaseNode'; +import type { TSEnumDeclaration } from '../../declaration/TSEnumDeclaration/spec'; +import type { TSEnumMember } from '../../element/TSEnumMember/spec'; + +export interface TSEnumBody extends BaseNode { + type: AST_NODE_TYPES.TSEnumBody; + members: TSEnumMember[]; + parent: TSEnumDeclaration; +} diff --git a/packages/ast-spec/src/special/spec.ts b/packages/ast-spec/src/special/spec.ts index c906deb52957..24ef5463f150 100644 --- a/packages/ast-spec/src/special/spec.ts +++ b/packages/ast-spec/src/special/spec.ts @@ -11,6 +11,7 @@ export * from './PrivateIdentifier/spec'; export * from './Program/spec'; export * from './SwitchCase/spec'; export * from './TSClassImplements/spec'; +export * from './TSEnumBody/spec'; export * from './TSExternalModuleReference/spec'; export * from './TSInterfaceBody/spec'; export * from './TSInterfaceHeritage/spec'; diff --git a/packages/ast-spec/src/unions/Node.ts b/packages/ast-spec/src/unions/Node.ts index b0952e0e1863..e224e9711b22 100644 --- a/packages/ast-spec/src/unions/Node.ts +++ b/packages/ast-spec/src/unions/Node.ts @@ -92,6 +92,7 @@ import type { Program } from '../special/Program/spec'; import type { SwitchCase } from '../special/SwitchCase/spec'; import type { TemplateElement } from '../special/TemplateElement/spec'; import type { TSClassImplements } from '../special/TSClassImplements/spec'; +import type { TSEnumBody } from '../special/TSEnumBody/spec'; import type { TSExternalModuleReference } from '../special/TSExternalModuleReference/spec'; import type { TSInterfaceBody } from '../special/TSInterfaceBody/spec'; import type { TSInterfaceHeritage } from '../special/TSInterfaceHeritage/spec'; @@ -272,6 +273,7 @@ export type Node = | TSDeclareFunction | TSDeclareKeyword | TSEmptyBodyFunctionExpression + | TSEnumBody | TSEnumDeclaration | TSEnumMember | TSExportAssignment diff --git a/packages/eslint-plugin-internal/src/rules/prefer-ast-types-enum.ts b/packages/eslint-plugin-internal/src/rules/prefer-ast-types-enum.ts index 9e97e44d2536..5f70bc7cc414 100755 --- a/packages/eslint-plugin-internal/src/rules/prefer-ast-types-enum.ts +++ b/packages/eslint-plugin-internal/src/rules/prefer-ast-types-enum.ts @@ -41,9 +41,8 @@ export default createRule({ Literal(node: TSESTree.Literal): void { if ( node.parent.type === AST_NODE_TYPES.TSEnumMember && - node.parent.parent.type === AST_NODE_TYPES.TSEnumDeclaration && ['AST_NODE_TYPES', 'AST_TOKEN_TYPES', 'DefinitionType'].includes( - node.parent.parent.id.name, + node.parent.parent.parent.id.name, ) ) { return; diff --git a/packages/eslint-plugin/src/rules/brace-style.ts b/packages/eslint-plugin/src/rules/brace-style.ts index 1021c9c9a48c..e38b9240fe34 100644 --- a/packages/eslint-plugin/src/rules/brace-style.ts +++ b/packages/eslint-plugin/src/rules/brace-style.ts @@ -132,7 +132,7 @@ export default createRule({ TSEnumDeclaration(node): void { const closingCurly = context.sourceCode.getLastToken(node)!; const openingCurly = context.sourceCode.getTokenBefore( - node.members.length ? node.members[0] : closingCurly, + node.body.members.length ? node.body.members[0] : closingCurly, )!; validateCurlyPair(openingCurly, closingCurly); diff --git a/packages/eslint-plugin/src/rules/comma-dangle.ts b/packages/eslint-plugin/src/rules/comma-dangle.ts index 60b48ecd0b2c..aad665ad58b1 100644 --- a/packages/eslint-plugin/src/rules/comma-dangle.ts +++ b/packages/eslint-plugin/src/rules/comma-dangle.ts @@ -116,7 +116,7 @@ export default createRule({ function getLastItem(node: TSESTree.Node): TSESTree.Node | null { switch (node.type) { case AST_NODE_TYPES.TSEnumDeclaration: - return last(node.members); + return last(node.body.members); case AST_NODE_TYPES.TSTypeParameterDeclaration: return last(node.params); case AST_NODE_TYPES.TSTupleType: diff --git a/packages/eslint-plugin/src/rules/indent.ts b/packages/eslint-plugin/src/rules/indent.ts index 9ebb23d75844..544e3dbdc826 100644 --- a/packages/eslint-plugin/src/rules/indent.ts +++ b/packages/eslint-plugin/src/rules/indent.ts @@ -249,8 +249,9 @@ export default createRule({ // transform it to an ObjectExpression return rules['ObjectExpression, ObjectPattern']({ type: AST_NODE_TYPES.ObjectExpression, - properties: ( - node.members as (TSESTree.TSEnumMember | TSESTree.TypeElement)[] + properties: (node.type === AST_NODE_TYPES.TSEnumDeclaration + ? node.body.members + : node.members ).map( member => TSPropertySignatureToProperty(member) as TSESTree.Property, diff --git a/packages/eslint-plugin/src/rules/no-duplicate-enum-values.ts b/packages/eslint-plugin/src/rules/no-duplicate-enum-values.ts index f5a58ce14f12..d8ac6586666d 100644 --- a/packages/eslint-plugin/src/rules/no-duplicate-enum-values.ts +++ b/packages/eslint-plugin/src/rules/no-duplicate-enum-values.ts @@ -37,7 +37,7 @@ export default createRule({ return { TSEnumDeclaration(node: TSESTree.TSEnumDeclaration): void { - const enumMembers = node.members; + const enumMembers = node.body.members; const seenValues = new Set(); enumMembers.forEach(member => { diff --git a/packages/eslint-plugin/src/rules/no-mixed-enums.ts b/packages/eslint-plugin/src/rules/no-mixed-enums.ts index e8a06d9861e0..d95d4a9058b7 100644 --- a/packages/eslint-plugin/src/rules/no-mixed-enums.ts +++ b/packages/eslint-plugin/src/rules/no-mixed-enums.ts @@ -51,7 +51,7 @@ export default createRule({ if ( definition.node.type === AST_NODE_TYPES.TSEnumDeclaration && definition.node.range[0] < node.range[0] && - definition.node.members.length > 0 + definition.node.body.members.length > 0 ) { found.previousSibling = definition.node; break; @@ -146,7 +146,7 @@ export default createRule({ // enum MyEnum { A } // enum MyEnum { B } if (previousSibling) { - return getMemberType(previousSibling.members[0]); + return getMemberType(previousSibling.body.members[0]); } // Case: Namespace declaration merging @@ -185,12 +185,12 @@ export default createRule({ } // Finally, we default to the type of the first enum member - return getMemberType(node.members[0]); + return getMemberType(node.body.members[0]); } return { TSEnumDeclaration(node): void { - if (!node.members.length) { + if (!node.body.members.length) { return; } @@ -199,7 +199,7 @@ export default createRule({ return; } - for (const member of node.members) { + for (const member of node.body.members) { const currentType = getMemberType(member); if (currentType === AllowedType.Unknown) { return; diff --git a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts index 27572b4f8f7f..00dae8cccb96 100644 --- a/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts +++ b/packages/eslint-plugin/src/rules/prefer-enum-initializers.ts @@ -24,7 +24,7 @@ export default createRule<[], MessageIds>({ defaultOptions: [], create(context) { function TSEnumDeclaration(node: TSESTree.TSEnumDeclaration): void { - const { members } = node; + const { members } = node.body; members.forEach((member, index) => { if (member.initializer == null) { diff --git a/packages/eslint-plugin/tests/rules/indent/indent.test.ts b/packages/eslint-plugin/tests/rules/indent/indent.test.ts index 5974cb5198ee..3d316d1506b9 100644 --- a/packages/eslint-plugin/tests/rules/indent/indent.test.ts +++ b/packages/eslint-plugin/tests/rules/indent/indent.test.ts @@ -181,17 +181,6 @@ class Foo { c : number } ) -} - `, - ], - }, - { - node: 'TSEnumDeclaration, TSEnumMember', - code: [ - ` -enum Foo { - bar = 1, - baz = 1, } `, ], @@ -1471,96 +1460,6 @@ class Foo {} }, ], }, - { - code: ` -enum Foo { -bar, -baz = 1, -buzz = '', -} - `, - output: ` -enum Foo { - bar, - baz = 1, - buzz = '', -} - `, - errors: [ - { - messageId: 'wrongIndentation', - data: { - expected: '4 spaces', - actual: 0, - }, - line: 3, - column: 1, - }, - { - messageId: 'wrongIndentation', - data: { - expected: '4 spaces', - actual: 0, - }, - line: 4, - column: 1, - }, - { - messageId: 'wrongIndentation', - data: { - expected: '4 spaces', - actual: 0, - }, - line: 5, - column: 1, - }, - ], - }, - { - code: ` -const enum Foo { -bar, -baz = 1, -buzz = '', -} - `, - output: ` -const enum Foo { - bar, - baz = 1, - buzz = '', -} - `, - errors: [ - { - messageId: 'wrongIndentation', - data: { - expected: '4 spaces', - actual: 0, - }, - line: 3, - column: 1, - }, - { - messageId: 'wrongIndentation', - data: { - expected: '4 spaces', - actual: 0, - }, - line: 4, - column: 1, - }, - { - messageId: 'wrongIndentation', - data: { - expected: '4 spaces', - actual: 0, - }, - line: 5, - column: 1, - }, - ], - }, { // eslint-disable-next-line @typescript-eslint/internal/plugin-test-formatting code: ` diff --git a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts index 368a5cd05676..489ec4aeefa8 100644 --- a/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts +++ b/packages/eslint-plugin/tests/rules/lines-around-comment.test.ts @@ -131,67 +131,6 @@ type A = { ], }, - // Enum - { - code: unIndent` -enum A { - // line - a, -} -`, - options: [ - { - beforeLineComment: true, - allowEnumStart: true, - }, - ], - }, - { - code: unIndent` -enum A { - /* block - comment */ - a, -} -`, - options: [ - { - beforeBlockComment: true, - allowEnumStart: true, - }, - ], - }, - { - code: unIndent` -enum A { - a, - // line -} -`, - options: [ - { - afterLineComment: true, - allowEnumEnd: true, - }, - ], - }, - { - code: unIndent` -enum A { - a, - /* block - comment */ -} -`, - options: [ - { - beforeBlockComment: false, - afterBlockComment: true, - allowEnumEnd: true, - }, - ], - }, - // TS module { code: unIndent` @@ -676,147 +615,6 @@ type A = { errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 3 }], }, - // Enum - { - code: unIndent` -enum A { - a, - // line -} -`, - output: unIndent` -enum A { - a, - - // line -} -`, - options: [ - { - beforeLineComment: true, - allowEnumStart: true, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 3 }], - }, - { - code: unIndent` -enum A { - a, - /* block - comment */ -} -`, - output: unIndent` -enum A { - a, - - /* block - comment */ -} -`, - options: [ - { - beforeBlockComment: true, - allowEnumStart: true, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 3 }], - }, - { - code: unIndent` -enum A { - // line - a, -} -`, - output: unIndent` -enum A { - - // line - a, -} -`, - options: [ - { - beforeLineComment: true, - allowEnumStart: false, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Line, line: 2 }], - }, - { - code: unIndent` -enum A { - /* block - comment */ - a, -} -`, - output: unIndent` -enum A { - - /* block - comment */ - a, -} -`, - options: [ - { - beforeBlockComment: true, - allowEnumStart: false, - }, - ], - errors: [{ messageId: 'before', type: AST_TOKEN_TYPES.Block, line: 2 }], - }, - { - code: unIndent` -enum A { - a, - // line -} -`, - output: unIndent` -enum A { - a, - // line - -} -`, - options: [ - { - afterLineComment: true, - allowEnumEnd: false, - }, - ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Line, line: 3 }], - }, - { - code: unIndent` -enum A { - a, - /* block - comment */ -} -`, - output: unIndent` -enum A { - a, - /* block - comment */ - -} -`, - options: [ - { - beforeBlockComment: false, - afterBlockComment: true, - allowEnumEnd: false, - }, - ], - errors: [{ messageId: 'after', type: AST_TOKEN_TYPES.Block, line: 3 }], - }, - // TS module { code: unIndent` diff --git a/packages/scope-manager/src/referencer/Referencer.ts b/packages/scope-manager/src/referencer/Referencer.ts index dac19c71a2c7..f10263642fa1 100644 --- a/packages/scope-manager/src/referencer/Referencer.ts +++ b/packages/scope-manager/src/referencer/Referencer.ts @@ -676,7 +676,7 @@ class Referencer extends Visitor { new TSEnumNameDefinition(node.id, node), ); - for (const member of node.members) { + for (const member of node.body.members) { // TS resolves literal named members to be actual names // enum Foo { // 'a' = 1, diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 9d068cbf2432..c2cf7c2c8bb2 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -2896,13 +2896,26 @@ export class Converter { } case SyntaxKind.EnumDeclaration: { - const result = this.createNode(node, { - type: AST_NODE_TYPES.TSEnumDeclaration, - const: hasModifier(SyntaxKind.ConstKeyword, node), - declare: hasModifier(SyntaxKind.DeclareKeyword, node), - id: this.convertChild(node.name), - members: node.members.map(el => this.convertChild(el)), - }); + const members = node.members.map(el => this.convertChild(el)); + const result = this.createNode( + node, + this.#withDeprecatedGetter( + { + type: AST_NODE_TYPES.TSEnumDeclaration, + body: this.createNode(node, { + type: AST_NODE_TYPES.TSEnumBody, + members, + range: [node.members.pos - 1, node.end], + }), + const: hasModifier(SyntaxKind.ConstKeyword, node), + declare: hasModifier(SyntaxKind.DeclareKeyword, node), + id: this.convertChild(node.name), + }, + 'members', + 'body.members', + node.members.map(el => this.convertChild(el)), + ), + ); return this.fixExports(node, result); } @@ -3518,6 +3531,45 @@ export class Converter { 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/linting/troubleshooting#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, + writable: true, + value, + }); + }, + }); + + return node as Properties & Record; + } + #throwError(node: ts.Node | number, message: string): asserts node is never { let start; let end; 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 f78ba52fb17d..1797ffd62302 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 @@ -177,6 +177,7 @@ export interface EstreeToTsNodeTypes { [AST_NODE_TYPES.TSConstructorType]: ts.ConstructorTypeNode; [AST_NODE_TYPES.TSConstructSignatureDeclaration]: ts.ConstructSignatureDeclaration; [AST_NODE_TYPES.TSDeclareFunction]: ts.FunctionDeclaration; + [AST_NODE_TYPES.TSEnumBody]: ts.EnumDeclaration; [AST_NODE_TYPES.TSEnumDeclaration]: ts.EnumDeclaration; [AST_NODE_TYPES.TSEnumMember]: ts.EnumMember; [AST_NODE_TYPES.TSExportAssignment]: ts.ExportAssignment; diff --git a/packages/visitor-keys/src/visitor-keys.ts b/packages/visitor-keys/src/visitor-keys.ts index 04aa32e5af20..c849790dbb52 100644 --- a/packages/visitor-keys/src/visitor-keys.ts +++ b/packages/visitor-keys/src/visitor-keys.ts @@ -206,7 +206,8 @@ const additionalKeys: AdditionalKeys = { TSDeclareFunction: SharedVisitorKeys.Function, TSDeclareKeyword: [], TSEmptyBodyFunctionExpression: ['id', ...SharedVisitorKeys.FunctionType], - TSEnumDeclaration: ['id', 'members'], + TSEnumBody: ['members'], + TSEnumDeclaration: ['id', 'body'], TSEnumMember: ['id', 'initializer'], TSExportAssignment: ['expression'], TSExportKeyword: [], From 2d835ee86763e6dcc83b77b9d582d43b01e6c945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Fri, 19 Apr 2024 11:21:19 -0400 Subject: [PATCH 03/53] fix(typescript-estree): enable dot globs for project by default (#8818) --- .../typescript-estree/src/parseSettings/resolveProjectList.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/typescript-estree/src/parseSettings/resolveProjectList.ts b/packages/typescript-estree/src/parseSettings/resolveProjectList.ts index 9c3b8480499a..60ec3c17f4ed 100644 --- a/packages/typescript-estree/src/parseSettings/resolveProjectList.ts +++ b/packages/typescript-estree/src/parseSettings/resolveProjectList.ts @@ -98,6 +98,7 @@ export function resolveProjectList( ? [] : globSync([...globProjects, ...projectFolderIgnoreList], { cwd: options.tsconfigRootDir, + dot: true, }), ) .map(project => From 1a22a531d7f0b519be3785e3d5f088a8ace1776b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Fri, 19 Apr 2024 11:24:03 -0400 Subject: [PATCH 04/53] feat(typescript-estree): split TSMappedType typeParameter into constraint and key (#7065) * feat(typescript-estree): split TSMappedType typeParameter into constraint and key * Aha, refactor to deduplicate - and fix property references * Updated scope-manager snapshots * Fixed snapshots, though not the bug * Remove comment, oops * Switch TSMappedType type visiting to be more manual * Lint fixes * Update packages/scope-manager/src/referencer/TypeVisitor.ts Co-authored-by: Brad Zacher * Avoided node.parent as directed --------- Co-authored-by: Brad Zacher --- .../snapshots/1-TSESTree-AST.shot | 98 ++++++------- .../snapshots/5-AST-Alignment-AST.shot | 136 +++++++++++------- .../snapshots/1-TSESTree-AST.shot | 54 +++---- .../snapshots/5-AST-Alignment-AST.shot | 72 +++++----- .../snapshots/1-TSESTree-AST.shot | 54 +++---- .../snapshots/5-AST-Alignment-AST.shot | 72 +++++----- .../snapshots/1-TSESTree-AST.shot | 54 +++---- .../snapshots/5-AST-Alignment-AST.shot | 72 +++++----- .../snapshots/1-TSESTree-AST.shot | 42 ++---- .../snapshots/5-AST-Alignment-AST.shot | 55 ++++--- .../mapped/snapshots/1-TSESTree-AST.shot | 54 +++---- .../mapped/snapshots/5-AST-Alignment-AST.shot | 76 +++++----- .../ast-spec/src/type/TSMappedType/spec.ts | 6 + packages/eslint-plugin/src/rules/indent.ts | 2 + .../src/util/collectUnusedVariables.ts | 2 +- .../src/definition/TypeDefinition.ts | 1 + .../src/referencer/TypeVisitor.ts | 7 +- .../mapped-named-literal-no-references.ts | 2 + ...mapped-named-literal-no-references.ts.shot | 88 ++++++++++++ .../mapped-named-literal-referenced.ts | 2 + .../mapped-named-literal-referenced.ts.shot | 88 ++++++++++++ .../type-declaration/mapped-named.ts.shot | 4 +- .../fixtures/type-declaration/mapped.ts.shot | 4 +- packages/typescript-estree/src/convert.ts | 41 +++--- .../tests/lib/convert.test.ts | 96 ++++++++++++- packages/visitor-keys/src/visitor-keys.ts | 2 +- 26 files changed, 725 insertions(+), 459 deletions(-) create mode 100644 packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-no-references.ts create mode 100644 packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-no-references.ts.shot create mode 100644 packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-referenced.ts create mode 100644 packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-referenced.ts.shot diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-named-type/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-named-type/snapshots/1-TSESTree-AST.shot index ef00d6dd163a..57cd9d3e565c 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-named-type/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-named-type/snapshots/1-TSESTree-AST.shot @@ -21,6 +21,49 @@ Program { }, typeAnnotation: TSMappedType { type: "TSMappedType", + constraint: TSTypeOperator { + type: "TSTypeOperator", + operator: "keyof", + typeAnnotation: TSTypeReference { + type: "TSTypeReference", + typeName: Identifier { + type: "Identifier", + decorators: [], + name: "T", + optional: false, + + range: [104, 105], + loc: { + start: { column: 14, line: 4 }, + end: { column: 15, line: 4 }, + }, + }, + + range: [104, 105], + loc: { + start: { column: 14, line: 4 }, + end: { column: 15, line: 4 }, + }, + }, + + range: [98, 105], + loc: { + start: { column: 8, line: 4 }, + end: { column: 15, line: 4 }, + }, + }, + key: Identifier { + type: "Identifier", + decorators: [], + name: "P", + optional: false, + + range: [93, 94], + loc: { + start: { column: 3, line: 4 }, + end: { column: 4, line: 4 }, + }, + }, nameType: TSLiteralType { type: "TSLiteralType", literal: Literal { @@ -92,61 +135,6 @@ Program { end: { column: 29, line: 4 }, }, }, - typeParameter: TSTypeParameter { - type: "TSTypeParameter", - const: false, - constraint: TSTypeOperator { - type: "TSTypeOperator", - operator: "keyof", - typeAnnotation: TSTypeReference { - type: "TSTypeReference", - typeName: Identifier { - type: "Identifier", - decorators: [], - name: "T", - optional: false, - - range: [104, 105], - loc: { - start: { column: 14, line: 4 }, - end: { column: 15, line: 4 }, - }, - }, - - range: [104, 105], - loc: { - start: { column: 14, line: 4 }, - end: { column: 15, line: 4 }, - }, - }, - - range: [98, 105], - loc: { - start: { column: 8, line: 4 }, - end: { column: 15, line: 4 }, - }, - }, - in: false, - name: Identifier { - type: "Identifier", - decorators: [], - name: "P", - optional: false, - - range: [93, 94], - loc: { - start: { column: 3, line: 4 }, - end: { column: 4, line: 4 }, - }, - }, - out: false, - - range: [93, 105], - loc: { - start: { column: 3, line: 4 }, - end: { column: 15, line: 4 }, - }, - }, range: [88, 122], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-named-type/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-named-type/snapshots/5-AST-Alignment-AST.shot index 368c485e82da..3c7484cb3764 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-named-type/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-named-type/snapshots/5-AST-Alignment-AST.shot @@ -25,6 +25,49 @@ exports[`AST Fixtures legacy-fixtures types mapped-named-type AST Alignment - AS }, typeAnnotation: TSMappedType { type: 'TSMappedType', +- constraint: TSTypeOperator { +- type: 'TSTypeOperator', +- operator: 'keyof', +- typeAnnotation: TSTypeReference { +- type: 'TSTypeReference', +- typeName: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'T', +- optional: false, +- +- range: [104, 105], +- loc: { +- start: { column: 14, line: 4 }, +- end: { column: 15, line: 4 }, +- }, +- }, +- +- range: [104, 105], +- loc: { +- start: { column: 14, line: 4 }, +- end: { column: 15, line: 4 }, +- }, +- }, +- +- range: [98, 105], +- loc: { +- start: { column: 8, line: 4 }, +- end: { column: 15, line: 4 }, +- }, +- }, +- key: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'P', +- optional: false, +- +- range: [93, 94], +- loc: { +- start: { column: 3, line: 4 }, +- end: { column: 4, line: 4 }, +- }, +- }, nameType: TSLiteralType { type: 'TSLiteralType', literal: Literal { @@ -96,62 +139,45 @@ exports[`AST Fixtures legacy-fixtures types mapped-named-type AST Alignment - AS end: { column: 29, line: 4 }, }, }, - typeParameter: TSTypeParameter { - type: 'TSTypeParameter', -- const: false, - constraint: TSTypeOperator { - type: 'TSTypeOperator', - operator: 'keyof', - typeAnnotation: TSTypeReference { - type: 'TSTypeReference', - typeName: Identifier { - type: 'Identifier', -- decorators: Array [], - name: 'T', -- optional: false, - - range: [104, 105], - loc: { - start: { column: 14, line: 4 }, - end: { column: 15, line: 4 }, - }, - }, - - range: [104, 105], - loc: { - start: { column: 14, line: 4 }, - end: { column: 15, line: 4 }, - }, - }, - - range: [98, 105], - loc: { - start: { column: 8, line: 4 }, - end: { column: 15, line: 4 }, - }, - }, -- in: false, -- name: Identifier { -- type: 'Identifier', -- decorators: Array [], -- name: 'P', -- optional: false, -- -- range: [93, 94], -- loc: { -- start: { column: 3, line: 4 }, -- end: { column: 4, line: 4 }, -- }, -- }, -- out: false, ++ typeParameter: TSTypeParameter { ++ type: 'TSTypeParameter', ++ constraint: TSTypeOperator { ++ type: 'TSTypeOperator', ++ operator: 'keyof', ++ typeAnnotation: TSTypeReference { ++ type: 'TSTypeReference', ++ typeName: Identifier { ++ type: 'Identifier', ++ name: 'T', ++ ++ range: [104, 105], ++ loc: { ++ start: { column: 14, line: 4 }, ++ end: { column: 15, line: 4 }, ++ }, ++ }, ++ ++ range: [104, 105], ++ loc: { ++ start: { column: 14, line: 4 }, ++ end: { column: 15, line: 4 }, ++ }, ++ }, ++ ++ range: [98, 105], ++ loc: { ++ start: { column: 8, line: 4 }, ++ end: { column: 15, line: 4 }, ++ }, ++ }, + name: 'P', - - range: [93, 105], - loc: { - start: { column: 3, line: 4 }, - end: { column: 15, line: 4 }, - }, - }, ++ ++ range: [93, 105], ++ loc: { ++ start: { column: 3, line: 4 }, ++ end: { column: 15, line: 4 }, ++ }, ++ }, range: [88, 122], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-minus/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-minus/snapshots/1-TSESTree-AST.shot index 5e7781c3ef65..3ddeb8b5a60c 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-minus/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-minus/snapshots/1-TSESTree-AST.shot @@ -19,6 +19,27 @@ Program { type: "TSTypeAnnotation", typeAnnotation: TSMappedType { type: "TSMappedType", + constraint: TSStringKeyword { + type: "TSStringKeyword", + + range: [100, 106], + loc: { + start: { column: 27, line: 3 }, + end: { column: 33, line: 3 }, + }, + }, + key: Identifier { + type: "Identifier", + decorators: [], + name: "P", + optional: false, + + range: [95, 96], + loc: { + start: { column: 22, line: 3 }, + end: { column: 23, line: 3 }, + }, + }, nameType: null, optional: "-", readonly: "-", @@ -31,39 +52,6 @@ Program { end: { column: 44, line: 3 }, }, }, - typeParameter: TSTypeParameter { - type: "TSTypeParameter", - const: false, - constraint: TSStringKeyword { - type: "TSStringKeyword", - - range: [100, 106], - loc: { - start: { column: 27, line: 3 }, - end: { column: 33, line: 3 }, - }, - }, - in: false, - name: Identifier { - type: "Identifier", - decorators: [], - name: "P", - optional: false, - - range: [95, 96], - loc: { - start: { column: 22, line: 3 }, - end: { column: 23, line: 3 }, - }, - }, - out: false, - - range: [95, 106], - loc: { - start: { column: 22, line: 3 }, - end: { column: 33, line: 3 }, - }, - }, range: [82, 119], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-minus/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-minus/snapshots/5-AST-Alignment-AST.shot index 5a1e7b20c516..be92a34ed653 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-minus/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-minus/snapshots/5-AST-Alignment-AST.shot @@ -23,6 +23,27 @@ exports[`AST Fixtures legacy-fixtures types mapped-readonly-minus AST Alignment type: 'TSTypeAnnotation', typeAnnotation: TSMappedType { type: 'TSMappedType', +- constraint: TSStringKeyword { +- type: 'TSStringKeyword', +- +- range: [100, 106], +- loc: { +- start: { column: 27, line: 3 }, +- end: { column: 33, line: 3 }, +- }, +- }, +- key: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'P', +- optional: false, +- +- range: [95, 96], +- loc: { +- start: { column: 22, line: 3 }, +- end: { column: 23, line: 3 }, +- }, +- }, nameType: null, optional: '-', readonly: '-', @@ -35,41 +56,26 @@ exports[`AST Fixtures legacy-fixtures types mapped-readonly-minus AST Alignment end: { column: 44, line: 3 }, }, }, - typeParameter: TSTypeParameter { - type: 'TSTypeParameter', -- const: false, - constraint: TSStringKeyword { - type: 'TSStringKeyword', ++ typeParameter: TSTypeParameter { ++ type: 'TSTypeParameter', ++ constraint: TSStringKeyword { ++ type: 'TSStringKeyword', - range: [100, 106], - loc: { - start: { column: 27, line: 3 }, - end: { column: 33, line: 3 }, - }, - }, -- in: false, -- name: Identifier { -- type: 'Identifier', -- decorators: Array [], -- name: 'P', -- optional: false, ++ range: [100, 106], ++ loc: { ++ start: { column: 27, line: 3 }, ++ end: { column: 33, line: 3 }, ++ }, ++ }, + name: 'P', - -- range: [95, 96], -- loc: { -- start: { column: 22, line: 3 }, -- end: { column: 23, line: 3 }, -- }, -- }, -- out: false, -- - range: [95, 106], - loc: { - start: { column: 22, line: 3 }, - end: { column: 33, line: 3 }, - }, - }, - ++ ++ range: [95, 106], ++ loc: { ++ start: { column: 22, line: 3 }, ++ end: { column: 33, line: 3 }, ++ }, ++ }, ++ range: [82, 119], loc: { start: { column: 9, line: 3 }, diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-plus/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-plus/snapshots/1-TSESTree-AST.shot index fcdef4d6c306..0f3690a03bbb 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-plus/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-plus/snapshots/1-TSESTree-AST.shot @@ -19,6 +19,27 @@ Program { type: "TSTypeAnnotation", typeAnnotation: TSMappedType { type: "TSMappedType", + constraint: TSStringKeyword { + type: "TSStringKeyword", + + range: [100, 106], + loc: { + start: { column: 27, line: 3 }, + end: { column: 33, line: 3 }, + }, + }, + key: Identifier { + type: "Identifier", + decorators: [], + name: "P", + optional: false, + + range: [95, 96], + loc: { + start: { column: 22, line: 3 }, + end: { column: 23, line: 3 }, + }, + }, nameType: null, optional: "+", readonly: "+", @@ -31,39 +52,6 @@ Program { end: { column: 44, line: 3 }, }, }, - typeParameter: TSTypeParameter { - type: "TSTypeParameter", - const: false, - constraint: TSStringKeyword { - type: "TSStringKeyword", - - range: [100, 106], - loc: { - start: { column: 27, line: 3 }, - end: { column: 33, line: 3 }, - }, - }, - in: false, - name: Identifier { - type: "Identifier", - decorators: [], - name: "P", - optional: false, - - range: [95, 96], - loc: { - start: { column: 22, line: 3 }, - end: { column: 23, line: 3 }, - }, - }, - out: false, - - range: [95, 106], - loc: { - start: { column: 22, line: 3 }, - end: { column: 33, line: 3 }, - }, - }, range: [82, 119], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-plus/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-plus/snapshots/5-AST-Alignment-AST.shot index 6beae1721224..9dd0ad409179 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-plus/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly-plus/snapshots/5-AST-Alignment-AST.shot @@ -23,6 +23,27 @@ exports[`AST Fixtures legacy-fixtures types mapped-readonly-plus AST Alignment - type: 'TSTypeAnnotation', typeAnnotation: TSMappedType { type: 'TSMappedType', +- constraint: TSStringKeyword { +- type: 'TSStringKeyword', +- +- range: [100, 106], +- loc: { +- start: { column: 27, line: 3 }, +- end: { column: 33, line: 3 }, +- }, +- }, +- key: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'P', +- optional: false, +- +- range: [95, 96], +- loc: { +- start: { column: 22, line: 3 }, +- end: { column: 23, line: 3 }, +- }, +- }, nameType: null, optional: '+', readonly: '+', @@ -35,41 +56,26 @@ exports[`AST Fixtures legacy-fixtures types mapped-readonly-plus AST Alignment - end: { column: 44, line: 3 }, }, }, - typeParameter: TSTypeParameter { - type: 'TSTypeParameter', -- const: false, - constraint: TSStringKeyword { - type: 'TSStringKeyword', ++ typeParameter: TSTypeParameter { ++ type: 'TSTypeParameter', ++ constraint: TSStringKeyword { ++ type: 'TSStringKeyword', - range: [100, 106], - loc: { - start: { column: 27, line: 3 }, - end: { column: 33, line: 3 }, - }, - }, -- in: false, -- name: Identifier { -- type: 'Identifier', -- decorators: Array [], -- name: 'P', -- optional: false, ++ range: [100, 106], ++ loc: { ++ start: { column: 27, line: 3 }, ++ end: { column: 33, line: 3 }, ++ }, ++ }, + name: 'P', - -- range: [95, 96], -- loc: { -- start: { column: 22, line: 3 }, -- end: { column: 23, line: 3 }, -- }, -- }, -- out: false, -- - range: [95, 106], - loc: { - start: { column: 22, line: 3 }, - end: { column: 33, line: 3 }, - }, - }, - ++ ++ range: [95, 106], ++ loc: { ++ start: { column: 22, line: 3 }, ++ end: { column: 33, line: 3 }, ++ }, ++ }, ++ range: [82, 119], loc: { start: { column: 9, line: 3 }, diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly/snapshots/1-TSESTree-AST.shot index f70b1ed6956b..c28ad65a923f 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly/snapshots/1-TSESTree-AST.shot @@ -19,6 +19,27 @@ Program { type: "TSTypeAnnotation", typeAnnotation: TSMappedType { type: "TSMappedType", + constraint: TSStringKeyword { + type: "TSStringKeyword", + + range: [99, 105], + loc: { + start: { column: 26, line: 3 }, + end: { column: 32, line: 3 }, + }, + }, + key: Identifier { + type: "Identifier", + decorators: [], + name: "P", + optional: false, + + range: [94, 95], + loc: { + start: { column: 21, line: 3 }, + end: { column: 22, line: 3 }, + }, + }, nameType: null, optional: true, readonly: true, @@ -31,39 +52,6 @@ Program { end: { column: 42, line: 3 }, }, }, - typeParameter: TSTypeParameter { - type: "TSTypeParameter", - const: false, - constraint: TSStringKeyword { - type: "TSStringKeyword", - - range: [99, 105], - loc: { - start: { column: 26, line: 3 }, - end: { column: 32, line: 3 }, - }, - }, - in: false, - name: Identifier { - type: "Identifier", - decorators: [], - name: "P", - optional: false, - - range: [94, 95], - loc: { - start: { column: 21, line: 3 }, - end: { column: 22, line: 3 }, - }, - }, - out: false, - - range: [94, 105], - loc: { - start: { column: 21, line: 3 }, - end: { column: 32, line: 3 }, - }, - }, range: [82, 117], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly/snapshots/5-AST-Alignment-AST.shot index 8c8500647fd6..af26bcceb36b 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-readonly/snapshots/5-AST-Alignment-AST.shot @@ -23,6 +23,27 @@ exports[`AST Fixtures legacy-fixtures types mapped-readonly AST Alignment - AST type: 'TSTypeAnnotation', typeAnnotation: TSMappedType { type: 'TSMappedType', +- constraint: TSStringKeyword { +- type: 'TSStringKeyword', +- +- range: [99, 105], +- loc: { +- start: { column: 26, line: 3 }, +- end: { column: 32, line: 3 }, +- }, +- }, +- key: Identifier { +- type: 'Identifier', +- decorators: Array [], +- name: 'P', +- optional: false, +- +- range: [94, 95], +- loc: { +- start: { column: 21, line: 3 }, +- end: { column: 22, line: 3 }, +- }, +- }, nameType: null, optional: true, readonly: true, @@ -35,41 +56,26 @@ exports[`AST Fixtures legacy-fixtures types mapped-readonly AST Alignment - AST end: { column: 42, line: 3 }, }, }, - typeParameter: TSTypeParameter { - type: 'TSTypeParameter', -- const: false, - constraint: TSStringKeyword { - type: 'TSStringKeyword', ++ typeParameter: TSTypeParameter { ++ type: 'TSTypeParameter', ++ constraint: TSStringKeyword { ++ type: 'TSStringKeyword', - range: [99, 105], - loc: { - start: { column: 26, line: 3 }, - end: { column: 32, line: 3 }, - }, - }, -- in: false, -- name: Identifier { -- type: 'Identifier', -- decorators: Array [], -- name: 'P', -- optional: false, ++ range: [99, 105], ++ loc: { ++ start: { column: 26, line: 3 }, ++ end: { column: 32, line: 3 }, ++ }, ++ }, + name: 'P', - -- range: [94, 95], -- loc: { -- start: { column: 21, line: 3 }, -- end: { column: 22, line: 3 }, -- }, -- }, -- out: false, -- - range: [94, 105], - loc: { - start: { column: 21, line: 3 }, - end: { column: 32, line: 3 }, - }, - }, - ++ ++ range: [94, 105], ++ loc: { ++ start: { column: 21, line: 3 }, ++ end: { column: 32, line: 3 }, ++ }, ++ }, ++ range: [82, 117], loc: { start: { column: 9, line: 3 }, diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-untypped/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-untypped/snapshots/1-TSESTree-AST.shot index d57cfcd0f424..09ca20f18bf2 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-untypped/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-untypped/snapshots/1-TSESTree-AST.shot @@ -19,40 +19,28 @@ Program { type: "TSTypeAnnotation", typeAnnotation: TSMappedType { type: "TSMappedType", - nameType: null, - typeParameter: TSTypeParameter { - type: "TSTypeParameter", - const: false, - constraint: TSStringKeyword { - type: "TSStringKeyword", - - range: [90, 96], - loc: { - start: { column: 17, line: 3 }, - end: { column: 23, line: 3 }, - }, - }, - in: false, - name: Identifier { - type: "Identifier", - decorators: [], - name: "P", - optional: false, + constraint: TSStringKeyword { + type: "TSStringKeyword", - range: [85, 86], - loc: { - start: { column: 12, line: 3 }, - end: { column: 13, line: 3 }, - }, + range: [90, 96], + loc: { + start: { column: 17, line: 3 }, + end: { column: 23, line: 3 }, }, - out: false, + }, + key: Identifier { + type: "Identifier", + decorators: [], + name: "P", + optional: false, - range: [85, 96], + range: [85, 86], loc: { start: { column: 12, line: 3 }, - end: { column: 23, line: 3 }, + end: { column: 13, line: 3 }, }, }, + nameType: null, range: [82, 99], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-untypped/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-untypped/snapshots/5-AST-Alignment-AST.shot index 5af936438b5b..7dd7abd04846 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-untypped/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped-untypped/snapshots/5-AST-Alignment-AST.shot @@ -23,41 +23,40 @@ exports[`AST Fixtures legacy-fixtures types mapped-untypped AST Alignment - AST type: 'TSTypeAnnotation', typeAnnotation: TSMappedType { type: 'TSMappedType', - nameType: null, - typeParameter: TSTypeParameter { - type: 'TSTypeParameter', -- const: false, - constraint: TSStringKeyword { - type: 'TSStringKeyword', +- constraint: TSStringKeyword { +- type: 'TSStringKeyword', ++ nameType: null, ++ typeParameter: TSTypeParameter { ++ type: 'TSTypeParameter', ++ constraint: TSStringKeyword { ++ type: 'TSStringKeyword', - range: [90, 96], - loc: { - start: { column: 17, line: 3 }, - end: { column: 23, line: 3 }, - }, +- range: [90, 96], +- loc: { +- start: { column: 17, line: 3 }, +- end: { column: 23, line: 3 }, ++ range: [90, 96], ++ loc: { ++ start: { column: 17, line: 3 }, ++ end: { column: 23, line: 3 }, ++ }, }, -- in: false, -- name: Identifier { -- type: 'Identifier', -- decorators: Array [], -- name: 'P', -- optional: false, -+ name: 'P', +- }, +- key: Identifier { +- type: 'Identifier', +- decorators: Array [], + name: 'P', +- optional: false, -- range: [85, 86], -- loc: { -- start: { column: 12, line: 3 }, -- end: { column: 13, line: 3 }, -- }, -- }, -- out: false, -- - range: [85, 96], +- range: [85, 86], ++ range: [85, 96], loc: { start: { column: 12, line: 3 }, - end: { column: 23, line: 3 }, +- end: { column: 13, line: 3 }, ++ end: { column: 23, line: 3 }, }, }, +- nameType: null, range: [82, 99], loc: { diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped/snapshots/1-TSESTree-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped/snapshots/1-TSESTree-AST.shot index 82119bfda8e2..98ccc34fe16a 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped/snapshots/1-TSESTree-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped/snapshots/1-TSESTree-AST.shot @@ -19,47 +19,35 @@ Program { type: "TSTypeAnnotation", typeAnnotation: TSMappedType { type: "TSMappedType", - nameType: null, - typeAnnotation: TSNumberKeyword { - type: "TSNumberKeyword", + constraint: TSStringKeyword { + type: "TSStringKeyword", - range: [99, 105], + range: [90, 96], loc: { - start: { column: 26, line: 3 }, - end: { column: 32, line: 3 }, + start: { column: 17, line: 3 }, + end: { column: 23, line: 3 }, }, }, - typeParameter: TSTypeParameter { - type: "TSTypeParameter", - const: false, - constraint: TSStringKeyword { - type: "TSStringKeyword", + key: Identifier { + type: "Identifier", + decorators: [], + name: "P", + optional: false, - range: [90, 96], - loc: { - start: { column: 17, line: 3 }, - end: { column: 23, line: 3 }, - }, - }, - in: false, - name: Identifier { - type: "Identifier", - decorators: [], - name: "P", - optional: false, - - range: [85, 86], - loc: { - start: { column: 12, line: 3 }, - end: { column: 13, line: 3 }, - }, + range: [85, 86], + loc: { + start: { column: 12, line: 3 }, + end: { column: 13, line: 3 }, }, - out: false, + }, + nameType: null, + typeAnnotation: TSNumberKeyword { + type: "TSNumberKeyword", - range: [85, 96], + range: [99, 105], loc: { - start: { column: 12, line: 3 }, - end: { column: 23, line: 3 }, + start: { column: 26, line: 3 }, + end: { column: 32, line: 3 }, }, }, diff --git a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped/snapshots/5-AST-Alignment-AST.shot b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped/snapshots/5-AST-Alignment-AST.shot index 2fcdfabe0986..62e39e79d426 100644 --- a/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped/snapshots/5-AST-Alignment-AST.shot +++ b/packages/ast-spec/src/legacy-fixtures/types/fixtures/mapped/snapshots/5-AST-Alignment-AST.shot @@ -23,48 +23,54 @@ exports[`AST Fixtures legacy-fixtures types mapped AST Alignment - AST 1`] = ` type: 'TSTypeAnnotation', typeAnnotation: TSMappedType { type: 'TSMappedType', - nameType: null, - typeAnnotation: TSNumberKeyword { - type: 'TSNumberKeyword', +- constraint: TSStringKeyword { +- type: 'TSStringKeyword', ++ nameType: null, ++ typeAnnotation: TSNumberKeyword { ++ type: 'TSNumberKeyword', - range: [99, 105], +- range: [90, 96], ++ range: [99, 105], loc: { - start: { column: 26, line: 3 }, - end: { column: 32, line: 3 }, +- start: { column: 17, line: 3 }, +- end: { column: 23, line: 3 }, ++ start: { column: 26, line: 3 }, ++ end: { column: 32, line: 3 }, }, }, - typeParameter: TSTypeParameter { - type: 'TSTypeParameter', -- const: false, - constraint: TSStringKeyword { - type: 'TSStringKeyword', +- key: Identifier { +- type: 'Identifier', +- decorators: Array [], ++ typeParameter: TSTypeParameter { ++ type: 'TSTypeParameter', ++ constraint: TSStringKeyword { ++ type: 'TSStringKeyword', ++ ++ range: [90, 96], ++ loc: { ++ start: { column: 17, line: 3 }, ++ end: { column: 23, line: 3 }, ++ }, ++ }, + name: 'P', +- optional: false, - range: [90, 96], - loc: { - start: { column: 17, line: 3 }, - end: { column: 23, line: 3 }, - }, - }, -- in: false, -- name: Identifier { -- type: 'Identifier', -- decorators: Array [], -- name: 'P', -- optional: false, -+ name: 'P', - -- range: [85, 86], -- loc: { -- start: { column: 12, line: 3 }, -- end: { column: 13, line: 3 }, -- }, -- }, -- out: false, -- - range: [85, 96], +- range: [85, 86], ++ range: [85, 96], loc: { start: { column: 12, line: 3 }, - end: { column: 23, line: 3 }, +- end: { column: 13, line: 3 }, +- }, +- }, +- nameType: null, +- typeAnnotation: TSNumberKeyword { +- type: 'TSNumberKeyword', +- +- range: [99, 105], +- loc: { +- start: { column: 26, line: 3 }, +- end: { column: 32, line: 3 }, ++ end: { column: 23, line: 3 }, }, }, diff --git a/packages/ast-spec/src/type/TSMappedType/spec.ts b/packages/ast-spec/src/type/TSMappedType/spec.ts index 8b81c4f77bb6..44f64ac8621a 100644 --- a/packages/ast-spec/src/type/TSMappedType/spec.ts +++ b/packages/ast-spec/src/type/TSMappedType/spec.ts @@ -1,11 +1,17 @@ import type { AST_NODE_TYPES } from '../../ast-node-types'; import type { BaseNode } from '../../base/BaseNode'; +import type { Identifier } from '../../expression/Identifier/spec'; import type { TSTypeParameter } from '../../special/TSTypeParameter/spec'; import type { TypeNode } from '../../unions/TypeNode'; export interface TSMappedType extends BaseNode { type: AST_NODE_TYPES.TSMappedType; + + /** @deprecated Use {@link `constraint`} and {@link `key`} instead. */ typeParameter: TSTypeParameter; + + constraint: TypeNode; + key: Identifier; readonly: boolean | '-' | '+' | undefined; optional: boolean | '-' | '+' | undefined; typeAnnotation: TypeNode | undefined; diff --git a/packages/eslint-plugin/src/rules/indent.ts b/packages/eslint-plugin/src/rules/indent.ts index 544e3dbdc826..8fc8a4191a9c 100644 --- a/packages/eslint-plugin/src/rules/indent.ts +++ b/packages/eslint-plugin/src/rules/indent.ts @@ -383,6 +383,7 @@ export default createRule({ TSMappedType(node: TSESTree.TSMappedType) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const squareBracketStart = context.sourceCode.getTokenBefore( + // eslint-disable-next-line deprecation/deprecation node.typeParameter, )!; @@ -393,6 +394,7 @@ export default createRule({ { parent: node, type: AST_NODE_TYPES.Property, + // eslint-disable-next-line deprecation/deprecation key: node.typeParameter as any, value: node.typeAnnotation as any, diff --git a/packages/eslint-plugin/src/util/collectUnusedVariables.ts b/packages/eslint-plugin/src/util/collectUnusedVariables.ts index 54a4e91750a4..443167a3e3c0 100644 --- a/packages/eslint-plugin/src/util/collectUnusedVariables.ts +++ b/packages/eslint-plugin/src/util/collectUnusedVariables.ts @@ -317,7 +317,7 @@ class UnusedVarsVisitor< protected TSMappedType(node: TSESTree.TSMappedType): void { // mapped types create a variable for their type name, but it's not necessary to reference it, // so we shouldn't consider it as unused for the purpose of this rule. - this.markVariableAsUsed(node.typeParameter.name); + this.markVariableAsUsed(node.key); } protected TSMethodSignature = this.visitFunctionTypeSignature; diff --git a/packages/scope-manager/src/definition/TypeDefinition.ts b/packages/scope-manager/src/definition/TypeDefinition.ts index 506274d42734..75de333bb0ad 100644 --- a/packages/scope-manager/src/definition/TypeDefinition.ts +++ b/packages/scope-manager/src/definition/TypeDefinition.ts @@ -6,6 +6,7 @@ import { DefinitionType } from './DefinitionType'; class TypeDefinition extends DefinitionBase< DefinitionType.Type, | TSESTree.TSInterfaceDeclaration + | TSESTree.TSMappedType | TSESTree.TSTypeAliasDeclaration | TSESTree.TSTypeParameter, null, diff --git a/packages/scope-manager/src/referencer/TypeVisitor.ts b/packages/scope-manager/src/referencer/TypeVisitor.ts index 778b2d673336..ab3b8a0b1bf4 100644 --- a/packages/scope-manager/src/referencer/TypeVisitor.ts +++ b/packages/scope-manager/src/referencer/TypeVisitor.ts @@ -199,7 +199,12 @@ class TypeVisitor extends Visitor { protected TSMappedType(node: TSESTree.TSMappedType): void { // mapped types key can only be referenced within their return value this.#referencer.scopeManager.nestMappedTypeScope(node); - this.visitChildren(node); + this.#referencer + .currentScope() + .defineIdentifier(node.key, new TypeDefinition(node.key, node)); + this.visit(node.constraint); + this.visit(node.nameType); + this.visit(node.typeAnnotation); this.#referencer.close(node); } diff --git a/packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-no-references.ts b/packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-no-references.ts new file mode 100644 index 000000000000..3ae8ec6a9176 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-no-references.ts @@ -0,0 +1,2 @@ +type T = 1; +type M = { [T in string as T]: 1 }; diff --git a/packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-no-references.ts.shot b/packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-no-references.ts.shot new file mode 100644 index 000000000000..cbb18cc721b7 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-no-references.ts.shot @@ -0,0 +1,88 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`type-declaration mapped-named-literal-no-references 1`] = ` +ScopeManager { + variables: [ + ImplicitGlobalConstTypeVariable, + Variable$2 { + defs: [ + TypeDefinition$1 { + name: Identifier<"T">, + node: TSTypeAliasDeclaration$1, + }, + ], + name: "T", + references: [], + isValueVariable: false, + isTypeVariable: true, + }, + Variable$3 { + defs: [ + TypeDefinition$2 { + name: Identifier<"M">, + node: TSTypeAliasDeclaration$2, + }, + ], + name: "M", + references: [], + isValueVariable: false, + isTypeVariable: true, + }, + Variable$4 { + defs: [ + TypeDefinition$3 { + name: Identifier<"T">, + node: TSMappedType$3, + }, + ], + name: "T", + references: [ + Reference$1 { + identifier: Identifier<"T">, + isRead: true, + isTypeReference: true, + isValueReference: false, + isWrite: false, + resolved: Variable$4, + }, + ], + isValueVariable: false, + isTypeVariable: true, + }, + ], + scopes: [ + GlobalScope$1 { + block: Program$4, + isStrict: false, + references: [], + set: Map { + "const" => ImplicitGlobalConstTypeVariable, + "T" => Variable$2, + "M" => Variable$3, + }, + type: "global", + upper: null, + variables: [ + ImplicitGlobalConstTypeVariable, + Variable$2, + Variable$3, + ], + }, + MappedTypeScope$2 { + block: TSMappedType$3, + isStrict: true, + references: [ + Reference$1, + ], + set: Map { + "T" => Variable$4, + }, + type: "mappedType", + upper: GlobalScope$1, + variables: [ + Variable$4, + ], + }, + ], +} +`; diff --git a/packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-referenced.ts b/packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-referenced.ts new file mode 100644 index 000000000000..a7b4c95de066 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-referenced.ts @@ -0,0 +1,2 @@ +type T = 1; // should have 1 reference +type M = { [K in string as T]: 1 }; diff --git a/packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-referenced.ts.shot b/packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-referenced.ts.shot new file mode 100644 index 000000000000..c1b6bd45a180 --- /dev/null +++ b/packages/scope-manager/tests/fixtures/type-declaration/mapped-named-literal-referenced.ts.shot @@ -0,0 +1,88 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`type-declaration mapped-named-literal-referenced 1`] = ` +ScopeManager { + variables: [ + ImplicitGlobalConstTypeVariable, + Variable$2 { + defs: [ + TypeDefinition$1 { + name: Identifier<"T">, + node: TSTypeAliasDeclaration$1, + }, + ], + name: "T", + references: [ + Reference$1 { + identifier: Identifier<"T">, + isRead: true, + isTypeReference: true, + isValueReference: false, + isWrite: false, + resolved: Variable$2, + }, + ], + isValueVariable: false, + isTypeVariable: true, + }, + Variable$3 { + defs: [ + TypeDefinition$2 { + name: Identifier<"M">, + node: TSTypeAliasDeclaration$2, + }, + ], + name: "M", + references: [], + isValueVariable: false, + isTypeVariable: true, + }, + Variable$4 { + defs: [ + TypeDefinition$3 { + name: Identifier<"K">, + node: TSMappedType$3, + }, + ], + name: "K", + references: [], + isValueVariable: false, + isTypeVariable: true, + }, + ], + scopes: [ + GlobalScope$1 { + block: Program$4, + isStrict: false, + references: [], + set: Map { + "const" => ImplicitGlobalConstTypeVariable, + "T" => Variable$2, + "M" => Variable$3, + }, + type: "global", + upper: null, + variables: [ + ImplicitGlobalConstTypeVariable, + Variable$2, + Variable$3, + ], + }, + MappedTypeScope$2 { + block: TSMappedType$3, + isStrict: true, + references: [ + Reference$1, + ], + set: Map { + "K" => Variable$4, + }, + type: "mappedType", + upper: GlobalScope$1, + variables: [ + Variable$4, + ], + }, + ], +} +`; diff --git a/packages/scope-manager/tests/fixtures/type-declaration/mapped-named.ts.shot b/packages/scope-manager/tests/fixtures/type-declaration/mapped-named.ts.shot index c5e5927384fc..11aa7739cb76 100644 --- a/packages/scope-manager/tests/fixtures/type-declaration/mapped-named.ts.shot +++ b/packages/scope-manager/tests/fixtures/type-declaration/mapped-named.ts.shot @@ -41,7 +41,7 @@ ScopeManager { defs: [ TypeDefinition$3 { name: Identifier<"k">, - node: TSTypeParameter$3, + node: TSMappedType$3, }, ], name: "k", @@ -95,7 +95,7 @@ ScopeManager { ], }, MappedTypeScope$2 { - block: TSMappedType$5, + block: TSMappedType$3, isStrict: true, references: [ Reference$2, diff --git a/packages/scope-manager/tests/fixtures/type-declaration/mapped.ts.shot b/packages/scope-manager/tests/fixtures/type-declaration/mapped.ts.shot index 6790ca3dd92c..59aab2c5e1de 100644 --- a/packages/scope-manager/tests/fixtures/type-declaration/mapped.ts.shot +++ b/packages/scope-manager/tests/fixtures/type-declaration/mapped.ts.shot @@ -41,7 +41,7 @@ ScopeManager { defs: [ TypeDefinition$3 { name: Identifier<"k">, - node: TSTypeParameter$3, + node: TSMappedType$3, }, ], name: "k", @@ -87,7 +87,7 @@ ScopeManager { ], }, MappedTypeScope$2 { - block: TSMappedType$5, + block: TSMappedType$3, isStrict: true, references: [ Reference$2, diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index c2cf7c2c8bb2..5ffe6008d7d2 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -2639,20 +2639,29 @@ export class Converter { ); } - return this.createNode(node, { - type: AST_NODE_TYPES.TSMappedType, - nameType: this.convertChild(node.nameType) ?? null, - optional: - node.questionToken && - (node.questionToken.kind === SyntaxKind.QuestionToken || - getTextForTokenKind(node.questionToken.kind)), - readonly: - node.readonlyToken && - (node.readonlyToken.kind === SyntaxKind.ReadonlyKeyword || - getTextForTokenKind(node.readonlyToken.kind)), - typeAnnotation: node.type && this.convertChild(node.type), - typeParameter: this.convertChild(node.typeParameter), - }); + return this.createNode( + node, + this.#withDeprecatedGetter( + { + type: AST_NODE_TYPES.TSMappedType, + constraint: this.convertChild(node.typeParameter.constraint), + key: this.convertChild(node.typeParameter.name), + nameType: this.convertChild(node.nameType) ?? null, + optional: + node.questionToken && + (node.questionToken.kind === SyntaxKind.QuestionToken || + getTextForTokenKind(node.questionToken.kind)), + readonly: + node.readonlyToken && + (node.readonlyToken.kind === SyntaxKind.ReadonlyKeyword || + getTextForTokenKind(node.readonlyToken.kind)), + typeAnnotation: node.type && this.convertChild(node.type), + }, + 'typeParameter', + "'constraint' and 'key'", + this.convertChild(node.typeParameter), + ), + ); } case SyntaxKind.ParenthesizedExpression: @@ -2912,7 +2921,7 @@ export class Converter { id: this.convertChild(node.name), }, 'members', - 'body.members', + `'body.members'`, node.members.map(el => this.convertChild(el)), ), ); @@ -3550,7 +3559,7 @@ export class Converter { : (): Value => { if (!warned) { process.emitWarning( - `The '${deprecatedKey}' property is deprecated on ${node.type} nodes. Use '${preferredKey}' instead. See https://typescript-eslint.io/linting/troubleshooting#the-key-property-is-deprecated-on-type-nodes-use-key-instead-warnings.`, + `The '${deprecatedKey}' property is deprecated on ${node.type} nodes. Use ${preferredKey} instead. See https://typescript-eslint.io/linting/troubleshooting#the-key-property-is-deprecated-on-type-nodes-use-key-instead-warnings.`, 'DeprecationWarning', ); warned = true; diff --git a/packages/typescript-estree/tests/lib/convert.test.ts b/packages/typescript-estree/tests/lib/convert.test.ts index 9988c969b6a7..640a3d9946ab 100644 --- a/packages/typescript-estree/tests/lib/convert.test.ts +++ b/packages/typescript-estree/tests/lib/convert.test.ts @@ -297,7 +297,29 @@ describe('convert', () => { return maps.tsNodeToESTreeNodeMap.get(tsCallExpression); }; - it('warns on a deprecated property access when suppressDeprecatedPropertyWarnings is false', () => { + const getTsMappedType = ( + converterOptions?: ConverterOptions, + ): TSESTree.TSMappedType => { + const ast = convertCode(` + type MappedType = { + [Key in Type]: Value; + }; + `); + const tsMappedType = (ast.statements[0] as ts.TypeAliasDeclaration) + .type as ts.MappedTypeNode; + const instance = new Converter(ast, { + shouldPreserveNodeMaps: true, + ...converterOptions, + }); + + instance.convertProgram(); + + const maps = instance.getASTMaps(); + + return maps.tsNodeToESTreeNodeMap.get(tsMappedType); + }; + + it('warns on a deprecated aliased property access when suppressDeprecatedPropertyWarnings is false', () => { const emitWarning = jest .spyOn(process, 'emitWarning') .mockImplementation(); @@ -314,7 +336,7 @@ describe('convert', () => { ); }); - it('does not warn on a subsequent deprecated property access when suppressDeprecatedPropertyWarnings is false', () => { + it('does not warn on a subsequent deprecated aliased property access when suppressDeprecatedPropertyWarnings is false', () => { const emitWarning = jest .spyOn(process, 'emitWarning') .mockImplementation(); @@ -330,7 +352,7 @@ describe('convert', () => { expect(emitWarning).toHaveBeenCalledTimes(1); }); - it('does not warn on a deprecated property access when suppressDeprecatedPropertyWarnings is true', () => { + it('does not warn on a deprecated aliased property access when suppressDeprecatedPropertyWarnings is true', () => { const emitWarning = jest .spyOn(process, 'emitWarning') .mockImplementation(); @@ -344,13 +366,13 @@ describe('convert', () => { expect(emitWarning).not.toHaveBeenCalled(); }); - it('does not allow enumeration of deprecated properties', () => { + it('does not allow enumeration of deprecated aliased properties', () => { const esCallExpression = getEsCallExpression(); expect(Object.keys(esCallExpression)).not.toContain('typeParameters'); }); - it('allows writing to the deprecated property as a new enumerable value', () => { + it('allows writing to the deprecated aliased property as a new enumerable value', () => { const esCallExpression = getEsCallExpression(); // eslint-disable-next-line deprecation/deprecation @@ -360,5 +382,69 @@ describe('convert', () => { expect(esCallExpression.typeParameters).toBeUndefined(); expect(Object.keys(esCallExpression)).toContain('typeParameters'); }); + + it('warns on a deprecated getter property access when suppressDeprecatedPropertyWarnings is false', () => { + const emitWarning = jest + .spyOn(process, 'emitWarning') + .mockImplementation(); + const tsMappedType = getTsMappedType({ + suppressDeprecatedPropertyWarnings: false, + }); + + // eslint-disable-next-line deprecation/deprecation + tsMappedType.typeParameter; + + expect(emitWarning).toHaveBeenCalledWith( + `The 'typeParameter' property is deprecated on TSMappedType nodes. Use 'constraint' and 'key' instead. See https://typescript-eslint.io/linting/troubleshooting#the-key-property-is-deprecated-on-type-nodes-use-key-instead-warnings.`, + 'DeprecationWarning', + ); + }); + + it('does not warn on a subsequent deprecated getter property access when suppressDeprecatedPropertyWarnings is false', () => { + const emitWarning = jest + .spyOn(process, 'emitWarning') + .mockImplementation(); + const tsMappedType = getTsMappedType({ + suppressDeprecatedPropertyWarnings: false, + }); + + /* eslint-disable deprecation/deprecation */ + tsMappedType.typeParameter; + tsMappedType.typeParameter; + /* eslint-enable deprecation/deprecation */ + + expect(emitWarning).toHaveBeenCalledTimes(1); + }); + + it('does not warn on a deprecated getter property access when suppressDeprecatedPropertyWarnings is true', () => { + const emitWarning = jest + .spyOn(process, 'emitWarning') + .mockImplementation(); + const tsMappedType = getTsMappedType({ + suppressDeprecatedPropertyWarnings: true, + }); + + // eslint-disable-next-line deprecation/deprecation + tsMappedType.typeParameter; + + expect(emitWarning).not.toHaveBeenCalled(); + }); + + it('does not allow enumeration of deprecated getter properties', () => { + const tsMappedType = getTsMappedType(); + + expect(Object.keys(tsMappedType)).not.toContain('typeParameter'); + }); + + it('allows writing to the deprecated getter property as a new enumerable value', () => { + const tsMappedType = getTsMappedType(); + + // eslint-disable-next-line deprecation/deprecation + tsMappedType.typeParameter = undefined!; + + // eslint-disable-next-line deprecation/deprecation + expect(tsMappedType.typeParameter).toBeUndefined(); + expect(Object.keys(tsMappedType)).toContain('typeParameter'); + }); }); }); diff --git a/packages/visitor-keys/src/visitor-keys.ts b/packages/visitor-keys/src/visitor-keys.ts index c849790dbb52..fca073dd2a59 100644 --- a/packages/visitor-keys/src/visitor-keys.ts +++ b/packages/visitor-keys/src/visitor-keys.ts @@ -225,7 +225,7 @@ const additionalKeys: AdditionalKeys = { TSIntersectionType: ['types'], TSIntrinsicKeyword: [], TSLiteralType: ['literal'], - TSMappedType: ['nameType', 'typeParameter', 'typeAnnotation'], + TSMappedType: ['key', 'constraint', 'nameType', 'typeAnnotation'], TSMethodSignature: ['typeParameters', 'key', 'params', 'returnType'], TSModuleBlock: ['body'], TSModuleDeclaration: ['id', 'body'], From adf8f3bd1cedb4cf862d2bfaed06be288519a33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josh=20Goldberg=20=E2=9C=A8?= Date: Fri, 19 Apr 2024 14:42:51 -0400 Subject: [PATCH 05/53] feat(eslint-plugin): remove formatting/layout rules (#8833) * feat(eslint-plugin): remove formatting/layout rules * Remove "layout" * feat(typescript-estree): add defaultProject for project service (#8815) * feat(typescript-estree): add defaultProject for project service * Thanks Jake, service.setCompilerOptionsForInferredProjects * Cleaned up a bit and added happy path testing * Update packages/typescript-estree/src/parser-options.ts Co-authored-by: Brad Zacher --------- Co-authored-by: Brad Zacher * feat(eslint-plugin): replace `no-new-symbol` with `no-new-native-nonconstructor` (#8895) Co-authored-by: Brad Zacher * Add tombstones * 'deprecated' typo * Let's mention both issues * Fix unused variable issue --------- Co-authored-by: Brad Zacher Co-authored-by: Dave --- packages/eslint-plugin/docs/rules/README.md | 3 - .../eslint-plugin/docs/rules/block-spacing.md | 11 + .../docs/rules/block-spacing.mdx | 13 - .../eslint-plugin/docs/rules/brace-style.md | 11 + .../eslint-plugin/docs/rules/brace-style.mdx | 13 - .../eslint-plugin/docs/rules/comma-dangle.md | 11 + .../eslint-plugin/docs/rules/comma-dangle.mdx | 23 - .../eslint-plugin/docs/rules/comma-spacing.md | 11 + .../docs/rules/comma-spacing.mdx | 13 - .../docs/rules/func-call-spacing.md | 11 + .../docs/rules/func-call-spacing.mdx | 13 - packages/eslint-plugin/docs/rules/indent.md | 11 + packages/eslint-plugin/docs/rules/indent.mdx | 21 - .../eslint-plugin/docs/rules/key-spacing.md | 11 + .../eslint-plugin/docs/rules/key-spacing.mdx | 13 - .../docs/rules/keyword-spacing.md | 11 + .../docs/rules/keyword-spacing.mdx | 13 - .../docs/rules/lines-around-comment.md | 11 + .../docs/rules/lines-around-comment.mdx | 28 - .../docs/rules/lines-between-class-members.md | 11 + .../rules/lines-between-class-members.mdx | 58 - .../docs/rules/member-delimiter-style.md | 11 + .../docs/rules/member-delimiter-style.mdx | 170 - .../docs/rules/no-extra-parens.md | 11 + .../docs/rules/no-extra-parens.mdx | 13 - .../eslint-plugin/docs/rules/no-extra-semi.md | 11 + .../docs/rules/no-extra-semi.mdx | 15 - .../docs/rules/object-curly-spacing.md | 11 + .../docs/rules/object-curly-spacing.mdx | 13 - .../rules/padding-line-between-statements.md | 11 + .../rules/padding-line-between-statements.mdx | 36 - packages/eslint-plugin/docs/rules/quotes.md | 11 + packages/eslint-plugin/docs/rules/quotes.mdx | 13 - packages/eslint-plugin/docs/rules/semi.md | 11 + packages/eslint-plugin/docs/rules/semi.mdx | 15 - .../docs/rules/space-before-blocks.md | 11 + .../docs/rules/space-before-blocks.mdx | 49 - .../docs/rules/space-before-function-paren.md | 11 + .../rules/space-before-function-paren.mdx | 13 - .../docs/rules/space-infix-ops.md | 11 + .../docs/rules/space-infix-ops.mdx | 19 - .../docs/rules/type-annotation-spacing.md | 11 + .../docs/rules/type-annotation-spacing.mdx | 335 - .../eslint-plugin/src/rules/block-spacing.ts | 167 - .../eslint-plugin/src/rules/brace-style.ts | 142 - .../eslint-plugin/src/rules/comma-dangle.ts | 190 - .../eslint-plugin/src/rules/comma-spacing.ts | 201 - .../src/rules/func-call-spacing.ts | 185 - packages/eslint-plugin/src/rules/indent.ts | 495 -- packages/eslint-plugin/src/rules/index.ts | 42 - .../eslint-plugin/src/rules/key-spacing.ts | 440 -- .../src/rules/keyword-spacing.ts | 124 - .../src/rules/lines-around-comment.ts | 456 -- .../src/rules/lines-between-class-members.ts | 78 - .../src/rules/member-delimiter-style.ts | 352 - .../src/rules/no-extra-parens.ts | 308 - .../eslint-plugin/src/rules/no-extra-semi.ts | 41 - .../src/rules/object-curly-spacing.ts | 293 - .../rules/padding-line-between-statements.ts | 825 -- packages/eslint-plugin/src/rules/quotes.ts | 80 - packages/eslint-plugin/src/rules/semi.ts | 75 - .../src/rules/space-before-blocks.ts | 94 - .../src/rules/space-before-function-paren.ts | 196 - .../src/rules/space-infix-ops.ts | 187 - .../src/rules/type-annotation-spacing.ts | 289 - .../src/util/getESLintCoreRule.ts | 15 - .../lines-between-class-members.shot | 33 - .../member-delimiter-style.shot | 58 - .../space-before-blocks.shot | 33 - .../type-annotation-spacing.shot | 311 - packages/eslint-plugin/tests/docs.test.ts | 30 +- .../indent/indent-invalid-fixture-1.js | 530 -- .../fixtures/indent/indent-valid-fixture-1.js | 530 -- .../tests/rules/block-spacing.test.ts | 148 - .../tests/rules/brace-style.test.ts | 1188 --- .../tests/rules/comma-dangle.test.ts | 259 - .../tests/rules/comma-spacing.test.ts | 854 -- .../tests/rules/func-call-spacing.test.ts | 533 -- .../tests/rules/indent/indent.test.ts | 1639 ---- .../eslint-plugin/tests/rules/indent/utils.ts | 23 - .../tests/rules/key-spacing.test.ts | 1456 ---- .../tests/rules/keyword-spacing.test.ts | 334 - .../tests/rules/lines-around-comment.test.ts | 851 -- .../rules/lines-between-class-members.test.ts | 338 - .../rules/member-delimiter-style.test.ts | 3627 --------- .../tests/rules/no-extra-parens.test.ts | 794 -- .../tests/rules/no-extra-semi.test.ts | 433 - .../tests/rules/object-curly-spacing.test.ts | 2137 ----- .../padding-line-between-statements.test.ts | 5134 ------------ .../eslint-plugin/tests/rules/quotes.test.ts | 1157 --- .../eslint-plugin/tests/rules/semi.test.ts | 1105 --- .../tests/rules/space-before-blocks.test.ts | 259 - .../rules/space-before-function-paren.test.ts | 595 -- .../tests/rules/space-infix-ops.test.ts | 2397 ------ .../rules/type-annotation-spacing.test.ts | 6998 ----------------- .../tests/schema-snapshots/block-spacing.shot | 18 - .../tests/schema-snapshots/brace-style.shot | 33 - .../tests/schema-snapshots/comma-dangle.shot | 95 - .../tests/schema-snapshots/comma-spacing.shot | 34 - .../schema-snapshots/func-call-spacing.shot | 57 - .../tests/schema-snapshots/key-spacing.shot | 228 - .../schema-snapshots/keyword-spacing.shot | 1146 --- .../lines-around-comment.shot | 118 - .../lines-between-class-members.shot | 86 - .../member-delimiter-style.shot | 132 - .../schema-snapshots/no-extra-parens.shot | 91 - .../tests/schema-snapshots/no-extra-semi.shot | 14 - .../object-curly-spacing.shot | 36 - .../padding-line-between-statements.shot | 273 - .../tests/schema-snapshots/quotes.shot | 46 - .../tests/schema-snapshots/semi.shot | 74 - .../schema-snapshots/space-before-blocks.shot | 45 - .../space-before-function-paren.shot | 49 - .../schema-snapshots/space-infix-ops.shot | 29 - .../type-annotation-spacing.shot | 83 - .../tools/generate-breaking-changes.mts | 3 +- .../eslint-plugin/typings/eslint-rules.d.ts | 568 -- packages/utils/src/ts-eslint/Rule.ts | 3 +- .../src/ts-eslint/eslint/ESLintShared.ts | 2 +- .../src/components/RulesTable/index.tsx | 23 +- packages/website/src/components/constants.ts | 6 - 121 files changed, 262 insertions(+), 43222 deletions(-) create mode 100644 packages/eslint-plugin/docs/rules/block-spacing.md delete mode 100644 packages/eslint-plugin/docs/rules/block-spacing.mdx create mode 100644 packages/eslint-plugin/docs/rules/brace-style.md delete mode 100644 packages/eslint-plugin/docs/rules/brace-style.mdx create mode 100644 packages/eslint-plugin/docs/rules/comma-dangle.md delete mode 100644 packages/eslint-plugin/docs/rules/comma-dangle.mdx create mode 100644 packages/eslint-plugin/docs/rules/comma-spacing.md delete mode 100644 packages/eslint-plugin/docs/rules/comma-spacing.mdx create mode 100644 packages/eslint-plugin/docs/rules/func-call-spacing.md delete mode 100644 packages/eslint-plugin/docs/rules/func-call-spacing.mdx create mode 100644 packages/eslint-plugin/docs/rules/indent.md delete mode 100644 packages/eslint-plugin/docs/rules/indent.mdx create mode 100644 packages/eslint-plugin/docs/rules/key-spacing.md delete mode 100644 packages/eslint-plugin/docs/rules/key-spacing.mdx create mode 100644 packages/eslint-plugin/docs/rules/keyword-spacing.md delete mode 100644 packages/eslint-plugin/docs/rules/keyword-spacing.mdx create mode 100644 packages/eslint-plugin/docs/rules/lines-around-comment.md delete mode 100644 packages/eslint-plugin/docs/rules/lines-around-comment.mdx create mode 100644 packages/eslint-plugin/docs/rules/lines-between-class-members.md delete mode 100644 packages/eslint-plugin/docs/rules/lines-between-class-members.mdx create mode 100644 packages/eslint-plugin/docs/rules/member-delimiter-style.md delete mode 100644 packages/eslint-plugin/docs/rules/member-delimiter-style.mdx create mode 100644 packages/eslint-plugin/docs/rules/no-extra-parens.md delete mode 100644 packages/eslint-plugin/docs/rules/no-extra-parens.mdx create mode 100644 packages/eslint-plugin/docs/rules/no-extra-semi.md delete mode 100644 packages/eslint-plugin/docs/rules/no-extra-semi.mdx create mode 100644 packages/eslint-plugin/docs/rules/object-curly-spacing.md delete mode 100644 packages/eslint-plugin/docs/rules/object-curly-spacing.mdx create mode 100644 packages/eslint-plugin/docs/rules/padding-line-between-statements.md delete mode 100644 packages/eslint-plugin/docs/rules/padding-line-between-statements.mdx create mode 100644 packages/eslint-plugin/docs/rules/quotes.md delete mode 100644 packages/eslint-plugin/docs/rules/quotes.mdx create mode 100644 packages/eslint-plugin/docs/rules/semi.md delete mode 100644 packages/eslint-plugin/docs/rules/semi.mdx create mode 100644 packages/eslint-plugin/docs/rules/space-before-blocks.md delete mode 100644 packages/eslint-plugin/docs/rules/space-before-blocks.mdx create mode 100644 packages/eslint-plugin/docs/rules/space-before-function-paren.md delete mode 100644 packages/eslint-plugin/docs/rules/space-before-function-paren.mdx create mode 100644 packages/eslint-plugin/docs/rules/space-infix-ops.md delete mode 100644 packages/eslint-plugin/docs/rules/space-infix-ops.mdx create mode 100644 packages/eslint-plugin/docs/rules/type-annotation-spacing.md delete mode 100644 packages/eslint-plugin/docs/rules/type-annotation-spacing.mdx delete mode 100644 packages/eslint-plugin/src/rules/block-spacing.ts delete mode 100644 packages/eslint-plugin/src/rules/brace-style.ts delete mode 100644 packages/eslint-plugin/src/rules/comma-dangle.ts delete mode 100644 packages/eslint-plugin/src/rules/comma-spacing.ts delete mode 100644 packages/eslint-plugin/src/rules/func-call-spacing.ts delete mode 100644 packages/eslint-plugin/src/rules/indent.ts delete mode 100644 packages/eslint-plugin/src/rules/key-spacing.ts delete mode 100644 packages/eslint-plugin/src/rules/keyword-spacing.ts delete mode 100644 packages/eslint-plugin/src/rules/lines-around-comment.ts delete mode 100644 packages/eslint-plugin/src/rules/lines-between-class-members.ts delete mode 100644 packages/eslint-plugin/src/rules/member-delimiter-style.ts delete mode 100644 packages/eslint-plugin/src/rules/no-extra-parens.ts delete mode 100644 packages/eslint-plugin/src/rules/no-extra-semi.ts delete mode 100644 packages/eslint-plugin/src/rules/object-curly-spacing.ts delete mode 100644 packages/eslint-plugin/src/rules/padding-line-between-statements.ts delete mode 100644 packages/eslint-plugin/src/rules/quotes.ts delete mode 100644 packages/eslint-plugin/src/rules/semi.ts delete mode 100644 packages/eslint-plugin/src/rules/space-before-blocks.ts delete mode 100644 packages/eslint-plugin/src/rules/space-before-function-paren.ts delete mode 100644 packages/eslint-plugin/src/rules/space-infix-ops.ts delete mode 100644 packages/eslint-plugin/src/rules/type-annotation-spacing.ts delete mode 100644 packages/eslint-plugin/tests/docs-eslint-output-snapshots/lines-between-class-members.shot delete mode 100644 packages/eslint-plugin/tests/docs-eslint-output-snapshots/member-delimiter-style.shot delete mode 100644 packages/eslint-plugin/tests/docs-eslint-output-snapshots/space-before-blocks.shot delete mode 100644 packages/eslint-plugin/tests/docs-eslint-output-snapshots/type-annotation-spacing.shot delete mode 100644 packages/eslint-plugin/tests/fixtures/indent/indent-invalid-fixture-1.js delete mode 100644 packages/eslint-plugin/tests/fixtures/indent/indent-valid-fixture-1.js delete mode 100644 packages/eslint-plugin/tests/rules/block-spacing.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/brace-style.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/comma-dangle.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/comma-spacing.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/func-call-spacing.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/indent/indent.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/indent/utils.ts delete mode 100644 packages/eslint-plugin/tests/rules/key-spacing.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/keyword-spacing.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/lines-around-comment.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/lines-between-class-members.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/member-delimiter-style.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/no-extra-parens.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/no-extra-semi.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/object-curly-spacing.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/padding-line-between-statements.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/quotes.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/semi.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/space-before-blocks.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/space-before-function-paren.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/space-infix-ops.test.ts delete mode 100644 packages/eslint-plugin/tests/rules/type-annotation-spacing.test.ts delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/block-spacing.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/brace-style.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/comma-dangle.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/comma-spacing.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/func-call-spacing.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/key-spacing.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/keyword-spacing.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/lines-around-comment.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/lines-between-class-members.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/member-delimiter-style.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/no-extra-parens.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/no-extra-semi.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/object-curly-spacing.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/padding-line-between-statements.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/quotes.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/semi.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/space-before-blocks.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/space-before-function-paren.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/space-infix-ops.shot delete mode 100644 packages/eslint-plugin/tests/schema-snapshots/type-annotation-spacing.shot diff --git a/packages/eslint-plugin/docs/rules/README.md b/packages/eslint-plugin/docs/rules/README.md index 32c1cefc16e0..75c5723d748a 100644 --- a/packages/eslint-plugin/docs/rules/README.md +++ b/packages/eslint-plugin/docs/rules/README.md @@ -33,9 +33,6 @@ import RulesTable from "@site/src/components/RulesTable"; - Sometimes, it is not safe to automatically fix the code with an auto-fixer. But in these cases, we often have a good guess of what the correct fix should be, and we can provide it as a suggestion to the developer. - `💭 requires type information` refers to whether the rule requires [typed linting](/getting-started/typed-linting). - `🧱 extension rule` means that the rule is an extension of an [core ESLint rule](https://eslint.org/docs/latest/rules) (see [Extension Rules](#extension-rules)). -- `📐 formatting rule` means that the rule has to do with formatting. - - We [strongly recommend against using ESLint for formatting](/troubleshooting/formatting). - - Soon, formatting rules will be moved to the [ESLint stylistic plugin](https://eslint.style). - `💀 deprecated rule` means that the rule should no longer be used and will be removed from the plugin in a future version. ## Extension Rules diff --git a/packages/eslint-plugin/docs/rules/block-spacing.md b/packages/eslint-plugin/docs/rules/block-spacing.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/block-spacing.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/block-spacing.mdx b/packages/eslint-plugin/docs/rules/block-spacing.mdx deleted file mode 100644 index de933a031de5..000000000000 --- a/packages/eslint-plugin/docs/rules/block-spacing.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: 'Disallow or enforce spaces inside of blocks after opening block and before closing block.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/block-spacing** for documentation. - -This rule extends the base [`eslint/block-spacing`](https://eslint.org/docs/rules/block-spacing) rule. -This version adds support for TypeScript related blocks (interfaces, object type literals and enums). diff --git a/packages/eslint-plugin/docs/rules/brace-style.md b/packages/eslint-plugin/docs/rules/brace-style.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/brace-style.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/brace-style.mdx b/packages/eslint-plugin/docs/rules/brace-style.mdx deleted file mode 100644 index a1e4cb18fc7c..000000000000 --- a/packages/eslint-plugin/docs/rules/brace-style.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: 'Enforce consistent brace style for blocks.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/brace-style** for documentation. - -This rule extends the base [`eslint/brace-style`](https://eslint.org/docs/rules/brace-style) rule. -It adds support for `enum`, `interface`, `namespace` and `module` declarations. diff --git a/packages/eslint-plugin/docs/rules/comma-dangle.md b/packages/eslint-plugin/docs/rules/comma-dangle.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/comma-dangle.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/comma-dangle.mdx b/packages/eslint-plugin/docs/rules/comma-dangle.mdx deleted file mode 100644 index fa934f5ee4bf..000000000000 --- a/packages/eslint-plugin/docs/rules/comma-dangle.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -description: 'Require or disallow trailing commas.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/comma-dangle** for documentation. - -This rule extends the base [`eslint/comma-dangle`](https://eslint.org/docs/rules/comma-dangle) rule. -It adds support for TypeScript syntax. - -See the [ESLint documentation](https://eslint.org/docs/rules/comma-dangle) for more details on the `comma-dangle` rule. - -## Options - -In addition to the options supported by the `comma-dangle` rule in ESLint core, the rule adds the following options: - -- `"enums"` is for trailing comma in enum. (e.g. `enum Foo = {Bar,}`) -- `"generics"` is for trailing comma in generic. (e.g. `function foo() {}`) -- `"tuples"` is for trailing comma in tuple. (e.g. `type Foo = [string,]`) diff --git a/packages/eslint-plugin/docs/rules/comma-spacing.md b/packages/eslint-plugin/docs/rules/comma-spacing.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/comma-spacing.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/comma-spacing.mdx b/packages/eslint-plugin/docs/rules/comma-spacing.mdx deleted file mode 100644 index 249f8933e656..000000000000 --- a/packages/eslint-plugin/docs/rules/comma-spacing.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: 'Enforce consistent spacing before and after commas.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/comma-spacing** for documentation. - -This rule extends the base [`eslint/comma-spacing`](https://eslint.org/docs/rules/comma-spacing) rule. -It adds support for trailing comma in a types parameters list. diff --git a/packages/eslint-plugin/docs/rules/func-call-spacing.md b/packages/eslint-plugin/docs/rules/func-call-spacing.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/func-call-spacing.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/func-call-spacing.mdx b/packages/eslint-plugin/docs/rules/func-call-spacing.mdx deleted file mode 100644 index defc8d976d7d..000000000000 --- a/packages/eslint-plugin/docs/rules/func-call-spacing.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: 'Require or disallow spacing between function identifiers and their invocations.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/func-call-spacing** for documentation. - -This rule extends the base [`eslint/func-call-spacing`](https://eslint.org/docs/rules/func-call-spacing) rule. -It adds support for generic type parameters on function calls. diff --git a/packages/eslint-plugin/docs/rules/indent.md b/packages/eslint-plugin/docs/rules/indent.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/indent.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/indent.mdx b/packages/eslint-plugin/docs/rules/indent.mdx deleted file mode 100644 index cf74735986a9..000000000000 --- a/packages/eslint-plugin/docs/rules/indent.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -description: 'Enforce consistent indentation.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/indent** for documentation. - -## Warning - -:::warning - -Please read [Issue #1824: Problems with the indent rule](https://github.com/typescript-eslint/typescript-eslint/issues/1824) before using this rule! - -::: - -This rule extends the base [`eslint/indent`](https://eslint.org/docs/rules/indent) rule. -It adds support for TypeScript nodes. diff --git a/packages/eslint-plugin/docs/rules/key-spacing.md b/packages/eslint-plugin/docs/rules/key-spacing.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/key-spacing.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/key-spacing.mdx b/packages/eslint-plugin/docs/rules/key-spacing.mdx deleted file mode 100644 index da0ebae30511..000000000000 --- a/packages/eslint-plugin/docs/rules/key-spacing.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: 'Enforce consistent spacing between property names and type annotations in types and interfaces.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/key-spacing** for documentation. - -This rule extends the base [`eslint/key-spacing`](https://eslint.org/docs/rules/key-spacing) rule. -It adds support for type annotations on interfaces, classes and type literals properties. diff --git a/packages/eslint-plugin/docs/rules/keyword-spacing.md b/packages/eslint-plugin/docs/rules/keyword-spacing.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/keyword-spacing.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/keyword-spacing.mdx b/packages/eslint-plugin/docs/rules/keyword-spacing.mdx deleted file mode 100644 index c092b46a84f6..000000000000 --- a/packages/eslint-plugin/docs/rules/keyword-spacing.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: 'Enforce consistent spacing before and after keywords.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/keyword-spacing** for documentation. - -This rule extends the base [`eslint/keyword-spacing`](https://eslint.org/docs/rules/keyword-spacing) rule. -It adds support for generic type parameters on function calls. diff --git a/packages/eslint-plugin/docs/rules/lines-around-comment.md b/packages/eslint-plugin/docs/rules/lines-around-comment.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/lines-around-comment.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/lines-around-comment.mdx b/packages/eslint-plugin/docs/rules/lines-around-comment.mdx deleted file mode 100644 index bf612ceaaea8..000000000000 --- a/packages/eslint-plugin/docs/rules/lines-around-comment.mdx +++ /dev/null @@ -1,28 +0,0 @@ ---- -description: 'Require empty lines around comments.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/lines-around-comment** for documentation. - -This rule extends the base [`eslint/lines-around-comment`](https://eslint.org/docs/rules/lines-around-comment) rule. -It adds support for TypeScript syntax. - -See the [ESLint documentation](https://eslint.org/docs/rules/lines-around-comment) for more details on the `lines-around-comment` rule. - -## Options - -In addition to the options supported by the `lines-around-comment` rule in ESLint core, the rule adds the following options: - -- `allowEnumEnd: true` doesn't require a blank line after an enum body block end -- `allowEnumStart: true` doesn't require a blank line before an enum body block start -- `allowInterfaceEnd: true` doesn't require a blank line before an interface body block end -- `allowInterfaceStart: true` doesn't require a blank line after an interface body block start -- `allowModuleEnd: true` doesn't require a blank line before a module body block end -- `allowModuleStart: true` doesn't require a blank line after a module body block start -- `allowTypeEnd: true` doesn't require a blank line before a type literal block end -- `allowTypeStart: true` doesn't require a blank line after a type literal block start diff --git a/packages/eslint-plugin/docs/rules/lines-between-class-members.md b/packages/eslint-plugin/docs/rules/lines-between-class-members.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/lines-between-class-members.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/lines-between-class-members.mdx b/packages/eslint-plugin/docs/rules/lines-between-class-members.mdx deleted file mode 100644 index c093b715ee9e..000000000000 --- a/packages/eslint-plugin/docs/rules/lines-between-class-members.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -description: 'Require or disallow an empty line between class members.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/lines-between-class-members** for documentation. - -This rule extends the base [`eslint/lines-between-class-members`](https://eslint.org/docs/rules/lines-between-class-members) rule. -It adds support for ignoring overload methods in a class. - -## Options - -In addition to the options supported by the `lines-between-class-members` rule in ESLint core, the rule adds the following options: - -- Object option: - - - `"exceptAfterOverload": true` (default) - Skip checking empty lines after overload class members - - `"exceptAfterOverload": false` - **do not** skip checking empty lines after overload class members - -- [See the other options allowed](https://github.com/eslint/eslint/blob/main/docs/rules/lines-between-class-members.md#options) - -### `exceptAfterOverload: true` - -Examples of **correct** code for the `{ "exceptAfterOverload": true }` option: - -```ts option='"always", { "exceptAfterOverload": true }' showPlaygroundButton -class foo { - bar(a: string): void; - bar(a: string, b: string): void; - bar(a: string, b: string) {} - - baz() {} - - qux() {} -} -``` - -### `exceptAfterOverload: false` - -Examples of **correct** code for the `{ "exceptAfterOverload": false }` option: - -```ts option='"always", { "exceptAfterOverload": false }' showPlaygroundButton -class foo { - bar(a: string): void; - - bar(a: string, b: string): void; - - bar(a: string, b: string) {} - - baz() {} - - qux() {} -} -``` diff --git a/packages/eslint-plugin/docs/rules/member-delimiter-style.md b/packages/eslint-plugin/docs/rules/member-delimiter-style.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/member-delimiter-style.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/member-delimiter-style.mdx b/packages/eslint-plugin/docs/rules/member-delimiter-style.mdx deleted file mode 100644 index 7e3c5547cbc1..000000000000 --- a/packages/eslint-plugin/docs/rules/member-delimiter-style.mdx +++ /dev/null @@ -1,170 +0,0 @@ ---- -description: 'Require a specific member delimiter style for interfaces and type literals.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/member-delimiter-style** for documentation. - -TypeScript allows three delimiters between members in interfaces and type aliases: - -{/* prettier-ignore */} -```ts -interface Foo { - // Semicolons (default, preferred in TypeScript): - name: string; - - // Commas (JSON-like): - name: string, - - // Line breaks (none): - name: string -} -``` - -For code readability, it's generally best to use the same style consistently in your codebase. - -This rule enforces keeping to one configurable code style. -It can also standardize the presence (or absence) of a delimiter in the last member of a construct, as well as a separate delimiter syntax for single line declarations. - -## Options - -Default config: - -```json -{ - "multiline": { - "delimiter": "semi", - "requireLast": true - }, - "singleline": { - "delimiter": "semi", - "requireLast": false - }, - "multilineDetection": "brackets" -} -``` - -`multiline` config only applies to multiline `interface`/`type` definitions. -`singleline` config only applies to single line `interface`/`type` definitions. -The two configs are entirely separate, and do not effect one another. - -`multilineDetection` determines what counts as multiline - -- `"brackets"` (default) any newlines in the type or interface make it multiline. -- `"last-member"` if the last member of the interface is on the same line as the last bracket, it is counted as a single line. - -### `delimiter` - -Accepts three values (or two for `singleline`): - -- `comma` - each member should be delimited with a comma (`,`). -- `semi` - each member should be delimited with a semicolon (`;`). -- `none` - each member should be delimited with nothing. - -:::note -`none` is not an option for `singleline` because having no delimiter between members on a single line is a syntax error in TS. -::: - -### `requireLast` - -Determines whether or not the last member in the `interface`/`type` should have a delimiter: - -- `true` - the last member **_must_** have a delimiter. -- `false` - the last member **_must not_** have a delimiter. - -### `overrides` - -Allows you to specify options specifically for either `interface`s or `type` definitions / inline `type`s. - -For example, to require commas for `type`s, and semicolons for multiline `interface`s: - -```json -{ - "multiline": { - "delimiter": "comma", - "requireLast": true - }, - "singleline": { - "delimiter": "comma", - "requireLast": true - }, - "overrides": { - "interface": { - "multiline": { - "delimiter": "semi", - "requireLast": true - } - } - } -} -``` - -## Examples - -Examples of code for this rule with the default config: - - - - -{/* prettier-ignore */} -```ts -// missing semicolon delimiter -interface Foo { - name: string - greet(): string -} - -// using incorrect delimiter -interface Bar { - name: string, - greet(): string, -} - -// missing last member delimiter -interface Baz { - name: string; - greet(): string -} - -// incorrect delimiter -type FooBar = { name: string, greet(): string } - -// last member should not have delimiter -type FooBar = { name: string; greet(): string; } -``` - - - - -{/* prettier-ignore */} -```ts -interface Foo { - name: string; - greet(): string; -} - -interface Foo { name: string } - -type Bar = { - name: string; - greet(): string; -} - -type Bar = { name: string } - -type FooBar = { name: string; greet(): string } -``` - - - - -## When Not To Use It - -If you specifically want to use both member delimiter kinds for stylistic reasons, or don't wish to enforce one style over the other, you can avoid this rule. - -However, keep in mind that inconsistent style can harm readability in a project. -We recommend picking a single option for this rule that works best for your project. diff --git a/packages/eslint-plugin/docs/rules/no-extra-parens.md b/packages/eslint-plugin/docs/rules/no-extra-parens.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-extra-parens.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/no-extra-parens.mdx b/packages/eslint-plugin/docs/rules/no-extra-parens.mdx deleted file mode 100644 index 4c56e6ecd279..000000000000 --- a/packages/eslint-plugin/docs/rules/no-extra-parens.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: 'Disallow unnecessary parentheses.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/no-extra-parens** for documentation. - -This rule extends the base [`eslint/no-extra-parens`](https://eslint.org/docs/rules/no-extra-parens) rule. -It adds support for TypeScript type assertions. diff --git a/packages/eslint-plugin/docs/rules/no-extra-semi.md b/packages/eslint-plugin/docs/rules/no-extra-semi.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-extra-semi.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/no-extra-semi.mdx b/packages/eslint-plugin/docs/rules/no-extra-semi.mdx deleted file mode 100644 index ec23c28eca8d..000000000000 --- a/packages/eslint-plugin/docs/rules/no-extra-semi.mdx +++ /dev/null @@ -1,15 +0,0 @@ ---- -description: 'Disallow unnecessary semicolons.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/no-extra-semi** for documentation. - -This rule extends the base [`eslint/no-extra-semi`](https://eslint.org/docs/rules/no-extra-semi) rule. -It adds support for class properties. - -Note that this rule is classified as a "Suggestion" rule instead of a "Layout & Formatting" rule because [adding extra semi-colons actually changes the AST of the program](https://typescript-eslint.io/play/#ts=5.1.6&showAST=es&fileType=.ts&code=MYewdgzgLgBAHjAvDAjAbg0A&eslintrc=N4KABGBEBOCuA2BTAzpAXGUEKQHYHsBaRADwBdoBDQ5RAWwEt0p8AzVyAGnG0gAEyATwAOKAMbQGwssWTwGuMgHoCxclRr0mGSImjR80SDwC%2BIE0A&tsconfig=&tokens=false). With that said, modern TypeScript formatters will remove extra semi-colons automatically during the formatting process. Thus, if you [use a formatter](/troubleshooting/formatting), then enabling this rule is probably unnecessary. diff --git a/packages/eslint-plugin/docs/rules/object-curly-spacing.md b/packages/eslint-plugin/docs/rules/object-curly-spacing.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/object-curly-spacing.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/object-curly-spacing.mdx b/packages/eslint-plugin/docs/rules/object-curly-spacing.mdx deleted file mode 100644 index af82a39f986b..000000000000 --- a/packages/eslint-plugin/docs/rules/object-curly-spacing.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: 'Enforce consistent spacing inside braces.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/object-curly-spacing** for documentation. - -This rule extends the base [`eslint/object-curly-spacing`](https://eslint.org/docs/rules/object-curly-spacing) rule. -It adds support for TypeScript's object types. diff --git a/packages/eslint-plugin/docs/rules/padding-line-between-statements.md b/packages/eslint-plugin/docs/rules/padding-line-between-statements.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/padding-line-between-statements.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/padding-line-between-statements.mdx b/packages/eslint-plugin/docs/rules/padding-line-between-statements.mdx deleted file mode 100644 index e1bf6365703d..000000000000 --- a/packages/eslint-plugin/docs/rules/padding-line-between-statements.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -description: 'Require or disallow padding lines between statements.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/padding-line-between-statements** for documentation. - -This rule extends the base [`eslint/padding-line-between-statements`](https://eslint.org/docs/rules/padding-line-between-statements) rule. -It adds support for TypeScript constructs such as `interface` and `type`. - -## Options - -In addition to options provided by ESLint, `interface` and `type` can be used as statement types. - -For example, to add blank lines before interfaces and type definitions: - -```jsonc -{ - // Example - Add blank lines before interface and type definitions. - "padding-line-between-statements": "off", - "@typescript-eslint/padding-line-between-statements": [ - "error", - { - "blankLine": "always", - "prev": "*", - "next": ["interface", "type"], - }, - ], -} -``` - -**Note:** ESLint `cjs-export` and `cjs-import` statement types are renamed to `exports` and `require` respectively. diff --git a/packages/eslint-plugin/docs/rules/quotes.md b/packages/eslint-plugin/docs/rules/quotes.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/quotes.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/quotes.mdx b/packages/eslint-plugin/docs/rules/quotes.mdx deleted file mode 100644 index 800bb2b24580..000000000000 --- a/packages/eslint-plugin/docs/rules/quotes.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: 'Enforce the consistent use of either backticks, double, or single quotes.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/quotes** for documentation. - -This rule extends the base [`eslint/quotes`](https://eslint.org/docs/rules/quotes) rule. -It adds support for TypeScript features which allow quoted names, but not backtick quoted names. diff --git a/packages/eslint-plugin/docs/rules/semi.md b/packages/eslint-plugin/docs/rules/semi.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/semi.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/semi.mdx b/packages/eslint-plugin/docs/rules/semi.mdx deleted file mode 100644 index a0cd3e3653b4..000000000000 --- a/packages/eslint-plugin/docs/rules/semi.mdx +++ /dev/null @@ -1,15 +0,0 @@ ---- -description: 'Require or disallow semicolons instead of ASI.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/semi** for documentation. - -This rule extends the base [`eslint/semi`](https://eslint.org/docs/rules/semi) rule. -It adds support for TypeScript features that require semicolons. - -See also the [`@typescript-eslint/member-delimiter-style`](member-delimiter-style.mdx) rule, which allows you to specify the delimiter for `type` and `interface` members. diff --git a/packages/eslint-plugin/docs/rules/space-before-blocks.md b/packages/eslint-plugin/docs/rules/space-before-blocks.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/space-before-blocks.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/space-before-blocks.mdx b/packages/eslint-plugin/docs/rules/space-before-blocks.mdx deleted file mode 100644 index 84f6cf93e336..000000000000 --- a/packages/eslint-plugin/docs/rules/space-before-blocks.mdx +++ /dev/null @@ -1,49 +0,0 @@ ---- -description: 'Enforce consistent spacing before blocks.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/space-before-blocks** for documentation. - -This rule extends the base [`eslint/space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks) rule. -It adds support for interfaces and enums. - - - - -{/* prettier-ignore */} -```ts -enum Breakpoint{ - Large, - Medium, -} - -interface State{ - currentBreakpoint: Breakpoint; -} -``` - - - - -```ts -enum Breakpoint { - Large, - Medium, -} - -interface State { - currentBreakpoint: Breakpoint; -} -``` - - - - -## Options - -In case a more specific options object is passed these blocks will follow `classes` configuration option. diff --git a/packages/eslint-plugin/docs/rules/space-before-function-paren.md b/packages/eslint-plugin/docs/rules/space-before-function-paren.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/space-before-function-paren.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/space-before-function-paren.mdx b/packages/eslint-plugin/docs/rules/space-before-function-paren.mdx deleted file mode 100644 index 8fd3e915bc42..000000000000 --- a/packages/eslint-plugin/docs/rules/space-before-function-paren.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -description: 'Enforce consistent spacing before function parenthesis.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/space-before-function-paren** for documentation. - -This rule extends the base [`eslint/space-before-function-paren`](https://eslint.org/docs/rules/space-before-function-paren) rule. -It adds support for generic type parameters on function calls. diff --git a/packages/eslint-plugin/docs/rules/space-infix-ops.md b/packages/eslint-plugin/docs/rules/space-infix-ops.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/space-infix-ops.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/space-infix-ops.mdx b/packages/eslint-plugin/docs/rules/space-infix-ops.mdx deleted file mode 100644 index 6293ca22e2f5..000000000000 --- a/packages/eslint-plugin/docs/rules/space-infix-ops.mdx +++ /dev/null @@ -1,19 +0,0 @@ ---- -description: 'Require spacing around infix operators.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/space-infix-ops** for documentation. - -This rule extends the base [`eslint/space-infix-ops`](https://eslint.org/docs/rules/space-infix-ops) rule. -It adds support for enum members. - -```ts -enum MyEnum { - KEY = 'value', -} -``` diff --git a/packages/eslint-plugin/docs/rules/type-annotation-spacing.md b/packages/eslint-plugin/docs/rules/type-annotation-spacing.md new file mode 100644 index 000000000000..45d53728c3f8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/type-annotation-spacing.md @@ -0,0 +1,11 @@ +:::danger Deprecated + +This rule has been moved to the [ESLint stylistic plugin](https://eslint.style). +See [#8072](https://github.com/typescript-eslint/typescript-eslint/issues/8072) and [#8074](https://github.com/typescript-eslint/typescript-eslint/issues/8074) for more information. + +::: + + diff --git a/packages/eslint-plugin/docs/rules/type-annotation-spacing.mdx b/packages/eslint-plugin/docs/rules/type-annotation-spacing.mdx deleted file mode 100644 index 423dc90da83d..000000000000 --- a/packages/eslint-plugin/docs/rules/type-annotation-spacing.mdx +++ /dev/null @@ -1,335 +0,0 @@ ---- -description: 'Require consistent spacing around type annotations.' ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -> 🛑 This file is source code, not the primary documentation location! 🛑 -> -> See **https://typescript-eslint.io/rules/type-annotation-spacing** for documentation. - -Spacing around type annotations improves readability of the code. Although the most commonly used style guideline for type annotations in TypeScript prescribes adding a space after the colon, but not before it, it is subjective to the preferences of a project. For example: - -{/* prettier-ignore */} -```ts -// with space after, but not before (default if no option is specified) -let foo: string = "bar"; - -// with no spaces -let foo:string = "bar"; - -// with space before and after -let foo : string = "bar"; - -// with space before, but not after -let foo :string = "bar"; - -// with spaces before and after the fat arrow (default if no option is specified) -type Foo = (string: name) => string; - -// with no spaces between the fat arrow -type Foo = (string: name)=>string; - -// with space after, but not before the fat arrow -type Foo = (string: name)=> string; - -// with space before, but not after the fat arrow -type Foo = (string: name) =>string; -``` - -## Examples - -This rule aims to enforce specific spacing patterns around type annotations and function types in type literals. - - - - -{/* prettier-ignore */} -```ts -let foo:string = "bar"; -let foo :string = "bar"; -let foo : string = "bar"; - -function foo():string {} -function foo() :string {} -function foo() : string {} - -class Foo { - name:string; -} - -class Foo { - name :string; -} - -class Foo { - name : string; -} - -type Foo = ()=>{}; -type Foo = () =>{}; -type Foo = ()=> {}; -``` - - - - -{/* prettier-ignore */} -```ts -let foo: string = "bar"; - -function foo(): string {} - -class Foo { - name: string; -} - -type Foo = () => {}; -``` - - - - -## Options - -### after - -```json -{ "before": false, "after": true } -``` - - - - -{/* prettier-ignore */} -```ts option='{ "before": false, "after": true }' -let foo:string = "bar"; -let foo :string = "bar"; -let foo : string = "bar"; - -function foo():string {} -function foo() :string {} -function foo() : string {} - -class Foo { - name:string; -} - -class Foo { - name :string; -} - -class Foo { - name : string; -} - -type Foo = ()=>{}; -type Foo = () =>{}; -type Foo = () => {}; -``` - - - - -{/* prettier-ignore */} -```ts option='{ "before": false, "after": true }' -let foo: string = "bar"; - -function foo(): string {} - -class Foo { - name: string; -} - -type Foo = ()=> {}; -``` - - - - -### before - -```json -{ "before": true, "after": true } -``` - - - - -{/* prettier-ignore */} -```ts option='{ "before": true, "after": true }' -let foo: string = "bar"; -let foo:string = "bar"; -let foo :string = "bar"; - -function foo(): string {} -function foo():string {} -function foo() :string {} - -class Foo { - name: string; -} - -class Foo { - name:string; -} - -class Foo { - name :string; -} - -type Foo = ()=>{}; -type Foo = () =>{}; -type Foo = ()=> {}; -``` - - - - -{/* prettier-ignore */} -```ts option='{ "before": true, "after": true }' -let foo : string = "bar"; - -function foo() : string {} - -class Foo { - name : string; -} - -type Foo = () => {}; -``` - - - - -### overrides - colon - -```json -{ - "before": false, - "after": false, - "overrides": { "colon": { "before": true, "after": true } } -} -``` - - - - -{/* prettier-ignore */} -```ts option='{"before":false,"after":false,"overrides":{"colon":{"before":true,"after":true}}}' -let foo: string = "bar"; -let foo:string = "bar"; -let foo :string = "bar"; - -function foo(): string {} -function foo():string {} -function foo() :string {} - -class Foo { - name: string; -} - -class Foo { - name:string; -} - -class Foo { - name :string; -} - -type Foo = () =>{}; -type Foo = ()=> {}; -type Foo = () => {}; -``` - - - - -{/* prettier-ignore */} -```ts option='{"before":false,"after":false,"overrides":{"colon":{"before":true,"after":true}}}' -let foo : string = "bar"; - -function foo() : string {} - -class Foo { - name : string; -} - -type Foo = { - name : (name : string)=>string; -} - -type Foo = ()=>{}; -``` - - - - -### overrides - arrow - -```json -{ - "before": false, - "after": false, - "overrides": { "arrow": { "before": true, "after": true } } -} -``` - - - - -{/* prettier-ignore */} -```ts option='{"before":false,"after":false,"overrides":{"arrow":{"before":true,"after":true}}}' -let foo: string = "bar"; -let foo : string = "bar"; -let foo :string = "bar"; - -function foo(): string {} -function foo():string {} -function foo() :string {} - -class Foo { - name: string; -} - -class Foo { - name : string; -} - -class Foo { - name :string; -} - -type Foo = ()=>{}; -type Foo = () =>{}; -type Foo = ()=> {}; -``` - - - - -{/* prettier-ignore */} -```ts option='{"before":false,"after":false,"overrides":{"arrow":{"before":true,"after":true}}}' -let foo:string = "bar"; - -function foo():string {} - -class Foo { - name:string; -} - -type Foo = () => {}; -``` - - - - -## When Not To Use It - -If you don't want to enforce spacing for your type annotations, you can safely turn this rule off. - -## Further Reading - -- [TypeScript Type System](https://basarat.gitbooks.io/typescript/docs/types/type-system.html) -- [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html) diff --git a/packages/eslint-plugin/src/rules/block-spacing.ts b/packages/eslint-plugin/src/rules/block-spacing.ts deleted file mode 100644 index 05db7c76199c..000000000000 --- a/packages/eslint-plugin/src/rules/block-spacing.ts +++ /dev/null @@ -1,167 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_TOKEN_TYPES } from '@typescript-eslint/utils'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule, isTokenOnSameLine } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('block-spacing'); - -export type Options = InferOptionsTypeFromRule; -export type MessageIds = InferMessageIdsTypeFromRule; - -export default createRule({ - name: 'block-spacing', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/block-spacing'], - type: 'layout', - docs: { - description: - 'Disallow or enforce spaces inside of blocks after opening block and before closing block', - extendsBaseRule: true, - }, - fixable: 'whitespace', - hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, - messages: baseRule.meta.messages, - }, - defaultOptions: ['always'], - - create(context, [whenToApplyOption]) { - const baseRules = baseRule.create(context); - const always = whenToApplyOption !== 'never'; - const messageId = always ? 'missing' : 'extra'; - /** - * Gets the open brace token from a given node. - * @returns The token of the open brace. - */ - function getOpenBrace( - node: TSESTree.TSEnumDeclaration, - ): TSESTree.PunctuatorToken { - // guaranteed for enums - // This is the only change made here from the base rule - return context.sourceCode.getFirstToken(node, { - filter: token => - token.type === AST_TOKEN_TYPES.Punctuator && token.value === '{', - }) as TSESTree.PunctuatorToken; - } - - /** - * Checks whether or not: - * - given tokens are on same line. - * - there is/isn't a space between given tokens. - * @param left A token to check. - * @param right The token which is next to `left`. - * @returns - * When the option is `"always"`, `true` if there are one or more spaces between given tokens. - * When the option is `"never"`, `true` if there are not any spaces between given tokens. - * If given tokens are not on same line, it's always `true`. - */ - function isValid(left: TSESTree.Token, right: TSESTree.Token): boolean { - return ( - !isTokenOnSameLine(left, right) || - context.sourceCode.isSpaceBetween(left, right) === always - ); - } - - /** - * Checks and reports invalid spacing style inside braces. - */ - function checkSpacingInsideBraces(node: TSESTree.TSEnumDeclaration): void { - // Gets braces and the first/last token of content. - const openBrace = getOpenBrace(node); - const closeBrace = context.sourceCode.getLastToken(node)!; - const firstToken = context.sourceCode.getTokenAfter(openBrace, { - includeComments: true, - })!; - const lastToken = context.sourceCode.getTokenBefore(closeBrace, { - includeComments: true, - })!; - - // Skip if the node is invalid or empty. - if ( - openBrace.value !== '{' || - closeBrace.type !== AST_TOKEN_TYPES.Punctuator || - closeBrace.value !== '}' || - firstToken === closeBrace - ) { - return; - } - - // Skip line comments for option never - if (!always && firstToken.type === AST_TOKEN_TYPES.Line) { - return; - } - - if (!isValid(openBrace, firstToken)) { - let loc = openBrace.loc; - - if (messageId === 'extra') { - loc = { - start: openBrace.loc.end, - end: firstToken.loc.start, - }; - } - - context.report({ - node, - loc, - messageId, - data: { - location: 'after', - token: openBrace.value, - }, - fix(fixer) { - if (always) { - return fixer.insertTextBefore(firstToken, ' '); - } - - return fixer.removeRange([openBrace.range[1], firstToken.range[0]]); - }, - }); - } - if (!isValid(lastToken, closeBrace)) { - let loc = closeBrace.loc; - - if (messageId === 'extra') { - loc = { - start: lastToken.loc.end, - end: closeBrace.loc.start, - }; - } - context.report({ - node, - loc, - messageId, - data: { - location: 'before', - token: closeBrace.value, - }, - fix(fixer) { - if (always) { - return fixer.insertTextAfter(lastToken, ' '); - } - - return fixer.removeRange([lastToken.range[1], closeBrace.range[0]]); - }, - }); - } - } - return { - ...baseRules, - - // This code worked "out of the box" for interface and type literal - // Enums were very close to match as well, the only reason they are not is that was that enums don't have a body node in the parser - // So the opening brace punctuator starts in the middle of the node - `getFirstToken` in - // the base rule did not filter for the first opening brace punctuator - TSInterfaceBody: baseRules.BlockStatement as never, - TSTypeLiteral: baseRules.BlockStatement as never, - TSEnumDeclaration: checkSpacingInsideBraces, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/brace-style.ts b/packages/eslint-plugin/src/rules/brace-style.ts deleted file mode 100644 index e38b9240fe34..000000000000 --- a/packages/eslint-plugin/src/rules/brace-style.ts +++ /dev/null @@ -1,142 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { TSESTree } from '@typescript-eslint/utils'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule, isTokenOnSameLine } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('brace-style'); - -export type Options = InferOptionsTypeFromRule; -export type MessageIds = InferMessageIdsTypeFromRule; - -export default createRule({ - name: 'brace-style', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/brace-style'], - type: 'layout', - docs: { - description: 'Enforce consistent brace style for blocks', - extendsBaseRule: true, - }, - messages: baseRule.meta.messages, - fixable: baseRule.meta.fixable, - hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, - }, - defaultOptions: ['1tbs'], - create(context) { - const [style, { allowSingleLine } = { allowSingleLine: false }] = - // eslint-disable-next-line no-restricted-syntax -- Use raw options for extended rules. - context.options; - - const isAllmanStyle = style === 'allman'; - - const rules = baseRule.create(context); - - /** - * Checks a pair of curly brackets based on the user's config - */ - function validateCurlyPair( - openingCurlyToken: TSESTree.Token, - closingCurlyToken: TSESTree.Token, - ): void { - if ( - allowSingleLine && - isTokenOnSameLine(openingCurlyToken, closingCurlyToken) - ) { - return; - } - - const tokenBeforeOpeningCurly = - context.sourceCode.getTokenBefore(openingCurlyToken)!; - const tokenBeforeClosingCurly = - context.sourceCode.getTokenBefore(closingCurlyToken)!; - const tokenAfterOpeningCurly = - context.sourceCode.getTokenAfter(openingCurlyToken)!; - - if ( - !isAllmanStyle && - !isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurlyToken) - ) { - context.report({ - node: openingCurlyToken, - messageId: 'nextLineOpen', - fix: fixer => { - const textRange: TSESTree.Range = [ - tokenBeforeOpeningCurly.range[1], - openingCurlyToken.range[0], - ]; - const textBetween = context.sourceCode.text.slice( - textRange[0], - textRange[1], - ); - - if (textBetween.trim()) { - return null; - } - - return fixer.replaceTextRange(textRange, ' '); - }, - }); - } - - if ( - isAllmanStyle && - isTokenOnSameLine(tokenBeforeOpeningCurly, openingCurlyToken) - ) { - context.report({ - node: openingCurlyToken, - messageId: 'sameLineOpen', - fix: fixer => fixer.insertTextBefore(openingCurlyToken, '\n'), - }); - } - - if ( - isTokenOnSameLine(openingCurlyToken, tokenAfterOpeningCurly) && - tokenAfterOpeningCurly !== closingCurlyToken - ) { - context.report({ - node: openingCurlyToken, - messageId: 'blockSameLine', - fix: fixer => fixer.insertTextAfter(openingCurlyToken, '\n'), - }); - } - - if ( - isTokenOnSameLine(tokenBeforeClosingCurly, closingCurlyToken) && - tokenBeforeClosingCurly !== openingCurlyToken - ) { - context.report({ - node: closingCurlyToken, - messageId: 'singleLineClose', - fix: fixer => fixer.insertTextBefore(closingCurlyToken, '\n'), - }); - } - } - - return { - ...rules, - 'TSInterfaceBody, TSModuleBlock'( - node: TSESTree.TSInterfaceBody | TSESTree.TSModuleBlock, - ): void { - const openingCurly = context.sourceCode.getFirstToken(node)!; - const closingCurly = context.sourceCode.getLastToken(node)!; - - validateCurlyPair(openingCurly, closingCurly); - }, - TSEnumDeclaration(node): void { - const closingCurly = context.sourceCode.getLastToken(node)!; - const openingCurly = context.sourceCode.getTokenBefore( - node.body.members.length ? node.body.members[0] : closingCurly, - )!; - - validateCurlyPair(openingCurly, closingCurly); - }, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/comma-dangle.ts b/packages/eslint-plugin/src/rules/comma-dangle.ts deleted file mode 100644 index aad665ad58b1..000000000000 --- a/packages/eslint-plugin/src/rules/comma-dangle.ts +++ /dev/null @@ -1,190 +0,0 @@ -import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule, isCommaToken } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('comma-dangle'); - -export type Options = InferOptionsTypeFromRule; -export type MessageIds = InferMessageIdsTypeFromRule; - -type Option = Options[0]; -type NormalizedOptions = Required< - Pick, 'enums' | 'generics' | 'tuples'> ->; - -const OPTION_VALUE_SCHEME = [ - 'always-multiline', - 'always', - 'never', - 'only-multiline', -]; - -const DEFAULT_OPTION_VALUE = 'never'; - -function normalizeOptions(options: Option): NormalizedOptions { - if (typeof options === 'string') { - return { - enums: options, - generics: options, - tuples: options, - }; - } - return { - enums: options.enums ?? DEFAULT_OPTION_VALUE, - generics: options.generics ?? DEFAULT_OPTION_VALUE, - tuples: options.tuples ?? DEFAULT_OPTION_VALUE, - }; -} - -export default createRule({ - name: 'comma-dangle', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/comma-dangle'], - type: 'layout', - docs: { - description: 'Require or disallow trailing commas', - extendsBaseRule: true, - }, - schema: { - $defs: { - value: { - type: 'string', - enum: OPTION_VALUE_SCHEME, - }, - valueWithIgnore: { - type: 'string', - enum: [...OPTION_VALUE_SCHEME, 'ignore'], - }, - }, - type: 'array', - items: [ - { - oneOf: [ - { - $ref: '#/$defs/value', - }, - { - type: 'object', - properties: { - arrays: { $ref: '#/$defs/valueWithIgnore' }, - objects: { $ref: '#/$defs/valueWithIgnore' }, - imports: { $ref: '#/$defs/valueWithIgnore' }, - exports: { $ref: '#/$defs/valueWithIgnore' }, - functions: { $ref: '#/$defs/valueWithIgnore' }, - enums: { $ref: '#/$defs/valueWithIgnore' }, - generics: { $ref: '#/$defs/valueWithIgnore' }, - tuples: { $ref: '#/$defs/valueWithIgnore' }, - }, - additionalProperties: false, - }, - ], - }, - ], - additionalItems: false, - }, - fixable: 'code', - hasSuggestions: baseRule.meta.hasSuggestions, - messages: baseRule.meta.messages, - }, - defaultOptions: ['never'], - create(context, [options]) { - const rules = baseRule.create(context); - - const normalizedOptions = normalizeOptions(options); - - const predicate = { - always: forceComma, - 'always-multiline': forceCommaIfMultiline, - 'only-multiline': allowCommaIfMultiline, - never: forbidComma, - // https://github.com/typescript-eslint/typescript-eslint/issues/7220 - // eslint-disable-next-line @typescript-eslint/explicit-function-return-type, @typescript-eslint/no-empty-function - ignore: () => {}, - }; - - function last(nodes: TSESTree.Node[]): TSESTree.Node | null { - return nodes[nodes.length - 1] ?? null; - } - - function getLastItem(node: TSESTree.Node): TSESTree.Node | null { - switch (node.type) { - case AST_NODE_TYPES.TSEnumDeclaration: - return last(node.body.members); - case AST_NODE_TYPES.TSTypeParameterDeclaration: - return last(node.params); - case AST_NODE_TYPES.TSTupleType: - return last(node.elementTypes); - default: - return null; - } - } - - function getTrailingToken(node: TSESTree.Node): TSESTree.Token | null { - const last = getLastItem(node); - const trailing = last && context.sourceCode.getTokenAfter(last); - return trailing; - } - - function isMultiline(node: TSESTree.Node): boolean { - const last = getLastItem(node); - const lastToken = context.sourceCode.getLastToken(node); - return last?.loc.end.line !== lastToken?.loc.end.line; - } - - function forbidComma(node: TSESTree.Node): void { - const last = getLastItem(node); - const trailing = getTrailingToken(node); - if (last && trailing && isCommaToken(trailing)) { - context.report({ - node, - messageId: 'unexpected', - fix(fixer) { - return fixer.remove(trailing); - }, - }); - } - } - - function forceComma(node: TSESTree.Node): void { - const last = getLastItem(node); - const trailing = getTrailingToken(node); - if (last && trailing && !isCommaToken(trailing)) { - context.report({ - node, - messageId: 'missing', - fix(fixer) { - return fixer.insertTextAfter(last, ','); - }, - }); - } - } - - function allowCommaIfMultiline(node: TSESTree.Node): void { - if (!isMultiline(node)) { - forbidComma(node); - } - } - - function forceCommaIfMultiline(node: TSESTree.Node): void { - if (isMultiline(node)) { - forceComma(node); - } else { - forbidComma(node); - } - } - - return { - ...rules, - TSEnumDeclaration: predicate[normalizedOptions.enums], - TSTypeParameterDeclaration: predicate[normalizedOptions.generics], - TSTupleType: predicate[normalizedOptions.tuples], - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/comma-spacing.ts b/packages/eslint-plugin/src/rules/comma-spacing.ts deleted file mode 100644 index cd283e4c97ac..000000000000 --- a/packages/eslint-plugin/src/rules/comma-spacing.ts +++ /dev/null @@ -1,201 +0,0 @@ -import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_TOKEN_TYPES } from '@typescript-eslint/utils'; - -import { - createRule, - isClosingBraceToken, - isClosingBracketToken, - isClosingParenToken, - isCommaToken, - isTokenOnSameLine, -} from '../util'; - -type Options = [ - { - before: boolean; - after: boolean; - }, -]; -type MessageIds = 'missing' | 'unexpected'; - -export default createRule({ - name: 'comma-spacing', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/comma-spacing'], - type: 'layout', - docs: { - description: 'Enforce consistent spacing before and after commas', - extendsBaseRule: true, - }, - fixable: 'whitespace', - schema: [ - { - type: 'object', - properties: { - before: { - type: 'boolean', - default: false, - }, - after: { - type: 'boolean', - default: true, - }, - }, - additionalProperties: false, - }, - ], - messages: { - unexpected: `There should be no space {{loc}} ','.`, - missing: `A space is required {{loc}} ','.`, - }, - }, - defaultOptions: [ - { - before: false, - after: true, - }, - ], - create(context, [{ before: spaceBefore, after: spaceAfter }]) { - const tokensAndComments = context.sourceCode.tokensAndComments; - const ignoredTokens = new Set(); - - /** - * Adds null elements of the ArrayExpression or ArrayPattern node to the ignore list - * @param node node to evaluate - */ - function addNullElementsToIgnoreList( - node: TSESTree.ArrayExpression | TSESTree.ArrayPattern, - ): void { - let previousToken = context.sourceCode.getFirstToken(node); - for (const element of node.elements) { - let token: TSESTree.Token | null; - if (element == null) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - token = context.sourceCode.getTokenAfter(previousToken!); - if (token && isCommaToken(token)) { - ignoredTokens.add(token); - } - } else { - token = context.sourceCode.getTokenAfter(element); - } - - previousToken = token; - } - } - - /** - * Adds type parameters trailing comma token to the ignore list - * @param node node to evaluate - */ - function addTypeParametersTrailingCommaToIgnoreList( - node: TSESTree.TSTypeParameterDeclaration, - ): void { - const paramLength = node.params.length; - if (paramLength) { - const param = node.params[paramLength - 1]; - const afterToken = context.sourceCode.getTokenAfter(param); - if (afterToken && isCommaToken(afterToken)) { - ignoredTokens.add(afterToken); - } - } - } - - /** - * Validates the spacing around a comma token. - * @param commaToken The token representing the comma - * @param prevToken The last token before the comma - * @param nextToken The first token after the comma - */ - function validateCommaSpacing( - commaToken: TSESTree.PunctuatorToken, - prevToken: TSESTree.Token | null, - nextToken: TSESTree.Token | null, - ): void { - if ( - prevToken && - isTokenOnSameLine(prevToken, commaToken) && - spaceBefore !== context.sourceCode.isSpaceBetween(prevToken, commaToken) - ) { - context.report({ - node: commaToken, - data: { - loc: 'before', - }, - messageId: spaceBefore ? 'missing' : 'unexpected', - fix: fixer => - spaceBefore - ? fixer.insertTextBefore(commaToken, ' ') - : fixer.replaceTextRange( - [prevToken.range[1], commaToken.range[0]], - '', - ), - }); - } - - if (nextToken && isClosingParenToken(nextToken)) { - return; - } - - if ( - spaceAfter && - nextToken && - (isClosingBraceToken(nextToken) || isClosingBracketToken(nextToken)) - ) { - return; - } - - if (!spaceAfter && nextToken && nextToken.type === AST_TOKEN_TYPES.Line) { - return; - } - - if ( - nextToken && - isTokenOnSameLine(commaToken, nextToken) && - spaceAfter !== context.sourceCode.isSpaceBetween(commaToken, nextToken) - ) { - context.report({ - node: commaToken, - data: { - loc: 'after', - }, - messageId: spaceAfter ? 'missing' : 'unexpected', - fix: fixer => - spaceAfter - ? fixer.insertTextAfter(commaToken, ' ') - : fixer.replaceTextRange( - [commaToken.range[1], nextToken.range[0]], - '', - ), - }); - } - } - - return { - TSTypeParameterDeclaration: addTypeParametersTrailingCommaToIgnoreList, - ArrayExpression: addNullElementsToIgnoreList, - ArrayPattern: addNullElementsToIgnoreList, - - 'Program:exit'(): void { - tokensAndComments.forEach((token, i) => { - if (!isCommaToken(token)) { - return; - } - - const prevToken = tokensAndComments[i - 1]; - const nextToken = tokensAndComments.at(i + 1); - - validateCommaSpacing( - token, - isCommaToken(prevToken) || ignoredTokens.has(token) - ? null - : prevToken, - (nextToken && isCommaToken(nextToken)) || ignoredTokens.has(token) - ? null - : nextToken ?? null, - ); - }); - }, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/func-call-spacing.ts b/packages/eslint-plugin/src/rules/func-call-spacing.ts deleted file mode 100644 index 80df92aaa701..000000000000 --- a/packages/eslint-plugin/src/rules/func-call-spacing.ts +++ /dev/null @@ -1,185 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { TSESTree } from '@typescript-eslint/utils'; - -import { - createRule, - isNotOptionalChainPunctuator, - isOpeningParenToken, - isOptionalCallExpression, - LINEBREAK_MATCHER, -} from '../util'; - -export type Options = [ - 'always' | 'never', - { - allowNewlines?: boolean; - }?, -]; -export type MessageIds = - | 'missing' - | 'unexpectedNewline' - | 'unexpectedWhitespace'; - -export default createRule({ - name: 'func-call-spacing', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/func-call-spacing'], - type: 'layout', - docs: { - description: - 'Require or disallow spacing between function identifiers and their invocations', - extendsBaseRule: true, - }, - fixable: 'whitespace', - schema: { - anyOf: [ - { - type: 'array', - items: [ - { - type: 'string', - enum: ['never'], - }, - ], - minItems: 0, - maxItems: 1, - }, - { - type: 'array', - items: [ - { - type: 'string', - enum: ['always'], - }, - { - type: 'object', - properties: { - allowNewlines: { - type: 'boolean', - }, - }, - additionalProperties: false, - }, - ], - minItems: 0, - maxItems: 2, - }, - ], - }, - - messages: { - unexpectedWhitespace: - 'Unexpected whitespace between function name and paren.', - unexpectedNewline: 'Unexpected newline between function name and paren.', - missing: 'Missing space between function name and paren.', - }, - }, - defaultOptions: ['never', {}], - create(context, [option, config]) { - const text = context.sourceCode.getText(); - - /** - * Check if open space is present in a function name - * @param node node to evaluate - * @private - */ - function checkSpacing( - node: TSESTree.CallExpression | TSESTree.NewExpression, - ): void { - const isOptionalCall = isOptionalCallExpression(node); - - const closingParenToken = context.sourceCode.getLastToken(node)!; - const lastCalleeTokenWithoutPossibleParens = - context.sourceCode.getLastToken(node.typeArguments ?? node.callee)!; - const openingParenToken = context.sourceCode.getFirstTokenBetween( - lastCalleeTokenWithoutPossibleParens, - closingParenToken, - isOpeningParenToken, - ); - if (!openingParenToken || openingParenToken.range[1] >= node.range[1]) { - // new expression with no parens... - return; - } - const lastCalleeToken = context.sourceCode.getTokenBefore( - openingParenToken, - isNotOptionalChainPunctuator, - )!; - - const textBetweenTokens = text - .slice(lastCalleeToken.range[1], openingParenToken.range[0]) - .replace(/\/\*.*?\*\//gu, ''); - const hasWhitespace = /\s/u.test(textBetweenTokens); - const hasNewline = - hasWhitespace && LINEBREAK_MATCHER.test(textBetweenTokens); - - if (option === 'never') { - if (hasWhitespace) { - return context.report({ - node, - loc: lastCalleeToken.loc.start, - messageId: 'unexpectedWhitespace', - fix(fixer) { - /* - * Only autofix if there is no newline - * https://github.com/eslint/eslint/issues/7787 - */ - if ( - !hasNewline && - // don't fix optional calls - !isOptionalCall - ) { - return fixer.removeRange([ - lastCalleeToken.range[1], - openingParenToken.range[0], - ]); - } - - return null; - }, - }); - } - } else if (isOptionalCall) { - // disallow: - // foo?. (); - // foo ?.(); - // foo ?. (); - if (hasWhitespace || hasNewline) { - context.report({ - node, - loc: lastCalleeToken.loc.start, - messageId: 'unexpectedWhitespace', - }); - } - } else { - if (!hasWhitespace) { - context.report({ - node, - loc: lastCalleeToken.loc.start, - messageId: 'missing', - fix(fixer) { - return fixer.insertTextBefore(openingParenToken, ' '); - }, - }); - } else if (!config!.allowNewlines && hasNewline) { - context.report({ - node, - loc: lastCalleeToken.loc.start, - messageId: 'unexpectedNewline', - fix(fixer) { - return fixer.replaceTextRange( - [lastCalleeToken.range[1], openingParenToken.range[0]], - ' ', - ); - }, - }); - } - } - } - - return { - CallExpression: checkSpacing, - NewExpression: checkSpacing, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/indent.ts b/packages/eslint-plugin/src/rules/indent.ts deleted file mode 100644 index 8fc8a4191a9c..000000000000 --- a/packages/eslint-plugin/src/rules/indent.ts +++ /dev/null @@ -1,495 +0,0 @@ -/** - * Note this file is rather type-unsafe in its current state. - * This is due to some really funky type conversions between different node types. - * This is done intentionally based on the internal implementation of the base indent rule. - */ -/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment */ - -import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('indent'); - -type Options = InferOptionsTypeFromRule; -type MessageIds = InferMessageIdsTypeFromRule; - -const KNOWN_NODES = new Set([ - // Class properties aren't yet supported by eslint... - AST_NODE_TYPES.PropertyDefinition, - - // ts keywords - AST_NODE_TYPES.TSAbstractKeyword, - AST_NODE_TYPES.TSAnyKeyword, - AST_NODE_TYPES.TSBooleanKeyword, - AST_NODE_TYPES.TSNeverKeyword, - AST_NODE_TYPES.TSNumberKeyword, - AST_NODE_TYPES.TSStringKeyword, - AST_NODE_TYPES.TSSymbolKeyword, - AST_NODE_TYPES.TSUndefinedKeyword, - AST_NODE_TYPES.TSUnknownKeyword, - AST_NODE_TYPES.TSVoidKeyword, - AST_NODE_TYPES.TSNullKeyword, - - // ts specific nodes we want to support - AST_NODE_TYPES.TSAbstractPropertyDefinition, - AST_NODE_TYPES.TSAbstractMethodDefinition, - AST_NODE_TYPES.TSArrayType, - AST_NODE_TYPES.TSAsExpression, - AST_NODE_TYPES.TSCallSignatureDeclaration, - AST_NODE_TYPES.TSConditionalType, - AST_NODE_TYPES.TSConstructorType, - AST_NODE_TYPES.TSConstructSignatureDeclaration, - AST_NODE_TYPES.TSDeclareFunction, - AST_NODE_TYPES.TSEmptyBodyFunctionExpression, - AST_NODE_TYPES.TSEnumDeclaration, - AST_NODE_TYPES.TSEnumMember, - AST_NODE_TYPES.TSExportAssignment, - AST_NODE_TYPES.TSExternalModuleReference, - AST_NODE_TYPES.TSFunctionType, - AST_NODE_TYPES.TSImportType, - AST_NODE_TYPES.TSIndexedAccessType, - AST_NODE_TYPES.TSIndexSignature, - AST_NODE_TYPES.TSInferType, - AST_NODE_TYPES.TSInterfaceBody, - AST_NODE_TYPES.TSInterfaceDeclaration, - AST_NODE_TYPES.TSInterfaceHeritage, - AST_NODE_TYPES.TSIntersectionType, - AST_NODE_TYPES.TSImportEqualsDeclaration, - AST_NODE_TYPES.TSLiteralType, - AST_NODE_TYPES.TSMappedType, - AST_NODE_TYPES.TSMethodSignature, - 'TSMinusToken', - AST_NODE_TYPES.TSModuleBlock, - AST_NODE_TYPES.TSModuleDeclaration, - AST_NODE_TYPES.TSNonNullExpression, - AST_NODE_TYPES.TSParameterProperty, - 'TSPlusToken', - AST_NODE_TYPES.TSPropertySignature, - AST_NODE_TYPES.TSQualifiedName, - 'TSQuestionToken', - AST_NODE_TYPES.TSRestType, - AST_NODE_TYPES.TSThisType, - AST_NODE_TYPES.TSTupleType, - AST_NODE_TYPES.TSTypeAnnotation, - AST_NODE_TYPES.TSTypeLiteral, - AST_NODE_TYPES.TSTypeOperator, - AST_NODE_TYPES.TSTypeParameter, - AST_NODE_TYPES.TSTypeParameterDeclaration, - AST_NODE_TYPES.TSTypeParameterInstantiation, - AST_NODE_TYPES.TSTypeReference, - AST_NODE_TYPES.TSUnionType, - AST_NODE_TYPES.Decorator, -]); - -export default createRule({ - name: 'indent', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/indent'], - type: 'layout', - docs: { - description: 'Enforce consistent indentation', - // too opinionated to be recommended - extendsBaseRule: true, - }, - fixable: 'whitespace', - hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, - messages: baseRule.meta.messages, - }, - defaultOptions: [ - // typescript docs and playground use 4 space indent - 4, - { - // typescript docs indent the case from the switch - // https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-8.html#example-4 - SwitchCase: 1, - flatTernaryExpressions: false, - ignoredNodes: [], - }, - ], - create(context, optionsWithDefaults) { - // because we extend the base rule, have to update opts on the context - // the context defines options as readonly though... - const contextWithDefaults: typeof context = Object.create(context, { - options: { - writable: false, - configurable: false, - value: optionsWithDefaults, - }, - }); - - const rules = baseRule.create(contextWithDefaults); - - /** - * Converts from a TSPropertySignature to a Property - * @param node a TSPropertySignature node - * @param [type] the type to give the new node - * @returns a Property node - */ - function TSPropertySignatureToProperty( - node: - | TSESTree.TSEnumMember - | TSESTree.TSPropertySignature - | TSESTree.TypeElement, - type: - | AST_NODE_TYPES.Property - | AST_NODE_TYPES.PropertyDefinition = AST_NODE_TYPES.Property, - ): TSESTree.Node | null { - const base = { - // indent doesn't actually use these - key: null as any, - value: null as any, - - // Property flags - computed: false, - method: false, - kind: 'init', - // this will stop eslint from interrogating the type literal - shorthand: true, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - }; - if (type === AST_NODE_TYPES.Property) { - return { - type, - ...base, - } as TSESTree.Property; - } - return { - type, - accessibility: undefined, - declare: false, - decorators: [], - definite: false, - optional: false, - override: false, - readonly: false, - static: false, - typeAnnotation: undefined, - ...base, - } as TSESTree.PropertyDefinition; - } - - return Object.assign({}, rules, { - // overwrite the base rule here so we can use our KNOWN_NODES list instead - '*:exit'(node: TSESTree.Node) { - // For nodes we care about, skip the default handling, because it just marks the node as ignored... - if (!KNOWN_NODES.has(node.type)) { - rules['*:exit'](node); - } - }, - - VariableDeclaration(node: TSESTree.VariableDeclaration) { - // https://github.com/typescript-eslint/typescript-eslint/issues/441 - if (node.declarations.length === 0) { - return; - } - - return rules.VariableDeclaration(node); - }, - - TSAsExpression(node: TSESTree.TSAsExpression) { - // transform it to a BinaryExpression - return rules['BinaryExpression, LogicalExpression']({ - type: AST_NODE_TYPES.BinaryExpression, - operator: 'as' as any, - left: node.expression, - // the first typeAnnotation includes the as token - right: node.typeAnnotation as any, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - }); - }, - - TSConditionalType(node: TSESTree.TSConditionalType) { - // transform it to a ConditionalExpression - return rules.ConditionalExpression({ - type: AST_NODE_TYPES.ConditionalExpression, - test: { - parent: node, - type: AST_NODE_TYPES.BinaryExpression, - operator: 'extends' as any, - left: node.checkType as any, - right: node.extendsType as any, - - // location data - range: [node.checkType.range[0], node.extendsType.range[1]], - loc: { - start: node.checkType.loc.start, - end: node.extendsType.loc.end, - }, - }, - consequent: node.trueType as any, - alternate: node.falseType as any, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - }); - }, - - 'TSEnumDeclaration, TSTypeLiteral'( - node: TSESTree.TSEnumDeclaration | TSESTree.TSTypeLiteral, - ) { - // transform it to an ObjectExpression - return rules['ObjectExpression, ObjectPattern']({ - type: AST_NODE_TYPES.ObjectExpression, - properties: (node.type === AST_NODE_TYPES.TSEnumDeclaration - ? node.body.members - : node.members - ).map( - member => - TSPropertySignatureToProperty(member) as TSESTree.Property, - ), - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - }); - }, - - TSImportEqualsDeclaration(node: TSESTree.TSImportEqualsDeclaration) { - // transform it to an VariableDeclaration - // use VariableDeclaration instead of ImportDeclaration because it's essentially the same thing - const { id, moduleReference } = node; - - return rules.VariableDeclaration({ - type: AST_NODE_TYPES.VariableDeclaration, - kind: 'const' as const, - declarations: [ - { - type: AST_NODE_TYPES.VariableDeclarator, - range: [id.range[0], moduleReference.range[1]], - loc: { - start: id.loc.start, - end: moduleReference.loc.end, - }, - id: id, - init: { - type: AST_NODE_TYPES.CallExpression, - callee: { - type: AST_NODE_TYPES.Identifier, - name: 'require', - range: [ - moduleReference.range[0], - moduleReference.range[0] + 'require'.length, - ], - loc: { - start: moduleReference.loc.start, - end: { - line: moduleReference.loc.end.line, - column: moduleReference.loc.start.line + 'require'.length, - }, - }, - }, - arguments: - 'expression' in moduleReference - ? [moduleReference.expression] - : [], - - // location data - range: moduleReference.range, - loc: moduleReference.loc, - }, - } as TSESTree.VariableDeclarator, - ], - declare: false, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - }); - }, - - TSIndexedAccessType(node: TSESTree.TSIndexedAccessType) { - // convert to a MemberExpression - return rules['MemberExpression, JSXMemberExpression, MetaProperty']({ - type: AST_NODE_TYPES.MemberExpression, - object: node.objectType as any, - property: node.indexType as any, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - optional: false, - computed: true, - }); - }, - - TSInterfaceBody(node: TSESTree.TSInterfaceBody) { - // transform it to an ClassBody - return rules['BlockStatement, ClassBody']({ - type: AST_NODE_TYPES.ClassBody, - body: node.body.map( - p => - TSPropertySignatureToProperty( - p, - AST_NODE_TYPES.PropertyDefinition, - ) as TSESTree.PropertyDefinition, - ), - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - }); - }, - - 'TSInterfaceDeclaration[extends.length > 0]'( - node: TSESTree.TSInterfaceDeclaration, - ) { - // transform it to a ClassDeclaration - return rules[ - 'ClassDeclaration[superClass], ClassExpression[superClass]' - ]({ - type: AST_NODE_TYPES.ClassDeclaration, - body: node.body as any, - id: null, - // TODO: This is invalid, there can be more than one extends in interface - superClass: node.extends[0].expression as any, - abstract: false, - declare: false, - decorators: [], - implements: [], - superTypeArguments: undefined, - superTypeParameters: undefined, - typeParameters: undefined, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - }); - }, - - TSMappedType(node: TSESTree.TSMappedType) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const squareBracketStart = context.sourceCode.getTokenBefore( - // eslint-disable-next-line deprecation/deprecation - node.typeParameter, - )!; - - // transform it to an ObjectExpression - return rules['ObjectExpression, ObjectPattern']({ - type: AST_NODE_TYPES.ObjectExpression, - properties: [ - { - parent: node, - type: AST_NODE_TYPES.Property, - // eslint-disable-next-line deprecation/deprecation - key: node.typeParameter as any, - value: node.typeAnnotation as any, - - // location data - range: [ - squareBracketStart.range[0], - node.typeAnnotation - ? node.typeAnnotation.range[1] - : squareBracketStart.range[0], - ], - loc: { - start: squareBracketStart.loc.start, - end: node.typeAnnotation - ? node.typeAnnotation.loc.end - : squareBracketStart.loc.end, - }, - kind: 'init' as const, - computed: false, - method: false, - optional: false, - shorthand: false, - }, - ], - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - }); - }, - - TSModuleBlock(node: TSESTree.TSModuleBlock) { - // transform it to a BlockStatement - return rules['BlockStatement, ClassBody']({ - type: AST_NODE_TYPES.BlockStatement, - body: node.body as any, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - }); - }, - - TSQualifiedName(node: TSESTree.TSQualifiedName) { - return rules['MemberExpression, JSXMemberExpression, MetaProperty']({ - type: AST_NODE_TYPES.MemberExpression, - object: node.left as any, - property: node.right as any, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - optional: false, - computed: false, - }); - }, - - TSTupleType(node: TSESTree.TSTupleType) { - // transform it to an ArrayExpression - return rules['ArrayExpression, ArrayPattern']({ - type: AST_NODE_TYPES.ArrayExpression, - elements: node.elementTypes as any, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - }); - }, - - TSTypeParameterDeclaration(node: TSESTree.TSTypeParameterDeclaration) { - if (!node.params.length) { - return; - } - - const [name, ...attributes] = node.params; - - // JSX is about the closest we can get because the angle brackets - // it's not perfect but it works! - return rules.JSXOpeningElement({ - type: AST_NODE_TYPES.JSXOpeningElement, - selfClosing: false, - name: name as any, - attributes: attributes as any, - typeArguments: undefined, - typeParameters: undefined, - - // location data - parent: node.parent, - range: node.range, - loc: node.loc, - }); - }, - }); - }, -}); diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 64c6a872ed69..ae1da2b6d438 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -6,12 +6,8 @@ import awaitThenable from './await-thenable'; import banTsComment from './ban-ts-comment'; import banTslintComment from './ban-tslint-comment'; import banTypes from './ban-types'; -import blockSpacing from './block-spacing'; -import braceStyle from './brace-style'; import classLiteralPropertyStyle from './class-literal-property-style'; import classMethodsUseThis from './class-methods-use-this'; -import commaDangle from './comma-dangle'; -import commaSpacing from './comma-spacing'; import consistentGenericConstructors from './consistent-generic-constructors'; import consistentIndexedObjectStyle from './consistent-indexed-object-style'; import consistentReturn from './consistent-return'; @@ -24,15 +20,8 @@ import dotNotation from './dot-notation'; import explicitFunctionReturnType from './explicit-function-return-type'; import explicitMemberAccessibility from './explicit-member-accessibility'; import explicitModuleBoundaryTypes from './explicit-module-boundary-types'; -import funcCallSpacing from './func-call-spacing'; -import indent from './indent'; import initDeclarations from './init-declarations'; -import keySpacing from './key-spacing'; -import keywordSpacing from './keyword-spacing'; -import linesAroundComment from './lines-around-comment'; -import linesBetweenClassMembers from './lines-between-class-members'; import maxParams from './max-params'; -import memberDelimiterStyle from './member-delimiter-style'; import memberOrdering from './member-ordering'; import methodSignatureStyle from './method-signature-style'; import namingConvention from './naming-convention'; @@ -49,8 +38,6 @@ import noEmptyFunction from './no-empty-function'; import noEmptyInterface from './no-empty-interface'; import noExplicitAny from './no-explicit-any'; import noExtraNonNullAssertion from './no-extra-non-null-assertion'; -import noExtraParens from './no-extra-parens'; -import noExtraSemi from './no-extra-semi'; import noExtraneousClass from './no-extraneous-class'; import noFloatingPromises from './no-floating-promises'; import noForInArray from './no-for-in-array'; @@ -100,9 +87,7 @@ import noUselessEmptyExport from './no-useless-empty-export'; import noUselessTemplateLiterals from './no-useless-template-literals'; import noVarRequires from './no-var-requires'; import nonNullableTypeAssertionStyle from './non-nullable-type-assertion-style'; -import objectCurlySpacing from './object-curly-spacing'; import onlyThrowError from './only-throw-error'; -import paddingLineBetweenStatements from './padding-line-between-statements'; import parameterProperties from './parameter-properties'; import preferAsConst from './prefer-as-const'; import preferDestructuring from './prefer-destructuring'; @@ -124,21 +109,15 @@ import preferReturnThisType from './prefer-return-this-type'; import preferStringStartsEndsWith from './prefer-string-starts-ends-with'; import preferTsExpectError from './prefer-ts-expect-error'; import promiseFunctionAsync from './promise-function-async'; -import quotes from './quotes'; import requireArraySortCompare from './require-array-sort-compare'; import requireAwait from './require-await'; import restrictPlusOperands from './restrict-plus-operands'; import restrictTemplateExpressions from './restrict-template-expressions'; import returnAwait from './return-await'; -import semi from './semi'; import sortTypeConstituents from './sort-type-constituents'; -import spaceBeforeBlocks from './space-before-blocks'; -import spaceBeforeFunctionParen from './space-before-function-paren'; -import spaceInfixOps from './space-infix-ops'; import strictBooleanExpressions from './strict-boolean-expressions'; import switchExhaustivenessCheck from './switch-exhaustiveness-check'; import tripleSlashReference from './triple-slash-reference'; -import typeAnnotationSpacing from './type-annotation-spacing'; import typedef from './typedef'; import unboundMethod from './unbound-method'; import unifiedSignatures from './unified-signatures'; @@ -151,12 +130,8 @@ export default { 'ban-ts-comment': banTsComment, 'ban-tslint-comment': banTslintComment, 'ban-types': banTypes, - 'block-spacing': blockSpacing, - 'brace-style': braceStyle, 'class-literal-property-style': classLiteralPropertyStyle, 'class-methods-use-this': classMethodsUseThis, - 'comma-dangle': commaDangle, - 'comma-spacing': commaSpacing, 'consistent-generic-constructors': consistentGenericConstructors, 'consistent-indexed-object-style': consistentIndexedObjectStyle, 'consistent-return': consistentReturn, @@ -169,15 +144,8 @@ export default { 'explicit-function-return-type': explicitFunctionReturnType, 'explicit-member-accessibility': explicitMemberAccessibility, 'explicit-module-boundary-types': explicitModuleBoundaryTypes, - 'func-call-spacing': funcCallSpacing, - indent: indent, 'init-declarations': initDeclarations, - 'key-spacing': keySpacing, - 'keyword-spacing': keywordSpacing, - 'lines-around-comment': linesAroundComment, - 'lines-between-class-members': linesBetweenClassMembers, 'max-params': maxParams, - 'member-delimiter-style': memberDelimiterStyle, 'member-ordering': memberOrdering, 'method-signature-style': methodSignatureStyle, 'naming-convention': namingConvention, @@ -194,8 +162,6 @@ export default { 'no-empty-interface': noEmptyInterface, 'no-explicit-any': noExplicitAny, 'no-extra-non-null-assertion': noExtraNonNullAssertion, - 'no-extra-parens': noExtraParens, - 'no-extra-semi': noExtraSemi, 'no-extraneous-class': noExtraneousClass, 'no-floating-promises': noFloatingPromises, 'no-for-in-array': noForInArray, @@ -245,9 +211,7 @@ export default { 'no-useless-template-literals': noUselessTemplateLiterals, 'no-var-requires': noVarRequires, 'non-nullable-type-assertion-style': nonNullableTypeAssertionStyle, - 'object-curly-spacing': objectCurlySpacing, 'only-throw-error': onlyThrowError, - 'padding-line-between-statements': paddingLineBetweenStatements, 'parameter-properties': parameterProperties, 'prefer-as-const': preferAsConst, 'prefer-destructuring': preferDestructuring, @@ -269,21 +233,15 @@ export default { 'prefer-string-starts-ends-with': preferStringStartsEndsWith, 'prefer-ts-expect-error': preferTsExpectError, 'promise-function-async': promiseFunctionAsync, - quotes: quotes, 'require-array-sort-compare': requireArraySortCompare, 'require-await': requireAwait, 'restrict-plus-operands': restrictPlusOperands, 'restrict-template-expressions': restrictTemplateExpressions, 'return-await': returnAwait, - semi: semi, 'sort-type-constituents': sortTypeConstituents, - 'space-before-blocks': spaceBeforeBlocks, - 'space-before-function-paren': spaceBeforeFunctionParen, - 'space-infix-ops': spaceInfixOps, 'strict-boolean-expressions': strictBooleanExpressions, 'switch-exhaustiveness-check': switchExhaustivenessCheck, 'triple-slash-reference': tripleSlashReference, - 'type-annotation-spacing': typeAnnotationSpacing, typedef: typedef, 'unbound-method': unboundMethod, 'unified-signatures': unifiedSignatures, diff --git a/packages/eslint-plugin/src/rules/key-spacing.ts b/packages/eslint-plugin/src/rules/key-spacing.ts deleted file mode 100644 index 7ca41df72cc0..000000000000 --- a/packages/eslint-plugin/src/rules/key-spacing.ts +++ /dev/null @@ -1,440 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { - createRule, - getStringLength, - isClosingBracketToken, - isColonToken, -} from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('key-spacing'); - -export type Options = InferOptionsTypeFromRule; -export type MessageIds = InferMessageIdsTypeFromRule; - -// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -const baseSchema = Array.isArray(baseRule.meta.schema) - ? baseRule.meta.schema[0] - : baseRule.meta.schema; - -export default createRule({ - name: 'key-spacing', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/key-spacing'], - type: 'layout', - docs: { - description: - 'Enforce consistent spacing between property names and type annotations in types and interfaces', - extendsBaseRule: true, - }, - fixable: 'whitespace', - hasSuggestions: baseRule.meta.hasSuggestions, - schema: [baseSchema], - messages: baseRule.meta.messages, - }, - defaultOptions: [{}], - - create(context, [options]) { - const baseRules = baseRule.create(context); - - /** - * @returns the column of the position after converting all unicode characters in the line to 1 char length - */ - function adjustedColumn(position: TSESTree.Position): number { - const line = position.line - 1; // position.line is 1-indexed - return getStringLength( - context.sourceCode.lines.at(line)!.slice(0, position.column), - ); - } - - /** - * Starting from the given a node (a property.key node here) looks forward - * until it finds the last token before a colon punctuator and returns it. - */ - function getLastTokenBeforeColon(node: TSESTree.Node): TSESTree.Token { - const colonToken = context.sourceCode.getTokenAfter(node, isColonToken)!; - - return context.sourceCode.getTokenBefore(colonToken)!; - } - - type KeyTypeNode = - | TSESTree.PropertyDefinition - | TSESTree.TSIndexSignature - | TSESTree.TSPropertySignature; - - type KeyTypeNodeWithTypeAnnotation = KeyTypeNode & { - typeAnnotation: TSESTree.TSTypeAnnotation; - }; - - function isKeyTypeNode( - node: TSESTree.Node, - ): node is KeyTypeNodeWithTypeAnnotation { - return ( - (node.type === AST_NODE_TYPES.TSPropertySignature || - node.type === AST_NODE_TYPES.TSIndexSignature || - node.type === AST_NODE_TYPES.PropertyDefinition) && - !!node.typeAnnotation - ); - } - - function isApplicable( - node: TSESTree.Node, - ): node is KeyTypeNodeWithTypeAnnotation { - return ( - isKeyTypeNode(node) && - node.typeAnnotation.loc.start.line === node.loc.end.line - ); - } - - /** - * To handle index signatures, to get the whole text for the parameters - */ - function getKeyText(node: KeyTypeNodeWithTypeAnnotation): string { - if (node.type !== AST_NODE_TYPES.TSIndexSignature) { - return context.sourceCode.getText(node.key); - } - - const code = context.sourceCode.getText(node); - return code.slice( - 0, - context.sourceCode.getTokenAfter( - node.parameters.at(-1)!, - isClosingBracketToken, - )!.range[1] - node.range[0], - ); - } - - /** - * To handle index signatures, be able to get the end position of the parameters - */ - function getKeyLocEnd( - node: KeyTypeNodeWithTypeAnnotation, - ): TSESTree.Position { - return getLastTokenBeforeColon( - node.type !== AST_NODE_TYPES.TSIndexSignature - ? node.key - : node.parameters.at(-1)!, - ).loc.end; - } - - function checkBeforeColon( - node: KeyTypeNodeWithTypeAnnotation, - expectedWhitespaceBeforeColon: number, - mode: 'minimum' | 'strict', - ): void { - const { typeAnnotation } = node; - const colon = typeAnnotation.loc.start.column; - const keyEnd = getKeyLocEnd(node); - const difference = colon - keyEnd.column - expectedWhitespaceBeforeColon; - if (mode === 'strict' ? difference : difference < 0) { - context.report({ - node, - messageId: difference > 0 ? 'extraKey' : 'missingKey', - fix: fixer => { - if (difference > 0) { - return fixer.removeRange([ - typeAnnotation.range[0] - difference, - typeAnnotation.range[0], - ]); - } - return fixer.insertTextBefore( - typeAnnotation, - ' '.repeat(-difference), - ); - }, - data: { - computed: '', - key: getKeyText(node), - }, - }); - } - } - - function checkAfterColon( - node: KeyTypeNodeWithTypeAnnotation, - expectedWhitespaceAfterColon: number, - mode: 'minimum' | 'strict', - ): void { - const { typeAnnotation } = node; - const colonToken = context.sourceCode.getFirstToken(typeAnnotation)!; - const typeStart = context.sourceCode.getTokenAfter(colonToken, { - includeComments: true, - })!.loc.start.column; - const difference = - typeStart - - colonToken.loc.start.column - - 1 - - expectedWhitespaceAfterColon; - if (mode === 'strict' ? difference : difference < 0) { - context.report({ - node, - messageId: difference > 0 ? 'extraValue' : 'missingValue', - fix: fixer => { - if (difference > 0) { - return fixer.removeRange([ - colonToken.range[1], - colonToken.range[1] + difference, - ]); - } - return fixer.insertTextAfter(colonToken, ' '.repeat(-difference)); - }, - data: { - computed: '', - key: getKeyText(node), - }, - }); - } - } - - // adapted from https://github.com/eslint/eslint/blob/ba74253e8bd63e9e163bbee0540031be77e39253/lib/rules/key-spacing.js#L356 - function continuesAlignGroup( - lastMember: TSESTree.Node, - candidate: TSESTree.Node, - ): boolean { - const groupEndLine = lastMember.loc.start.line; - const candidateValueStartLine = ( - isKeyTypeNode(candidate) ? candidate.typeAnnotation : candidate - ).loc.start.line; - - if (candidateValueStartLine === groupEndLine) { - return false; - } - - if (candidateValueStartLine - groupEndLine === 1) { - return true; - } - - /* - * Check that the first comment is adjacent to the end of the group, the - * last comment is adjacent to the candidate property, and that successive - * comments are adjacent to each other. - */ - const leadingComments = context.sourceCode.getCommentsBefore(candidate); - - if ( - leadingComments.length && - leadingComments[0].loc.start.line - groupEndLine <= 1 && - candidateValueStartLine - leadingComments.at(-1)!.loc.end.line <= 1 - ) { - for (let i = 1; i < leadingComments.length; i++) { - if ( - leadingComments[i].loc.start.line - - leadingComments[i - 1].loc.end.line > - 1 - ) { - return false; - } - } - return true; - } - - return false; - } - - function checkAlignGroup(group: TSESTree.Node[]): void { - let alignColumn = 0; - const align: 'colon' | 'value' = - (typeof options.align === 'object' - ? options.align.on - : typeof options.multiLine?.align === 'object' - ? options.multiLine.align.on - : options.multiLine?.align ?? options.align) ?? 'colon'; - const beforeColon = - (typeof options.align === 'object' - ? options.align.beforeColon - : options.multiLine - ? typeof options.multiLine.align === 'object' - ? options.multiLine.align.beforeColon - : options.multiLine.beforeColon - : options.beforeColon) ?? false; - const expectedWhitespaceBeforeColon = beforeColon ? 1 : 0; - const afterColon = - (typeof options.align === 'object' - ? options.align.afterColon - : options.multiLine - ? typeof options.multiLine.align === 'object' - ? options.multiLine.align.afterColon - : options.multiLine.afterColon - : options.afterColon) ?? true; - const expectedWhitespaceAfterColon = afterColon ? 1 : 0; - const mode = - (typeof options.align === 'object' - ? options.align.mode - : options.multiLine - ? typeof options.multiLine.align === 'object' - ? // same behavior as in original rule - options.multiLine.align.mode ?? options.multiLine.mode - : options.multiLine.mode - : options.mode) ?? 'strict'; - - for (const node of group) { - if (isKeyTypeNode(node)) { - const keyEnd = adjustedColumn(getKeyLocEnd(node)); - alignColumn = Math.max( - alignColumn, - align === 'colon' - ? keyEnd + expectedWhitespaceBeforeColon - : keyEnd + - ':'.length + - expectedWhitespaceAfterColon + - expectedWhitespaceBeforeColon, - ); - } - } - - for (const node of group) { - if (!isApplicable(node)) { - continue; - } - const { typeAnnotation } = node; - const toCheck = - align === 'colon' ? typeAnnotation : typeAnnotation.typeAnnotation; - const difference = adjustedColumn(toCheck.loc.start) - alignColumn; - - if (difference) { - context.report({ - node, - messageId: - difference > 0 - ? align === 'colon' - ? 'extraKey' - : 'extraValue' - : align === 'colon' - ? 'missingKey' - : 'missingValue', - fix: fixer => { - if (difference > 0) { - return fixer.removeRange([ - toCheck.range[0] - difference, - toCheck.range[0], - ]); - } - return fixer.insertTextBefore(toCheck, ' '.repeat(-difference)); - }, - data: { - computed: '', - key: getKeyText(node), - }, - }); - } - - if (align === 'colon') { - checkAfterColon(node, expectedWhitespaceAfterColon, mode); - } else { - checkBeforeColon(node, expectedWhitespaceBeforeColon, mode); - } - } - } - - function checkIndividualNode( - node: TSESTree.Node, - { singleLine }: { singleLine: boolean }, - ): void { - const beforeColon = - (singleLine - ? options.singleLine - ? options.singleLine.beforeColon - : options.beforeColon - : options.multiLine - ? options.multiLine.beforeColon - : options.beforeColon) ?? false; - const expectedWhitespaceBeforeColon = beforeColon ? 1 : 0; - const afterColon = - (singleLine - ? options.singleLine - ? options.singleLine.afterColon - : options.afterColon - : options.multiLine - ? options.multiLine.afterColon - : options.afterColon) ?? true; - const expectedWhitespaceAfterColon = afterColon ? 1 : 0; - const mode = - (singleLine - ? options.singleLine - ? options.singleLine.mode - : options.mode - : options.multiLine - ? options.multiLine.mode - : options.mode) ?? 'strict'; - - if (isApplicable(node)) { - checkBeforeColon(node, expectedWhitespaceBeforeColon, mode); - checkAfterColon(node, expectedWhitespaceAfterColon, mode); - } - } - - function validateBody( - body: - | TSESTree.ClassBody - | TSESTree.TSInterfaceBody - | TSESTree.TSTypeLiteral, - ): void { - const isSingleLine = body.loc.start.line === body.loc.end.line; - - const members = - body.type === AST_NODE_TYPES.TSTypeLiteral ? body.members : body.body; - - let alignGroups: TSESTree.Node[][] = []; - let unalignedElements: TSESTree.Node[] = []; - - if (options.align || options.multiLine?.align) { - let currentAlignGroup: TSESTree.Node[] = []; - alignGroups.push(currentAlignGroup); - - let prevNode: TSESTree.Node | undefined = undefined; - - for (const node of members) { - let prevAlignedNode = currentAlignGroup.at(-1); - if (prevAlignedNode !== prevNode) { - prevAlignedNode = undefined; - } - - if (prevAlignedNode && continuesAlignGroup(prevAlignedNode, node)) { - currentAlignGroup.push(node); - } else if (prevNode?.loc.start.line === node.loc.start.line) { - if (prevAlignedNode) { - // Here, prevNode === prevAlignedNode === currentAlignGroup.at(-1) - unalignedElements.push(prevAlignedNode); - currentAlignGroup.pop(); - } - unalignedElements.push(node); - } else { - currentAlignGroup = [node]; - alignGroups.push(currentAlignGroup); - } - - prevNode = node; - } - - unalignedElements = unalignedElements.concat( - ...alignGroups.filter(group => group.length === 1), - ); - alignGroups = alignGroups.filter(group => group.length >= 2); - } else { - unalignedElements = members; - } - - for (const group of alignGroups) { - checkAlignGroup(group); - } - - for (const node of unalignedElements) { - checkIndividualNode(node, { singleLine: isSingleLine }); - } - } - return { - ...baseRules, - TSTypeLiteral: validateBody, - TSInterfaceBody: validateBody, - ClassBody: validateBody, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/keyword-spacing.ts b/packages/eslint-plugin/src/rules/keyword-spacing.ts deleted file mode 100644 index 7b47c8294260..000000000000 --- a/packages/eslint-plugin/src/rules/keyword-spacing.ts +++ /dev/null @@ -1,124 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; -import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule, deepMerge, nullThrows, NullThrowsReasons } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('keyword-spacing'); - -export type Options = InferOptionsTypeFromRule; -export type MessageIds = InferMessageIdsTypeFromRule; - -// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -const baseSchema = Array.isArray(baseRule.meta.schema) - ? baseRule.meta.schema[0] - : baseRule.meta.schema; -const schema = deepMerge( - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- https://github.com/microsoft/TypeScript/issues/17002 - baseSchema, - { - properties: { - overrides: { - properties: { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access - type: baseSchema.properties.overrides.properties.import, - }, - }, - }, - }, -) as unknown as JSONSchema4; - -export default createRule({ - name: 'keyword-spacing', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/keyword-spacing'], - type: 'layout', - docs: { - description: 'Enforce consistent spacing before and after keywords', - extendsBaseRule: true, - }, - fixable: 'whitespace', - hasSuggestions: baseRule.meta.hasSuggestions, - schema: [schema], - messages: baseRule.meta.messages, - }, - defaultOptions: [{}], - - create(context, [{ after, overrides }]) { - const baseRules = baseRule.create(context); - return { - ...baseRules, - TSAsExpression(node): void { - const asToken = nullThrows( - context.sourceCode.getTokenAfter( - node.expression, - token => token.value === 'as', - ), - NullThrowsReasons.MissingToken('as', node.type), - ); - const oldTokenType = asToken.type; - // as is a contextual keyword, so it's always reported as an Identifier - // the rule looks for keyword tokens, so we temporarily override it - // we mutate it at the token level because the rule calls sourceCode.getFirstToken, - // so mutating a copy would not change the underlying copy returned by that method - asToken.type = AST_TOKEN_TYPES.Keyword; - - // use this selector just because it is just a call to `checkSpacingAroundFirstToken` - baseRules.DebuggerStatement(asToken as never); - - // make sure to reset the type afterward so we don't permanently mutate the AST - asToken.type = oldTokenType; - }, - 'ImportDeclaration[importKind=type]'( - node: TSESTree.ImportDeclaration, - ): void { - const { type: typeOptionOverride = {} } = overrides ?? {}; - const typeToken = context.sourceCode.getFirstToken(node, { skip: 1 })!; - const punctuatorToken = context.sourceCode.getTokenAfter(typeToken)!; - if ( - node.specifiers[0]?.type === AST_NODE_TYPES.ImportDefaultSpecifier - ) { - return; - } - const spacesBetweenTypeAndPunctuator = - punctuatorToken.range[0] - typeToken.range[1]; - if ( - (typeOptionOverride.after ?? after) === true && - spacesBetweenTypeAndPunctuator === 0 - ) { - context.report({ - loc: typeToken.loc, - messageId: 'expectedAfter', - data: { value: 'type' }, - fix(fixer) { - return fixer.insertTextAfter(typeToken, ' '); - }, - }); - } - if ( - (typeOptionOverride.after ?? after) === false && - spacesBetweenTypeAndPunctuator > 0 - ) { - context.report({ - loc: typeToken.loc, - messageId: 'unexpectedAfter', - data: { value: 'type' }, - fix(fixer) { - return fixer.removeRange([ - typeToken.range[1], - typeToken.range[1] + spacesBetweenTypeAndPunctuator, - ]); - }, - }); - } - }, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/lines-around-comment.ts b/packages/eslint-plugin/src/rules/lines-around-comment.ts deleted file mode 100644 index 0c3fe87c6f17..000000000000 --- a/packages/eslint-plugin/src/rules/lines-around-comment.ts +++ /dev/null @@ -1,456 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule, isCommentToken, isTokenOnSameLine } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('lines-around-comment'); - -export type Options = InferOptionsTypeFromRule; -export type MessageIds = InferMessageIdsTypeFromRule; - -const COMMENTS_IGNORE_PATTERN = - /^\s*(?:eslint|jshint\s+|jslint\s+|istanbul\s+|globals?\s+|exported\s+|jscs)/u; - -/** - * @returns an array with with any line numbers that are empty. - */ -function getEmptyLineNums(lines: string[]): number[] { - const emptyLines = lines - .map((line, i) => ({ - code: line.trim(), - num: i + 1, - })) - .filter(line => !line.code) - .map(line => line.num); - - return emptyLines; -} - -/** - * @returns an array with with any line numbers that contain comments. - */ -function getCommentLineNums(comments: TSESTree.Comment[]): number[] { - const lines: number[] = []; - - comments.forEach(token => { - const start = token.loc.start.line; - const end = token.loc.end.line; - - lines.push(start, end); - }); - return lines; -} - -export default createRule({ - name: 'lines-around-comment', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/lines-around-comment'], - type: 'layout', - docs: { - description: 'Require empty lines around comments', - extendsBaseRule: true, - }, - schema: [ - { - type: 'object', - properties: { - beforeBlockComment: { - type: 'boolean', - default: true, - }, - afterBlockComment: { - type: 'boolean', - default: false, - }, - beforeLineComment: { - type: 'boolean', - default: false, - }, - afterLineComment: { - type: 'boolean', - default: false, - }, - allowBlockStart: { - type: 'boolean', - default: false, - }, - allowBlockEnd: { - type: 'boolean', - default: false, - }, - allowClassStart: { - type: 'boolean', - }, - allowClassEnd: { - type: 'boolean', - }, - allowObjectStart: { - type: 'boolean', - }, - allowObjectEnd: { - type: 'boolean', - }, - allowArrayStart: { - type: 'boolean', - }, - allowArrayEnd: { - type: 'boolean', - }, - allowInterfaceStart: { - type: 'boolean', - }, - allowInterfaceEnd: { - type: 'boolean', - }, - allowTypeStart: { - type: 'boolean', - }, - allowTypeEnd: { - type: 'boolean', - }, - allowEnumStart: { - type: 'boolean', - }, - allowEnumEnd: { - type: 'boolean', - }, - allowModuleStart: { - type: 'boolean', - }, - allowModuleEnd: { - type: 'boolean', - }, - ignorePattern: { - type: 'string', - }, - applyDefaultIgnorePatterns: { - type: 'boolean', - }, - }, - additionalProperties: false, - }, - ], - fixable: baseRule.meta.fixable, - hasSuggestions: baseRule.meta.hasSuggestions, - messages: baseRule.meta.messages, - }, - defaultOptions: [ - { - beforeBlockComment: true, - }, - ], - create(context, [_options]) { - const options = _options!; - const defaultIgnoreRegExp = COMMENTS_IGNORE_PATTERN; - const customIgnoreRegExp = new RegExp(options.ignorePattern ?? '', 'u'); - - const comments = context.sourceCode.getAllComments(); - - const lines = context.sourceCode.lines; - const commentLines = getCommentLineNums(comments); - const emptyLines = getEmptyLineNums(lines); - const commentAndEmptyLines = new Set(commentLines.concat(emptyLines)); - - /** - * @returns whether comments are on lines starting with or ending with code. - */ - function codeAroundComment(token: TSESTree.Token): boolean { - let currentToken: TSESTree.Token | null = token; - - do { - currentToken = context.sourceCode.getTokenBefore(currentToken, { - includeComments: true, - }); - } while (currentToken && isCommentToken(currentToken)); - - if (currentToken && isTokenOnSameLine(currentToken, token)) { - return true; - } - - currentToken = token; - do { - currentToken = context.sourceCode.getTokenAfter(currentToken, { - includeComments: true, - }); - } while (currentToken && isCommentToken(currentToken)); - - if (currentToken && isTokenOnSameLine(token, currentToken)) { - return true; - } - - return false; - } - - /** - * @returns whether comments are inside a node type. - */ - function isParentNodeType( - parent: TSESTree.Node, - nodeType: T, - ): parent is Extract { - return parent.type === nodeType; - } - - /** - * @returns the parent node that contains the given token. - */ - function getParentNodeOfToken(token: TSESTree.Token): TSESTree.Node | null { - const node = context.sourceCode.getNodeByRangeIndex(token.range[0]); - - return node; - } - - /** - * @returns whether comments are at the parent start. - */ - function isCommentAtParentStart( - token: TSESTree.Token, - nodeType: TSESTree.AST_NODE_TYPES, - ): boolean { - const parent = getParentNodeOfToken(token); - - if (parent && isParentNodeType(parent, nodeType)) { - const parentStartNodeOrToken = parent; - - return ( - token.loc.start.line - parentStartNodeOrToken.loc.start.line === 1 - ); - } - - return false; - } - - /** - * @returns whether comments are at the parent end. - */ - function isCommentAtParentEnd( - token: TSESTree.Token, - nodeType: TSESTree.AST_NODE_TYPES, - ): boolean { - const parent = getParentNodeOfToken(token); - - return ( - !!parent && - isParentNodeType(parent, nodeType) && - parent.loc.end.line - token.loc.end.line === 1 - ); - } - - function isCommentAtInterfaceStart(token: TSESTree.Comment): boolean { - return isCommentAtParentStart(token, AST_NODE_TYPES.TSInterfaceBody); - } - - function isCommentAtInterfaceEnd(token: TSESTree.Comment): boolean { - return isCommentAtParentEnd(token, AST_NODE_TYPES.TSInterfaceBody); - } - - function isCommentAtTypeStart(token: TSESTree.Comment): boolean { - return isCommentAtParentStart(token, AST_NODE_TYPES.TSTypeLiteral); - } - - function isCommentAtTypeEnd(token: TSESTree.Comment): boolean { - return isCommentAtParentEnd(token, AST_NODE_TYPES.TSTypeLiteral); - } - - function isCommentAtEnumStart(token: TSESTree.Comment): boolean { - return isCommentAtParentStart(token, AST_NODE_TYPES.TSEnumDeclaration); - } - - function isCommentAtEnumEnd(token: TSESTree.Comment): boolean { - return isCommentAtParentEnd(token, AST_NODE_TYPES.TSEnumDeclaration); - } - - function isCommentAtModuleStart(token: TSESTree.Comment): boolean { - return isCommentAtParentStart(token, AST_NODE_TYPES.TSModuleBlock); - } - - function isCommentAtModuleEnd(token: TSESTree.Comment): boolean { - return isCommentAtParentEnd(token, AST_NODE_TYPES.TSModuleBlock); - } - - function isCommentNearTSConstruct(token: TSESTree.Comment): boolean { - return ( - isCommentAtInterfaceStart(token) || - isCommentAtInterfaceEnd(token) || - isCommentAtTypeStart(token) || - isCommentAtTypeEnd(token) || - isCommentAtEnumStart(token) || - isCommentAtEnumEnd(token) || - isCommentAtModuleStart(token) || - isCommentAtModuleEnd(token) - ); - } - - function checkForEmptyLine( - token: TSESTree.Comment, - { before, after }: { before?: boolean; after?: boolean }, - ): void { - // the base rule handles comments away from TS constructs blocks correctly, we skip those - if (!isCommentNearTSConstruct(token)) { - return; - } - - if ( - options.applyDefaultIgnorePatterns !== false && - defaultIgnoreRegExp.test(token.value) - ) { - return; - } - - if (options.ignorePattern && customIgnoreRegExp.test(token.value)) { - return; - } - - const prevLineNum = token.loc.start.line - 1; - const nextLineNum = token.loc.end.line + 1; - - // we ignore all inline comments - if (codeAroundComment(token)) { - return; - } - - const interfaceStartAllowed = - Boolean(options.allowInterfaceStart) && - isCommentAtInterfaceStart(token); - const interfaceEndAllowed = - Boolean(options.allowInterfaceEnd) && isCommentAtInterfaceEnd(token); - const typeStartAllowed = - Boolean(options.allowTypeStart) && isCommentAtTypeStart(token); - const typeEndAllowed = - Boolean(options.allowTypeEnd) && isCommentAtTypeEnd(token); - const enumStartAllowed = - Boolean(options.allowEnumStart) && isCommentAtEnumStart(token); - const enumEndAllowed = - Boolean(options.allowEnumEnd) && isCommentAtEnumEnd(token); - const moduleStartAllowed = - Boolean(options.allowModuleStart) && isCommentAtModuleStart(token); - const moduleEndAllowed = - Boolean(options.allowModuleEnd) && isCommentAtModuleEnd(token); - - const exceptionStartAllowed = - interfaceStartAllowed || - typeStartAllowed || - enumStartAllowed || - moduleStartAllowed; - const exceptionEndAllowed = - interfaceEndAllowed || - typeEndAllowed || - enumEndAllowed || - moduleEndAllowed; - - const previousTokenOrComment = context.sourceCode.getTokenBefore(token, { - includeComments: true, - }); - const nextTokenOrComment = context.sourceCode.getTokenAfter(token, { - includeComments: true, - }); - - // check for newline before - if ( - !exceptionStartAllowed && - before && - !commentAndEmptyLines.has(prevLineNum) && - !( - isCommentToken(previousTokenOrComment!) && - isTokenOnSameLine(previousTokenOrComment, token) - ) - ) { - const lineStart = token.range[0] - token.loc.start.column; - const range = [lineStart, lineStart] as const; - - context.report({ - node: token, - messageId: 'before', - fix(fixer) { - return fixer.insertTextBeforeRange(range, '\n'); - }, - }); - } - - // check for newline after - if ( - !exceptionEndAllowed && - after && - !commentAndEmptyLines.has(nextLineNum) && - !( - isCommentToken(nextTokenOrComment!) && - isTokenOnSameLine(token, nextTokenOrComment) - ) - ) { - context.report({ - node: token, - messageId: 'after', - fix(fixer) { - return fixer.insertTextAfter(token, '\n'); - }, - }); - } - } - - /** - * A custom report function for the baseRule to ignore false positive errors - * caused by TS-specific codes - */ - const customReport: typeof context.report = descriptor => { - if ('node' in descriptor) { - if ( - descriptor.node.type === AST_TOKEN_TYPES.Line || - descriptor.node.type === AST_TOKEN_TYPES.Block - ) { - if (isCommentNearTSConstruct(descriptor.node)) { - return; - } - } - } - return context.report(descriptor); - }; - - const customContext = { report: customReport }; - - // we can't directly proxy `context` because its `report` property is non-configurable - // and non-writable. So we proxy `customContext` and redirect all - // property access to the original context except for `report` - const proxiedContext = new Proxy( - customContext as typeof context, - { - get(target, path, receiver): unknown { - if (path !== 'report') { - return Reflect.get(context, path, receiver); - } - return Reflect.get(target, path, receiver); - }, - }, - ); - - const rules = baseRule.create(proxiedContext); - - return { - Program(): void { - rules.Program(); - - comments.forEach(token => { - if (token.type === AST_TOKEN_TYPES.Line) { - if (options.beforeLineComment || options.afterLineComment) { - checkForEmptyLine(token, { - after: options.afterLineComment, - before: options.beforeLineComment, - }); - } - } else if (options.beforeBlockComment || options.afterBlockComment) { - checkForEmptyLine(token, { - after: options.afterBlockComment, - before: options.beforeBlockComment, - }); - } - }); - }, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/lines-between-class-members.ts b/packages/eslint-plugin/src/rules/lines-between-class-members.ts deleted file mode 100644 index 60da9308757a..000000000000 --- a/packages/eslint-plugin/src/rules/lines-between-class-members.ts +++ /dev/null @@ -1,78 +0,0 @@ -import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; -import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule, deepMerge } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('lines-between-class-members'); - -type Options = InferOptionsTypeFromRule; -type MessageIds = InferMessageIdsTypeFromRule; - -const schema = Object.values( - deepMerge( - { ...baseRule.meta.schema }, - { - 1: { - properties: { - exceptAfterOverload: { - type: 'boolean', - default: true, - }, - }, - }, - }, - ), -) as JSONSchema4[]; - -export default createRule({ - name: 'lines-between-class-members', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/lines-between-class-members'], - type: 'layout', - docs: { - description: 'Require or disallow an empty line between class members', - extendsBaseRule: true, - }, - fixable: 'whitespace', - hasSuggestions: baseRule.meta.hasSuggestions, - schema, - messages: baseRule.meta.messages, - }, - defaultOptions: [ - 'always', - { - exceptAfterOverload: true, - exceptAfterSingleLine: false, - }, - ], - create(context, [firstOption, secondOption]) { - const rules = baseRule.create(context); - const exceptAfterOverload = - secondOption?.exceptAfterOverload && firstOption === 'always'; - - function isOverload(node: TSESTree.Node): boolean { - return ( - (node.type === AST_NODE_TYPES.TSAbstractMethodDefinition || - node.type === AST_NODE_TYPES.MethodDefinition) && - node.value.type === AST_NODE_TYPES.TSEmptyBodyFunctionExpression - ); - } - - return { - ClassBody(node): void { - const body = exceptAfterOverload - ? node.body.filter(node => !isOverload(node)) - : node.body; - - rules.ClassBody({ ...node, body }); - }, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/member-delimiter-style.ts b/packages/eslint-plugin/src/rules/member-delimiter-style.ts deleted file mode 100644 index 428f31f3667e..000000000000 --- a/packages/eslint-plugin/src/rules/member-delimiter-style.ts +++ /dev/null @@ -1,352 +0,0 @@ -import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; -import type { JSONSchema4 } from '@typescript-eslint/utils/json-schema'; - -import { createRule, deepMerge } from '../util'; - -type Delimiter = 'comma' | 'none' | 'semi'; -// need type's implicit index sig for deepMerge -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -type TypeOptions = { - delimiter?: Delimiter; - requireLast?: boolean; -}; -type TypeOptionsWithType = TypeOptions & { - type: string; -}; -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -type BaseOptions = { - multiline?: TypeOptions; - singleline?: TypeOptions; -}; -type Config = BaseOptions & { - overrides?: { - typeLiteral?: BaseOptions; - interface?: BaseOptions; - }; - multilineDetection?: 'brackets' | 'last-member'; -}; -type Options = [Config]; -type MessageIds = - | 'expectedComma' - | 'expectedSemi' - | 'unexpectedComma' - | 'unexpectedSemi'; -type LastTokenType = TSESTree.Token; - -interface MakeFixFunctionParams { - optsNone: boolean; - optsSemi: boolean; - lastToken: LastTokenType; - commentsAfterLastToken: LastTokenType | undefined; - missingDelimiter: boolean; - lastTokenLine: string; - isSingleLine: boolean; -} - -type MakeFixFunctionReturnType = - | ((fixer: TSESLint.RuleFixer) => TSESLint.RuleFix) - | null; - -const isLastTokenEndOfLine = (token: LastTokenType, line: string): boolean => { - const positionInLine = token.loc.start.column; - - return positionInLine === line.length - 1; -}; - -const isCommentsEndOfLine = ( - token: LastTokenType, - comments: LastTokenType | undefined, - line: string, -): boolean => { - if (!comments) { - return false; - } - - if (comments.loc.end.line > token.loc.end.line) { - return true; - } - - const positionInLine = comments.loc.end.column; - - return positionInLine === line.length; -}; - -const makeFixFunction = ({ - optsNone, - optsSemi, - lastToken, - commentsAfterLastToken, - missingDelimiter, - lastTokenLine, - isSingleLine, -}: MakeFixFunctionParams): MakeFixFunctionReturnType => { - // if removing is the action but last token is not the end of the line - if ( - optsNone && - !isLastTokenEndOfLine(lastToken, lastTokenLine) && - !isCommentsEndOfLine(lastToken, commentsAfterLastToken, lastTokenLine) && - !isSingleLine - ) { - return null; - } - - return (fixer: TSESLint.RuleFixer): TSESLint.RuleFix => { - if (optsNone) { - // remove the unneeded token - return fixer.remove(lastToken); - } - - const token = optsSemi ? ';' : ','; - - if (missingDelimiter) { - // add the missing delimiter - return fixer.insertTextAfter(lastToken, token); - } - - // correct the current delimiter - return fixer.replaceText(lastToken, token); - }; -}; - -const BASE_SCHEMA: JSONSchema4 = { - type: 'object', - properties: { - multiline: { - type: 'object', - properties: { - delimiter: { $ref: '#/items/0/$defs/multiLineOption' }, - requireLast: { type: 'boolean' }, - }, - additionalProperties: false, - }, - singleline: { - type: 'object', - properties: { - delimiter: { $ref: '#/items/0/$defs/singleLineOption' }, - requireLast: { type: 'boolean' }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, -}; - -export default createRule({ - name: 'member-delimiter-style', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/member-delimiter-style'], - type: 'layout', - docs: { - description: - 'Require a specific member delimiter style for interfaces and type literals', - }, - fixable: 'whitespace', - messages: { - unexpectedComma: 'Unexpected separator (,).', - unexpectedSemi: 'Unexpected separator (;).', - expectedComma: 'Expected a comma.', - expectedSemi: 'Expected a semicolon.', - }, - schema: [ - { - $defs: { - multiLineOption: { - type: 'string', - enum: ['none', 'semi', 'comma'], - }, - // note can't have "none" for single line delimiter as it's invalid syntax - singleLineOption: { - type: 'string', - enum: ['semi', 'comma'], - }, - // note - need to define this last as it references the enums - delimiterConfig: BASE_SCHEMA, - }, - type: 'object', - properties: { - ...BASE_SCHEMA.properties, - overrides: { - type: 'object', - properties: { - interface: { - $ref: '#/items/0/$defs/delimiterConfig', - }, - typeLiteral: { - $ref: '#/items/0/$defs/delimiterConfig', - }, - }, - additionalProperties: false, - }, - multilineDetection: { - type: 'string', - enum: ['brackets', 'last-member'], - }, - }, - additionalProperties: false, - }, - ], - }, - defaultOptions: [ - { - multiline: { - delimiter: 'semi', - requireLast: true, - }, - singleline: { - delimiter: 'semi', - requireLast: false, - }, - multilineDetection: 'brackets', - }, - ], - create(context, [options]) { - // use the base options as the defaults for the cases - const baseOptions = options; - const overrides = baseOptions.overrides ?? {}; - const interfaceOptions: BaseOptions = deepMerge( - baseOptions, - overrides.interface, - ); - const typeLiteralOptions: BaseOptions = deepMerge( - baseOptions, - overrides.typeLiteral, - ); - - /** - * Check the last token in the given member. - * @param member the member to be evaluated. - * @param opts the options to be validated. - * @param isLast a flag indicating `member` is the last in the interface or type literal. - */ - function checkLastToken( - member: TSESTree.TypeElement, - opts: TypeOptionsWithType, - isLast: boolean, - ): void { - /** - * Resolves the boolean value for the given setting enum value - * @param type the option name - */ - function getOption(type: Delimiter): boolean { - if (isLast && !opts.requireLast) { - // only turn the option on if its expecting no delimiter for the last member - return type === 'none'; - } - return opts.delimiter === type; - } - - let messageId: MessageIds | null = null; - let missingDelimiter = false; - const lastToken = context.sourceCode.getLastToken(member, { - includeComments: false, - }); - - if (!lastToken) { - return; - } - - const commentsAfterLastToken = context.sourceCode - .getCommentsAfter(lastToken) - .pop(); - - const sourceCodeLines = context.sourceCode.getLines(); - const lastTokenLine = sourceCodeLines[lastToken.loc.start.line - 1]; - - const optsSemi = getOption('semi'); - const optsComma = getOption('comma'); - const optsNone = getOption('none'); - - if (lastToken.value === ';') { - if (optsComma) { - messageId = 'expectedComma'; - } else if (optsNone) { - missingDelimiter = true; - messageId = 'unexpectedSemi'; - } - } else if (lastToken.value === ',') { - if (optsSemi) { - messageId = 'expectedSemi'; - } else if (optsNone) { - missingDelimiter = true; - messageId = 'unexpectedComma'; - } - } else { - if (optsSemi) { - missingDelimiter = true; - messageId = 'expectedSemi'; - } else if (optsComma) { - missingDelimiter = true; - messageId = 'expectedComma'; - } - } - - if (messageId) { - context.report({ - node: lastToken, - loc: { - start: { - line: lastToken.loc.end.line, - column: lastToken.loc.end.column, - }, - end: { - line: lastToken.loc.end.line, - column: lastToken.loc.end.column, - }, - }, - messageId, - fix: makeFixFunction({ - optsNone, - optsSemi, - lastToken, - commentsAfterLastToken, - missingDelimiter, - lastTokenLine, - isSingleLine: opts.type === 'single-line', - }), - }); - } - } - - /** - * Check the member separator being used matches the delimiter. - * @param node the node to be evaluated. - */ - function checkMemberSeparatorStyle( - node: TSESTree.TSInterfaceBody | TSESTree.TSTypeLiteral, - ): void { - const members = - node.type === AST_NODE_TYPES.TSInterfaceBody ? node.body : node.members; - - let isSingleLine = node.loc.start.line === node.loc.end.line; - if ( - options.multilineDetection === 'last-member' && - !isSingleLine && - members.length > 0 - ) { - const lastMember = members[members.length - 1]; - if (lastMember.loc.end.line === node.loc.end.line) { - isSingleLine = true; - } - } - - const typeOpts = - node.type === AST_NODE_TYPES.TSInterfaceBody - ? interfaceOptions - : typeLiteralOptions; - const opts = isSingleLine - ? { ...typeOpts.singleline, type: 'single-line' } - : { ...typeOpts.multiline, type: 'multi-line' }; - - members.forEach((member, index) => { - checkLastToken(member, opts, index === members.length - 1); - }); - } - - return { - TSInterfaceBody: checkMemberSeparatorStyle, - TSTypeLiteral: checkMemberSeparatorStyle, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/no-extra-parens.ts b/packages/eslint-plugin/src/rules/no-extra-parens.ts deleted file mode 100644 index 0ed0f4c6b4de..000000000000 --- a/packages/eslint-plugin/src/rules/no-extra-parens.ts +++ /dev/null @@ -1,308 +0,0 @@ -// any is required to work around manipulating the AST in weird ways -/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment */ - -import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule, isOpeningParenToken, isTypeAssertion } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('no-extra-parens'); - -type Options = InferOptionsTypeFromRule; -type MessageIds = InferMessageIdsTypeFromRule; - -export default createRule({ - name: 'no-extra-parens', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/no-extra-parens'], - type: 'layout', - docs: { - description: 'Disallow unnecessary parentheses', - extendsBaseRule: true, - }, - fixable: 'code', - hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, - messages: baseRule.meta.messages, - }, - defaultOptions: ['all'], - create(context) { - const rules = baseRule.create(context); - - function binaryExp( - node: TSESTree.BinaryExpression | TSESTree.LogicalExpression, - ): void { - const rule = rules.BinaryExpression as (n: typeof node) => void; - - // makes the rule think it should skip the left or right - const isLeftTypeAssertion = isTypeAssertion(node.left); - const isRightTypeAssertion = isTypeAssertion(node.right); - if (isLeftTypeAssertion && isRightTypeAssertion) { - return; // ignore - } - if (isLeftTypeAssertion) { - return rule({ - ...node, - left: { - ...node.left, - type: AST_NODE_TYPES.SequenceExpression as any, - }, - }); - } - if (isRightTypeAssertion) { - return rule({ - ...node, - right: { - ...node.right, - type: AST_NODE_TYPES.SequenceExpression as any, - }, - }); - } - - return rule(node); - } - function callExp( - node: TSESTree.CallExpression | TSESTree.NewExpression, - ): void { - const rule = rules.CallExpression as (n: typeof node) => void; - - if (isTypeAssertion(node.callee)) { - // reduces the precedence of the node so the rule thinks it needs to be wrapped - return rule({ - ...node, - callee: { - ...node.callee, - type: AST_NODE_TYPES.SequenceExpression as any, - }, - }); - } - - if ( - node.arguments.length === 1 && - // is there any opening parenthesis in type arguments - context.sourceCode.getTokenAfter(node.callee, isOpeningParenToken) !== - context.sourceCode.getTokenBefore( - node.arguments[0], - isOpeningParenToken, - ) - ) { - return rule({ - ...node, - arguments: [ - { - ...node.arguments[0], - type: AST_NODE_TYPES.SequenceExpression as any, - }, - ], - }); - } - - return rule(node); - } - function unaryUpdateExpression( - node: TSESTree.UnaryExpression | TSESTree.UpdateExpression, - ): void { - const rule = rules.UnaryExpression as (n: typeof node) => void; - - if (isTypeAssertion(node.argument)) { - // reduces the precedence of the node so the rule thinks it needs to be wrapped - return rule({ - ...node, - argument: { - ...node.argument, - type: AST_NODE_TYPES.SequenceExpression as any, - }, - }); - } - - return rule(node); - } - - const overrides: TSESLint.RuleListener = { - // ArrayExpression - ArrowFunctionExpression(node) { - if (!isTypeAssertion(node.body)) { - return rules.ArrowFunctionExpression(node); - } - }, - // AssignmentExpression - AwaitExpression(node) { - if (isTypeAssertion(node.argument)) { - // reduces the precedence of the node so the rule thinks it needs to be wrapped - return rules.AwaitExpression({ - ...node, - argument: { - ...node.argument, - type: AST_NODE_TYPES.SequenceExpression as any, - }, - }); - } - return rules.AwaitExpression(node); - }, - BinaryExpression: binaryExp, - CallExpression: callExp, - ClassDeclaration(node) { - if (node.superClass?.type === AST_NODE_TYPES.TSAsExpression) { - return rules.ClassDeclaration({ - ...node, - superClass: { - ...node.superClass, - type: AST_NODE_TYPES.SequenceExpression as any, - }, - }); - } - return rules.ClassDeclaration(node); - }, - ClassExpression(node) { - if (node.superClass?.type === AST_NODE_TYPES.TSAsExpression) { - return rules.ClassExpression({ - ...node, - superClass: { - ...node.superClass, - type: AST_NODE_TYPES.SequenceExpression as any, - }, - }); - } - return rules.ClassExpression(node); - }, - ConditionalExpression(node) { - // reduces the precedence of the node so the rule thinks it needs to be wrapped - if (isTypeAssertion(node.test)) { - return rules.ConditionalExpression({ - ...node, - test: { - ...node.test, - type: AST_NODE_TYPES.SequenceExpression as any, - }, - }); - } - if (isTypeAssertion(node.consequent)) { - return rules.ConditionalExpression({ - ...node, - consequent: { - ...node.consequent, - type: AST_NODE_TYPES.SequenceExpression as any, - }, - }); - } - if (isTypeAssertion(node.alternate)) { - // reduces the precedence of the node so the rule thinks it needs to be wrapped - return rules.ConditionalExpression({ - ...node, - alternate: { - ...node.alternate, - type: AST_NODE_TYPES.SequenceExpression as any, - }, - }); - } - return rules.ConditionalExpression(node); - }, - ForInStatement(node): void { - if (isTypeAssertion(node.right)) { - // as of 7.20.0 there's no way to skip checking the right of the ForIn - // so just don't validate it at all - return; - } - - return rules.ForInStatement(node); - }, - ForOfStatement(node): void { - if (isTypeAssertion(node.right)) { - // makes the rule skip checking of the right - return rules.ForOfStatement({ - ...node, - type: AST_NODE_TYPES.ForOfStatement, - right: { - ...node.right, - type: AST_NODE_TYPES.SequenceExpression as any, - }, - }); - } - - return rules.ForOfStatement(node); - }, - // DoWhileStatement - ForStatement(node) { - // make the rule skip the piece by removing it entirely - if (node.init && isTypeAssertion(node.init)) { - return rules.ForStatement({ - ...node, - init: null, - }); - } - if (node.test && isTypeAssertion(node.test)) { - return rules.ForStatement({ - ...node, - test: null, - }); - } - if (node.update && isTypeAssertion(node.update)) { - return rules.ForStatement({ - ...node, - update: null, - }); - } - - return rules.ForStatement(node); - }, - 'ForStatement > *.init:exit'(node: TSESTree.Node) { - if (!isTypeAssertion(node)) { - return rules['ForStatement > *.init:exit'](node); - } - }, - // IfStatement - LogicalExpression: binaryExp, - MemberExpression(node) { - if (isTypeAssertion(node.object)) { - // reduces the precedence of the node so the rule thinks it needs to be wrapped - return rules.MemberExpression({ - ...node, - object: { - ...node.object, - type: AST_NODE_TYPES.SequenceExpression as any, - }, - }); - } - - return rules.MemberExpression(node); - }, - NewExpression: callExp, - // ObjectExpression - // ReturnStatement - // SequenceExpression - SpreadElement(node) { - if (!isTypeAssertion(node.argument)) { - return rules.SpreadElement(node); - } - }, - SwitchCase(node) { - if (node.test && !isTypeAssertion(node.test)) { - return rules.SwitchCase(node); - } - }, - // SwitchStatement - ThrowStatement(node) { - if (node.argument && !isTypeAssertion(node.argument)) { - return rules.ThrowStatement(node); - } - }, - UnaryExpression: unaryUpdateExpression, - UpdateExpression: unaryUpdateExpression, - // VariableDeclarator - // WhileStatement - // WithStatement - i'm not going to even bother implementing this terrible and never used feature - YieldExpression(node) { - if (node.argument && !isTypeAssertion(node.argument)) { - return rules.YieldExpression(node); - } - }, - }; - return Object.assign({}, rules, overrides); - }, -}); diff --git a/packages/eslint-plugin/src/rules/no-extra-semi.ts b/packages/eslint-plugin/src/rules/no-extra-semi.ts deleted file mode 100644 index 4d68f2d6db46..000000000000 --- a/packages/eslint-plugin/src/rules/no-extra-semi.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('no-extra-semi'); - -type Options = InferOptionsTypeFromRule; -type MessageIds = InferMessageIdsTypeFromRule; - -export default createRule({ - name: 'no-extra-semi', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/no-extra-semi'], - type: 'suggestion', - docs: { - description: 'Disallow unnecessary semicolons', - extendsBaseRule: true, - }, - fixable: 'code', - hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, - messages: baseRule.meta.messages, - }, - defaultOptions: [], - create(context) { - const rules = baseRule.create(context); - - return { - ...rules, - 'TSAbstractMethodDefinition, TSAbstractPropertyDefinition'( - node: never, - ): void { - rules['MethodDefinition, PropertyDefinition, StaticBlock'](node); - }, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/object-curly-spacing.ts b/packages/eslint-plugin/src/rules/object-curly-spacing.ts deleted file mode 100644 index 4edd5a688f99..000000000000 --- a/packages/eslint-plugin/src/rules/object-curly-spacing.ts +++ /dev/null @@ -1,293 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES, AST_TOKEN_TYPES } from '@typescript-eslint/utils'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { - createRule, - isClosingBraceToken, - isClosingBracketToken, - isTokenOnSameLine, -} from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('object-curly-spacing'); - -export type Options = InferOptionsTypeFromRule; -export type MessageIds = InferMessageIdsTypeFromRule; - -export default createRule({ - name: 'object-curly-spacing', - // eslint-disable-next-line eslint-plugin/prefer-message-ids,eslint-plugin/require-meta-type,eslint-plugin/require-meta-schema,eslint-plugin/require-meta-fixable -- all in base rule - https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/issues/274 - meta: { - ...baseRule.meta, - deprecated: true, - docs: { - description: 'Enforce consistent spacing inside braces', - extendsBaseRule: true, - }, - replacedBy: ['@stylistic/ts/object-curly-spacing'], - }, - defaultOptions: ['never'], - create(context) { - // eslint-disable-next-line no-restricted-syntax -- Use raw options for extended rules. - const [firstOption, secondOption] = context.options; - const spaced = firstOption === 'always'; - - /** - * Determines whether an option is set, relative to the spacing option. - * If spaced is "always", then check whether option is set to false. - * If spaced is "never", then check whether option is set to true. - * @param option The option to exclude. - * @returns Whether or not the property is excluded. - */ - function isOptionSet( - option: 'arraysInObjects' | 'objectsInObjects', - ): boolean { - return secondOption ? secondOption[option] === !spaced : false; - } - - const options = { - spaced, - arraysInObjectsException: isOptionSet('arraysInObjects'), - objectsInObjectsException: isOptionSet('objectsInObjects'), - }; - - //-------------------------------------------------------------------------- - // Helpers - //-------------------------------------------------------------------------- - - /** - * Reports that there shouldn't be a space after the first token - * @param node The node to report in the event of an error. - * @param token The token to use for the report. - */ - function reportNoBeginningSpace( - node: TSESTree.TSMappedType | TSESTree.TSTypeLiteral, - token: TSESTree.Token, - ): void { - const nextToken = context.sourceCode.getTokenAfter(token, { - includeComments: true, - })!; - - context.report({ - node, - loc: { start: token.loc.end, end: nextToken.loc.start }, - messageId: 'unexpectedSpaceAfter', - data: { - token: token.value, - }, - fix(fixer) { - return fixer.removeRange([token.range[1], nextToken.range[0]]); - }, - }); - } - - /** - * Reports that there shouldn't be a space before the last token - * @param node The node to report in the event of an error. - * @param token The token to use for the report. - */ - function reportNoEndingSpace( - node: TSESTree.TSMappedType | TSESTree.TSTypeLiteral, - token: TSESTree.Token, - ): void { - const previousToken = context.sourceCode.getTokenBefore(token, { - includeComments: true, - })!; - - context.report({ - node, - loc: { start: previousToken.loc.end, end: token.loc.start }, - messageId: 'unexpectedSpaceBefore', - data: { - token: token.value, - }, - fix(fixer) { - return fixer.removeRange([previousToken.range[1], token.range[0]]); - }, - }); - } - - /** - * Reports that there should be a space after the first token - * @param node The node to report in the event of an error. - * @param token The token to use for the report. - */ - function reportRequiredBeginningSpace( - node: TSESTree.TSMappedType | TSESTree.TSTypeLiteral, - token: TSESTree.Token, - ): void { - context.report({ - node, - loc: token.loc, - messageId: 'requireSpaceAfter', - data: { - token: token.value, - }, - fix(fixer) { - return fixer.insertTextAfter(token, ' '); - }, - }); - } - - /** - * Reports that there should be a space before the last token - * @param node The node to report in the event of an error. - * @param token The token to use for the report. - */ - function reportRequiredEndingSpace( - node: TSESTree.TSMappedType | TSESTree.TSTypeLiteral, - token: TSESTree.Token, - ): void { - context.report({ - node, - loc: token.loc, - messageId: 'requireSpaceBefore', - data: { - token: token.value, - }, - fix(fixer) { - return fixer.insertTextBefore(token, ' '); - }, - }); - } - - /** - * Determines if spacing in curly braces is valid. - * @param node The AST node to check. - * @param first The first token to check (should be the opening brace) - * @param second The second token to check (should be first after the opening brace) - * @param penultimate The penultimate token to check (should be last before closing brace) - * @param last The last token to check (should be closing brace) - */ - function validateBraceSpacing( - node: TSESTree.TSMappedType | TSESTree.TSTypeLiteral, - first: TSESTree.Token, - second: TSESTree.Token, - penultimate: TSESTree.Token, - last: TSESTree.Token, - ): void { - if (isTokenOnSameLine(first, second)) { - const firstSpaced = context.sourceCode.isSpaceBetween(first, second); - const secondType = context.sourceCode.getNodeByRangeIndex( - second.range[0], - )!.type; - - const openingCurlyBraceMustBeSpaced = - options.arraysInObjectsException && - [ - AST_NODE_TYPES.TSMappedType, - AST_NODE_TYPES.TSIndexSignature, - ].includes(secondType) - ? !options.spaced - : options.spaced; - - if (openingCurlyBraceMustBeSpaced && !firstSpaced) { - reportRequiredBeginningSpace(node, first); - } - if ( - !openingCurlyBraceMustBeSpaced && - firstSpaced && - second.type !== AST_TOKEN_TYPES.Line - ) { - reportNoBeginningSpace(node, first); - } - } - - if (isTokenOnSameLine(penultimate, last)) { - const shouldCheckPenultimate = - (options.arraysInObjectsException && - isClosingBracketToken(penultimate)) || - (options.objectsInObjectsException && - isClosingBraceToken(penultimate)); - const penultimateType = shouldCheckPenultimate - ? context.sourceCode.getNodeByRangeIndex(penultimate.range[0])!.type - : undefined; - - const closingCurlyBraceMustBeSpaced = - (options.arraysInObjectsException && - penultimateType === AST_NODE_TYPES.TSTupleType) || - (options.objectsInObjectsException && - penultimateType !== undefined && - [ - AST_NODE_TYPES.TSMappedType, - AST_NODE_TYPES.TSTypeLiteral, - ].includes(penultimateType)) - ? !options.spaced - : options.spaced; - - const lastSpaced = context.sourceCode.isSpaceBetween(penultimate, last); - - if (closingCurlyBraceMustBeSpaced && !lastSpaced) { - reportRequiredEndingSpace(node, last); - } - if (!closingCurlyBraceMustBeSpaced && lastSpaced) { - reportNoEndingSpace(node, last); - } - } - } - - /** - * Gets '}' token of an object node. - * - * Because the last token of object patterns might be a type annotation, - * this traverses tokens preceded by the last property, then returns the - * first '}' token. - * @param node The node to get. This node is an - * ObjectExpression or an ObjectPattern. And this node has one or - * more properties. - * @returns '}' token. - */ - function getClosingBraceOfObject( - node: TSESTree.TSTypeLiteral, - ): TSESTree.Token | null { - const lastProperty = node.members[node.members.length - 1]; - - return context.sourceCode.getTokenAfter( - lastProperty, - isClosingBraceToken, - ); - } - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - - const rules = baseRule.create(context); - return { - ...rules, - TSMappedType(node: TSESTree.TSMappedType): void { - const first = context.sourceCode.getFirstToken(node)!; - const last = context.sourceCode.getLastToken(node)!; - const second = context.sourceCode.getTokenAfter(first, { - includeComments: true, - })!; - const penultimate = context.sourceCode.getTokenBefore(last, { - includeComments: true, - })!; - - validateBraceSpacing(node, first, second, penultimate, last); - }, - TSTypeLiteral(node: TSESTree.TSTypeLiteral): void { - if (node.members.length === 0) { - return; - } - - const first = context.sourceCode.getFirstToken(node)!; - const last = getClosingBraceOfObject(node)!; - const second = context.sourceCode.getTokenAfter(first, { - includeComments: true, - })!; - const penultimate = context.sourceCode.getTokenBefore(last, { - includeComments: true, - })!; - - validateBraceSpacing(node, first, second, penultimate, last); - }, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/padding-line-between-statements.ts b/packages/eslint-plugin/src/rules/padding-line-between-statements.ts deleted file mode 100644 index 20d6b8e1dfd4..000000000000 --- a/packages/eslint-plugin/src/rules/padding-line-between-statements.ts +++ /dev/null @@ -1,825 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; - -import { - createRule, - isClosingBraceToken, - isFunction, - isNotSemicolonToken, - isParenthesized, - isSemicolonToken, - isTokenOnSameLine, -} from '../util'; - -/** - * This rule is a replica of padding-line-between-statements. - * - * Ideally we would want to extend the rule support typescript specific support. - * But since not all the state is exposed by the eslint and eslint has frozen stylistic rules, - * (see - https://eslint.org/blog/2020/05/changes-to-rules-policies for details.) - * we are forced to re-implement the rule here. - * - * We have tried to keep the implementation as close as possible to the eslint implementation, to make - * patching easier for future contributors. - * - * Reference rule - https://github.com/eslint/eslint/blob/main/lib/rules/padding-line-between-statements.js - */ - -type NodeTest = ( - node: TSESTree.Node, - sourceCode: TSESLint.SourceCode, -) => boolean; - -interface NodeTestObject { - test: NodeTest; -} - -interface PaddingOption { - blankLine: keyof typeof PaddingTypes; - prev: string[] | string; - next: string[] | string; -} - -type MessageIds = 'expectedBlankLine' | 'unexpectedBlankLine'; -type Options = PaddingOption[]; - -const LT = `[${Array.from( - new Set(['\r\n', '\r', '\n', '\u2028', '\u2029']), -).join('')}]`; -const PADDING_LINE_SEQUENCE = new RegExp( - String.raw`^(\s*?${LT})\s*${LT}(\s*;?)$`, - 'u', -); - -/** - * Creates tester which check if a node starts with specific keyword with the - * appropriate AST_NODE_TYPES. - * @param keyword The keyword to test. - * @returns the created tester. - * @private - */ -function newKeywordTester( - type: AST_NODE_TYPES | AST_NODE_TYPES[], - keyword: string, -): NodeTestObject { - return { - test(node, sourceCode): boolean { - const isSameKeyword = sourceCode.getFirstToken(node)?.value === keyword; - const isSameType = Array.isArray(type) - ? type.some(val => val === node.type) - : type === node.type; - - return isSameKeyword && isSameType; - }, - }; -} - -/** - * Creates tester which check if a node starts with specific keyword and spans a single line. - * @param keyword The keyword to test. - * @returns the created tester. - * @private - */ -function newSinglelineKeywordTester(keyword: string): NodeTestObject { - return { - test(node, sourceCode): boolean { - return ( - node.loc.start.line === node.loc.end.line && - sourceCode.getFirstToken(node)!.value === keyword - ); - }, - }; -} - -/** - * Creates tester which check if a node starts with specific keyword and spans multiple lines. - * @param keyword The keyword to test. - * @returns the created tester. - * @private - */ -function newMultilineKeywordTester(keyword: string): NodeTestObject { - return { - test(node, sourceCode): boolean { - return ( - node.loc.start.line !== node.loc.end.line && - sourceCode.getFirstToken(node)!.value === keyword - ); - }, - }; -} - -/** - * Creates tester which check if a node is specific type. - * @param type The node type to test. - * @returns the created tester. - * @private - */ -function newNodeTypeTester(type: AST_NODE_TYPES): NodeTestObject { - return { - test: (node): boolean => node.type === type, - }; -} - -/** - * Skips a chain expression node - * @param node The node to test - * @returns A non-chain expression - * @private - */ -function skipChainExpression(node: TSESTree.Node): TSESTree.Node { - return node.type === AST_NODE_TYPES.ChainExpression ? node.expression : node; -} - -/** - * Checks the given node is an expression statement of IIFE. - * @param node The node to check. - * @returns `true` if the node is an expression statement of IIFE. - * @private - */ -function isIIFEStatement(node: TSESTree.Node): boolean { - if (node.type === AST_NODE_TYPES.ExpressionStatement) { - let expression = skipChainExpression(node.expression); - if (expression.type === AST_NODE_TYPES.UnaryExpression) { - expression = skipChainExpression(expression.argument); - } - if (expression.type === AST_NODE_TYPES.CallExpression) { - let node: TSESTree.Node = expression.callee; - while (node.type === AST_NODE_TYPES.SequenceExpression) { - node = node.expressions[node.expressions.length - 1]; - } - return isFunction(node); - } - } - return false; -} - -/** - * Checks the given node is a CommonJS require statement - * @param node The node to check. - * @returns `true` if the node is a CommonJS require statement. - * @private - */ -function isCJSRequire(node: TSESTree.Node): boolean { - if (node.type === AST_NODE_TYPES.VariableDeclaration) { - const declaration = node.declarations.at(0); - if (declaration?.init) { - let call = declaration.init; - while (call.type === AST_NODE_TYPES.MemberExpression) { - call = call.object; - } - if ( - call.type === AST_NODE_TYPES.CallExpression && - call.callee.type === AST_NODE_TYPES.Identifier - ) { - return call.callee.name === 'require'; - } - } - } - return false; -} - -/** - * Checks whether the given node is a block-like statement. - * This checks the last token of the node is the closing brace of a block. - * @param node The node to check. - * @param sourceCode The source code to get tokens. - * @returns `true` if the node is a block-like statement. - * @private - */ -function isBlockLikeStatement( - node: TSESTree.Node, - sourceCode: TSESLint.SourceCode, -): boolean { - // do-while with a block is a block-like statement. - if ( - node.type === AST_NODE_TYPES.DoWhileStatement && - node.body.type === AST_NODE_TYPES.BlockStatement - ) { - return true; - } - - /** - * IIFE is a block-like statement specially from - * JSCS#disallowPaddingNewLinesAfterBlocks. - */ - if (isIIFEStatement(node)) { - return true; - } - - // Checks the last token is a closing brace of blocks. - const lastToken = sourceCode.getLastToken(node, isNotSemicolonToken); - const belongingNode = - lastToken && isClosingBraceToken(lastToken) - ? sourceCode.getNodeByRangeIndex(lastToken.range[0]) - : null; - - return ( - !!belongingNode && - (belongingNode.type === AST_NODE_TYPES.BlockStatement || - belongingNode.type === AST_NODE_TYPES.SwitchStatement) - ); -} - -/** - * Check whether the given node is a directive or not. - * @param node The node to check. - * @param sourceCode The source code object to get tokens. - * @returns `true` if the node is a directive. - */ -function isDirective( - node: TSESTree.Node, - sourceCode: TSESLint.SourceCode, -): boolean { - return ( - node.type === AST_NODE_TYPES.ExpressionStatement && - (node.parent.type === AST_NODE_TYPES.Program || - (node.parent.type === AST_NODE_TYPES.BlockStatement && - isFunction(node.parent.parent))) && - node.expression.type === AST_NODE_TYPES.Literal && - typeof node.expression.value === 'string' && - !isParenthesized(node.expression, sourceCode) - ); -} - -/** - * Check whether the given node is a part of directive prologue or not. - * @param node The node to check. - * @param sourceCode The source code object to get tokens. - * @returns `true` if the node is a part of directive prologue. - */ -function isDirectivePrologue( - node: TSESTree.Node, - sourceCode: TSESLint.SourceCode, -): boolean { - if ( - isDirective(node, sourceCode) && - node.parent && - 'body' in node.parent && - Array.isArray(node.parent.body) - ) { - for (const sibling of node.parent.body) { - if (sibling === node) { - break; - } - if (!isDirective(sibling, sourceCode)) { - return false; - } - } - return true; - } - return false; -} - -/** - * Checks the given node is a CommonJS export statement - * @param node The node to check. - * @returns `true` if the node is a CommonJS export statement. - * @private - */ -function isCJSExport(node: TSESTree.Node): boolean { - if (node.type === AST_NODE_TYPES.ExpressionStatement) { - const expression = node.expression; - if (expression.type === AST_NODE_TYPES.AssignmentExpression) { - let left = expression.left; - if (left.type === AST_NODE_TYPES.MemberExpression) { - while (left.object.type === AST_NODE_TYPES.MemberExpression) { - left = left.object; - } - return ( - left.object.type === AST_NODE_TYPES.Identifier && - (left.object.name === 'exports' || - (left.object.name === 'module' && - left.property.type === AST_NODE_TYPES.Identifier && - left.property.name === 'exports')) - ); - } - } - } - return false; -} - -/** - * Check whether the given node is an expression - * @param node The node to check. - * @param sourceCode The source code object to get tokens. - * @returns `true` if the node is an expression - */ -function isExpression( - node: TSESTree.Node, - sourceCode: TSESLint.SourceCode, -): boolean { - return ( - node.type === AST_NODE_TYPES.ExpressionStatement && - !isDirectivePrologue(node, sourceCode) - ); -} - -/** - * Gets the actual last token. - * - * If a semicolon is semicolon-less style's semicolon, this ignores it. - * For example: - * - * foo() - * ;[1, 2, 3].forEach(bar) - * @param node The node to get. - * @param sourceCode The source code to get tokens. - * @private - */ -function getActualLastToken( - node: TSESTree.Node, - sourceCode: TSESLint.SourceCode, -): TSESTree.Token | null { - const semiToken = sourceCode.getLastToken(node)!; - const prevToken = sourceCode.getTokenBefore(semiToken); - const nextToken = sourceCode.getTokenAfter(semiToken); - const isSemicolonLessStyle = - prevToken && - nextToken && - prevToken.range[0] >= node.range[0] && - isSemicolonToken(semiToken) && - semiToken.loc.start.line !== prevToken.loc.end.line && - semiToken.loc.end.line === nextToken.loc.start.line; - - return isSemicolonLessStyle ? prevToken : semiToken; -} - -/** - * This returns the concatenation of the first 2 captured strings. - * @param _ Unused. Whole matched string. - * @param trailingSpaces The trailing spaces of the first line. - * @param indentSpaces The indentation spaces of the last line. - * @returns The concatenation of trailingSpaces and indentSpaces. - * @private - */ -function replacerToRemovePaddingLines( - _: string, - trailingSpaces: string, - indentSpaces: string, -): string { - return trailingSpaces + indentSpaces; -} - -/** - * Check and report statements for `any` configuration. - * It does nothing. - * - * @private - */ -function verifyForAny(): void { - // Empty -} - -/** - * Check and report statements for `never` configuration. - * This autofix removes blank lines between the given 2 statements. - * However, if comments exist between 2 blank lines, it does not remove those - * blank lines automatically. - * @param context The rule context to report. - * @param _ Unused. The previous node to check. - * @param nextNode The next node to check. - * @param paddingLines The array of token pairs that blank - * lines exist between the pair. - * - * @private - */ -function verifyForNever( - context: TSESLint.RuleContext, - _: TSESTree.Node, - nextNode: TSESTree.Node, - paddingLines: [TSESTree.Token, TSESTree.Token][], -): void { - if (paddingLines.length === 0) { - return; - } - - context.report({ - node: nextNode, - messageId: 'unexpectedBlankLine', - fix(fixer) { - if (paddingLines.length >= 2) { - return null; - } - - const prevToken = paddingLines[0][0]; - const nextToken = paddingLines[0][1]; - const start = prevToken.range[1]; - const end = nextToken.range[0]; - const text = context.sourceCode.text - .slice(start, end) - .replace(PADDING_LINE_SEQUENCE, replacerToRemovePaddingLines); - - return fixer.replaceTextRange([start, end], text); - }, - }); -} - -/** - * Check and report statements for `always` configuration. - * This autofix inserts a blank line between the given 2 statements. - * If the `prevNode` has trailing comments, it inserts a blank line after the - * trailing comments. - * @param context The rule context to report. - * @param prevNode The previous node to check. - * @param nextNode The next node to check. - * @param paddingLines The array of token pairs that blank - * lines exist between the pair. - * - * @private - */ -function verifyForAlways( - context: TSESLint.RuleContext, - prevNode: TSESTree.Node, - nextNode: TSESTree.Node, - paddingLines: [TSESTree.Token, TSESTree.Token][], -): void { - if (paddingLines.length > 0) { - return; - } - - context.report({ - node: nextNode, - messageId: 'expectedBlankLine', - fix(fixer) { - let prevToken = getActualLastToken(prevNode, context.sourceCode)!; - const nextToken = - context.sourceCode.getFirstTokenBetween(prevToken, nextNode, { - includeComments: true, - - /** - * Skip the trailing comments of the previous node. - * This inserts a blank line after the last trailing comment. - * - * For example: - * - * foo(); // trailing comment. - * // comment. - * bar(); - * - * Get fixed to: - * - * foo(); // trailing comment. - * - * // comment. - * bar(); - * @param token The token to check. - * @returns `true` if the token is not a trailing comment. - * @private - */ - filter(token) { - if (isTokenOnSameLine(prevToken, token)) { - prevToken = token; - return false; - } - return true; - }, - }) ?? nextNode; - const insertText = isTokenOnSameLine(prevToken, nextToken) - ? '\n\n' - : '\n'; - - return fixer.insertTextAfter(prevToken, insertText); - }, - }); -} - -/** - * Types of blank lines. - * `any`, `never`, and `always` are defined. - * Those have `verify` method to check and report statements. - * @private - */ -const PaddingTypes = { - any: { verify: verifyForAny }, - never: { verify: verifyForNever }, - always: { verify: verifyForAlways }, -}; - -/** - * Types of statements. - * Those have `test` method to check it matches to the given statement. - * @private - */ -const StatementTypes: Record = { - '*': { test: (): boolean => true }, - 'block-like': { test: isBlockLikeStatement }, - exports: { test: isCJSExport }, - require: { test: isCJSRequire }, - directive: { test: isDirectivePrologue }, - expression: { test: isExpression }, - iife: { test: isIIFEStatement }, - - 'multiline-block-like': { - test: (node, sourceCode) => - node.loc.start.line !== node.loc.end.line && - isBlockLikeStatement(node, sourceCode), - }, - 'multiline-expression': { - test: (node, sourceCode) => - node.loc.start.line !== node.loc.end.line && - node.type === AST_NODE_TYPES.ExpressionStatement && - !isDirectivePrologue(node, sourceCode), - }, - - 'multiline-const': newMultilineKeywordTester('const'), - 'multiline-let': newMultilineKeywordTester('let'), - 'multiline-var': newMultilineKeywordTester('var'), - 'singleline-const': newSinglelineKeywordTester('const'), - 'singleline-let': newSinglelineKeywordTester('let'), - 'singleline-var': newSinglelineKeywordTester('var'), - - block: newNodeTypeTester(AST_NODE_TYPES.BlockStatement), - empty: newNodeTypeTester(AST_NODE_TYPES.EmptyStatement), - function: newNodeTypeTester(AST_NODE_TYPES.FunctionDeclaration), - - break: newKeywordTester(AST_NODE_TYPES.BreakStatement, 'break'), - case: newKeywordTester(AST_NODE_TYPES.SwitchCase, 'case'), - class: newKeywordTester(AST_NODE_TYPES.ClassDeclaration, 'class'), - const: newKeywordTester(AST_NODE_TYPES.VariableDeclaration, 'const'), - continue: newKeywordTester(AST_NODE_TYPES.ContinueStatement, 'continue'), - debugger: newKeywordTester(AST_NODE_TYPES.DebuggerStatement, 'debugger'), - default: newKeywordTester( - [AST_NODE_TYPES.SwitchCase, AST_NODE_TYPES.ExportDefaultDeclaration], - 'default', - ), - do: newKeywordTester(AST_NODE_TYPES.DoWhileStatement, 'do'), - export: newKeywordTester( - [ - AST_NODE_TYPES.ExportDefaultDeclaration, - AST_NODE_TYPES.ExportNamedDeclaration, - ], - 'export', - ), - for: newKeywordTester( - [ - AST_NODE_TYPES.ForStatement, - AST_NODE_TYPES.ForInStatement, - AST_NODE_TYPES.ForOfStatement, - ], - 'for', - ), - if: newKeywordTester(AST_NODE_TYPES.IfStatement, 'if'), - import: newKeywordTester(AST_NODE_TYPES.ImportDeclaration, 'import'), - let: newKeywordTester(AST_NODE_TYPES.VariableDeclaration, 'let'), - return: newKeywordTester(AST_NODE_TYPES.ReturnStatement, 'return'), - switch: newKeywordTester(AST_NODE_TYPES.SwitchStatement, 'switch'), - throw: newKeywordTester(AST_NODE_TYPES.ThrowStatement, 'throw'), - try: newKeywordTester(AST_NODE_TYPES.TryStatement, 'try'), - var: newKeywordTester(AST_NODE_TYPES.VariableDeclaration, 'var'), - while: newKeywordTester( - [AST_NODE_TYPES.WhileStatement, AST_NODE_TYPES.DoWhileStatement], - 'while', - ), - with: newKeywordTester(AST_NODE_TYPES.WithStatement, 'with'), - - // Additional Typescript constructs - interface: newKeywordTester( - AST_NODE_TYPES.TSInterfaceDeclaration, - 'interface', - ), - type: newKeywordTester(AST_NODE_TYPES.TSTypeAliasDeclaration, 'type'), -}; - -//------------------------------------------------------------------------------ -// Rule Definition -//------------------------------------------------------------------------------ - -export default createRule({ - name: 'padding-line-between-statements', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/padding-line-between-statements'], - type: 'layout', - docs: { - description: 'Require or disallow padding lines between statements', - extendsBaseRule: true, - }, - fixable: 'whitespace', - hasSuggestions: false, - // This is intentionally an array schema as you can pass 0..n config objects - schema: { - $defs: { - paddingType: { - type: 'string', - enum: Object.keys(PaddingTypes), - }, - statementType: { - anyOf: [ - { - type: 'string', - enum: Object.keys(StatementTypes), - }, - { - type: 'array', - items: { - type: 'string', - enum: Object.keys(StatementTypes), - }, - minItems: 1, - uniqueItems: true, - additionalItems: false, - }, - ], - }, - }, - type: 'array', - additionalItems: false, - items: { - type: 'object', - properties: { - blankLine: { $ref: '#/$defs/paddingType' }, - prev: { $ref: '#/$defs/statementType' }, - next: { $ref: '#/$defs/statementType' }, - }, - additionalProperties: false, - required: ['blankLine', 'prev', 'next'], - }, - }, - messages: { - unexpectedBlankLine: 'Unexpected blank line before this statement.', - expectedBlankLine: 'Expected blank line before this statement.', - }, - }, - defaultOptions: [], - create(context) { - // eslint-disable-next-line no-restricted-syntax -- We need all raw options. - const configureList = context.options; - - type Scope = { - upper: Scope; - prevNode: TSESTree.Node | null; - } | null; - - let scopeInfo: Scope = null; - - /** - * Processes to enter to new scope. - * This manages the current previous statement. - * - * @private - */ - function enterScope(): void { - scopeInfo = { - upper: scopeInfo, - prevNode: null, - }; - } - - /** - * Processes to exit from the current scope. - * - * @private - */ - function exitScope(): void { - if (scopeInfo) { - scopeInfo = scopeInfo.upper; - } - } - - /** - * Checks whether the given node matches the given type. - * @param node The statement node to check. - * @param type The statement type to check. - * @returns `true` if the statement node matched the type. - * @private - */ - function match(node: TSESTree.Node, type: string[] | string): boolean { - let innerStatementNode = node; - - while (innerStatementNode.type === AST_NODE_TYPES.LabeledStatement) { - innerStatementNode = innerStatementNode.body; - } - - if (Array.isArray(type)) { - return type.some(match.bind(null, innerStatementNode)); - } - - return StatementTypes[type].test(innerStatementNode, context.sourceCode); - } - - /** - * Finds the last matched configure from configureList. - * @paramprevNode The previous statement to match. - * @paramnextNode The current statement to match. - * @returns The tester of the last matched configure. - * @private - */ - function getPaddingType( - prevNode: TSESTree.Node, - nextNode: TSESTree.Node, - ): (typeof PaddingTypes)[keyof typeof PaddingTypes] { - for (let i = configureList.length - 1; i >= 0; --i) { - const configure = configureList[i]; - if ( - match(prevNode, configure.prev) && - match(nextNode, configure.next) - ) { - return PaddingTypes[configure.blankLine]; - } - } - return PaddingTypes.any; - } - - /** - * Gets padding line sequences between the given 2 statements. - * Comments are separators of the padding line sequences. - * @paramprevNode The previous statement to count. - * @paramnextNode The current statement to count. - * @returns The array of token pairs. - * @private - */ - function getPaddingLineSequences( - prevNode: TSESTree.Node, - nextNode: TSESTree.Node, - ): [TSESTree.Token, TSESTree.Token][] { - const pairs: [TSESTree.Token, TSESTree.Token][] = []; - let prevToken: TSESTree.Token = getActualLastToken( - prevNode, - context.sourceCode, - )!; - - if (nextNode.loc.start.line - prevToken.loc.end.line >= 2) { - do { - const token: TSESTree.Token = context.sourceCode.getTokenAfter( - prevToken, - { - includeComments: true, - }, - )!; - - if (token.loc.start.line - prevToken.loc.end.line >= 2) { - pairs.push([prevToken, token]); - } - prevToken = token; - } while (prevToken.range[0] < nextNode.range[0]); - } - - return pairs; - } - - /** - * Verify padding lines between the given node and the previous node. - * @param node The node to verify. - * - * @private - */ - function verify(node: TSESTree.Node): void { - if ( - !node.parent || - ![ - AST_NODE_TYPES.BlockStatement, - AST_NODE_TYPES.Program, - AST_NODE_TYPES.SwitchCase, - AST_NODE_TYPES.SwitchStatement, - AST_NODE_TYPES.TSModuleBlock, - ].includes(node.parent.type) - ) { - return; - } - - // Save this node as the current previous statement. - const prevNode = scopeInfo!.prevNode; - - // Verify. - if (prevNode) { - const type = getPaddingType(prevNode, node); - const paddingLines = getPaddingLineSequences(prevNode, node); - - type.verify(context, prevNode, node, paddingLines); - } - - scopeInfo!.prevNode = node; - } - - /** - * Verify padding lines between the given node and the previous node. - * Then process to enter to new scope. - * @param node The node to verify. - * - * @private - */ - function verifyThenEnterScope(node: TSESTree.Node): void { - verify(node); - enterScope(); - } - - return { - Program: enterScope, - BlockStatement: enterScope, - SwitchStatement: enterScope, - TSModuleBlock: enterScope, - 'Program:exit': exitScope, - 'BlockStatement:exit': exitScope, - 'SwitchStatement:exit': exitScope, - 'TSModuleBlock:exit': exitScope, - - ':statement': verify, - - SwitchCase: verifyThenEnterScope, - TSDeclareFunction: verifyThenEnterScope, - 'SwitchCase:exit': exitScope, - 'TSDeclareFunction:exit': exitScope, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/quotes.ts b/packages/eslint-plugin/src/rules/quotes.ts deleted file mode 100644 index 2e1f3d6cd901..000000000000 --- a/packages/eslint-plugin/src/rules/quotes.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('quotes'); - -export type Options = InferOptionsTypeFromRule; -export type MessageIds = InferMessageIdsTypeFromRule; - -export default createRule({ - name: 'quotes', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/quotes'], - type: 'layout', - docs: { - description: - 'Enforce the consistent use of either backticks, double, or single quotes', - extendsBaseRule: true, - }, - fixable: 'code', - hasSuggestions: baseRule.meta.hasSuggestions, - messages: baseRule.meta.messages, - schema: baseRule.meta.schema, - }, - defaultOptions: [ - 'double', - { - allowTemplateLiterals: false, - avoidEscape: false, - }, - ], - create(context, [option]) { - const rules = baseRule.create(context); - - function isAllowedAsNonBacktick(node: TSESTree.Literal): boolean { - const parent = node.parent; - - switch (parent.type) { - case AST_NODE_TYPES.TSAbstractMethodDefinition: - case AST_NODE_TYPES.TSMethodSignature: - case AST_NODE_TYPES.TSPropertySignature: - case AST_NODE_TYPES.TSModuleDeclaration: - case AST_NODE_TYPES.TSLiteralType: - case AST_NODE_TYPES.TSExternalModuleReference: - return true; - - case AST_NODE_TYPES.TSEnumMember: - return node === parent.id; - - case AST_NODE_TYPES.TSAbstractPropertyDefinition: - case AST_NODE_TYPES.PropertyDefinition: - return node === parent.key; - - default: - return false; - } - } - - return { - Literal(node): void { - if (option === 'backtick' && isAllowedAsNonBacktick(node)) { - return; - } - - rules.Literal(node); - }, - - TemplateLiteral(node): void { - rules.TemplateLiteral(node); - }, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/semi.ts b/packages/eslint-plugin/src/rules/semi.ts deleted file mode 100644 index 674ea99e3a96..000000000000 --- a/packages/eslint-plugin/src/rules/semi.ts +++ /dev/null @@ -1,75 +0,0 @@ -import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('semi'); - -export type Options = InferOptionsTypeFromRule; -export type MessageIds = InferMessageIdsTypeFromRule; - -export default createRule({ - name: 'semi', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/semi'], - type: 'layout', - docs: { - description: 'Require or disallow semicolons instead of ASI', - // too opinionated to be recommended - extendsBaseRule: true, - }, - fixable: 'code', - hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, - messages: baseRule.meta.messages, - }, - defaultOptions: [ - 'always', - { - omitLastInOneLineBlock: false, - beforeStatementContinuationChars: 'any', - }, - ], - create(context) { - const rules = baseRule.create(context); - const checkForSemicolon = - rules.ExpressionStatement as TSESLint.RuleFunction; - - /* - The following nodes are handled by the member-delimiter-style rule - AST_NODE_TYPES.TSCallSignatureDeclaration, - AST_NODE_TYPES.TSConstructSignatureDeclaration, - AST_NODE_TYPES.TSIndexSignature, - AST_NODE_TYPES.TSMethodSignature, - AST_NODE_TYPES.TSPropertySignature, - */ - const nodesToCheck = [ - AST_NODE_TYPES.PropertyDefinition, - AST_NODE_TYPES.TSAbstractPropertyDefinition, - AST_NODE_TYPES.TSDeclareFunction, - AST_NODE_TYPES.TSExportAssignment, - AST_NODE_TYPES.TSImportEqualsDeclaration, - AST_NODE_TYPES.TSTypeAliasDeclaration, - AST_NODE_TYPES.TSEmptyBodyFunctionExpression, - ].reduce((acc, node) => { - acc[node as string] = checkForSemicolon; - return acc; - }, {}); - - return { - ...rules, - ...nodesToCheck, - ExportDefaultDeclaration(node): void { - if (node.declaration.type !== AST_NODE_TYPES.TSInterfaceDeclaration) { - rules.ExportDefaultDeclaration(node); - } - }, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/space-before-blocks.ts b/packages/eslint-plugin/src/rules/space-before-blocks.ts deleted file mode 100644 index cb50e5b57eb0..000000000000 --- a/packages/eslint-plugin/src/rules/space-before-blocks.ts +++ /dev/null @@ -1,94 +0,0 @@ -import type { TSESTree } from '@typescript-eslint/utils'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule, isTokenOnSameLine } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('space-before-blocks'); - -export type Options = InferOptionsTypeFromRule; -export type MessageIds = InferMessageIdsTypeFromRule; - -export default createRule({ - name: 'space-before-blocks', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/space-before-blocks'], - type: 'layout', - docs: { - description: 'Enforce consistent spacing before blocks', - extendsBaseRule: true, - }, - fixable: baseRule.meta.fixable, - hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, - messages: { - // @ts-expect-error -- we report on this messageId so we need to ensure it's there in case ESLint changes in future - unexpectedSpace: 'Unexpected space before opening brace.', - // @ts-expect-error -- we report on this messageId so we need to ensure it's there in case ESLint changes in future - missingSpace: 'Missing space before opening brace.', - ...baseRule.meta.messages, - }, - }, - defaultOptions: ['always'], - create(context, [config]) { - const rules = baseRule.create(context); - - let requireSpace = true; - - if (typeof config === 'object') { - requireSpace = config.classes === 'always'; - } else if (config === 'never') { - requireSpace = false; - } - - function checkPrecedingSpace( - node: TSESTree.Token | TSESTree.TSInterfaceBody, - ): void { - const precedingToken = context.sourceCode.getTokenBefore(node); - if (precedingToken && isTokenOnSameLine(precedingToken, node)) { - const hasSpace = context.sourceCode.isSpaceBetween( - precedingToken, - node as TSESTree.Token, - ); - - if (requireSpace && !hasSpace) { - context.report({ - node, - messageId: 'missingSpace', - fix(fixer) { - return fixer.insertTextBefore(node, ' '); - }, - }); - } else if (!requireSpace && hasSpace) { - context.report({ - node, - messageId: 'unexpectedSpace', - fix(fixer) { - return fixer.removeRange([ - precedingToken.range[1], - node.range[0], - ]); - }, - }); - } - } - } - - function checkSpaceAfterEnum(node: TSESTree.TSEnumDeclaration): void { - const punctuator = context.sourceCode.getTokenAfter(node.id); - if (punctuator) { - checkPrecedingSpace(punctuator); - } - } - - return { - ...rules, - TSEnumDeclaration: checkSpaceAfterEnum, - TSInterfaceBody: checkPrecedingSpace, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/space-before-function-paren.ts b/packages/eslint-plugin/src/rules/space-before-function-paren.ts deleted file mode 100644 index 78ea98239db7..000000000000 --- a/packages/eslint-plugin/src/rules/space-before-function-paren.ts +++ /dev/null @@ -1,196 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import type { TSESTree } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; - -import { createRule, isOpeningParenToken } from '../util'; - -type Option = 'always' | 'never'; -type FuncOption = Option | 'ignore'; - -export type Options = [ - | Option - | { - anonymous?: FuncOption; - named?: FuncOption; - asyncArrow?: FuncOption; - }, -]; -export type MessageIds = 'missing' | 'unexpected'; - -export default createRule({ - name: 'space-before-function-paren', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/space-before-function-paren'], - type: 'layout', - docs: { - description: 'Enforce consistent spacing before function parenthesis', - extendsBaseRule: true, - }, - fixable: 'whitespace', - schema: [ - { - oneOf: [ - { - type: 'string', - enum: ['always', 'never'], - }, - { - type: 'object', - properties: { - anonymous: { - type: 'string', - enum: ['always', 'never', 'ignore'], - }, - named: { - type: 'string', - enum: ['always', 'never', 'ignore'], - }, - asyncArrow: { - type: 'string', - enum: ['always', 'never', 'ignore'], - }, - }, - additionalProperties: false, - }, - ], - }, - ], - messages: { - unexpected: 'Unexpected space before function parentheses.', - missing: 'Missing space before function parentheses.', - }, - }, - defaultOptions: ['always'], - - create(context, [firstOption]) { - const baseConfig = typeof firstOption === 'string' ? firstOption : 'always'; - const overrideConfig = typeof firstOption === 'object' ? firstOption : {}; - - /** - * Determines whether a function has a name. - */ - function isNamedFunction( - node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.TSDeclareFunction - | TSESTree.TSEmptyBodyFunctionExpression, - ): boolean { - if (node.id != null) { - return true; - } - - const parent = node.parent; - - return ( - parent.type === AST_NODE_TYPES.MethodDefinition || - parent.type === AST_NODE_TYPES.TSAbstractMethodDefinition || - (parent.type === AST_NODE_TYPES.Property && - (parent.kind === 'get' || parent.kind === 'set' || parent.method)) - ); - } - - /** - * Gets the config for a given function - */ - function getConfigForFunction( - node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.TSDeclareFunction - | TSESTree.TSEmptyBodyFunctionExpression, - ): FuncOption { - if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) { - // Always ignore non-async functions and arrow functions without parens, e.g. async foo => bar - if ( - node.async && - isOpeningParenToken( - context.sourceCode.getFirstToken(node, { skip: 1 })!, - ) - ) { - return overrideConfig.asyncArrow ?? baseConfig; - } - } else if (isNamedFunction(node)) { - return overrideConfig.named ?? baseConfig; - - // `generator-star-spacing` should warn anonymous generators. E.g. `function* () {}` - } else if (!node.generator) { - return overrideConfig.anonymous ?? baseConfig; - } - - return 'ignore'; - } - - /** - * Checks the parens of a function node - * @param node A function node - */ - function checkFunction( - node: - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.TSDeclareFunction - | TSESTree.TSEmptyBodyFunctionExpression, - ): void { - const functionConfig = getConfigForFunction(node); - - if (functionConfig === 'ignore') { - return; - } - - let leftToken: TSESTree.Token; - let rightToken: TSESTree.Token; - if (node.typeParameters) { - leftToken = context.sourceCode.getLastToken(node.typeParameters)!; - rightToken = context.sourceCode.getTokenAfter(leftToken)!; - } else { - rightToken = context.sourceCode.getFirstToken( - node, - isOpeningParenToken, - )!; - leftToken = context.sourceCode.getTokenBefore(rightToken)!; - } - - const hasSpacing = context.sourceCode.isSpaceBetween( - leftToken, - rightToken, - ); - - if (hasSpacing && functionConfig === 'never') { - context.report({ - node, - loc: { - start: leftToken.loc.end, - end: rightToken.loc.start, - }, - messageId: 'unexpected', - fix: fixer => - fixer.removeRange([leftToken.range[1], rightToken.range[0]]), - }); - } else if ( - !hasSpacing && - functionConfig === 'always' && - (!node.typeParameters || node.id) - ) { - context.report({ - node, - loc: rightToken.loc, - messageId: 'missing', - fix: fixer => fixer.insertTextAfter(leftToken, ' '), - }); - } - } - - return { - ArrowFunctionExpression: checkFunction, - FunctionDeclaration: checkFunction, - FunctionExpression: checkFunction, - TSEmptyBodyFunctionExpression: checkFunction, - TSDeclareFunction: checkFunction, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/space-infix-ops.ts b/packages/eslint-plugin/src/rules/space-infix-ops.ts deleted file mode 100644 index 432f7907b270..000000000000 --- a/packages/eslint-plugin/src/rules/space-infix-ops.ts +++ /dev/null @@ -1,187 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { AST_TOKEN_TYPES, TSESTree } from '@typescript-eslint/utils'; - -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../util'; -import { createRule, isNotOpeningParenToken } from '../util'; -import { getESLintCoreRule } from '../util/getESLintCoreRule'; - -const baseRule = getESLintCoreRule('space-infix-ops'); - -export type Options = InferOptionsTypeFromRule; -export type MessageIds = InferMessageIdsTypeFromRule; - -const UNIONS = ['|', '&']; - -export default createRule({ - name: 'space-infix-ops', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/space-infix-ops'], - type: 'layout', - docs: { - description: 'Require spacing around infix operators', - extendsBaseRule: true, - }, - fixable: baseRule.meta.fixable, - hasSuggestions: baseRule.meta.hasSuggestions, - schema: baseRule.meta.schema, - messages: { - // @ts-expect-error -- we report on this messageId so we need to ensure it's there in case ESLint changes in future - missingSpace: "Operator '{{operator}}' must be spaced.", - ...baseRule.meta.messages, - }, - }, - defaultOptions: [ - { - int32Hint: false, - }, - ], - create(context) { - const rules = baseRule.create(context); - - function report(operator: TSESTree.Token): void { - context.report({ - node: operator, - messageId: 'missingSpace', - data: { - operator: operator.value, - }, - fix(fixer) { - const previousToken = context.sourceCode.getTokenBefore(operator); - const afterToken = context.sourceCode.getTokenAfter(operator); - let fixString = ''; - - if (operator.range[0] - previousToken!.range[1] === 0) { - fixString = ' '; - } - - fixString += operator.value; - - if (afterToken!.range[0] - operator.range[1] === 0) { - fixString += ' '; - } - - return fixer.replaceText(operator, fixString); - }, - }); - } - - function isSpaceChar(token: TSESTree.Token): boolean { - return ( - token.type === AST_TOKEN_TYPES.Punctuator && /^[=?:]$/.test(token.value) - ); - } - - function checkAndReportAssignmentSpace( - leftNode: TSESTree.Node | TSESTree.Token | null, - rightNode?: TSESTree.Node | TSESTree.Token | null, - ): void { - if (!rightNode || !leftNode) { - return; - } - - const operator = context.sourceCode.getFirstTokenBetween( - leftNode, - rightNode, - isSpaceChar, - )!; - - const prev = context.sourceCode.getTokenBefore(operator)!; - const next = context.sourceCode.getTokenAfter(operator)!; - - if ( - !context.sourceCode.isSpaceBetween(prev, operator) || - !context.sourceCode.isSpaceBetween(operator, next) - ) { - report(operator); - } - } - - /** - * Check if it has an assignment char and report if it's faulty - * @param node The node to report - */ - function checkForEnumAssignmentSpace(node: TSESTree.TSEnumMember): void { - checkAndReportAssignmentSpace(node.id, node.initializer); - } - - /** - * Check if it has an assignment char and report if it's faulty - * @param node The node to report - */ - function checkForPropertyDefinitionAssignmentSpace( - node: TSESTree.PropertyDefinition, - ): void { - const leftNode = - node.optional && !node.typeAnnotation - ? context.sourceCode.getTokenAfter(node.key) - : node.typeAnnotation ?? node.key; - - checkAndReportAssignmentSpace(leftNode, node.value); - } - - /** - * Check if it is missing spaces between type annotations chaining - * @param typeAnnotation TypeAnnotations list - */ - function checkForTypeAnnotationSpace( - typeAnnotation: TSESTree.TSIntersectionType | TSESTree.TSUnionType, - ): void { - const types = typeAnnotation.types; - - types.forEach(type => { - const skipFunctionParenthesis = - type.type === TSESTree.AST_NODE_TYPES.TSFunctionType - ? isNotOpeningParenToken - : 0; - const operator = context.sourceCode.getTokenBefore( - type, - skipFunctionParenthesis, - ); - - if (operator != null && UNIONS.includes(operator.value)) { - const prev = context.sourceCode.getTokenBefore(operator); - const next = context.sourceCode.getTokenAfter(operator); - - if ( - !context.sourceCode.isSpaceBetween(prev!, operator) || - !context.sourceCode.isSpaceBetween(operator, next!) - ) { - report(operator); - } - } - }); - } - - /** - * Check if it has an assignment char and report if it's faulty - * @param node The node to report - */ - function checkForTypeAliasAssignment( - node: TSESTree.TSTypeAliasDeclaration, - ): void { - checkAndReportAssignmentSpace( - node.typeParameters ?? node.id, - node.typeAnnotation, - ); - } - - function checkForTypeConditional(node: TSESTree.TSConditionalType): void { - checkAndReportAssignmentSpace(node.extendsType, node.trueType); - checkAndReportAssignmentSpace(node.trueType, node.falseType); - } - - return { - ...rules, - TSEnumMember: checkForEnumAssignmentSpace, - PropertyDefinition: checkForPropertyDefinitionAssignmentSpace, - TSTypeAliasDeclaration: checkForTypeAliasAssignment, - TSUnionType: checkForTypeAnnotationSpace, - TSIntersectionType: checkForTypeAnnotationSpace, - TSConditionalType: checkForTypeConditional, - }; - }, -}); diff --git a/packages/eslint-plugin/src/rules/type-annotation-spacing.ts b/packages/eslint-plugin/src/rules/type-annotation-spacing.ts deleted file mode 100644 index 47fbf80d8ac0..000000000000 --- a/packages/eslint-plugin/src/rules/type-annotation-spacing.ts +++ /dev/null @@ -1,289 +0,0 @@ -import type { TSESTree } from '@typescript-eslint/utils'; - -import { - createRule, - isClassOrTypeElement, - isFunction, - isFunctionOrFunctionType, - isIdentifier, - isTSConstructorType, - isTSFunctionType, - isVariableDeclarator, -} from '../util'; - -interface WhitespaceRule { - readonly before?: boolean; - readonly after?: boolean; -} - -interface WhitespaceOverride { - readonly colon?: WhitespaceRule; - readonly arrow?: WhitespaceRule; - readonly variable?: WhitespaceRule; - readonly property?: WhitespaceRule; - readonly parameter?: WhitespaceRule; - readonly returnType?: WhitespaceRule; -} - -interface Config extends WhitespaceRule { - readonly overrides?: WhitespaceOverride; -} - -type WhitespaceRules = Required; - -type Options = [Config?]; -type MessageIds = - | 'expectedSpaceAfter' - | 'expectedSpaceBefore' - | 'unexpectedSpaceAfter' - | 'unexpectedSpaceBefore' - | 'unexpectedSpaceBetween'; - -function createRules(options?: Config): WhitespaceRules { - const globals = { - ...(options?.before !== undefined ? { before: options.before } : {}), - ...(options?.after !== undefined ? { after: options.after } : {}), - }; - const override = options?.overrides ?? {}; - const colon = { - ...{ before: false, after: true }, - ...globals, - ...override.colon, - }; - const arrow = { - ...{ before: true, after: true }, - ...globals, - ...override.arrow, - }; - - return { - colon: colon, - arrow: arrow, - variable: { ...colon, ...override.variable }, - property: { ...colon, ...override.property }, - parameter: { ...colon, ...override.parameter }, - returnType: { ...colon, ...override.returnType }, - }; -} - -function getIdentifierRules( - rules: WhitespaceRules, - node: TSESTree.Node | undefined, -): WhitespaceRule { - const scope = node?.parent; - - if (isVariableDeclarator(scope)) { - return rules.variable; - } else if (isFunctionOrFunctionType(scope)) { - return rules.parameter; - } - return rules.colon; -} - -function getRules( - rules: WhitespaceRules, - node: TSESTree.TypeNode, -): WhitespaceRule { - const scope = node.parent.parent; - - if (isTSFunctionType(scope) || isTSConstructorType(scope)) { - return rules.arrow; - } else if (isIdentifier(scope)) { - return getIdentifierRules(rules, scope); - } else if (isClassOrTypeElement(scope)) { - return rules.property; - } else if (isFunction(scope)) { - return rules.returnType; - } - return rules.colon; -} - -export default createRule({ - name: 'type-annotation-spacing', - meta: { - deprecated: true, - replacedBy: ['@stylistic/ts/type-annotation-spacing'], - type: 'layout', - docs: { - description: 'Require consistent spacing around type annotations', - }, - fixable: 'whitespace', - messages: { - expectedSpaceAfter: "Expected a space after the '{{type}}'.", - expectedSpaceBefore: "Expected a space before the '{{type}}'.", - unexpectedSpaceAfter: "Unexpected space after the '{{type}}'.", - unexpectedSpaceBefore: "Unexpected space before the '{{type}}'.", - unexpectedSpaceBetween: - "Unexpected space between the '{{previousToken}}' and the '{{type}}'.", - }, - schema: [ - { - $defs: { - spacingConfig: { - type: 'object', - properties: { - before: { type: 'boolean' }, - after: { type: 'boolean' }, - }, - additionalProperties: false, - }, - }, - type: 'object', - properties: { - before: { type: 'boolean' }, - after: { type: 'boolean' }, - overrides: { - type: 'object', - properties: { - colon: { $ref: '#/items/0/$defs/spacingConfig' }, - arrow: { $ref: '#/items/0/$defs/spacingConfig' }, - variable: { $ref: '#/items/0/$defs/spacingConfig' }, - parameter: { $ref: '#/items/0/$defs/spacingConfig' }, - property: { $ref: '#/items/0/$defs/spacingConfig' }, - returnType: { $ref: '#/items/0/$defs/spacingConfig' }, - }, - additionalProperties: false, - }, - }, - additionalProperties: false, - }, - ], - }, - defaultOptions: [ - // technically there is a default, but the overrides mean - // that if we apply them here, it will break the no override case. - {}, - ], - create(context, [options]) { - const punctuators = [':', '=>']; - - const ruleSet = createRules(options); - - /** - * Checks if there's proper spacing around type annotations (no space - * before colon, one space after). - */ - function checkTypeAnnotationSpacing( - typeAnnotation: TSESTree.TypeNode, - ): void { - const nextToken = typeAnnotation; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const punctuatorTokenEnd = context.sourceCode.getTokenBefore(nextToken)!; - let punctuatorTokenStart = punctuatorTokenEnd; - let previousToken = - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - context.sourceCode.getTokenBefore(punctuatorTokenEnd)!; - let type = punctuatorTokenEnd.value; - - if (!punctuators.includes(type)) { - return; - } - - const { before, after } = getRules(ruleSet, typeAnnotation); - - if (type === ':' && previousToken.value === '?') { - if ( - context.sourceCode.isSpaceBetween(previousToken, punctuatorTokenStart) - ) { - context.report({ - node: punctuatorTokenStart, - messageId: 'unexpectedSpaceBetween', - data: { - type, - previousToken: previousToken.value, - }, - fix(fixer) { - return fixer.removeRange([ - previousToken.range[1], - punctuatorTokenStart.range[0], - ]); - }, - }); - } - - // shift the start to the ? - type = '?:'; - punctuatorTokenStart = previousToken; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - previousToken = context.sourceCode.getTokenBefore(previousToken)!; - - // handle the +/- modifiers for optional modification operators - if (previousToken.value === '+' || previousToken.value === '-') { - type = `${previousToken.value}?:`; - punctuatorTokenStart = previousToken; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - previousToken = context.sourceCode.getTokenBefore(previousToken)!; - } - } - - const previousDelta = - punctuatorTokenStart.range[0] - previousToken.range[1]; - const nextDelta = nextToken.range[0] - punctuatorTokenEnd.range[1]; - - if (after && nextDelta === 0) { - context.report({ - node: punctuatorTokenEnd, - messageId: 'expectedSpaceAfter', - data: { - type, - }, - fix(fixer) { - return fixer.insertTextAfter(punctuatorTokenEnd, ' '); - }, - }); - } else if (!after && nextDelta > 0) { - context.report({ - node: punctuatorTokenEnd, - messageId: 'unexpectedSpaceAfter', - data: { - type, - }, - fix(fixer) { - return fixer.removeRange([ - punctuatorTokenEnd.range[1], - nextToken.range[0], - ]); - }, - }); - } - - if (before && previousDelta === 0) { - context.report({ - node: punctuatorTokenStart, - messageId: 'expectedSpaceBefore', - data: { - type, - }, - fix(fixer) { - return fixer.insertTextAfter(previousToken, ' '); - }, - }); - } else if (!before && previousDelta > 0) { - context.report({ - node: punctuatorTokenStart, - messageId: 'unexpectedSpaceBefore', - data: { - type, - }, - fix(fixer) { - return fixer.removeRange([ - previousToken.range[1], - punctuatorTokenStart.range[0], - ]); - }, - }); - } - } - - return { - TSMappedType(node): void { - if (node.typeAnnotation) { - checkTypeAnnotationSpacing(node.typeAnnotation); - } - }, - TSTypeAnnotation(node): void { - checkTypeAnnotationSpacing(node.typeAnnotation); - }, - }; - }, -}); diff --git a/packages/eslint-plugin/src/util/getESLintCoreRule.ts b/packages/eslint-plugin/src/util/getESLintCoreRule.ts index be28069d2877..59b22c7292fd 100644 --- a/packages/eslint-plugin/src/util/getESLintCoreRule.ts +++ b/packages/eslint-plugin/src/util/getESLintCoreRule.ts @@ -4,23 +4,13 @@ import { builtinRules } from 'eslint/use-at-your-own-risk'; interface RuleMap { /* eslint-disable @typescript-eslint/consistent-type-imports -- more concise to use inline imports */ 'arrow-parens': typeof import('eslint/lib/rules/arrow-parens'); - 'block-spacing': typeof import('eslint/lib/rules/block-spacing'); - 'brace-style': typeof import('eslint/lib/rules/brace-style'); - 'comma-dangle': typeof import('eslint/lib/rules/comma-dangle'); 'consistent-return': typeof import('eslint/lib/rules/consistent-return'); 'dot-notation': typeof import('eslint/lib/rules/dot-notation'); - indent: typeof import('eslint/lib/rules/indent'); 'init-declarations': typeof import('eslint/lib/rules/init-declarations'); - 'key-spacing': typeof import('eslint/lib/rules/key-spacing'); - 'keyword-spacing': typeof import('eslint/lib/rules/keyword-spacing'); - 'lines-around-comment': typeof import('eslint/lib/rules/lines-around-comment'); - 'lines-between-class-members': typeof import('eslint/lib/rules/lines-between-class-members'); 'max-params': typeof import('eslint/lib/rules/max-params'); 'no-dupe-args': typeof import('eslint/lib/rules/no-dupe-args'); 'no-dupe-class-members': typeof import('eslint/lib/rules/no-dupe-class-members'); 'no-empty-function': typeof import('eslint/lib/rules/no-empty-function'); - 'no-extra-parens': typeof import('eslint/lib/rules/no-extra-parens'); - 'no-extra-semi': typeof import('eslint/lib/rules/no-extra-semi'); 'no-implicit-globals': typeof import('eslint/lib/rules/no-implicit-globals'); 'no-invalid-this': typeof import('eslint/lib/rules/no-invalid-this'); 'no-loop-func': typeof import('eslint/lib/rules/no-loop-func'); @@ -31,13 +21,8 @@ interface RuleMap { 'no-unused-expressions': typeof import('eslint/lib/rules/no-unused-expressions'); 'no-useless-constructor': typeof import('eslint/lib/rules/no-useless-constructor'); 'no-restricted-globals': typeof import('eslint/lib/rules/no-restricted-globals'); - 'object-curly-spacing': typeof import('eslint/lib/rules/object-curly-spacing'); 'prefer-const': typeof import('eslint/lib/rules/prefer-const'); 'prefer-destructuring': typeof import('eslint/lib/rules/prefer-destructuring'); - quotes: typeof import('eslint/lib/rules/quotes'); - semi: typeof import('eslint/lib/rules/semi'); - 'space-before-blocks': typeof import('eslint/lib/rules/space-before-blocks'); - 'space-infix-ops': typeof import('eslint/lib/rules/space-infix-ops'); strict: typeof import('eslint/lib/rules/strict'); /* eslint-enable @typescript-eslint/consistent-type-imports */ } diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/lines-between-class-members.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/lines-between-class-members.shot deleted file mode 100644 index 551df7110d76..000000000000 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/lines-between-class-members.shot +++ /dev/null @@ -1,33 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Validating rule docs lines-between-class-members.mdx code examples ESLint output 1`] = ` -"Options: "always", { "exceptAfterOverload": true } - -class foo { - bar(a: string): void; - bar(a: string, b: string): void; - bar(a: string, b: string) {} - - baz() {} - - qux() {} -} -" -`; - -exports[`Validating rule docs lines-between-class-members.mdx code examples ESLint output 2`] = ` -"Options: "always", { "exceptAfterOverload": false } - -class foo { - bar(a: string): void; - - bar(a: string, b: string): void; - - bar(a: string, b: string) {} - - baz() {} - - qux() {} -} -" -`; diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/member-delimiter-style.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/member-delimiter-style.shot deleted file mode 100644 index b1b5eb48f627..000000000000 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/member-delimiter-style.shot +++ /dev/null @@ -1,58 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Validating rule docs member-delimiter-style.mdx code examples ESLint output 1`] = ` -"Incorrect - -// missing semicolon delimiter -interface Foo { - name: string - ~ Expected a semicolon. - greet(): string - ~ Expected a semicolon. -} - -// using incorrect delimiter -interface Bar { - name: string, - ~ Expected a semicolon. - greet(): string, - ~ Expected a semicolon. -} - -// missing last member delimiter -interface Baz { - name: string; - greet(): string - ~ Expected a semicolon. -} - -// incorrect delimiter -type FooBar = { name: string, greet(): string } - ~ Expected a semicolon. - -// last member should not have delimiter -type FooBar = { name: string; greet(): string; } - ~ Unexpected separator (;). -" -`; - -exports[`Validating rule docs member-delimiter-style.mdx code examples ESLint output 2`] = ` -"Correct - -interface Foo { - name: string; - greet(): string; -} - -interface Foo { name: string } - -type Bar = { - name: string; - greet(): string; -} - -type Bar = { name: string } - -type FooBar = { name: string; greet(): string } -" -`; diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/space-before-blocks.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/space-before-blocks.shot deleted file mode 100644 index 74158461fac1..000000000000 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/space-before-blocks.shot +++ /dev/null @@ -1,33 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Validating rule docs space-before-blocks.mdx code examples ESLint output 1`] = ` -"Incorrect - -enum Breakpoint{ - ~ Missing space before opening brace. - Large, - Medium, -} - -interface State{ - ~ Missing space before opening brace. - currentBreakpoint: Breakpoint; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -} -~ -" -`; - -exports[`Validating rule docs space-before-blocks.mdx code examples ESLint output 2`] = ` -"Correct - -enum Breakpoint { - Large, - Medium, -} - -interface State { - currentBreakpoint: Breakpoint; -} -" -`; diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/type-annotation-spacing.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/type-annotation-spacing.shot deleted file mode 100644 index 765805e9bbab..000000000000 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/type-annotation-spacing.shot +++ /dev/null @@ -1,311 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Validating rule docs type-annotation-spacing.mdx code examples ESLint output 1`] = ` -"Incorrect - -let foo:string = "bar"; - ~ Expected a space after the ':'. -let foo :string = "bar"; - ~ Expected a space after the ':'. - ~ Unexpected space before the ':'. -let foo : string = "bar"; - ~ Unexpected space before the ':'. - -function foo():string {} - ~ Expected a space after the ':'. -function foo() :string {} - ~ Expected a space after the ':'. - ~ Unexpected space before the ':'. -function foo() : string {} - ~ Unexpected space before the ':'. - -class Foo { - name:string; - ~ Expected a space after the ':'. -} - -class Foo { - name :string; - ~ Expected a space after the ':'. - ~ Unexpected space before the ':'. -} - -class Foo { - name : string; - ~ Unexpected space before the ':'. -} - -type Foo = ()=>{}; - ~~ Expected a space after the '=>'. - ~~ Expected a space before the '=>'. -type Foo = () =>{}; - ~~ Expected a space after the '=>'. -type Foo = ()=> {}; - ~~ Expected a space before the '=>'. -" -`; - -exports[`Validating rule docs type-annotation-spacing.mdx code examples ESLint output 2`] = ` -"Correct - -let foo: string = "bar"; - -function foo(): string {} - -class Foo { - name: string; -} - -type Foo = () => {}; -" -`; - -exports[`Validating rule docs type-annotation-spacing.mdx code examples ESLint output 3`] = ` -"Incorrect -Options: { "before": false, "after": true } - -let foo:string = "bar"; - ~ Expected a space after the ':'. -let foo :string = "bar"; - ~ Expected a space after the ':'. - ~ Unexpected space before the ':'. -let foo : string = "bar"; - ~ Unexpected space before the ':'. - -function foo():string {} - ~ Expected a space after the ':'. -function foo() :string {} - ~ Expected a space after the ':'. - ~ Unexpected space before the ':'. -function foo() : string {} - ~ Unexpected space before the ':'. - -class Foo { - name:string; - ~ Expected a space after the ':'. -} - -class Foo { - name :string; - ~ Expected a space after the ':'. - ~ Unexpected space before the ':'. -} - -class Foo { - name : string; - ~ Unexpected space before the ':'. -} - -type Foo = ()=>{}; - ~~ Expected a space after the '=>'. -type Foo = () =>{}; - ~~ Expected a space after the '=>'. - ~~ Unexpected space before the '=>'. -type Foo = () => {}; - ~~ Unexpected space before the '=>'. -" -`; - -exports[`Validating rule docs type-annotation-spacing.mdx code examples ESLint output 4`] = ` -"Correct -Options: { "before": false, "after": true } - -let foo: string = "bar"; - -function foo(): string {} - -class Foo { - name: string; -} - -type Foo = ()=> {}; -" -`; - -exports[`Validating rule docs type-annotation-spacing.mdx code examples ESLint output 5`] = ` -"Incorrect -Options: { "before": true, "after": true } - -let foo: string = "bar"; - ~ Expected a space before the ':'. -let foo:string = "bar"; - ~ Expected a space after the ':'. - ~ Expected a space before the ':'. -let foo :string = "bar"; - ~ Expected a space after the ':'. - -function foo(): string {} - ~ Expected a space before the ':'. -function foo():string {} - ~ Expected a space after the ':'. - ~ Expected a space before the ':'. -function foo() :string {} - ~ Expected a space after the ':'. - -class Foo { - name: string; - ~ Expected a space before the ':'. -} - -class Foo { - name:string; - ~ Expected a space after the ':'. - ~ Expected a space before the ':'. -} - -class Foo { - name :string; - ~ Expected a space after the ':'. -} - -type Foo = ()=>{}; - ~~ Expected a space after the '=>'. - ~~ Expected a space before the '=>'. -type Foo = () =>{}; - ~~ Expected a space after the '=>'. -type Foo = ()=> {}; - ~~ Expected a space before the '=>'. -" -`; - -exports[`Validating rule docs type-annotation-spacing.mdx code examples ESLint output 6`] = ` -"Correct -Options: { "before": true, "after": true } - -let foo : string = "bar"; - -function foo() : string {} - -class Foo { - name : string; -} - -type Foo = () => {}; -" -`; - -exports[`Validating rule docs type-annotation-spacing.mdx code examples ESLint output 7`] = ` -"Incorrect -Options: {"before":false,"after":false,"overrides":{"colon":{"before":true,"after":true}}} - -let foo: string = "bar"; - ~ Expected a space before the ':'. -let foo:string = "bar"; - ~ Expected a space after the ':'. - ~ Expected a space before the ':'. -let foo :string = "bar"; - ~ Expected a space after the ':'. - -function foo(): string {} - ~ Expected a space before the ':'. -function foo():string {} - ~ Expected a space after the ':'. - ~ Expected a space before the ':'. -function foo() :string {} - ~ Expected a space after the ':'. - -class Foo { - name: string; - ~ Expected a space before the ':'. -} - -class Foo { - name:string; - ~ Expected a space after the ':'. - ~ Expected a space before the ':'. -} - -class Foo { - name :string; - ~ Expected a space after the ':'. -} - -type Foo = () =>{}; - ~~ Unexpected space before the '=>'. -type Foo = ()=> {}; - ~~ Unexpected space after the '=>'. -type Foo = () => {}; - ~~ Unexpected space after the '=>'. - ~~ Unexpected space before the '=>'. -" -`; - -exports[`Validating rule docs type-annotation-spacing.mdx code examples ESLint output 8`] = ` -"Correct -Options: {"before":false,"after":false,"overrides":{"colon":{"before":true,"after":true}}} - -let foo : string = "bar"; - -function foo() : string {} - -class Foo { - name : string; -} - -type Foo = { - name : (name : string)=>string; -} - -type Foo = ()=>{}; -" -`; - -exports[`Validating rule docs type-annotation-spacing.mdx code examples ESLint output 9`] = ` -"Incorrect -Options: {"before":false,"after":false,"overrides":{"arrow":{"before":true,"after":true}}} - -let foo: string = "bar"; - ~ Unexpected space after the ':'. -let foo : string = "bar"; - ~ Unexpected space after the ':'. - ~ Unexpected space before the ':'. -let foo :string = "bar"; - ~ Unexpected space before the ':'. - -function foo(): string {} - ~ Unexpected space after the ':'. -function foo():string {} -function foo() :string {} - ~ Unexpected space before the ':'. - -class Foo { - name: string; - ~ Unexpected space after the ':'. -} - -class Foo { - name : string; - ~ Unexpected space after the ':'. - ~ Unexpected space before the ':'. -} - -class Foo { - name :string; - ~ Unexpected space before the ':'. -} - -type Foo = ()=>{}; - ~~ Expected a space after the '=>'. - ~~ Expected a space before the '=>'. -type Foo = () =>{}; - ~~ Expected a space after the '=>'. -type Foo = ()=> {}; - ~~ Expected a space before the '=>'. -" -`; - -exports[`Validating rule docs type-annotation-spacing.mdx code examples ESLint output 10`] = ` -"Correct -Options: {"before":false,"after":false,"overrides":{"arrow":{"before":true,"after":true}}} - -let foo:string = "bar"; - -function foo():string {} - -class Foo { - name:string; -} - -type Foo = () => {}; -" -`; diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index 049c0d168e4f..3c3842793ad0 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -125,14 +125,39 @@ describe('Validating rule docs', () => { unistUtilVisit = await dynamicImport('unist-util-visit'); }); + const oldStylisticRules = [ + 'block-spacing.md', + 'brace-style.md', + 'camelcase.md', + 'comma-dangle.md', + 'comma-spacing.md', + 'func-call-spacing.md', + 'indent.md', + 'key-spacing.md', + 'keyword-spacing.md', + 'lines-around-comment.md', + 'lines-between-class-members.md', + 'member-delimiter-style.md', + 'no-extra-parens.md', + 'no-extra-semi.md', + 'object-curly-spacing.md', + 'padding-line-between-statements.md', + 'quotes.md', + 'semi.md', + 'space-before-blocks.md', + 'space-before-function-paren.md', + 'space-infix-ops.md', + 'type-annotation-spacing.md', + ]; + const ignoredFiles = new Set([ 'README.md', 'TEMPLATE.md', // These rule docs were left behind on purpose for legacy reasons. See the // comments in the files for more information. - 'camelcase.md', 'no-duplicate-imports.mdx', 'no-parameter-properties.mdx', + ...oldStylisticRules, ]); const rulesWithComplexOptions = new Set(['array-type', 'member-ordering']); @@ -235,8 +260,7 @@ describe('Validating rule docs', () => { if ( !rulesWithComplexOptions.has(ruleName) && Array.isArray(schema) && - !rule.meta.docs?.extendsBaseRule && - rule.meta.type !== 'layout' + !rule.meta.docs?.extendsBaseRule ) { test('each rule option should be mentioned in a heading', () => { const headingTextAfterOptions = headings diff --git a/packages/eslint-plugin/tests/fixtures/indent/indent-invalid-fixture-1.js b/packages/eslint-plugin/tests/fixtures/indent/indent-invalid-fixture-1.js deleted file mode 100644 index f03507ff61ea..000000000000 --- a/packages/eslint-plugin/tests/fixtures/indent/indent-invalid-fixture-1.js +++ /dev/null @@ -1,530 +0,0 @@ -if (a) { - var b = c; - var d = e - * f; - var e = f; // <- -// -> - function g() { - if (h) { - var i = j; - } // <- - } // <- - - while (k) l++; - while (m) { - n--; // -> - } // <- - - do { - o = p + - q; // NO ERROR: DON'T VALIDATE MULTILINE STATEMENTS - o = p + - q; - } while(r); // <- - - for (var s in t) { - u++; - } - - for (;;) { - v++; // <- - } - - if ( w ) { - x++; - } else if (y) { - z++; // <- - aa++; - } else { // <- - bb++; // -> -} // -> -} - -/**/var b; // NO ERROR: single line multi-line comments followed by code is OK -/* - * - */ var b; // NO ERROR: multi-line comments followed by code is OK - -var arr = [ - a, - b, - c, - function (){ - d - }, // <- - {}, - { - a: b, - c: d, - d: e - }, - [ - f, - g, - h, - i - ], - [j] -]; - -var obj = { - a: { - b: { - c: d, - e: f, - g: h + - i // NO ERROR: DON'T VALIDATE MULTILINE STATEMENTS - } - }, - g: [ - h, - i, - j, - k - ] -}; - -var arrObject = {a:[ - a, - b, // NO ERROR: INDENT ONCE WHEN MULTIPLE INDENTED EXPRESSIONS ARE ON SAME LINE - c -]}; - -var objArray = [{ - a: b, - b: c, // NO ERROR: INDENT ONCE WHEN MULTIPLE INDENTED EXPRESSIONS ARE ON SAME LINE - c: d -}]; - -var arrArray = [[ - a, - b, // NO ERROR: INDENT ONCE WHEN MULTIPLE INDENTED EXPRESSIONS ARE ON SAME LINE - c -]]; - -var objObject = {a:{ - a: b, - b: c, // NO ERROR: INDENT ONCE WHEN MULTIPLE INDENTED EXPRESSIONS ARE ON SAME LINE - c: d -}}; - - -switch (a) { - case 'a': - var a = 'b'; // -> - break; - case 'b': - var a = 'b'; - break; - case 'c': - var a = 'b'; // <- - break; - case 'd': - var a = 'b'; - break; // -> - case 'f': - var a = 'b'; - break; - case 'g': { - var a = 'b'; - break; - } - case 'z': - default: - break; // <- -} - -a.b('hi') - .c(a.b()) // <- - .d(); // <- - -if ( a ) { - if ( b ) { -d.e(f) // -> - .g() // -> - .h(); // -> - - i.j(m) - .k() // NO ERROR: DON'T VALIDATE MULTILINE STATEMENTS - .l(); // NO ERROR: DON'T VALIDATE MULTILINE STATEMENTS - - n.o(p) // <- - .q() // <- - .r(); // <- - } -} - -var a = b, - c = function () { - h = i; // -> - j = k; - l = m; // <- - }, - e = { - f: g, - n: o, - p: q - }, - r = [ - s, - t, - u - ]; - -var a = function () { -b = c; // -> - d = e; - f = g; // <- -}; - -function c(a, b) { - if (a || (a && - b)) { // NO ERROR: DON'T VALIDATE MULTILINE STATEMENTS - return d; - } -} - -if ( a - || b ) { -var x; // -> - var c, - d = function(a, - b) { // <- - a; // -> - b; - c; // <- - } -} - - -a({ - d: 1 -}); - -a( -1 -); - -a( - b({ - d: 1 - }) -); - -a( - b( - c({ - d: 1, - e: 1, - f: 1 - }) - ) -); - -a({ d: 1 }); - -aa( - b({ // NO ERROR: CallExpression args not linted by default - c: d, // -> - e: f, - f: g - }) // -> -); - -aaaaaa( - b, - c, - { - d: a - } -); - -a(b, c, - d, e, - f, g // NO ERROR: alignment of arguments of callExpression not checked - ); // <- - -a( - ); // <- - -aaaaaa( - b, - c, { - d: a - }, { - e: f - } -); - -a.b() - .c(function(){ - var a; - }).d.e; - -if (a == 'b') { - if (c && d) e = f - else g('h').i('j') -} - -a = function (b, c) { - return a(function () { - var d = e - var f = g - var h = i - - if (!j) k('l', (m = n)) - if (o) p - else if (q) r - }) -} - -var a = function() { - "b" - .replace(/a/, "a") - .replace(/bc?/, function(e) { - return "b" + (e.f === 2 ? "c" : "f"); - }) - .replace(/d/, "d"); -}; - -$(b) - .on('a', 'b', function() { $(c).e('f'); }) - .on('g', 'h', function() { $(i).j('k'); }); - -a - .b('c', - 'd'); // NO ERROR: CallExpression args not linted by default - -a - .b('c', [ 'd', function(e) { - e++; - }]); - -var a = function() { - a++; - b++; // <- - c++; // <- - }, - b; - -var b = [ - a, - b, - c - ], - c; - -var c = { - a: 1, - b: 2, - c: 3 - }, - d; - -// holes in arrays indentation -x = [ - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 -]; - -try { - a++; - b++; // <- -c++; // -> -} catch (d) { - e++; - f++; // <- -g++; // -> -} finally { - h++; - i++; // <- -j++; // -> -} - -if (array.some(function(){ - return true; -})) { -a++; // -> - b++; - c++; // <- -} - -var a = b.c(function() { - d++; - }), - e; - -switch (true) { - case (a - && b): -case (c // -> -&& d): - case (e // <- - && f): - case (g -&& h): - var i = j; // <- - var k = l; - var m = n; // -> -} - -if (a) { - b(); -} -else { -c(); // -> - d(); - e(); // <- -} - -if (a) b(); -else { -c(); // -> - d(); - e(); // <- -} - -if (a) { - b(); -} else c(); - -if (a) { - b(); -} -else c(); - -a(); - -if( "very very long multi line" + - "with weird indentation" ) { - b(); -a(); // -> - c(); // <- -} - -a( "very very long multi line" + - "with weird indentation", function() { - b(); -a(); // -> - c(); // <- - }); // <- - -a = function(content, dom) { - b(); - c(); // <- -d(); // -> -}; - -a = function(content, dom) { - b(); - c(); // <- - d(); // -> - }; - -a = function(content, dom) { - b(); // -> - }; - -a = function(content, dom) { -b(); // -> - }; - -a('This is a terribly long description youll ' + - 'have to read', function () { - b(); // <- - c(); // <- - }); // <- - -if ( - array.some(function(){ - return true; - }) -) { -a++; // -> - b++; - c++; // <- -} - -function c(d) { - return { - e: function(f, g) { - } - }; -} - -function a(b) { - switch(x) { - case 1: - if (foo) { - return 5; - } - } -} - -function a(b) { - switch(x) { - case 1: - c; - } -} - -function a(b) { - switch(x) { - case 1: c; - } -} - -function test() { - var a = 1; - { - a(); - } -} - -{ - a(); -} - -function a(b) { - switch(x) { - case 1: - { // <- - a(); // -> - } - break; - default: - { - b(); - } - } -} - -switch (a) { - default: - if (b) - c(); -} - -function test(x) { - switch (x) { - case 1: - return function() { - var a = 5; - return a; - }; - } -} - -switch (a) { - default: - if (b) - c(); -} diff --git a/packages/eslint-plugin/tests/fixtures/indent/indent-valid-fixture-1.js b/packages/eslint-plugin/tests/fixtures/indent/indent-valid-fixture-1.js deleted file mode 100644 index 5c298429f69d..000000000000 --- a/packages/eslint-plugin/tests/fixtures/indent/indent-valid-fixture-1.js +++ /dev/null @@ -1,530 +0,0 @@ -if (a) { - var b = c; - var d = e - * f; - var e = f; // <- - // -> - function g() { - if (h) { - var i = j; - } // <- - } // <- - - while (k) l++; - while (m) { - n--; // -> - } // <- - - do { - o = p + - q; // NO ERROR: DON'T VALIDATE MULTILINE STATEMENTS - o = p + - q; - } while(r); // <- - - for (var s in t) { - u++; - } - - for (;;) { - v++; // <- - } - - if ( w ) { - x++; - } else if (y) { - z++; // <- - aa++; - } else { // <- - bb++; // -> - } // -> -} - -/**/var b; // NO ERROR: single line multi-line comments followed by code is OK -/* - * - */ var b; // NO ERROR: multi-line comments followed by code is OK - -var arr = [ - a, - b, - c, - function (){ - d - }, // <- - {}, - { - a: b, - c: d, - d: e - }, - [ - f, - g, - h, - i - ], - [j] -]; - -var obj = { - a: { - b: { - c: d, - e: f, - g: h + - i // NO ERROR: DON'T VALIDATE MULTILINE STATEMENTS - } - }, - g: [ - h, - i, - j, - k - ] -}; - -var arrObject = {a:[ - a, - b, // NO ERROR: INDENT ONCE WHEN MULTIPLE INDENTED EXPRESSIONS ARE ON SAME LINE - c -]}; - -var objArray = [{ - a: b, - b: c, // NO ERROR: INDENT ONCE WHEN MULTIPLE INDENTED EXPRESSIONS ARE ON SAME LINE - c: d -}]; - -var arrArray = [[ - a, - b, // NO ERROR: INDENT ONCE WHEN MULTIPLE INDENTED EXPRESSIONS ARE ON SAME LINE - c -]]; - -var objObject = {a:{ - a: b, - b: c, // NO ERROR: INDENT ONCE WHEN MULTIPLE INDENTED EXPRESSIONS ARE ON SAME LINE - c: d -}}; - - -switch (a) { - case 'a': - var a = 'b'; // -> - break; - case 'b': - var a = 'b'; - break; - case 'c': - var a = 'b'; // <- - break; - case 'd': - var a = 'b'; - break; // -> - case 'f': - var a = 'b'; - break; - case 'g': { - var a = 'b'; - break; - } - case 'z': - default: - break; // <- -} - -a.b('hi') - .c(a.b()) // <- - .d(); // <- - -if ( a ) { - if ( b ) { - d.e(f) // -> - .g() // -> - .h(); // -> - - i.j(m) - .k() // NO ERROR: DON'T VALIDATE MULTILINE STATEMENTS - .l(); // NO ERROR: DON'T VALIDATE MULTILINE STATEMENTS - - n.o(p) // <- - .q() // <- - .r(); // <- - } -} - -var a = b, - c = function () { - h = i; // -> - j = k; - l = m; // <- - }, - e = { - f: g, - n: o, - p: q - }, - r = [ - s, - t, - u - ]; - -var a = function () { - b = c; // -> - d = e; - f = g; // <- -}; - -function c(a, b) { - if (a || (a && - b)) { // NO ERROR: DON'T VALIDATE MULTILINE STATEMENTS - return d; - } -} - -if ( a - || b ) { - var x; // -> - var c, - d = function(a, - b) { // <- - a; // -> - b; - c; // <- - } -} - - -a({ - d: 1 -}); - -a( -1 -); - -a( - b({ - d: 1 - }) -); - -a( - b( - c({ - d: 1, - e: 1, - f: 1 - }) - ) -); - -a({ d: 1 }); - -aa( - b({ // NO ERROR: CallExpression args not linted by default - c: d, // -> - e: f, - f: g - }) // -> -); - -aaaaaa( - b, - c, - { - d: a - } -); - -a(b, c, - d, e, - f, g // NO ERROR: alignment of arguments of callExpression not checked -); // <- - -a( -); // <- - -aaaaaa( - b, - c, { - d: a - }, { - e: f - } -); - -a.b() - .c(function(){ - var a; - }).d.e; - -if (a == 'b') { - if (c && d) e = f - else g('h').i('j') -} - -a = function (b, c) { - return a(function () { - var d = e - var f = g - var h = i - - if (!j) k('l', (m = n)) - if (o) p - else if (q) r - }) -} - -var a = function() { - "b" - .replace(/a/, "a") - .replace(/bc?/, function(e) { - return "b" + (e.f === 2 ? "c" : "f"); - }) - .replace(/d/, "d"); -}; - -$(b) - .on('a', 'b', function() { $(c).e('f'); }) - .on('g', 'h', function() { $(i).j('k'); }); - -a - .b('c', - 'd'); // NO ERROR: CallExpression args not linted by default - -a - .b('c', [ 'd', function(e) { - e++; - }]); - -var a = function() { - a++; - b++; // <- - c++; // <- - }, - b; - -var b = [ - a, - b, - c - ], - c; - -var c = { - a: 1, - b: 2, - c: 3 - }, - d; - -// holes in arrays indentation -x = [ - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 -]; - -try { - a++; - b++; // <- - c++; // -> -} catch (d) { - e++; - f++; // <- - g++; // -> -} finally { - h++; - i++; // <- - j++; // -> -} - -if (array.some(function(){ - return true; -})) { - a++; // -> - b++; - c++; // <- -} - -var a = b.c(function() { - d++; - }), - e; - -switch (true) { - case (a - && b): - case (c // -> -&& d): - case (e // <- - && f): - case (g -&& h): - var i = j; // <- - var k = l; - var m = n; // -> -} - -if (a) { - b(); -} -else { - c(); // -> - d(); - e(); // <- -} - -if (a) b(); -else { - c(); // -> - d(); - e(); // <- -} - -if (a) { - b(); -} else c(); - -if (a) { - b(); -} -else c(); - -a(); - -if( "very very long multi line" + - "with weird indentation" ) { - b(); - a(); // -> - c(); // <- -} - -a( "very very long multi line" + - "with weird indentation", function() { - b(); - a(); // -> - c(); // <- -}); // <- - -a = function(content, dom) { - b(); - c(); // <- - d(); // -> -}; - -a = function(content, dom) { - b(); - c(); // <- - d(); // -> -}; - -a = function(content, dom) { - b(); // -> -}; - -a = function(content, dom) { - b(); // -> -}; - -a('This is a terribly long description youll ' + - 'have to read', function () { - b(); // <- - c(); // <- -}); // <- - -if ( - array.some(function(){ - return true; - }) -) { - a++; // -> - b++; - c++; // <- -} - -function c(d) { - return { - e: function(f, g) { - } - }; -} - -function a(b) { - switch(x) { - case 1: - if (foo) { - return 5; - } - } -} - -function a(b) { - switch(x) { - case 1: - c; - } -} - -function a(b) { - switch(x) { - case 1: c; - } -} - -function test() { - var a = 1; - { - a(); - } -} - -{ - a(); -} - -function a(b) { - switch(x) { - case 1: - { // <- - a(); // -> - } - break; - default: - { - b(); - } - } -} - -switch (a) { - default: - if (b) - c(); -} - -function test(x) { - switch (x) { - case 1: - return function() { - var a = 5; - return a; - }; - } -} - -switch (a) { - default: - if (b) - c(); -} diff --git a/packages/eslint-plugin/tests/rules/block-spacing.test.ts b/packages/eslint-plugin/tests/rules/block-spacing.test.ts deleted file mode 100644 index dabb15e692f3..000000000000 --- a/packages/eslint-plugin/tests/rules/block-spacing.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -import type { - InvalidTestCase, - ValidTestCase, -} from '@typescript-eslint/rule-tester'; -import { RuleTester } from '@typescript-eslint/rule-tester'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; - -import rule from '../../src/rules/block-spacing'; - -const ruleTester = new RuleTester({ - parser: '@typescript-eslint/parser', -}); - -type InvalidBlockSpacingTestCase = InvalidTestCase< - 'extra' | 'missing', - ['always' | 'never'] ->; - -const options = ['always', 'never'] as const; -const typeDeclarations = [ - { - nodeType: AST_NODE_TYPES.TSInterfaceBody, - stringPrefix: 'interface Foo ', - }, - { - nodeType: AST_NODE_TYPES.TSTypeLiteral, - stringPrefix: 'type Foo = ', - }, - { - nodeType: AST_NODE_TYPES.TSEnumDeclaration, - stringPrefix: 'enum Foo ', - }, - { - nodeType: AST_NODE_TYPES.TSEnumDeclaration, - stringPrefix: 'const enum Foo ', - }, -]; -const emptyBlocks = ['{}', '{ }']; -const singlePropertyBlocks = ['{bar: true}', '{ bar: true }']; -const blockComment = '/* comment */'; - -ruleTester.run('block-spacing', rule, { - valid: [ - // Empty blocks don't apply - ...options.flatMap(option => - typeDeclarations.flatMap(typeDec => - emptyBlocks.map>(blockType => ({ - code: typeDec.stringPrefix + blockType, - options: [option], - })), - ), - ), - ...typeDeclarations.flatMap>( - typeDec => { - const property = - typeDec.nodeType === AST_NODE_TYPES.TSEnumDeclaration - ? 'bar = 1' - : 'bar: true;'; - return [ - { - code: `${typeDec.stringPrefix}{ /* comment */ ${property} /* comment */ } // always`, - options: ['always'], - }, - { - code: `${typeDec.stringPrefix}{/* comment */ ${property} /* comment */} // never`, - options: ['never'], - }, - { - code: `${typeDec.stringPrefix}{ //comment\n ${property}}`, - options: ['never'], - }, - ]; - }, - ), - ], - invalid: [ - ...options.flatMap(option => - typeDeclarations.flatMap(typeDec => { - return singlePropertyBlocks.flatMap( - (blockType, blockIndex) => { - // These are actually valid, so filter them out - if ( - (option === 'always' && blockType.startsWith('{ ')) || - (option === 'never' && blockType.startsWith('{bar')) - ) { - return []; - } - const reverseBlockType = singlePropertyBlocks[1 - blockIndex]; - let code = `${typeDec.stringPrefix}${blockType}; /* ${option} */`; - let output = `${typeDec.stringPrefix}${reverseBlockType}; /* ${option} */`; - if (typeDec.nodeType === AST_NODE_TYPES.TSEnumDeclaration) { - output = output.replace(':', '='); - code = code.replace(':', '='); - } - - return { - code, - options: [option], - output, - errors: [ - { - type: typeDec.nodeType, - messageId: option === 'always' ? 'missing' : 'extra', - data: { location: 'after', token: '{' }, - }, - { - type: typeDec.nodeType, - messageId: option === 'always' ? 'missing' : 'extra', - data: { location: 'before', token: '}' }, - }, - ], - }; - }, - ); - }), - ), - // With block comments - ...options.flatMap(option => - typeDeclarations.flatMap(typeDec => { - const property = - typeDec.nodeType === AST_NODE_TYPES.TSEnumDeclaration - ? 'bar = 1' - : 'bar: true;'; - const alwaysSpace = option === 'always' ? '' : ' '; - const neverSpace = option === 'always' ? ' ' : ''; - return [ - { - code: `${typeDec.stringPrefix}{${alwaysSpace}${blockComment}${property}${blockComment}${alwaysSpace}} /* ${option} */`, - output: `${typeDec.stringPrefix}{${neverSpace}${blockComment}${property}${blockComment}${neverSpace}} /* ${option} */`, - options: [option], - errors: [ - { - type: typeDec.nodeType, - messageId: option === 'always' ? 'missing' : 'extra', - data: { location: 'after', token: '{' }, - }, - { - type: typeDec.nodeType, - messageId: option === 'always' ? 'missing' : 'extra', - data: { location: 'before', token: '}' }, - }, - ], - }, - ]; - }), - ), - ], -}); diff --git a/packages/eslint-plugin/tests/rules/brace-style.test.ts b/packages/eslint-plugin/tests/rules/brace-style.test.ts deleted file mode 100644 index ec1ac6b3bc5f..000000000000 --- a/packages/eslint-plugin/tests/rules/brace-style.test.ts +++ /dev/null @@ -1,1188 +0,0 @@ -/* eslint-disable eslint-comments/no-use */ -// this rule tests the position of braces, which prettier will want to fix and break the tests -/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ -/* eslint-enable eslint-comments/no-use */ - -import { RuleTester } from '@typescript-eslint/rule-tester'; - -import rule from '../../src/rules/brace-style'; - -const ruleTester = new RuleTester({ - parser: '@typescript-eslint/parser', - parserOptions: { - ecmaVersion: 6, - sourceType: 'module', - ecmaFeatures: {}, - }, -}); - -ruleTester.run('brace-style', rule, { - valid: [ - { - code: ` -function f() { - if (true) - return { x: 1 }; - else { - var y = 2; - return y; - } -} - `, - }, - { - code: ` -if (tag === 1) glyph.id = pbf.readVarint(); -else if (tag === 2) glyph.bitmap = pbf.readBytes(); - `, - }, - { - code: ` -function foo () { - return; -} - `, - }, - { - code: ` -function a(b, -c, -d) { } - `, - }, - { - code: ` -!function foo () { - return; -} - `, - }, - { - code: ` -!function a(b, -c, -d) { } - `, - }, - { - code: ` -if (foo) { - bar(); -} - `, - }, - { - code: ` -if (a) { - b(); -} else { - c(); -} - `, - }, - { - code: ` -while (foo) { - bar(); -} - `, - }, - { - code: ` -for (;;) { - bar(); -} - `, - }, - { - code: ` -with (foo) { - bar(); -} - `, - }, - { - code: ` -switch (foo) { - case 'bar': break; -} - `, - }, - { - code: ` -try { - bar(); -} catch (e) { - baz(); -} - `, - }, - { - code: ` -do { - bar(); -} while (true) - `, - }, - { - code: ` -for (foo in bar) { - baz(); -} - `, - }, - { - code: ` -if (a && - b && - c) { - } - `, - }, - { - code: ` -switch(0) { -} - `, - }, - { - code: ` -class Foo { -} - `, - }, - { - code: ` -(class { -}) - `, - }, - { - code: ` -class -Foo { -} - `, - }, - { - code: ` -class Foo { - bar() { - } -} - `, - }, - { - code: ` -if (foo) { -} -else { -} - `, - options: ['stroustrup'], - }, - { - code: ` -if (foo) -{ -} -else -{ -} - `, - options: ['allman'], - }, - { - code: ` -try { - bar(); -} -catch (e) { - baz(); -} - `, - options: ['stroustrup'], - }, - { - code: ` -try -{ - bar(); -} -catch (e) -{ - baz(); -} - `, - options: ['allman'], - }, - { - code: 'function foo () { return; }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'function foo () { a(); b(); return; }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'function a(b,c,d) { }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: '!function foo () { return; }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: '!function a(b,c,d) { }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'if (foo) { bar(); }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'if (a) { b(); } else { c(); }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'while (foo) { bar(); }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'for (;;) { bar(); }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'with (foo) { bar(); }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: "switch (foo) { case 'bar': break; }", - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'try { bar(); } catch (e) { baz(); }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'do { bar(); } while (true)', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'for (foo in bar) { baz(); }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'if (a && b && c) { }', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'switch(0) {}', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: ` -if (foo) {} -else {} - `, - options: ['stroustrup', { allowSingleLine: true }], - }, - { - code: ` -try { bar(); } -catch (e) { baz(); } - `, - options: ['stroustrup', { allowSingleLine: true }], - }, - { - code: 'var foo = () => { return; }', - options: ['stroustrup', { allowSingleLine: true }], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: ` -if (foo) {} -else {} - `, - options: ['allman', { allowSingleLine: true }], - }, - { - code: ` -try { bar(); } -catch (e) { baz(); } - `, - options: ['allman', { allowSingleLine: true }], - }, - { - code: 'var foo = () => { return; }', - options: ['allman', { allowSingleLine: true }], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: ` -if (tag === 1) fontstack.name = pbf.readString(); -else if (tag === 2) fontstack.range = pbf.readString(); -else if (tag === 3) { - var glyph = pbf.readMessage(readGlyph, {}); - fontstack.glyphs[glyph.id] = glyph; -} - `, - options: ['1tbs'], - }, - { - code: ` -if (tag === 1) fontstack.name = pbf.readString(); -else if (tag === 2) fontstack.range = pbf.readString(); -else if (tag === 3) { - var glyph = pbf.readMessage(readGlyph, {}); - fontstack.glyphs[glyph.id] = glyph; -} - `, - options: ['stroustrup'], - }, - { - code: ` -switch(x) -{ - case 1: - bar(); -} - `, - options: ['allman'], - }, - { - code: 'switch(x) {}', - options: ['allman', { allowSingleLine: true }], - }, - { - code: ` -class Foo { -} - `, - options: ['stroustrup'], - }, - { - code: ` -(class { -}) - `, - options: ['stroustrup'], - }, - { - code: ` -class Foo -{ -} - `, - options: ['allman'], - }, - { - code: ` -(class -{ -}) - `, - options: ['allman'], - }, - { - code: ` -class -Foo -{ -} - `, - options: ['allman'], - }, - { - code: 'class Foo {}', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: 'class Foo {}', - options: ['allman', { allowSingleLine: true }], - }, - { - code: '(class {})', - options: ['1tbs', { allowSingleLine: true }], - }, - { - code: '(class {})', - options: ['allman', { allowSingleLine: true }], - }, - - // https://github.com/eslint/eslint/issues/7908 - { - code: '{}', - }, - { - code: ` -if (foo) { -} -{ -} - `, - }, - { - code: ` -switch (foo) { - case bar: - baz(); - { - qux(); - } -} - `, - }, - { - code: ` -{ -} - `, - }, - { - code: ` -{ - { - } -} - `, - }, - - // https://github.com/eslint/eslint/issues/7974 - { - code: ` -class Ball { - throw() {} - catch() {} -} - `, - }, - { - code: ` -({ - and() {}, - finally() {} -}) - `, - }, - { - code: ` -(class { - or() {} - else() {} -}) - `, - }, - { - code: ` -if (foo) bar = function() {} -else baz() - `, - }, - { - code: ` -interface Foo { -} - `, - options: ['1tbs'], - }, - { - code: ` -interface Foo { -} - `, - options: ['stroustrup'], - }, - { - code: ` -interface Foo -{ -} - `, - options: ['allman'], - }, - { - code: ` -module "Foo" { -} - `, - options: ['1tbs'], - }, - { - code: ` -module "Foo" { -} - `, - options: ['stroustrup'], - }, - { - code: ` -module "Foo" -{ -} - `, - options: ['allman'], - }, - { - code: ` -namespace Foo { -} - `, - options: ['1tbs'], - }, - { - code: ` -namespace Foo { -} - `, - options: ['stroustrup'], - }, - { - code: ` -namespace Foo -{ -} - `, - options: ['allman'], - }, - { - code: ` -enum Foo -{ - A, - B -} - `, - options: ['allman'], - }, - { - code: ` -enum Foo { - A, - B -} - `, - options: ['1tbs'], - }, - { - code: ` -enum Foo { - A, - B -} - `, - options: ['stroustrup'], - }, - { - code: 'enum Foo { A, B }', - options: ['1tbs', { allowSingleLine: true }], - }, - ], - - invalid: [ - { - code: ` -if (f) { - bar; -} -else - baz; - `, - output: ` -if (f) { - bar; -} else - baz; - `, - errors: [{ messageId: 'nextLineClose' }], - }, - { - code: 'var foo = () => { return; }', - output: 'var foo = () => {\n return; \n}', - parserOptions: { ecmaVersion: 6 }, - errors: [ - { messageId: 'blockSameLine' }, - { messageId: 'singleLineClose' }, - ], - }, - { - code: 'function foo() { return; }', - output: 'function foo() {\n return; \n}', - errors: [ - { messageId: 'blockSameLine' }, - { messageId: 'singleLineClose' }, - ], - }, - { - code: 'function foo() \n { \n return; }', - output: 'function foo() { \n return; \n}', - errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], - }, - { - code: '!function foo() \n { \n return; }', - output: '!function foo() { \n return; \n}', - errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], - }, - { - code: 'if (foo) \n { \n bar(); }', - output: 'if (foo) { \n bar(); \n}', - errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], - }, - { - code: 'if (a) { \nb();\n } else \n { c(); }', - output: 'if (a) { \nb();\n } else {\n c(); \n}', - errors: [ - { messageId: 'nextLineOpen' }, - { messageId: 'blockSameLine' }, - { messageId: 'singleLineClose' }, - ], - }, - { - code: 'while (foo) \n { \n bar(); }', - output: 'while (foo) { \n bar(); \n}', - errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], - }, - { - code: 'for (;;) \n { \n bar(); }', - output: 'for (;;) { \n bar(); \n}', - errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], - }, - { - code: 'with (foo) \n { \n bar(); }', - output: 'with (foo) { \n bar(); \n}', - errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], - }, - { - code: "switch (foo) \n { \n case 'bar': break; }", - output: "switch (foo) { \n case 'bar': break; \n}", - errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], - }, - { - code: 'switch (foo) \n { }', - output: 'switch (foo) { }', - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'try \n { \n bar(); \n } catch (e) {}', - output: 'try { \n bar(); \n } catch (e) {}', - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'try { \n bar(); \n } catch (e) \n {}', - output: 'try { \n bar(); \n } catch (e) {}', - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'do \n { \n bar(); \n} while (true)', - output: 'do { \n bar(); \n} while (true)', - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'for (foo in bar) \n { \n baz(); \n }', - output: 'for (foo in bar) { \n baz(); \n }', - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'for (foo of bar) \n { \n baz(); \n }', - output: 'for (foo of bar) { \n baz(); \n }', - parserOptions: { ecmaVersion: 6 }, - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'try { \n bar(); \n }\ncatch (e) {\n}', - output: 'try { \n bar(); \n } catch (e) {\n}', - errors: [{ messageId: 'nextLineClose' }], - }, - { - code: 'try { \n bar(); \n } catch (e) {\n}\n finally {\n}', - output: 'try { \n bar(); \n } catch (e) {\n} finally {\n}', - errors: [{ messageId: 'nextLineClose' }], - }, - { - code: 'if (a) { \nb();\n } \n else { \nc();\n }', - output: 'if (a) { \nb();\n } else { \nc();\n }', - errors: [{ messageId: 'nextLineClose' }], - }, - { - code: 'try { \n bar(); \n }\ncatch (e) {\n} finally {\n}', - output: 'try { \n bar(); \n }\ncatch (e) {\n}\n finally {\n}', - options: ['stroustrup'], - errors: [{ messageId: 'sameLineClose' }], - }, - { - code: 'try { \n bar(); \n } catch (e) {\n}\n finally {\n}', - output: 'try { \n bar(); \n }\n catch (e) {\n}\n finally {\n}', - options: ['stroustrup'], - errors: [{ messageId: 'sameLineClose' }], - }, - { - code: 'if (a) { \nb();\n } else { \nc();\n }', - output: 'if (a) { \nb();\n }\n else { \nc();\n }', - options: ['stroustrup'], - errors: [{ messageId: 'sameLineClose' }], - }, - { - code: 'if (foo) {\nbaz();\n} else if (bar) {\nbaz();\n}\nelse {\nqux();\n}', - output: - 'if (foo) {\nbaz();\n}\n else if (bar) {\nbaz();\n}\nelse {\nqux();\n}', - options: ['stroustrup'], - errors: [{ messageId: 'sameLineClose' }], - }, - { - code: 'if (foo) {\npoop();\n} \nelse if (bar) {\nbaz();\n} else if (thing) {\nboom();\n}\nelse {\nqux();\n}', - output: - 'if (foo) {\npoop();\n} \nelse if (bar) {\nbaz();\n}\n else if (thing) {\nboom();\n}\nelse {\nqux();\n}', - options: ['stroustrup'], - errors: [{ messageId: 'sameLineClose' }], - }, - { - code: 'try { \n bar(); \n }\n catch (e) {\n}\n finally {\n}', - output: 'try \n{ \n bar(); \n }\n catch (e) \n{\n}\n finally \n{\n}', - options: ['allman'], - errors: [ - { messageId: 'sameLineOpen', line: 1 }, - { messageId: 'sameLineOpen', line: 4 }, - { messageId: 'sameLineOpen', line: 6 }, - ], - }, - { - code: 'switch(x) { case 1: \nbar(); }\n ', - output: 'switch(x) \n{\n case 1: \nbar(); \n}\n ', - options: ['allman'], - errors: [ - { messageId: 'sameLineOpen', line: 1 }, - { messageId: 'blockSameLine', line: 1 }, - { messageId: 'singleLineClose', line: 2 }, - ], - }, - { - code: 'if (a) { \nb();\n } else { \nc();\n }', - output: 'if (a) \n{ \nb();\n }\n else \n{ \nc();\n }', - options: ['allman'], - errors: [ - { messageId: 'sameLineOpen' }, - { messageId: 'sameLineClose' }, - { messageId: 'sameLineOpen' }, - ], - }, - { - code: 'if (foo) {\nbaz();\n} else if (bar) {\nbaz();\n}\nelse {\nqux();\n}', - output: - 'if (foo) \n{\nbaz();\n}\n else if (bar) \n{\nbaz();\n}\nelse \n{\nqux();\n}', - options: ['allman'], - errors: [ - { messageId: 'sameLineOpen' }, - { messageId: 'sameLineClose' }, - { messageId: 'sameLineOpen' }, - { messageId: 'sameLineOpen' }, - ], - }, - { - code: 'if (foo)\n{ poop();\n} \nelse if (bar) {\nbaz();\n} else if (thing) {\nboom();\n}\nelse {\nqux();\n}', - output: - 'if (foo)\n{\n poop();\n} \nelse if (bar) \n{\nbaz();\n}\n else if (thing) \n{\nboom();\n}\nelse \n{\nqux();\n}', - options: ['allman'], - errors: [ - { messageId: 'blockSameLine' }, - { messageId: 'sameLineOpen' }, - { messageId: 'sameLineClose' }, - { messageId: 'sameLineOpen' }, - { messageId: 'sameLineOpen' }, - ], - }, - { - code: 'if (foo)\n{\n bar(); }', - output: 'if (foo)\n{\n bar(); \n}', - options: ['allman'], - errors: [{ messageId: 'singleLineClose' }], - }, - { - code: 'try\n{\n somethingRisky();\n} catch (e)\n{\n handleError()\n}', - output: - 'try\n{\n somethingRisky();\n}\n catch (e)\n{\n handleError()\n}', - options: ['allman'], - errors: [{ messageId: 'sameLineClose' }], - }, - // allowSingleLine: true - { - code: 'function foo() { return; \n}', - output: 'function foo() {\n return; \n}', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'blockSameLine' }], - }, - { - code: 'function foo() { a(); b(); return; \n}', - output: 'function foo() {\n a(); b(); return; \n}', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'blockSameLine' }], - }, - { - code: 'function foo() { \n return; }', - output: 'function foo() { \n return; \n}', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'singleLineClose' }], - }, - { - code: 'function foo() {\na();\nb();\nreturn; }', - output: 'function foo() {\na();\nb();\nreturn; \n}', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'singleLineClose' }], - }, - { - code: '!function foo() { \n return; }', - output: '!function foo() { \n return; \n}', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'singleLineClose' }], - }, - { - code: 'if (a) { b();\n } else { c(); }', - output: 'if (a) {\n b();\n } else { c(); }', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'blockSameLine' }], - }, - { - code: 'if (a) { b(); }\nelse { c(); }', - output: 'if (a) { b(); } else { c(); }', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'nextLineClose' }], - }, - { - code: 'while (foo) { \n bar(); }', - output: 'while (foo) { \n bar(); \n}', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'singleLineClose' }], - }, - { - code: 'for (;;) { bar(); \n }', - output: 'for (;;) {\n bar(); \n }', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'blockSameLine' }], - }, - { - code: 'with (foo) { bar(); \n }', - output: 'with (foo) {\n bar(); \n }', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'blockSameLine' }], - }, - { - code: 'switch (foo) \n { \n case `bar`: break; }', - output: 'switch (foo) { \n case `bar`: break; \n}', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'nextLineOpen' }, { messageId: 'singleLineClose' }], - }, - { - code: 'switch (foo) \n { }', - output: 'switch (foo) { }', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'try { bar(); }\ncatch (e) { baz(); }', - output: 'try { bar(); } catch (e) { baz(); }', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'nextLineClose' }], - }, - { - code: 'try \n { \n bar(); \n } catch (e) {}', - output: 'try { \n bar(); \n } catch (e) {}', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'try { \n bar(); \n } catch (e) \n {}', - output: 'try { \n bar(); \n } catch (e) {}', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'do \n { \n bar(); \n} while (true)', - output: 'do { \n bar(); \n} while (true)', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'for (foo in bar) \n { \n baz(); \n }', - output: 'for (foo in bar) { \n baz(); \n }', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'try { \n bar(); \n }\ncatch (e) {\n}', - output: 'try { \n bar(); \n } catch (e) {\n}', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'nextLineClose' }], - }, - { - code: 'try { \n bar(); \n } catch (e) {\n}\n finally {\n}', - output: 'try { \n bar(); \n } catch (e) {\n} finally {\n}', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'nextLineClose' }], - }, - { - code: 'if (a) { \nb();\n } \n else { \nc();\n }', - output: 'if (a) { \nb();\n } else { \nc();\n }', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'nextLineClose' }], - }, - { - code: 'try { \n bar(); \n }\ncatch (e) {\n} finally {\n}', - output: 'try { \n bar(); \n }\ncatch (e) {\n}\n finally {\n}', - options: ['stroustrup', { allowSingleLine: true }], - errors: [{ messageId: 'sameLineClose' }], - }, - { - code: 'try { \n bar(); \n } catch (e) {\n}\n finally {\n}', - output: 'try { \n bar(); \n }\n catch (e) {\n}\n finally {\n}', - options: ['stroustrup', { allowSingleLine: true }], - errors: [{ messageId: 'sameLineClose' }], - }, - { - code: 'if (a) { \nb();\n } else { \nc();\n }', - output: 'if (a) { \nb();\n }\n else { \nc();\n }', - options: ['stroustrup', { allowSingleLine: true }], - errors: [{ messageId: 'sameLineClose' }], - }, - { - code: 'if (foo)\n{ poop();\n} \nelse if (bar) {\nbaz();\n} else if (thing) {\nboom();\n}\nelse {\nqux();\n}', - output: - 'if (foo)\n{\n poop();\n} \nelse if (bar) \n{\nbaz();\n}\n else if (thing) \n{\nboom();\n}\nelse \n{\nqux();\n}', - options: ['allman', { allowSingleLine: true }], - errors: [ - { messageId: 'blockSameLine' }, - { messageId: 'sameLineOpen' }, - { messageId: 'sameLineClose' }, - { messageId: 'sameLineOpen' }, - { messageId: 'sameLineOpen' }, - ], - }, - // Comment interferes with fix - { - code: 'if (foo) // comment \n{\nbar();\n}', - output: null, - errors: [{ messageId: 'nextLineOpen' }], - }, - // https://github.com/eslint/eslint/issues/7493 - { - code: 'if (foo) {\n bar\n.baz }', - output: 'if (foo) {\n bar\n.baz \n}', - errors: [{ messageId: 'singleLineClose' }], - }, - { - code: 'if (foo)\n{\n bar\n.baz }', - output: 'if (foo)\n{\n bar\n.baz \n}', - options: ['allman'], - errors: [{ messageId: 'singleLineClose' }], - }, - { - code: 'if (foo) { bar\n.baz }', - output: 'if (foo) {\n bar\n.baz \n}', - options: ['1tbs', { allowSingleLine: true }], - errors: [ - { messageId: 'blockSameLine' }, - { messageId: 'singleLineClose' }, - ], - }, - { - code: 'if (foo) { bar\n.baz }', - output: 'if (foo) \n{\n bar\n.baz \n}', - options: ['allman', { allowSingleLine: true }], - errors: [ - { messageId: 'sameLineOpen' }, - { messageId: 'blockSameLine' }, - { messageId: 'singleLineClose' }, - ], - }, - { - code: 'switch (x) {\n case 1: foo() }', - output: 'switch (x) {\n case 1: foo() \n}', - options: ['1tbs', { allowSingleLine: true }], - errors: [{ messageId: 'singleLineClose' }], - }, - { - code: 'class Foo\n{\n}', - output: 'class Foo {\n}', - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: '(class\n{\n})', - output: '(class {\n})', - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'class Foo{\n}', - output: 'class Foo\n{\n}', - options: ['allman'], - errors: [{ messageId: 'sameLineOpen' }], - }, - { - code: '(class {\n})', - output: '(class \n{\n})', - options: ['allman'], - errors: [{ messageId: 'sameLineOpen' }], - }, - { - code: 'class Foo {\nbar() {\n}}', - output: 'class Foo {\nbar() {\n}\n}', - errors: [{ messageId: 'singleLineClose' }], - }, - { - code: '(class Foo {\nbar() {\n}})', - output: '(class Foo {\nbar() {\n}\n})', - errors: [{ messageId: 'singleLineClose' }], - }, - { - code: 'class\nFoo{}', - output: 'class\nFoo\n{}', - options: ['allman'], - errors: [{ messageId: 'sameLineOpen' }], - }, - // https://github.com/eslint/eslint/issues/7621 - { - code: ` -if (foo) -{ - bar -} -else { - baz -} - `, - output: ` -if (foo) { - bar -} else { - baz -} - `, - errors: [{ messageId: 'nextLineOpen' }, { messageId: 'nextLineClose' }], - }, - { - code: ` -interface Foo -{ -} - `, - output: ` -interface Foo { -} - `, - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: ` -interface Foo -{ -} - `, - output: ` -interface Foo { -} - `, - options: ['stroustrup'], - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'interface Foo { \n }', - output: 'interface Foo \n{ \n }', - options: ['allman'], - errors: [{ messageId: 'sameLineOpen' }], - }, - { - code: ` -module "Foo" -{ -} - `, - output: ` -module "Foo" { -} - `, - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: ` -module "Foo" -{ -} - `, - output: ` -module "Foo" { -} - `, - options: ['stroustrup'], - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'module "Foo" { \n }', - output: 'module "Foo" \n{ \n }', - options: ['allman'], - errors: [{ messageId: 'sameLineOpen' }], - }, - { - code: ` -namespace Foo -{ -} - `, - output: ` -namespace Foo { -} - `, - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: ` -namespace Foo -{ -} - `, - output: ` -namespace Foo { -} - `, - options: ['stroustrup'], - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'namespace Foo { \n }', - output: 'namespace Foo \n{ \n }', - options: ['allman'], - errors: [{ messageId: 'sameLineOpen' }], - }, - { - code: ` -enum Foo -{ -} - `, - output: ` -enum Foo { -} - `, - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: ` -enum Foo -{ -} - `, - output: ` -enum Foo { -} - `, - options: ['stroustrup'], - errors: [{ messageId: 'nextLineOpen' }], - }, - { - code: 'enum Foo { A }', - output: 'enum Foo \n{\n A \n}', - options: ['allman'], - errors: [ - { messageId: 'sameLineOpen' }, - { messageId: 'blockSameLine' }, - { messageId: 'singleLineClose' }, - ], - }, - ], -}); diff --git a/packages/eslint-plugin/tests/rules/comma-dangle.test.ts b/packages/eslint-plugin/tests/rules/comma-dangle.test.ts deleted file mode 100644 index 148f05c0a774..000000000000 --- a/packages/eslint-plugin/tests/rules/comma-dangle.test.ts +++ /dev/null @@ -1,259 +0,0 @@ -/* eslint-disable eslint-comments/no-use */ -// this rule tests the new lines, which prettier will want to fix and break the tests -/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ -/* eslint-enable eslint-comments/no-use */ -import { RuleTester } from '@typescript-eslint/rule-tester'; - -import rule from '../../src/rules/comma-dangle'; - -const ruleTester = new RuleTester({ - parser: '@typescript-eslint/parser', -}); - -ruleTester.run('comma-dangle', rule, { - valid: [ - // default - { code: 'enum Foo {}' }, - { code: 'enum Foo {\n}' }, - { code: 'enum Foo {Bar}' }, - { code: 'function Foo() {}' }, - { code: 'type Foo = []' }, - { code: 'type Foo = [\n]' }, - - // never - { code: 'enum Foo {Bar}', options: ['never'] }, - { code: 'enum Foo {Bar\n}', options: ['never'] }, - { code: 'enum Foo {Bar\n}', options: [{ enums: 'never' }] }, - { code: 'function Foo() {}', options: ['never'] }, - { code: 'function Foo() {}', options: ['never'] }, - { code: 'function Foo() {}', options: [{ generics: 'never' }] }, - { code: 'type Foo = [string]', options: ['never'] }, - { code: 'type Foo = [string]', options: [{ tuples: 'never' }] }, - - // always - { code: 'enum Foo {Bar,}', options: ['always'] }, - { code: 'enum Foo {Bar,\n}', options: ['always'] }, - { code: 'enum Foo {Bar,\n}', options: [{ enums: 'always' }] }, - { code: 'function Foo() {}', options: ['always'] }, - { code: 'function Foo() {}', options: ['always'] }, - { code: 'function Foo() {}', options: [{ generics: 'always' }] }, - { code: 'type Foo = [string,]', options: ['always'] }, - { code: 'type Foo = [string,\n]', options: [{ tuples: 'always' }] }, - - // always-multiline - { code: 'enum Foo {Bar}', options: ['always-multiline'] }, - { code: 'enum Foo {Bar,\n}', options: ['always-multiline'] }, - { code: 'enum Foo {Bar,\n}', options: [{ enums: 'always-multiline' }] }, - { code: 'function Foo() {}', options: ['always-multiline'] }, - { code: 'function Foo() {}', options: ['always-multiline'] }, - { - code: 'function Foo() {}', - options: [{ generics: 'always-multiline' }], - }, - { code: 'type Foo = [string]', options: ['always-multiline'] }, - { code: 'type Foo = [string,\n]', options: ['always-multiline'] }, - { - code: 'type Foo = [string,\n]', - options: [{ tuples: 'always-multiline' }], - }, - - // only-multiline - { code: 'enum Foo {Bar}', options: ['only-multiline'] }, - { code: 'enum Foo {Bar\n}', options: ['only-multiline'] }, - { code: 'enum Foo {Bar,\n}', options: ['only-multiline'] }, - { code: 'enum Foo {Bar,\n}', options: [{ enums: 'only-multiline' }] }, - { code: 'function Foo() {}', options: ['only-multiline'] }, - { code: 'function Foo() {}', options: ['only-multiline'] }, - { code: 'function Foo() {}', options: ['only-multiline'] }, - { - code: 'function Foo() {}', - options: [{ generics: 'only-multiline' }], - }, - { - code: 'function Foo() {}', - options: [{ generics: 'only-multiline' }], - }, - { code: 'type Foo = [string\n]', options: [{ tuples: 'only-multiline' }] }, - { code: 'type Foo = [string,\n]', options: [{ tuples: 'only-multiline' }] }, - - // ignore - { code: 'const a = () => {}', options: [{ generics: 'ignore' }] }, - - // each options - { - code: ` -const Obj = { a: 1 }; -enum Foo {Bar} -function Baz() {} -type Qux = [string, -] - `, - options: [ - { - enums: 'never', - generics: 'always', - tuples: 'always-multiline', - }, - ], - }, - ], - invalid: [ - // base rule - { - code: 'const Foo = {bar: 1,}', - output: 'const Foo = {bar: 1}', - errors: [{ messageId: 'unexpected' }], - }, - - // default - { - code: 'enum Foo {Bar,}', - output: 'enum Foo {Bar}', - errors: [{ messageId: 'unexpected' }], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - errors: [{ messageId: 'unexpected' }], - }, - { - code: 'type Foo = [string,]', - output: 'type Foo = [string]', - errors: [{ messageId: 'unexpected' }], - }, - - // never - { - code: 'enum Foo {Bar,}', - output: 'enum Foo {Bar}', - options: ['never'], - errors: [{ messageId: 'unexpected' }], - }, - { - code: 'enum Foo {Bar,\n}', - output: 'enum Foo {Bar\n}', - options: ['never'], - errors: [{ messageId: 'unexpected' }], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - options: ['never'], - errors: [{ messageId: 'unexpected' }], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - options: ['never'], - errors: [{ messageId: 'unexpected' }], - }, - { - code: 'type Foo = [string,]', - output: 'type Foo = [string]', - options: ['never'], - errors: [{ messageId: 'unexpected' }], - }, - { - code: 'type Foo = [string,\n]', - output: 'type Foo = [string\n]', - options: ['never'], - errors: [{ messageId: 'unexpected' }], - }, - - // always - { - code: 'enum Foo {Bar}', - output: 'enum Foo {Bar,}', - options: ['always'], - errors: [{ messageId: 'missing' }], - }, - { - code: 'enum Foo {Bar\n}', - output: 'enum Foo {Bar,\n}', - options: ['always'], - errors: [{ messageId: 'missing' }], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - options: ['always'], - errors: [{ messageId: 'missing' }], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - options: ['always'], - errors: [{ messageId: 'missing' }], - }, - { - code: 'type Foo = [string]', - output: 'type Foo = [string,]', - options: ['always'], - errors: [{ messageId: 'missing' }], - }, - { - code: 'type Foo = [string\n]', - output: 'type Foo = [string,\n]', - options: ['always'], - errors: [{ messageId: 'missing' }], - }, - - // always-multiline - { - code: 'enum Foo {Bar,}', - output: 'enum Foo {Bar}', - options: ['always-multiline'], - errors: [{ messageId: 'unexpected' }], - }, - { - code: 'enum Foo {Bar\n}', - output: 'enum Foo {Bar,\n}', - options: ['always-multiline'], - errors: [{ messageId: 'missing' }], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - options: ['always-multiline'], - errors: [{ messageId: 'unexpected' }], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - options: ['always-multiline'], - errors: [{ messageId: 'missing' }], - }, - { - code: 'type Foo = [string,]', - output: 'type Foo = [string]', - options: ['always-multiline'], - errors: [{ messageId: 'unexpected' }], - }, - { - code: 'type Foo = [string\n]', - output: 'type Foo = [string,\n]', - options: ['always-multiline'], - errors: [{ messageId: 'missing' }], - }, - - // only-multiline - { - code: 'enum Foo {Bar,}', - output: 'enum Foo {Bar}', - options: ['only-multiline'], - errors: [{ messageId: 'unexpected' }], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - options: ['only-multiline'], - errors: [{ messageId: 'unexpected' }], - }, - { - code: 'type Foo = [string,]', - output: 'type Foo = [string]', - options: ['only-multiline'], - errors: [{ messageId: 'unexpected' }], - }, - ], -}); diff --git a/packages/eslint-plugin/tests/rules/comma-spacing.test.ts b/packages/eslint-plugin/tests/rules/comma-spacing.test.ts deleted file mode 100644 index d86edef2ae34..000000000000 --- a/packages/eslint-plugin/tests/rules/comma-spacing.test.ts +++ /dev/null @@ -1,854 +0,0 @@ -/* eslint-disable eslint-comments/no-use */ -// this rule tests the spacing, which prettier will want to fix and break the tests -/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ -/* eslint-enable eslint-comments/no-use */ - -import { RuleTester } from '@typescript-eslint/rule-tester'; - -import rule from '../../src/rules/comma-spacing'; - -const ruleTester = new RuleTester({ - parser: '@typescript-eslint/parser', -}); - -ruleTester.run('comma-spacing', rule, { - valid: [ - "foo(1, true/* comment */, 'text');", - "foo(1, true /* comment */, 'text');", - "foo(1, true/* comment *//* comment */, 'text');", - "foo(1, true/* comment */ /* comment */, 'text');", - "foo(1, true, /* comment */ 'text');", - "foo(1, // comment\n true, /* comment */ 'text');", - { - code: "foo(1, // comment\n true,/* comment */ 'text');", - options: [{ before: false, after: false }], - }, - 'const a = 1, b = 2;', - 'const foo = [, ];', - 'const foo = [1, ];', - 'const foo = [, 2];', - 'const foo = [1, 2];', - 'const foo = [, , ];', - 'const foo = [1, , ];', - 'const foo = [, 2, ];', - 'const foo = [, , 3];', - 'const foo = [1, 2, ];', - 'const foo = [, 2, 3];', - 'const foo = [1, , 3];', - 'const foo = [1, 2, 3];', - "const foo = {'foo':'foo', 'baz':'baz'};", - "const foo = {'foo':'foo', 'baz':\n'baz'};", - "const foo = {'foo':\n'foo', 'baz':\n'baz'};", - 'function foo(a, b){}', - 'function foo(a, b = 1){}', - 'function foo(a = 1, b, c){}', - 'const foo = (a, b) => {}', - 'const foo = (a=1, b) => {}', - 'const foo = a => a + 2', - 'a, b', - 'const a = (1 + 2, 2)', - 'a(b, c)', - 'new A(b, c)', - 'foo((a), b)', - 'const b = ((1 + 2), 2)', - 'parseInt((a + b), 10)', - 'go.boom((a + b), 10)', - 'go.boom((a + b), 10, (4))', - 'const x = [ (a + c), (b + b) ]', - "[' , ']", - '[` , `]', - '`${[1, 2]}`', - 'fn(a, b,)', - 'const fn = (a, b,) => {}', - 'const fn = function (a, b,) {}', - "foo(/,/, 'a')", - "const x = ',,,,,';", - "const code = 'var foo = 1, bar = 3;'", - "['apples', \n 'oranges'];", - "{x: 'var x,y,z'}", - { - code: "const foo = {'foo':\n'bar' ,'baz':\n'qur'};", - options: [{ before: true, after: false }], - }, - { - code: 'const a = 1 ,b = 2;', - options: [{ before: true, after: false }], - }, - { - code: 'function foo(a ,b){}', - options: [{ before: true, after: false }], - }, - { - code: 'const arr = [,];', - options: [{ before: true, after: false }], - }, - { - code: 'const arr = [1 ,];', - options: [{ before: true, after: false }], - }, - { - code: 'const arr = [ ,2];', - options: [{ before: true, after: false }], - }, - { - code: 'const arr = [1 ,2];', - options: [{ before: true, after: false }], - }, - { - code: 'const arr = [,,];', - options: [{ before: true, after: false }], - }, - { - code: 'const arr = [1 , ,];', - options: [{ before: true, after: false }], - }, - { - code: 'const arr = [ ,2 ,];', - options: [{ before: true, after: false }], - }, - { - code: 'const arr = [ , ,3];', - options: [{ before: true, after: false }], - }, - { - code: 'const arr = [1 ,2 ,];', - options: [{ before: true, after: false }], - }, - { - code: 'const arr = [ ,2 ,3];', - options: [{ before: true, after: false }], - }, - { - code: 'const arr = [1 , ,3];', - options: [{ before: true, after: false }], - }, - { - code: 'const arr = [1 ,2 ,3];', - options: [{ before: true, after: false }], - }, - { - code: "const obj = {'foo':'bar' , 'baz':'qur'};", - options: [{ before: true, after: true }], - }, - { - code: 'const a = 1 , b = 2;', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [, ];', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [1 , ];', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [ , 2];', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [1 , 2];', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [, , ];', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [1 , , ];', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [ , 2 , ];', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [ , , 3];', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [1 , 2 , ];', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [, 2 , 3];', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [1 , , 3];', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [1 , 2 , 3];', - options: [{ before: true, after: true }], - }, - { - code: 'a , b', - options: [{ before: true, after: true }], - }, - { - code: 'const arr = [,];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [ ,];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [1,];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [,2];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [ ,2];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [1,2];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [,,];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [ ,,];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [1,,];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [,2,];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [ ,2,];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [,,3];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [1,2,];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [,2,3];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [1,,3];', - options: [{ before: false, after: false }], - }, - { - code: 'const arr = [1,2,3];', - options: [{ before: false, after: false }], - }, - { - code: 'const a = (1 + 2,2)', - options: [{ before: false, after: false }], - }, - 'const a; console.log(`${a}`, "a");', - 'const [a, b] = [1, 2];', - 'const [a, b, ] = [1, 2];', - 'const [a, , b] = [1, 2, 3];', - 'const [ , b] = a;', - 'const [, b] = a;', - { - code: ',', - parserOptions: { - ecmaFeatures: { jsx: true }, - }, - }, - { - code: ' , ', - parserOptions: { - ecmaFeatures: { jsx: true }, - }, - }, - { - code: 'Hello, world', - options: [{ before: true, after: false }], - parserOptions: { ecmaFeatures: { jsx: true } }, - }, - 'const Foo = (foo: T) => {}', - 'function foo() {}', - 'class Foo {}', - 'interface Foo{}', - 'interface A<> {}', - 'let foo,', - 'const arr = [,];', - 'const arr = [ ,];', - 'const arr = [ , ];', - 'const arr = [1,];', - 'const arr = [ , 2];', - 'const arr = [,,];', - 'const arr = [ ,,];', - 'const arr = [, ,];', - 'const arr = [,, ];', - 'const arr = [ , ,];', - 'const arr = [ ,, ];', - 'const arr = [ , , ];', - 'const arr = [,, 3];', - 'const arr = [1, 2, 3,];', - 'const arr = [1, 2, 3, ];', - "const obj = {'foo':'bar', 'baz':'qur', };", - "const obj = {'foo':'bar', 'baz':'qur',};", - { code: 'const arr = [ ,];', options: [{ before: true, after: false }] }, - { code: 'const arr = [, ];', options: [{ before: true, after: false }] }, - { code: 'const arr = [ , ];', options: [{ before: true, after: false }] }, - { code: 'const arr = [ ,,];', options: [{ before: true, after: false }] }, - { code: 'const arr = [, ,];', options: [{ before: true, after: false }] }, - { code: 'const arr = [,, ];', options: [{ before: true, after: false }] }, - { code: 'const arr = [ , ,];', options: [{ before: true, after: false }] }, - { code: 'const arr = [ ,, ];', options: [{ before: true, after: false }] }, - { code: 'const arr = [, , ];', options: [{ before: true, after: false }] }, - { code: 'const arr = [ , , ];', options: [{ before: true, after: false }] }, - { - code: 'const arr = [ , , ];', - options: [{ before: false, after: false }], - }, - { code: 'const [a, b,] = [1, 2];', parserOptions: { ecmaVersion: 6 } }, - { - code: 'Hello, world', - options: [{ before: true, after: false }], - parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } }, - }, - { code: '[a, /**/ , ]', options: [{ before: false, after: true }] }, - { code: '[a , /**/, ]', options: [{ before: true, after: true }] }, - { - code: '[a, /**/ , ] = foo', - options: [{ before: false, after: true }], - parserOptions: { ecmaVersion: 6 }, - }, - { - code: '[a , /**/, ] = foo', - options: [{ before: true, after: true }], - parserOptions: { ecmaVersion: 6 }, - }, - ], - - invalid: [ - { - code: 'a(b,c)', - output: 'a(b , c)', - options: [{ before: true, after: true }], - errors: [ - { - messageId: 'missing', - column: 4, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'missing', - column: 4, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'new A(b,c)', - output: 'new A(b , c)', - options: [{ before: true, after: true }], - errors: [ - { - messageId: 'missing', - column: 8, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'missing', - column: 8, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'const a = 1 ,b = 2;', - output: 'const a = 1, b = 2;', - errors: [ - { - messageId: 'unexpected', - column: 13, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'missing', - column: 13, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'const arr = [1 , 2];', - output: 'const arr = [1, 2];', - errors: [ - { - messageId: 'unexpected', - column: 16, - line: 1, - data: { loc: 'before' }, - }, - ], - }, - { - code: 'const arr = [1 , ];', - output: 'const arr = [1, ];', - errors: [ - { - messageId: 'unexpected', - column: 16, - line: 1, - data: { loc: 'before' }, - }, - ], - }, - { - code: 'const arr = [1 , ];', - output: 'const arr = [1 ,];', - options: [{ before: true, after: false }], - errors: [ - { - messageId: 'unexpected', - column: 16, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'const arr = [1 ,2];', - output: 'const arr = [1, 2];', - errors: [ - { - messageId: 'unexpected', - column: 16, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: `missing`, - column: 16, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'const arr = [(1) , 2];', - output: 'const arr = [(1), 2];', - errors: [ - { - messageId: 'unexpected', - column: 18, - line: 1, - data: { loc: 'before' }, - }, - ], - }, - { - code: 'const arr = [1, 2];', - output: 'const arr = [1 ,2];', - options: [{ before: true, after: false }], - errors: [ - { - messageId: 'missing', - column: 15, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'unexpected', - column: 15, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'const arr = [1\n , 2];', - output: 'const arr = [1\n ,2];', - options: [{ before: false, after: false }], - errors: [ - { - messageId: 'unexpected', - column: 3, - line: 2, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'const arr = [1,\n 2];', - output: 'const arr = [1 ,\n 2];', - options: [{ before: true, after: false }], - errors: [ - { - messageId: 'missing', - column: 15, - line: 1, - data: { loc: 'before' }, - }, - ], - }, - { - code: "const obj = {'foo':\n'bar', 'baz':\n'qur'};", - output: "const obj = {'foo':\n'bar' ,'baz':\n'qur'};", - options: [{ before: true, after: false }], - errors: [ - { - messageId: 'missing', - column: 6, - line: 2, - data: { loc: 'before' }, - }, - { - messageId: 'unexpected', - column: 6, - line: 2, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'const obj = {a: 1\n ,b: 2};', - output: 'const obj = {a: 1\n , b: 2};', - options: [{ before: false, after: true }], - errors: [ - { - messageId: 'missing', - column: 3, - line: 2, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'const obj = {a: 1 ,\n b: 2};', - output: 'const obj = {a: 1,\n b: 2};', - options: [{ before: false, after: false }], - errors: [ - { - messageId: 'unexpected', - column: 19, - line: 1, - data: { loc: 'before' }, - }, - ], - }, - { - code: 'const arr = [1 ,2];', - output: 'const arr = [1 , 2];', - options: [{ before: true, after: true }], - errors: [ - { - messageId: 'missing', - column: 16, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'const arr = [1,2];', - output: 'const arr = [1 , 2];', - options: [{ before: true, after: true }], - errors: [ - { - messageId: 'missing', - column: 15, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'missing', - column: 15, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: "const obj = {'foo':\n'bar','baz':\n'qur'};", - output: "const obj = {'foo':\n'bar' , 'baz':\n'qur'};", - options: [{ before: true, after: true }], - errors: [ - { - messageId: 'missing', - column: 6, - line: 2, - data: { loc: 'before' }, - }, - { - messageId: 'missing', - column: 6, - line: 2, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'const arr = [1 , 2];', - output: 'const arr = [1,2];', - options: [{ before: false, after: false }], - errors: [ - { - messageId: 'unexpected', - column: 16, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'unexpected', - column: 16, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'a ,b', - output: 'a, b', - options: [{ before: false, after: true }], - errors: [ - { - messageId: 'unexpected', - column: 3, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'missing', - column: 3, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'function foo(a,b){}', - output: 'function foo(a , b){}', - options: [{ before: true, after: true }], - errors: [ - { - messageId: 'missing', - column: 15, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'missing', - column: 15, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'const foo = (a,b) => {}', - output: 'const foo = (a , b) => {}', - options: [{ before: true, after: true }], - errors: [ - { - messageId: 'missing', - column: 15, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'missing', - column: 15, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'const foo = (a = 1,b) => {}', - output: 'const foo = (a = 1 , b) => {}', - options: [{ before: true, after: true }], - errors: [ - { - messageId: 'missing', - column: 19, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'missing', - column: 19, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'function foo(a = 1 ,b = 2) {}', - output: 'function foo(a = 1, b = 2) {}', - options: [{ before: false, after: true }], - errors: [ - { - messageId: 'unexpected', - column: 20, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'missing', - column: 20, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: '{foo(1 ,2)}', - output: '{foo(1, 2)}', - parserOptions: { - ecmaFeatures: { jsx: true }, - }, - errors: [ - { - messageId: 'unexpected', - column: 11, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'missing', - column: 11, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: "foo(1, true/* comment */ , 'foo');", - output: "foo(1, true/* comment */, 'foo');", - errors: [ - { - messageId: 'unexpected', - column: 26, - line: 1, - data: { loc: 'before' }, - }, - ], - }, - { - code: "foo(1, true,/* comment */ 'foo');", - output: "foo(1, true, /* comment */ 'foo');", - errors: [ - { - messageId: 'missing', - column: 12, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: "foo(404,// comment\n true, 'hello');", - output: "foo(404, // comment\n true, 'hello');", - errors: [ - { - messageId: 'missing', - column: 8, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - errors: [ - { - messageId: 'missing', - column: 15, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - errors: [ - { - messageId: 'unexpected', - column: 16, - line: 1, - data: { loc: 'before' }, - }, - ], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - errors: [ - { - messageId: 'unexpected', - column: 16, - line: 1, - data: { loc: 'before' }, - }, - { - messageId: 'missing', - column: 16, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - options: [{ before: false, after: false }], - errors: [ - { - messageId: 'unexpected', - column: 15, - line: 1, - data: { loc: 'after' }, - }, - ], - }, - { - code: 'function Foo() {}', - output: 'function Foo() {}', - options: [{ before: true, after: false }], - errors: [ - { - messageId: 'missing', - column: 15, - line: 1, - data: { loc: 'before' }, - }, - ], - }, - { - code: 'let foo ,', - output: 'let foo,', - errors: [ - { - messageId: 'unexpected', - column: 9, - line: 1, - data: { loc: 'before' }, - }, - ], - }, - ], -}); diff --git a/packages/eslint-plugin/tests/rules/func-call-spacing.test.ts b/packages/eslint-plugin/tests/rules/func-call-spacing.test.ts deleted file mode 100644 index 443e4e92f19d..000000000000 --- a/packages/eslint-plugin/tests/rules/func-call-spacing.test.ts +++ /dev/null @@ -1,533 +0,0 @@ -/* eslint-disable eslint-comments/no-use */ -// this rule tests the spacing, which prettier will want to fix and break the tests -/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ -/* eslint-enable eslint-comments/no-use */ - -import { RuleTester } from '@typescript-eslint/rule-tester'; -import type { TSESLint } from '@typescript-eslint/utils'; - -import type { MessageIds, Options } from '../../src/rules/func-call-spacing'; -import rule from '../../src/rules/func-call-spacing'; - -const ruleTester = new RuleTester({ - parser: '@typescript-eslint/parser', -}); - -ruleTester.run('func-call-spacing', rule, { - valid: [ - ...[ - 'f();', - 'f(a, b);', - 'f.b();', - 'f.b().c();', - 'f()()', - '(function() {}())', - 'var f = new Foo()', - 'var f = new Foo', - 'f( (0) )', - '(function(){ if (foo) { bar(); } }());', - 'f(0, (1))', - "describe/**/('foo', function () {});", - 'new (foo())', - '( f )( 0 )', - '( (f) )( (0) )', - '( f()() )(0)', - 'f()', - 'f(b, b)', - 'f.b(b, b)', - '(function() {}())', - '((function() {})())', - '( f )( 0 )', - '( (f) )( (0) )', - '( f()() )(0)', - - // optional call - 'f?.();', - 'f?.(a, b);', - 'f?.b();', - 'f?.b()?.c();', - 'f.b?.();', - 'f.b?.().c();', - 'f()?.()', - '(function() {}?.())', - 'f?.( (0) )', - '(function(){ if (foo) { bar(); } }?.());', - 'f?.(0, (1))', - "describe/**/?.('foo', function () {});", - "describe?./**/('foo', function () {});", - '( f )?.( 0 )', - '( (f) )?.( (0) )', - '( f?.()() )(0)', - '( f()?.() )(0)', - '( f?.()?.() )(0)', - '( f?.()() )?.(0)', - '( f()?.() )?.(0)', - '( f?.()?.() )?.(0)', - 'f?.()', - 'f?.(b, b)', - 'f.b?.(b, b)', - 'f?.b(b, b)', - 'f?.b?.(b, b)', - '(function() {}?.())', - '((function() {})())', - '( f )?.( 0 )', - '( (f) )?.( (0) )', - '( f()() )?.(0)', - ].map>(code => ({ - code, - options: ['never'], - })), - - ...[ - 'f ();', - 'f (a, b);', - 'f.b ();', - 'f.b ().c ();', - 'f () ()', - '(function() {} ())', - 'var f = new Foo ()', - 'var f = new Foo', - 'f ( (0) )', - 'f (0) (1)', - 'f ();\n t ();', - '( f ) ( 0 )', - '( (f) ) ( (0) )', - 'f ()', - 'f (b, b)', - 'f.b (b, b)', - '(function() {} ())', - '((function() {}) ())', - '( f ) ( 0 )', - '( (f) ) ( (0) )', - '( f () ) (0)', - - // optional call - 'f?.b ();', - 'f?.b ()?.c ();', - 'f?.b (b, b)', - ].map>(code => ({ - code, - options: ['always'], - })), - ...[ - 'f\n();', - 'f.b \n ();', - 'f\n() ().b \n()\n ()', - 'var f = new Foo\n();', - 'f// comment\n()', - 'f // comment\n ()', - 'f\n/*\n*/\n()', - 'f\r();', - 'f\u2028();', - 'f\u2029();', - 'f\r\n();', - - // optional call - 'f?.b \n ();', - 'f\n() ()?.b \n()\n ()', - ].map>(code => ({ - code, - options: ['always', { allowNewlines: true }], - })), - ], - invalid: [ - // "never" - ...[ - { - code: 'f ();', - output: 'f();', - }, - { - code: 'f (a, b);', - output: 'f(a, b);', - }, - { - code: 'f.b ();', - output: 'f.b();', - errors: [ - { - messageId: 'unexpectedWhitespace' as const, - column: 3, - }, - ], - }, - { - code: 'f.b().c ();', - output: 'f.b().c();', - errors: [ - { - messageId: 'unexpectedWhitespace' as const, - column: 7, - }, - ], - }, - { - code: 'f() ()', - output: 'f()()', - }, - { - code: '(function() {} ())', - output: '(function() {}())', - }, - { - code: 'var f = new Foo ()', - output: 'var f = new Foo()', - }, - { - code: 'f ( (0) )', - output: 'f( (0) )', - }, - { - code: 'f(0) (1)', - output: 'f(0)(1)', - }, - { - code: 'f ();\n t ();', - output: 'f();\n t();', - errors: [ - { - messageId: 'unexpectedWhitespace' as const, - }, - { - messageId: 'unexpectedWhitespace' as const, - }, - ], - }, - - // https://github.com/eslint/eslint/issues/7787 - { - code: 'f\n();', - output: null, // no change - }, - { - code: ` -this.cancelled.add(request) -this.decrement(request) -(request.reject(new api.Cancel())) - `, - output: null, // no change - errors: [ - { - messageId: 'unexpectedWhitespace' as const, - line: 3, - column: 23, - }, - ], - }, - { - code: ` -var a = foo -(function(global) {}(this)); - `, - output: null, // no change - errors: [ - { - messageId: 'unexpectedWhitespace' as const, - line: 2, - column: 9, - }, - ], - }, - { - code: ` -var a = foo -(baz()) - `, - output: null, // no change - errors: [ - { - messageId: 'unexpectedWhitespace' as const, - line: 2, - column: 9, - }, - ], - }, - { - code: 'f\r();', - output: null, // no change - }, - { - code: 'f\u2028();', - output: null, // no change - }, - { - code: 'f\u2029();', - output: null, // no change - }, - { - code: 'f\r\n();', - output: null, // no change - }, - ].map>(code => ({ - options: ['never'], - errors: [ - { - messageId: 'unexpectedWhitespace', - }, - ], - ...code, - })), - - // "always" - ...[ - { - code: 'f();', - output: 'f ();', - }, - { - code: 'f(a, b);', - output: 'f (a, b);', - }, - { - code: 'f() ()', - output: 'f () ()', - }, - { - code: 'var f = new Foo()', - output: 'var f = new Foo ()', - }, - { - code: 'f( (0) )', - output: 'f ( (0) )', - }, - { - code: 'f(0) (1)', - output: 'f (0) (1)', - }, - ].map>(code => ({ - options: ['always'], - errors: [ - { - messageId: 'missing', - }, - ], - ...code, - })), - ...[ - { - code: 'f\n();', - output: 'f ();', - }, - { - code: 'f\n(a, b);', - output: 'f (a, b);', - }, - { - code: 'f.b();', - output: 'f.b ();', - errors: [ - { - messageId: 'missing' as const, - column: 3, - }, - ], - }, - { - code: 'f.b\n();', - output: 'f.b ();', - }, - { - code: 'f.b().c ();', - output: 'f.b ().c ();', - errors: [ - { - messageId: 'missing' as const, - column: 3, - }, - ], - }, - { - code: 'f.b\n().c ();', - output: 'f.b ().c ();', - }, - { - code: 'f\n() ()', - output: 'f () ()', - }, - { - code: 'f\n()()', - output: 'f () ()', - errors: [ - { - messageId: 'unexpectedNewline' as const, - }, - { - messageId: 'missing' as const, - }, - ], - }, - { - code: '(function() {}())', - output: '(function() {} ())', - errors: [ - { - messageId: 'missing' as const, - }, - ], - }, - { - code: 'f();\n t();', - output: 'f ();\n t ();', - errors: [ - { - messageId: 'missing' as const, - }, - { - messageId: 'missing' as const, - }, - ], - }, - { - code: 'f\r();', - output: 'f ();', - }, - { - code: 'f\u2028();', - output: 'f ();', - errors: [ - { - messageId: 'unexpectedNewline' as const, - }, - ], - }, - { - code: 'f\u2029();', - output: 'f ();', - errors: [ - { - messageId: 'unexpectedNewline' as const, - }, - ], - }, - { - code: 'f\r\n();', - output: 'f ();', - }, - ].map>(code => ({ - options: ['always'], - errors: [ - { - messageId: - code.code.includes('\n') || code.code.includes('\r') - ? 'unexpectedNewline' - : 'unexpectedWhitespace', - }, - ], - ...code, - })), - - // "always", "allowNewlines": true - ...[ - { - code: 'f();', - output: 'f ();', - }, - { - code: 'f(a, b);', - output: 'f (a, b);', - }, - { - code: 'f.b();', - output: 'f.b ();', - errors: [ - { - messageId: 'missing' as const, - column: 3, - }, - ], - }, - { - code: 'f.b().c ();', - output: 'f.b ().c ();', - }, - { - code: 'f() ()', - output: 'f () ()', - }, - { - code: '(function() {}())', - output: '(function() {} ())', - }, - { - code: 'var f = new Foo()', - output: 'var f = new Foo ()', - }, - { - code: 'f( (0) )', - output: 'f ( (0) )', - }, - { - code: 'f(0) (1)', - output: 'f (0) (1)', - }, - { - code: 'f();\n t();', - output: 'f ();\n t ();', - errors: [ - { - messageId: 'missing' as const, - }, - { - messageId: 'missing' as const, - }, - ], - }, - ].map>(code => ({ - options: ['always', { allowNewlines: true }], - errors: [ - { - messageId: 'missing', - }, - ], - ...code, - })), - - // optional chain - ...[ - 'f ?.();', - 'f?. ();', - 'f ?. ();', - 'f\n?.();', - 'f?.\n();', - 'f\n?.\n();', - ].reduce[]>((acc, code) => { - acc.push( - { - options: ['always', { allowNewlines: true }], - errors: [ - { - messageId: 'unexpectedWhitespace', - }, - ], - code, - // apply no fixers to it - output: null, - }, - { - options: ['always'], - errors: [ - { - messageId: 'unexpectedWhitespace', - }, - ], - code, - // apply no fixers to it - output: null, - }, - { - options: ['never'], - errors: [ - { - messageId: 'unexpectedWhitespace', - }, - ], - code, - // apply no fixers to it - output: null, - }, - ); - - return acc; - }, []), - ], -}); diff --git a/packages/eslint-plugin/tests/rules/indent/indent.test.ts b/packages/eslint-plugin/tests/rules/indent/indent.test.ts deleted file mode 100644 index 3d316d1506b9..000000000000 --- a/packages/eslint-plugin/tests/rules/indent/indent.test.ts +++ /dev/null @@ -1,1639 +0,0 @@ -/* eslint-disable eslint-comments/no-use */ -// this rule tests the spacing, which prettier will want to fix and break the tests -/* eslint "@typescript-eslint/internal/plugin-test-formatting": ["error", { formatWithPrettier: false }] */ -/* eslint-enable eslint-comments/no-use */ - -import { RuleTester } from '@typescript-eslint/rule-tester'; -import type { TSESLint } from '@typescript-eslint/utils'; -import { AST_NODE_TYPES } from '@typescript-eslint/utils'; - -import rule from '../../../src/rules/indent'; -import type { - InferMessageIdsTypeFromRule, - InferOptionsTypeFromRule, -} from '../../../src/util'; - -type MessageIds = InferMessageIdsTypeFromRule; -type Options = InferOptionsTypeFromRule; - -/** - * Marks a test case as a plain javascript case which should be indented the same - */ -function nonTsTestCase(example: TemplateStringsArray): string { - return [`// Non-TS Test Case`, example].join('\n'); -} - -const individualNodeTests = [ - { - node: AST_NODE_TYPES.ClassDeclaration, - code: [ - ` -abstract class Foo { - constructor() {} - method() { - console.log('hi'); - } -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSAbstractPropertyDefinition, - code: [ - ` -class Foo { - abstract bar : baz; - abstract foo : { - a : number - b : number - }; -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSAbstractMethodDefinition, - code: [ - ` -class Foo { - abstract bar() : baz; - abstract foo() : { - a : number - b : number - }; -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSArrayType, - code: [ - ` -type foo = ArrType[]; - `, - ], - }, - { - node: AST_NODE_TYPES.TSAsExpression, - code: [ - ` -const foo = {} as { - foo: string, - bar: number, -}; - `, - nonTsTestCase` -const foo = {} === -{ - foo: string, - bar: number, -}; - `, - ` -const foo = {} as -{ - foo: string, - bar: number, -}; - `, - ], - }, - { - node: AST_NODE_TYPES.TSConditionalType, - code: [ - nonTsTestCase` -const Foo = T - ? { - a: number, - b: boolean - } - : { - c: string - }; - `, - ` -type Foo = T extends string - ? { - a: number, - b: boolean - } - : { - c: string - }; - `, - nonTsTestCase` -const Foo = T ? { - a: number, - b: boolean -} : string; - `, - ` -type Foo = T extends string ? { - a: number, - b: boolean -} : string; - `, - ], - }, - { - node: AST_NODE_TYPES.TSConstructorType, - code: [ - ` -type Constructor = new ( - ...args: any[] -) => T; - `, - ], - }, - { - node: 'TSConstructSignature', - code: [ - ` -interface Foo { - new () : Foo - new () : { - bar : string - baz : string - } -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSDeclareFunction, - code: [ - ` -declare function foo() : { - bar : number, - baz : string, -}; - `, - ], - }, - { - node: AST_NODE_TYPES.TSEmptyBodyFunctionExpression, - code: [ - ` -class Foo { - constructor( - a : string, - b : { - c : number - } - ) -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSExportAssignment, - code: [ - ` -export = { - a: 1, - b: 2, -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSFunctionType, - code: [ - ` -const foo: () => void = () => ({ - a: 1, - b: 2, -}); - `, - ` -const foo: () => { - a: number, - b: number, -} = () => ({ - a: 1, - b: 2, -}); - `, - ` -const foo: ({ - a: number, - b: number, -}) => void = (arg) => ({ - a: 1, - b: 2, -}); - `, - ` -const foo: ({ - a: number, - b: number, -}) => { - a: number, - b: number, -} = (arg) => ({ - a: arg.a, - b: arg.b, -}); - `, - ], - }, - { - node: AST_NODE_TYPES.TSImportType, - code: [ - ` -const foo: import("bar") = { - a: 1, - b: 2, -}; - `, - ` -const foo: import( - "bar" -) = { - a: 1, - b: 2, -}; - `, - ], - }, - { - node: AST_NODE_TYPES.TSIndexedAccessType, - code: [ - nonTsTestCase` -const Foo = Bar[ - 'asdf' -]; - `, - ` -type Foo = Bar[ - 'asdf' -]; - `, - ], - }, - { - node: AST_NODE_TYPES.TSIndexSignature, - code: [ - ` -type Foo = { - [a : string] : { - x : foo - [b : number] : boolean - } -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSInferType, - code: [ - ` -type Foo = T extends string - ? infer U - : { - a : string - }; - `, - ], - }, - { - node: 'TSInterfaceBody, TSInterfaceDeclaration', - code: [ - ` -interface Foo { - a : string - b : { - c : number - d : boolean - } -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSInterfaceHeritage, - code: [ - ` -interface Foo extends Bar { - a : string - b : { - c : number - d : boolean - } -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSIntersectionType, - code: [ - ` -type Foo = "string" & { - a : number -} & number; - `, - ], - }, - { - node: 'TSImportEqualsDeclaration, TSExternalModuleReference', - code: [ - nonTsTestCase` -const foo = require( - 'asdf' -); - `, - ` -import foo = require( - 'asdf' -); - `, - ], - }, - // TSLiteralType - { - node: AST_NODE_TYPES.TSMappedType, - code: [ - ` -type Partial = { - [P in keyof T]: T[P]; -} - `, - ` -// TSQuestionToken -type Partial = { - [P in keyof T]?: T[P]; -} - `, - ` -// TSPlusToken -type Partial = { - [P in keyof T]+?: T[P]; -} - `, - ` -// TSMinusToken -type Partial = { - [P in keyof T]-?: T[P]; -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSMethodSignature, - code: [ - ` -interface Foo { - method() : string - method2() : { - a : number - b : string - } -} - `, - ], - }, - // TSMinusToken - tested in TSMappedType - { - node: 'TSModuleBlock, TSModuleDeclaration', - code: [ - ` -declare module "foo" { - export const bar : { - a : string, - b : number, - } -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSNonNullExpression, - code: [ - nonTsTestCase` -const foo = a - .b. - c; - `, - ` -const foo = a! - .b!. - c; - `, - ], - }, - { - node: AST_NODE_TYPES.TSParameterProperty, - code: [ - ` -class Foo { - constructor( - private foo : string, - public bar : { - a : string, - b : number, - } - ) { - console.log('foo') - } -} - `, - ], - }, - // TSPlusToken - tested in TSMappedType - { - node: AST_NODE_TYPES.TSPropertySignature, - code: [ - ` -interface Foo { - bar : string - baz : { - a : string - b : number - } -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSQualifiedName, - code: [ - ` -const a: Foo.bar = { - a: 1, - b: 2, -}; - `, - nonTsTestCase` -const a = Foo. - bar - .baz = { - a: 1, - b: 2, - }; - `, - ` -const a: Foo. - bar - .baz = { - a: 1, - b: 2, - }; - `, - ], - }, - // TSQuestionToken - tested in TSMappedType - { - node: AST_NODE_TYPES.TSRestType, - code: [ - ` -type foo = [ - string, - ...string[], -]; - `, - ], - }, - { - node: AST_NODE_TYPES.TSThisType, - code: [ - ` -declare class MyArray extends Array { - sort(compareFn?: (a: T, b: T) => number): this; - meth() : { - a: number, - } -} - `, - ], - }, - { - node: AST_NODE_TYPES.TSTupleType, - code: [ - nonTsTestCase` -const foo = [ - string, - number, -]; - `, - ` -type foo = [ - string, - number, -]; - `, - nonTsTestCase` -const foo = [ - [ - string, - number, - ], -]; - `, - ` -type foo = [ - [ - string, - number, - ], -]; - `, - ], - }, - // TSTypeAnnotation - tested in everything.. - // TSTypeLiteral - tested in everything.. - { - node: AST_NODE_TYPES.TSTypeOperator, - code: [ - ` -type T = keyof { - a: 1, - b: 2, -}; - `, - ], - }, - { - node: 'TSTypeParameter, TSTypeParameterDeclaration', - code: [ - ` -type Foo = { - a : unknown, - b : never, -} - `, - ` -function foo< - T, - U ->() { - console.log(''); -} - `, - ], - }, - // TSTypeReference - tested in everything.. - { - node: AST_NODE_TYPES.TSUnionType, - code: [ - ` -type Foo = string | { - a : number -} | number; - `, - ], - }, -].reduce>( - (acc, testCase) => { - const indent = ' '; - - const validCases = [...acc.valid]; - const invalidCases = [...acc.invalid]; - - const codeCases = testCase.code.map(code => - [ - '', // newline to make test error messages nicer - `// ${testCase.node}`, // add comment to easily identify which node a test belongs to - code.trim(), // remove leading/trailing spaces from the case - ].join('\n'), - ); - - codeCases.forEach(code => { - // valid test case is just the code - validCases.push(code); - - const invalid = { - // test the fixer by removing all the spaces - code: code.replace(new RegExp(indent, 'g'), ''), - output: code, - errors: code - .split('\n') - .map | null>((line, lineNum) => { - const indentCount = line.split(indent).length - 1; - const spaceCount = indentCount * indent.length; - - if (indentCount < 1) { - return null; - } - - return { - messageId: 'wrongIndentation', - data: { - expected: `${spaceCount} spaces`, - actual: 0, - }, - line: lineNum + 1, - column: 1, - }; - }) - .filter( - (error): error is TSESLint.TestCaseError => - error != null, - ), - }; - if (invalid.errors.length > 0) { - invalidCases.push(invalid); - } - }); - - return { ...acc, valid: validCases, invalid: invalidCases }; - }, - { valid: [], invalid: [] }, -); - -const ruleTester = new RuleTester({ - parserOptions: { - ecmaVersion: 6, - sourceType: 'module', - ecmaFeatures: {}, - }, - parser: '@typescript-eslint/parser', -}); - -ruleTester.run('indent', rule, { - valid: [ - ...individualNodeTests.valid, - ` -@Component({ - components: { - ErrorPage: () => import('@/components/ErrorPage.vue'), - }, - head: { - titleTemplate(title) { - if (title) { - return \`test\` - } - return 'Title' - }, - htmlAttrs: { - lang: 'en', - }, - meta: [ - { charset: 'utf-8' }, - { name: 'viewport', content: 'width=device-width, initial-scale=1' }, - ], - }, -}) -export default class App extends Vue -{ - get error() - { - return this.$store.state.errorHandler.error - } -} - `, - // https://github.com/eslint/typescript-eslint-parser/issues/474 - ` -/** - * @param {string} name - * @param {number} age - * @returns {string} - */ -function foo(name: string, age: number): string {} - `, - ` -const firebaseApp = firebase.apps.length - ? firebase.app() - : firebase.initializeApp({ - apiKey: __FIREBASE_API_KEY__, - authDomain: __FIREBASE_AUTH_DOMAIN__, - databaseURL: __FIREBASE_DATABASE_URL__, - projectId: __FIREBASE_PROJECT_ID__, - storageBucket: __FIREBASE_STORAGE_BUCKET__, - messagingSenderId: __FIREBASE_MESSAGING_SENDER_ID__, - }) - `, - // https://github.com/bradzacher/eslint-plugin-typescript/issues/271 - { - code: ` -const foo = { - a: 1, - b: 2 - }, - bar = 1; - `, - options: [4, { VariableDeclarator: { const: 3 } }], - }, - { - code: ` -const foo : Foo = { - a: 1, - b: 2 - }, - bar = 1; - `, - options: [4, { VariableDeclarator: { const: 3 } }], - }, - { - code: ` -const name: string = ' Typescript ' - .toUpperCase() - .trim(), - - greeting: string = (" Hello " + name) - .toUpperCase() - .trim(); - `, - options: [2, { VariableDeclarator: { const: 3 } }], - }, - { - code: ` -const div: JQuery = $('
') - .addClass('some-class') - .appendTo($('body')), - - button: JQuery = $('