From 5c79f4637b0c11488df2f9935b26f461dd2c4f44 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Sat, 3 Aug 2024 01:03:24 +0200 Subject: [PATCH] Update to flat config, ESLint 9, and typescript-eslint 8 Fixes #87 --- eslint.config.js | 1 + index.js | 1302 +++++++++++++++++++++++----------------------- package.json | 26 +- readme.md | 55 +- test/_x.ts | 0 test/test.js | 13 +- tsconfig.json | 5 +- 7 files changed, 694 insertions(+), 708 deletions(-) create mode 100644 eslint.config.js delete mode 100644 test/_x.ts diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..0e8a9cc --- /dev/null +++ b/eslint.config.js @@ -0,0 +1 @@ +export {default} from './index.js'; diff --git a/index.js b/index.js index 193641a..71a7c8f 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,5 @@ -'use strict'; +import typescriptEslint from 'typescript-eslint'; +import jsConfig from 'eslint-config-xo'; const getNamingConventionRule = ({isTsx}) => ({ '@typescript-eslint/naming-convention': [ @@ -74,675 +75,674 @@ const getNamingConventionRule = ({isTsx}) => ({ ] }); -module.exports = { - parser: require.resolve('@typescript-eslint/parser'), - parserOptions: { - warnOnUnsupportedTypeScriptVersion: false, - sourceType: 'module', - project: 'tsconfig.json', - ecmaFeatures: { - jsx: true +const rules = { + '@typescript-eslint/adjacent-overload-signatures': 'error', + '@typescript-eslint/array-type': [ + 'error', + { + default: 'array-simple' } - }, - plugins: [ - '@typescript-eslint' - ], - settings: { - 'import/resolver': { - node: { - extensions: [ - '.js', - '.jsx', - '.ts', - '.tsx' - ] - } - }, - 'import/parsers': { - [require.resolve('@typescript-eslint/parser')]: [ - '.ts', - '.tsx' - ] + ], + '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/ban-ts-comment': [ + 'error', + { + 'ts-expect-error': 'allow-with-description', + minimumDescriptionLength: 4 } - }, - rules: { - '@typescript-eslint/adjacent-overload-signatures': 'error', - '@typescript-eslint/array-type': [ - 'error', - { - default: 'array-simple' - } - ], - '@typescript-eslint/await-thenable': 'error', - '@typescript-eslint/ban-ts-comment': [ - 'error', - { - 'ts-expect-error': 'allow-with-description', - minimumDescriptionLength: 4 - } - ], - '@typescript-eslint/ban-tslint-comment': 'error', - '@typescript-eslint/ban-types': [ - 'error', - { - extendDefaults: false, - types: { - '{}': { - message: 'The `{}` type is mostly the same as `unknown`. You probably want `Record` instead.', - fixWith: 'Record' - }, - object: { - message: 'The `object` type is hard to use. Use `Record` instead. See: https://github.com/typescript-eslint/typescript-eslint/pull/848', - fixWith: 'Record' - }, - null: { - message: 'Use `undefined` instead. See: https://github.com/sindresorhus/meta/issues/7', - fixWith: 'undefined' - }, - Buffer: { - message: 'Use Uint8Array instead. See: https://sindresorhus.com/blog/goodbye-nodejs-buffer', - suggest: [ - 'Uint8Array' - ] - }, - '[]': 'Don\'t use the empty array type `[]`. It only allows empty arrays. Use `SomeType[]` instead.', - '[[]]': 'Don\'t use `[[]]`. It only allows an array with a single element which is an empty array. Use `SomeType[][]` instead.', - '[[[]]]': 'Don\'t use `[[[]]]`. Use `SomeType[][][]` instead.', - '[[[[]]]]': 'ur drunk 🤡', - '[[[[[]]]]]': '🦄💥' - } - } - ], - '@typescript-eslint/class-literal-property-style': [ - 'error', - 'getters' - ], - '@typescript-eslint/consistent-generic-constructors': [ - 'error', - 'constructor' - ], - '@typescript-eslint/consistent-indexed-object-style': 'error', - 'brace-style': 'off', - '@typescript-eslint/brace-style': [ - 'error', - '1tbs', - { - allowSingleLine: false - } - ], - 'comma-dangle': 'off', - '@typescript-eslint/comma-dangle': [ - 'error', - 'always-multiline' - ], - 'comma-spacing': 'off', - '@typescript-eslint/comma-spacing': [ - 'error', - { - before: false, - after: true - } - ], - 'default-param-last': 'off', - '@typescript-eslint/default-param-last': 'error', - 'dot-notation': 'off', - '@typescript-eslint/dot-notation': 'error', - '@typescript-eslint/consistent-type-assertions': [ - 'error', - { - assertionStyle: 'as', - objectLiteralTypeAssertions: 'allow-as-parameter' - } - ], - '@typescript-eslint/consistent-type-definitions': [ - 'error', - 'type' - ], - '@typescript-eslint/consistent-type-exports': [ - 'error', - { - fixMixedExportsWithInlineTypeSpecifier: true + ], + '@typescript-eslint/ban-tslint-comment': 'error', + '@typescript-eslint/no-restricted-types': [ + 'error', + { + types: { + object: { + message: 'The `object` type is hard to use. Use `Record` instead. See: https://github.com/typescript-eslint/typescript-eslint/pull/848', + fixWith: 'Record' + }, + null: { + message: 'Use `undefined` instead. See: https://github.com/sindresorhus/meta/issues/7', + fixWith: 'undefined' + }, + Buffer: { + message: 'Use Uint8Array instead. See: https://sindresorhus.com/blog/goodbye-nodejs-buffer', + suggest: [ + 'Uint8Array' + ] + }, + '[]': 'Don\'t use the empty array type `[]`. It only allows empty arrays. Use `SomeType[]` instead.', + '[[]]': 'Don\'t use `[[]]`. It only allows an array with a single element which is an empty array. Use `SomeType[][]` instead.', + '[[[]]]': 'Don\'t use `[[[]]]`. Use `SomeType[][][]` instead.', + '[[[[]]]]': 'ur drunk 🤡', + '[[[[[]]]]]': '🦄💥' } - ], - '@typescript-eslint/consistent-type-imports': [ - 'error', - { - fixStyle: 'inline-type-imports' + } + ], + '@typescript-eslint/class-literal-property-style': [ + 'error', + 'getters' + ], + '@typescript-eslint/consistent-generic-constructors': [ + 'error', + 'constructor' + ], + '@typescript-eslint/consistent-indexed-object-style': 'error', + 'brace-style': 'off', + '@stylistic/brace-style': [ + 'error', + '1tbs', + { + allowSingleLine: false + } + ], + 'comma-dangle': 'off', + '@stylistic/comma-dangle': [ + 'error', + 'always-multiline' + ], + 'comma-spacing': 'off', + '@stylistic/comma-spacing': [ + 'error', + { + before: false, + after: true + } + ], + 'default-param-last': 'off', + '@typescript-eslint/default-param-last': 'error', + 'dot-notation': 'off', + '@typescript-eslint/dot-notation': 'error', + '@typescript-eslint/consistent-type-assertions': [ + 'error', + { + assertionStyle: 'as', + objectLiteralTypeAssertions: 'allow-as-parameter' + } + ], + '@typescript-eslint/consistent-type-definitions': [ + 'error', + 'type' + ], + '@typescript-eslint/consistent-type-exports': [ + 'error', + { + fixMixedExportsWithInlineTypeSpecifier: true + } + ], + '@typescript-eslint/consistent-type-imports': [ + 'error', + { + fixStyle: 'inline-type-imports' + } + ], + + // Disabled because it's too annoying. Enable it when it's more mature, smarter, and more flexible. + // https://github.com/typescript-eslint/typescript-eslint/search?q=%22explicit-function-return-type%22&state=open&type=Issues + // '@typescript-eslint/explicit-function-return-type': [ + // 'error', + // { + // allowExpressions: true, + // allowTypedFunctionExpressions: true, + // allowHigherOrderFunctions: true, + // allowConciseArrowFunctionExpressionsStartingWithVoid: false, + // allowIIFE: true + // } + // ], + + // TODO: This rule should be removed if/when we enable `@typescript-eslint/explicit-function-return-type`. + // Disabled for now as it has too many false-positives. + // https://github.com/typescript-eslint/typescript-eslint/search?q=%22explicit-module-boundary-types%22&state=open&type=Issues + // '@typescript-eslint/explicit-module-boundary-types': [ + // 'error', + // { + // allowTypedFunctionExpressions: true, + // allowHigherOrderFunctions: true, + // allowDirectConstAssertionInArrowFunctions: true, + // shouldTrackReferences: true + // } + // ], + + 'func-call-spacing': 'off', + '@stylistic/func-call-spacing': [ + 'error', + 'never' + ], + indent: 'off', + '@stylistic/indent': [ + 'error', + 'tab', + { + SwitchCase: 1 + } + ], + 'keyword-spacing': 'off', + '@stylistic/keyword-spacing': 'error', + 'lines-between-class-members': 'off', + '@stylistic/lines-between-class-members': [ + 'error', + 'always', + { + // Workaround to allow class fields to not have lines between them. + // TODO: Get ESLint to add an option to ignore class fields. + exceptAfterSingleLine: true + } + ], + '@stylistic/member-delimiter-style': [ + 'error', + { + multiline: { + delimiter: 'semi', + requireLast: true + }, + singleline: { + delimiter: 'semi', + requireLast: false } - ], + } + ], + '@typescript-eslint/member-ordering': [ + 'error', + { + default: [ + 'signature', + + 'public-static-field', + 'public-static-method', + + 'protected-static-field', + 'protected-static-method', + + 'private-static-field', + 'private-static-method', + + 'static-field', + 'static-method', + + 'public-decorated-field', + 'public-instance-field', + 'public-abstract-field', + 'public-field', + + 'protected-decorated-field', + 'protected-instance-field', + 'protected-abstract-field', + 'protected-field', + + 'private-decorated-field', + 'private-instance-field', + 'private-field', + + 'instance-field', + 'abstract-field', + 'decorated-field', + 'field', + + 'public-constructor', + 'protected-constructor', + 'private-constructor', + 'constructor', + + 'public-decorated-method', + 'public-instance-method', + 'public-abstract-method', + 'public-method', + + 'protected-decorated-method', + 'protected-instance-method', + 'protected-abstract-method', + 'protected-method', + + 'private-decorated-method', + 'private-instance-method', + 'private-method', + + 'instance-method', + 'abstract-method', + 'decorated-method', + 'method' + ] + } + ], - // Disabled because it's too annoying. Enable it when it's more mature, smarter, and more flexible. - // https://github.com/typescript-eslint/typescript-eslint/search?q=%22explicit-function-return-type%22&state=open&type=Issues - // '@typescript-eslint/explicit-function-return-type': [ - // 'error', - // { - // allowExpressions: true, - // allowTypedFunctionExpressions: true, - // allowHigherOrderFunctions: true, - // allowConciseArrowFunctionExpressionsStartingWithVoid: false, - // allowIIFE: true - // } - // ], - - // TODO: This rule should be removed if/when we enable `@typescript-eslint/explicit-function-return-type`. - // Disabled for now as it has too many false-positives. - // https://github.com/typescript-eslint/typescript-eslint/search?q=%22explicit-module-boundary-types%22&state=open&type=Issues - // '@typescript-eslint/explicit-module-boundary-types': [ - // 'error', - // { - // allowTypedFunctionExpressions: true, - // allowHigherOrderFunctions: true, - // allowDirectConstAssertionInArrowFunctions: true, - // shouldTrackReferences: true - // } - // ], + // Disabled for now as it causes too many weird TypeScript issues. I'm not sure whether the problems are caused by bugs in TS or problems in my types. + // TODO: Try to re-enable this again in 2026. + // '@typescript-eslint/method-signature-style': 'error', + + // We use `@typescript-eslint/naming-convention` in favor of `camelcase`. + camelcase: 'off', + // Known issues: + // - https://github.com/typescript-eslint/typescript-eslint/issues/1485 + // - https://github.com/typescript-eslint/typescript-eslint/issues/1484 + // TODO: Prevent `_` prefix on private fields when TypeScript 3.8 is out. + ...getNamingConventionRule({isTsx: false}), + '@typescript-eslint/no-base-to-string': 'error', + 'no-array-constructor': 'off', + '@typescript-eslint/no-array-constructor': 'error', + '@typescript-eslint/no-array-delete': 'error', + 'no-dupe-class-members': 'off', + '@typescript-eslint/no-dupe-class-members': 'error', + '@typescript-eslint/no-confusing-void-expression': 'error', + '@typescript-eslint/no-duplicate-enum-values': 'error', + '@typescript-eslint/no-duplicate-type-constituents': 'error', + '@typescript-eslint/no-dynamic-delete': 'error', + 'no-empty-function': 'off', + '@typescript-eslint/no-empty-function': 'error', + '@typescript-eslint/no-empty-interface': [ + 'error', + { + allowSingleExtends: true + } + ], + '@typescript-eslint/no-empty-object-type': 'error', + + // TODO: Try to enable this again in 2025. + // Disabled for now. This is a great rule. It's just that TypeScript is not good enough yet to not use `any` in many places. + // For example: https://github.com/sindresorhus/refined-github/pull/2391#discussion_r318995182 + // '@typescript-eslint/no-explicit-any': [ + // 'error', + // { + // fixToUnknown: true, + // ignoreRestArgs: true + // } + // ], + + '@typescript-eslint/no-extra-non-null-assertion': 'error', + + // Disabled because it's buggy. It transforms `...(personalToken ? {Authorization: `token ${personalToken}`} : {})` into `...personalToken ? {Authorization: `token ${personalToken}`} : {}` which is not valid. + // https://github.com/typescript-eslint/typescript-eslint/search?q=%22no-extra-parens%22&state=open&type=Issues + 'no-extra-parens': 'off', + // '@typescript-eslint/no-extra-parens': [ + // 'error', + // 'all', + // { + // conditionalAssign: false, + // nestedBinaryExpressions: false, + // ignoreJSX: 'multi-line' + // } + // ], + + 'no-extra-semi': 'off', + '@stylistic/no-extra-semi': 'error', + 'no-loop-func': 'off', + '@typescript-eslint/no-loop-func': 'error', + '@typescript-eslint/no-extraneous-class': [ + 'error', + { + allowConstructorOnly: false, + allowEmpty: false, + allowStaticOnly: false, + allowWithDecorator: true + } + ], + 'no-void': [ + 'error', + { + allowAsStatement: true // To allow `ignoreVoid` in `@typescript-eslint/no-floating-promises` + } + ], + '@typescript-eslint/no-floating-promises': [ + 'error', + { + checkThenables: true, + ignoreVoid: true, // Prepend a function call with `void` to mark it as not needing to be await'ed, which silences this rule. + ignoreIIFE: true + } + ], + '@typescript-eslint/no-for-in-array': 'error', + '@typescript-eslint/no-inferrable-types': 'error', - 'func-call-spacing': 'off', - '@typescript-eslint/func-call-spacing': [ - 'error', - 'never' - ], - indent: 'off', - '@typescript-eslint/indent': [ - 'error', - 'tab', - { - SwitchCase: 1 - } - ], - 'keyword-spacing': 'off', - '@typescript-eslint/keyword-spacing': 'error', - 'lines-between-class-members': 'off', - '@typescript-eslint/lines-between-class-members': [ - 'error', - 'always', - { - // Workaround to allow class fields to not have lines between them. - // TODO: Get ESLint to add an option to ignore class fields. - exceptAfterSingleLine: true - } - ], - '@typescript-eslint/member-delimiter-style': [ - 'error', - { - multiline: { - delimiter: 'semi', - requireLast: true - }, - singleline: { - delimiter: 'semi', - requireLast: false - } - } - ], - '@typescript-eslint/member-ordering': [ - 'error', - { - default: [ - 'signature', - - 'public-static-field', - 'public-static-method', - - 'protected-static-field', - 'protected-static-method', - - 'private-static-field', - 'private-static-method', - - 'static-field', - 'static-method', - - 'public-decorated-field', - 'public-instance-field', - 'public-abstract-field', - 'public-field', - - 'protected-decorated-field', - 'protected-instance-field', - 'protected-abstract-field', - 'protected-field', - - 'private-decorated-field', - 'private-instance-field', - 'private-field', - - 'instance-field', - 'abstract-field', - 'decorated-field', - 'field', - - 'public-constructor', - 'protected-constructor', - 'private-constructor', - 'constructor', - - 'public-decorated-method', - 'public-instance-method', - 'public-abstract-method', - 'public-method', - - 'protected-decorated-method', - 'protected-instance-method', - 'protected-abstract-method', - 'protected-method', - - 'private-decorated-method', - 'private-instance-method', - 'private-method', - - 'instance-method', - 'abstract-method', - 'decorated-method', - 'method' - ] - } - ], + // Disabled for now as it has too many false-positives. + // '@typescript-eslint/no-invalid-void-type': 'error', - // Disabled for now as it causes too many weird TypeScript issues. I'm not sure whether the problems are caused by bugs in TS or problems in my types. - // TODO: Try to re-enable this again in 2026. - // '@typescript-eslint/method-signature-style': 'error', - - // We use `@typescript-eslint/naming-convention` in favor of `camelcase`. - camelcase: 'off', - // Known issues: - // - https://github.com/typescript-eslint/typescript-eslint/issues/1485 - // - https://github.com/typescript-eslint/typescript-eslint/issues/1484 - // TODO: Prevent `_` prefix on private fields when TypeScript 3.8 is out. - ...getNamingConventionRule({isTsx: false}), - '@typescript-eslint/no-base-to-string': 'error', - 'no-array-constructor': 'off', - '@typescript-eslint/no-array-constructor': 'error', - '@typescript-eslint/no-array-delete': 'error', - 'no-dupe-class-members': 'off', - '@typescript-eslint/no-dupe-class-members': 'error', - '@typescript-eslint/no-confusing-void-expression': 'error', - '@typescript-eslint/no-duplicate-enum-values': 'error', - '@typescript-eslint/no-duplicate-type-constituents': 'error', - '@typescript-eslint/no-dynamic-delete': 'error', - 'no-empty-function': 'off', - '@typescript-eslint/no-empty-function': 'error', - '@typescript-eslint/no-empty-interface': [ - 'error', - { - allowSingleExtends: true - } - ], + '@typescript-eslint/no-meaningless-void-operator': 'error', + '@typescript-eslint/no-misused-new': 'error', + '@typescript-eslint/no-misused-promises': [ + 'error', + { + checksConditionals: true, - // TODO: Try to enable this again in 2025. - // Disabled for now. This is a great rule. It's just that TypeScript is not good enough yet to not use `any` in many places. - // For example: https://github.com/sindresorhus/refined-github/pull/2391#discussion_r318995182 - // '@typescript-eslint/no-explicit-any': [ - // 'error', - // { - // fixToUnknown: true, - // ignoreRestArgs: true - // } - // ], - - '@typescript-eslint/no-extra-non-null-assertion': 'error', - - // Disabled because it's buggy. It transforms `...(personalToken ? {Authorization: `token ${personalToken}`} : {})` into `...personalToken ? {Authorization: `token ${personalToken}`} : {}` which is not valid. - // https://github.com/typescript-eslint/typescript-eslint/search?q=%22no-extra-parens%22&state=open&type=Issues - 'no-extra-parens': 'off', - // '@typescript-eslint/no-extra-parens': [ - // 'error', - // 'all', - // { - // conditionalAssign: false, - // nestedBinaryExpressions: false, - // ignoreJSX: 'multi-line' - // } - // ], - - 'no-extra-semi': 'off', - '@typescript-eslint/no-extra-semi': 'error', - 'no-loop-func': 'off', - '@typescript-eslint/no-loop-func': 'error', - 'no-loss-of-precision': 'off', - '@typescript-eslint/no-loss-of-precision': 'error', - '@typescript-eslint/no-extraneous-class': [ - 'error', - { - allowConstructorOnly: false, - allowEmpty: false, - allowStaticOnly: false, - allowWithDecorator: true - } - ], - 'no-void': [ - 'error', - { - allowAsStatement: true // To allow `ignoreVoid` in `@typescript-eslint/no-floating-promises` - } - ], - '@typescript-eslint/no-floating-promises': [ - 'error', - { - checkThenables: true, - ignoreVoid: true, // Prepend a function call with `void` to mark it as not needing to be await'ed, which silences this rule. - ignoreIIFE: true - } - ], - '@typescript-eslint/no-for-in-array': 'error', - '@typescript-eslint/no-inferrable-types': 'error', - - // Disabled for now as it has too many false-positives. - // '@typescript-eslint/no-invalid-void-type': 'error', - - '@typescript-eslint/no-meaningless-void-operator': 'error', - '@typescript-eslint/no-misused-new': 'error', - '@typescript-eslint/no-misused-promises': [ - 'error', - { - checksConditionals: true, - - // TODO: I really want this to be `true`, but it makes it inconvenient to use - // async functions as event handlers... I need to find a good way to handle that. - // https://github.com/sindresorhus/refined-github/pull/2391#discussion_r318990466 - checksVoidReturn: false - } - ], - '@typescript-eslint/no-namespace': 'error', - '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', - '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', - - // Disabled for now. There are just too many places where you need to use it because of incorrect types, for example, the Node.js types. - // TODO: Try to enable this again in 2023. - // '@typescript-eslint/no-non-null-assertion': 'error', - - 'no-redeclare': 'off', - '@typescript-eslint/no-redeclare': 'error', - 'no-restricted-imports': 'off', - '@typescript-eslint/no-restricted-imports': [ - 'error', - { - paths: [ - 'error', - 'domain', - 'freelist', - 'smalloc', - 'punycode', - 'sys', - 'querystring', - 'colors' - ], - }, - ], + // TODO: I really want this to be `true`, but it makes it inconvenient to use + // async functions as event handlers... I need to find a good way to handle that. + // https://github.com/sindresorhus/refined-github/pull/2391#discussion_r318990466 + checksVoidReturn: false + } + ], + '@typescript-eslint/no-namespace': 'error', + '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + + // Disabled for now. There are just too many places where you need to use it because of incorrect types, for example, the Node.js types. + // TODO: Try to enable this again in 2023. + // '@typescript-eslint/no-non-null-assertion': 'error', + + 'no-redeclare': 'off', + '@typescript-eslint/no-redeclare': 'error', + 'no-restricted-imports': 'off', + '@typescript-eslint/no-restricted-imports': [ + 'error', + { + paths: [ + 'error', + 'domain', + 'freelist', + 'smalloc', + 'punycode', + 'sys', + 'querystring', + 'colors' + ], + }, + ], - // The rule is buggy and keeps inferring `any` for types that are not `any`. Just a lot of false-positives. - // '@typescript-eslint/no-redundant-type-constituents': 'error', + // The rule is buggy and keeps inferring `any` for types that are not `any`. Just a lot of false-positives. + // '@typescript-eslint/no-redundant-type-constituents': 'error', - '@typescript-eslint/no-require-imports': 'error', - '@typescript-eslint/no-this-alias': [ - 'error', - { - allowDestructuring: true - } - ], - 'no-throw-literal': 'off', - '@typescript-eslint/only-throw-error': [ - 'error', - { - // This should ideally be `false`, but it makes rethrowing errors inconvenient. There should be a separate `allowRethrowingUnknown` option. - allowThrowingUnknown: true, - allowThrowingAny: false - } - ], - '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', - - // `no-unnecessary-condition` is essentially a stricter version of `no-constant-condition`, but that isn't currently enabled - 'no-constant-condition': 'error', - - // TODO: Try to enable this again in 2025 *if* the following are resolved: - // - https://github.com/microsoft/TypeScript/issues/36393 - // - The rule needs a way to ignore runtime type-checks: https://github.com/sindresorhus/refined-github/pull/3168 - // - Run the rule on https://github.com/sindresorhus/refined-github and ensure there are no false-positives - // - // Also related: https://github.com/typescript-eslint/typescript-eslint/issues/1798 - // Also disable `no-constant-condition` when this is enabled - // '@typescript-eslint/no-unnecessary-condition': 'error', - - '@typescript-eslint/no-unnecessary-parameter-property-assignment': 'error', - '@typescript-eslint/no-unnecessary-qualifier': 'error', - '@typescript-eslint/no-unnecessary-type-arguments': 'error', - '@typescript-eslint/no-unnecessary-type-assertion': 'error', - '@typescript-eslint/no-unnecessary-type-constraint': 'error', - - // TODO: Enable at some point. Currently disabled because it's marked as unstable. - // https://typescript-eslint.io/rules/no-unnecessary-type-parameters/ - // '@typescript-eslint/no-unnecessary-type-parameters': 'error', - - '@typescript-eslint/no-unsafe-argument': 'error', - '@typescript-eslint/no-unsafe-assignment': 'error', - '@typescript-eslint/no-unsafe-call': 'error', - '@typescript-eslint/no-unsafe-declaration-merging': 'error', - '@typescript-eslint/no-unsafe-enum-comparison': 'error', - '@typescript-eslint/no-unsafe-function-type': 'error', - - // Disabled until TypeScrpt supports the `node:` protocol. - // '@typescript-eslint/no-unsafe-member-access': 'error', - - '@typescript-eslint/no-unsafe-return': 'error', - '@typescript-eslint/no-useless-empty-export': 'error', - 'no-unused-expressions': 'off', - '@typescript-eslint/no-unused-expressions': 'error', - 'no-unused-vars': 'off', - // NOTE: TypeScript already catches unused variables. Let us know if there's something this rule catches that TypeScript does not. - // '@typescript-eslint/no-unused-vars': [ - // 'error', - // { - // vars: 'all', - // args: 'after-used', - // ignoreRestSiblings: true, - // argsIgnorePattern: /^_/.source, - // caughtErrors: 'all', - // caughtErrorsIgnorePattern: /^_$/.source - // } - // ], - 'no-useless-constructor': 'off', - '@typescript-eslint/no-useless-constructor': 'error', - 'object-curly-spacing': 'off', - '@typescript-eslint/object-curly-spacing': [ - 'error', - 'never' - ], - 'padding-line-between-statements': 'off', - '@typescript-eslint/padding-line-between-statements': [ - 'error', - { - blankLine: 'always', - prev: 'multiline-block-like', - next: '*' - } - ], - '@typescript-eslint/no-var-requires': 'error', - '@typescript-eslint/no-wrapper-object-types': 'error', - '@typescript-eslint/non-nullable-type-assertion-style': 'error', - '@typescript-eslint/parameter-properties': [ - 'error', - { - prefer: 'parameter-property' - } - ], - '@typescript-eslint/prefer-as-const': 'error', - '@typescript-eslint/prefer-find': 'error', - '@typescript-eslint/prefer-for-of': 'error', - '@typescript-eslint/prefer-function-type': 'error', - '@typescript-eslint/prefer-includes': 'error', - '@typescript-eslint/prefer-literal-enum-member': 'error', - '@typescript-eslint/prefer-namespace-keyword': 'error', - '@typescript-eslint/prefer-nullish-coalescing': [ - 'error', - { - ignoreTernaryTests: false, - ignoreConditionalTests: false, - ignoreMixedLogicalExpressions: false - } - ], - '@typescript-eslint/prefer-optional-chain': 'error', - 'prefer-promise-reject-errors': 'off', - '@typescript-eslint/prefer-promise-reject-errors': 'error', - '@typescript-eslint/prefer-readonly': 'error', - - // TODO: Try to enable this again in 2023. - // Disabled for now as it's too annoying and will cause too much churn. It also has bugs: https://github.com/typescript-eslint/typescript-eslint/search?q=%22prefer-readonly-parameter-types%22+is:issue&state=open&type=issues - // '@typescript-eslint/prefer-readonly-parameter-types': [ - // 'error', - // { - // checkParameterProperties: true, - // ignoreInferredTypes: true - // } - // ], - - '@typescript-eslint/prefer-reduce-type-parameter': 'error', - '@typescript-eslint/prefer-string-starts-ends-with': 'error', - '@typescript-eslint/promise-function-async': 'error', - quotes: 'off', - '@typescript-eslint/quotes': [ - 'error', - 'single' - ], - '@typescript-eslint/restrict-plus-operands': [ - 'error', - { - allowAny: false - } - ], - '@typescript-eslint/restrict-template-expressions': [ - 'error', - { - allowNumber: true - } - ], - '@typescript-eslint/return-await': 'error', - '@typescript-eslint/require-array-sort-compare': [ - 'error', - { - ignoreStringArrays: true - } - ], + '@typescript-eslint/no-require-imports': 'error', + '@typescript-eslint/no-this-alias': [ + 'error', + { + allowDestructuring: true + } + ], + 'no-throw-literal': 'off', + '@typescript-eslint/only-throw-error': [ + 'error', + { + // This should ideally be `false`, but it makes rethrowing errors inconvenient. There should be a separate `allowRethrowingUnknown` option. + allowThrowingUnknown: true, + allowThrowingAny: false + } + ], + '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', + + // `no-unnecessary-condition` is essentially a stricter version of `no-constant-condition`, but that isn't currently enabled + 'no-constant-condition': 'error', + + // TODO: Try to enable this again in 2025 *if* the following are resolved: + // - https://github.com/microsoft/TypeScript/issues/36393 + // - The rule needs a way to ignore runtime type-checks: https://github.com/sindresorhus/refined-github/pull/3168 + // - Run the rule on https://github.com/sindresorhus/refined-github and ensure there are no false-positives + // + // Also related: https://github.com/typescript-eslint/typescript-eslint/issues/1798 + // Also disable `no-constant-condition` when this is enabled + // '@typescript-eslint/no-unnecessary-condition': 'error', + + '@typescript-eslint/no-unnecessary-parameter-property-assignment': 'error', + '@typescript-eslint/no-unnecessary-qualifier': 'error', + '@typescript-eslint/no-unnecessary-type-arguments': 'error', + '@typescript-eslint/no-unnecessary-type-assertion': 'error', + '@typescript-eslint/no-unnecessary-type-constraint': 'error', + + // TODO: Enable at some point. Currently disabled because it's marked as unstable. + // https://typescript-eslint.io/rules/no-unnecessary-type-parameters/ + // '@typescript-eslint/no-unnecessary-type-parameters': 'error', + + '@typescript-eslint/no-unsafe-argument': 'error', + '@typescript-eslint/no-unsafe-assignment': 'error', + '@typescript-eslint/no-unsafe-call': 'error', + '@typescript-eslint/no-unsafe-declaration-merging': 'error', + '@typescript-eslint/no-unsafe-enum-comparison': 'error', + '@typescript-eslint/no-unsafe-function-type': 'error', + + // Disabled until TypeScrpt supports the `node:` protocol. + // '@typescript-eslint/no-unsafe-member-access': 'error', + + '@typescript-eslint/no-unsafe-return': 'error', + '@typescript-eslint/no-useless-empty-export': 'error', + 'no-unused-expressions': 'off', + '@typescript-eslint/no-unused-expressions': 'error', + 'no-unused-vars': 'off', + // NOTE: TypeScript already catches unused variables. Let us know if there's something this rule catches that TypeScript does not. + // '@typescript-eslint/no-unused-vars': [ + // 'error', + // { + // vars: 'all', + // args: 'after-used', + // ignoreRestSiblings: true, + // argsIgnorePattern: /^_/.source, + // caughtErrors: 'all', + // caughtErrorsIgnorePattern: /^_$/.source + // } + // ], + 'no-useless-constructor': 'off', + '@typescript-eslint/no-useless-constructor': 'error', + 'object-curly-spacing': 'off', + '@stylistic/object-curly-spacing': [ + 'error', + 'never' + ], + 'padding-line-between-statements': 'off', + '@stylistic/padding-line-between-statements': [ + 'error', + { + blankLine: 'always', + prev: 'multiline-block-like', + next: '*' + } + ], + '@typescript-eslint/no-wrapper-object-types': 'error', + '@typescript-eslint/non-nullable-type-assertion-style': 'error', + '@typescript-eslint/parameter-properties': [ + 'error', + { + prefer: 'parameter-property' + } + ], + '@typescript-eslint/prefer-as-const': 'error', + '@typescript-eslint/prefer-find': 'error', + '@typescript-eslint/prefer-for-of': 'error', + '@typescript-eslint/prefer-function-type': 'error', + '@typescript-eslint/prefer-includes': 'error', + '@typescript-eslint/prefer-literal-enum-member': 'error', + '@typescript-eslint/prefer-namespace-keyword': 'error', + '@typescript-eslint/prefer-nullish-coalescing': [ + 'error', + { + ignoreTernaryTests: false, + ignoreConditionalTests: false, + ignoreMixedLogicalExpressions: false + } + ], + '@typescript-eslint/prefer-optional-chain': 'error', + 'prefer-promise-reject-errors': 'off', + '@typescript-eslint/prefer-promise-reject-errors': 'error', + '@typescript-eslint/prefer-readonly': 'error', + + // TODO: Try to enable this again in 2023. + // Disabled for now as it's too annoying and will cause too much churn. It also has bugs: https://github.com/typescript-eslint/typescript-eslint/search?q=%22prefer-readonly-parameter-types%22+is:issue&state=open&type=issues + // '@typescript-eslint/prefer-readonly-parameter-types': [ + // 'error', + // { + // checkParameterProperties: true, + // ignoreInferredTypes: true + // } + // ], + + '@typescript-eslint/prefer-reduce-type-parameter': 'error', + '@typescript-eslint/prefer-string-starts-ends-with': 'error', + '@typescript-eslint/promise-function-async': 'error', + quotes: 'off', + '@stylistic/quotes': [ + 'error', + 'single' + ], + '@typescript-eslint/restrict-plus-operands': [ + 'error', + { + allowAny: false + } + ], + '@typescript-eslint/restrict-template-expressions': [ + 'error', + { + allowNumber: true + } + ], + '@typescript-eslint/return-await': 'error', + '@typescript-eslint/require-array-sort-compare': [ + 'error', + { + ignoreStringArrays: true + } + ], - // Disabled for now. It's too buggy. It fails to detect when try/catch is used, await inside blocks, etc. It's also common to have async functions without await for various reasons. - // 'require-await': 'off', - // '@typescript-eslint/require-await': 'error', - - 'space-before-function-paren': 'off', - '@typescript-eslint/space-before-function-paren': [ - 'error', - { - anonymous: 'always', - named: 'never', - asyncArrow: 'always' - } - ], - 'space-infix-ops': 'off', - '@typescript-eslint/space-infix-ops': 'error', - semi: 'off', - '@typescript-eslint/semi': [ - 'error', - 'always' - ], - 'space-before-blocks': 'off', - '@typescript-eslint/space-before-blocks': [ - 'error', - 'always' - ], + // Disabled for now. It's too buggy. It fails to detect when try/catch is used, await inside blocks, etc. It's also common to have async functions without await for various reasons. + // 'require-await': 'off', + // '@typescript-eslint/require-await': 'error', - // TODO: Reconsider enabling it again in 2023. - // NOTE: The rule was complete redone in typescript-eslint v3, so this config needs to be changed before this is enabled. - // Disabled for now as it's too strict. - // Relevant discussion: https://github.com/sindresorhus/refined-github/pull/2521#discussion_r343013852 - // '@typescript-eslint/strict-boolean-expressions': [ - // 'error', - // { - // allowNullable: true, - // allowSafe: true - // } - // ], - - 'default-case': 'off', // It conflicts with `@typescript-eslint/switch-exhaustiveness-check`. It would still be nice to have this rule for non-exhaustive switches though. - '@typescript-eslint/switch-exhaustiveness-check': [ - 'error', - { - allowDefaultCaseForExhaustiveSwitch: false, - requireDefaultForNonUnion: true - } - ], - '@typescript-eslint/triple-slash-reference': [ - 'error', - { - path: 'never', - types: 'never', - lib: 'never' - } - ], - '@typescript-eslint/type-annotation-spacing': 'error', - - // Disabled as it crashes on most code. - // https://github.com/typescript-eslint/typescript-eslint/search?q=%22unbound-method%22&state=open&type=Issues - // '@typescript-eslint/unbound-method': [ - // 'error', - // { - // ignoreStatic: true - // } - // ], - - '@typescript-eslint/prefer-regexp-exec': 'error', - '@typescript-eslint/prefer-return-this-type': 'error', - '@typescript-eslint/unified-signatures': [ - 'error', - { - ignoreDifferentlyNamedParameters: true - } - ], - '@typescript-eslint/use-unknown-in-catch-callback-variable': 'error', + 'space-before-function-paren': 'off', + '@stylistic/space-before-function-paren': [ + 'error', + { + anonymous: 'always', + named: 'never', + asyncArrow: 'always' + } + ], + 'space-infix-ops': 'off', + '@stylistic/space-infix-ops': 'error', + semi: 'off', + '@stylistic/semi': [ + 'error', + 'always' + ], + 'space-before-blocks': 'off', + '@stylistic/space-before-blocks': [ + 'error', + 'always' + ], + + // TODO: Reconsider enabling it again in 2023. + // NOTE: The rule was complete redone in typescript-eslint v3, so this config needs to be changed before this is enabled. + // Disabled for now as it's too strict. + // Relevant discussion: https://github.com/sindresorhus/refined-github/pull/2521#discussion_r343013852 + // '@typescript-eslint/strict-boolean-expressions': [ + // 'error', + // { + // allowNullable: true, + // allowSafe: true + // } + // ], + + 'default-case': 'off', // It conflicts with `@typescript-eslint/switch-exhaustiveness-check`. It would still be nice to have this rule for non-exhaustive switches though. + '@typescript-eslint/switch-exhaustiveness-check': [ + 'error', + { + allowDefaultCaseForExhaustiveSwitch: false, + requireDefaultForNonUnion: true + } + ], + '@typescript-eslint/triple-slash-reference': [ + 'error', + { + path: 'never', + types: 'never', + lib: 'never' + } + ], + '@stylistic/type-annotation-spacing': 'error', + + // Disabled as it crashes on most code. + // https://github.com/typescript-eslint/typescript-eslint/search?q=%22unbound-method%22&state=open&type=Issues + // '@typescript-eslint/unbound-method': [ + // 'error', + // { + // ignoreStatic: true + // } + // ], + + '@typescript-eslint/prefer-regexp-exec': 'error', + '@typescript-eslint/prefer-return-this-type': 'error', + '@typescript-eslint/unified-signatures': [ + 'error', + { + ignoreDifferentlyNamedParameters: true + } + ], + '@typescript-eslint/use-unknown-in-catch-callback-variable': 'error', - // Disabled per typescript-eslint recommendation: https://github.com/typescript-eslint/typescript-eslint/blob/e26e43ffba96f6d46198b22f1c8dd5c814db2652/docs/getting-started/linting/FAQ.md#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors - 'no-undef': 'off', + // Disabled per typescript-eslint recommendation: https://github.com/typescript-eslint/typescript-eslint/blob/e26e43ffba96f6d46198b22f1c8dd5c814db2652/docs/getting-started/linting/FAQ.md#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors + 'no-undef': 'off', - // TypeScript might have features not supported in a specific Node.js version. - 'node/no-unsupported-features/es-syntax': 'off', - 'node/no-unsupported-features/es-builtins': 'off', + // TypeScript might have features not supported in a specific Node.js version. + 'node/no-unsupported-features/es-syntax': 'off', + 'node/no-unsupported-features/es-builtins': 'off', - // Even though we already use `@typescript-eslint/ban-types`, `unicorn/no-null` is useful for catching literal usage. - // https://github.com/xojs/eslint-config-xo-typescript/issues/69 - // 'unicorn/no-null': 'off', + // Even though we already use `@typescript-eslint/no-restricted-types`, `unicorn/no-null` is useful for catching literal usage. + // https://github.com/xojs/eslint-config-xo-typescript/issues/69 + // 'unicorn/no-null': 'off', - // The rule is buggy with TS and it's not needed as TS already enforces valid imports and references at compile-time. - 'import/namespace': 'off', + // The rule is buggy with TS and it's not needed as TS already enforces valid imports and references at compile-time. + 'import/namespace': 'off', - // TypeScript already does a better job at this. - 'import/named': 'off', + // TypeScript already does a better job at this. + 'import/named': 'off', - // `import/no-duplicates` works better with TypeScript. - 'no-duplicate-imports': 'off' - }, - overrides: [ - { - files: [ - '**/*.d.ts' - ], - rules: { - '@typescript-eslint/no-unused-vars': 'off' - } + // `import/no-duplicates` works better with TypeScript. + 'no-duplicate-imports': 'off' +}; + +export default typescriptEslint.config( + ...jsConfig, + { + plugins: { + '@typescript-eslint': typescriptEslint.plugin }, - { - files: [ - '**/*.test-d.ts' - ], - rules: { - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-confusing-void-expression': 'off' // Conflicts with `expectError` assertion. + languageOptions: { + sourceType: 'module', + parser: typescriptEslint.parser, + parserOptions: { + projectService: true, + warnOnUnsupportedTypeScriptVersion: false, + ecmaFeatures: { + jsx: true + } } }, - { - files: [ - '**/*.tsx' - ], - rules: { - ...getNamingConventionRule({isTsx: true}) - } + // Disabled temporarily. + // settings: { + // 'import/resolver': { + // node: { + // extensions: [ + // '.js', + // '.jsx', + // '.ts', + // '.tsx' + // ] + // } + // }, + // 'import/parsers': { + // [require.resolve('@typescript-eslint/parser')]: [ + // '.ts', + // '.tsx' + // ] + // } + // }, + rules + }, + { + files: [ + '**/*.d.ts' + ], + rules: { + '@typescript-eslint/no-unused-vars': 'off' } - ] -}; + }, + { + files: [ + '**/*.test-d.ts' + ], + rules: { + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-confusing-void-expression': 'off' // Conflicts with `expectError` assertion. + } + }, + { + files: [ + '**/*.tsx' + ], + rules: { + ...getNamingConventionRule({isTsx: true}) + } + } +); diff --git a/package.json b/package.json index 025f672..7b1794f 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,14 @@ "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, + "type": "module", + "exports": { + ".": "./index.js", + "./space": "space.js" + }, "sideEffects": false, "engines": { - "node": ">=18" + "node": ">=18.18" }, "scripts": { "test": "ava" @@ -44,17 +49,18 @@ "hint", "simple" ], + "dependencies": { + "@stylistic/eslint-plugin": "^2.6.1", + "eslint-config-xo": "^0.46.0", + "typescript-eslint": "^8.0.0" + }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", - "ava": "^6.1.2", - "eslint": "^8.56.0", - "typescript": "^5.3.3" + "ava": "^6.1.3", + "eslint": "^9.8.0", + "typescript": "^5.5.4" }, "peerDependencies": { - "@typescript-eslint/eslint-plugin": ">=7.17.0", - "@typescript-eslint/parser": ">=7.17.0", - "eslint": ">=8.56.0", - "typescript": ">=5.0.0" + "eslint": ">=9.8.0", + "typescript": ">=5.5.0" } } diff --git a/readme.md b/readme.md index 09bb8be..dca9060 100644 --- a/readme.md +++ b/readme.md @@ -1,13 +1,15 @@ # eslint-config-xo-typescript -> ESLint [shareable config](https://eslint.org/docs/developer-guide/shareable-configs.html) for TypeScript to be used with [eslint-config-xo](https://github.com/xojs/eslint-config-xo) +> ESLint [shareable config](https://eslint.org/docs/developer-guide/shareable-configs.html) for TypeScript + +This config also includes [eslint-config-xo](https://github.com/xojs/eslint-config-xo). **This config assumes your project is [ESM](https://nodejs.org/api/esm.html) and that you use a [strict config](https://github.com/sindresorhus/tsconfig/blob/main/tsconfig.json).** ## Install ```sh -npm install --save-dev eslint-config-xo eslint-config-xo-typescript @typescript-eslint/parser @typescript-eslint/eslint-plugin +npm install --save-dev eslint-config-xo-typescript ``` ## Use with XO @@ -18,50 +20,25 @@ npm install --save-dev eslint-config-xo eslint-config-xo-typescript @typescript- Add some ESLint config to your package.json (or `.eslintrc`): -```json -{ - "name": "my-awesome-project", - "eslintConfig": { - "extends": [ - "xo", - "xo-typescript" - ] - } -} +```js +// eslint.config.js +import xoTypeScript from 'eslint-plugin-xo-typescript'; + +export default [ + ...xoTypeScript, +]; ``` Use the `space` sub-config if you want 2 space indentation instead of tabs: -```json -{ - "name": "my-awesome-project", - "eslintConfig": { - "extends": [ - "xo", - "xo-typescript/space" - ] - } -} -``` +```js +import xoTypeScriptSpace from 'eslint-plugin-xo-typescript/space'; -*Note:* If your `tsconfig.json` is not in the same directory as `package.json`, you will have to set the path yourself: - -```json -{ - "name": "my-awesome-project", - "eslintConfig": { - "extends": [ - "xo", - "xo-typescript" - ], - "parserOptions": { - "project": "some-path/tsconfig.json" - } - } -} +export default [ + ...xoTypeScriptSpace, +]; ``` ## Related -- [eslint-config-xo](https://github.com/xojs/eslint-config-xo) - ESLint shareable config for XO - [XO](https://github.com/xojs/xo) diff --git a/test/_x.ts b/test/_x.ts deleted file mode 100644 index e69de29..0000000 diff --git a/test/test.js b/test/test.js index 7bb9f58..4046e07 100644 --- a/test/test.js +++ b/test/test.js @@ -1,23 +1,22 @@ -const test = require('ava'); -const {ESLint} = require('eslint'); - -const config = require('../index.js'); +import test from 'ava'; +import {ESLint} from 'eslint'; +import config from '../index.js'; const hasRule = (errors, ruleId) => errors.some(error => error.ruleId === ruleId); async function runEslint(string, config) { const eslint = new ESLint({ - useEslintrc: false, + overrideConfigFile: true, overrideConfig: config, }); - const [firstResult] = await eslint.lintText(string, {filePath: 'test/_x.ts'}); + const [firstResult] = await eslint.lintText(string, {filePath: 'test/test.js'}); return firstResult.messages; } test('main', async t => { - const errors = await runEslint('const foo: number = 5;', config); + const errors = await runEslint('const foo: number = 5;\n', config); t.true(hasRule(errors, '@typescript-eslint/no-inferrable-types'), JSON.stringify(errors)); t.is(errors.length, 1); }); diff --git a/tsconfig.json b/tsconfig.json index fa135f5..6925a9a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,10 @@ { "compilerOptions": { - "strict": true, + "strict": true }, + "files": [ + "test/test.js" + ], "include": [ "test" ]