diff --git a/eslint.config.mjs b/eslint.config.mjs index 8ce7890b0867..395b8dde0dac 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,9 +1,9 @@ // @ts-check +import eslintCommentsPlugin from '@eslint-community/eslint-plugin-eslint-comments/configs'; import { fixupConfigRules, fixupPluginRules } from '@eslint/compat'; import { FlatCompat } from '@eslint/eslintrc'; import eslint from '@eslint/js'; -import eslintCommentsPlugin from '@eslint-community/eslint-plugin-eslint-comments/configs'; import tseslintInternalPlugin from '@typescript-eslint/eslint-plugin-internal'; import vitestPlugin from '@vitest/eslint-plugin'; import eslintPluginPlugin from 'eslint-plugin-eslint-plugin'; @@ -594,10 +594,10 @@ export default tseslint.config( rules: { '@typescript-eslint/internal/prefer-ast-types-enum': 'off', 'import/no-default-export': 'off', + 'react-hooks/exhaustive-deps': 'warn', // TODO: enable it later 'react/jsx-no-target-blank': 'off', 'react/no-unescaped-entities': 'off', 'react/prop-types': 'off', - 'react-hooks/exhaustive-deps': 'warn', // TODO: enable it later }, settings: { react: { @@ -632,7 +632,26 @@ export default tseslint.config( name: 'all-files', rules: { '@typescript-eslint/sort-type-constituents': 'off', - 'perfectionist/sort-classes': 'error', + 'perfectionist/sort-classes': [ + 'error', + { + groups: [ + 'index-signature', + 'static-property', + 'static-block', + ['protected-property', 'protected-accessor-property'], + ['private-property', 'private-accessor-property'], + ['property', 'accessor-property'], + 'constructor', + 'static-method', + 'protected-method', + 'private-method', + 'method', + ['get-method', 'set-method'], + 'unknown', + ], + }, + ], 'perfectionist/sort-enums': 'off', 'perfectionist/sort-objects': 'error', 'perfectionist/sort-union-types': [ @@ -644,6 +663,20 @@ export default tseslint.config( ], }, }, + { + files: ['packages/ast-spec/src/**/*.ts'], + rules: { + 'perfectionist/sort-interfaces': [ + 'error', + { + customGroups: { + first: ['^type$'], + }, + groups: ['first', 'unknown'], + }, + ], + }, + }, { files: [ 'packages/eslint-plugin/src/rules/*.ts', @@ -655,10 +688,10 @@ export default tseslint.config( 'error', { customGroups: { - first: ['loc', 'name', 'node', 'type'], - fourth: ['fix'], - second: ['meta', 'messageId', 'start'], - third: ['defaultOptions', 'data', 'end'], + first: ['^loc$', '^name$', '^node$', '^type$'], + fourth: ['^fix$'], + second: ['^meta$', '^messageId$', '^start$'], + third: ['^defaultOptions$', '^data$', '^end$'], }, groups: ['first', 'second', 'third', 'fourth', 'unknown'], }, @@ -672,7 +705,7 @@ export default tseslint.config( 'perfectionist/sort-objects': [ 'error', { - customGroups: { top: ['valid'] }, + customGroups: { top: ['^valid$'] }, groups: ['top', 'unknown'], }, ], @@ -686,8 +719,8 @@ export default tseslint.config( 'error', { customGroups: { - first: ['type'], - second: ['loc', 'range'], + first: ['^type$'], + second: ['^loc$', '^range$'], }, groups: ['first', 'second'], }, diff --git a/package.json b/package.json index a51cd0073937..5ea26cb0ba81 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-jsdoc": "^50.5.0", "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-perfectionist": "^3.9.1", + "eslint-plugin-perfectionist": "^4.12.3", "eslint-plugin-react": "^7.37.3", "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-regexp": "^2.7.0", diff --git a/packages/ast-spec/src/expression/UnaryExpression/spec.ts b/packages/ast-spec/src/expression/UnaryExpression/spec.ts index 2d9f09f317b8..b5733e5ed85a 100644 --- a/packages/ast-spec/src/expression/UnaryExpression/spec.ts +++ b/packages/ast-spec/src/expression/UnaryExpression/spec.ts @@ -3,5 +3,5 @@ import type { UnaryExpressionBase } from '../../base/UnaryExpressionBase'; export interface UnaryExpression extends UnaryExpressionBase { type: AST_NODE_TYPES.UnaryExpression; - operator: '!' | '+' | '~' | '-' | 'delete' | 'typeof' | 'void'; + operator: '!' | '+' | '-' | 'delete' | 'typeof' | 'void' | '~'; } diff --git a/packages/eslint-plugin/src/rules/no-restricted-types.ts b/packages/eslint-plugin/src/rules/no-restricted-types.ts index b4d9e427c07f..2901ac5a9cf5 100644 --- a/packages/eslint-plugin/src/rules/no-restricted-types.ts +++ b/packages/eslint-plugin/src/rules/no-restricted-types.ts @@ -36,7 +36,7 @@ function stringifyNode( } function getCustomMessage( - bannedType: string | { fixWith?: string; message?: string } | true | null, + bannedType: string | true | { fixWith?: string; message?: string } | null, ): string { if (!bannedType || bannedType === true) { return ''; diff --git a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts index d846b0bbc59b..3e8c5914fa9f 100644 --- a/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts +++ b/packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts @@ -47,13 +47,13 @@ export type Options = [ ignoreIfStatements?: boolean; ignoreMixedLogicalExpressions?: boolean; ignorePrimitives?: + | true | { bigint?: boolean; boolean?: boolean; number?: boolean; string?: boolean; - } - | true; + }; ignoreTernaryTests?: boolean; }, ]; diff --git a/packages/eslint-plugin/src/util/collectUnusedVariables.ts b/packages/eslint-plugin/src/util/collectUnusedVariables.ts index d61b121fa5ba..983eb1472c14 100644 --- a/packages/eslint-plugin/src/util/collectUnusedVariables.ts +++ b/packages/eslint-plugin/src/util/collectUnusedVariables.ts @@ -230,9 +230,7 @@ class UnusedVarsVisitor extends Visitor { private markVariableAsUsed( variableOrIdentifier: ScopeVariable | TSESTree.Identifier, ): void; - private markVariableAsUsed(name: string, parent: TSESTree.Node): void; - private markVariableAsUsed( variableOrIdentifierOrName: string | ScopeVariable | TSESTree.Identifier, parent?: TSESTree.Node, diff --git a/packages/eslint-plugin/tests/RuleTester.ts b/packages/eslint-plugin/tests/RuleTester.ts index bc0317c8a466..7e003e7fa5f1 100644 --- a/packages/eslint-plugin/tests/RuleTester.ts +++ b/packages/eslint-plugin/tests/RuleTester.ts @@ -46,10 +46,10 @@ export function batchedSingleLineTests< Options extends readonly unknown[], >( options: + | ValidTestCase | ({ output?: string | null; - } & Omit, 'output'>) - | ValidTestCase, + } & Omit, 'output'>), ): (InvalidTestCase | ValidTestCase)[] { // -- eslint counts lines from 1 const lineOffset = options.code.startsWith('\n') ? 2 : 1; diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index 5a70229dd33f..0a72f5b1ed80 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -520,11 +520,11 @@ declare module 'eslint/lib/rules/prefer-destructuring' { object?: boolean; } type Option0 = + | DestructuringTypeConfig | { AssignmentExpression?: DestructuringTypeConfig; VariableDeclarator?: DestructuringTypeConfig; - } - | DestructuringTypeConfig; + }; export interface Option1 { enforceForRenamedProperties?: boolean; } @@ -554,6 +554,7 @@ declare module 'eslint/lib/rules/no-restricted-imports' { } )[]; export type ArrayOfStringOrObjectPatterns = + | string[] | { // extended allowTypeImports?: boolean; @@ -561,15 +562,14 @@ declare module 'eslint/lib/rules/no-restricted-imports' { group?: string[]; regex?: string; message?: string; - }[] - | string[]; + }[]; export type RuleListener = + | Record | { ExportAllDeclaration(node: TSESTree.ExportAllDeclaration): void; ExportNamedDeclaration(node: TSESTree.ExportNamedDeclaration): void; ImportDeclaration(node: TSESTree.ImportDeclaration): void; - } - | Record; + }; } export interface ObjectOfPathsAndPatterns { diff --git a/packages/rule-tester/src/types/DependencyConstraint.ts b/packages/rule-tester/src/types/DependencyConstraint.ts index c271e8bba3ff..e4ab6824000f 100644 --- a/packages/rule-tester/src/types/DependencyConstraint.ts +++ b/packages/rule-tester/src/types/DependencyConstraint.ts @@ -10,8 +10,8 @@ export interface SemverVersionConstraint { readonly range: string; } export type AtLeastVersionConstraint = - | `${number}.${number}.${number}` | `${number}.${number}.${number}-${string}` + | `${number}.${number}.${number}` | `${number}.${number}` | `${number}`; export type VersionConstraint = diff --git a/packages/scope-manager/src/referencer/Referencer.ts b/packages/scope-manager/src/referencer/Referencer.ts index 0d1231c611c5..1f621d8f7823 100644 --- a/packages/scope-manager/src/referencer/Referencer.ts +++ b/packages/scope-manager/src/referencer/Referencer.ts @@ -90,9 +90,7 @@ export class Referencer extends Visitor { } } public currentScope(): Scope; - public currentScope(throwOnNull: true): Scope | null; - public currentScope(dontThrowOnNull?: true): Scope | null { if (!dontThrowOnNull) { assert(this.scopeManager.currentScope, 'aaa'); diff --git a/packages/type-utils/src/isUnsafeAssignment.ts b/packages/type-utils/src/isUnsafeAssignment.ts index 41fcc8aab1c1..c54f7ef5b7c6 100644 --- a/packages/type-utils/src/isUnsafeAssignment.ts +++ b/packages/type-utils/src/isUnsafeAssignment.ts @@ -21,7 +21,7 @@ export function isUnsafeAssignment( receiver: ts.Type, checker: ts.TypeChecker, senderNode: TSESTree.Node | null, -): { receiver: ts.Type; sender: ts.Type } | false { +): false | { receiver: ts.Type; sender: ts.Type } { return isUnsafeAssignmentWorker( type, receiver, @@ -37,7 +37,7 @@ function isUnsafeAssignmentWorker( checker: ts.TypeChecker, senderNode: TSESTree.Node | null, visited: Map>, -): { receiver: ts.Type; sender: ts.Type } | false { +): false | { receiver: ts.Type; sender: ts.Type } { if (isTypeAnyType(type)) { // Allow assignment of any ==> unknown. if (isTypeUnknownType(receiver)) { diff --git a/packages/types/src/parser-options.ts b/packages/types/src/parser-options.ts index 6cf485d9653f..e90c42fa640a 100644 --- a/packages/types/src/parser-options.ts +++ b/packages/types/src/parser-options.ts @@ -8,7 +8,6 @@ export type DebugLevel = export type CacheDurationSeconds = number | 'Infinity'; export type EcmaVersion = - | 'latest' | 3 | 5 | 6 @@ -33,6 +32,7 @@ export type EcmaVersion = | 2023 | 2024 | 2025 + | 'latest' | undefined; export type SourceTypeClassic = 'module' | 'script'; diff --git a/packages/utils/src/ts-eslint/Rule.ts b/packages/utils/src/ts-eslint/Rule.ts index 35cd1c7f7a0c..3b40254b0a6e 100644 --- a/packages/utils/src/ts-eslint/Rule.ts +++ b/packages/utils/src/ts-eslint/Rule.ts @@ -766,11 +766,11 @@ export type AnyRuleModuleWithMetaDocs = RuleModuleWithMetaDocs< */ export type LooseRuleDefinition = // TODO - remove RuleCreateFunction once we no longer support ESLint 8 + | LooseRuleCreateFunction | { create: LooseRuleCreateFunction; meta?: object | undefined; - } - | LooseRuleCreateFunction; + }; /* eslint-disable-next-line @typescript-eslint/no-explicit-any -- intentionally using `any` to allow bi-directional assignment (unknown and diff --git a/packages/utils/src/ts-eslint/SourceCode.ts b/packages/utils/src/ts-eslint/SourceCode.ts index f4afb79f2dad..7d932a7eeff0 100644 --- a/packages/utils/src/ts-eslint/SourceCode.ts +++ b/packages/utils/src/ts-eslint/SourceCode.ts @@ -416,6 +416,7 @@ namespace SourceCode { export type CursorWithSkipOptions = | number + | FilterPredicate | { /** * The predicate function to choose tokens. @@ -429,11 +430,11 @@ namespace SourceCode { * The count of tokens the cursor skips. */ skip?: number; - } - | FilterPredicate; + }; export type CursorWithCountOptions = | number + | FilterPredicate | { /** * The maximum count of tokens the cursor iterates. @@ -447,8 +448,7 @@ namespace SourceCode { * The flag to iterate comments as well. */ includeComments?: boolean; - } - | FilterPredicate; + }; } class SourceCode extends (ESLintSourceCode as typeof SourceCodeBase) {} diff --git a/packages/website/src/components/layout/EditorTabs.tsx b/packages/website/src/components/layout/EditorTabs.tsx index a093a57791c7..50f11a9b9f78 100644 --- a/packages/website/src/components/layout/EditorTabs.tsx +++ b/packages/website/src/components/layout/EditorTabs.tsx @@ -10,7 +10,7 @@ export interface EditorTabsProps { readonly change: (name: T) => void; readonly showModal?: (name: T) => void; readonly showVisualEditor?: boolean; - readonly tabs: ({ label: string; value: T } | T)[]; + readonly tabs: (T | { label: string; value: T })[]; } function EditorTabs({ diff --git a/packages/website/src/theme/MDXComponents/index.tsx b/packages/website/src/theme/MDXComponents/index.tsx index 3efbee2d2408..624cdea67c10 100644 --- a/packages/website/src/theme/MDXComponents/index.tsx +++ b/packages/website/src/theme/MDXComponents/index.tsx @@ -1,5 +1,5 @@ -import Admonition from '@theme/Admonition'; import MDXComponents from '@theme-original/MDXComponents'; +import Admonition from '@theme/Admonition'; import { BaseRuleReference } from './BaseRuleReference'; import { HiddenHeading } from './HiddenHeading'; diff --git a/packages/website/webpack.plugin.js b/packages/website/webpack.plugin.js index c49dee7706af..be18ae1179a2 100644 --- a/packages/website/webpack.plugin.js +++ b/packages/website/webpack.plugin.js @@ -1,9 +1,9 @@ // eslint-disable-next-line @typescript-eslint/no-require-imports -const webpack = require('webpack'); +const CopyPlugin = require('copy-webpack-plugin'); // eslint-disable-next-line @typescript-eslint/no-require-imports const path = require('node:path'); // eslint-disable-next-line @typescript-eslint/no-require-imports -const CopyPlugin = require('copy-webpack-plugin'); +const webpack = require('webpack'); module.exports = function (/*context, options*/) { return { diff --git a/tools/scripts/generate-contributors.mts b/tools/scripts/generate-contributors.mts index d380f0382012..d051ab6c34e0 100644 --- a/tools/scripts/generate-contributors.mts +++ b/tools/scripts/generate-contributors.mts @@ -70,7 +70,7 @@ async function getData( async function* fetchUsers(page = 1): AsyncIterableIterator { while (page <= MATCH_PAGE_NUMBER) { console.log(`Fetching page ${page} of contributors...`); - const contributors = await getData<{ message: string } | Contributor[]>( + const contributors = await getData( `${contributorsApiUrl}&page=${page}`, ); diff --git a/yarn.lock b/yarn.lock index 89d54984e2db..eb4c70f161e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5675,7 +5675,7 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/types@8.32.1, @typescript-eslint/types@^8.9.0, @typescript-eslint/types@workspace:*, @typescript-eslint/types@workspace:^, @typescript-eslint/types@workspace:packages/types": +"@typescript-eslint/types@8.32.1, @typescript-eslint/types@^8.32.1, @typescript-eslint/types@workspace:*, @typescript-eslint/types@workspace:^, @typescript-eslint/types@workspace:packages/types": version: 0.0.0-use.local resolution: "@typescript-eslint/types@workspace:packages/types" dependencies: @@ -5725,7 +5725,7 @@ __metadata: eslint-plugin-import: ^2.31.0 eslint-plugin-jsdoc: ^50.5.0 eslint-plugin-jsx-a11y: ^6.10.2 - eslint-plugin-perfectionist: ^3.9.1 + eslint-plugin-perfectionist: ^4.12.3 eslint-plugin-react: ^7.37.3 eslint-plugin-react-hooks: ^5.0.0 eslint-plugin-regexp: ^2.7.0 @@ -5774,7 +5774,7 @@ __metadata: languageName: unknown linkType: soft -"@typescript-eslint/utils@8.32.1, @typescript-eslint/utils@^8.9.0, @typescript-eslint/utils@workspace:*, @typescript-eslint/utils@workspace:^, @typescript-eslint/utils@workspace:packages/utils": +"@typescript-eslint/utils@8.32.1, @typescript-eslint/utils@^8.32.1, @typescript-eslint/utils@workspace:*, @typescript-eslint/utils@workspace:^, @typescript-eslint/utils@workspace:packages/utils": version: 0.0.0-use.local resolution: "@typescript-eslint/utils@workspace:packages/utils" dependencies: @@ -9642,30 +9642,16 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-perfectionist@npm:^3.9.1": - version: 3.9.1 - resolution: "eslint-plugin-perfectionist@npm:3.9.1" +"eslint-plugin-perfectionist@npm:^4.12.3": + version: 4.13.0 + resolution: "eslint-plugin-perfectionist@npm:4.13.0" dependencies: - "@typescript-eslint/types": ^8.9.0 - "@typescript-eslint/utils": ^8.9.0 - minimatch: ^9.0.5 - natural-compare-lite: ^1.4.0 + "@typescript-eslint/types": ^8.32.1 + "@typescript-eslint/utils": ^8.32.1 + natural-orderby: ^5.0.0 peerDependencies: - astro-eslint-parser: ^1.0.2 - eslint: ">=8.0.0" - svelte: ">=3.0.0" - svelte-eslint-parser: ^0.41.1 - vue-eslint-parser: ">=9.0.0" - peerDependenciesMeta: - astro-eslint-parser: - optional: true - svelte: - optional: true - svelte-eslint-parser: - optional: true - vue-eslint-parser: - optional: true - checksum: 12bdc52586dd379bb174cb90c11d280173da88c079a3100eb2676ba9e8abf02e3989706fb06fc774331dc61909be27a0ba5c7ac591e9a777c793d7f8e5eb187d + eslint: ">=8.45.0" + checksum: 8dacb19fcd1cb1c5362c8715f72946aaed91be4f35c3cf4465d8e82a0a1b436bd6e7c36973cfc8e25221413d3a9a0d1e73901d40cca295e91a0d61dcdb0f9aa6 languageName: node linkType: hard @@ -14605,13 +14591,6 @@ __metadata: languageName: node linkType: hard -"natural-compare-lite@npm:^1.4.0": - version: 1.4.0 - resolution: "natural-compare-lite@npm:1.4.0" - checksum: 5222ac3986a2b78dd6069ac62cbb52a7bf8ffc90d972ab76dfe7b01892485d229530ed20d0c62e79a6b363a663b273db3bde195a1358ce9e5f779d4453887225 - languageName: node - linkType: hard - "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -14619,6 +14598,13 @@ __metadata: languageName: node linkType: hard +"natural-orderby@npm:^5.0.0": + version: 5.0.0 + resolution: "natural-orderby@npm:5.0.0" + checksum: bfdc62b91fb1b08bd3a8a9b66ec3476c17f867544f34778ce85344ffcff257342d632e94bc125c97f02b89c746479edd62c8727a310c94cab1a1d4a016eeeb46 + languageName: node + linkType: hard + "negotiator@npm:0.6.3, negotiator@npm:^0.6.3": version: 0.6.3 resolution: "negotiator@npm:0.6.3"