diff --git a/.markdownlint.json b/.markdownlint.json index ba2c8eb2df50..55f1ef99429f 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -69,7 +69,8 @@ "details", "summary", "Tabs", - "TabItem" + "TabItem", + "RulesTable" ] }, // MD034/no-bare-urls - Bare URL used diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 60a769945183..5124ce841ae0 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -89,152 +89,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int ## Supported Rules - - -**Key**: :white_check_mark: = recommended, :lock: = strict, :wrench: = fixable, :thought_balloon: = requires type information - -| Name | Description | :white_check_mark::lock: | :wrench: | :thought_balloon: | -| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------ | -------- | ----------------- | -| [`@typescript-eslint/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md) | Require that member overloads be consecutive | :white_check_mark: | | | -| [`@typescript-eslint/array-type`](./docs/rules/array-type.md) | Require using either `T[]` or `Array` for arrays | :lock: | :wrench: | | -| [`@typescript-eslint/await-thenable`](./docs/rules/await-thenable.md) | Disallow awaiting a value that is not a Thenable | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/ban-ts-comment`](./docs/rules/ban-ts-comment.md) | Disallow `@ts-` comments or require descriptions after directive | :white_check_mark: | | | -| [`@typescript-eslint/ban-tslint-comment`](./docs/rules/ban-tslint-comment.md) | Disallow `// tslint:` comments | :lock: | :wrench: | | -| [`@typescript-eslint/ban-types`](./docs/rules/ban-types.md) | Disallow certain types | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/class-literal-property-style`](./docs/rules/class-literal-property-style.md) | Enforce that literals on classes are exposed in a consistent style | :lock: | :wrench: | | -| [`@typescript-eslint/consistent-generic-constructors`](./docs/rules/consistent-generic-constructors.md) | Enforce specifying generic type arguments on type annotation or constructor name of a constructor call | :lock: | :wrench: | | -| [`@typescript-eslint/consistent-indexed-object-style`](./docs/rules/consistent-indexed-object-style.md) | Require or disallow the `Record` type | :lock: | :wrench: | | -| [`@typescript-eslint/consistent-type-assertions`](./docs/rules/consistent-type-assertions.md) | Enforce consistent usage of type assertions | :lock: | | | -| [`@typescript-eslint/consistent-type-definitions`](./docs/rules/consistent-type-definitions.md) | Enforce type definitions to consistently use either `interface` or `type` | :lock: | :wrench: | | -| [`@typescript-eslint/consistent-type-exports`](./docs/rules/consistent-type-exports.md) | Enforce consistent usage of type exports | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/consistent-type-imports`](./docs/rules/consistent-type-imports.md) | Enforce consistent usage of type imports | | :wrench: | | -| [`@typescript-eslint/explicit-function-return-type`](./docs/rules/explicit-function-return-type.md) | Require explicit return types on functions and class methods | | | | -| [`@typescript-eslint/explicit-member-accessibility`](./docs/rules/explicit-member-accessibility.md) | Require explicit accessibility modifiers on class properties and methods | | :wrench: | | -| [`@typescript-eslint/explicit-module-boundary-types`](./docs/rules/explicit-module-boundary-types.md) | Require explicit return and argument types on exported functions' and classes' public class methods | | | | -| [`@typescript-eslint/member-delimiter-style`](./docs/rules/member-delimiter-style.md) | Require a specific member delimiter style for interfaces and type literals | | :wrench: | | -| [`@typescript-eslint/member-ordering`](./docs/rules/member-ordering.md) | Require a consistent member declaration order | | | | -| [`@typescript-eslint/method-signature-style`](./docs/rules/method-signature-style.md) | Enforce using a particular method signature syntax | | :wrench: | | -| [`@typescript-eslint/naming-convention`](./docs/rules/naming-convention.md) | Enforce naming conventions for everything across a codebase | | | :thought_balloon: | -| [`@typescript-eslint/no-base-to-string`](./docs/rules/no-base-to-string.md) | Require `.toString()` to only be called on objects which provide useful information when stringified | :lock: | | :thought_balloon: | -| [`@typescript-eslint/no-confusing-non-null-assertion`](./docs/rules/no-confusing-non-null-assertion.md) | Disallow non-null assertion in locations that may be confusing | :lock: | :wrench: | | -| [`@typescript-eslint/no-confusing-void-expression`](./docs/rules/no-confusing-void-expression.md) | Require expressions of type void to appear in statement position | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-duplicate-enum-values`](./docs/rules/no-duplicate-enum-values.md) | Disallow duplicate enum member values | :lock: | | | -| [`@typescript-eslint/no-dynamic-delete`](./docs/rules/no-dynamic-delete.md) | Disallow using the `delete` operator on computed key expressions | :lock: | :wrench: | | -| [`@typescript-eslint/no-empty-interface`](./docs/rules/no-empty-interface.md) | Disallow the declaration of empty interfaces | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-explicit-any`](./docs/rules/no-explicit-any.md) | Disallow the `any` type | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-extra-non-null-assertion`](./docs/rules/no-extra-non-null-assertion.md) | Disallow extra non-null assertion | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-extraneous-class`](./docs/rules/no-extraneous-class.md) | Disallow classes used as namespaces | :lock: | | | -| [`@typescript-eslint/no-floating-promises`](./docs/rules/no-floating-promises.md) | Require Promise-like statements to be handled appropriately | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-for-in-array`](./docs/rules/no-for-in-array.md) | Disallow iterating over an array with a for-in loop | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-inferrable-types`](./docs/rules/no-inferrable-types.md) | Disallow explicit type declarations for variables or parameters initialized to a number, string, or boolean | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-invalid-void-type`](./docs/rules/no-invalid-void-type.md) | Disallow `void` type outside of generic or return types | :lock: | | | -| [`@typescript-eslint/no-meaningless-void-operator`](./docs/rules/no-meaningless-void-operator.md) | Disallow the `void` operator except when used to discard a value | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-misused-new`](./docs/rules/no-misused-new.md) | Enforce valid definition of `new` and `constructor` | :white_check_mark: | | | -| [`@typescript-eslint/no-misused-promises`](./docs/rules/no-misused-promises.md) | Disallow Promises in places not designed to handle them | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-namespace`](./docs/rules/no-namespace.md) | Disallow custom TypeScript modules and namespaces | :white_check_mark: | | | -| [`@typescript-eslint/no-non-null-asserted-nullish-coalescing`](./docs/rules/no-non-null-asserted-nullish-coalescing.md) | Disallow non-null assertions in the left operand of a nullish coalescing operator | :lock: | | | -| [`@typescript-eslint/no-non-null-asserted-optional-chain`](./docs/rules/no-non-null-asserted-optional-chain.md) | Disallow non-null assertions after an optional chain expression | :white_check_mark: | | | -| [`@typescript-eslint/no-non-null-assertion`](./docs/rules/no-non-null-assertion.md) | Disallow non-null assertions using the `!` postfix operator | :white_check_mark: | | | -| [`@typescript-eslint/no-redundant-type-constituents`](./docs/rules/no-redundant-type-constituents.md) | Disallow members of unions and intersections that do nothing or override type information | | | :thought_balloon: | -| [`@typescript-eslint/no-require-imports`](./docs/rules/no-require-imports.md) | Disallow invocation of `require()` | | | | -| [`@typescript-eslint/no-this-alias`](./docs/rules/no-this-alias.md) | Disallow aliasing `this` | :white_check_mark: | | | -| [`@typescript-eslint/no-type-alias`](./docs/rules/no-type-alias.md) | Disallow type aliases | | | | -| [`@typescript-eslint/no-unnecessary-boolean-literal-compare`](./docs/rules/no-unnecessary-boolean-literal-compare.md) | Disallow unnecessary equality comparisons against boolean literals | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-condition`](./docs/rules/no-unnecessary-condition.md) | Disallow conditionals where the type is always truthy or always falsy | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-qualifier`](./docs/rules/no-unnecessary-qualifier.md) | Disallow unnecessary namespace qualifiers | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-type-arguments`](./docs/rules/no-unnecessary-type-arguments.md) | Disallow type arguments that are equal to the default | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-type-assertion`](./docs/rules/no-unnecessary-type-assertion.md) | Disallow type assertions that do not change the type of an expression | :white_check_mark: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-type-constraint`](./docs/rules/no-unnecessary-type-constraint.md) | Disallow unnecessary constraints on generic types | :white_check_mark: | | | -| [`@typescript-eslint/no-unsafe-argument`](./docs/rules/no-unsafe-argument.md) | Disallow calling a function with a value with type `any` | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-unsafe-assignment`](./docs/rules/no-unsafe-assignment.md) | Disallow assigning a value with type `any` to variables and properties | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-unsafe-call`](./docs/rules/no-unsafe-call.md) | Disallow calling a value with type `any` | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-unsafe-member-access`](./docs/rules/no-unsafe-member-access.md) | Disallow member access on a value with type `any` | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-unsafe-return`](./docs/rules/no-unsafe-return.md) | Disallow returning a value with type `any` from a function | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-useless-empty-export`](./docs/rules/no-useless-empty-export.md) | Disallow empty exports that don't change anything in a module file | | :wrench: | | -| [`@typescript-eslint/no-var-requires`](./docs/rules/no-var-requires.md) | Disallow `require` statements except in import statements | :white_check_mark: | | | -| [`@typescript-eslint/non-nullable-type-assertion-style`](./docs/rules/non-nullable-type-assertion-style.md) | Enforce non-null assertions over explicit type casts | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/parameter-properties`](./docs/rules/parameter-properties.md) | Require or disallow parameter properties in class constructors | | | | -| [`@typescript-eslint/prefer-as-const`](./docs/rules/prefer-as-const.md) | Enforce the use of `as const` over literal type | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/prefer-enum-initializers`](./docs/rules/prefer-enum-initializers.md) | Require each enum member value to be explicitly initialized | | | | -| [`@typescript-eslint/prefer-for-of`](./docs/rules/prefer-for-of.md) | Enforce the use of `for-of` loop over the standard `for` loop where possible | :lock: | | | -| [`@typescript-eslint/prefer-function-type`](./docs/rules/prefer-function-type.md) | Enforce using function types instead of interfaces with call signatures | :lock: | :wrench: | | -| [`@typescript-eslint/prefer-includes`](./docs/rules/prefer-includes.md) | Enforce `includes` method over `indexOf` method | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-literal-enum-member`](./docs/rules/prefer-literal-enum-member.md) | Require all enum members to be literal values | :lock: | | | -| [`@typescript-eslint/prefer-namespace-keyword`](./docs/rules/prefer-namespace-keyword.md) | Require using `namespace` keyword over `module` keyword to declare custom TypeScript modules | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/prefer-nullish-coalescing`](./docs/rules/prefer-nullish-coalescing.md) | Enforce using the nullish coalescing operator instead of logical chaining | :lock: | | :thought_balloon: | -| [`@typescript-eslint/prefer-optional-chain`](./docs/rules/prefer-optional-chain.md) | Enforce using concise optional chain expressions instead of chained logical ands | :lock: | | | -| [`@typescript-eslint/prefer-readonly`](./docs/rules/prefer-readonly.md) | Require private members to be marked as `readonly` if they're never modified outside of the constructor | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-readonly-parameter-types`](./docs/rules/prefer-readonly-parameter-types.md) | Require function parameters to be typed as `readonly` to prevent accidental mutation of inputs | | | :thought_balloon: | -| [`@typescript-eslint/prefer-reduce-type-parameter`](./docs/rules/prefer-reduce-type-parameter.md) | Enforce using type parameter when calling `Array#reduce` instead of casting | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-regexp-exec`](./docs/rules/prefer-regexp-exec.md) | Enforce `RegExp#exec` over `String#match` if no global flag is provided | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-return-this-type`](./docs/rules/prefer-return-this-type.md) | Enforce that `this` is used when only `this` type is returned | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-string-starts-ends-with`](./docs/rules/prefer-string-starts-ends-with.md) | Enforce using `String#startsWith` and `String#endsWith` over other equivalent methods of checking substrings | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-ts-expect-error`](./docs/rules/prefer-ts-expect-error.md) | Enforce using `@ts-expect-error` over `@ts-ignore` | :lock: | :wrench: | | -| [`@typescript-eslint/promise-function-async`](./docs/rules/promise-function-async.md) | Require any function or method that returns a Promise to be marked async | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/require-array-sort-compare`](./docs/rules/require-array-sort-compare.md) | Require `Array#sort` calls to always provide a `compareFunction` | | | :thought_balloon: | -| [`@typescript-eslint/restrict-plus-operands`](./docs/rules/restrict-plus-operands.md) | Require both operands of addition to have type `number` or `string` | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/restrict-template-expressions`](./docs/rules/restrict-template-expressions.md) | Enforce template literal expressions to be of `string` type | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/sort-type-union-intersection-members`](./docs/rules/sort-type-union-intersection-members.md) | Enforce members of a type union/intersection to be sorted alphabetically | | :wrench: | | -| [`@typescript-eslint/strict-boolean-expressions`](./docs/rules/strict-boolean-expressions.md) | Disallow certain types in boolean expressions | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/switch-exhaustiveness-check`](./docs/rules/switch-exhaustiveness-check.md) | Require switch-case statements to be exhaustive with union type | | | :thought_balloon: | -| [`@typescript-eslint/triple-slash-reference`](./docs/rules/triple-slash-reference.md) | Disallow certain triple slash directives in favor of ES6-style import declarations | :white_check_mark: | | | -| [`@typescript-eslint/type-annotation-spacing`](./docs/rules/type-annotation-spacing.md) | Require consistent spacing around type annotations | | :wrench: | | -| [`@typescript-eslint/typedef`](./docs/rules/typedef.md) | Require type annotations in certain places | | | | -| [`@typescript-eslint/unbound-method`](./docs/rules/unbound-method.md) | Enforce unbound methods are called with their expected scope | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/unified-signatures`](./docs/rules/unified-signatures.md) | Disallow two overloads that could be unified into one with a union or an optional/rest parameter | :lock: | | | - - - -### Extension Rules - -In some cases, ESLint provides a rule itself, but it doesn't support TypeScript syntax; either it crashes, or it ignores the syntax, or it falsely reports against it. -In these cases, we create what we call an extension rule; a rule within our plugin that has the same functionality, but also supports TypeScript. - - - -**Key**: :white_check_mark: = recommended, :lock: = strict, :wrench: = fixable, :thought_balloon: = requires type information - -| Name | Description | :white_check_mark::lock: | :wrench: | :thought_balloon: | -| ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------ | -------- | ----------------- | -| [`@typescript-eslint/brace-style`](./docs/rules/brace-style.md) | Enforce consistent brace style for blocks | | :wrench: | | -| [`@typescript-eslint/comma-dangle`](./docs/rules/comma-dangle.md) | Require or disallow trailing commas | | :wrench: | | -| [`@typescript-eslint/comma-spacing`](./docs/rules/comma-spacing.md) | Enforce consistent spacing before and after commas | | :wrench: | | -| [`@typescript-eslint/default-param-last`](./docs/rules/default-param-last.md) | Enforce default parameters to be last | | | | -| [`@typescript-eslint/dot-notation`](./docs/rules/dot-notation.md) | Enforce dot notation whenever possible | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/func-call-spacing`](./docs/rules/func-call-spacing.md) | Require or disallow spacing between function identifiers and their invocations | | :wrench: | | -| [`@typescript-eslint/indent`](./docs/rules/indent.md) | Enforce consistent indentation | | :wrench: | | -| [`@typescript-eslint/init-declarations`](./docs/rules/init-declarations.md) | Require or disallow initialization in variable declarations | | | | -| [`@typescript-eslint/keyword-spacing`](./docs/rules/keyword-spacing.md) | Enforce consistent spacing before and after keywords | | :wrench: | | -| [`@typescript-eslint/lines-between-class-members`](./docs/rules/lines-between-class-members.md) | Require or disallow an empty line between class members | | :wrench: | | -| [`@typescript-eslint/no-array-constructor`](./docs/rules/no-array-constructor.md) | Disallow generic `Array` constructors | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-dupe-class-members`](./docs/rules/no-dupe-class-members.md) | Disallow duplicate class members | | | | -| [`@typescript-eslint/no-empty-function`](./docs/rules/no-empty-function.md) | Disallow empty functions | :white_check_mark: | | | -| [`@typescript-eslint/no-extra-parens`](./docs/rules/no-extra-parens.md) | Disallow unnecessary parentheses | | :wrench: | | -| [`@typescript-eslint/no-extra-semi`](./docs/rules/no-extra-semi.md) | Disallow unnecessary semicolons | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-implied-eval`](./docs/rules/no-implied-eval.md) | Disallow the use of `eval()`-like methods | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-invalid-this`](./docs/rules/no-invalid-this.md) | Disallow `this` keywords outside of classes or class-like objects | | | | -| [`@typescript-eslint/no-loop-func`](./docs/rules/no-loop-func.md) | Disallow function declarations that contain unsafe references inside loop statements | | | | -| [`@typescript-eslint/no-loss-of-precision`](./docs/rules/no-loss-of-precision.md) | Disallow literal numbers that lose precision | :white_check_mark: | | | -| [`@typescript-eslint/no-magic-numbers`](./docs/rules/no-magic-numbers.md) | Disallow magic numbers | | | | -| [`@typescript-eslint/no-redeclare`](./docs/rules/no-redeclare.md) | Disallow variable redeclaration | | | | -| [`@typescript-eslint/no-restricted-imports`](./docs/rules/no-restricted-imports.md) | Disallow specified modules when loaded by `import` | | | | -| [`@typescript-eslint/no-shadow`](./docs/rules/no-shadow.md) | Disallow variable declarations from shadowing variables declared in the outer scope | | | | -| [`@typescript-eslint/no-throw-literal`](./docs/rules/no-throw-literal.md) | Disallow throwing literals as exceptions | :lock: | | :thought_balloon: | -| [`@typescript-eslint/no-unused-expressions`](./docs/rules/no-unused-expressions.md) | Disallow unused expressions | | | | -| [`@typescript-eslint/no-unused-vars`](./docs/rules/no-unused-vars.md) | Disallow unused variables | :white_check_mark: | | | -| [`@typescript-eslint/no-use-before-define`](./docs/rules/no-use-before-define.md) | Disallow the use of variables before they are defined | | | | -| [`@typescript-eslint/no-useless-constructor`](./docs/rules/no-useless-constructor.md) | Disallow unnecessary constructors | :lock: | | | -| [`@typescript-eslint/object-curly-spacing`](./docs/rules/object-curly-spacing.md) | Enforce consistent spacing inside braces | | :wrench: | | -| [`@typescript-eslint/padding-line-between-statements`](./docs/rules/padding-line-between-statements.md) | Require or disallow padding lines between statements | | :wrench: | | -| [`@typescript-eslint/quotes`](./docs/rules/quotes.md) | Enforce the consistent use of either backticks, double, or single quotes | | :wrench: | | -| [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/return-await`](./docs/rules/return-await.md) | Enforce consistent returning of awaited values | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/semi`](./docs/rules/semi.md) | Require or disallow semicolons instead of ASI | | :wrench: | | -| [`@typescript-eslint/space-before-blocks`](./docs/rules/space-before-blocks.md) | Enforce consistent spacing before blocks | | :wrench: | | -| [`@typescript-eslint/space-before-function-paren`](./docs/rules/space-before-function-paren.md) | Enforce consistent spacing before function parenthesis | | :wrench: | | -| [`@typescript-eslint/space-infix-ops`](./docs/rules/space-infix-ops.md) | Require spacing around infix operators | | :wrench: | | - - +For the exhaustive list of supported rules, [please see our website](https://typescript-eslint.io/rules/). ## Contributing diff --git a/packages/eslint-plugin/docs/rules/README.md b/packages/eslint-plugin/docs/rules/README.md index 805f126f8228..7c6bd00d36df 100644 --- a/packages/eslint-plugin/docs/rules/README.md +++ b/packages/eslint-plugin/docs/rules/README.md @@ -11,149 +11,13 @@ See [Configs](/docs/linting/configs) for how to enable recommended rules using c ## Supported Rules - +import RulesTable from "@site/src/components/RulesTable"; -**Key**: :white_check_mark: = recommended, :lock: = strict, :wrench: = fixable, :thought_balloon: = requires type information - -| Name | Description | :white_check_mark::lock: | :wrench: | :thought_balloon: | -| ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------ | ------------------------ | -------- | ----------------- | -| [`@typescript-eslint/adjacent-overload-signatures`](./adjacent-overload-signatures.md) | Require that member overloads be consecutive | :white_check_mark: | | | -| [`@typescript-eslint/array-type`](./array-type.md) | Require using either `T[]` or `Array` for arrays | :lock: | :wrench: | | -| [`@typescript-eslint/await-thenable`](./await-thenable.md) | Disallow awaiting a value that is not a Thenable | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/ban-ts-comment`](./ban-ts-comment.md) | Disallow `@ts-` comments or require descriptions after directive | :white_check_mark: | | | -| [`@typescript-eslint/ban-tslint-comment`](./ban-tslint-comment.md) | Disallow `// tslint:` comments | :lock: | :wrench: | | -| [`@typescript-eslint/ban-types`](./ban-types.md) | Disallow certain types | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/class-literal-property-style`](./class-literal-property-style.md) | Enforce that literals on classes are exposed in a consistent style | :lock: | :wrench: | | -| [`@typescript-eslint/consistent-generic-constructors`](./consistent-generic-constructors.md) | Enforce specifying generic type arguments on type annotation or constructor name of a constructor call | :lock: | :wrench: | | -| [`@typescript-eslint/consistent-indexed-object-style`](./consistent-indexed-object-style.md) | Require or disallow the `Record` type | :lock: | :wrench: | | -| [`@typescript-eslint/consistent-type-assertions`](./consistent-type-assertions.md) | Enforce consistent usage of type assertions | :lock: | | | -| [`@typescript-eslint/consistent-type-definitions`](./consistent-type-definitions.md) | Enforce type definitions to consistently use either `interface` or `type` | :lock: | :wrench: | | -| [`@typescript-eslint/consistent-type-exports`](./consistent-type-exports.md) | Enforce consistent usage of type exports | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/consistent-type-imports`](./consistent-type-imports.md) | Enforce consistent usage of type imports | | :wrench: | | -| [`@typescript-eslint/explicit-function-return-type`](./explicit-function-return-type.md) | Require explicit return types on functions and class methods | | | | -| [`@typescript-eslint/explicit-member-accessibility`](./explicit-member-accessibility.md) | Require explicit accessibility modifiers on class properties and methods | | :wrench: | | -| [`@typescript-eslint/explicit-module-boundary-types`](./explicit-module-boundary-types.md) | Require explicit return and argument types on exported functions' and classes' public class methods | | | | -| [`@typescript-eslint/member-delimiter-style`](./member-delimiter-style.md) | Require a specific member delimiter style for interfaces and type literals | | :wrench: | | -| [`@typescript-eslint/member-ordering`](./member-ordering.md) | Require a consistent member declaration order | | | | -| [`@typescript-eslint/method-signature-style`](./method-signature-style.md) | Enforce using a particular method signature syntax | | :wrench: | | -| [`@typescript-eslint/naming-convention`](./naming-convention.md) | Enforce naming conventions for everything across a codebase | | | :thought_balloon: | -| [`@typescript-eslint/no-base-to-string`](./no-base-to-string.md) | Require `.toString()` to only be called on objects which provide useful information when stringified | :lock: | | :thought_balloon: | -| [`@typescript-eslint/no-confusing-non-null-assertion`](./no-confusing-non-null-assertion.md) | Disallow non-null assertion in locations that may be confusing | :lock: | :wrench: | | -| [`@typescript-eslint/no-confusing-void-expression`](./no-confusing-void-expression.md) | Require expressions of type void to appear in statement position | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-duplicate-enum-values`](./no-duplicate-enum-values.md) | Disallow duplicate enum member values | :lock: | | | -| [`@typescript-eslint/no-dynamic-delete`](./no-dynamic-delete.md) | Disallow using the `delete` operator on computed key expressions | :lock: | :wrench: | | -| [`@typescript-eslint/no-empty-interface`](./no-empty-interface.md) | Disallow the declaration of empty interfaces | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-explicit-any`](./no-explicit-any.md) | Disallow the `any` type | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-extra-non-null-assertion`](./no-extra-non-null-assertion.md) | Disallow extra non-null assertion | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-extraneous-class`](./no-extraneous-class.md) | Disallow classes used as namespaces | :lock: | | | -| [`@typescript-eslint/no-floating-promises`](./no-floating-promises.md) | Require Promise-like statements to be handled appropriately | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-for-in-array`](./no-for-in-array.md) | Disallow iterating over an array with a for-in loop | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-inferrable-types`](./no-inferrable-types.md) | Disallow explicit type declarations for variables or parameters initialized to a number, string, or boolean | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-invalid-void-type`](./no-invalid-void-type.md) | Disallow `void` type outside of generic or return types | :lock: | | | -| [`@typescript-eslint/no-meaningless-void-operator`](./no-meaningless-void-operator.md) | Disallow the `void` operator except when used to discard a value | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-misused-new`](./no-misused-new.md) | Enforce valid definition of `new` and `constructor` | :white_check_mark: | | | -| [`@typescript-eslint/no-misused-promises`](./no-misused-promises.md) | Disallow Promises in places not designed to handle them | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-namespace`](./no-namespace.md) | Disallow custom TypeScript modules and namespaces | :white_check_mark: | | | -| [`@typescript-eslint/no-non-null-asserted-nullish-coalescing`](./no-non-null-asserted-nullish-coalescing.md) | Disallow non-null assertions in the left operand of a nullish coalescing operator | :lock: | | | -| [`@typescript-eslint/no-non-null-asserted-optional-chain`](./no-non-null-asserted-optional-chain.md) | Disallow non-null assertions after an optional chain expression | :white_check_mark: | | | -| [`@typescript-eslint/no-non-null-assertion`](./no-non-null-assertion.md) | Disallow non-null assertions using the `!` postfix operator | :white_check_mark: | | | -| [`@typescript-eslint/no-redundant-type-constituents`](./no-redundant-type-constituents.md) | Disallow members of unions and intersections that do nothing or override type information | | | :thought_balloon: | -| [`@typescript-eslint/no-require-imports`](./no-require-imports.md) | Disallow invocation of `require()` | | | | -| [`@typescript-eslint/no-this-alias`](./no-this-alias.md) | Disallow aliasing `this` | :white_check_mark: | | | -| [`@typescript-eslint/no-type-alias`](./no-type-alias.md) | Disallow type aliases | | | | -| [`@typescript-eslint/no-unnecessary-boolean-literal-compare`](./no-unnecessary-boolean-literal-compare.md) | Disallow unnecessary equality comparisons against boolean literals | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-condition`](./no-unnecessary-condition.md) | Disallow conditionals where the type is always truthy or always falsy | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-qualifier`](./no-unnecessary-qualifier.md) | Disallow unnecessary namespace qualifiers | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-type-arguments`](./no-unnecessary-type-arguments.md) | Disallow type arguments that are equal to the default | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-type-assertion`](./no-unnecessary-type-assertion.md) | Disallow type assertions that do not change the type of an expression | :white_check_mark: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/no-unnecessary-type-constraint`](./no-unnecessary-type-constraint.md) | Disallow unnecessary constraints on generic types | :white_check_mark: | | | -| [`@typescript-eslint/no-unsafe-argument`](./no-unsafe-argument.md) | Disallow calling a function with a value with type `any` | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-unsafe-assignment`](./no-unsafe-assignment.md) | Disallow assigning a value with type `any` to variables and properties | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-unsafe-call`](./no-unsafe-call.md) | Disallow calling a value with type `any` | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-unsafe-member-access`](./no-unsafe-member-access.md) | Disallow member access on a value with type `any` | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-unsafe-return`](./no-unsafe-return.md) | Disallow returning a value with type `any` from a function | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-useless-empty-export`](./no-useless-empty-export.md) | Disallow empty exports that don't change anything in a module file | | :wrench: | | -| [`@typescript-eslint/no-var-requires`](./no-var-requires.md) | Disallow `require` statements except in import statements | :white_check_mark: | | | -| [`@typescript-eslint/non-nullable-type-assertion-style`](./non-nullable-type-assertion-style.md) | Enforce non-null assertions over explicit type casts | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/parameter-properties`](./parameter-properties.md) | Require or disallow parameter properties in class constructors | | | | -| [`@typescript-eslint/prefer-as-const`](./prefer-as-const.md) | Enforce the use of `as const` over literal type | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/prefer-enum-initializers`](./prefer-enum-initializers.md) | Require each enum member value to be explicitly initialized | | | | -| [`@typescript-eslint/prefer-for-of`](./prefer-for-of.md) | Enforce the use of `for-of` loop over the standard `for` loop where possible | :lock: | | | -| [`@typescript-eslint/prefer-function-type`](./prefer-function-type.md) | Enforce using function types instead of interfaces with call signatures | :lock: | :wrench: | | -| [`@typescript-eslint/prefer-includes`](./prefer-includes.md) | Enforce `includes` method over `indexOf` method | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-literal-enum-member`](./prefer-literal-enum-member.md) | Require all enum members to be literal values | :lock: | | | -| [`@typescript-eslint/prefer-namespace-keyword`](./prefer-namespace-keyword.md) | Require using `namespace` keyword over `module` keyword to declare custom TypeScript modules | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/prefer-nullish-coalescing`](./prefer-nullish-coalescing.md) | Enforce using the nullish coalescing operator instead of logical chaining | :lock: | | :thought_balloon: | -| [`@typescript-eslint/prefer-optional-chain`](./prefer-optional-chain.md) | Enforce using concise optional chain expressions instead of chained logical ands | :lock: | | | -| [`@typescript-eslint/prefer-readonly`](./prefer-readonly.md) | Require private members to be marked as `readonly` if they're never modified outside of the constructor | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-readonly-parameter-types`](./prefer-readonly-parameter-types.md) | Require function parameters to be typed as `readonly` to prevent accidental mutation of inputs | | | :thought_balloon: | -| [`@typescript-eslint/prefer-reduce-type-parameter`](./prefer-reduce-type-parameter.md) | Enforce using type parameter when calling `Array#reduce` instead of casting | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-regexp-exec`](./prefer-regexp-exec.md) | Enforce `RegExp#exec` over `String#match` if no global flag is provided | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-return-this-type`](./prefer-return-this-type.md) | Enforce that `this` is used when only `this` type is returned | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-string-starts-ends-with`](./prefer-string-starts-ends-with.md) | Enforce using `String#startsWith` and `String#endsWith` over other equivalent methods of checking substrings | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/prefer-ts-expect-error`](./prefer-ts-expect-error.md) | Enforce using `@ts-expect-error` over `@ts-ignore` | :lock: | :wrench: | | -| [`@typescript-eslint/promise-function-async`](./promise-function-async.md) | Require any function or method that returns a Promise to be marked async | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/require-array-sort-compare`](./require-array-sort-compare.md) | Require `Array#sort` calls to always provide a `compareFunction` | | | :thought_balloon: | -| [`@typescript-eslint/restrict-plus-operands`](./restrict-plus-operands.md) | Require both operands of addition to have type `number` or `string` | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/restrict-template-expressions`](./restrict-template-expressions.md) | Enforce template literal expressions to be of `string` type | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/sort-type-union-intersection-members`](./sort-type-union-intersection-members.md) | Enforce members of a type union/intersection to be sorted alphabetically | | :wrench: | | -| [`@typescript-eslint/strict-boolean-expressions`](./strict-boolean-expressions.md) | Disallow certain types in boolean expressions | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/switch-exhaustiveness-check`](./switch-exhaustiveness-check.md) | Require switch-case statements to be exhaustive with union type | | | :thought_balloon: | -| [`@typescript-eslint/triple-slash-reference`](./triple-slash-reference.md) | Disallow certain triple slash directives in favor of ES6-style import declarations | :white_check_mark: | | | -| [`@typescript-eslint/type-annotation-spacing`](./type-annotation-spacing.md) | Require consistent spacing around type annotations | | :wrench: | | -| [`@typescript-eslint/typedef`](./typedef.md) | Require type annotations in certain places | | | | -| [`@typescript-eslint/unbound-method`](./unbound-method.md) | Enforce unbound methods are called with their expected scope | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/unified-signatures`](./unified-signatures.md) | Disallow two overloads that could be unified into one with a union or an optional/rest parameter | :lock: | | | - - + ## Extension Rules In some cases, ESLint provides a rule itself, but it doesn't support TypeScript syntax; either it crashes, or it ignores the syntax, or it falsely reports against it. In these cases, we create what we call an extension rule; a rule within our plugin that has the same functionality, but also supports TypeScript. - - -**Key**: :white_check_mark: = recommended, :lock: = strict, :wrench: = fixable, :thought_balloon: = requires type information - -| Name | Description | :white_check_mark::lock: | :wrench: | :thought_balloon: | -| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------ | -------- | ----------------- | -| [`@typescript-eslint/brace-style`](./brace-style.md) | Enforce consistent brace style for blocks | | :wrench: | | -| [`@typescript-eslint/comma-dangle`](./comma-dangle.md) | Require or disallow trailing commas | | :wrench: | | -| [`@typescript-eslint/comma-spacing`](./comma-spacing.md) | Enforce consistent spacing before and after commas | | :wrench: | | -| [`@typescript-eslint/default-param-last`](./default-param-last.md) | Enforce default parameters to be last | | | | -| [`@typescript-eslint/dot-notation`](./dot-notation.md) | Enforce dot notation whenever possible | :lock: | :wrench: | :thought_balloon: | -| [`@typescript-eslint/func-call-spacing`](./func-call-spacing.md) | Require or disallow spacing between function identifiers and their invocations | | :wrench: | | -| [`@typescript-eslint/indent`](./indent.md) | Enforce consistent indentation | | :wrench: | | -| [`@typescript-eslint/init-declarations`](./init-declarations.md) | Require or disallow initialization in variable declarations | | | | -| [`@typescript-eslint/keyword-spacing`](./keyword-spacing.md) | Enforce consistent spacing before and after keywords | | :wrench: | | -| [`@typescript-eslint/lines-between-class-members`](./lines-between-class-members.md) | Require or disallow an empty line between class members | | :wrench: | | -| [`@typescript-eslint/no-array-constructor`](./no-array-constructor.md) | Disallow generic `Array` constructors | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-dupe-class-members`](./no-dupe-class-members.md) | Disallow duplicate class members | | | | -| [`@typescript-eslint/no-empty-function`](./no-empty-function.md) | Disallow empty functions | :white_check_mark: | | | -| [`@typescript-eslint/no-extra-parens`](./no-extra-parens.md) | Disallow unnecessary parentheses | | :wrench: | | -| [`@typescript-eslint/no-extra-semi`](./no-extra-semi.md) | Disallow unnecessary semicolons | :white_check_mark: | :wrench: | | -| [`@typescript-eslint/no-implied-eval`](./no-implied-eval.md) | Disallow the use of `eval()`-like methods | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/no-invalid-this`](./no-invalid-this.md) | Disallow `this` keywords outside of classes or class-like objects | | | | -| [`@typescript-eslint/no-loop-func`](./no-loop-func.md) | Disallow function declarations that contain unsafe references inside loop statements | | | | -| [`@typescript-eslint/no-loss-of-precision`](./no-loss-of-precision.md) | Disallow literal numbers that lose precision | :white_check_mark: | | | -| [`@typescript-eslint/no-magic-numbers`](./no-magic-numbers.md) | Disallow magic numbers | | | | -| [`@typescript-eslint/no-redeclare`](./no-redeclare.md) | Disallow variable redeclaration | | | | -| [`@typescript-eslint/no-restricted-imports`](./no-restricted-imports.md) | Disallow specified modules when loaded by `import` | | | | -| [`@typescript-eslint/no-shadow`](./no-shadow.md) | Disallow variable declarations from shadowing variables declared in the outer scope | | | | -| [`@typescript-eslint/no-throw-literal`](./no-throw-literal.md) | Disallow throwing literals as exceptions | :lock: | | :thought_balloon: | -| [`@typescript-eslint/no-unused-expressions`](./no-unused-expressions.md) | Disallow unused expressions | | | | -| [`@typescript-eslint/no-unused-vars`](./no-unused-vars.md) | Disallow unused variables | :white_check_mark: | | | -| [`@typescript-eslint/no-use-before-define`](./no-use-before-define.md) | Disallow the use of variables before they are defined | | | | -| [`@typescript-eslint/no-useless-constructor`](./no-useless-constructor.md) | Disallow unnecessary constructors | :lock: | | | -| [`@typescript-eslint/object-curly-spacing`](./object-curly-spacing.md) | Enforce consistent spacing inside braces | | :wrench: | | -| [`@typescript-eslint/padding-line-between-statements`](./padding-line-between-statements.md) | Require or disallow padding lines between statements | | :wrench: | | -| [`@typescript-eslint/quotes`](./quotes.md) | Enforce the consistent use of either backticks, double, or single quotes | | :wrench: | | -| [`@typescript-eslint/require-await`](./require-await.md) | Disallow async functions which have no `await` expression | :white_check_mark: | | :thought_balloon: | -| [`@typescript-eslint/return-await`](./return-await.md) | Enforce consistent returning of awaited values | | :wrench: | :thought_balloon: | -| [`@typescript-eslint/semi`](./semi.md) | Require or disallow semicolons instead of ASI | | :wrench: | | -| [`@typescript-eslint/space-before-blocks`](./space-before-blocks.md) | Enforce consistent spacing before blocks | | :wrench: | | -| [`@typescript-eslint/space-before-function-paren`](./space-before-function-paren.md) | Enforce consistent spacing before function parenthesis | | :wrench: | | -| [`@typescript-eslint/space-infix-ops`](./space-infix-ops.md) | Require spacing around infix operators | | :wrench: | | - - + diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index efb5edd4f2bf..13262e4ce73c 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -9,12 +9,6 @@ import { titleCase } from 'title-case'; const docsRoot = path.resolve(__dirname, '../docs/rules'); const rulesData = Object.entries(rules); -function createRuleLink(ruleName: string, readmePath: string): string { - return `[\`@typescript-eslint/${ruleName}\`](${ - readmePath.includes('docs/rules') ? '.' : './docs/rules' - }/${ruleName}.md)`; -} - function parseMarkdownFile(filePath: string): marked.TokensList { const file = fs.readFileSync(filePath, 'utf-8'); @@ -24,27 +18,6 @@ function parseMarkdownFile(filePath: string): marked.TokensList { }); } -function parseReadme(readmePath: string): { - base: marked.Tokens.Table; - extension: marked.Tokens.Table; -} { - const readme = parseMarkdownFile(readmePath); - - // find the table - const rulesTables = readme.filter( - (token): token is marked.Tokens.Table => - 'type' in token && token.type === 'table', - ); - if (rulesTables.length !== 2) { - throw Error('Could not find both rules tables in README.md'); - } - - return { - base: rulesTables[0], - extension: rulesTables[1], - }; -} - function isEmptySchema(schema: JSONSchema4 | JSONSchema4[]): boolean { return Array.isArray(schema) ? schema.length === 0 @@ -207,87 +180,3 @@ describe('Validating rule metadata', () => { }); } }); - -describe.each([ - path.join(__dirname, '../README.md'), - path.join(__dirname, '../docs/rules/README.md'), -])('%s', readmePath => { - const rulesTables = parseReadme(readmePath); - const notDeprecated = rulesData.filter(([, rule]) => !rule.meta.deprecated); - const baseRules = notDeprecated.filter( - ([, rule]) => !rule.meta.docs?.extendsBaseRule, - ); - const extensionRules = notDeprecated.filter( - ([, rule]) => rule.meta.docs?.extendsBaseRule, - ); - - it('All non-deprecated base rules should have a row in the base rules table, and the table should be ordered alphabetically', () => { - const baseRuleNames = baseRules - .map(([ruleName]) => ruleName) - .sort() - .map(ruleName => createRuleLink(ruleName, readmePath)); - - expect(rulesTables.base.rows.map(row => row[0].text)).toStrictEqual( - baseRuleNames, - ); - }); - it('All non-deprecated extension rules should have a row in the base rules table, and the table should be ordered alphabetically', () => { - const extensionRuleNames = extensionRules - .map(([ruleName]) => ruleName) - .sort() - .map(ruleName => createRuleLink(ruleName, readmePath)); - - expect(rulesTables.extension.rows.map(row => row[0].text)).toStrictEqual( - extensionRuleNames, - ); - }); - - for (const [ruleName, rule] of notDeprecated) { - describe(`Checking rule ${ruleName}`, () => { - const ruleRow: string[] | undefined = ( - rule.meta.docs?.extendsBaseRule - ? rulesTables.extension.rows - : rulesTables.base.rows - ) - .find(row => row[0].text.includes(`/${ruleName}.md`)) - ?.map(cell => cell.text); - if (!ruleRow) { - // rule is in the wrong table, the first two tests will catch this, so no point in creating noise; - // these tests will ofc fail in that case - return; - } - - it('Link column should be correct', () => { - expect(ruleRow[0]).toBe(createRuleLink(ruleName, readmePath)); - }); - - it('Description column should be correct', () => { - expect(ruleRow[1]).toBe(rule.meta.docs?.description); - }); - - it('Recommended column should be correct', () => { - expect(ruleRow[2]).toBe( - rule.meta.docs?.recommended === 'strict' - ? ':lock:' - : rule.meta.docs?.recommended - ? ':white_check_mark:' - : '', - ); - }); - - it('Fixable column should be correct', () => { - expect(ruleRow[3]).toBe( - rule.meta.fixable !== undefined ? ':wrench:' : '', - ); - }); - - it('Requiring type information column should be correct', () => { - expect(ruleRow[4]).toBe( - rule.meta.docs?.requiresTypeChecking === true - ? ':thought_balloon:' - : '', - ); - }); - }); - } -}); diff --git a/packages/website/docusaurusConfig.ts b/packages/website/docusaurusConfig.ts index ed8d4deaa0cf..c91f2d3e7aca 100644 --- a/packages/website/docusaurusConfig.ts +++ b/packages/website/docusaurusConfig.ts @@ -5,6 +5,7 @@ import type { UserThemeConfig as ThemeCommonConfig } from '@docusaurus/theme-com import type { UserThemeConfig as AlgoliaThemeConfig } from '@docusaurus/theme-search-algolia'; import type { Config } from '@docusaurus/types'; +import { rulesMeta } from './rulesMeta'; import npm2yarnPlugin from '@docusaurus/remark-plugin-npm2yarn'; import tabsPlugin from 'remark-docusaurus-tabs'; import { addRuleAttributesList } from './plugins/add-rule-attributes-list'; @@ -175,6 +176,9 @@ const config: Config = { projectName: 'typescript-eslint', clientModules: [require.resolve('./src/clientModules.js')], presets: [['classic', presetClassicOptions]], + customFields: { + rules: rulesMeta, + }, plugins: [ require.resolve('./webpack.plugin'), ['@docusaurus/plugin-content-docs', pluginContentDocsOptions], diff --git a/packages/website/plugins/add-rule-attributes-list.ts b/packages/website/plugins/add-rule-attributes-list.ts index 381ca2638a6b..febbfc355576 100644 --- a/packages/website/plugins/add-rule-attributes-list.ts +++ b/packages/website/plugins/add-rule-attributes-list.ts @@ -1,3 +1,4 @@ +import type * as unist from 'unist'; import type * as mdast from 'mdast'; import type { Plugin } from 'unified'; @@ -13,191 +14,25 @@ const addRuleAttributesList: Plugin = () => { if (rule == null) { return; } - const config = ((): 'recommended' | 'strict' | null => { - switch (rule.meta.docs?.recommended) { - case 'error': - case 'warn': - return 'recommended'; - - case 'strict': - return 'strict'; - - default: - return null; - } - })(); - const autoFixable = rule.meta.fixable != null; - const suggestionFixable = rule.meta.hasSuggestions === true; - const requiresTypeInfo = rule.meta.docs?.requiresTypeChecking === true; - - const parent = root as mdast.Parent; - /* - This just outputs a list with a heading like: - -## Attributes - -- [ ] Config - - [ ] ✅ Recommended - - [ ] 🔒 Strict -- [ ] Fixable - - [ ] 🔧 Automated Fixer (`--fix`) - - [ ] 🛠 Suggestion Fixer -- [ ] 💭 Requires type information - */ - const heading = Heading({ - depth: 2, - text: 'Attributes', - }); - const ruleAttributes = List({ - children: [ - NestedList({ - checked: config != null, - children: [ - ListItem({ - checked: config === 'recommended', - text: '✅ Recommended', - }), - ListItem({ - checked: config === 'strict' || config === 'recommended', - text: '🔒 Strict', - }), - ], - text: 'Included in configs', - }), - NestedList({ - checked: autoFixable || suggestionFixable, - children: [ - ListItem({ - checked: autoFixable, - text: '🔧 Automated Fixer', - }), - ListItem({ - checked: suggestionFixable, - text: '🛠 Suggestion Fixer', - }), - ], - text: 'Fixable', - }), - ListItem({ - checked: requiresTypeInfo, - text: '💭 Requires type information', - }), - ], - }); + const parent = root as unist.Parent; const h2Idx = parent.children.findIndex( - child => child.type === 'heading' && child.depth === 2, + child => child.type === 'heading' && (child as mdast.Heading).depth === 2, ); + // The actual content will be injected on client side. + const attrNode = { + type: 'jsx', + value: ``, + }; if (h2Idx != null) { // insert it just before the first h2 in the doc // this should be just after the rule's description - parent.children.splice(h2Idx, 0, heading, ruleAttributes); + parent.children.splice(h2Idx, 0, attrNode); } else { // failing that, add it to the end - parent.children.push(heading, ruleAttributes); + parent.children.push(attrNode); } }; }; -function Heading({ - depth, - text, - id = text.toLowerCase(), -}: { - depth: mdast.Heading['depth']; - id?: string; - text: string; -}): mdast.Heading { - return { - type: 'heading', - depth, - children: [ - { - type: 'text', - value: text, - }, - ], - data: { - hProperties: { - id, - }, - id, - }, - }; -} - -function Paragraph({ text }: { text: string }): mdast.Paragraph { - return { - type: 'paragraph', - children: [ - { - type: 'text', - value: text, - }, - ], - }; -} - -function ListItem({ - checked, - text, -}: { - checked: boolean; - text: string; -}): mdast.ListItem { - return { - type: 'listItem', - spread: false, - checked: checked, - children: [ - { - type: 'paragraph', - children: [ - { - type: 'text', - value: text, - }, - ], - }, - ], - }; -} - -function NestedList({ - children, - checked, - text, -}: { - children: mdast.ListItem[]; - checked: boolean; - text: string; -}): mdast.ListItem { - return { - type: 'listItem', - spread: false, - checked: checked, - children: [ - Paragraph({ - text, - }), - List({ - children, - }), - ], - data: { - className: 'test', - }, - }; -} - -function List({ children }: { children: mdast.ListItem[] }): mdast.List { - return { - type: 'list', - ordered: false, - start: null, - spread: false, - children, - }; -} - export { addRuleAttributesList }; diff --git a/packages/website/rulesMeta.ts b/packages/website/rulesMeta.ts new file mode 100644 index 000000000000..8aa5f317469f --- /dev/null +++ b/packages/website/rulesMeta.ts @@ -0,0 +1,15 @@ +import * as eslintPlugin from '@typescript-eslint/eslint-plugin'; + +export const rulesMeta = Object.entries(eslintPlugin.rules).map( + ([name, content]) => ({ + name, + type: content.meta.type, + docs: content.meta.docs, + fixable: content.meta.fixable, + hasSuggestions: content.meta.hasSuggestions, + deprecated: content.meta.deprecated, + replacedBy: content.meta.replacedBy, + }), +); + +export type RulesMeta = typeof rulesMeta; diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx new file mode 100644 index 000000000000..6629637a44b4 --- /dev/null +++ b/packages/website/src/components/RulesTable/index.tsx @@ -0,0 +1,196 @@ +import React, { useState, useMemo } from 'react'; +import clsx from 'clsx'; +import Link from '@docusaurus/Link'; +import { useRulesMeta } from '@site/src/hooks/useRulesMeta'; +import type { RulesMeta } from '@site/rulesMeta'; + +import styles from './styles.module.css'; + +function interpolateCode(text: string): (JSX.Element | string)[] | string { + const fragments = text.split(/`(.*?)`/); + if (fragments.length === 1) { + return text; + } + return fragments.map((v, i) => (i % 2 === 0 ? v : {v})); +} + +function RuleRow({ rule }: { rule: RulesMeta[number] }): JSX.Element | null { + if (!rule.docs || !rule.docs.url) { + return null; + } + return ( + + + + @typescript-eslint/{rule.name} + +
+ {interpolateCode(rule.docs.description)} + + + {rule.docs.recommended === 'strict' + ? '🔒' + : rule.docs.recommended + ? '✅' + : ''} + + + {rule.fixable ? '🔧\n' : '\n'} + {rule.hasSuggestions ? '🛠' : ''} + + + {rule.docs.requiresTypeChecking ? '💭' : ''} + + + ); +} + +const filterModes = ['neutral', 'include', 'exclude'] as const; +type FilterMode = typeof filterModes[number]; + +function RuleFilterCheckBox({ + label, + setMode, + mode, +}: { + label: string; + setMode: (mode: FilterMode) => void; + mode: FilterMode; +}): JSX.Element { + const toNextMode = (): void => + setMode(filterModes[(filterModes.indexOf(mode) + 1) % filterModes.length]); + return ( +
  • + +
  • + ); +} + +function match(mode: FilterMode, value: boolean): boolean | undefined { + if (mode === 'exclude') { + return !value; + } + if (mode === 'include') { + return value; + } + return undefined; +} + +export default function RulesTable({ + extensionRules, +}: { + extensionRules?: boolean; +}): JSX.Element { + const rules = useRulesMeta(); + const [showRecommended, setShowRecommended] = useState('neutral'); + const [showStrict, setShowStrict] = useState('neutral'); + const [showFixable, setShowFixable] = useState('neutral'); + const [showHasSuggestions, setShowHasSuggestion] = + useState('neutral'); + const [showTypeCheck, setShowTypeCheck] = useState('neutral'); + const relevantRules = useMemo( + () => + rules + .filter(r => !!extensionRules === !!r.docs?.extendsBaseRule) + .filter(r => { + const opinions = [ + match( + showRecommended, + r.docs?.recommended === 'error' || r.docs?.recommended === 'warn', + ), + match(showStrict, r.docs?.recommended === 'strict'), + match(showFixable, !!r.fixable), + match(showHasSuggestions, !!r.hasSuggestions), + match(showTypeCheck, !!r.docs?.requiresTypeChecking), + ].filter((o): o is boolean => o !== undefined); + return opinions.every(o => o); + }), + [ + rules, + extensionRules, + showRecommended, + showStrict, + showFixable, + showHasSuggestions, + showTypeCheck, + ], + ); + return ( + <> +
      + { + setShowRecommended(newMode); + + if (newMode === 'include' && showStrict === 'include') { + setShowStrict('exclude'); + } + }} + label="✅ recommended" + /> + { + setShowStrict(newMode); + + if (newMode === 'include' && showRecommended === 'include') { + setShowRecommended('exclude'); + } + }} + label="🔒 strict" + /> + + + +
    + + + + + + + + + + + {relevantRules.map(rule => ( + + ))} + +
    Rule✅{'\n'}🔒🔧{'\n'}🛠💭
    + + ); +} diff --git a/packages/website/src/components/RulesTable/styles.module.css b/packages/website/src/components/RulesTable/styles.module.css new file mode 100644 index 000000000000..6e2cc5ac27e3 --- /dev/null +++ b/packages/website/src/components/RulesTable/styles.module.css @@ -0,0 +1,119 @@ +:root { + --gray-border: var(--ifm-color-secondary-dark); + --gray-border-shadow: var(--ifm-color-secondary-dark); + --blue-bg: rgba(148, 197, 225, 0.25); + --blue-border: var(--ifm-color-primary-lighter); + --blue-border-shadow: var(--ifm-color-primary-lightest); + --red-bg: rgba(250, 56, 62, 0.25); + --red-border: rgba(198, 10, 10, 0.4); + --red-border-shadow: rgba(255, 0, 0, 0.4); +} + +[data-theme='dark'] { + --gray-border-shadow: var(--ifm-color-secondary); + --blue-bg: rgba(35, 78, 128, 0.5); + --blue-border: var(--ifm-color-primary-dark); + --red-border: rgba(255, 0, 0, 0.3); + --red-border-shadow: rgb(235, 107, 107); +} + +.checkboxList { + flex-wrap: wrap; +} + +.checkboxListItem { + user-select: none; + white-space: nowrap; + height: 32px; + font-size: 0.8rem; + margin-top: 0.5rem; + display: inline-flex; + margin-right: 0.5rem; +} + +.checkboxListItem:last-child { + margin-right: 0; +} + +.checkboxLabel { + display: flex; + align-items: center; + cursor: pointer; + line-height: 1.5; + border-radius: 4px; + padding: 0.275rem 0.8rem; + opacity: 0.85; + transition: opacity 200ms ease-out; + border: 2px solid var(--gray-border); + background-color: var(--ifm-background-color); +} + +[data-theme='dark'] .checkboxLabel { + border: 2px solid #666666; +} + +.checkboxLabel:hover { + opacity: 1; + box-shadow: 0 0 2px 1px var(--gray-border-shadow); +} + +[data-theme='dark'] .checkboxLabel:hover { + box-shadow: 0 0 2px 0 var(--gray-border-shadow); +} + +.checkboxLabel:focus-visible { + outline: 2px solid currentColor; +} + +.checkboxLabel.activated { + opacity: 0.9; + background-color: var(--blue-bg); + border: 2px solid var(--blue-border); +} + +.checkboxLabel.activated:hover { + opacity: 0.75; + box-shadow: 0 0 2px 1px var(--blue-border-shadow); +} + +.checkboxLabel.deactivated { + opacity: 0.9; + background-color: var(--red-bg); + border: 2px solid var(--red-border); +} + +.checkboxLabel.deactivated:hover { + opacity: 0.75; + box-shadow: 0 0 2px 1px var(--red-border-shadow); +} + +.visual::after { + content: '✖'; + display: inline-block; + margin-left: 0; + opacity: 0; + width: 0; + transition: margin-left var(--ifm-transition-fast), + opacity var(--ifm-transition-fast), width var(--ifm-transition-fast); +} + +.visual.visual-exclude::after, +.visual.visual-include::after { + margin-left: -0.5rem; + opacity: 1; + width: 1rem; +} + +.visual.visual-include::after { + content: '✔'; +} + +.rulesTable, +.ruleCol { + width: 100%; +} + +.attrCol { + min-width: 2.5em; + text-align: center; +} diff --git a/packages/website/src/hooks/useRulesMeta.ts b/packages/website/src/hooks/useRulesMeta.ts new file mode 100644 index 000000000000..fc6ca0d230d0 --- /dev/null +++ b/packages/website/src/hooks/useRulesMeta.ts @@ -0,0 +1,12 @@ +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import type { RulesMeta } from '@site/rulesMeta'; + +export function useRulesMeta(): RulesMeta { + const { + siteConfig: { customFields }, + } = useDocusaurusContext(); + if (!customFields) { + throw new Error('Custom fields not found in config'); + } + return customFields.rules as RulesMeta; +} diff --git a/packages/website/src/theme/MDXComponents/RuleAttributes.module.css b/packages/website/src/theme/MDXComponents/RuleAttributes.module.css new file mode 100644 index 000000000000..97c07287baba --- /dev/null +++ b/packages/website/src/theme/MDXComponents/RuleAttributes.module.css @@ -0,0 +1,7 @@ +.taskList { + list-style: none; +} + +:not(.taskList > li) > .taskList { + padding-left: 0; +} diff --git a/packages/website/src/theme/MDXComponents/RuleAttributes.tsx b/packages/website/src/theme/MDXComponents/RuleAttributes.tsx new file mode 100644 index 000000000000..74726a63fc37 --- /dev/null +++ b/packages/website/src/theme/MDXComponents/RuleAttributes.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import type { TSESLint } from '@typescript-eslint/utils'; +import { useRulesMeta } from '@site/src/hooks/useRulesMeta'; + +import styles from './RuleAttributes.module.css'; + +export function RuleAttributes({ name }: { name: string }): JSX.Element | null { + const rules = useRulesMeta(); + const rule = rules.find(rule => rule.name === name); + if (!rule) { + return null; + } + return ( + <> +

    Attributes

    +
      +
    • + + Included in configs +
        +
      • + + ✅ Recommended +
      • +
      • + + 🔒 Strict +
      • +
      +
    • +
    • + + Fixable +
        +
      • + + 🔧 Automated Fixer +
      • +
      • + + 🛠 Suggestion Fixer +
      • +
      +
    • +
    • + + 💭 Requires type information +
    • +
    + + ); +} diff --git a/packages/website/src/theme/MDXComponents/index.tsx b/packages/website/src/theme/MDXComponents/index.tsx new file mode 100644 index 000000000000..2ef24ef1c72f --- /dev/null +++ b/packages/website/src/theme/MDXComponents/index.tsx @@ -0,0 +1,8 @@ +import MDXComponents from '@theme-original/MDXComponents'; +import { RuleAttributes } from './RuleAttributes'; + +// eslint-disable-next-line import/no-default-export +export default { + ...MDXComponents, + 'rule-attributes': RuleAttributes, +}; diff --git a/packages/website/tsconfig.json b/packages/website/tsconfig.json index 2dd71ec3f252..13f1948404e4 100644 --- a/packages/website/tsconfig.json +++ b/packages/website/tsconfig.json @@ -16,5 +16,11 @@ }, "types": ["@docusaurus/module-type-aliases", "@docusaurus/theme-classic"] }, - "include": ["src/", "plugins/", "typings", "./docusaurusConfig.ts"] + "include": [ + "src/", + "plugins/", + "typings", + "./docusaurusConfig.ts", + "./rulesMeta.ts" + ] }