feat(linter): support vitest/no-disabled-tests (#3717)

This commit is contained in:
mysteryven 2024-06-24 15:16:31 +00:00
parent ef82c78a72
commit 328445b4ca
15 changed files with 323 additions and 21 deletions

View file

@ -0,0 +1,5 @@
{
"rules": {
"vitest/no-disabled-tests": "error"
}
}

View file

@ -0,0 +1,3 @@
test.skip('foo', () => {
// ...
})

View file

@ -208,6 +208,10 @@ pub struct EnablePlugins {
#[bpaf(switch, hide_usage)] #[bpaf(switch, hide_usage)]
pub jest_plugin: bool, pub jest_plugin: bool,
/// Enable the Vitest plugin and detect test problems
#[bpaf(switch, hide_usage)]
pub vitest_plugin: bool,
/// Enable the JSX-a11y plugin and detect accessibility problems /// Enable the JSX-a11y plugin and detect accessibility problems
#[bpaf(switch, hide_usage)] #[bpaf(switch, hide_usage)]
pub jsx_a11y_plugin: bool, pub jsx_a11y_plugin: bool,

View file

@ -101,6 +101,7 @@ impl Runner for LintRunner {
.with_import_plugin(enable_plugins.import_plugin) .with_import_plugin(enable_plugins.import_plugin)
.with_jsdoc_plugin(enable_plugins.jsdoc_plugin) .with_jsdoc_plugin(enable_plugins.jsdoc_plugin)
.with_jest_plugin(enable_plugins.jest_plugin) .with_jest_plugin(enable_plugins.jest_plugin)
.with_vitest_plugin(enable_plugins.vitest_plugin)
.with_jsx_a11y_plugin(enable_plugins.jsx_a11y_plugin) .with_jsx_a11y_plugin(enable_plugins.jsx_a11y_plugin)
.with_nextjs_plugin(enable_plugins.nextjs_plugin) .with_nextjs_plugin(enable_plugins.nextjs_plugin)
.with_react_perf_plugin(enable_plugins.react_perf_plugin); .with_react_perf_plugin(enable_plugins.react_perf_plugin);
@ -488,4 +489,26 @@ mod test {
assert!(test_invalid_options(&["--tsconfig", "oxc/tsconfig.json"]) assert!(test_invalid_options(&["--tsconfig", "oxc/tsconfig.json"])
.contains("oxc/tsconfig.json\" does not exist, Please provide a valid tsconfig file.")); .contains("oxc/tsconfig.json\" does not exist, Please provide a valid tsconfig file."));
} }
#[test]
fn test_enable_vitest_plugin() {
let args = &[
"-c",
"fixtures/eslintrc_vitest_replace/eslintrc.json",
"fixtures/eslintrc_vitest_replace/foo.js",
];
let result = test(args);
assert_eq!(result.number_of_files, 1);
assert_eq!(result.number_of_errors, 0);
let args = &[
"--vitest-plugin",
"-c",
"fixtures/eslintrc_vitest_replace/eslintrc.json",
"fixtures/eslintrc_vitest_replace/foo.js",
];
let result = test(args);
assert_eq!(result.number_of_files, 1);
assert_eq!(result.number_of_errors, 1);
}
} }

View file

@ -0,0 +1,5 @@
{
"rules": {
"vitest/no-disabled-tests": "error"
}
}

View file

