diff --git a/crates/biome_analyze/src/rule.rs b/crates/biome_analyze/src/rule.rs index d6b6aa382db0..018365e85653 100644 --- a/crates/biome_analyze/src/rule.rs +++ b/crates/biome_analyze/src/rule.rs @@ -14,6 +14,7 @@ use biome_diagnostics::{ Visit, }; use biome_rowan::{AstNode, BatchMutation, BatchMutationExt, Language, TextRange}; +use std::cmp::Ordering; use std::fmt::Debug; #[derive(Debug, Clone)] @@ -56,7 +57,7 @@ impl Display for FixKind { } } -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq)] pub enum RuleSource { /// Rules from [Rust Clippy](https://rust-lang.github.io/rust-clippy/master/index.html) Clippy(&'static str), @@ -86,6 +87,54 @@ pub enum RuleSource { EslintMysticatea(&'static str), } +impl PartialEq for RuleSource { + fn eq(&self, other: &Self) -> bool { + std::mem::discriminant(self) == std::mem::discriminant(other) + } +} + +impl std::fmt::Display for RuleSource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + RuleSource::Clippy(_) => write!(f, "Clippy"), + RuleSource::Eslint(_) => write!(f, "ESLint"), + RuleSource::EslintImport(_) => write!(f, "eslint-plugin-import"), + RuleSource::EslintImportAccess(_) => write!(f, "eslint-plugin-import-access"), + RuleSource::EslintJest(_) => write!(f, "eslint-plugin-jest"), + RuleSource::EslintJsxA11y(_) => write!(f, "eslint-plugin-jsx-a11y"), + RuleSource::EslintReact(_) => write!(f, "eslint-plugin-react"), + RuleSource::EslintReactHooks(_) => write!(f, "eslint-plugin-react-hooks"), + RuleSource::EslintSonarJs(_) => write!(f, "eslint-plugin-sonarjs"), + RuleSource::EslintStylistic(_) => write!(f, "eslint-plugin-stylistic"), + RuleSource::EslintTypeScript(_) => write!(f, "eslint-plugin-typescript"), + RuleSource::EslintUnicorn(_) => write!(f, "eslint-plugin-unicorn"), + RuleSource::EslintMysticatea(_) => write!(f, "eslint-plugin-mysticates"), + } + } +} + +impl PartialOrd for RuleSource { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for RuleSource { + fn cmp(&self, other: &Self) -> Ordering { + if let (RuleSource::Eslint(self_rule), RuleSource::Eslint(other_rule)) = (self, other) { + self_rule.cmp(other_rule) + } else if self.is_eslint() { + Ordering::Greater + } else if other.is_eslint() { + Ordering::Less + } else { + let self_rule = self.as_rule_name(); + let other_rule = other.as_rule_name(); + self_rule.cmp(other_rule) + } + } +} + impl RuleSource { pub fn as_rule_name(&self) -> &'static str { match self { @@ -105,7 +154,7 @@ impl RuleSource { } } - pub fn as_rule_url(&self) -> String { + pub fn to_rule_url(&self) -> String { match self { Self::Clippy(rule_name) => format!("https://rust-lang.github.io/rust-clippy/master/#/{rule_name}"), Self::Eslint(rule_name) => format!("https://eslint.org/docs/latest/rules/{rule_name}"), @@ -124,7 +173,37 @@ impl RuleSource { } pub fn as_url_and_rule_name(&self) -> (String, &'static str) { - (self.as_rule_url(), self.as_rule_name()) + (self.to_rule_url(), self.as_rule_name()) + } + + /// Original ESLint rule + pub const fn is_eslint(&self) -> bool { + matches!(self, Self::Eslint(_)) + } + + /// TypeScript plugin + pub const fn is_eslint_typescript(&self) -> bool { + matches!(self, Self::EslintTypeScript(_)) + } + + /// All ESLint plugins, exception for the TypeScript one + pub const fn is_eslint_plugin(&self) -> bool { + matches!( + self, + Self::EslintImport(_) + | Self::EslintImportAccess(_) + | Self::EslintJest(_) + | Self::EslintStylistic(_) + | Self::EslintJsxA11y(_) + | Self::EslintReact(_) + | Self::EslintReactHooks(_) + | Self::EslintSonarJs(_) + | Self::EslintUnicorn(_) + ) + } + + pub const fn is_clippy(&self) -> bool { + matches!(self, Self::Clippy(_)) } } @@ -137,6 +216,12 @@ pub enum RuleSourceKind { Inspired, } +impl RuleSourceKind { + pub const fn is_inspired(&self) -> bool { + matches!(self, Self::Inspired) + } +} + impl RuleMetadata { pub const fn new(version: &'static str, name: &'static str, docs: &'static str) -> Self { Self { diff --git a/website/astro.config.ts b/website/astro.config.ts index ef4c14eefe4a..760d12562211 100644 --- a/website/astro.config.ts +++ b/website/astro.config.ts @@ -210,14 +210,18 @@ export default defineConfig({ }, }, { - label: "Lint rules", + label: "Rules", link: "/linter/rules", translations: { - ja: "Lintルール", - "zh-CN": "Lint 规则", - "pt-BR": "Regras do Linter", + ja: "ルール", + "zh-CN": "规则", + "pt-BR": "Regras", }, }, + { + label: "Rules sources", + link: "/linter/rules-sources", + }, ], }, ], diff --git a/website/src/components/generated/Sources.md b/website/src/components/generated/Sources.md new file mode 100644 index 000000000000..9305ba17b0bf --- /dev/null +++ b/website/src/components/generated/Sources.md @@ -0,0 +1,186 @@ +## Clippy rules to Biome +| Clippy rule name | Biome rule name | +| ---- | ---- | +| [approx_constant](https://rust-lang.github.io/rust-clippy/master/#/approx_constant) |[noApproximativeNumericConstant](/linter/rules//lint/rules/no-approximative-numeric-constant) | +| [match_str_case_mismatch](https://rust-lang.github.io/rust-clippy/master/#/match_str_case_mismatch) |[noStringCaseMismatch](/linter/rules//lint/rules/no-string-case-mismatch) | +| [misrefactored_assign_op](https://rust-lang.github.io/rust-clippy/master/#/misrefactored_assign_op) |[noMisrefactoredShorthandAssign](/linter/rules//lint/rules/no-misrefactored-shorthand-assign) | +## ESLint rules to Biome +| ESLint rule name | Biome rule name | +| ---- | ---- | +| [constructor-super](https://eslint.org/docs/latest/rules/constructor-super) |[noInvalidConstructorSuper](/linter/rules//lint/rules/no-invalid-constructor-super) | +| [curly](https://eslint.org/docs/latest/rules/curly) |[useBlockStatements](/linter/rules//lint/rules/use-block-statements) | +| [default-case-last](https://eslint.org/docs/latest/rules/default-case-last) |[useDefaultSwitchClauseLast](/linter/rules//lint/rules/use-default-switch-clause-last) | +| [default-param-last](https://eslint.org/docs/latest/rules/default-param-last) |[useDefaultParameterLast](/linter/rules//lint/rules/use-default-parameter-last) | +| [dot-notation](https://eslint.org/docs/latest/rules/dot-notation) |[useLiteralKeys](/linter/rules//lint/rules/use-literal-keys) | +| [eqeqeq](https://eslint.org/docs/latest/rules/eqeqeq) |[noDoubleEquals](/linter/rules//lint/rules/no-double-equals) | +| [for-direction](https://eslint.org/docs/latest/rules/for-direction) |[useValidForDirection](/linter/rules//lint/rules/use-valid-for-direction) | +| [getter-return](https://eslint.org/docs/latest/rules/getter-return) |[useGetterReturn](/linter/rules//lint/rules/use-getter-return) | +| [no-async-promise-executor](https://eslint.org/docs/latest/rules/no-async-promise-executor) |[noAsyncPromiseExecutor](/linter/rules//lint/rules/no-async-promise-executor) | +| [no-case-declarations](https://eslint.org/docs/latest/rules/no-case-declarations) |[noSwitchDeclarations](/linter/rules//lint/rules/no-switch-declarations) | +| [no-class-assign](https://eslint.org/docs/latest/rules/no-class-assign) |[noClassAssign](/linter/rules//lint/rules/no-class-assign) | +| [no-compare-neg-zero](https://eslint.org/docs/latest/rules/no-compare-neg-zero) |[noCompareNegZero](/linter/rules//lint/rules/no-compare-neg-zero) | +| [no-cond-assign](https://eslint.org/docs/latest/rules/no-cond-assign) |[noAssignInExpressions](/linter/rules//lint/rules/no-assign-in-expressions) | +| [no-console](https://eslint.org/docs/latest/rules/no-console) |[noConsoleLog](/linter/rules//lint/rules/no-console-log) | +| [no-const-assign](https://eslint.org/docs/latest/rules/no-const-assign) |[noConstAssign](/linter/rules//lint/rules/no-const-assign) | +| [no-constant-condition](https://eslint.org/docs/latest/rules/no-constant-condition) |[noConstantCondition](/linter/rules//lint/rules/no-constant-condition) | +| [no-constructor-return](https://eslint.org/docs/latest/rules/no-constructor-return) |[noConstructorReturn](/linter/rules//lint/rules/no-constructor-return) | +| [no-control-regex](https://eslint.org/docs/latest/rules/no-control-regex) |[noControlCharactersInRegex](/linter/rules//lint/rules/no-control-characters-in-regex) | +| [no-debugger](https://eslint.org/docs/latest/rules/no-debugger) |[noDebugger](/linter/rules//lint/rules/no-debugger) | +| [no-dupe-args](https://eslint.org/docs/latest/rules/no-dupe-args) |[noDuplicateParameters](/linter/rules//lint/rules/no-duplicate-parameters) | +| [no-dupe-class-members](https://eslint.org/docs/latest/rules/no-dupe-class-members) |[noDuplicateClassMembers](/linter/rules//lint/rules/no-duplicate-class-members) | +| [no-dupe-keys](https://eslint.org/docs/latest/rules/no-dupe-keys) |[noDuplicateObjectKeys](/linter/rules//lint/rules/no-duplicate-object-keys) | +| [no-duplicate-case](https://eslint.org/docs/latest/rules/no-duplicate-case) |[noDuplicateCase](/linter/rules//lint/rules/no-duplicate-case) | +| [no-else-return](https://eslint.org/docs/latest/rules/no-else-return) |[noUselessElse](/linter/rules//lint/rules/no-useless-else) | +| [no-empty-character-class](https://eslint.org/docs/latest/rules/no-empty-character-class) |[noEmptyCharacterClassInRegex](/linter/rules//lint/rules/no-empty-character-class-in-regex) | +| [no-empty-pattern](https://eslint.org/docs/latest/rules/no-empty-pattern) |[noEmptyPattern](/linter/rules//lint/rules/no-empty-pattern) | +| [no-ex-assign](https://eslint.org/docs/latest/rules/no-ex-assign) |[noCatchAssign](/linter/rules//lint/rules/no-catch-assign) | +| [no-extra-boolean-cast](https://eslint.org/docs/latest/rules/no-extra-boolean-cast) |[noExtraBooleanCast](/linter/rules//lint/rules/no-extra-boolean-cast) | +| [no-extra-label](https://eslint.org/docs/latest/rules/no-extra-label) |[noUselessLabel](/linter/rules//lint/rules/no-useless-label) | +| [no-fallthrough](https://eslint.org/docs/latest/rules/no-fallthrough) |[noFallthroughSwitchClause](/linter/rules//lint/rules/no-fallthrough-switch-clause) | +| [no-func-assign](https://eslint.org/docs/latest/rules/no-func-assign) |[noFunctionAssign](/linter/rules//lint/rules/no-function-assign) | +| [no-import-assign](https://eslint.org/docs/latest/rules/no-import-assign) |[noImportAssign](/linter/rules//lint/rules/no-import-assign) | +| [no-inner-declarations](https://eslint.org/docs/latest/rules/no-inner-declarations) |[noInnerDeclarations](/linter/rules//lint/rules/no-inner-declarations) | +| [no-label-var](https://eslint.org/docs/latest/rules/no-label-var) |[noLabelVar](/linter/rules//lint/rules/no-label-var) | +| [no-labels](https://eslint.org/docs/latest/rules/no-labels) |[noConfusingLabels](/linter/rules//lint/rules/no-confusing-labels) | +| [no-lonely-if](https://eslint.org/docs/latest/rules/no-lonely-if) |[useCollapsedElseIf](/linter/rules//lint/rules/use-collapsed-else-if) | +| [no-loss-of-precision](https://eslint.org/docs/latest/rules/no-loss-of-precision) |[noPrecisionLoss](/linter/rules//lint/rules/no-precision-loss) | +| [no-negated-condition](https://eslint.org/docs/latest/rules/no-negated-condition) |[noNegationElse](/linter/rules//lint/rules/no-negation-else) | +| [no-new-native-nonconstructor](https://eslint.org/docs/latest/rules/no-new-native-nonconstructor) |[noInvalidNewBuiltin](/linter/rules//lint/rules/no-invalid-new-builtin) | +| [no-new-symbol](https://eslint.org/docs/latest/rules/no-new-symbol) |[noNewSymbol](/linter/rules//lint/rules/no-new-symbol) | +| [no-nonoctal-decimal-escape](https://eslint.org/docs/latest/rules/no-nonoctal-decimal-escape) |[noNonoctalDecimalEscape](/linter/rules//lint/rules/no-nonoctal-decimal-escape) | +| [no-obj-calls](https://eslint.org/docs/latest/rules/no-obj-calls) |[noGlobalObjectCalls](/linter/rules//lint/rules/no-global-object-calls) | +| [no-param-reassign](https://eslint.org/docs/latest/rules/no-param-reassign) |[noParameterAssign](/linter/rules//lint/rules/no-parameter-assign) | +| [no-prototype-builtins](https://eslint.org/docs/latest/rules/no-prototype-builtins) |[noPrototypeBuiltins](/linter/rules//lint/rules/no-prototype-builtins) | +| [no-regex-spaces](https://eslint.org/docs/latest/rules/no-regex-spaces) |[noMultipleSpacesInRegularExpressionLiterals](/linter/rules//lint/rules/no-multiple-spaces-in-regular-expression-literals) | +| [no-restricted-globals](https://eslint.org/docs/latest/rules/no-restricted-globals) |[noRestrictedGlobals](/linter/rules//lint/rules/no-restricted-globals) | +| [no-self-assign](https://eslint.org/docs/latest/rules/no-self-assign) |[noSelfAssign](/linter/rules//lint/rules/no-self-assign) | +| [no-self-compare](https://eslint.org/docs/latest/rules/no-self-compare) |[noSelfCompare](/linter/rules//lint/rules/no-self-compare) | +| [no-sequences](https://eslint.org/docs/latest/rules/no-sequences) |[noCommaOperator](/linter/rules//lint/rules/no-comma-operator) | +| [no-setter-return](https://eslint.org/docs/latest/rules/no-setter-return) |[noSetterReturn](/linter/rules//lint/rules/no-setter-return) | +| [no-shadow-restricted-names](https://eslint.org/docs/latest/rules/no-shadow-restricted-names) |[noShadowRestrictedNames](/linter/rules//lint/rules/no-shadow-restricted-names) | +| [no-sparse-array](https://eslint.org/docs/latest/rules/no-sparse-array) |[noSparseArray](/linter/rules//lint/rules/no-sparse-array) | +| [no-this-before-super](https://eslint.org/docs/latest/rules/no-this-before-super) |[noUnreachableSuper](/linter/rules//lint/rules/no-unreachable-super) | +| [no-undef](https://eslint.org/docs/latest/rules/no-undef) |[noUndeclaredVariables](/linter/rules//lint/rules/no-undeclared-variables) | +| [no-unreachable](https://eslint.org/docs/latest/rules/no-unreachable) |[noUnreachable](/linter/rules//lint/rules/no-unreachable) | +| [no-unsafe-finally](https://eslint.org/docs/latest/rules/no-unsafe-finally) |[noUnsafeFinally](/linter/rules//lint/rules/no-unsafe-finally) | +| [no-unsafe-negation](https://eslint.org/docs/latest/rules/no-unsafe-negation) |[noUnsafeNegation](/linter/rules//lint/rules/no-unsafe-negation) | +| [no-unsafe-optional-chaining](https://eslint.org/docs/latest/rules/no-unsafe-optional-chaining) |[noUnsafeOptionalChaining](/linter/rules//lint/rules/no-unsafe-optional-chaining) | +| [no-unused-labels](https://eslint.org/docs/latest/rules/no-unused-labels) |[noUnusedLabels](/linter/rules//lint/rules/no-unused-labels) | +| [no-unused-vars](https://eslint.org/docs/latest/rules/no-unused-vars) |[noUnusedVariables](/linter/rules//lint/rules/no-unused-variables) | +| [no-useless-catch](https://eslint.org/docs/latest/rules/no-useless-catch) |[noUselessCatch](/linter/rules//lint/rules/no-useless-catch) | +| [no-useless-rename](https://eslint.org/docs/latest/rules/no-useless-rename) |[noUselessRename](/linter/rules//lint/rules/no-useless-rename) | +| [no-var](https://eslint.org/docs/latest/rules/no-var) |[noVar](/linter/rules//lint/rules/no-var) | +| [no-void](https://eslint.org/docs/latest/rules/no-void) |[noVoid](/linter/rules//lint/rules/no-void) | +| [no-with](https://eslint.org/docs/latest/rules/no-with) |[noWith](/linter/rules//lint/rules/no-with) | +| [one-var](https://eslint.org/docs/latest/rules/one-var) |[useSingleVarDeclarator](/linter/rules//lint/rules/use-single-var-declarator) | +| [operator-assignment](https://eslint.org/docs/latest/rules/operator-assignment) |[useShorthandAssign](/linter/rules//lint/rules/use-shorthand-assign) | +| [prefer-arrow-callback](https://eslint.org/docs/latest/rules/prefer-arrow-callback) |[useArrowFunction](/linter/rules//lint/rules/use-arrow-function) | +| [prefer-const](https://eslint.org/docs/latest/rules/prefer-const) |[useConst](/linter/rules//lint/rules/use-const) | +| [prefer-exponentiation-operator](https://eslint.org/docs/latest/rules/prefer-exponentiation-operator) |[useExponentiationOperator](/linter/rules//lint/rules/use-exponentiation-operator) | +| [prefer-numeric-literals](https://eslint.org/docs/latest/rules/prefer-numeric-literals) |[useNumericLiterals](/linter/rules//lint/rules/use-numeric-literals) | +| [prefer-regex-literals](https://eslint.org/docs/latest/rules/prefer-regex-literals) |[useRegexLiterals](/linter/rules//lint/rules/use-regex-literals) | +| [prefer-rest-params](https://eslint.org/docs/latest/rules/prefer-rest-params) |[noArguments](/linter/rules//lint/rules/no-arguments) | +| [prefer-template](https://eslint.org/docs/latest/rules/prefer-template) |[useTemplate](/linter/rules//lint/rules/use-template) | +| [require-yield](https://eslint.org/docs/latest/rules/require-yield) |[useYield](/linter/rules//lint/rules/use-yield) | +| [use-isnan](https://eslint.org/docs/latest/rules/use-isnan) |[useIsNan](/linter/rules//lint/rules/use-is-nan) | +| [valid-typeof](https://eslint.org/docs/latest/rules/valid-typeof) |[useValidTypeof](/linter/rules//lint/rules/use-valid-typeof) | +## eslint-plugin-import rules to Biome +| eslint-plugin-import rule name | Biome rule name | +| ---- | ---- | +| [no-default-export](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-default-export.md) |[noDefaultExport](/linter/rules//lint/rules/no-default-export) | +## eslint-plugin-jsx-a11y rules to Biome +| eslint-plugin-jsx-a11y rule name | Biome rule name | +| ---- | ---- | +| [alt-text](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/alt-text.md) |[useAltText](/linter/rules//lint/rules/use-alt-text) | +| [anchor-has-content](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/anchor-has-content.md) |[useAnchorContent](/linter/rules//lint/rules/use-anchor-content) | +| [anchor-is-valid](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/anchor-is-valid.md) |[useValidAnchor](/linter/rules//lint/rules/use-valid-anchor) | +| [aria-activedescendant-has-tabindex](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/aria-activedescendant-has-tabindex.md) |[useAriaActivedescendantWithTabindex](/linter/rules//lint/rules/use-aria-activedescendant-with-tabindex) | +| [aria-props](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/aria-props.md) |[useValidAriaProps](/linter/rules//lint/rules/use-valid-aria-props) | +| [aria-proptypes](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/aria-proptypes.md) |[useValidAriaValues](/linter/rules//lint/rules/use-valid-aria-values) | +| [aria-role](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/aria-role.md) |[useValidAriaRole](/linter/rules//lint/rules/use-valid-aria-role) | +| [aria-unsupported-elements](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/aria-unsupported-elements.md) |[noAriaUnsupportedElements](/linter/rules//lint/rules/no-aria-unsupported-elements) | +| [click-events-have-key-events](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/click-events-have-key-events.md) |[useKeyWithClickEvents](/linter/rules//lint/rules/use-key-with-click-events) | +| [heading-has-content](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/heading-has-content.md) |[useHeadingContent](/linter/rules//lint/rules/use-heading-content) | +| [html-has-lang](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/html-has-lang.md) |[useHtmlLang](/linter/rules//lint/rules/use-html-lang) | +| [iframe-has-title](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/iframe-has-title.md) |[useIframeTitle](/linter/rules//lint/rules/use-iframe-title) | +| [lang](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/lang.md) |[useValidLang](/linter/rules//lint/rules/use-valid-lang) | +| [media-has-caption](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/media-has-caption.md) |[useMediaCaption](/linter/rules//lint/rules/use-media-caption) | +| [mouse-events-have-key-events](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/mouse-events-have-key-events.md) |[useKeyWithMouseEvents](/linter/rules//lint/rules/use-key-with-mouse-events) | +| [no-access-key](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-access-key.md) |[noAccessKey](/linter/rules//lint/rules/no-access-key) | +| [no-aria-hidden-on-focusable](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-aria-hidden-on-focusable.md) |[noAriaHiddenOnFocusable](/linter/rules//lint/rules/no-aria-hidden-on-focusable) | +| [no-autofocus](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-autofocus.md) |[noAutofocus](/linter/rules//lint/rules/no-autofocus) | +| [no-distracting-elements](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-distracting-elements.md) |[noDistractingElements](/linter/rules//lint/rules/no-distracting-elements) | +| [no-interactive-element-to-noninteractive-role](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-interactive-element-to-noninteractive-role.md) |[noInteractiveElementToNoninteractiveRole](/linter/rules//lint/rules/no-interactive-element-to-noninteractive-role) | +| [no-noninteractive-element-to-interactive-role](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-noninteractive-element-to-interactive-role.md) |[noNoninteractiveElementToInteractiveRole](/linter/rules//lint/rules/no-noninteractive-element-to-interactive-role) | +| [no-noninteractive-tabindex](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-noninteractive-tabindex.md) |[noNoninteractiveTabindex](/linter/rules//lint/rules/no-noninteractive-tabindex) | +| [no-redundant-roles](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-redundant-roles.md) |[noRedundantAlt](/linter/rules//lint/rules/no-redundant-alt) | +| [role-has-required-aria-props](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/role-has-required-aria-props.md) |[useAriaPropsForRole](/linter/rules//lint/rules/use-aria-props-for-role) | +| [scope](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/scope.md) |[noHeaderScope](/linter/rules//lint/rules/no-header-scope) | +| [tabindex-no-positive](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/tabindex-no-positive.md) |[noPositiveTabindex](/linter/rules//lint/rules/no-positive-tabindex) | +## eslint-plugin-mysticates rules to Biome +| eslint-plugin-mysticates rule name | Biome rule name | +| ---- | ---- | +| [no-this-in-static](https://github.com/mysticatea/eslint-plugin/blob/master/docs/rules/no-this-in-static.md) |[noThisInStatic](/linter/rules//lint/rules/no-this-in-static) | +## eslint-plugin-react rules to Biome +| eslint-plugin-react rule name | Biome rule name | +| ---- | ---- | +| [button-has-type](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/button-has-type.md) |[useButtonType](/linter/rules//lint/rules/use-button-type) | +| [jsx-boolean-value](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md) |[noImplicitBoolean](/linter/rules//lint/rules/no-implicit-boolean) | +| [jsx-fragments](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-fragments.md) |[useFragmentSyntax](/linter/rules//lint/rules/use-fragment-syntax) | +| [jsx-no-comment-textnodes](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-comment-textnodes.md) |[noCommentText](/linter/rules//lint/rules/no-comment-text) | +| [jsx-no-duplicate-props](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md) |[noDuplicateJsxProps](/linter/rules//lint/rules/no-duplicate-jsx-props) | +| [jsx-no-target-blank](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-target-blank.md) |[noBlankTarget](/linter/rules//lint/rules/no-blank-target) | +| [jsx-no-useless-fragment](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-useless-fragment.md) |[noUselessFragments](/linter/rules//lint/rules/no-useless-fragments) | +| [no-array-index-key](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md) |[noArrayIndexKey](/linter/rules//lint/rules/no-array-index-key) | +| [no-children-prop](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-children-prop.md) |[noChildrenProp](/linter/rules//lint/rules/no-children-prop) | +| [no-danger](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-danger.md) |[noDangerouslySetInnerHtmlWithChildren](/linter/rules//lint/rules/no-dangerously-set-inner-html-with-children) | +| [no-danger-with-children](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-danger-with-children.md) |[noDangerouslySetInnerHtml](/linter/rules//lint/rules/no-dangerously-set-inner-html) | +| [void-dom-elements-no-children](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/void-dom-elements-no-children.md) |[noVoidElementsWithChildren](/linter/rules//lint/rules/no-void-elements-with-children) | +## eslint-plugin-react-hooks rules to Biome +| eslint-plugin-react-hooks rule name | Biome rule name | +| ---- | ---- | +| [exhaustive-deps](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md) |[useExhaustiveDependencies](/linter/rules//lint/rules/use-exhaustive-dependencies) | +| [rules-of-hooks](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md) |[useHookAtTopLevel](/linter/rules//lint/rules/use-hook-at-top-level) | +## eslint-plugin-sonarjs rules to Biome +| eslint-plugin-sonarjs rule name | Biome rule name | +| ---- | ---- | +| [cognitive-complexity](https://github.com/SonarSource/eslint-plugin-sonarjs/blob/HEAD/docs/rules/cognitive-complexity.md) |[noExcessiveCognitiveComplexity](/linter/rules//lint/rules/no-excessive-cognitive-complexity) | +## eslint-plugin-stylistic rules to Biome +| eslint-plugin-stylistic rule name | Biome rule name | +| ---- | ---- | +| [jsx-self-closing-comp](https://eslint.style/rules/default/jsx-self-closing-comp) |[useSelfClosingElements](/linter/rules//lint/rules/use-self-closing-elements) | +## eslint-plugin-typescript rules to Biome +| eslint-plugin-typescript rule name | Biome rule name | +| ---- | ---- | +| [array-type](https://typescript-eslint.io/rules/array-type) |[useShorthandArrayType](/linter/rules//lint/rules/use-shorthand-array-type) | +| [ban-types](https://typescript-eslint.io/rules/ban-types) |[noBannedTypes](/linter/rules//lint/rules/no-banned-types) | +| [naming-convention](https://typescript-eslint.io/rules/naming-convention) |[useNamingConvention](/linter/rules//lint/rules/use-naming-convention) | +| [no-empty-interface](https://typescript-eslint.io/rules/no-empty-interface) |[noEmptyInterface](/linter/rules//lint/rules/no-empty-interface) | +| [no-explicit-any](https://typescript-eslint.io/rules/no-explicit-any) |[noExplicitAny](/linter/rules//lint/rules/no-explicit-any) | +| [no-extra-non-null-assertion](https://typescript-eslint.io/rules/no-extra-non-null-assertion) |[noExtraNonNullAssertion](/linter/rules//lint/rules/no-extra-non-null-assertion) | +| [no-extraneous-class](https://typescript-eslint.io/rules/no-extraneous-class) |[noStaticOnlyClass](/linter/rules//lint/rules/no-static-only-class) | +| [no-inferrable-types](https://typescript-eslint.io/rules/no-inferrable-types) |[noInferrableTypes](/linter/rules//lint/rules/no-inferrable-types) | +| [no-invalid-void-type](https://typescript-eslint.io/rules/no-invalid-void-type) |[noConfusingVoidType](/linter/rules//lint/rules/no-confusing-void-type) | +| [no-misused-new](https://typescript-eslint.io/rules/no-misused-new) |[noMisleadingInstantiator](/linter/rules//lint/rules/no-misleading-instantiator) | +| [no-namespace](https://typescript-eslint.io/rules/no-namespace) |[noNamespace](/linter/rules//lint/rules/no-namespace) | +| [no-non-null-assertion](https://typescript-eslint.io/rules/no-non-null-assertion) |[noNonNullAssertion](/linter/rules//lint/rules/no-non-null-assertion) | +| [no-redeclare](https://typescript-eslint.io/rules/no-redeclare) |[noRedeclare](/linter/rules//lint/rules/no-redeclare) | +| [no-this-alias](https://typescript-eslint.io/rules/no-this-alias) |[noUselessThisAlias](/linter/rules//lint/rules/no-useless-this-alias) | +| [no-unnecessary-type-constraint](https://typescript-eslint.io/rules/no-unnecessary-type-constraint) |[noUselessTypeConstraint](/linter/rules//lint/rules/no-useless-type-constraint) | +| [no-unsafe-declaration-merging](https://typescript-eslint.io/rules/no-unsafe-declaration-merging) |[noUnsafeDeclarationMerging](/linter/rules//lint/rules/no-unsafe-declaration-merging) | +| [no-useless-constructor](https://typescript-eslint.io/rules/no-useless-constructor) |[noUselessConstructor](/linter/rules//lint/rules/no-useless-constructor) | +| [no-useless-empty-export](https://typescript-eslint.io/rules/no-useless-empty-export) |[noUselessEmptyExport](/linter/rules//lint/rules/no-useless-empty-export) | +| [no-useless-template-literals](https://typescript-eslint.io/rules/no-useless-template-literals) |[noUnusedTemplateLiteral](/linter/rules//lint/rules/no-unused-template-literal) | +| [parameter-properties](https://typescript-eslint.io/rules/parameter-properties) |[noParameterProperties](/linter/rules//lint/rules/no-parameter-properties) | +| [prefer-as-const](https://typescript-eslint.io/rules/prefer-as-const) |[useAsConstAssertion](/linter/rules//lint/rules/use-as-const-assertion) | +| [prefer-enum-initializers](https://typescript-eslint.io/rules/prefer-enum-initializers) |[useEnumInitializers](/linter/rules//lint/rules/use-enum-initializers) | +| [prefer-literal-enum-member](https://typescript-eslint.io/rules/prefer-literal-enum-member) |[useLiteralEnumMembers](/linter/rules//lint/rules/use-literal-enum-members) | +| [prefer-namespace-keyword](https://typescript-eslint.io/rules/prefer-namespace-keyword) |[useNamespaceKeyword](/linter/rules//lint/rules/use-namespace-keyword) | +| [prefer-optional-chain](https://typescript-eslint.io/rules/prefer-optional-chain) |[useOptionalChain](/linter/rules//lint/rules/use-optional-chain) | +## eslint-plugin-unicorn rules to Biome +| eslint-plugin-unicorn rule name | Biome rule name | +| ---- | ---- | +| [no-array-for-each](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-array-for-each.md) |[noForEach](/linter/rules//lint/rules/no-for-each) | +| [no-instanceof-array](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-instanceof-array.md) |[useIsArray](/linter/rules//lint/rules/use-is-array) | +| [no-useless-switch-case](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-switch-case.md) |[noUselessSwitchCase](/linter/rules//lint/rules/no-useless-switch-case) | +| [prefer-array-flat-map](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-array-flat-map.md) |[useFlatMap](/linter/rules//lint/rules/use-flat-map) | diff --git a/website/src/content/docs/linter/index.mdx b/website/src/content/docs/linter/index.mdx index ae411dea1868..5b5b678710d1 100644 --- a/website/src/content/docs/linter/index.mdx +++ b/website/src/content/docs/linter/index.mdx @@ -89,7 +89,6 @@ debugger; debugger; ``` - ## Configuration ### Enable a lint rule @@ -178,3 +177,7 @@ When they do _accept_ some, you can pass them by shaping the value of the rule d - `level` will indicate the severity of the diagnostic, valid values are: `"off"`, `"warn"` and `"error"`; - `options` will change based on the rule. + +## Migrating from other linters + +Many of Biome lint rules are inspired from other linters. If you want to migrate from other linters such as ESLint or `typescript-eslint`, check the [rule sources page](/linter/rule) diff --git a/website/src/content/docs/linter/rules-sources.mdx b/website/src/content/docs/linter/rules-sources.mdx new file mode 100644 index 000000000000..adc48559bece --- /dev/null +++ b/website/src/content/docs/linter/rules-sources.mdx @@ -0,0 +1,194 @@ +--- +title: Rules sources +description: A page that maps lint rules from other sources to Biome +--- + +:::note +Some **Biome** rules might **not** have options, compared to the original rule. +::: +## Clippy +| Clippy rule name | Biome rule name | +| ---- | ---- | +| [approx_constant](https://rust-lang.github.io/rust-clippy/master/#/approx_constant) |[noApproximativeNumericConstant](/linter/rules//lint/rules/no-approximative-numeric-constant) (inspired) | +| [match_str_case_mismatch](https://rust-lang.github.io/rust-clippy/master/#/match_str_case_mismatch) |[noStringCaseMismatch](/linter/rules//lint/rules/no-string-case-mismatch) | +| [misrefactored_assign_op](https://rust-lang.github.io/rust-clippy/master/#/misrefactored_assign_op) |[noMisrefactoredShorthandAssign](/linter/rules//lint/rules/no-misrefactored-shorthand-assign) | +## ESLint +| ESLint rule name | Biome rule name | +| ---- | ---- | +| [constructor-super](https://eslint.org/docs/latest/rules/constructor-super) |[noInvalidConstructorSuper](/linter/rules//lint/rules/no-invalid-constructor-super) | +| [curly](https://eslint.org/docs/latest/rules/curly) |[useBlockStatements](/linter/rules//lint/rules/use-block-statements) | +| [default-case-last](https://eslint.org/docs/latest/rules/default-case-last) |[useDefaultSwitchClauseLast](/linter/rules//lint/rules/use-default-switch-clause-last) | +| [default-param-last](https://eslint.org/docs/latest/rules/default-param-last) |[useDefaultParameterLast](/linter/rules//lint/rules/use-default-parameter-last) | +| [dot-notation](https://eslint.org/docs/latest/rules/dot-notation) |[useLiteralKeys](/linter/rules//lint/rules/use-literal-keys) | +| [eqeqeq](https://eslint.org/docs/latest/rules/eqeqeq) |[noDoubleEquals](/linter/rules//lint/rules/no-double-equals) | +| [for-direction](https://eslint.org/docs/latest/rules/for-direction) |[useValidForDirection](/linter/rules//lint/rules/use-valid-for-direction) | +| [getter-return](https://eslint.org/docs/latest/rules/getter-return) |[useGetterReturn](/linter/rules//lint/rules/use-getter-return) | +| [no-async-promise-executor](https://eslint.org/docs/latest/rules/no-async-promise-executor) |[noAsyncPromiseExecutor](/linter/rules//lint/rules/no-async-promise-executor) | +| [no-case-declarations](https://eslint.org/docs/latest/rules/no-case-declarations) |[noSwitchDeclarations](/linter/rules//lint/rules/no-switch-declarations) | +| [no-class-assign](https://eslint.org/docs/latest/rules/no-class-assign) |[noClassAssign](/linter/rules//lint/rules/no-class-assign) | +| [no-compare-neg-zero](https://eslint.org/docs/latest/rules/no-compare-neg-zero) |[noCompareNegZero](/linter/rules//lint/rules/no-compare-neg-zero) | +| [no-cond-assign](https://eslint.org/docs/latest/rules/no-cond-assign) |[noAssignInExpressions](/linter/rules//lint/rules/no-assign-in-expressions) (inspired) | +| [no-console](https://eslint.org/docs/latest/rules/no-console) |[noConsoleLog](/linter/rules//lint/rules/no-console-log) (inspired) | +| [no-const-assign](https://eslint.org/docs/latest/rules/no-const-assign) |[noConstAssign](/linter/rules//lint/rules/no-const-assign) | +| [no-constant-condition](https://eslint.org/docs/latest/rules/no-constant-condition) |[noConstantCondition](/linter/rules//lint/rules/no-constant-condition) | +| [no-constructor-return](https://eslint.org/docs/latest/rules/no-constructor-return) |[noConstructorReturn](/linter/rules//lint/rules/no-constructor-return) | +| [no-control-regex](https://eslint.org/docs/latest/rules/no-control-regex) |[noControlCharactersInRegex](/linter/rules//lint/rules/no-control-characters-in-regex) | +| [no-debugger](https://eslint.org/docs/latest/rules/no-debugger) |[noDebugger](/linter/rules//lint/rules/no-debugger) | +| [no-dupe-args](https://eslint.org/docs/latest/rules/no-dupe-args) |[noDuplicateParameters](/linter/rules//lint/rules/no-duplicate-parameters) | +| [no-dupe-class-members](https://eslint.org/docs/latest/rules/no-dupe-class-members) |[noDuplicateClassMembers](/linter/rules//lint/rules/no-duplicate-class-members) | +| [no-dupe-keys](https://eslint.org/docs/latest/rules/no-dupe-keys) |[noDuplicateObjectKeys](/linter/rules//lint/rules/no-duplicate-object-keys) | +| [no-duplicate-case](https://eslint.org/docs/latest/rules/no-duplicate-case) |[noDuplicateCase](/linter/rules//lint/rules/no-duplicate-case) | +| [no-else-return](https://eslint.org/docs/latest/rules/no-else-return) |[noUselessElse](/linter/rules//lint/rules/no-useless-else) (inspired) | +| [no-empty-character-class](https://eslint.org/docs/latest/rules/no-empty-character-class) |[noEmptyCharacterClassInRegex](/linter/rules//lint/rules/no-empty-character-class-in-regex) | +| [no-empty-pattern](https://eslint.org/docs/latest/rules/no-empty-pattern) |[noEmptyPattern](/linter/rules//lint/rules/no-empty-pattern) | +| [no-ex-assign](https://eslint.org/docs/latest/rules/no-ex-assign) |[noCatchAssign](/linter/rules//lint/rules/no-catch-assign) | +| [no-extra-boolean-cast](https://eslint.org/docs/latest/rules/no-extra-boolean-cast) |[noExtraBooleanCast](/linter/rules//lint/rules/no-extra-boolean-cast) | +| [no-extra-label](https://eslint.org/docs/latest/rules/no-extra-label) |[noUselessLabel](/linter/rules//lint/rules/no-useless-label) | +| [no-fallthrough](https://eslint.org/docs/latest/rules/no-fallthrough) |[noFallthroughSwitchClause](/linter/rules//lint/rules/no-fallthrough-switch-clause) | +| [no-func-assign](https://eslint.org/docs/latest/rules/no-func-assign) |[noFunctionAssign](/linter/rules//lint/rules/no-function-assign) | +| [no-import-assign](https://eslint.org/docs/latest/rules/no-import-assign) |[noImportAssign](/linter/rules//lint/rules/no-import-assign) | +| [no-inner-declarations](https://eslint.org/docs/latest/rules/no-inner-declarations) |[noInnerDeclarations](/linter/rules//lint/rules/no-inner-declarations) | +| [no-label-var](https://eslint.org/docs/latest/rules/no-label-var) |[noLabelVar](/linter/rules//lint/rules/no-label-var) | +| [no-labels](https://eslint.org/docs/latest/rules/no-labels) |[noConfusingLabels](/linter/rules//lint/rules/no-confusing-labels) (inspired) | +| [no-lonely-if](https://eslint.org/docs/latest/rules/no-lonely-if) |[useCollapsedElseIf](/linter/rules//lint/rules/use-collapsed-else-if) | +| [no-loss-of-precision](https://eslint.org/docs/latest/rules/no-loss-of-precision) |[noPrecisionLoss](/linter/rules//lint/rules/no-precision-loss) | +| [no-negated-condition](https://eslint.org/docs/latest/rules/no-negated-condition) |[noNegationElse](/linter/rules//lint/rules/no-negation-else) | +| [no-new-native-nonconstructor](https://eslint.org/docs/latest/rules/no-new-native-nonconstructor) |[noInvalidNewBuiltin](/linter/rules//lint/rules/no-invalid-new-builtin) | +| [no-new-symbol](https://eslint.org/docs/latest/rules/no-new-symbol) |[noNewSymbol](/linter/rules//lint/rules/no-new-symbol) | +| [no-nonoctal-decimal-escape](https://eslint.org/docs/latest/rules/no-nonoctal-decimal-escape) |[noNonoctalDecimalEscape](/linter/rules//lint/rules/no-nonoctal-decimal-escape) | +| [no-obj-calls](https://eslint.org/docs/latest/rules/no-obj-calls) |[noGlobalObjectCalls](/linter/rules//lint/rules/no-global-object-calls) | +| [no-param-reassign](https://eslint.org/docs/latest/rules/no-param-reassign) |[noParameterAssign](/linter/rules//lint/rules/no-parameter-assign) | +| [no-prototype-builtins](https://eslint.org/docs/latest/rules/no-prototype-builtins) |[noPrototypeBuiltins](/linter/rules//lint/rules/no-prototype-builtins) | +| [no-regex-spaces](https://eslint.org/docs/latest/rules/no-regex-spaces) |[noMultipleSpacesInRegularExpressionLiterals](/linter/rules//lint/rules/no-multiple-spaces-in-regular-expression-literals) | +| [no-restricted-globals](https://eslint.org/docs/latest/rules/no-restricted-globals) |[noRestrictedGlobals](/linter/rules//lint/rules/no-restricted-globals) | +| [no-self-assign](https://eslint.org/docs/latest/rules/no-self-assign) |[noSelfAssign](/linter/rules//lint/rules/no-self-assign) | +| [no-self-compare](https://eslint.org/docs/latest/rules/no-self-compare) |[noSelfCompare](/linter/rules//lint/rules/no-self-compare) | +| [no-sequences](https://eslint.org/docs/latest/rules/no-sequences) |[noCommaOperator](/linter/rules//lint/rules/no-comma-operator) | +| [no-setter-return](https://eslint.org/docs/latest/rules/no-setter-return) |[noSetterReturn](/linter/rules//lint/rules/no-setter-return) | +| [no-shadow-restricted-names](https://eslint.org/docs/latest/rules/no-shadow-restricted-names) |[noShadowRestrictedNames](/linter/rules//lint/rules/no-shadow-restricted-names) | +| [no-sparse-array](https://eslint.org/docs/latest/rules/no-sparse-array) |[noSparseArray](/linter/rules//lint/rules/no-sparse-array) | +| [no-this-before-super](https://eslint.org/docs/latest/rules/no-this-before-super) |[noUnreachableSuper](/linter/rules//lint/rules/no-unreachable-super) | +| [no-undef](https://eslint.org/docs/latest/rules/no-undef) |[noUndeclaredVariables](/linter/rules//lint/rules/no-undeclared-variables) | +| [no-unreachable](https://eslint.org/docs/latest/rules/no-unreachable) |[noUnreachable](/linter/rules//lint/rules/no-unreachable) | +| [no-unsafe-finally](https://eslint.org/docs/latest/rules/no-unsafe-finally) |[noUnsafeFinally](/linter/rules//lint/rules/no-unsafe-finally) | +| [no-unsafe-negation](https://eslint.org/docs/latest/rules/no-unsafe-negation) |[noUnsafeNegation](/linter/rules//lint/rules/no-unsafe-negation) | +| [no-unsafe-optional-chaining](https://eslint.org/docs/latest/rules/no-unsafe-optional-chaining) |[noUnsafeOptionalChaining](/linter/rules//lint/rules/no-unsafe-optional-chaining) | +| [no-unused-labels](https://eslint.org/docs/latest/rules/no-unused-labels) |[noUnusedLabels](/linter/rules//lint/rules/no-unused-labels) | +| [no-unused-vars](https://eslint.org/docs/latest/rules/no-unused-vars) |[noUnusedVariables](/linter/rules//lint/rules/no-unused-variables) | +| [no-useless-catch](https://eslint.org/docs/latest/rules/no-useless-catch) |[noUselessCatch](/linter/rules//lint/rules/no-useless-catch) | +| [no-useless-rename](https://eslint.org/docs/latest/rules/no-useless-rename) |[noUselessRename](/linter/rules//lint/rules/no-useless-rename) | +| [no-var](https://eslint.org/docs/latest/rules/no-var) |[noVar](/linter/rules//lint/rules/no-var) | +| [no-void](https://eslint.org/docs/latest/rules/no-void) |[noVoid](/linter/rules//lint/rules/no-void) | +| [no-with](https://eslint.org/docs/latest/rules/no-with) |[noWith](/linter/rules//lint/rules/no-with) | +| [one-var](https://eslint.org/docs/latest/rules/one-var) |[useSingleVarDeclarator](/linter/rules//lint/rules/use-single-var-declarator) | +| [operator-assignment](https://eslint.org/docs/latest/rules/operator-assignment) |[useShorthandAssign](/linter/rules//lint/rules/use-shorthand-assign) | +| [prefer-arrow-callback](https://eslint.org/docs/latest/rules/prefer-arrow-callback) |[useArrowFunction](/linter/rules//lint/rules/use-arrow-function) (inspired) | +| [prefer-const](https://eslint.org/docs/latest/rules/prefer-const) |[useConst](/linter/rules//lint/rules/use-const) | +| [prefer-exponentiation-operator](https://eslint.org/docs/latest/rules/prefer-exponentiation-operator) |[useExponentiationOperator](/linter/rules//lint/rules/use-exponentiation-operator) | +| [prefer-numeric-literals](https://eslint.org/docs/latest/rules/prefer-numeric-literals) |[useNumericLiterals](/linter/rules//lint/rules/use-numeric-literals) | +| [prefer-regex-literals](https://eslint.org/docs/latest/rules/prefer-regex-literals) |[useRegexLiterals](/linter/rules//lint/rules/use-regex-literals) | +| [prefer-rest-params](https://eslint.org/docs/latest/rules/prefer-rest-params) |[noArguments](/linter/rules//lint/rules/no-arguments) (inspired) | +| [prefer-template](https://eslint.org/docs/latest/rules/prefer-template) |[useTemplate](/linter/rules//lint/rules/use-template) | +| [require-yield](https://eslint.org/docs/latest/rules/require-yield) |[useYield](/linter/rules//lint/rules/use-yield) | +| [use-isnan](https://eslint.org/docs/latest/rules/use-isnan) |[useIsNan](/linter/rules//lint/rules/use-is-nan) | +| [valid-typeof](https://eslint.org/docs/latest/rules/valid-typeof) |[useValidTypeof](/linter/rules//lint/rules/use-valid-typeof) | +## eslint-plugin-import +| eslint-plugin-import rule name | Biome rule name | +| ---- | ---- | +| [no-default-export](https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-default-export.md) |[noDefaultExport](/linter/rules//lint/rules/no-default-export) (inspired) | +## eslint-plugin-jsx-a11y +| eslint-plugin-jsx-a11y rule name | Biome rule name | +| ---- | ---- | +| [alt-text](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/alt-text.md) |[useAltText](/linter/rules//lint/rules/use-alt-text) | +| [anchor-has-content](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/anchor-has-content.md) |[useAnchorContent](/linter/rules//lint/rules/use-anchor-content) | +| [anchor-is-valid](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/anchor-is-valid.md) |[useValidAnchor](/linter/rules//lint/rules/use-valid-anchor) | +| [aria-activedescendant-has-tabindex](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/aria-activedescendant-has-tabindex.md) |[useAriaActivedescendantWithTabindex](/linter/rules//lint/rules/use-aria-activedescendant-with-tabindex) | +| [aria-props](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/aria-props.md) |[useValidAriaProps](/linter/rules//lint/rules/use-valid-aria-props) | +| [aria-proptypes](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/aria-proptypes.md) |[useValidAriaValues](/linter/rules//lint/rules/use-valid-aria-values) | +| [aria-role](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/aria-role.md) |[useValidAriaRole](/linter/rules//lint/rules/use-valid-aria-role) | +| [aria-unsupported-elements](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/aria-unsupported-elements.md) |[noAriaUnsupportedElements](/linter/rules//lint/rules/no-aria-unsupported-elements) | +| [click-events-have-key-events](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/click-events-have-key-events.md) |[useKeyWithClickEvents](/linter/rules//lint/rules/use-key-with-click-events) | +| [heading-has-content](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/heading-has-content.md) |[useHeadingContent](/linter/rules//lint/rules/use-heading-content) | +| [html-has-lang](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/html-has-lang.md) |[useHtmlLang](/linter/rules//lint/rules/use-html-lang) | +| [iframe-has-title](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/iframe-has-title.md) |[useIframeTitle](/linter/rules//lint/rules/use-iframe-title) | +| [lang](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/lang.md) |[useValidLang](/linter/rules//lint/rules/use-valid-lang) | +| [media-has-caption](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/media-has-caption.md) |[useMediaCaption](/linter/rules//lint/rules/use-media-caption) | +| [mouse-events-have-key-events](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/mouse-events-have-key-events.md) |[useKeyWithMouseEvents](/linter/rules//lint/rules/use-key-with-mouse-events) | +| [no-access-key](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-access-key.md) |[noAccessKey](/linter/rules//lint/rules/no-access-key) (inspired) | +| [no-aria-hidden-on-focusable](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-aria-hidden-on-focusable.md) |[noAriaHiddenOnFocusable](/linter/rules//lint/rules/no-aria-hidden-on-focusable) | +| [no-autofocus](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-autofocus.md) |[noAutofocus](/linter/rules//lint/rules/no-autofocus) | +| [no-distracting-elements](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-distracting-elements.md) |[noDistractingElements](/linter/rules//lint/rules/no-distracting-elements) | +| [no-interactive-element-to-noninteractive-role](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-interactive-element-to-noninteractive-role.md) |[noInteractiveElementToNoninteractiveRole](/linter/rules//lint/rules/no-interactive-element-to-noninteractive-role) | +| [no-noninteractive-element-to-interactive-role](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-noninteractive-element-to-interactive-role.md) |[noNoninteractiveElementToInteractiveRole](/linter/rules//lint/rules/no-noninteractive-element-to-interactive-role) | +| [no-noninteractive-tabindex](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-noninteractive-tabindex.md) |[noNoninteractiveTabindex](/linter/rules//lint/rules/no-noninteractive-tabindex) | +| [no-redundant-roles](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/no-redundant-roles.md) |[noRedundantAlt](/linter/rules//lint/rules/no-redundant-alt) | +| [role-has-required-aria-props](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/role-has-required-aria-props.md) |[useAriaPropsForRole](/linter/rules//lint/rules/use-aria-props-for-role) | +| [scope](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/scope.md) |[noHeaderScope](/linter/rules//lint/rules/no-header-scope) | +| [tabindex-no-positive](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/docs/rules/tabindex-no-positive.md) |[noPositiveTabindex](/linter/rules//lint/rules/no-positive-tabindex) | +## eslint-plugin-mysticates +| eslint-plugin-mysticates rule name | Biome rule name | +| ---- | ---- | +| [no-this-in-static](https://github.com/mysticatea/eslint-plugin/blob/master/docs/rules/no-this-in-static.md) |[noThisInStatic](/linter/rules//lint/rules/no-this-in-static) (inspired) | +## eslint-plugin-react +| eslint-plugin-react rule name | Biome rule name | +| ---- | ---- | +| [button-has-type](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/button-has-type.md) |[useButtonType](/linter/rules//lint/rules/use-button-type) | +| [jsx-boolean-value](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md) |[noImplicitBoolean](/linter/rules//lint/rules/no-implicit-boolean) | +| [jsx-fragments](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-fragments.md) |[useFragmentSyntax](/linter/rules//lint/rules/use-fragment-syntax) | +| [jsx-no-comment-textnodes](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-comment-textnodes.md) |[noCommentText](/linter/rules//lint/rules/no-comment-text) | +| [jsx-no-duplicate-props](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md) |[noDuplicateJsxProps](/linter/rules//lint/rules/no-duplicate-jsx-props) | +| [jsx-no-target-blank](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-target-blank.md) |[noBlankTarget](/linter/rules//lint/rules/no-blank-target) | +| [jsx-no-useless-fragment](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-useless-fragment.md) |[noUselessFragments](/linter/rules//lint/rules/no-useless-fragments) | +| [no-array-index-key](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-array-index-key.md) |[noArrayIndexKey](/linter/rules//lint/rules/no-array-index-key) (inspired) | +| [no-children-prop](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-children-prop.md) |[noChildrenProp](/linter/rules//lint/rules/no-children-prop) | +| [no-danger](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-danger.md) |[noDangerouslySetInnerHtmlWithChildren](/linter/rules//lint/rules/no-dangerously-set-inner-html-with-children) | +| [no-danger-with-children](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-danger-with-children.md) |[noDangerouslySetInnerHtml](/linter/rules//lint/rules/no-dangerously-set-inner-html) | +| [void-dom-elements-no-children](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/void-dom-elements-no-children.md) |[noVoidElementsWithChildren](/linter/rules//lint/rules/no-void-elements-with-children) | +## eslint-plugin-react-hooks +| eslint-plugin-react-hooks rule name | Biome rule name | +| ---- | ---- | +| [exhaustive-deps](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md) |[useExhaustiveDependencies](/linter/rules//lint/rules/use-exhaustive-dependencies) (inspired) | +| [rules-of-hooks](https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md) |[useHookAtTopLevel](/linter/rules//lint/rules/use-hook-at-top-level) | +## eslint-plugin-sonarjs +| eslint-plugin-sonarjs rule name | Biome rule name | +| ---- | ---- | +| [cognitive-complexity](https://github.com/SonarSource/eslint-plugin-sonarjs/blob/HEAD/docs/rules/cognitive-complexity.md) |[noExcessiveCognitiveComplexity](/linter/rules//lint/rules/no-excessive-cognitive-complexity) (inspired) | +## eslint-plugin-stylistic +| eslint-plugin-stylistic rule name | Biome rule name | +| ---- | ---- | +| [jsx-self-closing-comp](https://eslint.style/rules/default/jsx-self-closing-comp) |[useSelfClosingElements](/linter/rules//lint/rules/use-self-closing-elements) (inspired) | +## eslint-plugin-typescript +| eslint-plugin-typescript rule name | Biome rule name | +| ---- | ---- | +| [array-type](https://typescript-eslint.io/rules/array-type) |[useShorthandArrayType](/linter/rules//lint/rules/use-shorthand-array-type) (inspired) | +| [ban-types](https://typescript-eslint.io/rules/ban-types) |[noBannedTypes](/linter/rules//lint/rules/no-banned-types) (inspired) | +| [naming-convention](https://typescript-eslint.io/rules/naming-convention) |[useNamingConvention](/linter/rules//lint/rules/use-naming-convention) (inspired) | +| [no-empty-interface](https://typescript-eslint.io/rules/no-empty-interface) |[noEmptyInterface](/linter/rules//lint/rules/no-empty-interface) (inspired) | +| [no-explicit-any](https://typescript-eslint.io/rules/no-explicit-any) |[noExplicitAny](/linter/rules//lint/rules/no-explicit-any) | +| [no-extra-non-null-assertion](https://typescript-eslint.io/rules/no-extra-non-null-assertion) |[noExtraNonNullAssertion](/linter/rules//lint/rules/no-extra-non-null-assertion) | +| [no-extraneous-class](https://typescript-eslint.io/rules/no-extraneous-class) |[noStaticOnlyClass](/linter/rules//lint/rules/no-static-only-class) | +| [no-inferrable-types](https://typescript-eslint.io/rules/no-inferrable-types) |[noInferrableTypes](/linter/rules//lint/rules/no-inferrable-types) | +| [no-invalid-void-type](https://typescript-eslint.io/rules/no-invalid-void-type) |[noConfusingVoidType](/linter/rules//lint/rules/no-confusing-void-type) | +| [no-misused-new](https://typescript-eslint.io/rules/no-misused-new) |[noMisleadingInstantiator](/linter/rules//lint/rules/no-misleading-instantiator) | +| [no-namespace](https://typescript-eslint.io/rules/no-namespace) |[noNamespace](/linter/rules//lint/rules/no-namespace) | +| [no-non-null-assertion](https://typescript-eslint.io/rules/no-non-null-assertion) |[noNonNullAssertion](/linter/rules//lint/rules/no-non-null-assertion) | +| [no-redeclare](https://typescript-eslint.io/rules/no-redeclare) |[noRedeclare](/linter/rules//lint/rules/no-redeclare) | +| [no-this-alias](https://typescript-eslint.io/rules/no-this-alias) |[noUselessThisAlias](/linter/rules//lint/rules/no-useless-this-alias) (inspired) | +| [no-unnecessary-type-constraint](https://typescript-eslint.io/rules/no-unnecessary-type-constraint) |[noUselessTypeConstraint](/linter/rules//lint/rules/no-useless-type-constraint) | +| [no-unsafe-declaration-merging](https://typescript-eslint.io/rules/no-unsafe-declaration-merging) |[noUnsafeDeclarationMerging](/linter/rules//lint/rules/no-unsafe-declaration-merging) | +| [no-useless-constructor](https://typescript-eslint.io/rules/no-useless-constructor) |[noUselessConstructor](/linter/rules//lint/rules/no-useless-constructor) | +| [no-useless-empty-export](https://typescript-eslint.io/rules/no-useless-empty-export) |[noUselessEmptyExport](/linter/rules//lint/rules/no-useless-empty-export) | +| [no-useless-template-literals](https://typescript-eslint.io/rules/no-useless-template-literals) |[noUnusedTemplateLiteral](/linter/rules//lint/rules/no-unused-template-literal) | +| [parameter-properties](https://typescript-eslint.io/rules/parameter-properties) |[noParameterProperties](/linter/rules//lint/rules/no-parameter-properties) (inspired) | +| [prefer-as-const](https://typescript-eslint.io/rules/prefer-as-const) |[useAsConstAssertion](/linter/rules//lint/rules/use-as-const-assertion) | +| [prefer-enum-initializers](https://typescript-eslint.io/rules/prefer-enum-initializers) |[useEnumInitializers](/linter/rules//lint/rules/use-enum-initializers) | +| [prefer-literal-enum-member](https://typescript-eslint.io/rules/prefer-literal-enum-member) |[useLiteralEnumMembers](/linter/rules//lint/rules/use-literal-enum-members) | +| [prefer-namespace-keyword](https://typescript-eslint.io/rules/prefer-namespace-keyword) |[useNamespaceKeyword](/linter/rules//lint/rules/use-namespace-keyword) | +| [prefer-optional-chain](https://typescript-eslint.io/rules/prefer-optional-chain) |[useOptionalChain](/linter/rules//lint/rules/use-optional-chain) | +## eslint-plugin-unicorn +| eslint-plugin-unicorn rule name | Biome rule name | +| ---- | ---- | +| [no-array-for-each](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-array-for-each.md) |[noForEach](/linter/rules//lint/rules/no-for-each) (inspired) | +| [no-instanceof-array](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-instanceof-array.md) |[useIsArray](/linter/rules//lint/rules/use-is-array) | +| [no-useless-switch-case](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-switch-case.md) |[noUselessSwitchCase](/linter/rules//lint/rules/no-useless-switch-case) | +| [prefer-array-flat-map](https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-array-flat-map.md) |[useFlatMap](/linter/rules//lint/rules/use-flat-map) | diff --git a/website/src/content/docs/linter/rules/index.mdx b/website/src/content/docs/linter/rules/index.mdx index 0fe698d44802..4f95ec37e41a 100644 --- a/website/src/content/docs/linter/rules/index.mdx +++ b/website/src/content/docs/linter/rules/index.mdx @@ -1,5 +1,5 @@ --- -title: Lint Rules +title: Rules description: List of available lint rules. --- diff --git a/xtask/lintdoc/src/main.rs b/xtask/lintdoc/src/main.rs index 8de485f7eb2a..2043256de0d2 100644 --- a/xtask/lintdoc/src/main.rs +++ b/xtask/lintdoc/src/main.rs @@ -1,3 +1,6 @@ +mod rules_sources; + +use crate::rules_sources::generate_rule_sources; use biome_analyze::{ AnalysisFilter, AnalyzerOptions, ControlFlow, FixKind, GroupCategory, Queryable, RegistryVisitor, Rule, RuleCategory, RuleFilter, RuleGroup, RuleMetadata, RuleSource, @@ -30,6 +33,7 @@ use xtask::{glue::fs2, *}; fn main() -> Result<()> { let root = project_root().join("website/src/content/docs/linter/rules"); let reference_groups = project_root().join("website/src/components/generated/Groups.astro"); + let rules_sources = project_root().join("website/src/content/docs/linter/rules-sources.mdx"); let reference_number_of_rules = project_root().join("website/src/components/generated/NumberOfRules.astro"); let reference_recommended_rules = @@ -52,7 +56,7 @@ fn main() -> Result<()> { let mut index = Vec::new(); let mut reference_buffer = Vec::new(); writeln!(index, "---")?; - writeln!(index, "title: Lint Rules")?; + writeln!(index, "title: Rules")?; writeln!(index, "description: List of available lint rules.")?; writeln!(index, "---")?; writeln!(index)?; @@ -145,6 +149,7 @@ fn main() -> Result<()> { reference_buffer, "" )?; + let rule_sources_buffer = generate_rule_sources(groups.clone())?; for (group, rules) in groups { generate_group( group, @@ -192,6 +197,7 @@ fn main() -> Result<()> { fs2::write(reference_groups, reference_buffer)?; fs2::write(reference_number_of_rules, number_of_rules_buffer)?; fs2::write(reference_recommended_rules, recommended_rules_buffer)?; + fs2::write(rules_sources, rule_sources_buffer)?; Ok(()) } diff --git a/xtask/lintdoc/src/rules_sources.rs b/xtask/lintdoc/src/rules_sources.rs new file mode 100644 index 000000000000..f404080695e4 --- /dev/null +++ b/xtask/lintdoc/src/rules_sources.rs @@ -0,0 +1,121 @@ +use biome_analyze::RuleMetadata; +use convert_case::{Case, Casing}; +use std::cmp::Ordering; +use std::collections::{BTreeMap, BTreeSet}; +use std::io::Write; +use xtask::*; + +#[derive(Debug, Eq, PartialEq)] +struct SourceSet { + source_rule_name: String, + source_link: String, + biome_rule_name: String, + biome_link: String, + inspired: bool, +} + +impl Ord for SourceSet { + fn cmp(&self, other: &Self) -> Ordering { + self.source_rule_name.cmp(&other.source_rule_name) + } +} + +impl PartialOrd for SourceSet { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl SourceSet {} + +pub(crate) fn generate_rule_sources( + rules: BTreeMap<&str, BTreeMap<&'static str, RuleMetadata>>, +) -> Result> { + let mut buffer = vec![]; + + writeln!( + buffer, + r#"--- +title: Rules sources +description: A page that maps lint rules from other sources to Biome +--- + "# + )?; + + writeln!( + buffer, + r#":::note +Some **Biome** rules might **not** have options, compared to the original rule. +:::"# + )?; + + let rules = rules + .into_iter() + .flat_map(|(_, rule)| rule) + .collect::>(); + + let mut rules_by_source = BTreeMap::>::new(); + + for (rule_name, metadata) in rules { + if let Some(source) = &metadata.source { + let set = rules_by_source.get_mut(&format!("{source}")); + if let Some(set) = set { + set.insert(SourceSet { + biome_rule_name: rule_name.to_string(), + biome_link: format!("/lint/rules/{}", rule_name.to_case(Case::Kebab)), + source_link: source.to_rule_url(), + source_rule_name: source.as_rule_name().to_string(), + inspired: metadata + .source_kind + .map(|kind| kind.is_inspired()) + .unwrap_or(false), + }); + } else { + let mut set = BTreeSet::new(); + set.insert(SourceSet { + biome_rule_name: rule_name.to_string(), + biome_link: format!("/lint/rules/{}", rule_name.to_case(Case::Kebab)), + source_link: source.to_rule_url(), + source_rule_name: source.as_rule_name().to_string(), + inspired: metadata + .source_kind + .map(|kind| kind.is_inspired()) + .unwrap_or(true), + }); + rules_by_source.insert(format!("{source}"), set); + } + } + } + + for (source, rules) in rules_by_source { + writeln!(buffer, "## {source}")?; + writeln!(buffer, r#"| {source} rule name | Biome rule name |"#)?; + writeln!(buffer, r#"| ---- | ---- |"#)?; + + push_to_table(rules, &mut buffer)?; + } + + Ok(buffer) +} + +fn push_to_table(source_set: BTreeSet, buffer: &mut Vec) -> Result { + let mut footnotes = 0; + for source_set in source_set { + write!( + buffer, + "| [{}]({}) |[{}](/linter/rules/{})", + source_set.source_rule_name, + source_set.source_link, + source_set.biome_rule_name, + source_set.biome_link + )?; + + if source_set.inspired { + footnotes += 1; + write!(buffer, " (inspired)")?; + } + writeln!(buffer, " |")?; + } + + Ok(footnotes) +}