From ee587e3e099786edc08887a5c5e24d5eb3945c1f Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Wed, 15 Nov 2023 20:50:01 +1030 Subject: [PATCH 01/10] feat: add support for flat configs --- .cspell.json | 2 + .eslintignore | 17 - .eslintrc.js | 457 --------------- .github/renovate.json5 | 5 + .github/workflows/ci.yml | 2 + .vscode/settings.json | 2 + docs/packages/Core.mdx | 12 + docs/packages/Parser.mdx | 10 +- eslint.config.js | 8 + eslint.config.mjs | 533 ++++++++++++++++++ nx.json | 8 +- package.json | 4 + packages/ast-spec/project.json | 5 +- packages/eslint-plugin-internal/index.d.ts | 9 + packages/eslint-plugin-internal/package.json | 1 + packages/eslint-plugin-internal/project.json | 5 +- packages/eslint-plugin-internal/src/index.ts | 10 + packages/eslint-plugin-internal/tsconfig.json | 2 +- packages/eslint-plugin-tslint/project.json | 5 +- .../eslint-plugin/eslint-recommended-raw.d.ts | 5 + packages/eslint-plugin/index.d.ts | 6 +- packages/eslint-plugin/package.json | 4 + packages/eslint-plugin/project.json | 5 +- packages/eslint-plugin/rules.d.ts | 1 + packages/eslint-plugin/src/configs/base.ts | 7 - .../src/configs/disable-type-checked.ts | 2 +- .../src/configs/eslint-recommended-raw.ts | 44 ++ .../src/configs/eslint-recommended.ts | 30 +- packages/eslint-plugin/src/index.ts | 10 + packages/integration-tests/project.json | 5 +- packages/parser/project.json | 5 +- packages/parser/src/parser.ts | 3 +- packages/repo-tools/package.json | 1 + packages/repo-tools/project.json | 5 +- packages/repo-tools/src/generate-configs.mts | 131 +++-- packages/repo-tools/src/generate-sponsors.mts | 2 +- packages/repo-tools/src/paths.mts | 4 + packages/repo-tools/tsconfig.build.json | 3 + .../project.json | 5 +- packages/scope-manager/project.json | 5 +- packages/type-utils/project.json | 5 +- packages/types/project.json | 5 +- packages/types/src/parser-options.ts | 4 +- .../typescript-eslint/{LICENCE => LICENSE} | 0 packages/typescript-eslint/jest.config.js | 8 + packages/typescript-eslint/package.json | 1 - packages/typescript-eslint/project.json | 5 +- .../typescript-eslint/src/config-helper.ts | 86 +++ packages/typescript-eslint/src/configs/all.ts | 165 ++++++ .../typescript-eslint/src/configs/base.ts | 14 + .../src/configs/disable-type-checked.ts | 67 +++ .../src/configs/eslint-recommended.ts | 12 + .../src/configs/recommended-type-checked.ts | 64 +++ .../src/configs/recommended.ts | 43 ++ .../src/configs/strict-type-checked.ts | 89 +++ .../typescript-eslint/src/configs/strict.ts | 53 ++ .../src/configs/stylistic-type-checked.ts | 45 ++ .../src/configs/stylistic.ts | 39 ++ packages/typescript-eslint/src/index.ts | 45 +- .../typescript-eslint/tests/configs.test.ts | 213 +++++++ packages/typescript-estree/project.json | 5 +- .../parseSettings/getProjectConfigFiles.ts | 7 +- .../typescript-estree/src/parser-options.ts | 2 +- packages/utils/project.json | 5 +- packages/utils/src/ts-eslint/Config.ts | 18 +- packages/utils/src/ts-eslint/Linter.ts | 4 +- packages/utils/src/ts-eslint/Parser.ts | 35 +- packages/visitor-keys/project.json | 5 +- packages/website-eslint/package.json | 2 +- packages/website-eslint/project.json | 5 +- packages/website/project.json | 5 +- tsconfig.base.json | 9 +- tsconfig.eslint.json => tsconfig.json | 2 + typings/eslint-plugin-eslint-comments.d.ts | 14 + typings/eslint-plugin-eslint-plugin.d.ts | 19 + typings/eslint-plugin-import.d.ts | 21 + typings/eslint-plugin-jest.d.ts | 21 + typings/eslint-plugin-jsdoc.d.ts | 25 + typings/eslint-plugin-jsx-a11y.d.ts | 15 + typings/eslint-plugin-react-hooks.d.ts | 14 + typings/eslint-plugin-react.d.ts | 16 + typings/eslint-plugin-simple-import-sort.d.ts | 8 + typings/eslint-plugin-unicorn.d.ts | 15 + typings/eslint__eslintrc.d.ts | 22 + typings/eslint__js.d.ts | 11 + yarn.lock | 57 +- 86 files changed, 2051 insertions(+), 654 deletions(-) delete mode 100644 .eslintignore delete mode 100644 .eslintrc.js create mode 100644 docs/packages/Core.mdx create mode 100644 eslint.config.js create mode 100644 eslint.config.mjs create mode 100644 packages/eslint-plugin-internal/index.d.ts create mode 100644 packages/eslint-plugin/eslint-recommended-raw.d.ts create mode 100644 packages/eslint-plugin/src/configs/eslint-recommended-raw.ts rename packages/typescript-eslint/{LICENCE => LICENSE} (100%) create mode 100644 packages/typescript-eslint/jest.config.js create mode 100644 packages/typescript-eslint/src/config-helper.ts create mode 100644 packages/typescript-eslint/src/configs/all.ts create mode 100644 packages/typescript-eslint/src/configs/base.ts create mode 100644 packages/typescript-eslint/src/configs/disable-type-checked.ts create mode 100644 packages/typescript-eslint/src/configs/eslint-recommended.ts create mode 100644 packages/typescript-eslint/src/configs/recommended-type-checked.ts create mode 100644 packages/typescript-eslint/src/configs/recommended.ts create mode 100644 packages/typescript-eslint/src/configs/strict-type-checked.ts create mode 100644 packages/typescript-eslint/src/configs/strict.ts create mode 100644 packages/typescript-eslint/src/configs/stylistic-type-checked.ts create mode 100644 packages/typescript-eslint/src/configs/stylistic.ts create mode 100644 packages/typescript-eslint/tests/configs.test.ts rename tsconfig.eslint.json => tsconfig.json (88%) create mode 100644 typings/eslint-plugin-eslint-comments.d.ts create mode 100644 typings/eslint-plugin-eslint-plugin.d.ts create mode 100644 typings/eslint-plugin-import.d.ts create mode 100644 typings/eslint-plugin-jest.d.ts create mode 100644 typings/eslint-plugin-jsdoc.d.ts create mode 100644 typings/eslint-plugin-jsx-a11y.d.ts create mode 100644 typings/eslint-plugin-react-hooks.d.ts create mode 100644 typings/eslint-plugin-react.d.ts create mode 100644 typings/eslint-plugin-simple-import-sort.d.ts create mode 100644 typings/eslint-plugin-unicorn.d.ts create mode 100644 typings/eslint__eslintrc.d.ts create mode 100644 typings/eslint__js.d.ts diff --git a/.cspell.json b/.cspell.json index c5bd12a5c9a..99feed384c1 100644 --- a/.cspell.json +++ b/.cspell.json @@ -100,6 +100,8 @@ "noninteractive", "Nrwl", "nullish", + "nx", + "nx's", "onboarded", "OOM", "OOMs", diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 431bc803029..00000000000 --- a/.eslintignore +++ /dev/null @@ -1,17 +0,0 @@ -node_modules -dist -jest.config.js -fixtures -coverage -__snapshots__ -.docusaurus -build - -# Files copied as part of the build -packages/types/src/generated/**/*.ts - -# Playground types downloaded from the web -packages/website/src/vendor - -# see the file header in eslint-base.test.js for more info -packages/rule-tester/tests/eslint-base diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 1398b8f04d8..00000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,457 +0,0 @@ -// @ts-check -/** @type {import('./packages/utils/src/ts-eslint/Linter').Linter.Config} */ -module.exports = { - root: true, - plugins: [ - '@typescript-eslint', - '@typescript-eslint/internal', - 'deprecation', - 'eslint-comments', - 'eslint-plugin', - 'import', - 'jest', - 'jsdoc', - 'simple-import-sort', - 'unicorn', - ], - env: { - es2020: true, - node: true, - }, - extends: [ - 'eslint:recommended', - 'plugin:eslint-plugin/recommended', - 'plugin:jsdoc/recommended-typescript-error', - 'plugin:@typescript-eslint/strict-type-checked', - 'plugin:@typescript-eslint/stylistic-type-checked', - ], - parserOptions: { - allowAutomaticSingleRunInference: true, - cacheLifetime: { - // we pretty well never create/change tsconfig structure - so no need to ever evict the cache - // in the rare case that we do - just need to manually restart their IDE. - glob: 'Infinity', - }, - project: [ - './tsconfig.eslint.json', - './packages/*/tsconfig.json', - /** - * We are currently in the process of transitioning to nx's out of the box structure and - * so need to manually specify converted packages' tsconfig.build.json and tsconfig.spec.json - * files here for now in addition to the tsconfig.json glob pattern. - * - * TODO(#4665): Clean this up once all packages have been transitioned. - */ - './packages/scope-manager/tsconfig.build.json', - './packages/scope-manager/tsconfig.spec.json', - ], - tsconfigRootDir: __dirname, - }, - rules: { - // make sure we're not leveraging any deprecated APIs - 'deprecation/deprecation': 'error', - - // TODO(#7130): Investigate changing these in or removing these from presets - '@typescript-eslint/no-confusing-void-expression': 'off', - '@typescript-eslint/prefer-string-starts-ends-with': 'off', - - // - // our plugin :D - // - - '@typescript-eslint/ban-ts-comment': [ - 'error', - { - 'ts-expect-error': 'allow-with-description', - 'ts-ignore': true, - 'ts-nocheck': true, - 'ts-check': false, - minimumDescriptionLength: 5, - }, - ], - '@typescript-eslint/consistent-type-imports': [ - 'error', - { prefer: 'type-imports', disallowTypeAnnotations: true }, - ], - '@typescript-eslint/explicit-function-return-type': [ - 'error', - { allowIIFEs: true }, - ], - '@typescript-eslint/no-explicit-any': 'error', - '@typescript-eslint/no-non-null-assertion': 'off', - 'no-constant-condition': 'off', - '@typescript-eslint/no-unnecessary-condition': [ - 'error', - { allowConstantLoopConditions: true }, - ], - '@typescript-eslint/no-var-requires': 'off', - '@typescript-eslint/prefer-literal-enum-member': [ - 'error', - { - allowBitwiseExpressions: true, - }, - ], - '@typescript-eslint/unbound-method': 'off', - '@typescript-eslint/restrict-template-expressions': [ - 'error', - { - allowNumber: true, - allowBoolean: true, - allowAny: true, - allowNullish: true, - allowRegExp: true, - }, - ], - '@typescript-eslint/no-unused-vars': [ - 'error', - { varsIgnorePattern: '^_', argsIgnorePattern: '^_' }, - ], - '@typescript-eslint/prefer-nullish-coalescing': [ - 'error', - { - ignoreConditionalTests: true, - ignorePrimitives: true, - }, - ], - - // - // Internal repo rules - // - - '@typescript-eslint/internal/no-poorly-typed-ts-props': 'error', - '@typescript-eslint/internal/no-typescript-default-import': 'error', - '@typescript-eslint/internal/prefer-ast-types-enum': 'error', - - // - // eslint-base - // - - curly: ['error', 'all'], - eqeqeq: [ - 'error', - 'always', - { - null: 'never', - }, - ], - 'logical-assignment-operators': 'error', - 'no-else-return': 'error', - 'no-mixed-operators': 'error', - 'no-console': 'error', - 'no-process-exit': 'error', - 'no-fallthrough': [ - 'error', - { commentPattern: '.*intentional fallthrough.*' }, - ], - 'one-var': ['error', 'never'], - - // - // eslint-plugin-eslint-comment - // - - // require a eslint-enable comment for every eslint-disable comment - 'eslint-comments/disable-enable-pair': [ - 'error', - { - allowWholeFile: true, - }, - ], - // disallow a eslint-enable comment for multiple eslint-disable comments - 'eslint-comments/no-aggregating-enable': 'error', - // disallow duplicate eslint-disable comments - 'eslint-comments/no-duplicate-disable': 'error', - // disallow eslint-disable comments without rule names - 'eslint-comments/no-unlimited-disable': 'error', - // disallow unused eslint-disable comments - 'eslint-comments/no-unused-disable': 'error', - // disallow unused eslint-enable comments - 'eslint-comments/no-unused-enable': 'error', - // disallow ESLint directive-comments - 'eslint-comments/no-use': [ - 'error', - { - allow: [ - 'eslint-disable', - 'eslint-disable-line', - 'eslint-disable-next-line', - 'eslint-enable', - 'global', - ], - }, - ], - - // - // eslint-plugin-import - // - - // disallow non-import statements appearing before import statements - 'import/first': 'error', - // Require a newline after the last import/require in a group - 'import/newline-after-import': 'error', - // Forbid import of modules using absolute paths - 'import/no-absolute-path': 'error', - // disallow AMD require/define - 'import/no-amd': 'error', - // forbid default exports - we want to standardize on named exports so that imported names are consistent - 'import/no-default-export': 'error', - // disallow imports from duplicate paths - 'import/no-duplicates': 'error', - // Forbid the use of extraneous packages - 'import/no-extraneous-dependencies': [ - 'error', - { - devDependencies: true, - peerDependencies: true, - optionalDependencies: false, - }, - ], - // Forbid mutable exports - 'import/no-mutable-exports': 'error', - // Prevent importing the default as if it were named - 'import/no-named-default': 'error', - // Prohibit named exports - 'import/no-named-export': 'off', // we want everything to be a named export - // Forbid a module from importing itself - 'import/no-self-import': 'error', - // Require modules with a single export to use a default export - 'import/prefer-default-export': 'off', // we want everything to be named - - // enforce a sort order across the codebase - 'simple-import-sort/imports': 'error', - - // - // eslint-plugin-jsdoc - // - - // We often use @remarks or other ad-hoc tag names - 'jsdoc/check-tag-names': 'off', - // https://github.com/gajus/eslint-plugin-jsdoc/issues/1169 - 'jsdoc/check-param-names': 'off', - // https://github.com/gajus/eslint-plugin-jsdoc/issues/1175 - 'jsdoc/require-jsdoc': 'off', - 'jsdoc/require-param': 'off', - 'jsdoc/require-returns': 'off', - 'jsdoc/require-yields': 'off', - 'jsdoc/tag-lines': 'off', - - // - // eslint-plugin-unicorn - // - - 'unicorn/no-typeof-undefined': 'error', - }, - overrides: [ - { - files: ['*.js'], - extends: ['plugin:@typescript-eslint/disable-type-checked'], - rules: { - // turn off other type-aware rules - 'deprecation/deprecation': 'off', - '@typescript-eslint/internal/no-poorly-typed-ts-props': 'off', - - // turn off rules that don't apply to JS code - '@typescript-eslint/explicit-function-return-type': 'off', - }, - }, - // all test files - { - files: [ - './packages/*/tests/**/*.spec.ts', - './packages/*/tests/**/*.test.ts', - './packages/*/tests/**/spec.ts', - './packages/*/tests/**/test.ts', - './packages/parser/tests/**/*.ts', - './packages/integration-tests/tools/integration-test-base.ts', - './packages/integration-tests/tools/pack-packages.ts', - ], - env: { - 'jest/globals': true, - }, - rules: { - '@typescript-eslint/no-empty-function': [ - 'error', - { allow: ['arrowFunctions'] }, - ], - '@typescript-eslint/no-unsafe-assignment': 'off', - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - 'eslint-plugin/consistent-output': 'off', // Might eventually be removed from `eslint-plugin/recommended`: https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/issues/284 - 'jest/no-disabled-tests': 'error', - 'jest/no-focused-tests': 'error', - 'jest/no-alias-methods': 'error', - 'jest/no-identical-title': 'error', - 'jest/no-jasmine-globals': 'error', - 'jest/no-test-prefixes': 'error', - 'jest/no-done-callback': 'error', - 'jest/no-test-return-statement': 'error', - 'jest/prefer-to-be': 'error', - 'jest/prefer-to-contain': 'error', - 'jest/prefer-to-have-length': 'error', - 'jest/prefer-spy-on': 'error', - 'jest/valid-expect': 'error', - 'jest/no-deprecated-functions': 'error', - }, - }, - // test utility scripts and website js files - { - files: ['tests/**/*.js'], - rules: { - '@typescript-eslint/explicit-function-return-type': 'off', - '@typescript-eslint/no-unsafe-call': 'off', - '@typescript-eslint/no-unsafe-member-access': 'off', - '@typescript-eslint/no-unsafe-return': 'off', - '@typescript-eslint/restrict-plus-operands': 'off', - }, - }, - // plugin source files - { - files: [ - './packages/eslint-plugin-internal/**/*.ts', - './packages/eslint-plugin-tslint/**/*.ts', - './packages/eslint-plugin/**/*.ts', - ], - rules: { - '@typescript-eslint/internal/no-typescript-estree-import': 'error', - }, - }, - // plugin rule source files - { - files: [ - './packages/eslint-plugin-internal/src/rules/**/*.ts', - './packages/eslint-plugin-tslint/src/rules/**/*.ts', - './packages/eslint-plugin/src/configs/**/*.ts', - './packages/eslint-plugin/src/rules/**/*.ts', - ], - rules: { - 'eslint-plugin/require-meta-docs-description': [ - 'error', - { pattern: '^(Enforce|Require|Disallow) .+[^. ]$' }, - ], - - // specifically for rules - default exports makes the tooling easier - 'import/no-default-export': 'off', - - 'no-restricted-syntax': [ - 'error', - { - selector: - 'ExportDefaultDeclaration Property[key.name="create"] MemberExpression[object.name="context"][property.name="options"]', - message: - "Retrieve options from create's second parameter so that defaultOptions are applied.", - }, - ], - }, - }, - // plugin rule tests - { - files: [ - './packages/eslint-plugin-internal/tests/rules/**/*.test.ts', - './packages/eslint-plugin-tslint/tests/rules/**/*.test.ts', - './packages/eslint-plugin/tests/rules/**/*.test.ts', - './packages/eslint-plugin/tests/eslint-rules/**/*.test.ts', - ], - rules: { - '@typescript-eslint/internal/plugin-test-formatting': 'error', - }, - }, - // files which list all the things - { - files: ['./packages/eslint-plugin/src/rules/index.ts'], - rules: { - // enforce alphabetical ordering - 'sort-keys': 'error', - 'import/order': ['error', { alphabetize: { order: 'asc' } }], - }, - }, - // tools and tests - { - files: [ - '**/tools/**/*.*t*', - '**/tests/**/*.ts', - './packages/repo-tools/**/*.*t*', - './packages/integration-tests/**/*.*t*', - ], - rules: { - // allow console logs in tools and tests - 'no-console': 'off', - }, - }, - // generated files - { - files: [ - './packages/scope-manager/src/lib/*.ts', - './packages/eslint-plugin/src/configs/*.ts', - ], - rules: { - '@typescript-eslint/internal/no-poorly-typed-ts-props': 'off', - '@typescript-eslint/internal/no-typescript-default-import': 'off', - '@typescript-eslint/internal/prefer-ast-types-enum': 'off', - }, - }, - // ast spec specific standardization - { - files: ['./packages/ast-spec/src/**/*.ts'], - rules: { - // disallow ALL unused vars - '@typescript-eslint/no-unused-vars': 'error', - '@typescript-eslint/sort-type-constituents': 'error', - }, - }, - { - files: ['./packages/ast-spec/**/*.ts'], - rules: { - 'no-restricted-imports': [ - 'error', - { - name: '@typescript-eslint/typescript-estree', - message: - 'To prevent nx build errors, all `typescript-estree` imports should be done via `packages/ast-spec/tests/util/parsers/typescript-estree-import.ts`.', - }, - ], - }, - }, - { - files: ['rollup.config.ts'], - rules: { - 'import/no-default-export': 'off', - }, - }, - { - files: ['./packages/website/**/*.{ts,tsx,mts,cts,js,jsx}'], - extends: [ - 'plugin:jsx-a11y/recommended', - 'plugin:react/recommended', - 'plugin:react-hooks/recommended', - ], - plugins: ['jsx-a11y', 'react', 'react-hooks'], - rules: { - '@typescript-eslint/internal/prefer-ast-types-enum': 'off', - 'import/no-default-export': 'off', - 'react/jsx-no-target-blank': 'off', - 'react/no-unescaped-entities': 'off', - 'react-hooks/exhaustive-deps': 'warn', // TODO: enable it later - }, - settings: { - react: { - version: 'detect', - }, - }, - }, - { - files: ['./packages/website/src/**/*.{ts,tsx}'], - rules: { - 'import/no-default-export': 'off', - // allow console logs in the website to help with debugging things in production - 'no-console': 'off', - }, - }, - { - files: ['./packages/website-eslint/src/mock/**/*.js', '*.d.ts'], - rules: { - // mocks and declaration files have to mirror their original package - 'import/no-default-export': 'off', - }, - }, - ], -}; diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 255a9a7bc38..bed1f941a20 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -78,6 +78,11 @@ matchPackagePrefixes: ['@types/jest', 'jest', '@jest'], groupName: 'jest', }, + { + matchPackageNames: ['eslint'], + matchPackagePrefixes: ['@eslint'], + groupName: 'eslint', + }, ], postUpdateOptions: [ // run yarn dedupe to cleanup the lockfile after updates diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9951a2cc4bc..922e4fda6c6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -113,6 +113,8 @@ jobs: - name: Run Check run: yarn ${{ matrix.lint-task }} + env: + ESLINT_USE_FLAT_CONFIG: true stylelint: name: Stylelint diff --git a/.vscode/settings.json b/.vscode/settings.json index 896fbaebbb3..ac5f882cbc0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,6 +9,8 @@ "typescript", "typescriptreact" ], + // required to make the extension pickup the flat config + "eslint.experimental.useFlatConfig": true, // When enabled, will trim trailing whitespace when saving a file. "files.trimTrailingWhitespace": true, diff --git a/docs/packages/Core.mdx b/docs/packages/Core.mdx new file mode 100644 index 00000000000..d20d5dcd33d --- /dev/null +++ b/docs/packages/Core.mdx @@ -0,0 +1,12 @@ +--- +id: core +sidebar_label: core +--- + +# `@typescript-eslint/core` + +> Tooling which enables you to use TypeScript with ESLint + +This package is the main entrypoint that you can use to consume our tooling with ESLint. + +// TODO(bradzacher) - docs on using the tooling diff --git a/docs/packages/Parser.mdx b/docs/packages/Parser.mdx index 5d586049310..35f61e0d8a6 100644 --- a/docs/packages/Parser.mdx +++ b/docs/packages/Parser.mdx @@ -45,8 +45,8 @@ interface ParserOptions { jsxFragmentName?: string | null; jsxPragma?: string | null; lib?: string[]; - programs?: import('typescript').Program; - project?: string | string[] | true; + programs?: import('typescript').Program[]; + project?: string | string[] | true | boolean | null; projectFolderIgnoreList?: string[]; tsconfigRootDir?: string; warnOnUnsupportedTypeScriptVersion?: boolean; @@ -212,13 +212,17 @@ This option allows you to provide a path to your project's `tsconfig.json`. **Th // array of paths and/or glob patterns project: ['./packages/**/tsconfig.json', './separate-package/tsconfig.json']; + + // ways to disable type-aware linting (useful for overrides configs) + project: false; + project: null; ``` - If `true`, each source file's parse will find the nearest `tsconfig.json` file to that source file. - This is done by checking that source file's directory tree for the nearest `tsconfig.json`. -- If you use project references, TypeScript will not automatically use project references to resolve files. This means that you will have to add each referenced tsconfig to the `project` field either separately, or via a glob. +- If you use project references, TypeScript will **not** automatically use project references to resolve files. This means that you will have to add each referenced tsconfig to the `project` field either separately, or via a glob. - Note that using wide globs `**` in your `parserOptions.project` may cause performance implications. Instead of globs that use `**` to recursively check all folders, prefer paths that use a single `*` at a time. For more info see [#2611](https://github.com/typescript-eslint/typescript-eslint/issues/2611). diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000000..218cd0a70eb --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,8 @@ +// @ts-check + +// TODO - https://github.com/eslint/eslint/pull/17909 +// either it gets back-ported (https://github.com/eslint/eslint/issues/17966) or we wait till v9 + +/** @type {import('@typescript-eslint/utils/ts-eslint').FlatConfig.ConfigPromise} */ +const config = (async () => (await import('./eslint.config.mjs')).default)(); +module.exports = config; diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000000..8ebb01f6953 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,533 @@ +// @ts-check + +import url from 'node:url'; + +import { FlatCompat } from '@eslint/eslintrc'; +import eslint from '@eslint/js'; +import tseslintInternalPlugin from '@typescript-eslint/eslint-plugin-internal'; +import deprecationPlugin from 'eslint-plugin-deprecation'; +import eslintCommentsPlugin from 'eslint-plugin-eslint-comments'; +import eslintPluginPlugin from 'eslint-plugin-eslint-plugin'; +import importPlugin from 'eslint-plugin-import'; +import jestPlugin from 'eslint-plugin-jest'; +import jsdocPlugin from 'eslint-plugin-jsdoc'; +import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'; +import reactPlugin from 'eslint-plugin-react'; +import reactHooksPlugin from 'eslint-plugin-react-hooks'; +import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort'; +import unicornPlugin from 'eslint-plugin-unicorn'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; + +const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); +const compat = new FlatCompat({ baseDirectory: __dirname }); + +export default tseslint.config( + // register all of the plugins up-front + { + // note - intentionally uses computed syntax to make it easy to sort the keys + plugins: { + ['@typescript-eslint']: tseslint.plugin, + ['@typescript-eslint/internal']: tseslintInternalPlugin, + ['deprecation']: deprecationPlugin, + ['eslint-comments']: eslintCommentsPlugin, + ['eslint-plugin']: eslintPluginPlugin, + ['import']: importPlugin, + ['jest']: jestPlugin, + ['jsdoc']: jsdocPlugin, + ['jsx-a11y']: jsxA11yPlugin, + ['react-hooks']: reactHooksPlugin, + ['react']: reactPlugin, + ['simple-import-sort']: simpleImportSortPlugin, + ['unicorn']: unicornPlugin, + }, + }, + { + // config with just ignores is the replacement for `.eslintignore` + ignores: [ + '**/jest.config.js', + '**/node_modules/**', + '**/dist/**', + '**/fixtures/**', + '**/coverage/**', + '**/__snapshots__/**', + '**/.docusaurus/**', + '**/build/**', + // Files copied as part of the build + 'packages/types/src/generated/**/*.ts', + // Playground types downloaded from the web + 'packages/website/src/vendor', + // see the file header in eslint-base.test.js for more info + 'packages/rule-tester/tests/eslint-base', + ], + }, + + // extends ... + eslint.configs.recommended, + ...compat.config(eslintPluginPlugin.configs.recommended), + ...tseslint.configs.strictTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + jsdocPlugin.configs['flat/recommended-typescript-error'], + + // base config + { + languageOptions: { + globals: { + ...globals.es2020, + ...globals.node, + }, + parserOptions: { + allowAutomaticSingleRunInference: true, + cacheLifetime: { + // we pretty well never create/change tsconfig structure - so no need to ever evict the cache + // in the rare case that we do - just need to manually restart their IDE. + glob: 'Infinity', + }, + sourceType: 'module', + project: [ + 'tsconfig.json', + 'packages/*/tsconfig.json', + /** + * We are currently in the process of transitioning to nx's out of the box structure and + * so need to manually specify converted packages' tsconfig.build.json and tsconfig.spec.json + * files here for now in addition to the tsconfig.json glob pattern. + * + * TODO(#4665): Clean this up once all packages have been transitioned. + */ + 'packages/scope-manager/tsconfig.build.json', + 'packages/scope-manager/tsconfig.spec.json', + ], + tsconfigRootDir: __dirname, + warnOnUnsupportedTypeScriptVersion: false, + }, + }, + rules: { + // make sure we're not leveraging any deprecated APIs + 'deprecation/deprecation': 'error', + + // TODO(#7130): Investigate changing these in or removing these from presets + '@typescript-eslint/no-confusing-void-expression': 'off', + '@typescript-eslint/prefer-string-starts-ends-with': 'off', + + // + // our plugin :D + // + + '@typescript-eslint/ban-ts-comment': [ + 'error', + { + 'ts-expect-error': 'allow-with-description', + 'ts-ignore': true, + 'ts-nocheck': true, + 'ts-check': false, + minimumDescriptionLength: 5, + }, + ], + '@typescript-eslint/consistent-type-imports': [ + 'error', + { prefer: 'type-imports', disallowTypeAnnotations: true }, + ], + '@typescript-eslint/explicit-function-return-type': [ + 'error', + { allowIIFEs: true }, + ], + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-non-null-assertion': 'off', + 'no-constant-condition': 'off', + '@typescript-eslint/no-unnecessary-condition': [ + 'error', + { allowConstantLoopConditions: true }, + ], + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/prefer-literal-enum-member': [ + 'error', + { + allowBitwiseExpressions: true, + }, + ], + '@typescript-eslint/unbound-method': 'off', + '@typescript-eslint/restrict-template-expressions': [ + 'error', + { + allowNumber: true, + allowBoolean: true, + allowAny: true, + allowNullish: true, + allowRegExp: true, + }, + ], + '@typescript-eslint/no-unused-vars': [ + 'error', + { varsIgnorePattern: '^_', argsIgnorePattern: '^_' }, + ], + '@typescript-eslint/prefer-nullish-coalescing': [ + 'error', + { + ignoreConditionalTests: true, + ignorePrimitives: true, + }, + ], + + // + // Internal repo rules + // + + '@typescript-eslint/internal/no-poorly-typed-ts-props': 'error', + '@typescript-eslint/internal/no-typescript-default-import': 'error', + '@typescript-eslint/internal/prefer-ast-types-enum': 'error', + + // + // eslint-base + // + + curly: ['error', 'all'], + eqeqeq: [ + 'error', + 'always', + { + null: 'never', + }, + ], + 'logical-assignment-operators': 'error', + 'no-else-return': 'error', + 'no-mixed-operators': 'error', + 'no-console': 'error', + 'no-process-exit': 'error', + 'no-fallthrough': [ + 'error', + { commentPattern: '.*intentional fallthrough.*' }, + ], + 'one-var': ['error', 'never'], + + // + // eslint-plugin-eslint-comment + // + + // require a eslint-enable comment for every eslint-disable comment + 'eslint-comments/disable-enable-pair': [ + 'error', + { + allowWholeFile: true, + }, + ], + // disallow a eslint-enable comment for multiple eslint-disable comments + 'eslint-comments/no-aggregating-enable': 'error', + // disallow duplicate eslint-disable comments + 'eslint-comments/no-duplicate-disable': 'error', + // disallow eslint-disable comments without rule names + 'eslint-comments/no-unlimited-disable': 'error', + // disallow unused eslint-disable comments + 'eslint-comments/no-unused-disable': 'error', + // disallow unused eslint-enable comments + 'eslint-comments/no-unused-enable': 'error', + // disallow ESLint directive-comments + 'eslint-comments/no-use': [ + 'error', + { + allow: [ + 'eslint-disable', + 'eslint-disable-line', + 'eslint-disable-next-line', + 'eslint-enable', + 'global', + ], + }, + ], + + // + // eslint-plugin-import + // + + // disallow non-import statements appearing before import statements + 'import/first': 'error', + // Require a newline after the last import/require in a group + 'import/newline-after-import': 'error', + // Forbid import of modules using absolute paths + 'import/no-absolute-path': 'error', + // disallow AMD require/define + 'import/no-amd': 'error', + // forbid default exports - we want to standardize on named exports so that imported names are consistent + 'import/no-default-export': 'error', + // disallow imports from duplicate paths + 'import/no-duplicates': 'error', + // Forbid the use of extraneous packages + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: true, + peerDependencies: true, + optionalDependencies: false, + }, + ], + // Forbid mutable exports + 'import/no-mutable-exports': 'error', + // Prevent importing the default as if it were named + 'import/no-named-default': 'error', + // Prohibit named exports + 'import/no-named-export': 'off', // we want everything to be a named export + // Forbid a module from importing itself + 'import/no-self-import': 'error', + // Require modules with a single export to use a default export + 'import/prefer-default-export': 'off', // we want everything to be named + + // enforce a sort order across the codebase + 'simple-import-sort/imports': 'error', + + // + // eslint-plugin-jsdoc + // + + // We often use @remarks or other ad-hoc tag names + 'jsdoc/check-tag-names': 'off', + // https://github.com/gajus/eslint-plugin-jsdoc/issues/1169 + 'jsdoc/check-param-names': 'off', + // https://github.com/gajus/eslint-plugin-jsdoc/issues/1175 + 'jsdoc/require-jsdoc': 'off', + 'jsdoc/require-param': 'off', + 'jsdoc/require-returns': 'off', + 'jsdoc/require-yields': 'off', + 'jsdoc/tag-lines': 'off', + + // + // eslint-plugin-unicorn + // + + 'unicorn/no-typeof-undefined': 'error', + }, + }, + { + files: ['**/*.js'], + extends: [tseslint.configs.disableTypeChecked], + rules: { + // turn off other type-aware rules + 'deprecation/deprecation': 'off', + '@typescript-eslint/internal/no-poorly-typed-ts-props': 'off', + + // turn off rules that don't apply to JS code + '@typescript-eslint/explicit-function-return-type': 'off', + }, + }, + + // + // test file linting + // + + // define the jest globals for all test files + { + files: ['packages/*/tests/**/*.{ts,tsx,cts,mts}'], + languageOptions: { + globals: { + ...jestPlugin.environments.globals.globals, + }, + }, + }, + // test file specific configuration + { + files: [ + 'packages/*/tests/**/*.spec.{ts,tsx,cts,mts}', + 'packages/*/tests/**/*.test.{ts,tsx,cts,mts}', + 'packages/*/tests/**/spec.{ts,tsx,cts,mts}', + 'packages/*/tests/**/test.{ts,tsx,cts,mts}', + 'packages/parser/tests/**/*.{ts,tsx,cts,mts}', + 'packages/integration-tests/tools/integration-test-base.{ts,tsx,cts,mts}', + 'packages/integration-tests/tools/pack-packages.{ts,tsx,cts,mts}', + ], + rules: { + '@typescript-eslint/no-empty-function': [ + 'error', + { allow: ['arrowFunctions'] }, + ], + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + 'eslint-plugin/consistent-output': 'off', // Might eventually be removed from `eslint-plugin/recommended`: https://github.com/not-an-aardvark/eslint-plugin-eslint-plugin/issues/284 + 'jest/no-disabled-tests': 'error', + 'jest/no-focused-tests': 'error', + 'jest/no-alias-methods': 'error', + 'jest/no-identical-title': 'error', + 'jest/no-jasmine-globals': 'error', + 'jest/no-test-prefixes': 'error', + 'jest/no-done-callback': 'error', + 'jest/no-test-return-statement': 'error', + 'jest/prefer-to-be': 'error', + 'jest/prefer-to-contain': 'error', + 'jest/prefer-to-have-length': 'error', + 'jest/prefer-spy-on': 'error', + 'jest/valid-expect': 'error', + 'jest/no-deprecated-functions': 'error', + }, + }, + // plugin rule tests + { + files: [ + 'packages/eslint-plugin-internal/tests/rules/**/*.test.{ts,tsx,cts,mts}', + 'packages/eslint-plugin-tslint/tests/rules/**/*.test.{ts,tsx,cts,mts}', + 'packages/eslint-plugin/tests/rules/**/*.test.{ts,tsx,cts,mts}', + 'packages/eslint-plugin/tests/eslint-rules/**/*.test.{ts,tsx,cts,mts}', + ], + rules: { + '@typescript-eslint/internal/plugin-test-formatting': 'error', + }, + }, + + // + // tools and tests + // + { + files: [ + '**/tools/**/*.{ts,tsx,cts,mts}', + '**/tests/**/*.{ts,tsx,cts,mts}', + 'packages/repo-tools/**/*.{ts,tsx,cts,mts}', + 'packages/integration-tests/**/*.{ts,tsx,cts,mts}', + ], + rules: { + // allow console logs in tools and tests + 'no-console': 'off', + }, + }, + { + files: ['eslint.config.{js,cjs,mjs}'], + rules: { + // requirement + 'import/no-default-export': 'off', + }, + }, + + // + // plugin source file linting + // + + { + files: [ + 'packages/eslint-plugin-internal/**/*.{ts,tsx,cts,mts}', + 'packages/eslint-plugin-tslint/**/*.{ts,tsx,cts,mts}', + 'packages/eslint-plugin/**/*.{ts,tsx,cts,mts}', + ], + rules: { + '@typescript-eslint/internal/no-typescript-estree-import': 'error', + }, + }, + { + files: [ + 'packages/eslint-plugin-internal/src/rules/**/*.{ts,tsx,cts,mts}', + 'packages/eslint-plugin-tslint/src/rules/**/*.{ts,tsx,cts,mts}', + 'packages/eslint-plugin/src/configs/**/*.{ts,tsx,cts,mts}', + 'packages/core/src/configs/**/*.{ts,tsx,cts,mts}', + 'packages/eslint-plugin/src/rules/**/*.{ts,tsx,cts,mts}', + ], + rules: { + 'eslint-plugin/require-meta-docs-description': [ + 'error', + { pattern: '^(Enforce|Require|Disallow) .+[^. ]$' }, + ], + + // specifically for rules - default exports makes the tooling easier + 'import/no-default-export': 'off', + + 'no-restricted-syntax': [ + 'error', + { + selector: + 'ExportDefaultDeclaration Property[key.name="create"] MemberExpression[object.name="context"][property.name="options"]', + message: + "Retrieve options from create's second parameter so that defaultOptions are applied.", + }, + ], + }, + }, + { + files: ['packages/eslint-plugin/src/rules/index.ts'], + rules: { + // enforce alphabetical ordering + 'sort-keys': 'error', + 'import/order': ['error', { alphabetize: { order: 'asc' } }], + }, + }, + + // + // generated files + // + + { + files: [ + 'packages/scope-manager/src/lib/*.{ts,tsx,cts,mts}', + 'packages/eslint-plugin/src/configs/*.{ts,tsx,cts,mts}', + 'packages/core/src/configs/*.{ts,tsx,cts,mts}', + ], + rules: { + '@typescript-eslint/internal/no-poorly-typed-ts-props': 'off', + '@typescript-eslint/internal/no-typescript-default-import': 'off', + '@typescript-eslint/internal/prefer-ast-types-enum': 'off', + }, + }, + + // + // ast spec linting + // + + { + files: ['packages/ast-spec/src/**/*.{ts,tsx,cts,mts}'], + rules: { + // disallow ALL unused vars + '@typescript-eslint/no-unused-vars': 'error', + '@typescript-eslint/sort-type-constituents': 'error', + }, + }, + { + files: ['packages/ast-spec/**/*.{ts,tsx,cts,mts}'], + rules: { + 'no-restricted-imports': [ + 'error', + { + name: '@typescript-eslint/typescript-estree', + message: + 'To prevent nx build errors, all `typescript-estree` imports should be done via `packages/ast-spec/tests/util/parsers/typescript-estree-import.ts`.', + }, + ], + }, + }, + + // + // website linting + // + + { + files: ['packages/website/**/*.{ts,tsx,mts,cts,js,jsx}'], + extends: [ + ...compat.config(jsxA11yPlugin.configs.recommended), + ...compat.config(reactPlugin.configs.recommended), + ...compat.config(reactHooksPlugin.configs.recommended), + ], + rules: { + '@typescript-eslint/internal/prefer-ast-types-enum': 'off', + 'import/no-default-export': 'off', + 'react/jsx-no-target-blank': 'off', + 'react/no-unescaped-entities': 'off', + 'react-hooks/exhaustive-deps': 'warn', // TODO: enable it later + }, + settings: { + react: { + version: 'detect', + }, + }, + }, + { + files: ['packages/website/src/**/*.{ts,tsx,cts,mts}'], + rules: { + 'import/no-default-export': 'off', + // allow console logs in the website to help with debugging things in production + 'no-console': 'off', + }, + }, + { + files: [ + 'packages/website-eslint/src/mock/**/*.js', + '**/*.d.{ts,tsx,cts,mts}', + ], + rules: { + // mocks and declaration files have to mirror their original package + 'import/no-default-export': 'off', + }, + }, +); diff --git a/nx.json b/nx.json index 0f59cb4dec0..0ba06155f9c 100644 --- a/nx.json +++ b/nx.json @@ -38,11 +38,15 @@ "cache": true }, "lint": { - "dependsOn": ["eslint-plugin:build"], + "dependsOn": [ + "eslint-plugin:build", + "parser:build", + "typescript-eslint:build" + ], "inputs": [ "default", - "{workspaceRoot}/.eslintrc.js", "{workspaceRoot}/package.json", + "{workspaceRoot}/eslint.config.js", "{workspaceRoot}/yarn.lock", "{workspaceRoot}/.eslintignore", { diff --git a/package.json b/package.json index 90a41e20e9f..76d617f5b32 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,8 @@ "@babel/eslint-parser": "^7.23.3", "@babel/parser": "^7.23.3", "@babel/types": "^7.23.3", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "^8.56.0", "@nx/eslint": "17.2.8", "@nx/jest": "17.2.8", "@nx/workspace": "17.2.8", @@ -81,6 +83,7 @@ "@types/yargs": "^17.0.32", "@typescript-eslint/eslint-plugin-internal": "workspace:^", "console-fail-test": "^0.2.3", + "cross-env": "^7.0.3", "cross-fetch": "^4.0.0", "cspell": "^7.0.0", "downlevel-dts": ">=0.11.0", @@ -98,6 +101,7 @@ "eslint-plugin-unicorn": "^50.0.1", "execa": "7.1.1", "glob": "^10.3.3", + "globals": "^13.23.0", "husky": "^8.0.3", "jest": "29.7.0", "jest-diff": "^29.6.2", diff --git a/packages/ast-spec/project.json b/packages/ast-spec/project.json index 1771bfa7b55..0076bc88031 100644 --- a/packages/ast-spec/project.json +++ b/packages/ast-spec/project.json @@ -14,10 +14,7 @@ }, "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/eslint-plugin-internal/index.d.ts b/packages/eslint-plugin-internal/index.d.ts new file mode 100644 index 00000000000..8c2962c75da --- /dev/null +++ b/packages/eslint-plugin-internal/index.d.ts @@ -0,0 +1,9 @@ +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +import type rules from './rules'; + +declare const cjsExport: { + meta: FlatConfig.PluginMeta; + rules: typeof rules; +}; +export = cjsExport; diff --git a/packages/eslint-plugin-internal/package.json b/packages/eslint-plugin-internal/package.json index 06347f372c2..cf4f38a90d7 100644 --- a/packages/eslint-plugin-internal/package.json +++ b/packages/eslint-plugin-internal/package.json @@ -3,6 +3,7 @@ "version": "6.20.0", "private": true, "main": "dist/index.js", + "types": "index.d.ts", "scripts": { "build": "tsc -b tsconfig.build.json", "clean": "tsc -b tsconfig.build.json --clean", diff --git a/packages/eslint-plugin-internal/project.json b/packages/eslint-plugin-internal/project.json index 235431ed3ce..36836344938 100644 --- a/packages/eslint-plugin-internal/project.json +++ b/packages/eslint-plugin-internal/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/eslint-plugin-internal/src/index.ts b/packages/eslint-plugin-internal/src/index.ts index 0802acef98a..cb15326b4df 100644 --- a/packages/eslint-plugin-internal/src/index.ts +++ b/packages/eslint-plugin-internal/src/index.ts @@ -1,5 +1,15 @@ import rules from './rules'; +// note - cannot migrate this to an import statement because it will make TSC copy the package.json to the dist folder +const { name, version } = require('../package.json') as { + name: string; + version: string; +}; + export = { rules, + meta: { + name, + version, + }, }; diff --git a/packages/eslint-plugin-internal/tsconfig.json b/packages/eslint-plugin-internal/tsconfig.json index cc78ba59388..83713e5c8bf 100644 --- a/packages/eslint-plugin-internal/tsconfig.json +++ b/packages/eslint-plugin-internal/tsconfig.json @@ -5,6 +5,6 @@ "target": "ES2022", "rootDir": "." }, - "include": ["src", "typings", "tests"], + "include": ["src", "typings", "tests", "index.d.ts"], "references": [{ "path": "../utils/tsconfig.build.json" }] } diff --git a/packages/eslint-plugin-tslint/project.json b/packages/eslint-plugin-tslint/project.json index ed97991ef7a..dc5d5b11a63 100644 --- a/packages/eslint-plugin-tslint/project.json +++ b/packages/eslint-plugin-tslint/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/eslint-plugin/eslint-recommended-raw.d.ts b/packages/eslint-plugin/eslint-recommended-raw.d.ts new file mode 100644 index 00000000000..da4a8496b4f --- /dev/null +++ b/packages/eslint-plugin/eslint-recommended-raw.d.ts @@ -0,0 +1,5 @@ +declare const config: (style: 'glob' | 'minimatch') => { + files: string[]; + rules: Record; +}; +export default config; diff --git a/packages/eslint-plugin/index.d.ts b/packages/eslint-plugin/index.d.ts index 30f22e9b09e..756a025eb97 100644 --- a/packages/eslint-plugin/index.d.ts +++ b/packages/eslint-plugin/index.d.ts @@ -1,9 +1,13 @@ -import type { ClassicConfig } from '@typescript-eslint/utils/ts-eslint'; +import type { + ClassicConfig, + FlatConfig, +} from '@typescript-eslint/utils/ts-eslint'; import type rules from './rules'; declare const cjsExport: { configs: Record; + meta: FlatConfig.PluginMeta; rules: typeof rules; }; export = cjsExport; diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 4eb17afea78..1dce853e378 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -21,6 +21,10 @@ "./use-at-your-own-risk/rules": { "types": "./rules.d.ts", "default": "./dist/rules/index.js" + }, + "./use-at-your-own-risk/eslint-recommended-raw": { + "types": "./eslint-recommended-raw.d.ts", + "default": "./dist/configs/eslint-recommended-raw.js" } }, "engines": { diff --git a/packages/eslint-plugin/project.json b/packages/eslint-plugin/project.json index a4967a65143..56e47529dd1 100644 --- a/packages/eslint-plugin/project.json +++ b/packages/eslint-plugin/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/eslint-plugin/rules.d.ts b/packages/eslint-plugin/rules.d.ts index 1778ba33ca7..71745fe4ef7 100644 --- a/packages/eslint-plugin/rules.d.ts +++ b/packages/eslint-plugin/rules.d.ts @@ -42,4 +42,5 @@ export type TypeScriptESLintRules = Record< RuleModule >; declare const rules: TypeScriptESLintRules; +// eslint-disable-next-line import/no-default-export export default rules; diff --git a/packages/eslint-plugin/src/configs/base.ts b/packages/eslint-plugin/src/configs/base.ts index 628ed42b760..652bf9db1ee 100644 --- a/packages/eslint-plugin/src/configs/base.ts +++ b/packages/eslint-plugin/src/configs/base.ts @@ -1,10 +1,3 @@ -// THIS CODE WAS AUTOMATICALLY GENERATED -// DO NOT EDIT THIS CODE BY HAND -// SEE https://typescript-eslint.io/linting/configs -// -// For developers working in the typescript-eslint monorepo: -// You can regenerate it using `yarn generate:configs` - export = { parser: '@typescript-eslint/parser', parserOptions: { sourceType: 'module' }, diff --git a/packages/eslint-plugin/src/configs/disable-type-checked.ts b/packages/eslint-plugin/src/configs/disable-type-checked.ts index 4cd82bf2414..ef30de7f2d3 100644 --- a/packages/eslint-plugin/src/configs/disable-type-checked.ts +++ b/packages/eslint-plugin/src/configs/disable-type-checked.ts @@ -6,7 +6,7 @@ // You can regenerate it using `yarn generate:configs` export = { - parserOptions: { project: null, program: null }, + parserOptions: { project: false, program: null }, rules: { '@typescript-eslint/await-thenable': 'off', '@typescript-eslint/consistent-type-exports': 'off', diff --git a/packages/eslint-plugin/src/configs/eslint-recommended-raw.ts b/packages/eslint-plugin/src/configs/eslint-recommended-raw.ts new file mode 100644 index 00000000000..aafff1c61c1 --- /dev/null +++ b/packages/eslint-plugin/src/configs/eslint-recommended-raw.ts @@ -0,0 +1,44 @@ +// NOTE: this file is isolated to be shared across legacy and flat configs +// it is exported via `./use-at-your-own-risk/eslint-recommended-raw` +// and it has types manually defined in `./eslint-recommended-raw.d.ts` + +/** + * This is a compatibility ruleset that: + * - disables rules from eslint:recommended which are already handled by TypeScript. + * - enables rules that make sense due to TS's typechecking / transpilation. + */ +export default ( + style: 'glob' | 'minimatch', +): { + files: string[]; + rules: Record; +} => ({ + files: + style === 'glob' + ? // classic configs use glob syntax + ['*.ts', '*.tsx', '*.mts', '*.cts'] + : // flat configs use minimatch syntax + ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'], + rules: { + 'constructor-super': 'off', // ts(2335) & ts(2377) + 'getter-return': 'off', // ts(2378) + 'no-const-assign': 'off', // ts(2588) + 'no-dupe-args': 'off', // ts(2300) + 'no-dupe-class-members': 'off', // ts(2393) & ts(2300) + 'no-dupe-keys': 'off', // ts(1117) + 'no-func-assign': 'off', // ts(2630) + 'no-import-assign': 'off', // ts(2632) & ts(2540) + 'no-new-symbol': 'off', // ts(7009) + 'no-obj-calls': 'off', // ts(2349) + 'no-redeclare': 'off', // ts(2451) + 'no-setter-return': 'off', // ts(2408) + 'no-this-before-super': 'off', // ts(2376) & ts(17009) + 'no-undef': 'off', // ts(2304) & ts(2552) + 'no-unreachable': 'off', // ts(7027) + 'no-unsafe-negation': 'off', // ts(2365) & ts(2322) & ts(2358) + 'no-var': 'error', // ts transpiles let/const to var, so no need for vars any more + 'prefer-const': 'error', // ts provides better types with const + 'prefer-rest-params': 'error', // ts provides better types with rest args over arguments + 'prefer-spread': 'error', // ts transpiles spread to apply, so no need for manual apply + }, +}); diff --git a/packages/eslint-plugin/src/configs/eslint-recommended.ts b/packages/eslint-plugin/src/configs/eslint-recommended.ts index a1cdae8759d..64310504742 100644 --- a/packages/eslint-plugin/src/configs/eslint-recommended.ts +++ b/packages/eslint-plugin/src/configs/eslint-recommended.ts @@ -1,34 +1,10 @@ +import eslintRecommended_raw from './eslint-recommended-raw'; + /** * This is a compatibility ruleset that: * - disables rules from eslint:recommended which are already handled by TypeScript. * - enables rules that make sense due to TS's typechecking / transpilation. */ export = { - overrides: [ - { - files: ['*.ts', '*.tsx', '*.mts', '*.cts'], - rules: { - 'constructor-super': 'off', // ts(2335) & ts(2377) - 'getter-return': 'off', // ts(2378) - 'no-const-assign': 'off', // ts(2588) - 'no-dupe-args': 'off', // ts(2300) - 'no-dupe-class-members': 'off', // ts(2393) & ts(2300) - 'no-dupe-keys': 'off', // ts(1117) - 'no-func-assign': 'off', // ts(2630) - 'no-import-assign': 'off', // ts(2632) & ts(2540) - 'no-new-symbol': 'off', // ts(7009) - 'no-obj-calls': 'off', // ts(2349) - 'no-redeclare': 'off', // ts(2451) - 'no-setter-return': 'off', // ts(2408) - 'no-this-before-super': 'off', // ts(2376) & ts(17009) - 'no-undef': 'off', // ts(2304) & ts(2552) - 'no-unreachable': 'off', // ts(7027) - 'no-unsafe-negation': 'off', // ts(2365) & ts(2322) & ts(2358) - 'no-var': 'error', // ts transpiles let/const to var, so no need for vars any more - 'prefer-const': 'error', // ts provides better types with const - 'prefer-rest-params': 'error', // ts provides better types with rest args over arguments - 'prefer-spread': 'error', // ts transpiles spread to apply, so no need for manual apply - }, - }, - ], + overrides: [eslintRecommended_raw('glob')], }; diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts index ece2bb0a20f..40f159cb0e4 100644 --- a/packages/eslint-plugin/src/index.ts +++ b/packages/eslint-plugin/src/index.ts @@ -10,6 +10,12 @@ import stylistic from './configs/stylistic'; import stylisticTypeChecked from './configs/stylistic-type-checked'; import rules from './rules'; +// note - cannot migrate this to an import statement because it will make TSC copy the package.json to the dist folder +const { name, version } = require('../package.json') as { + name: string; + version: string; +}; + export = { configs: { all, @@ -25,5 +31,9 @@ export = { stylistic, 'stylistic-type-checked': stylisticTypeChecked, }, + meta: { + name, + version, + }, rules, }; diff --git a/packages/integration-tests/project.json b/packages/integration-tests/project.json index 262793adf20..0299e92d157 100644 --- a/packages/integration-tests/project.json +++ b/packages/integration-tests/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/parser/project.json b/packages/parser/project.json index a0ee6fe1217..94b5289ff17 100644 --- a/packages/parser/project.json +++ b/packages/parser/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/parser/src/parser.ts b/packages/parser/src/parser.ts index 8577dd61bcc..17c5a2ff61b 100644 --- a/packages/parser/src/parser.ts +++ b/packages/parser/src/parser.ts @@ -10,6 +10,7 @@ import type { TSESTreeOptions, } from '@typescript-eslint/typescript-estree'; import { parseAndGenerateServices } from '@typescript-eslint/typescript-estree'; +import type { VisitorKeys } from '@typescript-eslint/visitor-keys'; import { visitorKeys } from '@typescript-eslint/visitor-keys'; import debug from 'debug'; import type * as ts from 'typescript'; @@ -24,7 +25,7 @@ interface ParseForESLintResult { comments?: TSESTree.Comment[]; }; services: ParserServices; - visitorKeys: typeof visitorKeys; + visitorKeys: VisitorKeys; scopeManager: ScopeManager; } diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index d21677ad7e9..dc5a7affedb 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -18,6 +18,7 @@ "devDependencies": { "@nx/devkit": "*", "@prettier/sync": "*", + "cross-env": "*", "cross-fetch": "*", "execa": "*", "prettier": "^3.0.3", diff --git a/packages/repo-tools/project.json b/packages/repo-tools/project.json index 911b3bd5b19..e90305a7593 100644 --- a/packages/repo-tools/project.json +++ b/packages/repo-tools/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/repo-tools/src/generate-configs.mts b/packages/repo-tools/src/generate-configs.mts index 5c5aa689ef4..e65b8bd189b 100644 --- a/packages/repo-tools/src/generate-configs.mts +++ b/packages/repo-tools/src/generate-configs.mts @@ -1,16 +1,21 @@ -import * as fs from 'node:fs'; -import * as path from 'node:path'; +import fs from 'node:fs'; +import path from 'node:path'; import eslintPlugin from '@typescript-eslint/eslint-plugin'; import type { ClassicConfig, + FlatConfig, Linter, RuleModule, RuleRecommendation, } from '@typescript-eslint/utils/ts-eslint'; import prettier from 'prettier'; -import { PACKAGES_ESLINT_PLUGIN, REPO_ROOT } from './paths.mts'; +import { + PACKAGES_ESLINT_PLUGIN, + PACKAGES_TYPESCRIPT_ESLINT, + REPO_ROOT, +} from './paths.mts'; // no need for us to bring in an entire dependency for a few simple terminal colors const chalk = { @@ -31,8 +36,24 @@ const AUTO_GENERATED_COMMENT_LINES = [ '', ] as const; +const EXTENDS_MODULES = [ + { + name: 'baseConfig', + packageRelativePath: './configs/base', + moduleRelativePath: './base', + }, + { + name: 'eslintRecommendedConfig', + packageRelativePath: './configs/eslint-recommended', + moduleRelativePath: './eslint-recommended', + }, +] as const; +const CLASSIC_EXTENDS: readonly string[] = EXTENDS_MODULES.map( + mod => mod.packageRelativePath, +); + async function main(): Promise { - function addAutoGeneratedComment(code: string): string { + function addAutoGeneratedComment(code?: string): string { return [...AUTO_GENERATED_COMMENT_LINES, code].join('\n'); } @@ -63,7 +84,6 @@ async function main(): Promise { ] as const, ), ); - const EXTENDS = ['./configs/base', './configs/eslint-recommended']; type RuleEntry = [string, RuleModule]; @@ -141,34 +161,97 @@ async function main(): Promise { const hyphens = '-'.repeat(35 - Math.ceil(name.length / 2)); console.log(chalk.blueBright(`\n${hyphens} ${name}.ts ${hyphens}`)); + const config = getConfig(); + + // + // 1. Classic Config - written to the eslint-plugin package + // These configs are just JSON blobs that we write as TS files + // + // note: we use `export =` because ESLint will import these configs via a commonjs import - const code = `export = ${JSON.stringify(getConfig())};`; - const configStr = await prettier.format(addAutoGeneratedComment(code), { + const classicCode = `export = ${JSON.stringify(config)};`; + const classicConfigStr = await prettier.format( + addAutoGeneratedComment(classicCode), + { + parser: 'typescript', + ...prettierConfig, + }, + ); + fs.writeFileSync( + path.join(PACKAGES_ESLINT_PLUGIN, 'src', 'configs', `${name}.ts`), + classicConfigStr, + ); + + // + // 2. Flat Config - written to the core package + // These configs are actual TS modules that import other configs + // + const flatCode: string[] = [ + ...AUTO_GENERATED_COMMENT_LINES, + "import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint';", + '', + ]; + const flatExtends: string[] = []; + const flatConfig: FlatConfig.Config = { + rules: config.rules, + }; + if (config.extends) { + for (const extendPath of config.extends) { + const config = EXTENDS_MODULES.find( + mod => mod.packageRelativePath === extendPath, + ); + if (config == null) { + throw new Error("Couldn't find config"); + } + flatCode.push( + `import ${config.name} from '${config.moduleRelativePath}';`, + ); + flatExtends.push(config.name); + } + flatCode.push(''); + } + if (config.parserOptions) { + flatConfig.languageOptions ??= {}; + flatConfig.languageOptions.parserOptions = config.parserOptions; + } + + const flatConfigJson = JSON.stringify(flatConfig); + if (flatExtends.length > 0) { + flatCode.push( + 'export default (plugin: FlatConfig.Plugin, parser: FlatConfig.Parser): FlatConfig.ConfigArray => [', + ...flatExtends.map(ext => `${ext}(plugin, parser),`), + flatConfigJson, + '];', + ); + } else { + flatCode.push( + `export default (_plugin: FlatConfig.Plugin, _parser: FlatConfig.Parser): FlatConfig.Config => (${flatConfigJson});`, + ); + } + const flatConfigStr = await prettier.format(flatCode.join('\n'), { parser: 'typescript', ...prettierConfig, }); fs.writeFileSync( - path.join(PACKAGES_ESLINT_PLUGIN, 'src', 'configs', `${name}.ts`), - configStr, + path.join(PACKAGES_TYPESCRIPT_ESLINT, 'src', 'configs', `${name}.ts`), + flatConfigStr, ); } interface ExtendedConfigSettings { - extraExtends?: readonly string[]; name: string; filters?: RuleFilter; ruleEntries: readonly RuleEntry[]; } async function writeExtendedConfig({ - extraExtends = [], filters: ruleFilter, name, ruleEntries, }: ExtendedConfigSettings): Promise { await writeConfig( () => ({ - extends: [...EXTENDS, ...extraExtends], + extends: [...CLASSIC_EXTENDS], rules: ruleEntries.reduce( (config, entry) => reducer(config, entry, ruleFilter), {}, @@ -186,28 +269,6 @@ async function main(): Promise { ); } - await writeConfig((): LinterConfig => { - const baseConfig: LinterConfig = { - parser: '@typescript-eslint/parser', - parserOptions: { - sourceType: 'module', - }, - plugins: ['@typescript-eslint'], - }; - - console.log(chalk.gray('Config values:')); - - const longestKey = Object.keys(baseConfig).reduce( - (previous, next) => Math.max(previous, next.length), - 0, - ); - for (const [key, value] of Object.entries(baseConfig)) { - console.log(' ', key.padEnd(longestKey), value); - } - - return baseConfig; - }, 'base'); - await writeExtendedConfig({ name: 'all', filters: { @@ -258,7 +319,7 @@ async function main(): Promise { await writeConfig( () => ({ parserOptions: { - project: null, + project: false, program: null, }, rules: allRuleEntries.reduce( diff --git a/packages/repo-tools/src/generate-sponsors.mts b/packages/repo-tools/src/generate-sponsors.mts index fbb4530e79d..142029a6133 100644 --- a/packages/repo-tools/src/generate-sponsors.mts +++ b/packages/repo-tools/src/generate-sponsors.mts @@ -3,7 +3,7 @@ import * as fs from 'fs'; import * as path from 'path'; import prettier from 'prettier'; -import { PACKAGES_WEBSITE } from './paths.mjs'; +import { PACKAGES_WEBSITE } from './paths.mts'; const graphqlEndpoint = 'https://api.opencollective.com/graphql/v2'; diff --git a/packages/repo-tools/src/paths.mts b/packages/repo-tools/src/paths.mts index c5dd2b2889c..9e158a4426b 100644 --- a/packages/repo-tools/src/paths.mts +++ b/packages/repo-tools/src/paths.mts @@ -9,4 +9,8 @@ export const PACKAGES = path.join(REPO_ROOT, 'packages'); export const PACKAGES_ESLINT_PLUGIN = path.join(PACKAGES, 'eslint-plugin'); export const PACKAGES_SCOPE_MANAGER = path.join(PACKAGES, 'scope-manager'); export const PACKAGES_TYPES = path.join(PACKAGES, 'types'); +export const PACKAGES_TYPESCRIPT_ESLINT = path.join( + PACKAGES, + 'typescript-eslint', +); export const PACKAGES_WEBSITE = path.join(PACKAGES, 'website'); diff --git a/packages/repo-tools/tsconfig.build.json b/packages/repo-tools/tsconfig.build.json index b9ac3e1b977..992e2f19be7 100644 --- a/packages/repo-tools/tsconfig.build.json +++ b/packages/repo-tools/tsconfig.build.json @@ -1,6 +1,9 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { + // repo tools are executed with tsx + "allowImportingTsExtensions": true, + "noEmit": true, "composite": true, "outDir": "./dist", "rootDir": "./src", diff --git a/packages/rule-schema-to-typescript-types/project.json b/packages/rule-schema-to-typescript-types/project.json index 6ada6d7344f..266c06799b2 100644 --- a/packages/rule-schema-to-typescript-types/project.json +++ b/packages/rule-schema-to-typescript-types/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/scope-manager/project.json b/packages/scope-manager/project.json index aa7e748088e..ba61ab95725 100644 --- a/packages/scope-manager/project.json +++ b/packages/scope-manager/project.json @@ -51,10 +51,7 @@ }, "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] }, "test": { "executor": "@nx/jest:jest" diff --git a/packages/type-utils/project.json b/packages/type-utils/project.json index db349093e90..4ac211280e6 100644 --- a/packages/type-utils/project.json +++ b/packages/type-utils/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/types/project.json b/packages/types/project.json index 9aea796fd5a..d470995b1e1 100644 --- a/packages/types/project.json +++ b/packages/types/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/types/src/parser-options.ts b/packages/types/src/parser-options.ts index 885645f4bba..e84e3ab2566 100644 --- a/packages/types/src/parser-options.ts +++ b/packages/types/src/parser-options.ts @@ -62,8 +62,8 @@ interface ParserOptions { filePath?: string; jsDocParsingMode?: JSDocParsingMode; loc?: boolean; - programs?: Program | null; - project?: string[] | string | true | null; + programs?: Program[] | null; + project?: string[] | string | boolean | null; projectFolderIgnoreList?: (RegExp | string)[]; range?: boolean; sourceType?: SourceType; diff --git a/packages/typescript-eslint/LICENCE b/packages/typescript-eslint/LICENSE similarity index 100% rename from packages/typescript-eslint/LICENCE rename to packages/typescript-eslint/LICENSE diff --git a/packages/typescript-eslint/jest.config.js b/packages/typescript-eslint/jest.config.js new file mode 100644 index 00000000000..88b83019e01 --- /dev/null +++ b/packages/typescript-eslint/jest.config.js @@ -0,0 +1,8 @@ +'use strict'; + +// @ts-check +/** @type {import('@jest/types').Config.InitialOptions} */ +module.exports = { + ...require('../../jest.config.base.js'), + testRegex: './tests/.+\\.ts$', +}; diff --git a/packages/typescript-eslint/package.json b/packages/typescript-eslint/package.json index efd1b4486c4..f0cbe7b9995 100644 --- a/packages/typescript-eslint/package.json +++ b/packages/typescript-eslint/package.json @@ -9,7 +9,6 @@ "LICENSE" ], "type": "commonjs", - "private": true, "exports": { ".": { "types": "./dist/index.d.ts", diff --git a/packages/typescript-eslint/project.json b/packages/typescript-eslint/project.json index 75cd8266b5e..f30e095e652 100644 --- a/packages/typescript-eslint/project.json +++ b/packages/typescript-eslint/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/typescript-eslint/src/config-helper.ts b/packages/typescript-eslint/src/config-helper.ts new file mode 100644 index 00000000000..4e1565ca814 --- /dev/null +++ b/packages/typescript-eslint/src/config-helper.ts @@ -0,0 +1,86 @@ +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +interface ConfigWithExtends extends FlatConfig.Config { + /** + * Allows you to "extend" a set of configs similar to `extends` from the + * classic configs. + * + * This is just a convenience short-hand to help reduce duplication. + * + * ```js + * export default tseslint.config({ + * files: ['** /*.ts'], + * extends: [ + * eslint.configs.recommended, + * tseslint.configs.recommended, + * ], + * rules: { + * '@typescript-eslint/array-type': 'error', + * '@typescript-eslint/consistent-type-imports': 'error', + * }, + * }) + * + * // expands to + * + * export default [ + * { + * files: ['** /*.ts'], + * ...eslint.configs.recommended, + * }, + * { + * files: ['** /*.ts'], + * ...tseslint.configs.recommended, + * }, + * { + * files: ['** /*.ts'], + * rules: { + * '@typescript-eslint/array-type': 'error', + * '@typescript-eslint/consistent-type-imports': 'error', + * }, + * }, + * ] + * ``` + */ + extends?: FlatConfig.ConfigArray; +} + +/** + * Utility function to make it easy to strictly type your "Flat" config file + * @example + * ```js + * // @ts-check + * + * import eslint from '@eslint/js'; + * import tseslint from 'typescript-eslint'; + * + * export default tseslint.config( + * tseslint.configs.recommended, + * eslint.configs.recommended, + * { + * rules: { + * '@typescript-eslint/array-type': 'error', + * }, + * }, + * ); + * ``` + */ +export function config( + ...configs: ConfigWithExtends[] +): FlatConfig.ConfigArray { + return configs.flatMap(configWithExtends => { + const { extends: extendsArr, ...config } = configWithExtends; + if (extendsArr == null || extendsArr.length === 0) { + return config; + } + + if (config.files) { + const files = config.files; + return [ + ...extendsArr.map(conf => ({ ...conf, files: [...files] })), + config, + ]; + } + + return [...extendsArr, config]; + }); +} diff --git a/packages/typescript-eslint/src/configs/all.ts b/packages/typescript-eslint/src/configs/all.ts new file mode 100644 index 00000000000..e366ba92800 --- /dev/null +++ b/packages/typescript-eslint/src/configs/all.ts @@ -0,0 +1,165 @@ +// THIS CODE WAS AUTOMATICALLY GENERATED +// DO NOT EDIT THIS CODE BY HAND +// SEE https://typescript-eslint.io/linting/configs +// +// For developers working in the typescript-eslint monorepo: +// You can regenerate it using `yarn generate:configs` + +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +import baseConfig from './base'; +import eslintRecommendedConfig from './eslint-recommended'; + +export default ( + plugin: FlatConfig.Plugin, + parser: FlatConfig.Parser, +): FlatConfig.ConfigArray => [ + baseConfig(plugin, parser), + eslintRecommendedConfig(plugin, parser), + { + rules: { + '@typescript-eslint/adjacent-overload-signatures': 'error', + '@typescript-eslint/array-type': 'error', + '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/ban-ts-comment': 'error', + '@typescript-eslint/ban-tslint-comment': 'error', + '@typescript-eslint/ban-types': 'error', + '@typescript-eslint/class-literal-property-style': 'error', + 'class-methods-use-this': 'off', + '@typescript-eslint/class-methods-use-this': 'error', + '@typescript-eslint/consistent-generic-constructors': 'error', + '@typescript-eslint/consistent-indexed-object-style': 'error', + '@typescript-eslint/consistent-type-assertions': 'error', + '@typescript-eslint/consistent-type-definitions': 'error', + '@typescript-eslint/consistent-type-exports': 'error', + '@typescript-eslint/consistent-type-imports': 'error', + 'default-param-last': 'off', + '@typescript-eslint/default-param-last': 'error', + 'dot-notation': 'off', + '@typescript-eslint/dot-notation': 'error', + '@typescript-eslint/explicit-function-return-type': 'error', + '@typescript-eslint/explicit-member-accessibility': 'error', + '@typescript-eslint/explicit-module-boundary-types': 'error', + 'init-declarations': 'off', + '@typescript-eslint/init-declarations': 'error', + 'max-params': 'off', + '@typescript-eslint/max-params': 'error', + '@typescript-eslint/member-ordering': 'error', + '@typescript-eslint/method-signature-style': 'error', + '@typescript-eslint/naming-convention': 'error', + 'no-array-constructor': 'off', + '@typescript-eslint/no-array-constructor': 'error', + '@typescript-eslint/no-array-delete': 'error', + '@typescript-eslint/no-base-to-string': 'error', + '@typescript-eslint/no-confusing-non-null-assertion': 'error', + '@typescript-eslint/no-confusing-void-expression': 'error', + 'no-dupe-class-members': 'off', + '@typescript-eslint/no-dupe-class-members': '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', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-extra-non-null-assertion': 'error', + '@typescript-eslint/no-extraneous-class': 'error', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-for-in-array': 'error', + 'no-implied-eval': 'off', + '@typescript-eslint/no-implied-eval': 'error', + '@typescript-eslint/no-import-type-side-effects': 'error', + '@typescript-eslint/no-inferrable-types': 'error', + 'no-invalid-this': 'off', + '@typescript-eslint/no-invalid-this': 'error', + '@typescript-eslint/no-invalid-void-type': 'error', + 'no-loop-func': 'off', + '@typescript-eslint/no-loop-func': 'error', + 'no-loss-of-precision': 'off', + '@typescript-eslint/no-loss-of-precision': 'error', + 'no-magic-numbers': 'off', + '@typescript-eslint/no-magic-numbers': 'error', + '@typescript-eslint/no-meaningless-void-operator': 'error', + '@typescript-eslint/no-misused-new': 'error', + '@typescript-eslint/no-misused-promises': 'error', + '@typescript-eslint/no-mixed-enums': 'error', + '@typescript-eslint/no-namespace': 'error', + '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + '@typescript-eslint/no-non-null-assertion': 'error', + 'no-redeclare': 'off', + '@typescript-eslint/no-redeclare': 'error', + '@typescript-eslint/no-redundant-type-constituents': 'error', + '@typescript-eslint/no-require-imports': 'error', + 'no-restricted-imports': 'off', + '@typescript-eslint/no-restricted-imports': 'error', + 'no-shadow': 'off', + '@typescript-eslint/no-shadow': 'error', + '@typescript-eslint/no-this-alias': 'error', + 'no-throw-literal': 'off', + '@typescript-eslint/no-throw-literal': 'error', + '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', + '@typescript-eslint/no-unnecessary-condition': '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', + '@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-member-access': 'error', + '@typescript-eslint/no-unsafe-return': 'error', + '@typescript-eslint/no-unsafe-unary-minus': 'error', + 'no-unused-expressions': 'off', + '@typescript-eslint/no-unused-expressions': 'error', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'error', + 'no-use-before-define': 'off', + '@typescript-eslint/no-use-before-define': 'error', + 'no-useless-constructor': 'off', + '@typescript-eslint/no-useless-constructor': 'error', + '@typescript-eslint/no-useless-empty-export': 'error', + '@typescript-eslint/no-useless-template-literals': 'error', + '@typescript-eslint/no-var-requires': 'error', + '@typescript-eslint/non-nullable-type-assertion-style': 'error', + '@typescript-eslint/parameter-properties': 'error', + '@typescript-eslint/prefer-as-const': 'error', + 'prefer-destructuring': 'off', + '@typescript-eslint/prefer-destructuring': 'error', + '@typescript-eslint/prefer-enum-initializers': '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', + '@typescript-eslint/prefer-optional-chain': 'error', + 'prefer-promise-reject-errors': 'off', + '@typescript-eslint/prefer-promise-reject-errors': 'error', + '@typescript-eslint/prefer-readonly': 'error', + '@typescript-eslint/prefer-readonly-parameter-types': 'error', + '@typescript-eslint/prefer-reduce-type-parameter': 'error', + '@typescript-eslint/prefer-regexp-exec': 'error', + '@typescript-eslint/prefer-return-this-type': 'error', + '@typescript-eslint/prefer-string-starts-ends-with': 'error', + '@typescript-eslint/prefer-ts-expect-error': 'error', + '@typescript-eslint/promise-function-async': 'error', + '@typescript-eslint/require-array-sort-compare': 'error', + 'require-await': 'off', + '@typescript-eslint/require-await': 'error', + '@typescript-eslint/restrict-plus-operands': 'error', + '@typescript-eslint/restrict-template-expressions': 'error', + 'no-return-await': 'off', + '@typescript-eslint/return-await': 'error', + '@typescript-eslint/sort-type-constituents': 'error', + '@typescript-eslint/strict-boolean-expressions': 'error', + '@typescript-eslint/switch-exhaustiveness-check': 'error', + '@typescript-eslint/triple-slash-reference': 'error', + '@typescript-eslint/typedef': 'error', + '@typescript-eslint/unbound-method': 'error', + '@typescript-eslint/unified-signatures': 'error', + }, + }, +]; diff --git a/packages/typescript-eslint/src/configs/base.ts b/packages/typescript-eslint/src/configs/base.ts new file mode 100644 index 00000000000..a25249a1822 --- /dev/null +++ b/packages/typescript-eslint/src/configs/base.ts @@ -0,0 +1,14 @@ +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +export default ( + plugin: FlatConfig.Plugin, + parser: FlatConfig.Parser, +): FlatConfig.Config => ({ + languageOptions: { + parser, + parserOptions: { sourceType: 'module' }, + }, + plugins: { + '@typescript-eslint': plugin, + }, +}); diff --git a/packages/typescript-eslint/src/configs/disable-type-checked.ts b/packages/typescript-eslint/src/configs/disable-type-checked.ts new file mode 100644 index 00000000000..fa52425e4f4 --- /dev/null +++ b/packages/typescript-eslint/src/configs/disable-type-checked.ts @@ -0,0 +1,67 @@ +// THIS CODE WAS AUTOMATICALLY GENERATED +// DO NOT EDIT THIS CODE BY HAND +// SEE https://typescript-eslint.io/linting/configs +// +// For developers working in the typescript-eslint monorepo: +// You can regenerate it using `yarn generate:configs` + +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +export default ( + _plugin: FlatConfig.Plugin, + _parser: FlatConfig.Parser, +): FlatConfig.Config => ({ + rules: { + '@typescript-eslint/await-thenable': 'off', + '@typescript-eslint/consistent-type-exports': 'off', + '@typescript-eslint/dot-notation': 'off', + '@typescript-eslint/naming-convention': 'off', + '@typescript-eslint/no-array-delete': 'off', + '@typescript-eslint/no-base-to-string': 'off', + '@typescript-eslint/no-confusing-void-expression': 'off', + '@typescript-eslint/no-duplicate-type-constituents': 'off', + '@typescript-eslint/no-floating-promises': 'off', + '@typescript-eslint/no-for-in-array': 'off', + '@typescript-eslint/no-implied-eval': 'off', + '@typescript-eslint/no-meaningless-void-operator': 'off', + '@typescript-eslint/no-misused-promises': 'off', + '@typescript-eslint/no-mixed-enums': 'off', + '@typescript-eslint/no-redundant-type-constituents': 'off', + '@typescript-eslint/no-throw-literal': 'off', + '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'off', + '@typescript-eslint/no-unnecessary-condition': 'off', + '@typescript-eslint/no-unnecessary-qualifier': 'off', + '@typescript-eslint/no-unnecessary-type-arguments': 'off', + '@typescript-eslint/no-unnecessary-type-assertion': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-enum-comparison': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/no-unsafe-unary-minus': 'off', + '@typescript-eslint/no-useless-template-literals': 'off', + '@typescript-eslint/non-nullable-type-assertion-style': 'off', + '@typescript-eslint/prefer-destructuring': 'off', + '@typescript-eslint/prefer-includes': 'off', + '@typescript-eslint/prefer-nullish-coalescing': 'off', + '@typescript-eslint/prefer-optional-chain': 'off', + '@typescript-eslint/prefer-promise-reject-errors': 'off', + '@typescript-eslint/prefer-readonly': 'off', + '@typescript-eslint/prefer-readonly-parameter-types': 'off', + '@typescript-eslint/prefer-reduce-type-parameter': 'off', + '@typescript-eslint/prefer-regexp-exec': 'off', + '@typescript-eslint/prefer-return-this-type': 'off', + '@typescript-eslint/prefer-string-starts-ends-with': 'off', + '@typescript-eslint/promise-function-async': 'off', + '@typescript-eslint/require-array-sort-compare': 'off', + '@typescript-eslint/require-await': 'off', + '@typescript-eslint/restrict-plus-operands': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/return-await': 'off', + '@typescript-eslint/strict-boolean-expressions': 'off', + '@typescript-eslint/switch-exhaustiveness-check': 'off', + '@typescript-eslint/unbound-method': 'off', + }, + languageOptions: { parserOptions: { project: false, program: null } }, +}); diff --git a/packages/typescript-eslint/src/configs/eslint-recommended.ts b/packages/typescript-eslint/src/configs/eslint-recommended.ts new file mode 100644 index 00000000000..b9885922ecc --- /dev/null +++ b/packages/typescript-eslint/src/configs/eslint-recommended.ts @@ -0,0 +1,12 @@ +import config from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/eslint-recommended-raw'; +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +/** + * This is a compatibility ruleset that: + * - disables rules from eslint:recommended which are already handled by TypeScript. + * - enables rules that make sense due to TS's typechecking / transpilation. + */ +export default ( + _plugin: FlatConfig.Plugin, + _parser: FlatConfig.Parser, +): FlatConfig.Config => config('minimatch'); diff --git a/packages/typescript-eslint/src/configs/recommended-type-checked.ts b/packages/typescript-eslint/src/configs/recommended-type-checked.ts new file mode 100644 index 00000000000..7d9ac811e1c --- /dev/null +++ b/packages/typescript-eslint/src/configs/recommended-type-checked.ts @@ -0,0 +1,64 @@ +// THIS CODE WAS AUTOMATICALLY GENERATED +// DO NOT EDIT THIS CODE BY HAND +// SEE https://typescript-eslint.io/linting/configs +// +// For developers working in the typescript-eslint monorepo: +// You can regenerate it using `yarn generate:configs` + +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +import baseConfig from './base'; +import eslintRecommendedConfig from './eslint-recommended'; + +export default ( + plugin: FlatConfig.Plugin, + parser: FlatConfig.Parser, +): FlatConfig.ConfigArray => [ + baseConfig(plugin, parser), + eslintRecommendedConfig(plugin, parser), + { + rules: { + '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/ban-ts-comment': 'error', + '@typescript-eslint/ban-types': 'error', + 'no-array-constructor': 'off', + '@typescript-eslint/no-array-constructor': 'error', + '@typescript-eslint/no-base-to-string': 'error', + '@typescript-eslint/no-duplicate-enum-values': 'error', + '@typescript-eslint/no-duplicate-type-constituents': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-extra-non-null-assertion': 'error', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-for-in-array': 'error', + 'no-implied-eval': 'off', + '@typescript-eslint/no-implied-eval': 'error', + 'no-loss-of-precision': 'off', + '@typescript-eslint/no-loss-of-precision': 'error', + '@typescript-eslint/no-misused-new': 'error', + '@typescript-eslint/no-misused-promises': 'error', + '@typescript-eslint/no-namespace': 'error', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + '@typescript-eslint/no-redundant-type-constituents': 'error', + '@typescript-eslint/no-this-alias': 'error', + '@typescript-eslint/no-unnecessary-type-assertion': 'error', + '@typescript-eslint/no-unnecessary-type-constraint': '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-member-access': 'error', + '@typescript-eslint/no-unsafe-return': 'error', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'error', + '@typescript-eslint/no-var-requires': 'error', + '@typescript-eslint/prefer-as-const': 'error', + 'require-await': 'off', + '@typescript-eslint/require-await': 'error', + '@typescript-eslint/restrict-plus-operands': 'error', + '@typescript-eslint/restrict-template-expressions': 'error', + '@typescript-eslint/triple-slash-reference': 'error', + '@typescript-eslint/unbound-method': 'error', + }, + }, +]; diff --git a/packages/typescript-eslint/src/configs/recommended.ts b/packages/typescript-eslint/src/configs/recommended.ts new file mode 100644 index 00000000000..ae5103d9fea --- /dev/null +++ b/packages/typescript-eslint/src/configs/recommended.ts @@ -0,0 +1,43 @@ +// THIS CODE WAS AUTOMATICALLY GENERATED +// DO NOT EDIT THIS CODE BY HAND +// SEE https://typescript-eslint.io/linting/configs +// +// For developers working in the typescript-eslint monorepo: +// You can regenerate it using `yarn generate:configs` + +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +import baseConfig from './base'; +import eslintRecommendedConfig from './eslint-recommended'; + +export default ( + plugin: FlatConfig.Plugin, + parser: FlatConfig.Parser, +): FlatConfig.ConfigArray => [ + baseConfig(plugin, parser), + eslintRecommendedConfig(plugin, parser), + { + rules: { + '@typescript-eslint/ban-ts-comment': 'error', + '@typescript-eslint/ban-types': 'error', + 'no-array-constructor': 'off', + '@typescript-eslint/no-array-constructor': 'error', + '@typescript-eslint/no-duplicate-enum-values': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-extra-non-null-assertion': 'error', + 'no-loss-of-precision': 'off', + '@typescript-eslint/no-loss-of-precision': 'error', + '@typescript-eslint/no-misused-new': 'error', + '@typescript-eslint/no-namespace': 'error', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + '@typescript-eslint/no-this-alias': 'error', + '@typescript-eslint/no-unnecessary-type-constraint': 'error', + '@typescript-eslint/no-unsafe-declaration-merging': 'error', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'error', + '@typescript-eslint/no-var-requires': 'error', + '@typescript-eslint/prefer-as-const': 'error', + '@typescript-eslint/triple-slash-reference': 'error', + }, + }, +]; diff --git a/packages/typescript-eslint/src/configs/strict-type-checked.ts b/packages/typescript-eslint/src/configs/strict-type-checked.ts new file mode 100644 index 00000000000..c1c4628d0cd --- /dev/null +++ b/packages/typescript-eslint/src/configs/strict-type-checked.ts @@ -0,0 +1,89 @@ +// THIS CODE WAS AUTOMATICALLY GENERATED +// DO NOT EDIT THIS CODE BY HAND +// SEE https://typescript-eslint.io/linting/configs +// +// For developers working in the typescript-eslint monorepo: +// You can regenerate it using `yarn generate:configs` + +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +import baseConfig from './base'; +import eslintRecommendedConfig from './eslint-recommended'; + +export default ( + plugin: FlatConfig.Plugin, + parser: FlatConfig.Parser, +): FlatConfig.ConfigArray => [ + baseConfig(plugin, parser), + eslintRecommendedConfig(plugin, parser), + { + rules: { + '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/ban-ts-comment': 'error', + '@typescript-eslint/ban-types': 'error', + 'no-array-constructor': 'off', + '@typescript-eslint/no-array-constructor': 'error', + '@typescript-eslint/no-array-delete': 'error', + '@typescript-eslint/no-base-to-string': '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', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-extra-non-null-assertion': 'error', + '@typescript-eslint/no-extraneous-class': 'error', + '@typescript-eslint/no-floating-promises': 'error', + '@typescript-eslint/no-for-in-array': 'error', + 'no-implied-eval': 'off', + '@typescript-eslint/no-implied-eval': 'error', + '@typescript-eslint/no-invalid-void-type': 'error', + 'no-loss-of-precision': 'off', + '@typescript-eslint/no-loss-of-precision': 'error', + '@typescript-eslint/no-meaningless-void-operator': 'error', + '@typescript-eslint/no-misused-new': 'error', + '@typescript-eslint/no-misused-promises': 'error', + '@typescript-eslint/no-mixed-enums': 'error', + '@typescript-eslint/no-namespace': 'error', + '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-redundant-type-constituents': 'error', + '@typescript-eslint/no-this-alias': 'error', + 'no-throw-literal': 'off', + '@typescript-eslint/no-throw-literal': 'error', + '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', + '@typescript-eslint/no-unnecessary-condition': 'error', + '@typescript-eslint/no-unnecessary-type-arguments': 'error', + '@typescript-eslint/no-unnecessary-type-assertion': 'error', + '@typescript-eslint/no-unnecessary-type-constraint': '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-member-access': 'error', + '@typescript-eslint/no-unsafe-return': 'error', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'error', + 'no-useless-constructor': 'off', + '@typescript-eslint/no-useless-constructor': 'error', + '@typescript-eslint/no-useless-template-literals': 'error', + '@typescript-eslint/no-var-requires': 'error', + '@typescript-eslint/prefer-as-const': 'error', + '@typescript-eslint/prefer-includes': 'error', + '@typescript-eslint/prefer-literal-enum-member': 'error', + 'prefer-promise-reject-errors': 'off', + '@typescript-eslint/prefer-promise-reject-errors': 'error', + '@typescript-eslint/prefer-reduce-type-parameter': 'error', + '@typescript-eslint/prefer-return-this-type': 'error', + '@typescript-eslint/prefer-ts-expect-error': 'error', + 'require-await': 'off', + '@typescript-eslint/require-await': 'error', + '@typescript-eslint/restrict-plus-operands': 'error', + '@typescript-eslint/restrict-template-expressions': 'error', + '@typescript-eslint/triple-slash-reference': 'error', + '@typescript-eslint/unbound-method': 'error', + '@typescript-eslint/unified-signatures': 'error', + }, + }, +]; diff --git a/packages/typescript-eslint/src/configs/strict.ts b/packages/typescript-eslint/src/configs/strict.ts new file mode 100644 index 00000000000..e54b7d07322 --- /dev/null +++ b/packages/typescript-eslint/src/configs/strict.ts @@ -0,0 +1,53 @@ +// THIS CODE WAS AUTOMATICALLY GENERATED +// DO NOT EDIT THIS CODE BY HAND +// SEE https://typescript-eslint.io/linting/configs +// +// For developers working in the typescript-eslint monorepo: +// You can regenerate it using `yarn generate:configs` + +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +import baseConfig from './base'; +import eslintRecommendedConfig from './eslint-recommended'; + +export default ( + plugin: FlatConfig.Plugin, + parser: FlatConfig.Parser, +): FlatConfig.ConfigArray => [ + baseConfig(plugin, parser), + eslintRecommendedConfig(plugin, parser), + { + rules: { + '@typescript-eslint/ban-ts-comment': 'error', + '@typescript-eslint/ban-types': 'error', + 'no-array-constructor': 'off', + '@typescript-eslint/no-array-constructor': 'error', + '@typescript-eslint/no-duplicate-enum-values': 'error', + '@typescript-eslint/no-dynamic-delete': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-extra-non-null-assertion': 'error', + '@typescript-eslint/no-extraneous-class': 'error', + '@typescript-eslint/no-invalid-void-type': 'error', + 'no-loss-of-precision': 'off', + '@typescript-eslint/no-loss-of-precision': 'error', + '@typescript-eslint/no-misused-new': 'error', + '@typescript-eslint/no-namespace': 'error', + '@typescript-eslint/no-non-null-asserted-nullish-coalescing': 'error', + '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-this-alias': 'error', + '@typescript-eslint/no-unnecessary-type-constraint': 'error', + '@typescript-eslint/no-unsafe-declaration-merging': 'error', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'error', + 'no-useless-constructor': 'off', + '@typescript-eslint/no-useless-constructor': 'error', + '@typescript-eslint/no-var-requires': 'error', + '@typescript-eslint/prefer-as-const': 'error', + '@typescript-eslint/prefer-literal-enum-member': 'error', + '@typescript-eslint/prefer-ts-expect-error': 'error', + '@typescript-eslint/triple-slash-reference': 'error', + '@typescript-eslint/unified-signatures': 'error', + }, + }, +]; diff --git a/packages/typescript-eslint/src/configs/stylistic-type-checked.ts b/packages/typescript-eslint/src/configs/stylistic-type-checked.ts new file mode 100644 index 00000000000..3923973100b --- /dev/null +++ b/packages/typescript-eslint/src/configs/stylistic-type-checked.ts @@ -0,0 +1,45 @@ +// THIS CODE WAS AUTOMATICALLY GENERATED +// DO NOT EDIT THIS CODE BY HAND +// SEE https://typescript-eslint.io/linting/configs +// +// For developers working in the typescript-eslint monorepo: +// You can regenerate it using `yarn generate:configs` + +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +import baseConfig from './base'; +import eslintRecommendedConfig from './eslint-recommended'; + +export default ( + plugin: FlatConfig.Plugin, + parser: FlatConfig.Parser, +): FlatConfig.ConfigArray => [ + baseConfig(plugin, parser), + eslintRecommendedConfig(plugin, parser), + { + rules: { + '@typescript-eslint/adjacent-overload-signatures': 'error', + '@typescript-eslint/array-type': 'error', + '@typescript-eslint/ban-tslint-comment': 'error', + '@typescript-eslint/class-literal-property-style': 'error', + '@typescript-eslint/consistent-generic-constructors': 'error', + '@typescript-eslint/consistent-indexed-object-style': 'error', + '@typescript-eslint/consistent-type-assertions': 'error', + '@typescript-eslint/consistent-type-definitions': 'error', + 'dot-notation': 'off', + '@typescript-eslint/dot-notation': 'error', + '@typescript-eslint/no-confusing-non-null-assertion': 'error', + 'no-empty-function': 'off', + '@typescript-eslint/no-empty-function': 'error', + '@typescript-eslint/no-empty-interface': 'error', + '@typescript-eslint/no-inferrable-types': 'error', + '@typescript-eslint/non-nullable-type-assertion-style': 'error', + '@typescript-eslint/prefer-for-of': 'error', + '@typescript-eslint/prefer-function-type': 'error', + '@typescript-eslint/prefer-namespace-keyword': 'error', + '@typescript-eslint/prefer-nullish-coalescing': 'error', + '@typescript-eslint/prefer-optional-chain': 'error', + '@typescript-eslint/prefer-string-starts-ends-with': 'error', + }, + }, +]; diff --git a/packages/typescript-eslint/src/configs/stylistic.ts b/packages/typescript-eslint/src/configs/stylistic.ts new file mode 100644 index 00000000000..4db1a78f9a0 --- /dev/null +++ b/packages/typescript-eslint/src/configs/stylistic.ts @@ -0,0 +1,39 @@ +// THIS CODE WAS AUTOMATICALLY GENERATED +// DO NOT EDIT THIS CODE BY HAND +// SEE https://typescript-eslint.io/linting/configs +// +// For developers working in the typescript-eslint monorepo: +// You can regenerate it using `yarn generate:configs` + +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +import baseConfig from './base'; +import eslintRecommendedConfig from './eslint-recommended'; + +export default ( + plugin: FlatConfig.Plugin, + parser: FlatConfig.Parser, +): FlatConfig.ConfigArray => [ + baseConfig(plugin, parser), + eslintRecommendedConfig(plugin, parser), + { + rules: { + '@typescript-eslint/adjacent-overload-signatures': 'error', + '@typescript-eslint/array-type': 'error', + '@typescript-eslint/ban-tslint-comment': 'error', + '@typescript-eslint/class-literal-property-style': 'error', + '@typescript-eslint/consistent-generic-constructors': 'error', + '@typescript-eslint/consistent-indexed-object-style': 'error', + '@typescript-eslint/consistent-type-assertions': 'error', + '@typescript-eslint/consistent-type-definitions': 'error', + '@typescript-eslint/no-confusing-non-null-assertion': 'error', + 'no-empty-function': 'off', + '@typescript-eslint/no-empty-function': 'error', + '@typescript-eslint/no-empty-interface': 'error', + '@typescript-eslint/no-inferrable-types': 'error', + '@typescript-eslint/prefer-for-of': 'error', + '@typescript-eslint/prefer-function-type': 'error', + '@typescript-eslint/prefer-namespace-keyword': 'error', + }, + }, +]; diff --git a/packages/typescript-eslint/src/index.ts b/packages/typescript-eslint/src/index.ts index 8a15f9183b5..ea63e2c9301 100644 --- a/packages/typescript-eslint/src/index.ts +++ b/packages/typescript-eslint/src/index.ts @@ -1,2 +1,43 @@ -// TODO(bradzacher) #7935 -export {}; +import pluginBase from '@typescript-eslint/eslint-plugin'; +import * as parserBase from '@typescript-eslint/parser'; +import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + +import { config } from './config-helper'; +import allConfig from './configs/all'; +import baseConfig from './configs/base'; +import disableTypeCheckedConfig from './configs/disable-type-checked'; +import eslintRecommendedConfig from './configs/eslint-recommended'; +import recommendedConfig from './configs/recommended'; +import recommendedTypeCheckedConfig from './configs/recommended-type-checked'; +import strictConfig from './configs/strict'; +import strictTypeCheckedConfig from './configs/strict-type-checked'; +import stylisticConfig from './configs/stylistic'; +import stylisticTypeCheckedConfig from './configs/stylistic-type-checked'; + +const parser: FlatConfig.Parser = { + meta: parserBase.meta, + parseForESLint: parserBase.parseForESLint, +}; + +const plugin: FlatConfig.Plugin = { + meta: pluginBase.meta, + rules: pluginBase.rules, +}; + +export = { + config, + configs: { + all: allConfig(plugin, parser), + base: baseConfig(plugin, parser), + disableTypeChecked: disableTypeCheckedConfig(plugin, parser), + eslintRecommended: eslintRecommendedConfig(plugin, parser), + recommended: recommendedConfig(plugin, parser), + recommendedTypeChecked: recommendedTypeCheckedConfig(plugin, parser), + strict: strictConfig(plugin, parser), + strictTypeChecked: strictTypeCheckedConfig(plugin, parser), + stylistic: stylisticConfig(plugin, parser), + stylisticTypeChecked: stylisticTypeCheckedConfig(plugin, parser), + }, + parser, + plugin, +}; diff --git a/packages/typescript-eslint/tests/configs.test.ts b/packages/typescript-eslint/tests/configs.test.ts new file mode 100644 index 00000000000..1b88f58b6e9 --- /dev/null +++ b/packages/typescript-eslint/tests/configs.test.ts @@ -0,0 +1,213 @@ +import rules from '@typescript-eslint/eslint-plugin/use-at-your-own-risk/rules'; +import type { + FlatConfig, + RuleRecommendation, +} from '@typescript-eslint/utils/ts-eslint'; + +import plugin from '../src/index'; + +const RULE_NAME_PREFIX = '@typescript-eslint/'; +const EXTENSION_RULES = Object.entries(rules) + .filter(([, rule]) => rule.meta.docs?.extendsBaseRule) + .map( + ([ruleName, rule]) => + [ + `${RULE_NAME_PREFIX}${ruleName}`, + typeof rule.meta.docs?.extendsBaseRule === 'string' + ? rule.meta.docs.extendsBaseRule + : ruleName, + ] as const, + ); + +function entriesToObject(value: [string, T][]): Record { + return value.reduce>((accum, [k, v]) => { + accum[k] = v; + return accum; + }, {}); +} + +function filterRules( + values: FlatConfig.Rules | undefined, +): [string, FlatConfig.RuleEntry][] { + expect(values).toBeDefined(); + return Object.entries(values!) + .filter((pair): pair is [string, FlatConfig.RuleEntry] => pair[1] != null) + .filter(([name]) => name.startsWith(RULE_NAME_PREFIX)); +} + +interface FilterAndMapRuleConfigsSettings { + excludeDeprecated?: boolean; + excludeTypeChecked?: boolean; + recommendations?: (RuleRecommendation | undefined)[]; +} + +function filterAndMapRuleConfigs({ + excludeDeprecated, + excludeTypeChecked, + recommendations, +}: FilterAndMapRuleConfigsSettings = {}): [string, string][] { + let result = Object.entries(rules); + + if (excludeDeprecated) { + result = result.filter(([, rule]) => !rule.meta.deprecated); + } + + if (excludeTypeChecked) { + result = result.filter(([, rule]) => !rule.meta.docs?.requiresTypeChecking); + } + + if (recommendations) { + result = result.filter(([, rule]) => + recommendations.includes(rule.meta.docs?.recommended), + ); + } + + return result.map(([name]) => [`${RULE_NAME_PREFIX}${name}`, 'error']); +} + +function itHasBaseRulesOverriden( + unfilteredConfigRules: FlatConfig.Rules | undefined, +): void { + it('has the base rules overriden by the appropriate extension rules', () => { + expect(unfilteredConfigRules).toBeDefined(); + const ruleNames = new Set(Object.keys(unfilteredConfigRules!)); + EXTENSION_RULES.forEach(([ruleName, extRuleName]) => { + if (ruleNames.has(ruleName)) { + // this looks a little weird, but it provides the cleanest test output style + expect(unfilteredConfigRules).toMatchObject({ + ...unfilteredConfigRules, + [extRuleName]: 'off', + }); + } + }); + }); +} + +describe('all.ts', () => { + const unfilteredConfigRules = plugin.configs.all[2]?.rules; + + it('contains all of the rules', () => { + const configRules = filterRules(unfilteredConfigRules); + // note: exclude deprecated rules, this config is allowed to change between minor versions + const ruleConfigs = filterAndMapRuleConfigs({ + excludeDeprecated: true, + }); + + expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + }); + + itHasBaseRulesOverriden(unfilteredConfigRules); +}); + +describe('disable-type-checked.ts', () => { + const unfilteredConfigRules = plugin.configs.disableTypeChecked.rules; + + it('disables all type checked rules', () => { + const configRules = filterRules(unfilteredConfigRules); + + const ruleConfigs: [string, string][] = Object.entries(rules) + .filter(([, rule]) => rule.meta.docs?.requiresTypeChecking) + .map(([name]) => [`${RULE_NAME_PREFIX}${name}`, 'off']); + + expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + }); +}); + +describe('recommended.ts', () => { + const unfilteredConfigRules = plugin.configs.recommended[2]?.rules; + + it('contains all recommended rules, excluding type checked ones', () => { + const configRules = filterRules(unfilteredConfigRules); + // note: include deprecated rules so that the config doesn't change between major bumps + const ruleConfigs = filterAndMapRuleConfigs({ + excludeTypeChecked: true, + recommendations: ['recommended'], + }); + + expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + }); + + itHasBaseRulesOverriden(unfilteredConfigRules); +}); + +describe('recommended-type-checked.ts', () => { + const unfilteredConfigRules = plugin.configs.recommendedTypeChecked[2]?.rules; + + it('contains all recommended rules', () => { + const configRules = filterRules(unfilteredConfigRules); + // note: include deprecated rules so that the config doesn't change between major bumps + const ruleConfigs = filterAndMapRuleConfigs({ + recommendations: ['recommended'], + }); + + expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + }); + + itHasBaseRulesOverriden(unfilteredConfigRules); +}); + +describe('strict.ts', () => { + const unfilteredConfigRules = plugin.configs.strict[2]?.rules; + + it('contains all strict rules, excluding type checked ones', () => { + const configRules = filterRules(unfilteredConfigRules); + // note: exclude deprecated rules, this config is allowed to change between minor versions + const ruleConfigs = filterAndMapRuleConfigs({ + excludeDeprecated: true, + excludeTypeChecked: true, + recommendations: ['recommended', 'strict'], + }); + + expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + }); + + itHasBaseRulesOverriden(unfilteredConfigRules); +}); + +describe('strict-type-checked.ts', () => { + const unfilteredConfigRules = plugin.configs.strictTypeChecked[2]?.rules; + + it('contains all strict rules', () => { + const configRules = filterRules(unfilteredConfigRules); + // note: exclude deprecated rules, this config is allowed to change between minor versions + const ruleConfigs = filterAndMapRuleConfigs({ + excludeDeprecated: true, + recommendations: ['recommended', 'strict'], + }); + expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + }); + + itHasBaseRulesOverriden(unfilteredConfigRules); +}); + +describe('stylistic.ts', () => { + const unfilteredConfigRules = plugin.configs.stylistic[2]?.rules; + + it('contains all stylistic rules, excluding deprecated or type checked ones', () => { + const configRules = filterRules(unfilteredConfigRules); + // note: include deprecated rules so that the config doesn't change between major bumps + const ruleConfigs = filterAndMapRuleConfigs({ + excludeTypeChecked: true, + recommendations: ['stylistic'], + }); + + expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + }); + + itHasBaseRulesOverriden(unfilteredConfigRules); +}); + +describe('stylistic-type-checked.ts', () => { + const unfilteredConfigRules = plugin.configs.stylisticTypeChecked[2]?.rules; + const configRules = filterRules(unfilteredConfigRules); + // note: include deprecated rules so that the config doesn't change between major bumps + const ruleConfigs = filterAndMapRuleConfigs({ + recommendations: ['stylistic'], + }); + + it('contains all stylistic rules, excluding deprecated ones', () => { + expect(entriesToObject(ruleConfigs)).toEqual(entriesToObject(configRules)); + }); + + itHasBaseRulesOverriden(unfilteredConfigRules); +}); diff --git a/packages/typescript-estree/project.json b/packages/typescript-estree/project.json index 146f8770834..231f8f93a01 100644 --- a/packages/typescript-estree/project.json +++ b/packages/typescript-estree/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/typescript-estree/src/parseSettings/getProjectConfigFiles.ts b/packages/typescript-estree/src/parseSettings/getProjectConfigFiles.ts index 64af27985f0..1765f2cb125 100644 --- a/packages/typescript-estree/src/parseSettings/getProjectConfigFiles.ts +++ b/packages/typescript-estree/src/parseSettings/getProjectConfigFiles.ts @@ -2,7 +2,8 @@ import debug from 'debug'; import * as fs from 'fs'; import * as path from 'path'; -import type { ParseSettings } from '.'; +import type { TSESTreeOptions } from '../parser-options'; +import type { ParseSettings } from './index'; const log = debug('typescript-eslint:typescript-estree:getProjectConfigFiles'); @@ -20,10 +21,10 @@ export function getProjectConfigFiles( ParseSettings, 'filePath' | 'tsconfigMatchCache' | 'tsconfigRootDir' >, - project: string[] | string | true | null | undefined, + project: TSESTreeOptions['project'], ): string[] | null { if (project !== true) { - if (project == null) { + if (project == null || project === false) { return null; } if (Array.isArray(project)) { diff --git a/packages/typescript-estree/src/parser-options.ts b/packages/typescript-estree/src/parser-options.ts index e86be6d5099..0753c29f8c3 100644 --- a/packages/typescript-estree/src/parser-options.ts +++ b/packages/typescript-estree/src/parser-options.ts @@ -167,7 +167,7 @@ interface ParseAndGenerateServicesOptions extends ParseOptions { * or `true` to find the nearest tsconfig.json to the file. * If this is provided, type information will be returned. */ - project?: string[] | string | true | null; + project?: string[] | string | boolean | null; /** * If you provide a glob (or globs) to the project option, you can use this option to ignore certain folders from diff --git a/packages/utils/project.json b/packages/utils/project.json index 89fa0c819d5..fe765a0faff 100644 --- a/packages/utils/project.json +++ b/packages/utils/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/utils/src/ts-eslint/Config.ts b/packages/utils/src/ts-eslint/Config.ts index 534a41d0e07..b88eae95a88 100644 --- a/packages/utils/src/ts-eslint/Config.ts +++ b/packages/utils/src/ts-eslint/Config.ts @@ -119,7 +119,7 @@ export namespace ClassicConfig { export namespace FlatConfig { export type EcmaVersion = ParserOptionsTypes.EcmaVersion; export type GlobalsConfig = SharedConfig.GlobalsConfig; - export type Parser = ParserType.ParserModule; + export type Parser = ParserType.LooseParserModule; export type ParserOptions = SharedConfig.ParserOptions; export type Processor = ProcessorType.ProcessorModule; export type RuleEntry = SharedConfig.RuleEntry; @@ -131,6 +131,9 @@ export namespace FlatConfig { export type SeverityString = SharedConfig.SeverityString; export type SourceType = ParserOptionsTypes.SourceType | 'commonjs'; + export interface SharedConfigs { + [key: string]: Config; + } export interface PluginMeta { /** * The meta.name property should match the npm package name for your plugin. @@ -146,7 +149,7 @@ export namespace FlatConfig { * Shared configurations bundled with the plugin. * Users will reference these directly in their config (i.e. `plugin.configs.recommended`). */ - configs?: Record; + configs?: SharedConfigs; /** * Metadata about your plugin for easier debugging and more effective caching of plugins. */ @@ -165,7 +168,13 @@ export namespace FlatConfig { rules?: Record; } export interface Plugins { - [pluginAlias: string]: Plugin; + /** + * We intentionally omit the `configs` key from this object because it avoids + * type conflicts with old plugins that haven't updated their configs to flat configs yet. + * It's valid to reference these old plugins because ESLint won't access the + * `.config` property of a plugin when evaluating a flat config. + */ + [pluginAlias: string]: Omit; } export interface LinterOptions { @@ -262,5 +271,6 @@ export namespace FlatConfig { settings?: Settings; } export type ConfigArray = Config[]; - export type ConfigFile = ConfigArray | (() => Promise); + export type ConfigPromise = Promise; + export type ConfigFile = ConfigArray | ConfigPromise; } diff --git a/packages/utils/src/ts-eslint/Linter.ts b/packages/utils/src/ts-eslint/Linter.ts index f48ae9c32b0..978ce3ed26a 100644 --- a/packages/utils/src/ts-eslint/Linter.ts +++ b/packages/utils/src/ts-eslint/Linter.ts @@ -26,7 +26,7 @@ declare class LinterBase { * @param parserId Name of the parser * @param parserModule The parser object */ - defineParser(parserId: string, parserModule: Parser.ParserModule): void; + defineParser(parserId: string, parserModule: Parser.LooseParserModule): void; /** * Defines a new linting rule. @@ -243,7 +243,7 @@ namespace Linter { } /** @deprecated use Parser.ParserModule */ - export type ParserModule = Parser.ParserModule; + export type ParserModule = Parser.LooseParserModule; /** @deprecated use Parser.ParseResult */ export type ESLintParseResult = Parser.ParseResult; diff --git a/packages/utils/src/ts-eslint/Parser.ts b/packages/utils/src/ts-eslint/Parser.ts index e709ef70df6..cfc97ac12f8 100644 --- a/packages/utils/src/ts-eslint/Parser.ts +++ b/packages/utils/src/ts-eslint/Parser.ts @@ -38,6 +38,39 @@ export namespace Parser { parseForESLint(text: string, options?: ParserOptions): ParseResult; }; + /** + * A loose definition of the ParserModule type for use with configs + * This type intended to relax validation of configs so that parsers that have + * different AST types or scope managers can still be passed to configs + */ + export type LooseParserModule = + | { + /** + * Information about the parser to uniquely identify it when serializing. + */ + meta?: ParserMeta; + /** + * Parses the given text into an ESTree AST + */ + parse(text: string, options?: unknown): unknown; + } + | { + /** + * Information about the parser to uniquely identify it when serializing. + */ + meta?: ParserMeta; + /** + * Parses the given text into an AST + */ + parseForESLint( + text: string, + options?: unknown, + ): { + // intentionally not using a Record to preserve optionals + [k in keyof ParseResult]: unknown; + }; + }; + export interface ParseResult { /** * The ESTree AST @@ -66,6 +99,6 @@ export namespace Parser { // eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style export interface VisitorKeys { - [nodeType: string]: string[]; + [nodeType: string]: readonly string[]; } } diff --git a/packages/visitor-keys/project.json b/packages/visitor-keys/project.json index 5bdacb98ffc..d1536ce04de 100644 --- a/packages/visitor-keys/project.json +++ b/packages/visitor-keys/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/website-eslint/package.json b/packages/website-eslint/package.json index 23f9035f86a..1806a5c8a45 100644 --- a/packages/website-eslint/package.json +++ b/packages/website-eslint/package.json @@ -27,7 +27,7 @@ "@typescript-eslint/utils": "6.20.0" }, "devDependencies": { - "@eslint/js": "8.56.0", + "@eslint/js": "*", "@typescript-eslint/eslint-plugin": "6.20.0", "@typescript-eslint/parser": "6.20.0", "@typescript-eslint/scope-manager": "6.20.0", diff --git a/packages/website-eslint/project.json b/packages/website-eslint/project.json index 3ed597db726..52ed1e539c3 100644 --- a/packages/website-eslint/project.json +++ b/packages/website-eslint/project.json @@ -6,10 +6,7 @@ "targets": { "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/packages/website/project.json b/packages/website/project.json index 34ab1692e8f..c9b3f62d96f 100644 --- a/packages/website/project.json +++ b/packages/website/project.json @@ -14,10 +14,7 @@ }, "lint": { "executor": "@nx/eslint:lint", - "outputs": ["{options.outputFile}"], - "options": { - "ignorePath": ".eslintignore" - } + "outputs": ["{options.outputFile}"] } } } diff --git a/tsconfig.base.json b/tsconfig.base.json index fd7ed6a809f..a00dd38697a 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -6,15 +6,16 @@ "declaration": true, "declarationMap": true, "esModuleInterop": true, + "lib": ["ES2021"], "module": "Node16", - "moduleResolution": "node16", + "moduleResolution": "Node16", "noImplicitReturns": true, + "paths": {}, "pretty": true, + "resolveJsonModule": true, "skipLibCheck": true, "sourceMap": true, "strict": true, - "target": "ES2021", - "lib": ["ES2021"], - "paths": {} + "target": "ES2021" } } diff --git a/tsconfig.eslint.json b/tsconfig.json similarity index 88% rename from tsconfig.eslint.json rename to tsconfig.json index 7e9f118ed40..ed328447a15 100644 --- a/tsconfig.eslint.json +++ b/tsconfig.json @@ -6,9 +6,11 @@ }, "extends": "./tsconfig.base.json", "include": [ + "typings", "tools/**/*.ts", "tools/**/*.mts", ".eslintrc.js", + "eslint.config.js", "jest.config.base.js", "jest.config.js", "jest.preset.js" diff --git a/typings/eslint-plugin-eslint-comments.d.ts b/typings/eslint-plugin-eslint-comments.d.ts new file mode 100644 index 00000000000..279a27f80fa --- /dev/null +++ b/typings/eslint-plugin-eslint-comments.d.ts @@ -0,0 +1,14 @@ +declare module 'eslint-plugin-eslint-comments' { + import type { + ClassicConfig, + Linter, + } from '@typescript-eslint/utils/ts-eslint'; + + declare const exprt: { + configs: { + recommended: ClassicConfig.Config; + }; + rules: NonNullable; + }; + export = exprt; +} diff --git a/typings/eslint-plugin-eslint-plugin.d.ts b/typings/eslint-plugin-eslint-plugin.d.ts new file mode 100644 index 00000000000..f0afd05081f --- /dev/null +++ b/typings/eslint-plugin-eslint-plugin.d.ts @@ -0,0 +1,19 @@ +declare module 'eslint-plugin-eslint-plugin' { + import type { + ClassicConfig, + Linter, + } from '@typescript-eslint/utils/ts-eslint'; + + declare const exprt: { + configs: { + all: ClassicConfig.Config; + recommended: ClassicConfig.Config; + rules: ClassicConfig.Config; + tests: ClassicConfig.Config; + 'rules-recommended': ClassicConfig.Config; + 'tests-recommended': ClassicConfig.Config; + }; + rules: NonNullable; + }; + export = exprt; +} diff --git a/typings/eslint-plugin-import.d.ts b/typings/eslint-plugin-import.d.ts new file mode 100644 index 00000000000..60742fad493 --- /dev/null +++ b/typings/eslint-plugin-import.d.ts @@ -0,0 +1,21 @@ +declare module 'eslint-plugin-import' { + import type { + ClassicConfig, + Linter, + } from '@typescript-eslint/utils/ts-eslint'; + + declare const exprt: { + configs: { + recommended: ClassicConfig.Config; + errors: ClassicConfig.Config; + warnings: ClassicConfig.Config; + 'stage-0': ClassicConfig.Config; + react: ClassicConfig.Config; + 'react-native': ClassicConfig.Config; + electron: ClassicConfig.Config; + typescript: ClassicConfig.Config; + }; + rules: NonNullable; + }; + export = exprt; +} diff --git a/typings/eslint-plugin-jest.d.ts b/typings/eslint-plugin-jest.d.ts new file mode 100644 index 00000000000..c7ccc8577e8 --- /dev/null +++ b/typings/eslint-plugin-jest.d.ts @@ -0,0 +1,21 @@ +declare module 'eslint-plugin-jest' { + import type { + ClassicConfig, + Linter, + } from '@typescript-eslint/utils/ts-eslint'; + + declare const exprt: { + configs: { + all: ClassicConfig.Config; + recommended: ClassicConfig.Config; + style: ClassicConfig.Config; + }; + environments: { + globals: { + globals: ClassicConfig.EnvironmentConfig; + }; + }; + rules: NonNullable; + }; + export = exprt; +} diff --git a/typings/eslint-plugin-jsdoc.d.ts b/typings/eslint-plugin-jsdoc.d.ts new file mode 100644 index 00000000000..3f7082bb233 --- /dev/null +++ b/typings/eslint-plugin-jsdoc.d.ts @@ -0,0 +1,25 @@ +declare module 'eslint-plugin-jsdoc' { + import type { + ClassicConfig, + FlatConfig, + Linter, + } from '@typescript-eslint/utils/ts-eslint'; + + declare const exprt: { + configs: { + 'flat/recommended': FlatConfig.Config; + 'flat/recommended-error': FlatConfig.Config; + 'flat/recommended-typescript': FlatConfig.Config; + 'flat/recommended-typescript-error': FlatConfig.Config; + 'flat/recommended-typescript-flavor': FlatConfig.Config; + 'flat/recommended-typescript-flavor-error': FlatConfig.Config; + }; + environments: { + globals: { + globals: ClassicConfig.EnvironmentConfig; + }; + }; + rules: NonNullable; + }; + export = exprt; +} diff --git a/typings/eslint-plugin-jsx-a11y.d.ts b/typings/eslint-plugin-jsx-a11y.d.ts new file mode 100644 index 00000000000..d5fba291eb1 --- /dev/null +++ b/typings/eslint-plugin-jsx-a11y.d.ts @@ -0,0 +1,15 @@ +declare module 'eslint-plugin-jsx-a11y' { + import type { + ClassicConfig, + Linter, + } from '@typescript-eslint/utils/ts-eslint'; + + declare const exprt: { + configs: { + recommended: ClassicConfig.Config; + strict: ClassicConfig.Config; + }; + rules: NonNullable; + }; + export = exprt; +} diff --git a/typings/eslint-plugin-react-hooks.d.ts b/typings/eslint-plugin-react-hooks.d.ts new file mode 100644 index 00000000000..ff5f964ccc0 --- /dev/null +++ b/typings/eslint-plugin-react-hooks.d.ts @@ -0,0 +1,14 @@ +declare module 'eslint-plugin-react-hooks' { + import type { + ClassicConfig, + Linter, + } from '@typescript-eslint/utils/ts-eslint'; + + declare const exprt: { + configs: { + recommended: ClassicConfig.Config; + }; + rules: NonNullable; + }; + export = exprt; +} diff --git a/typings/eslint-plugin-react.d.ts b/typings/eslint-plugin-react.d.ts new file mode 100644 index 00000000000..d1199b9c685 --- /dev/null +++ b/typings/eslint-plugin-react.d.ts @@ -0,0 +1,16 @@ +declare module 'eslint-plugin-react' { + import type { + ClassicConfig, + Linter, + } from '@typescript-eslint/utils/ts-eslint'; + + declare const exprt: { + configs: { + recommended: ClassicConfig.Config; + all: ClassicConfig.Config; + 'jsx-runtime': ClassicConfig.Config; + }; + rules: NonNullable; + }; + export = exprt; +} diff --git a/typings/eslint-plugin-simple-import-sort.d.ts b/typings/eslint-plugin-simple-import-sort.d.ts new file mode 100644 index 00000000000..fd515a9f662 --- /dev/null +++ b/typings/eslint-plugin-simple-import-sort.d.ts @@ -0,0 +1,8 @@ +declare module 'eslint-plugin-simple-import-sort' { + import type { Linter } from '@typescript-eslint/utils/ts-eslint'; + + declare const exprt: { + rules: NonNullable; + }; + export = exprt; +} diff --git a/typings/eslint-plugin-unicorn.d.ts b/typings/eslint-plugin-unicorn.d.ts new file mode 100644 index 00000000000..fa0e2752590 --- /dev/null +++ b/typings/eslint-plugin-unicorn.d.ts @@ -0,0 +1,15 @@ +declare module 'eslint-plugin-unicorn' { + import type { + ClassicConfig, + Linter, + } from '@typescript-eslint/utils/ts-eslint'; + + declare const exprt: { + configs: { + recommended: ClassicConfig.Config; + all: ClassicConfig.Config; + }; + rules: NonNullable; + }; + export = exprt; +} diff --git a/typings/eslint__eslintrc.d.ts b/typings/eslint__eslintrc.d.ts new file mode 100644 index 00000000000..78462ba5c88 --- /dev/null +++ b/typings/eslint__eslintrc.d.ts @@ -0,0 +1,22 @@ +declare module '@eslint/eslintrc' { + import type { + ClassicConfig, + FlatConfig, + } from '@typescript-eslint/utils/ts-eslint'; + + declare class FlatCompat { + constructor(options?: { + baseDirectory?: string; + resolvePluginsRelativeTo?: string; + }); + + config(eslintrcConfig: ClassicConfig.Config): FlatConfig.ConfigArray; + env(envConfig: ClassicConfig.EnvironmentConfig): FlatConfig.ConfigArray; + extends(...configsToExtend: string[]): FlatConfig.ConfigArray; + plugins(...plugins: string[]): FlatConfig.ConfigArray; + } + declare const exprt: { + FlatCompat: typeof FlatCompat; + }; + export = exprt; +} diff --git a/typings/eslint__js.d.ts b/typings/eslint__js.d.ts new file mode 100644 index 00000000000..6e50127f6b4 --- /dev/null +++ b/typings/eslint__js.d.ts @@ -0,0 +1,11 @@ +declare module '@eslint/js' { + import type { FlatConfig } from '@typescript-eslint/utils/ts-eslint'; + + declare const exprt: { + configs: { + all: FlatConfig.Config; + recommended: FlatConfig.Config; + }; + }; + export = exprt; +} diff --git a/yarn.lock b/yarn.lock index 3ca6d81842f..fda6cb7b6ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3265,7 +3265,14 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:8.56.0": +"@eslint/js@npm:*": + version: 8.53.0 + resolution: "@eslint/js@npm:8.53.0" + checksum: e0d5cfb0000aaee237c8e6d6d6e366faa60b1ef7f928ce17778373aa44d3b886368f6d5e1f97f913f0f16801aad016db8b8df78418c9d18825c15590328028af + languageName: node + linkType: hard + +"@eslint/js@npm:8.56.0, @eslint/js@npm:^8.56.0": version: 8.56.0 resolution: "@eslint/js@npm:8.56.0" checksum: 5804130574ef810207bdf321c265437814e7a26f4e6fac9b496de3206afd52f533e09ec002a3be06cd9adcc9da63e727f1883938e663c4e4751c007d5b58e539 @@ -3296,13 +3303,13 @@ __metadata: linkType: hard "@humanwhocodes/config-array@npm:^0.11.13": - version: 0.11.14 - resolution: "@humanwhocodes/config-array@npm:0.11.14" + version: 0.11.13 + resolution: "@humanwhocodes/config-array@npm:0.11.13" dependencies: - "@humanwhocodes/object-schema": ^2.0.2 - debug: ^4.3.1 + "@humanwhocodes/object-schema": ^2.0.1 + debug: ^4.1.1 minimatch: ^3.0.5 - checksum: 861ccce9eaea5de19546653bccf75bf09fe878bc39c3aab00aeee2d2a0e654516adad38dd1098aab5e3af0145bbcbf3f309bdf4d964f8dab9dcd5834ae4c02f2 + checksum: f8ea57b0d7ed7f2d64cd3944654976829d9da91c04d9c860e18804729a33f7681f78166ef4c761850b8c324d362f7d53f14c5c44907a6b38b32c703ff85e4805 languageName: node linkType: hard @@ -3313,10 +3320,10 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^2.0.2": - version: 2.0.2 - resolution: "@humanwhocodes/object-schema@npm:2.0.2" - checksum: 2fc11503361b5fb4f14714c700c02a3f4c7c93e9acd6b87a29f62c522d90470f364d6161b03d1cc618b979f2ae02aed1106fd29d302695d8927e2fc8165ba8ee +"@humanwhocodes/object-schema@npm:^2.0.1": + version: 2.0.1 + resolution: "@humanwhocodes/object-schema@npm:2.0.1" + checksum: 24929487b1ed48795d2f08346a0116cc5ee4634848bce64161fb947109352c562310fd159fc64dda0e8b853307f5794605191a9547f7341158559ca3c8262a45 languageName: node linkType: hard @@ -5496,6 +5503,7 @@ __metadata: dependencies: "@nx/devkit": "*" "@prettier/sync": "*" + cross-env: "*" cross-fetch: "*" execa: "*" prettier: ^3.0.3 @@ -5619,6 +5627,8 @@ __metadata: "@babel/eslint-parser": ^7.23.3 "@babel/parser": ^7.23.3 "@babel/types": ^7.23.3 + "@eslint/eslintrc": ^2.1.4 + "@eslint/js": ^8.56.0 "@nx/eslint": 17.2.8 "@nx/jest": 17.2.8 "@nx/workspace": 17.2.8 @@ -5640,6 +5650,7 @@ __metadata: "@types/yargs": ^17.0.32 "@typescript-eslint/eslint-plugin-internal": "workspace:^" console-fail-test: ^0.2.3 + cross-env: ^7.0.3 cross-fetch: ^4.0.0 cspell: ^7.0.0 downlevel-dts: ">=0.11.0" @@ -5657,6 +5668,7 @@ __metadata: eslint-plugin-unicorn: ^50.0.1 execa: 7.1.1 glob: ^10.3.3 + globals: ^13.23.0 husky: ^8.0.3 jest: 29.7.0 jest-diff: ^29.6.2 @@ -5797,7 +5809,7 @@ __metadata: version: 0.0.0-use.local resolution: "@typescript-eslint/website-eslint@workspace:packages/website-eslint" dependencies: - "@eslint/js": 8.56.0 + "@eslint/js": "*" "@typescript-eslint/eslint-plugin": 6.20.0 "@typescript-eslint/parser": 6.20.0 "@typescript-eslint/scope-manager": 6.20.0 @@ -7980,6 +7992,18 @@ __metadata: languageName: node linkType: hard +"cross-env@npm:*, cross-env@npm:^7.0.3": + version: 7.0.3 + resolution: "cross-env@npm:7.0.3" + dependencies: + cross-spawn: ^7.0.1 + bin: + cross-env: src/bin/cross-env.js + cross-env-shell: src/bin/cross-env-shell.js + checksum: 26f2f3ea2ab32617f57effb70d329c2070d2f5630adc800985d8b30b56e8bf7f5f439dd3a0358b79cee6f930afc23cf8e23515f17ccfb30092c6b62c6b630a79 + languageName: node + linkType: hard + "cross-fetch@npm:*, cross-fetch@npm:^4.0.0": version: 4.0.0 resolution: "cross-fetch@npm:4.0.0" @@ -7998,7 +8022,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" dependencies: @@ -10733,6 +10757,15 @@ __metadata: languageName: node linkType: hard +"globals@npm:^13.23.0": + version: 13.23.0 + resolution: "globals@npm:13.23.0" + dependencies: + type-fest: ^0.20.2 + checksum: 194c97cf8d1ef6ba59417234c2386549c4103b6e5f24b1ff1952de61a4753e5d2069435ba629de711a6480b1b1d114a98e2ab27f85e966d5a10c319c3bbd3dc3 + languageName: node + linkType: hard + "globalthis@npm:^1.0.3": version: 1.0.3 resolution: "globalthis@npm:1.0.3" From 70f8692f7ba69fc90918ee0bf496009d22b408a1 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Fri, 2 Feb 2024 18:00:41 +1030 Subject: [PATCH 02/10] add mjs config to nx.json --- nx.json | 1 + 1 file changed, 1 insertion(+) diff --git a/nx.json b/nx.json index 0ba06155f9c..0153da4c1a5 100644 --- a/nx.json +++ b/nx.json @@ -47,6 +47,7 @@ "default", "{workspaceRoot}/package.json", "{workspaceRoot}/eslint.config.js", + "{workspaceRoot}/eslint.config.mjs", "{workspaceRoot}/yarn.lock", "{workspaceRoot}/.eslintignore", { From efaa2fef655d06e246dae3fe26eacd8e8c527879 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sun, 4 Feb 2024 13:03:10 +1100 Subject: [PATCH 03/10] some docs --- docs/packages/Core.mdx | 12 -- docs/packages/TypeScript_ESLint.mdx | 130 ++++++++++++++++++ .../typescript-eslint/src/config-helper.ts | 10 +- 3 files changed, 135 insertions(+), 17 deletions(-) delete mode 100644 docs/packages/Core.mdx create mode 100644 docs/packages/TypeScript_ESLint.mdx diff --git a/docs/packages/Core.mdx b/docs/packages/Core.mdx deleted file mode 100644 index d20d5dcd33d..00000000000 --- a/docs/packages/Core.mdx +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: core -sidebar_label: core ---- - -# `@typescript-eslint/core` - -> Tooling which enables you to use TypeScript with ESLint - -This package is the main entrypoint that you can use to consume our tooling with ESLint. - -// TODO(bradzacher) - docs on using the tooling diff --git a/docs/packages/TypeScript_ESLint.mdx b/docs/packages/TypeScript_ESLint.mdx new file mode 100644 index 00000000000..6a5ffbf5458 --- /dev/null +++ b/docs/packages/TypeScript_ESLint.mdx @@ -0,0 +1,130 @@ +--- +id: typescript-eslint +sidebar_label: typescript-eslint +--- + +# `typescript-eslint` + +> Tooling which enables you to use TypeScript with ESLint + +This package is the main entrypoint that you can use to consume our tooling with ESLint. + +This package exports the following: + +| name | description | +| --------- | ---------------------------------------------------------- | +| `config` | A utility function for creating type-safe flat configs | +| `configs` | [Our eslint (flat) configs](../linting/Configurations.mdx) | +| `parser` | [Our parser](./Parser.mdx) | +| `plugin` | [Our plugin](./ESLint_Plugin.mdx) | + +## Usage + +The `tseslint.config` function is a simple utility which does a few things. First and foremost it is a strictly typed function - meaning it allows you to write type-safe configs! + +The simplest usage would be: + +```js title="eslint.config.js" +// @ts-check + +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, +); +``` + +You can also declare our plugin and parser in your config via this package. For example this config would enable our plugin, our parser, and type-aware linting with a few of our popular type-aware rules: + +```js title="eslint.config.js" +// @ts-check + +import tseslint from 'typescript-eslint'; + +export default tseslint.config( + { + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + languageOptions: { + parser: tseslint.parser,, + parserOptions: { + project: true, + }, + }, + rules: { + '@typescript-eslint/no-unsafe-argument': 'error', + '@typescript-eslint/no-unsafe-assignment': 'error', + '@typescript-eslint/no-unsafe-call': 'error', + '@typescript-eslint/no-unsafe-member-access': 'error', + '@typescript-eslint/no-unsafe-return': 'error', + }, + }, +); +``` + +:::warning +We **_strongly_** recommend declaring our plugin with the namespace `@typescript-eslint` as shown above. If you use our shared configs this is the namespace that they use. This has been the standard namespace for our plugin for many years and is what users are most familiar with. + +You may, however, choose to use a different namespace if you wish - but note that currently [flat configs allow the same plugin to be registered and configured under multiple namespaces](https://github.com/eslint/eslint/discussions/17766). This means that it's possible to have a plugin's rule turned on twice under different namespaces - leading to double reporting! + +If you choose your own namespace - you are responsible for ensuring that you deduplicate usages of the plugin to ensure each rule is only enabled once. +::: + +### Flat config `extends` + +The `tseslint.config` utility function also adds handling for the `extends` property on flat config objects. This allows you to more easily extend shared configs for specific file patterns whilst also overriding rules/options provided by those configs: + +```js +export default tseslint.config({ + files: ['**/*.ts'], + extends: [ + eslint.configs.recommended, + ...tseslint.configs.recommended, + ], + rules: { + '@typescript-eslint/array-type': 'error', + '@typescript-eslint/consistent-type-imports': 'error', + }, +}); + +// is the same as writing + +export default [ + ...[ + eslint.configs.recommended, + ...tseslint.configs.recommended, + ].map(conf => ({ + ...conf, + files: ['**/*.ts'], + })), + { + files: ['**/*.ts'], + rules: { + '@typescript-eslint/array-type': 'error', + '@typescript-eslint/consistent-type-imports': 'error', + }, + }, +]; +``` + +We found that this is a pretty common operation when writing ESLint configs which is why we provided this convenience property - for example in codebases with type-aware linting a config object like this is a very common way to disable TS-specific linting setups on JS files: + +```js +export default [ + { + files: ['**/*.js'], + extends: [tseslint.configs.disableTypeChecked], + rules: { + // turn off other type-aware rules + 'deprecation/deprecation': 'off', + '@typescript-eslint/internal/no-poorly-typed-ts-props': 'off', + + // turn off rules that don't apply to JS code + '@typescript-eslint/explicit-function-return-type': 'off', + }, + }, +]; +``` diff --git a/packages/typescript-eslint/src/config-helper.ts b/packages/typescript-eslint/src/config-helper.ts index 4e1565ca814..51af07ca639 100644 --- a/packages/typescript-eslint/src/config-helper.ts +++ b/packages/typescript-eslint/src/config-helper.ts @@ -24,13 +24,13 @@ interface ConfigWithExtends extends FlatConfig.Config { * * export default [ * { - * files: ['** /*.ts'], * ...eslint.configs.recommended, - * }, - * { * files: ['** /*.ts'], - * ...tseslint.configs.recommended, * }, + * ...tseslint.configs.recommended.map(conf => ({ + * ...conf, + * files: ['** /*.ts'], + * })), * { * files: ['** /*.ts'], * rules: { @@ -54,8 +54,8 @@ interface ConfigWithExtends extends FlatConfig.Config { * import tseslint from 'typescript-eslint'; * * export default tseslint.config( - * tseslint.configs.recommended, * eslint.configs.recommended, + * ...tseslint.configs.recommended, * { * rules: { * '@typescript-eslint/array-type': 'error', From 8c35e3e72130ad9194096947edbea59ae259b9b8 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sun, 4 Feb 2024 13:13:51 +1100 Subject: [PATCH 04/10] remove cross-env --- package.json | 1 - packages/repo-tools/package.json | 1 - yarn.lock | 16 +--------------- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/package.json b/package.json index 76d617f5b32..eb0ce25086c 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,6 @@ "@types/yargs": "^17.0.32", "@typescript-eslint/eslint-plugin-internal": "workspace:^", "console-fail-test": "^0.2.3", - "cross-env": "^7.0.3", "cross-fetch": "^4.0.0", "cspell": "^7.0.0", "downlevel-dts": ">=0.11.0", diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index dc5a7affedb..d21677ad7e9 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -18,7 +18,6 @@ "devDependencies": { "@nx/devkit": "*", "@prettier/sync": "*", - "cross-env": "*", "cross-fetch": "*", "execa": "*", "prettier": "^3.0.3", diff --git a/yarn.lock b/yarn.lock index fda6cb7b6ad..6034e5272c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5503,7 +5503,6 @@ __metadata: dependencies: "@nx/devkit": "*" "@prettier/sync": "*" - cross-env: "*" cross-fetch: "*" execa: "*" prettier: ^3.0.3 @@ -5650,7 +5649,6 @@ __metadata: "@types/yargs": ^17.0.32 "@typescript-eslint/eslint-plugin-internal": "workspace:^" console-fail-test: ^0.2.3 - cross-env: ^7.0.3 cross-fetch: ^4.0.0 cspell: ^7.0.0 downlevel-dts: ">=0.11.0" @@ -7992,18 +7990,6 @@ __metadata: languageName: node linkType: hard -"cross-env@npm:*, cross-env@npm:^7.0.3": - version: 7.0.3 - resolution: "cross-env@npm:7.0.3" - dependencies: - cross-spawn: ^7.0.1 - bin: - cross-env: src/bin/cross-env.js - cross-env-shell: src/bin/cross-env-shell.js - checksum: 26f2f3ea2ab32617f57effb70d329c2070d2f5630adc800985d8b30b56e8bf7f5f439dd3a0358b79cee6f930afc23cf8e23515f17ccfb30092c6b62c6b630a79 - languageName: node - linkType: hard - "cross-fetch@npm:*, cross-fetch@npm:^4.0.0": version: 4.0.0 resolution: "cross-fetch@npm:4.0.0" @@ -8022,7 +8008,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" dependencies: From 99053d2081dbeb8d573120a3776b3d3b7402e70d Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Sun, 4 Feb 2024 13:42:12 +1100 Subject: [PATCH 05/10] regen configs --- packages/repo-tools/src/generate-configs.mts | 6 ++++-- packages/repo-tools/src/generate-lib.mts | 11 +++++++++-- packages/repo-tools/src/paths.mts | 1 + packages/typescript-eslint/src/configs/all.ts | 1 + .../src/configs/disable-type-checked.ts | 1 + 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/repo-tools/src/generate-configs.mts b/packages/repo-tools/src/generate-configs.mts index 8f4b6e083af..acf7819a461 100644 --- a/packages/repo-tools/src/generate-configs.mts +++ b/packages/repo-tools/src/generate-configs.mts @@ -14,7 +14,7 @@ import prettier from 'prettier'; import { PACKAGES_ESLINT_PLUGIN, PACKAGES_TYPESCRIPT_ESLINT, - REPO_ROOT, + PRETTIER_CONFIG_PATH, } from './paths.mts'; // no need for us to bring in an entire dependency for a few simple terminal colors @@ -57,7 +57,9 @@ async function main(): Promise { return [...AUTO_GENERATED_COMMENT_LINES, code].join('\n'); } - const prettierConfig = await prettier.resolveConfig(REPO_ROOT); + const prettierConfig = await prettier.resolveConfig('file.ts', { + config: PRETTIER_CONFIG_PATH, + }); type LinterConfigRules = Record; diff --git a/packages/repo-tools/src/generate-lib.mts b/packages/repo-tools/src/generate-lib.mts index b732dfc62c1..bd15eadd2a4 100644 --- a/packages/repo-tools/src/generate-lib.mts +++ b/packages/repo-tools/src/generate-lib.mts @@ -16,7 +16,12 @@ import { ESLint } from '@typescript-eslint/utils/ts-eslint'; import { rimraf } from 'rimraf'; import ts from 'typescript'; -import { PACKAGES_SCOPE_MANAGER, PACKAGES_TYPES, REPO_ROOT } from './paths.mts'; +import { + PACKAGES_SCOPE_MANAGER, + PACKAGES_TYPES, + PRETTIER_CONFIG_PATH, + REPO_ROOT, +} from './paths.mts'; function parseAndAnalyze( code: string, @@ -57,7 +62,9 @@ function addAutoGeneratedComment(code: string[]): string { ].join('\n'); } -const PRETTIER_CONFIG = prettier.resolveConfig(REPO_ROOT); +const PRETTIER_CONFIG = prettier.resolveConfig('file.ts', { + config: PRETTIER_CONFIG_PATH, +}); const TS_LIB_FOLDER = path.join(REPO_ROOT, 'node_modules', 'typescript', 'lib'); const OUTPUT_FOLDER = path.join(PACKAGES_SCOPE_MANAGER, 'src', 'lib'); const TYPES_FILE = path.join(PACKAGES_TYPES, 'src', 'lib.ts'); diff --git a/packages/repo-tools/src/paths.mts b/packages/repo-tools/src/paths.mts index 9e158a4426b..6f761c6877c 100644 --- a/packages/repo-tools/src/paths.mts +++ b/packages/repo-tools/src/paths.mts @@ -14,3 +14,4 @@ export const PACKAGES_TYPESCRIPT_ESLINT = path.join( 'typescript-eslint', ); export const PACKAGES_WEBSITE = path.join(PACKAGES, 'website'); +export const PRETTIER_CONFIG_PATH = path.join(REPO_ROOT, '.prettierrc.json'); diff --git a/packages/typescript-eslint/src/configs/all.ts b/packages/typescript-eslint/src/configs/all.ts index e366ba92800..f891ff9b92f 100644 --- a/packages/typescript-eslint/src/configs/all.ts +++ b/packages/typescript-eslint/src/configs/all.ts @@ -129,6 +129,7 @@ export default ( 'prefer-destructuring': 'off', '@typescript-eslint/prefer-destructuring': 'error', '@typescript-eslint/prefer-enum-initializers': 'error', + '@typescript-eslint/prefer-find': 'error', '@typescript-eslint/prefer-for-of': 'error', '@typescript-eslint/prefer-function-type': 'error', '@typescript-eslint/prefer-includes': 'error', diff --git a/packages/typescript-eslint/src/configs/disable-type-checked.ts b/packages/typescript-eslint/src/configs/disable-type-checked.ts index fa52425e4f4..da3a0b7256d 100644 --- a/packages/typescript-eslint/src/configs/disable-type-checked.ts +++ b/packages/typescript-eslint/src/configs/disable-type-checked.ts @@ -43,6 +43,7 @@ export default ( '@typescript-eslint/no-useless-template-literals': 'off', '@typescript-eslint/non-nullable-type-assertion-style': 'off', '@typescript-eslint/prefer-destructuring': 'off', + '@typescript-eslint/prefer-find': 'off', '@typescript-eslint/prefer-includes': 'off', '@typescript-eslint/prefer-nullish-coalescing': 'off', '@typescript-eslint/prefer-optional-chain': 'off', From 9d3b7c624eeae47e09836a307b3d3422f88c9f60 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Tue, 6 Feb 2024 15:19:26 +1030 Subject: [PATCH 06/10] patch nx/eslint --- .../@nx-eslint-npm-17.3.1-a2f85d8c50.patch | 14 +++++++++++++ eslint.config.mjs | 1 + package.json | 3 ++- yarn.lock | 21 +++++++++++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 .yarn/patches/@nx-eslint-npm-17.3.1-a2f85d8c50.patch diff --git a/.yarn/patches/@nx-eslint-npm-17.3.1-a2f85d8c50.patch b/.yarn/patches/@nx-eslint-npm-17.3.1-a2f85d8c50.patch new file mode 100644 index 00000000000..66c1c932b81 --- /dev/null +++ b/.yarn/patches/@nx-eslint-npm-17.3.1-a2f85d8c50.patch @@ -0,0 +1,14 @@ +diff --git a/src/executors/lint/utility/eslint-utils.js b/src/executors/lint/utility/eslint-utils.js +index e5c40146ef3560047c0e3db88c80f8bf5042602a..86388a67f570249c2a23df47662a878ed774968b 100644 +--- a/src/executors/lint/utility/eslint-utils.js ++++ b/src/executors/lint/utility/eslint-utils.js +@@ -38,7 +38,8 @@ async function resolveAndInstantiateESLint(eslintConfigPath, options, useFlatCon + * not be any html files in the project, so keeping it true would break linting every time + */ + errorOnUnmatchedPattern: false, +- reportUnusedDisableDirectives: options.reportUnusedDisableDirectives || undefined, ++ // https://github.com/nrwl/nx/pull/21405 ++ // reportUnusedDisableDirectives: options.reportUnusedDisableDirectives || undefined, + }; + if (useFlatConfig) { + if (typeof options.useEslintrc !== 'undefined') { diff --git a/eslint.config.mjs b/eslint.config.mjs index 8ebb01f6953..66399e018ce 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -413,6 +413,7 @@ export default tseslint.config( 'packages/eslint-plugin-internal/src/rules/**/*.{ts,tsx,cts,mts}', 'packages/eslint-plugin-tslint/src/rules/**/*.{ts,tsx,cts,mts}', 'packages/eslint-plugin/src/configs/**/*.{ts,tsx,cts,mts}', + 'packages/typescript-eslint/src/configs/**/*.{ts,tsx,cts,mts}', 'packages/core/src/configs/**/*.{ts,tsx,cts,mts}', 'packages/eslint-plugin/src/rules/**/*.{ts,tsx,cts,mts}', ], diff --git a/package.json b/package.json index 0c60b629dee..a611dc176d8 100644 --- a/package.json +++ b/package.json @@ -141,7 +141,8 @@ "pretty-format": "^29", "react-split-pane@^0.1.92": "patch:react-split-pane@npm%3A0.1.92#./.yarn/patches/react-split-pane-npm-0.1.92-93dbf51dff.patch", "tsx": "^4.6.2", - "typescript": "5.3.3" + "typescript": "5.3.3", + "@nx/eslint@17.3.1": "patch:@nx/eslint@npm%3A17.3.1#./.yarn/patches/@nx-eslint-npm-17.3.1-a2f85d8c50.patch" }, "packageManager": "yarn@3.7.0" } diff --git a/yarn.lock b/yarn.lock index c04d5859504..d1eb45bf973 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4792,6 +4792,27 @@ __metadata: languageName: node linkType: hard +"@nx/eslint@patch:@nx/eslint@npm%3A17.3.1#./.yarn/patches/@nx-eslint-npm-17.3.1-a2f85d8c50.patch::locator=%40typescript-eslint%2Ftypescript-eslint%40workspace%3A.": + version: 17.3.1 + resolution: "@nx/eslint@patch:@nx/eslint@npm%3A17.3.1#./.yarn/patches/@nx-eslint-npm-17.3.1-a2f85d8c50.patch::version=17.3.1&hash=406746&locator=%40typescript-eslint%2Ftypescript-eslint%40workspace%3A." + dependencies: + "@nx/devkit": 17.3.1 + "@nx/js": 17.3.1 + "@nx/linter": 17.3.1 + tslib: ^2.3.0 + typescript: ~5.3.2 + peerDependencies: + eslint: ^8.0.0 + js-yaml: 4.1.0 + peerDependenciesMeta: + eslint: + optional: true + js-yaml: + optional: true + checksum: 6fb0c072953a2a6d64a038590b4dbda134b55f32a1c12d2913a5de91f4208cd9b6dde866c4e15ba8ed6115a69d36f2cbf5cc72a53c8fcdaf96cf2b96060131b2 + languageName: node + linkType: hard + "@nx/jest@npm:17.3.1": version: 17.3.1 resolution: "@nx/jest@npm:17.3.1" From 063e388e9482b7b2df3020315da1041f1ec50ef5 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Tue, 6 Feb 2024 15:27:38 +1030 Subject: [PATCH 07/10] format --- tsconfig.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index ed328447a15..61c58f53fec 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "types": ["@types/node"], "noEmit": true, - "allowJs": true + "allowJs": true, }, "extends": "./tsconfig.base.json", "include": [ @@ -13,6 +13,6 @@ "eslint.config.js", "jest.config.base.js", "jest.config.js", - "jest.preset.js" - ] + "jest.preset.js", + ], } From bc5ac8bc62acbf6fe0f05711f9e46492c1528dec Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Tue, 6 Feb 2024 15:27:55 +1030 Subject: [PATCH 08/10] format --- tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/tsconfig.json b/tsconfig.json index 61c58f53fec..91c6d707c01 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,6 +11,7 @@ "tools/**/*.mts", ".eslintrc.js", "eslint.config.js", + "eslint.config.mjs", "jest.config.base.js", "jest.config.js", "jest.preset.js", From 92133c071e9f508f0cf50abd7a727483bdba55c4 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Tue, 6 Feb 2024 16:08:23 +1030 Subject: [PATCH 09/10] format --- docs/packages/TypeScript_ESLint.mdx | 34 ++++++++++++++--------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/docs/packages/TypeScript_ESLint.mdx b/docs/packages/TypeScript_ESLint.mdx index 6a5ffbf5458..b9d2b84c1fb 100644 --- a/docs/packages/TypeScript_ESLint.mdx +++ b/docs/packages/TypeScript_ESLint.mdx @@ -43,26 +43,24 @@ You can also declare our plugin and parser in your config via this package. For import tseslint from 'typescript-eslint'; -export default tseslint.config( - { - plugins: { - '@typescript-eslint': tseslint.plugin, - }, - languageOptions: { - parser: tseslint.parser,, - parserOptions: { - project: true, - }, - }, - rules: { - '@typescript-eslint/no-unsafe-argument': 'error', - '@typescript-eslint/no-unsafe-assignment': 'error', - '@typescript-eslint/no-unsafe-call': 'error', - '@typescript-eslint/no-unsafe-member-access': 'error', - '@typescript-eslint/no-unsafe-return': 'error', +export default tseslint.config({ + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + languageOptions: { + parser: tseslint.parser, + parserOptions: { + project: true, }, }, -); + rules: { + '@typescript-eslint/no-unsafe-argument': 'error', + '@typescript-eslint/no-unsafe-assignment': 'error', + '@typescript-eslint/no-unsafe-call': 'error', + '@typescript-eslint/no-unsafe-member-access': 'error', + '@typescript-eslint/no-unsafe-return': 'error', + }, +}); ``` :::warning From 69084bd572ee17e706d331d4647422ce0e1252bd Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Wed, 7 Feb 2024 11:58:27 +1030 Subject: [PATCH 10/10] review fixes --- docs/Packages.mdx | 1 + docs/packages/TypeScript_ESLint.mdx | 4 +--- packages/website/sidebars/sidebar.base.js | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Packages.mdx b/docs/Packages.mdx index 52406fc6c11..e26b877518f 100644 --- a/docs/Packages.mdx +++ b/docs/Packages.mdx @@ -14,5 +14,6 @@ They are: - [`@typescript-eslint/parser`](./packages/Parser.mdx): An ESLint parser which allows for ESLint to lint TypeScript source code. - [`@typescript-eslint/rule-tester`](./packages/Rule_Tester.mdx): A utility for testing ESLint rules. - [`@typescript-eslint/scope-manager`](./packages/Scope_Manager.mdx): A fork of [`eslint-scope`](https://github.com/eslint/eslint-scope), enhanced to support TypeScript functionality. +- [`@typescript-eslint/typescript-eslint`](./packages/TypeScript_ESLint.mdx): Tooling which enables you to use TypeScript with ESLint. - [`@typescript-eslint/typescript-estree`](./packages/TypeScript_ESTree.mdx): The underlying code used by [`@typescript-eslint/parser`](./packages/Parser.mdx) that converts TypeScript source code into an [ESTree](https://github.com/estree/estree)-compatible form. - [`@typescript-eslint/utils`](./packages/Utils.mdx): Utilities for working with TypeScript + ESLint together. diff --git a/docs/packages/TypeScript_ESLint.mdx b/docs/packages/TypeScript_ESLint.mdx index b9d2b84c1fb..8f0463620e8 100644 --- a/docs/packages/TypeScript_ESLint.mdx +++ b/docs/packages/TypeScript_ESLint.mdx @@ -66,9 +66,7 @@ export default tseslint.config({ :::warning We **_strongly_** recommend declaring our plugin with the namespace `@typescript-eslint` as shown above. If you use our shared configs this is the namespace that they use. This has been the standard namespace for our plugin for many years and is what users are most familiar with. -You may, however, choose to use a different namespace if you wish - but note that currently [flat configs allow the same plugin to be registered and configured under multiple namespaces](https://github.com/eslint/eslint/discussions/17766). This means that it's possible to have a plugin's rule turned on twice under different namespaces - leading to double reporting! - -If you choose your own namespace - you are responsible for ensuring that you deduplicate usages of the plugin to ensure each rule is only enabled once. +You may choose a different namespace - but note that currently [flat configs allow the same plugin to be registered, configured, and have duplicate reports under multiple namespaces](https://github.com/eslint/eslint/discussions/17766). ::: ### Flat config `extends` diff --git a/packages/website/sidebars/sidebar.base.js b/packages/website/sidebars/sidebar.base.js index b4f68f1ac8d..4184f99dad0 100644 --- a/packages/website/sidebars/sidebar.base.js +++ b/packages/website/sidebars/sidebar.base.js @@ -72,6 +72,7 @@ module.exports = { 'packages/rule-tester', 'packages/scope-manager', 'packages/typescript-estree', + 'packages/typescript-eslint', 'packages/utils', ], label: 'Packages',