From 29db060c78e7c8e9c925e1348ed1e1af50e116af Mon Sep 17 00:00:00 2001 From: "Alexander S." Date: Wed, 4 Dec 2024 03:16:00 +0100 Subject: [PATCH] fix(linter): detect typescript eslint alias rules (#7622) closes #7233 --- .../oxlint-eslint.json | 8 +++ .../oxlint-typescript.json | 9 ++++ .../eslint_and_typescript_alias_rules/test.js | 6 +++ apps/oxlint/src/lint.rs | 23 +++++++++ crates/oxc_linter/src/config/rules.rs | 6 ++- crates/oxc_linter/src/utils/mod.rs | 50 +++++++++++++++++++ 6 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-eslint.json create mode 100644 apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-typescript.json create mode 100644 apps/oxlint/fixtures/eslint_and_typescript_alias_rules/test.js diff --git a/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-eslint.json b/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-eslint.json new file mode 100644 index 0000000000000..77325b77be833 --- /dev/null +++ b/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-eslint.json @@ -0,0 +1,8 @@ +{ + "categories": { + "correctness": "off" + }, + "rules": { + "no-magic-numbers": "error" + } +} \ No newline at end of file diff --git a/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-typescript.json b/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-typescript.json new file mode 100644 index 0000000000000..cb7d205cf37c9 --- /dev/null +++ b/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/oxlint-typescript.json @@ -0,0 +1,9 @@ +{ + "plugins": ["typescript"], + "categories": { + "correctness": "off" + }, + "rules": { + "typescript/no-magic-numbers": "error" + } +} \ No newline at end of file diff --git a/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/test.js b/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/test.js new file mode 100644 index 0000000000000..43867fe87af8c --- /dev/null +++ b/apps/oxlint/fixtures/eslint_and_typescript_alias_rules/test.js @@ -0,0 +1,6 @@ + + +const price = 200; +const price_with_tax = price * 0.19; // taxes are expensive + +console.debug(price_with_tax); \ No newline at end of file diff --git a/apps/oxlint/src/lint.rs b/apps/oxlint/src/lint.rs index b81846ff7fa3e..ffe842a861374 100644 --- a/apps/oxlint/src/lint.rs +++ b/apps/oxlint/src/lint.rs @@ -819,4 +819,27 @@ mod test { assert_eq!(result.number_of_warnings, 0); assert_eq!(result.number_of_errors, 1); } + + #[test] + fn test_eslint_and_typescript_alias_rules() { + let args = &[ + "-c", + "fixtures/eslint_and_typescript_alias_rules/oxlint-eslint.json", + "fixtures/eslint_and_typescript_alias_rules/test.js", + ]; + let result = test(args); + assert_eq!(result.number_of_files, 1); + assert_eq!(result.number_of_warnings, 0); + assert_eq!(result.number_of_errors, 1); + + let args = &[ + "-c", + "fixtures/eslint_and_typescript_alias_rules/oxlint-typescript.json", + "fixtures/eslint_and_typescript_alias_rules/test.js", + ]; + let result = test(args); + assert_eq!(result.number_of_files, 1); + assert_eq!(result.number_of_warnings, 0); + assert_eq!(result.number_of_errors, 1); + } } diff --git a/crates/oxc_linter/src/config/rules.rs b/crates/oxc_linter/src/config/rules.rs index cdd1c68c28556..a4084f93d81c6 100644 --- a/crates/oxc_linter/src/config/rules.rs +++ b/crates/oxc_linter/src/config/rules.rs @@ -11,7 +11,7 @@ use serde::{ use crate::{ rules::{RuleEnum, RULES}, - utils::is_jest_rule_adapted_to_vitest, + utils::{is_eslint_rule_adapted_to_typescript, is_jest_rule_adapted_to_vitest}, AllowWarnDeny, RuleWithSeverity, }; @@ -155,6 +155,10 @@ fn transform_rule_and_plugin_name<'a>( return (rule_name, "jest"); } + if plugin_name == "typescript" && is_eslint_rule_adapted_to_typescript(rule_name) { + return (rule_name, "eslint"); + } + (rule_name, plugin_name) } diff --git a/crates/oxc_linter/src/utils/mod.rs b/crates/oxc_linter/src/utils/mod.rs index 62fc5d8538578..c3627fb7d6cd8 100644 --- a/crates/oxc_linter/src/utils/mod.rs +++ b/crates/oxc_linter/src/utils/mod.rs @@ -34,6 +34,49 @@ const VITEST_COMPATIBLE_JEST_RULES: phf::Set<&'static str> = phf::phf_set! { "valid-expect", }; +// List of Eslint rules that have Typescript equivalents. +const TYPESCRIPT_COMPATIBLE_ESLINT_RULES: phf::Set<&'static str> = phf::phf_set! { + "class-methods-use-this", + "default-param-last", + "init-declarations", + "max-params", + "no-array-constructor", + "no-dupe-class-members", + "no-empty-function", + "no-invalid-this", + "no-loop-func", + "no-loss-of-precision", + "no-magic-numbers", + "no-redeclare", + "no-restricted-imports", + "no-shadow", + "no-unused-expressions", + "no-unused-vars", + "no-use-before-define", + "no-useless-constructor", + + // these rules are equivalents, but not supported + // "block-spacing", + // "brace-style", + // "comma-dangle", + // "comma-spacing", + // "func-call-spacing", + // "indent", + // "key-spacing", + // "keyword-spacing", + // "lines-around-comment", + // "lines-between-class-members", + // "no-extra-parens", + // "no-extra-semi", + // "object-curly-spacing", + // "padding-line-between-statements", + // "quotes", + // "semi", + // "space-before-blocks", + // "space-before-function-paren", + // "space-infix-ops", +}; + /// Check if the Jest rule is adapted to Vitest. /// Many Vitest rule are essentially ports of Jest plugin rules with minor modifications. /// For these rules, we use the corresponding jest rules with some adjustments for compatibility. @@ -41,6 +84,13 @@ pub fn is_jest_rule_adapted_to_vitest(rule_name: &str) -> bool { VITEST_COMPATIBLE_JEST_RULES.contains(rule_name) } +/// Check if the Eslint rule is adapted to Typescript. +/// Many Typescript rule are essentially ports of Eslint plugin rules with minor modifications. +/// For these rules, we use the corresponding eslint rules with some adjustments for compatibility. +pub fn is_eslint_rule_adapted_to_typescript(rule_name: &str) -> bool { + TYPESCRIPT_COMPATIBLE_ESLINT_RULES.contains(rule_name) +} + pub fn read_to_string(path: &Path) -> io::Result { // `simdutf8` is faster than `std::str::from_utf8` which `fs::read_to_string` uses internally let bytes = std::fs::read(path)?;