From d9c310e9434e01376366b1c932924cb0808a9505 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Tue, 31 May 2022 17:35:19 +0800 Subject: [PATCH 01/17] docs: autogenerate rules table on website --- packages/eslint-plugin/docs/rules/README.md | 141 +----------------- packages/eslint-plugin/tests/docs.test.ts | 5 +- packages/website/docusaurusConfig.ts | 4 + packages/website/rulesMeta.ts | 15 ++ .../src/components/RulesTable/index.tsx | 64 ++++++++ packages/website/tsconfig.json | 8 +- 6 files changed, 96 insertions(+), 141 deletions(-) create mode 100644 packages/website/rulesMeta.ts create mode 100644 packages/website/src/components/RulesTable/index.tsx diff --git a/packages/eslint-plugin/docs/rules/README.md b/packages/eslint-plugin/docs/rules/README.md index 41cdb518c1cd..0056a9d41564 100644 --- a/packages/eslint-plugin/docs/rules/README.md +++ b/packages/eslint-plugin/docs/rules/README.md @@ -11,148 +11,17 @@ See [Configs](/docs/linting/configs) for how to enable recommended rules using c ## Supported Rules - +**Key**: :white_check_mark: = recommended, :lock: = strict, :wrench: = fixable, 🛠 = has suggestions, :thought_balloon: = requires type information -**Key**: :white_check_mark: = recommended, :lock: = strict, :wrench: = fixable, :thought_balloon: = requires type information +import RulesTable from "@site/src/components/RulesTable"; -| 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-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: | | +**Key**: :white_check_mark: = recommended, :lock: = strict, :wrench: = fixable, 🛠 = has suggestions, :thought_balloon: = requires type information - + diff --git a/packages/eslint-plugin/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index efb5edd4f2bf..77f61c2267d0 100644 --- a/packages/eslint-plugin/tests/docs.test.ts +++ b/packages/eslint-plugin/tests/docs.test.ts @@ -208,10 +208,7 @@ describe('Validating rule metadata', () => { } }); -describe.each([ - path.join(__dirname, '../README.md'), - path.join(__dirname, '../docs/rules/README.md'), -])('%s', readmePath => { +describe.each([path.join(__dirname, '../README.md')])('%s', readmePath => { const rulesTables = parseReadme(readmePath); const notDeprecated = rulesData.filter(([, rule]) => !rule.meta.deprecated); const baseRules = notDeprecated.filter( diff --git a/packages/website/docusaurusConfig.ts b/packages/website/docusaurusConfig.ts index eab99e5d2eb3..177006b03b80 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'; @@ -158,6 +159,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/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..2df611663445 --- /dev/null +++ b/packages/website/src/components/RulesTable/index.tsx @@ -0,0 +1,64 @@ +import React from 'react'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import Link from '@docusaurus/Link'; +import type { RulesMeta } from '@site/rulesMeta'; + +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})); +} + +export default function RulesTable({ + extensionRules, +}: { + extensionRules?: boolean; +}): JSX.Element { + const rules = useDocusaurusContext().siteConfig.customFields! + .rules as RulesMeta; + const relevantRules = rules.filter( + r => !!extensionRules === !!r.docs?.extendsBaseRule, + ); + return ( + + + + + + + + + + + + {relevantRules.map( + rule => + rule.docs && ( + + + + + + + + ), + )} + +
NameDescription✅🔒🔧🛠💭
+ + @typescript-eslint/{rule.name} + + {interpolateCode(rule.docs.description)} + {rule.docs.recommended === 'strict' + ? '🔒' + : rule.docs.recommended + ? '✅' + : ''} + + {rule.fixable ? '🔧' : ''} + {rule.hasSuggestions ? '🛠' : ''} + {rule.docs.requiresTypeChecking ? '💭' : ''}
+ ); +} 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" + ] } From 5bb667cdcb40db1bdeb871c443515de9b8c97024 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Tue, 31 May 2022 18:05:32 +0800 Subject: [PATCH 02/17] migrate rule attributes to global data --- .../plugins/add-rule-attributes-list.ts | 180 +----------------- .../MDXComponents/RuleAttributes.module.css | 7 + .../theme/MDXComponents/RuleAttributes.tsx | 71 +++++++ .../website/src/theme/MDXComponents/index.tsx | 8 + 4 files changed, 93 insertions(+), 173 deletions(-) create mode 100644 packages/website/src/theme/MDXComponents/RuleAttributes.module.css create mode 100644 packages/website/src/theme/MDXComponents/RuleAttributes.tsx create mode 100644 packages/website/src/theme/MDXComponents/index.tsx diff --git a/packages/website/plugins/add-rule-attributes-list.ts b/packages/website/plugins/add-rule-attributes-list.ts index 381ca2638a6b..bdecbebbc4bb 100644 --- a/packages/website/plugins/add-rule-attributes-list.ts +++ b/packages/website/plugins/add-rule-attributes-list.ts @@ -13,191 +13,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 h2Idx = parent.children.findIndex( child => child.type === 'heading' && child.depth === 2, ); + // The actual content will be injected on client side. + const attrNode = { + type: 'jsx', + value: ``, + } as unknown as mdast.Content; 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/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..3460b05ba1ea --- /dev/null +++ b/packages/website/src/theme/MDXComponents/RuleAttributes.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import type { RulesMeta } from '@site/rulesMeta'; + +import styles from './RuleAttributes.module.css'; + +export function RuleAttributes({ name }: { name: string }): JSX.Element | null { + const rules = useDocusaurusContext().siteConfig.customFields! + .rules as RulesMeta; + 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, +}; From 47bbca75c4d7a0b7e3859ede751ebb0c26af2438 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Tue, 31 May 2022 18:16:12 +0800 Subject: [PATCH 03/17] add mdlint ignore --- .markdownlint.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 From ea763f98177a56d982bfc61c775a180b32c92578 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Tue, 31 May 2022 19:01:16 +0800 Subject: [PATCH 04/17] add filter --- packages/eslint-plugin/docs/rules/README.md | 4 - .../src/components/RulesTable/index.tsx | 171 ++++++++++++++---- .../components/RulesTable/styles.module.css | 57 ++++++ packages/website/src/css/custom.css | 16 ++ 4 files changed, 204 insertions(+), 44 deletions(-) create mode 100644 packages/website/src/components/RulesTable/styles.module.css diff --git a/packages/eslint-plugin/docs/rules/README.md b/packages/eslint-plugin/docs/rules/README.md index 0056a9d41564..7c6bd00d36df 100644 --- a/packages/eslint-plugin/docs/rules/README.md +++ b/packages/eslint-plugin/docs/rules/README.md @@ -11,8 +11,6 @@ See [Configs](/docs/linting/configs) for how to enable recommended rules using c ## Supported Rules -**Key**: :white_check_mark: = recommended, :lock: = strict, :wrench: = fixable, 🛠 = has suggestions, :thought_balloon: = requires type information - import RulesTable from "@site/src/components/RulesTable"; @@ -22,6 +20,4 @@ import RulesTable from "@site/src/components/RulesTable"; 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, 🛠 = has suggestions, :thought_balloon: = requires type information - diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index 2df611663445..80bb44fb62a3 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -1,8 +1,11 @@ -import React from 'react'; +import React, { useState, useId } from 'react'; +import clsx from 'clsx'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import Link from '@docusaurus/Link'; 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) { @@ -11,6 +14,75 @@ function interpolateCode(text: string): (JSX.Element | string)[] | string { return fragments.map((v, i) => (i % 2 === 0 ? v : {v})); } +function RuleRow({ rule }: { rule: RulesMeta[number] }): JSX.Element | null { + if (!rule.docs) { + return null; + } + return ( + + + + @typescript-eslint/{rule.name} + + + {interpolateCode(rule.docs.description)} + + {rule.docs.recommended === 'strict' + ? '🔒' + : rule.docs.recommended + ? '✅' + : ''} + + + {rule.fixable ? '🔧' : ''} + {rule.hasSuggestions ? '🛠' : ''} + + {rule.docs.requiresTypeChecking ? '💭' : ''} + + ); +} + +function RuleFilterCheckBox({ + label, + onToggle, + selected, +}: { + label: string; + onToggle: () => void; + selected: boolean; +}): JSX.Element { + const id = useId(); + return ( +
  • + { + if (e.key === 'Enter') { + onToggle(); + } + }} + onFocus={(e): void => { + if (e.relatedTarget) { + e.target.nextElementSibling?.dispatchEvent( + new KeyboardEvent('focus'), + ); + } + }} + onBlur={(e): void => { + e.target.nextElementSibling?.dispatchEvent(new KeyboardEvent('blur')); + }} + onChange={onToggle} + checked={selected} + /> + +
  • + ); +} + export default function RulesTable({ extensionRules, }: { @@ -18,47 +90,66 @@ export default function RulesTable({ }): JSX.Element { const rules = useDocusaurusContext().siteConfig.customFields! .rules as RulesMeta; + const [showRecommended, setShowRecommended] = useState(true); + const [showStrict, setShowStrict] = useState(true); + const [showFixable, setShowFixable] = useState(true); + const [showHasSuggestions, setShowHasSuggestion] = useState(true); + const [showTypeCheck, setShowTypeCheck] = useState(true); const relevantRules = rules.filter( - r => !!extensionRules === !!r.docs?.extendsBaseRule, + r => + !!extensionRules === !!r.docs?.extendsBaseRule && + ((showRecommended && + (r.docs?.recommended === 'error' || r.docs?.recommended === 'warn')) || + (showStrict && r.docs?.recommended === 'strict') || + (showFixable && !!r.fixable) || + (showHasSuggestions && r.hasSuggestions) || + (showTypeCheck && !!r.docs?.requiresTypeChecking)), ); return ( - - - - - - - - - - - - {relevantRules.map( - rule => - rule.docs && ( - - - - - - - - ), - )} - -
    NameDescription✅🔒🔧🛠💭
    - - @typescript-eslint/{rule.name} - - {interpolateCode(rule.docs.description)} - {rule.docs.recommended === 'strict' - ? '🔒' - : rule.docs.recommended - ? '✅' - : ''} - - {rule.fixable ? '🔧' : ''} - {rule.hasSuggestions ? '🛠' : ''} - {rule.docs.requiresTypeChecking ? '💭' : ''}
    + <> +
      + setShowRecommended(v => !v)} + label="✅ recommended" + /> + setShowStrict(v => !v)} + label="🔒 strict" + /> + setShowFixable(v => !v)} + label="🔧 fixable" + /> + setShowHasSuggestion(v => !v)} + label="🛠 has suggestions" + /> + setShowTypeCheck(v => !v)} + label="💭 requires type information" + /> +
    + + + + + + + + + + + + {relevantRules.map(rule => ( + + ))} + +
    NameDescription✅🔒🔧🛠💭
    + ); } 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..c33029367750 --- /dev/null +++ b/packages/website/src/components/RulesTable/styles.module.css @@ -0,0 +1,57 @@ +.checkboxList { + flex-wrap: wrap; +} + +.checkboxListItem { + user-select: none; + white-space: nowrap; + height: 32px; + font-size: 0.8rem; + margin-top: 0.5rem !important; + display: inline-flex; + margin-right: 0.5rem; +} + +.checkboxListItem:last-child { + margin-right: 0; +} + +.checkboxLabel:hover { + opacity: 1; + box-shadow: 0 0 2px 1px var(--ifm-color-secondary-darkest); +} + +input[type='checkbox'] + .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(--ifm-color-secondary-darkest); +} + +[data-theme='dark'] input[type='checkbox'] + .checkboxLabel { + border: 2px solid #666666; +} + +input:focus-visible + .checkboxLabel { + outline: 2px solid currentColor; +} + +input:checked + .checkboxLabel { + opacity: 0.9; + background-color: var(--site-color-checkbox-checked-bg); + border: 2px solid var(--ifm-color-primary-lightest); +} + +[data-theme='dark'] input:checked + .checkboxLabel { + border: 2px solid var(--ifm-color-primary-dark); +} + +input:checked + .checkboxLabel:hover { + opacity: 0.75; + box-shadow: 0 0 2px 1px var(--ifm-color-primary-dark); +} diff --git a/packages/website/src/css/custom.css b/packages/website/src/css/custom.css index 2324486fc7e5..689425156e7b 100644 --- a/packages/website/src/css/custom.css +++ b/packages/website/src/css/custom.css @@ -25,6 +25,8 @@ html:root { --docsearch-muted-color: #666; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); + + --site-color-checkbox-checked-bg: rgba(148, 197, 225, 0.25); } html[data-theme='dark']:root { @@ -62,6 +64,8 @@ html[data-theme='dark']:root { --docsearch-muted-color: #aaa; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); + + --site-color-checkbox-checked-bg: rgba(39, 58, 80, 0.477); } .header-github-link:hover { @@ -96,6 +100,18 @@ html[data-theme='dark'] .header-github-link:before { font-size: 0.8em; } +.screen-reader-only { + clip: rect(0 0 0 0); + border: 0; + clip-path: polygon(0 0, 0 0, 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + h1, h2, h3, From 0694aee1e137b989cd2f403277b8a148cec7abfe Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Tue, 31 May 2022 19:37:01 +0800 Subject: [PATCH 05/17] avoid redirecting to main site --- packages/website/src/components/RulesTable/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index 80bb44fb62a3..1340297de1c2 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -15,13 +15,13 @@ function interpolateCode(text: string): (JSX.Element | string)[] | string { } function RuleRow({ rule }: { rule: RulesMeta[number] }): JSX.Element | null { - if (!rule.docs) { + if (!rule.docs || !rule.docs.url) { return null; } return ( - + @typescript-eslint/{rule.name} From 4e8155518a12dca8056953a5e4f4e818566846a9 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Tue, 31 May 2022 21:31:44 +0800 Subject: [PATCH 06/17] merge two columns --- packages/website/src/components/RulesTable/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index 1340297de1c2..a53d54a0f2a4 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -24,8 +24,9 @@ function RuleRow({ rule }: { rule: RulesMeta[number] }): JSX.Element | null { @typescript-eslint/{rule.name} +
    + {interpolateCode(rule.docs.description)} - {interpolateCode(rule.docs.description)} {rule.docs.recommended === 'strict' ? '🔒' @@ -137,8 +138,7 @@ export default function RulesTable({ - - + From 6749355adc425b6e460781908d48d10620ab10d3 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Tue, 31 May 2022 23:08:49 +0800 Subject: [PATCH 07/17] this is hard --- .../src/components/RulesTable/index.tsx | 104 ++++++++++-------- .../components/RulesTable/styles.module.css | 29 ++++- 2 files changed, 80 insertions(+), 53 deletions(-) diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index a53d54a0f2a4..fcd79479e50e 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useId } from 'react'; +import React, { useState } from 'react'; import clsx from 'clsx'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import Link from '@docusaurus/Link'; @@ -43,47 +43,53 @@ function RuleRow({ rule }: { rule: RulesMeta[number] }): JSX.Element | null { ); } +const filterModes = ['include', 'neutral', 'exclude']; +type FilterMode = typeof filterModes[number]; + function RuleFilterCheckBox({ label, - onToggle, - selected, + setMode, + mode, }: { label: string; - onToggle: () => void; - selected: boolean; + setMode: (mode: FilterMode) => void; + mode: FilterMode; }): JSX.Element { - const id = useId(); + const toNextMode = (): void => + setMode(filterModes[(filterModes.indexOf(mode) + 1) % filterModes.length]); return (
  • - { if (e.key === 'Enter') { - onToggle(); - } - }} - onFocus={(e): void => { - if (e.relatedTarget) { - e.target.nextElementSibling?.dispatchEvent( - new KeyboardEvent('focus'), - ); + toNextMode(); } }} - onBlur={(e): void => { - e.target.nextElementSibling?.dispatchEvent(new KeyboardEvent('blur')); - }} - onChange={onToggle} - checked={selected} - /> - +
  • ); } +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, }: { @@ -91,47 +97,51 @@ export default function RulesTable({ }): JSX.Element { const rules = useDocusaurusContext().siteConfig.customFields! .rules as RulesMeta; - const [showRecommended, setShowRecommended] = useState(true); - const [showStrict, setShowStrict] = useState(true); - const [showFixable, setShowFixable] = useState(true); - const [showHasSuggestions, setShowHasSuggestion] = useState(true); - const [showTypeCheck, setShowTypeCheck] = useState(true); + 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 = rules.filter( r => !!extensionRules === !!r.docs?.extendsBaseRule && - ((showRecommended && - (r.docs?.recommended === 'error' || r.docs?.recommended === 'warn')) || - (showStrict && r.docs?.recommended === 'strict') || - (showFixable && !!r.fixable) || - (showHasSuggestions && r.hasSuggestions) || - (showTypeCheck && !!r.docs?.requiresTypeChecking)), + (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) ?? + true), ); return ( <>
      setShowRecommended(v => !v)} + mode={showRecommended} + setMode={setShowRecommended} label="✅ recommended" /> setShowStrict(v => !v)} + mode={showStrict} + setMode={setShowStrict} label="🔒 strict" /> setShowFixable(v => !v)} + mode={showFixable} + setMode={setShowFixable} label="🔧 fixable" /> setShowHasSuggestion(v => !v)} + mode={showHasSuggestions} + setMode={setShowHasSuggestion} label="🛠 has suggestions" /> setShowTypeCheck(v => !v)} + mode={showTypeCheck} + setMode={setShowTypeCheck} label="💭 requires type information" />
    diff --git a/packages/website/src/components/RulesTable/styles.module.css b/packages/website/src/components/RulesTable/styles.module.css index c33029367750..79f73164f611 100644 --- a/packages/website/src/components/RulesTable/styles.module.css +++ b/packages/website/src/components/RulesTable/styles.module.css @@ -21,7 +21,7 @@ box-shadow: 0 0 2px 1px var(--ifm-color-secondary-darkest); } -input[type='checkbox'] + .checkboxLabel { +.checkboxLabel { display: flex; align-items: center; cursor: pointer; @@ -31,27 +31,44 @@ input[type='checkbox'] + .checkboxLabel { opacity: 0.85; transition: opacity 200ms ease-out; border: 2px solid var(--ifm-color-secondary-darkest); + background-color: var(--ifm-background-color); } -[data-theme='dark'] input[type='checkbox'] + .checkboxLabel { +[data-theme='dark'] .checkboxLabel { border: 2px solid #666666; } -input:focus-visible + .checkboxLabel { +.checkboxLabel:focus-visible { outline: 2px solid currentColor; } -input:checked + .checkboxLabel { +.checkboxLabelActivated { opacity: 0.9; background-color: var(--site-color-checkbox-checked-bg); border: 2px solid var(--ifm-color-primary-lightest); } -[data-theme='dark'] input:checked + .checkboxLabel { +[data-theme='dark'] .checkboxLabelActivated { border: 2px solid var(--ifm-color-primary-dark); } -input:checked + .checkboxLabel:hover { +.checkboxLabelActivated:hover { opacity: 0.75; box-shadow: 0 0 2px 1px var(--ifm-color-primary-dark); } + +.checkboxLabelDeactivated { + opacity: 0.9; + background-color: #fa383e40; + border: 2px solid red; +} + +[data-theme='dark'] .checkboxLabelDeactivated { + background-color: #fa383e50; + border: 2px solid #ff000050; +} + +.checkboxLabelDeactivated:hover { + opacity: 0.75; + box-shadow: 0 0 2px 1px red; +} From b85e154f849a3c2cd7005086b81b17fb69b9e03d Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Wed, 1 Jun 2022 11:53:10 +0800 Subject: [PATCH 08/17] refactor --- .../src/components/RulesTable/index.tsx | 36 ++++++++++--------- packages/website/src/hooks/useRulesMeta.ts | 12 +++++++ .../theme/MDXComponents/RuleAttributes.tsx | 6 ++-- 3 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 packages/website/src/hooks/useRulesMeta.ts diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index fcd79479e50e..af1ea8d56e6a 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import clsx from 'clsx'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import Link from '@docusaurus/Link'; +import { useRulesMeta } from '@site/src/hooks/useRulesMeta'; import type { RulesMeta } from '@site/rulesMeta'; import styles from './styles.module.css'; @@ -95,27 +95,31 @@ export default function RulesTable({ }: { extensionRules?: boolean; }): JSX.Element { - const rules = useDocusaurusContext().siteConfig.customFields! - .rules as RulesMeta; + 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 = rules.filter( - r => - !!extensionRules === !!r.docs?.extendsBaseRule && - (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) ?? - true), - ); + const relevantRules = 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); + if (opinions.every(o => o)) { + return true; + } + return false; + }); return ( <>
      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.tsx b/packages/website/src/theme/MDXComponents/RuleAttributes.tsx index 3460b05ba1ea..aa42707f4a0a 100644 --- a/packages/website/src/theme/MDXComponents/RuleAttributes.tsx +++ b/packages/website/src/theme/MDXComponents/RuleAttributes.tsx @@ -1,12 +1,10 @@ import React from 'react'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import type { RulesMeta } from '@site/rulesMeta'; +import { useRulesMeta } from '@site/src/hooks/useRulesMeta'; import styles from './RuleAttributes.module.css'; export function RuleAttributes({ name }: { name: string }): JSX.Element | null { - const rules = useDocusaurusContext().siteConfig.customFields! - .rules as RulesMeta; + const rules = useRulesMeta(); const rule = rules.find(rule => rule.name === name); if (!rule) { return null; From c6ef6b934437cb056bfa4b553701bd1d06fdf66e Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Wed, 1 Jun 2022 12:15:20 +0800 Subject: [PATCH 09/17] tweak colors --- .../website/src/components/RulesTable/index.tsx | 2 +- .../src/components/RulesTable/styles.module.css | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index af1ea8d56e6a..40c845d25418 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -43,7 +43,7 @@ function RuleRow({ rule }: { rule: RulesMeta[number] }): JSX.Element | null { ); } -const filterModes = ['include', 'neutral', 'exclude']; +const filterModes = ['neutral', 'include', 'exclude']; type FilterMode = typeof filterModes[number]; function RuleFilterCheckBox({ diff --git a/packages/website/src/components/RulesTable/styles.module.css b/packages/website/src/components/RulesTable/styles.module.css index 79f73164f611..fa28575f1d38 100644 --- a/packages/website/src/components/RulesTable/styles.module.css +++ b/packages/website/src/components/RulesTable/styles.module.css @@ -45,7 +45,7 @@ .checkboxLabelActivated { opacity: 0.9; background-color: var(--site-color-checkbox-checked-bg); - border: 2px solid var(--ifm-color-primary-lightest); + border: 2px solid var(--ifm-color-primary-lighter); } [data-theme='dark'] .checkboxLabelActivated { @@ -54,21 +54,24 @@ .checkboxLabelActivated:hover { opacity: 0.75; - box-shadow: 0 0 2px 1px var(--ifm-color-primary-dark); + box-shadow: 0 0 2px 1px var(--ifm-color-primary-lightest); } .checkboxLabelDeactivated { opacity: 0.9; background-color: #fa383e40; - border: 2px solid red; + border: 2px solid #c60a0a70; } [data-theme='dark'] .checkboxLabelDeactivated { - background-color: #fa383e50; border: 2px solid #ff000050; } .checkboxLabelDeactivated:hover { opacity: 0.75; - box-shadow: 0 0 2px 1px red; + box-shadow: 0 0 2px 1px #ff000070; +} + +[data-theme='dark'] .checkboxLabelDeactivated:hover { + box-shadow: 0 0 2px 1px #eb6c6c; } From 29bea86375f4f6f9334568f65f5614124a30dbc4 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Wed, 1 Jun 2022 13:11:21 +0800 Subject: [PATCH 10/17] ok - memoize this --- .../src/components/RulesTable/index.tsx | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index 40c845d25418..e8aea21ffe4a 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useState, useMemo } from 'react'; import clsx from 'clsx'; import Link from '@docusaurus/Link'; import { useRulesMeta } from '@site/src/hooks/useRulesMeta'; @@ -102,24 +102,33 @@ export default function RulesTable({ const [showHasSuggestions, setShowHasSuggestion] = useState('neutral'); const [showTypeCheck, setShowTypeCheck] = useState('neutral'); - const relevantRules = 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); - if (opinions.every(o => o)) { - return true; - } - return false; - }); + 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 ( <>
        From a27757e655600a86bc9446c1ace702e2f5052b50 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Wed, 1 Jun 2022 14:07:48 +0800 Subject: [PATCH 11/17] refactors more --- .../components/RulesTable/styles.module.css | 19 ++++++++++++------- packages/website/src/css/custom.css | 16 ---------------- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/packages/website/src/components/RulesTable/styles.module.css b/packages/website/src/components/RulesTable/styles.module.css index fa28575f1d38..9c9abd2c2f49 100644 --- a/packages/website/src/components/RulesTable/styles.module.css +++ b/packages/website/src/components/RulesTable/styles.module.css @@ -16,11 +16,6 @@ margin-right: 0; } -.checkboxLabel:hover { - opacity: 1; - box-shadow: 0 0 2px 1px var(--ifm-color-secondary-darkest); -} - .checkboxLabel { display: flex; align-items: center; @@ -30,7 +25,7 @@ padding: 0.275rem 0.8rem; opacity: 0.85; transition: opacity 200ms ease-out; - border: 2px solid var(--ifm-color-secondary-darkest); + border: 2px solid var(--ifm-color-secondary-dark); background-color: var(--ifm-background-color); } @@ -38,17 +33,27 @@ border: 2px solid #666666; } +.checkboxLabel:hover { + opacity: 1; + box-shadow: 0 0 2px 1px var(--ifm-color-secondary-dark); +} + +[data-theme='dark'] .checkboxLabel:hover { + box-shadow: 0 0 2px 0 var(--ifm-color-secondary); +} + .checkboxLabel:focus-visible { outline: 2px solid currentColor; } .checkboxLabelActivated { opacity: 0.9; - background-color: var(--site-color-checkbox-checked-bg); + background-color: rgba(148, 197, 225, 0.25); border: 2px solid var(--ifm-color-primary-lighter); } [data-theme='dark'] .checkboxLabelActivated { + background-color: rgba(35, 78, 128, 0.5); border: 2px solid var(--ifm-color-primary-dark); } diff --git a/packages/website/src/css/custom.css b/packages/website/src/css/custom.css index 689425156e7b..2324486fc7e5 100644 --- a/packages/website/src/css/custom.css +++ b/packages/website/src/css/custom.css @@ -25,8 +25,6 @@ html:root { --docsearch-muted-color: #666; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); - - --site-color-checkbox-checked-bg: rgba(148, 197, 225, 0.25); } html[data-theme='dark']:root { @@ -64,8 +62,6 @@ html[data-theme='dark']:root { --docsearch-muted-color: #aaa; --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); - - --site-color-checkbox-checked-bg: rgba(39, 58, 80, 0.477); } .header-github-link:hover { @@ -100,18 +96,6 @@ html[data-theme='dark'] .header-github-link:before { font-size: 0.8em; } -.screen-reader-only { - clip: rect(0 0 0 0); - border: 0; - clip-path: polygon(0 0, 0 0, 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; -} - h1, h2, h3, From cc3075fa713142066829d558d66e4150f0370f4e Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Wed, 1 Jun 2022 15:21:56 +0800 Subject: [PATCH 12/17] Apply suggestions from code review Co-authored-by: Brad Zacher --- packages/website/src/components/RulesTable/index.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index e8aea21ffe4a..b66f032d9f78 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -158,13 +158,13 @@ export default function RulesTable({ label="💭 requires type information" />
      -
    NameDescriptionRule ✅🔒 🔧🛠 💭
    +
    - - - - + + + + From d9900fb4ab8cf1fe02b668febdd9817c384b96fd Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Wed, 1 Jun 2022 15:27:22 +0800 Subject: [PATCH 13/17] ok, use classes --- .../website/src/components/RulesTable/index.tsx | 10 +++++----- .../src/components/RulesTable/styles.module.css | 13 +++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index b66f032d9f78..402783833b6d 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -158,13 +158,13 @@ export default function RulesTable({ label="💭 requires type information" /> -
    Rule✅🔒🔧🛠💭Rule✅🔒🔧🛠💭
    +
    - - - - + + + + diff --git a/packages/website/src/components/RulesTable/styles.module.css b/packages/website/src/components/RulesTable/styles.module.css index 9c9abd2c2f49..a0e4de5a6370 100644 --- a/packages/website/src/components/RulesTable/styles.module.css +++ b/packages/website/src/components/RulesTable/styles.module.css @@ -80,3 +80,16 @@ [data-theme='dark'] .checkboxLabelDeactivated:hover { box-shadow: 0 0 2px 1px #eb6c6c; } + +.rulesTable, +.ruleCol { + width: 100%; +} + +.attrDblCol { + min-width: 4em; +} + +.attrCol { + min-width: 2.5em; +} From 583964747ce38ff6c647e4e79f8d48aa8d7643e8 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Wed, 1 Jun 2022 23:25:32 +0800 Subject: [PATCH 14/17] vertially arrange icons --- .../website/src/components/RulesTable/index.tsx | 14 ++++++++------ .../src/components/RulesTable/styles.module.css | 5 +---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index 402783833b6d..1779043866f1 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -27,18 +27,20 @@ function RuleRow({ rule }: { rule: RulesMeta[number] }): JSX.Element | null {
    {interpolateCode(rule.docs.description)} -
    - - + ); } @@ -162,8 +164,8 @@ export default function RulesTable({ - - + + diff --git a/packages/website/src/components/RulesTable/styles.module.css b/packages/website/src/components/RulesTable/styles.module.css index a0e4de5a6370..cc3faa75b88b 100644 --- a/packages/website/src/components/RulesTable/styles.module.css +++ b/packages/website/src/components/RulesTable/styles.module.css @@ -86,10 +86,7 @@ width: 100%; } -.attrDblCol { - min-width: 4em; -} - .attrCol { min-width: 2.5em; + text-align: center; } From 10d5ea04e064b4b92b031e527df1486bff692ab5 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Thu, 9 Jun 2022 16:09:07 +0800 Subject: [PATCH 15/17] Remove rules table from README --- packages/eslint-plugin/README.md | 146 +--------------------- packages/eslint-plugin/tests/docs.test.ts | 108 ---------------- 2 files changed, 1 insertion(+), 253 deletions(-) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index ff85922117e5..5124ce841ae0 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -89,151 +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-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/tests/docs.test.ts b/packages/eslint-plugin/tests/docs.test.ts index 77f61c2267d0..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,84 +180,3 @@ describe('Validating rule metadata', () => { }); } }); - -describe.each([path.join(__dirname, '../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:' - : '', - ); - }); - }); - } -}); From 1363c4eb8dcc2b3798bbf14e400217bd8c619a52 Mon Sep 17 00:00:00 2001 From: Joshua Chen Date: Thu, 9 Jun 2022 16:33:47 +0800 Subject: [PATCH 16/17] minor refactors --- .../plugins/add-rule-attributes-list.ts | 7 ++- .../src/components/RulesTable/index.tsx | 4 +- .../components/RulesTable/styles.module.css | 60 ++++++++++--------- .../theme/MDXComponents/RuleAttributes.tsx | 10 +++- 4 files changed, 46 insertions(+), 35 deletions(-) diff --git a/packages/website/plugins/add-rule-attributes-list.ts b/packages/website/plugins/add-rule-attributes-list.ts index bdecbebbc4bb..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'; @@ -14,15 +15,15 @@ const addRuleAttributesList: Plugin = () => { return; } - const parent = root as mdast.Parent; + 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: ``, - } as unknown as mdast.Content; + }; if (h2Idx != null) { // insert it just before the first h2 in the doc // this should be just after the rule's description diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index 1779043866f1..1630abe87e71 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -65,8 +65,8 @@ function RuleFilterCheckBox({ type="button" className={clsx( styles.checkboxLabel, - mode === 'include' && styles.checkboxLabelActivated, - mode === 'exclude' && styles.checkboxLabelDeactivated, + mode === 'include' && styles.activated, + mode === 'exclude' && styles.deactivated, )} onKeyDown={(e): void => { if (e.key === 'Enter') { diff --git a/packages/website/src/components/RulesTable/styles.module.css b/packages/website/src/components/RulesTable/styles.module.css index cc3faa75b88b..4a33c1d46059 100644 --- a/packages/website/src/components/RulesTable/styles.module.css +++ b/packages/website/src/components/RulesTable/styles.module.css @@ -1,3 +1,22 @@ +: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; } @@ -7,7 +26,7 @@ white-space: nowrap; height: 32px; font-size: 0.8rem; - margin-top: 0.5rem !important; + margin-top: 0.5rem; display: inline-flex; margin-right: 0.5rem; } @@ -25,7 +44,7 @@ padding: 0.275rem 0.8rem; opacity: 0.85; transition: opacity 200ms ease-out; - border: 2px solid var(--ifm-color-secondary-dark); + border: 2px solid var(--gray-border); background-color: var(--ifm-background-color); } @@ -35,50 +54,37 @@ .checkboxLabel:hover { opacity: 1; - box-shadow: 0 0 2px 1px var(--ifm-color-secondary-dark); + box-shadow: 0 0 2px 1px var(--gray-border-shadow); } [data-theme='dark'] .checkboxLabel:hover { - box-shadow: 0 0 2px 0 var(--ifm-color-secondary); + box-shadow: 0 0 2px 0 var(--gray-border-shadow); } .checkboxLabel:focus-visible { outline: 2px solid currentColor; } -.checkboxLabelActivated { +.checkboxLabel.activated { opacity: 0.9; - background-color: rgba(148, 197, 225, 0.25); - border: 2px solid var(--ifm-color-primary-lighter); -} - -[data-theme='dark'] .checkboxLabelActivated { - background-color: rgba(35, 78, 128, 0.5); - border: 2px solid var(--ifm-color-primary-dark); + background-color: var(--blue-bg); + border: 2px solid var(--blue-border); } -.checkboxLabelActivated:hover { +.checkboxLabel.activated:hover { opacity: 0.75; - box-shadow: 0 0 2px 1px var(--ifm-color-primary-lightest); + box-shadow: 0 0 2px 1px var(--blue-border-shadow); } -.checkboxLabelDeactivated { +.checkboxLabel.deactivated { opacity: 0.9; - background-color: #fa383e40; - border: 2px solid #c60a0a70; -} - -[data-theme='dark'] .checkboxLabelDeactivated { - border: 2px solid #ff000050; + background-color: var(--red-bg); + border: 2px solid var(--red-border); } -.checkboxLabelDeactivated:hover { +.checkboxLabel.deactivated:hover { opacity: 0.75; - box-shadow: 0 0 2px 1px #ff000070; -} - -[data-theme='dark'] .checkboxLabelDeactivated:hover { - box-shadow: 0 0 2px 1px #eb6c6c; + box-shadow: 0 0 2px 1px var(--red-border-shadow); } .rulesTable, diff --git a/packages/website/src/theme/MDXComponents/RuleAttributes.tsx b/packages/website/src/theme/MDXComponents/RuleAttributes.tsx index aa42707f4a0a..74726a63fc37 100644 --- a/packages/website/src/theme/MDXComponents/RuleAttributes.tsx +++ b/packages/website/src/theme/MDXComponents/RuleAttributes.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import type { TSESLint } from '@typescript-eslint/utils'; import { useRulesMeta } from '@site/src/hooks/useRulesMeta'; import styles from './RuleAttributes.module.css'; @@ -21,9 +22,12 @@ export function RuleAttributes({ name }: { name: string }): JSX.Element | null { ✅ Recommended From fc3eadf8583ad4fe1edaa0aa5631bda254d1e8bb Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Sat, 25 Jun 2022 14:47:17 -0400 Subject: [PATCH 17/17] Accessibility labels --- .../src/components/RulesTable/index.tsx | 22 ++++++++++++++++--- .../components/RulesTable/styles.module.css | 21 ++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/website/src/components/RulesTable/index.tsx b/packages/website/src/components/RulesTable/index.tsx index 1630abe87e71..6629637a44b4 100644 --- a/packages/website/src/components/RulesTable/index.tsx +++ b/packages/website/src/components/RulesTable/index.tsx @@ -45,7 +45,7 @@ function RuleRow({ rule }: { rule: RulesMeta[number] }): JSX.Element | null { ); } -const filterModes = ['neutral', 'include', 'exclude']; +const filterModes = ['neutral', 'include', 'exclude'] as const; type FilterMode = typeof filterModes[number]; function RuleFilterCheckBox({ @@ -76,6 +76,10 @@ function RuleFilterCheckBox({ onClick={toNextMode} aria-label={`Toggle the filter mode. Current: ${mode}`} > +
    {label} @@ -136,12 +140,24 @@ export default function RulesTable({
      { + setShowRecommended(newMode); + + if (newMode === 'include' && showStrict === 'include') { + setShowStrict('exclude'); + } + }} label="✅ recommended" /> { + setShowStrict(newMode); + + if (newMode === 'include' && showRecommended === 'include') { + setShowRecommended('exclude'); + } + }} label="🔒 strict" />
    Rule✅🔒🔧🛠💭Rule✅🔒🔧🛠💭
    + {rule.docs.recommended === 'strict' ? '🔒' : rule.docs.recommended ? '✅' : ''} - {rule.fixable ? '🔧' : ''} + + {rule.fixable ? '🔧\n' : '\n'} {rule.hasSuggestions ? '🛠' : ''} {rule.docs.requiresTypeChecking ? '💭' : ''} + {rule.docs.requiresTypeChecking ? '💭' : ''} +
    Rule✅🔒🔧🛠✅{'\n'}🔒🔧{'\n'}🛠 💭