@ -16,7 +16,9 @@ pub use self::{
rules::OxlintRules, rules::OxlintRules,
settings::{jsdoc::JSDocPluginSettings, OxlintSettings}, settings::{jsdoc::JSDocPluginSettings, OxlintSettings},
}; };
use crate::{rules::RuleEnum, AllowWarnDeny, RuleWithSeverity}; use crate::{
rules::RuleEnum, utils::is_jest_rule_adapted_to_vitest, AllowWarnDeny, RuleWithSeverity,
};
/// Oxlint Configuration File /// Oxlint Configuration File
/// ///
@ -113,8 +115,10 @@ impl OxlintConfig {
0 => unreachable!(), 0 => unreachable!(),
1 => { 1 => {
let rule_config = &rule_configs[0]; let rule_config = &rule_configs[0];
let rule_name = &rule_config.rule_name; let (rule_name, plugin_name) = transform_rule_and_plugin_name(
let plugin_name = &rule_config.plugin_name; &rule_config.rule_name,
&rule_config.plugin_name,
);
let severity = rule_config.severity; let severity = rule_config.severity;
match severity { match severity {
AllowWarnDeny::Warn | AllowWarnDeny::Deny => { AllowWarnDeny::Warn | AllowWarnDeny::Deny => {
@ -168,12 +172,26 @@ impl OxlintConfig {
} }
} }
fn transform_rule_and_plugin_name<'a>(
rule_name: &'a str,
plugin_name: &'a str,
) -> (&'a str, &'a str) {
if plugin_name == "vitest" && is_jest_rule_adapted_to_vitest(rule_name) {
return (rule_name, "jest");
}
(rule_name, plugin_name)
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::env; use std::env;
use rustc_hash::FxHashSet;
use serde::Deserialize; use serde::Deserialize;
use crate::rules::RULES;
use super::OxlintConfig; use super::OxlintConfig;
#[test] #[test]
@ -221,4 +239,17 @@ mod test {
assert_eq!(env.iter().count(), 1); assert_eq!(env.iter().count(), 1);
assert!(globals.is_enabled("foo")); assert!(globals.is_enabled("foo"));
} }
#[test]
fn test_vitest_rule_replace() {
let fixture_path: std::path::PathBuf =
env::current_dir().unwrap().join("fixtures/eslint_config_vitest_replace.json");
let config = OxlintConfig::from_file(&fixture_path).unwrap();
let mut set = FxHashSet::default();
config.override_rules(&mut set, &RULES);
let rule = set.into_iter().next().unwrap();
assert_eq!(rule.name(), "no-disabled-tests");
assert_eq!(rule.plugin_name(), "jest");
}
} }

View file

@ -7,6 +7,7 @@ use oxc_span::{SourceType, Span};
use oxc_syntax::module_record::ModuleRecord; use oxc_syntax::module_record::ModuleRecord;
use crate::{ use crate::{
config::OxlintRules,
disable_directives::{DisableDirectives, DisableDirectivesBuilder}, disable_directives::{DisableDirectives, DisableDirectivesBuilder},
fixer::{Fix, Message, RuleFixer}, fixer::{Fix, Message, RuleFixer},
javascript_globals::GLOBALS, javascript_globals::GLOBALS,
@ -131,6 +132,10 @@ impl<'a> LintContext<'a> {
&self.eslint_config.env &self.eslint_config.env
} }
pub fn rules(&self) -> &OxlintRules {
&self.eslint_config.rules
}
pub fn env_contains_var(&self, var: &str) -> bool { pub fn env_contains_var(&self, var: &str) -> bool {
for env in self.env().iter() { for env in self.env().iter() {
let env = GLOBALS.get(env).unwrap_or(&GLOBALS["builtin"]); let env = GLOBALS.get(env).unwrap_or(&GLOBALS["builtin"]);

View file

@ -4,7 +4,10 @@ use oxc_diagnostics::{Error, OxcDiagnostic, Severity};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use serde_json::{Number, Value}; use serde_json::{Number, Value};
use crate::{config::OxlintConfig, rules::RULES, RuleCategory, RuleEnum, RuleWithSeverity}; use crate::{
config::OxlintConfig, rules::RULES, utils::is_jest_rule_adapted_to_vitest, RuleCategory,
RuleEnum, RuleWithSeverity,
};
#[derive(Debug)] #[derive(Debug)]
pub struct LintOptions { pub struct LintOptions {
@ -21,6 +24,7 @@ pub struct LintOptions {
pub import_plugin: bool, pub import_plugin: bool,
pub jsdoc_plugin: bool, pub jsdoc_plugin: bool,
pub jest_plugin: bool, pub jest_plugin: bool,
pub vitest_plugin: bool,
pub jsx_a11y_plugin: bool, pub jsx_a11y_plugin: bool,
pub nextjs_plugin: bool, pub nextjs_plugin: bool,
pub react_perf_plugin: bool, pub react_perf_plugin: bool,
@ -39,6 +43,7 @@ impl Default for LintOptions {
import_plugin: false, import_plugin: false,
jsdoc_plugin: false, jsdoc_plugin: false,
jest_plugin: false, jest_plugin: false,
vitest_plugin: false,
jsx_a11y_plugin: false, jsx_a11y_plugin: false,
nextjs_plugin: false, nextjs_plugin: false,
react_perf_plugin: false, react_perf_plugin: false,
@ -109,6 +114,12 @@ impl LintOptions {
self self
} }
#[must_use]
pub fn with_vitest_plugin(mut self, yes: bool) -> Self {
self.vitest_plugin = yes;
self
}
#[must_use] #[must_use]
pub fn with_jsx_a11y_plugin(mut self, yes: bool) -> Self { pub fn with_jsx_a11y_plugin(mut self, yes: bool) -> Self {
self.jsx_a11y_plugin = yes; self.jsx_a11y_plugin = yes;
@ -277,7 +288,15 @@ impl LintOptions {
"typescript" => self.typescript_plugin, "typescript" => self.typescript_plugin,
"import" => self.import_plugin, "import" => self.import_plugin,
"jsdoc" => self.jsdoc_plugin, "jsdoc" => self.jsdoc_plugin,
"jest" => self.jest_plugin, "jest" => {
if self.jest_plugin {
return true;
}
if self.vitest_plugin && is_jest_rule_adapted_to_vitest(rule.name()) {
return true;
}
false
}
"jsx_a11y" => self.jsx_a11y_plugin, "jsx_a11y" => self.jsx_a11y_plugin,
"nextjs" => self.nextjs_plugin, "nextjs" => self.nextjs_plugin,
"react_perf" => self.react_perf_plugin, "react_perf" => self.react_perf_plugin,

View file

@ -7,8 +7,8 @@ use crate::{
context::LintContext, context::LintContext,
rule::Rule, rule::Rule,
utils::{ utils::{
collect_possible_jest_call_node, parse_general_jest_fn_call, JestFnKind, JestGeneralFnKind, collect_possible_jest_call_node, get_test_plugin_name, parse_general_jest_fn_call,
ParsedGeneralJestFnCall, PossibleJestNode, JestFnKind, JestGeneralFnKind, ParsedGeneralJestFnCall, PossibleJestNode,
}, },
}; };
@ -48,14 +48,25 @@ declare_oxc_lint!(
/// pending(); /// pending();
/// }); /// });
/// ``` /// ```
///
/// This rule is compatible with [eslint-plugin-vitest](https://github.com/veritem/eslint-plugin-vitest/blob/main/docs/rules/no-disabled-tests.md),
/// to use it, add the following configuration to your `.eslintrc.json`:
///
/// ```json
/// {
/// "rules": {
/// "vitest/no-disabled-tests": "error"
/// }
/// }
/// ```
NoDisabledTests, NoDisabledTests,
correctness correctness
); );
fn no_disabled_tests_diagnostic(x0: &str, x1: &str, span2: Span) -> OxcDiagnostic { fn no_disabled_tests_diagnostic(x0: &str, x1: &str, x2: &str, span3: Span) -> OxcDiagnostic {
OxcDiagnostic::warn(format!("eslint-plugin-jest(no-disabled-tests): {x0:?}")) OxcDiagnostic::warn(format!("{x0}(no-disabled-tests): {x1:?}"))
.with_help(format!("{x1:?}")) .with_help(format!("{x2:?}"))
.with_labels([span2.into()]) .with_labels([span3.into()])
} }
enum Message { enum Message {
@ -82,13 +93,19 @@ impl Message {
impl Rule for NoDisabledTests { impl Rule for NoDisabledTests {
fn run_once(&self, ctx: &LintContext) { fn run_once(&self, ctx: &LintContext) {
let plugin_name = get_test_plugin_name(ctx);
for possible_jest_node in &collect_possible_jest_call_node(ctx) { for possible_jest_node in &collect_possible_jest_call_node(ctx) {
run(possible_jest_node, ctx); run(possible_jest_node, plugin_name, ctx);
} }
} }
} }
fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>) { fn run<'a>(
possible_jest_node: &PossibleJestNode<'a, '_>,
plugin_name: &str,
ctx: &LintContext<'a>,
) {
let node = possible_jest_node.node; let node = possible_jest_node.node;
if let AstKind::CallExpression(call_expr) = node.kind() { if let AstKind::CallExpression(call_expr) = node.kind() {
if let Some(jest_fn_call) = parse_general_jest_fn_call(call_expr, possible_jest_node, ctx) { if let Some(jest_fn_call) = parse_general_jest_fn_call(call_expr, possible_jest_node, ctx) {
@ -103,7 +120,12 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>)
&& members.iter().all(|member| member.is_name_unequal("todo")) && members.iter().all(|member| member.is_name_unequal("todo"))
{ {
let (error, help) = Message::MissingFunction.details(); let (error, help) = Message::MissingFunction.details();
ctx.diagnostic(no_disabled_tests_diagnostic(error, help, call_expr.span)); ctx.diagnostic(no_disabled_tests_diagnostic(
plugin_name,
error,
help,
call_expr.span,
));
return; return;
} }
@ -115,7 +137,12 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>)
} else { } else {
Message::DisabledTestWithX.details() Message::DisabledTestWithX.details()
}; };
ctx.diagnostic(no_disabled_tests_diagnostic(error, help, call_expr.callee.span())); ctx.diagnostic(no_disabled_tests_diagnostic(
plugin_name,
error,
help,
call_expr.callee.span(),
));
return; return;
} }
@ -127,7 +154,12 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>)
} else { } else {
Message::DisabledTestWithSkip.details() Message::DisabledTestWithSkip.details()
}; };
ctx.diagnostic(no_disabled_tests_diagnostic(error, help, call_expr.callee.span())); ctx.diagnostic(no_disabled_tests_diagnostic(
plugin_name,
error,
help,
call_expr.callee.span(),
));
} }
} else if let Expression::Identifier(ident) = &call_expr.callee { } else if let Expression::Identifier(ident) = &call_expr.callee {
if ident.name.as_str() == "pending" if ident.name.as_str() == "pending"
@ -135,7 +167,12 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>)
{ {
// `describe('foo', function () { pending() })` // `describe('foo', function () { pending() })`
let (error, help) = Message::Pending.details(); let (error, help) = Message::Pending.details();
ctx.diagnostic(no_disabled_tests_diagnostic(error, help, call_expr.span)); ctx.diagnostic(no_disabled_tests_diagnostic(
plugin_name,
error,
help,
call_expr.span,
));
} }
} }
} }
@ -145,7 +182,7 @@ fn run<'a>(possible_jest_node: &PossibleJestNode<'a, '_>, ctx: &LintContext<'a>)
fn test() { fn test() {
use crate::tester::Tester; use crate::tester::Tester;
let pass = vec![ let mut pass = vec![
("describe('foo', function () {})", None), ("describe('foo', function () {})", None),
("it('foo', function () {})", None), ("it('foo', function () {})", None),
("describe.only('foo', function () {})", None), ("describe.only('foo', function () {})", None),
@ -184,7 +221,7 @@ fn test() {
("import { test } from './test-utils'; test('something');", None), ("import { test } from './test-utils'; test('something');", None),
]; ];
let fail = vec![ let mut fail = vec![
("describe.skip('foo', function () {})", None), ("describe.skip('foo', function () {})", None),
("describe.skip.each([1, 2, 3])('%s', (a, b) => {});", None), ("describe.skip.each([1, 2, 3])('%s', (a, b) => {});", None),
("xdescribe.each([1, 2, 3])('%s', (a, b) => {});", None), ("xdescribe.each([1, 2, 3])('%s', (a, b) => {});", None),
@ -213,5 +250,59 @@ fn test() {
("import { test } from '@jest/globals';test('something');", None), ("import { test } from '@jest/globals';test('something');", None),
]; ];
Tester::new(NoDisabledTests::NAME, pass, fail).with_jest_plugin(true).test_and_snapshot(); let pass_vitest = vec![
r#"describe("foo", function () {})"#,
r#"it("foo", function () {})"#,
r#"describe.only("foo", function () {})"#,
r#"it.only("foo", function () {})"#,
r#"it.each("foo", () => {})"#,
r#"it.concurrent("foo", function () {})"#,
r#"test("foo", function () {})"#,
r#"test.only("foo", function () {})"#,
r#"test.concurrent("foo", function () {})"#,
r#"describe[`${"skip"}`]("foo", function () {})"#,
r#"it.todo("fill this later")"#,
"var appliedSkip = describe.skip; appliedSkip.apply(describe)",
"var calledSkip = it.skip; calledSkip.call(it)",
"({ f: function () {} }).f()",
"(a || b).f()",
"itHappensToStartWithIt()",
"testSomething()",
"xitSomethingElse()",
"xitiViewMap()",
r#"
import { pending } from "actions"
test("foo", () => {
expect(pending()).toEqual({})
})
"#,
"
import { test } from './test-utils';
test('something');
",
];
let fail_vitest = vec![
r#"describe.skip("foo", function () {})"#,
r#"xtest("foo", function () {})"#,
r#"xit.each``("foo", function () {})"#,
r#"xtest.each``("foo", function () {})"#,
r#"xit.each([])("foo", function () {})"#,
r#"it("has title but no callback")"#,
r#"test("has title but no callback")"#,
r#"it("contains a call to pending", function () { pending() })"#,
"pending();",
r#"
import { describe } from 'vitest';
describe.skip("foo", function () {})
"#,
];
pass.extend(pass_vitest.into_iter().map(|x| (x, None)));
fail.extend(fail_vitest.into_iter().map(|x| (x, None)));
Tester::new(NoDisabledTests::NAME, pass, fail)
.with_jest_plugin(true)
.with_vitest_plugin(true)
.test_and_snapshot();
} }

View file

@ -182,3 +182,75 @@ source: crates/oxc_linter/src/tester.rs
· ───────────────── · ─────────────────
╰──── ╰────
help: "Add function argument" help: "Add function argument"
⚠ eslint-plugin-jest(no-disabled-tests): "Disabled test suite"
╭─[no_disabled_tests.tsx:1:1]
1 │ describe.skip("foo", function () {})
· ─────────────
╰────
help: "Remove the appending `.skip`"
⚠ eslint-plugin-jest(no-disabled-tests): "Disabled test"
╭─[no_disabled_tests.tsx:1:1]
1 │ xtest("foo", function () {})
· ─────
╰────
help: "Remove x prefix"
⚠ eslint-plugin-jest(no-disabled-tests): "Disabled test"
╭─[no_disabled_tests.tsx:1:1]
1 │ xit.each``("foo", function () {})
· ──────────
╰────
help: "Remove x prefix"
⚠ eslint-plugin-jest(no-disabled-tests): "Disabled test"
╭─[no_disabled_tests.tsx:1:1]
1 │ xtest.each``("foo", function () {})
· ────────────
╰────
help: "Remove x prefix"
⚠ eslint-plugin-jest(no-disabled-tests): "Disabled test"
╭─[no_disabled_tests.tsx:1:1]
1 │ xit.each([])("foo", function () {})
· ────────────
╰────
help: "Remove x prefix"
⚠ eslint-plugin-jest(no-disabled-tests): "Test is missing function argument"
╭─[no_disabled_tests.tsx:1:1]
1 │ it("has title but no callback")
· ───────────────────────────────
╰────
help: "Add function argument"
⚠ eslint-plugin-jest(no-disabled-tests): "Test is missing function argument"
╭─[no_disabled_tests.tsx:1:1]
1 │ test("has title but no callback")
· ─────────────────────────────────
╰────
help: "Add function argument"
⚠ eslint-plugin-jest(no-disabled-tests): "Call to pending()"
╭─[no_disabled_tests.tsx:1:48]
1 │ it("contains a call to pending", function () { pending() })
· ─────────
╰────
help: "Remove pending() call"
⚠ eslint-plugin-jest(no-disabled-tests): "Call to pending()"
╭─[no_disabled_tests.tsx:1:1]
1 │ pending();
· ─────────
╰────
help: "Remove pending() call"
⚠ eslint-plugin-vitest(no-disabled-tests): "Disabled test suite"
╭─[no_disabled_tests.tsx:3:13]
2 │ import { describe } from 'vitest';
3 │ describe.skip("foo", function () {})
· ─────────────
4 │
╰────
help: "Remove the appending `.skip`"

View file

@ -96,6 +96,7 @@ pub struct Tester {
current_working_directory: Box<Path>, current_working_directory: Box<Path>,
import_plugin: bool, import_plugin: bool,
jest_plugin: bool, jest_plugin: bool,
vitest_plugin: bool,
jsx_a11y_plugin: bool, jsx_a11y_plugin: bool,
nextjs_plugin: bool, nextjs_plugin: bool,
react_perf_plugin: bool, react_perf_plugin: bool,
@ -125,6 +126,7 @@ impl Tester {
jsx_a11y_plugin: false, jsx_a11y_plugin: false,
nextjs_plugin: false, nextjs_plugin: false,
react_perf_plugin: false, react_perf_plugin: false,
vitest_plugin: false,
} }
} }
@ -150,6 +152,11 @@ impl Tester {
self self
} }
pub fn with_vitest_plugin(mut self, yes: bool) -> Self {
self.vitest_plugin = yes;
self
}
pub fn with_jsx_a11y_plugin(mut self, yes: bool) -> Self { pub fn with_jsx_a11y_plugin(mut self, yes: bool) -> Self {
self.jsx_a11y_plugin = yes; self.jsx_a11y_plugin = yes;
self self
@ -255,6 +262,7 @@ impl Tester {
.with_fix(is_fix) .with_fix(is_fix)
.with_import_plugin(self.import_plugin) .with_import_plugin(self.import_plugin)
.with_jest_plugin(self.jest_plugin) .with_jest_plugin(self.jest_plugin)
.with_vitest_plugin(self.vitest_plugin)
.with_jsx_a11y_plugin(self.jsx_a11y_plugin) .with_jsx_a11y_plugin(self.jsx_a11y_plugin)
.with_nextjs_plugin(self.nextjs_plugin) .with_nextjs_plugin(self.nextjs_plugin)
.with_react_perf_plugin(self.react_perf_plugin); .with_react_perf_plugin(self.react_perf_plugin);

View file

@ -206,7 +206,7 @@ fn collect_ids_referenced_to_import<'a>(
}; };
let name = ctx.symbols().get_name(symbol_id); let name = ctx.symbols().get_name(symbol_id);
if import_decl.source.value == "@jest/globals" { if matches!(import_decl.source.value.as_str(), "@jest/globals" | "vitest") {
let original = find_original_name(import_decl, name); let original = find_original_name(import_decl, name);
let mut ret = vec![]; let mut ret = vec![];
for reference_id in reference_ids { for reference_id in reference_ids {

View file

@ -6,6 +6,39 @@ mod react_perf;
mod tree_shaking; mod tree_shaking;
mod unicorn; mod unicorn;
use crate::LintContext;
pub use self::{ pub use self::{
jest::*, jsdoc::*, nextjs::*, react::*, react_perf::*, tree_shaking::*, unicorn::*, jest::*, jsdoc::*, nextjs::*, react::*, react_perf::*, tree_shaking::*, unicorn::*,
}; };
/// 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.
pub fn is_jest_rule_adapted_to_vitest(rule_name: &str) -> bool {
matches!(rule_name, "no-disabled-tests")
}
pub fn get_test_plugin_name(ctx: &LintContext) -> &'static str {
if is_using_vitest(ctx) {
"eslint-plugin-vitest"
} else {
"eslint-plugin-jest"
}
}
fn is_using_vitest(ctx: &LintContext) -> bool {
// If import 'vitest' is found, we assume the user is using vitest.
if ctx
.semantic()
.module_record()
.import_entries
.iter()
.any(|entry| entry.module_request.name().as_str() == "vitest")
{
return true;
}
// Or, find the eslint config file
ctx.rules().iter().any(|rule| rule.plugin_name == "vitest")
}

View file

@ -58,6 +58,8 @@ Arguments:
Enable the experimental jsdoc plugin and detect JSDoc problems Enable the experimental jsdoc plugin and detect JSDoc problems
- **` --jest-plugin`** &mdash; - **` --jest-plugin`** &mdash;
Enable the Jest plugin and detect test problems Enable the Jest plugin and detect test problems
- **` --vitest-plugin`** &mdash;
Enable the Vitest plugin and detect test problems
- **` --jsx-a11y-plugin`** &mdash; - **` --jsx-a11y-plugin`** &mdash;
Enable the JSX-a11y plugin and detect accessibility problems Enable the JSX-a11y plugin and detect accessibility problems
- **` --nextjs-plugin`** &mdash; - **` --nextjs-plugin`** &mdash;

View file

@ -35,6 +35,7 @@ Enable Plugins
recommended to use along side with the `--tsconfig` option. recommended to use along side with the `--tsconfig` option.
--jsdoc-plugin Enable the experimental jsdoc plugin and detect JSDoc problems --jsdoc-plugin Enable the experimental jsdoc plugin and detect JSDoc problems
--jest-plugin Enable the Jest plugin and detect test problems --jest-plugin Enable the Jest plugin and detect test problems
--vitest-plugin Enable the Vitest plugin and detect test problems
--jsx-a11y-plugin Enable the JSX-a11y plugin and detect accessibility problems --jsx-a11y-plugin Enable the JSX-a11y plugin and detect accessibility problems
--nextjs-plugin Enable the Next.js plugin and detect Next.js problems --nextjs-plugin Enable the Next.js plugin and detect Next.js problems
--react-perf-plugin Enable the React performance plugin and detect rendering performance --react-perf-plugin Enable the React performance plugin and detect rendering performance