refactor(linter): improve the ergonomics around ESlintConfig (#3037)

This commit is contained in:
Boshen 2024-04-20 20:02:22 +08:00 committed by GitHub
parent d44301c871
commit 53c0ff5135
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 168 additions and 186 deletions

View file

@ -19,17 +19,18 @@ pub use self::{env::ESLintEnv, rules::ESLintRules, settings::ESLintSettings};
/// ESLint Config
/// <https://eslint.org/docs/latest/use/configure/configuration-files-new#configuration-objects>
#[derive(Debug, Deserialize)]
#[derive(Debug, Default, Deserialize)]
#[serde(default)]
pub struct ESLintConfig {
#[serde(default)]
rules: ESLintRules,
#[serde(default)]
settings: ESLintSettings,
#[serde(default)]
env: ESLintEnv,
pub(crate) rules: ESLintRules,
pub(crate) settings: ESLintSettings,
pub(crate) env: ESLintEnv,
}
impl ESLintConfig {
/// # Errors
///
/// * Parse Failure
pub fn from_file(path: &Path) -> Result<Self, Report> {
let mut string = std::fs::read_to_string(path).map_err(|e| {
FailedToParseConfigError(vec![Error::new(FailedToOpenFileError(path.to_path_buf(), e))])
@ -66,10 +67,6 @@ impl ESLintConfig {
Ok(config)
}
pub fn properties(self) -> (ESLintSettings, ESLintEnv) {
(self.settings, self.env)
}
#[allow(clippy::option_if_let_else)]
pub fn override_rules(
&self,

View file

@ -9,7 +9,7 @@ use crate::{
disable_directives::{DisableDirectives, DisableDirectivesBuilder},
fixer::{Fix, Message},
javascript_globals::GLOBALS,
ESLintEnv, ESLintSettings,
ESLintConfig, ESLintEnv, ESLintSettings,
};
pub struct LintContext<'a> {
@ -26,9 +26,7 @@ pub struct LintContext<'a> {
file_path: Box<Path>,
settings: Arc<ESLintSettings>,
env: Arc<ESLintEnv>,
pub eslint_config: Arc<ESLintConfig>,
}
impl<'a> LintContext<'a> {
@ -42,8 +40,7 @@ impl<'a> LintContext<'a> {
fix: false,
current_rule_name: "",
file_path,
settings: Arc::new(ESLintSettings::default()),
env: Arc::new(ESLintEnv::default()),
eslint_config: Arc::new(ESLintConfig::default()),
}
}
@ -54,14 +51,8 @@ impl<'a> LintContext<'a> {
}
#[must_use]
pub fn with_settings(mut self, settings: &Arc<ESLintSettings>) -> Self {
self.settings = Arc::clone(settings);
self
}
#[must_use]
pub fn with_env(mut self, env: &Arc<ESLintEnv>) -> Self {
self.env = Arc::clone(env);
pub fn with_eslint_config(mut self, eslint_config: &Arc<ESLintConfig>) -> Self {
self.eslint_config = Arc::clone(eslint_config);
self
}
@ -73,10 +64,6 @@ impl<'a> LintContext<'a> {
&self.disable_directives
}
pub fn settings(&self) -> &ESLintSettings {
&self.settings
}
pub fn source_text(&self) -> &'a str {
self.semantic().source_text()
}
@ -89,12 +76,16 @@ impl<'a> LintContext<'a> {
&self.file_path
}
pub fn envs(&self) -> &ESLintEnv {
&self.env
pub fn env(&self) -> &ESLintEnv {
&self.eslint_config.env
}
pub fn settings(&self) -> &ESLintSettings {
&self.eslint_config.settings
}
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"]);
if env.get(var).is_some() {
return true;

View file

@ -23,6 +23,12 @@ use std::{io::Write, rc::Rc, sync::Arc};
use oxc_diagnostics::Report;
pub use crate::{
config::ESLintConfig,
context::LintContext,
options::{AllowWarnDeny, LintOptions},
service::{LintService, LintServiceOptions},
};
use crate::{
config::{ESLintEnv, ESLintSettings},
fixer::Fix,
@ -30,11 +36,6 @@ use crate::{
rule::RuleCategory,
rules::{RuleEnum, RULES},
};
pub use crate::{
context::LintContext,
options::{AllowWarnDeny, LintOptions},
service::{LintService, LintServiceOptions},
};
use oxc_semantic::AstNode;
#[cfg(target_pointer_width = "64")]
@ -52,8 +53,7 @@ fn size_asserts() {
pub struct Linter {
rules: Vec<(/* rule name */ &'static str, RuleEnum)>,
options: LintOptions,
settings: Arc<ESLintSettings>,
env: Arc<ESLintEnv>,
eslint_config: Arc<ESLintConfig>,
}
impl Default for Linter {
@ -67,9 +67,9 @@ impl Linter {
///
/// Returns `Err` if there are any errors parsing the configuration file.
pub fn from_options(options: LintOptions) -> Result<Self, Report> {
let (rules, settings, env) = options.derive_rules_and_settings_and_env()?;
let (rules, eslint_config) = options.derive_rules_and_config()?;
let rules = rules.into_iter().map(|rule| (rule.name(), rule)).collect();
Ok(Self { rules, options, settings: Arc::new(settings), env: Arc::new(env) })
Ok(Self { rules, options, eslint_config: Arc::new(eslint_config) })
}
#[must_use]
@ -79,14 +79,14 @@ impl Linter {
}
#[must_use]
pub fn with_settings(mut self, settings: ESLintSettings) -> Self {
self.settings = Arc::new(settings);
pub fn with_eslint_config(mut self, eslint_config: ESLintConfig) -> Self {
self.eslint_config = Arc::new(eslint_config);
self
}
#[must_use]
pub fn with_envs(mut self, env: ESLintEnv) -> Self {
self.env = Arc::new(env);
pub fn with_fix(mut self, yes: bool) -> Self {
self.options.fix = yes;
self
}
@ -98,16 +98,9 @@ impl Linter {
self.rules.len()
}
#[must_use]
pub fn with_fix(mut self, yes: bool) -> Self {
self.options.fix = yes;
self
}
pub fn run<'a>(&self, ctx: LintContext<'a>) -> Vec<Message<'a>> {
let semantic = Rc::clone(ctx.semantic());
let mut ctx =
ctx.with_fix(self.options.fix).with_settings(&self.settings).with_env(&self.env);
let mut ctx = ctx.with_fix(self.options.fix).with_eslint_config(&self.eslint_config);
for (rule_name, rule) in &self.rules {
ctx.with_rule_name(rule_name);

View file

@ -1,5 +1,10 @@
use std::path::PathBuf;
use rustc_hash::FxHashSet;
use serde_json::{Number, Value};
use oxc_diagnostics::Error;
use crate::{
config::{
errors::{
@ -9,11 +14,8 @@ use crate::{
ESLintConfig,
},
rules::RULES,
ESLintEnv, ESLintSettings, RuleCategory, RuleEnum,
RuleCategory, RuleEnum,
};
use oxc_diagnostics::Error;
use rustc_hash::FxHashSet;
use serde_json::{Number, Value};
#[derive(Debug)]
pub struct LintOptions {
@ -29,7 +31,6 @@ pub struct LintOptions {
pub jsx_a11y_plugin: bool,
pub nextjs_plugin: bool,
pub react_perf_plugin: bool,
pub env: ESLintEnv,
}
impl Default for LintOptions {
@ -45,7 +46,6 @@ impl Default for LintOptions {
jsx_a11y_plugin: false,
nextjs_plugin: false,
react_perf_plugin: false,
env: ESLintEnv::default(),
}
}
}
@ -112,12 +112,6 @@ impl LintOptions {
self.react_perf_plugin = yes;
self
}
#[must_use]
pub fn with_env(mut self, env: Vec<String>) -> Self {
self.env = ESLintEnv::from_vec(env);
self
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
@ -186,9 +180,7 @@ impl LintOptions {
/// # Errors
///
/// * Returns `Err` if there are any errors parsing the configuration file.
pub fn derive_rules_and_settings_and_env(
&self,
) -> Result<(Vec<RuleEnum>, ESLintSettings, ESLintEnv), Error> {
pub fn derive_rules_and_config(&self) -> Result<(Vec<RuleEnum>, ESLintConfig), Error> {
let config =
self.config_path.as_ref().map(|path| ESLintConfig::from_file(path)).transpose()?;
@ -238,12 +230,10 @@ impl LintOptions {
let mut rules = rules.into_iter().collect::<Vec<_>>();
let (settings, env) = config.map(ESLintConfig::properties).unwrap_or_default();
// for stable diagnostics output ordering
rules.sort_unstable_by_key(RuleEnum::name);
Ok((rules, settings, env))
Ok((rules, config.unwrap_or_default()))
}
// get final filtered rules by reading `self.xxx_plugin`

View file

@ -111,7 +111,7 @@ fn test() {
*
*/
function quux (foo) {
}
",
None,
@ -123,7 +123,7 @@ fn test() {
* @access public
*/
function quux (foo) {
}
",
None,
@ -135,16 +135,16 @@ fn test() {
* @accessLevel package
*/
function quux (foo) {
}
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"tagNamePreference": {
"access": "accessLevel",
},
},
} },
})),
),
(
@ -165,7 +165,7 @@ fn test() {
* @public
*/
function quux (foo) {
}
",
None,
@ -177,14 +177,14 @@ fn test() {
* @private
*/
function quux (foo) {
}
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"ignorePrivate": true,
},
} },
})),
),
(
@ -205,7 +205,7 @@ fn test() {
* @access foo
*/
function quux (foo) {
}
",
None,
@ -217,14 +217,14 @@ fn test() {
* @access foo
*/
function quux (foo) {
}
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"ignorePrivate": true,
},
} },
})),
),
(
@ -238,11 +238,11 @@ fn test() {
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"tagNamePreference": {
"access": "accessLevel",
},
},
} },
})),
),
(
@ -251,16 +251,16 @@ fn test() {
* @access
*/
function quux (foo) {
}
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"tagNamePreference": {
"access": false,
},
},
} },
})),
),
(
@ -282,7 +282,7 @@ fn test() {
* @public
*/
function quux (foo) {
}
",
None,
@ -295,7 +295,7 @@ fn test() {
* @access private
*/
function quux (foo) {
}
",
None,
@ -308,14 +308,14 @@ fn test() {
* @access private
*/
function quux (foo) {
}
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"ignorePrivate": true,
},
} },
})),
),
(
@ -325,7 +325,7 @@ fn test() {
* @private
*/
function quux (foo) {
}
",
None,
@ -338,14 +338,14 @@ fn test() {
* @private
*/
function quux (foo) {
}
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"ignorePrivate": true,
},
} },
})),
),
(
@ -355,7 +355,7 @@ fn test() {
* @public
*/
function quux (foo) {
}
",
None,

View file

@ -318,7 +318,7 @@ fn test() {
* @property cfg.foo
*/
function quux ({foo, bar}) {
}
",
None,
@ -335,7 +335,7 @@ fn test() {
* @property cfg.foo
*/
quux ({foo, bar}) {
}
}
",
@ -352,7 +352,7 @@ fn test() {
* @property baz
*/
function quux ({foo, bar}, baz) {
}
",
None,
@ -368,7 +368,7 @@ fn test() {
* @property baz
*/
function quux ({foo, bar}, baz) {
}
"#,
None,
@ -409,11 +409,11 @@ fn test() {
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"tagNamePreference": {
"property": "prop",
},
},
} },
})),
),
];

View file

@ -135,11 +135,11 @@ fn test() {
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"tagNamePreference": {
"property": "prop",
},
},
} },
})),
),
(
@ -201,7 +201,7 @@ fn test() {
* @property {anotherType} yetAnotherProp This with a type and desc.
*/
function quux () {
}
",
None,
@ -250,11 +250,11 @@ fn test() {
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"tagNamePreference": {
"property": "prop",
},
},
} },
})),
),
(

View file

@ -130,11 +130,11 @@ fn test() {
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"tagNamePreference": {
"property": "prop",
},
},
} },
})),
),
];
@ -179,11 +179,11 @@ fn test() {
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"tagNamePreference": {
"property": "prop",
},
},
} },
})),
),
];

View file

@ -140,11 +140,11 @@ fn test() {
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"tagNamePreference": {
"property": "prop",
},
},
} },
})),
),
];

View file

@ -116,11 +116,11 @@ fn test() {
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"tagNamePreference": {
"property": "prop",
},
},
} },
})),
),
];
@ -165,11 +165,11 @@ fn test() {
",
None,
Some(serde_json::json!({
"jsdoc": {
"settings": { "jsdoc": {
"tagNamePreference": {
"property": "prop",
},
},
} },
})),
),
];

View file

@ -465,7 +465,9 @@ fn test() {
(
r#"<SomeComponent as="img" aria-label="" />"#,
None,
Some(serde_json::json!({ "jsx-a11y": { "polymorphicPropName": "as" } })),
Some(
serde_json::json!({ "settings": { "jsx-a11y": { "polymorphicPropName": "as" } } }),
),
),
(r"<object />", None, None),
(r"<object><div aria-hidden /></object>", None, None),

View file

@ -110,7 +110,9 @@ fn test() {
(
r"<Link>foo</Link>",
None,
Some(serde_json::json!({ "jsx-a11y": { "components": { "Link": "a" } } })),
Some(
serde_json::json!({ "settings": { "jsx-a11y": { "components": { "Link": "a" } } } }),
),
),
(r"<a title={title} />", None, None),
(r"<a aria-label={ariaLabel} />", None, None),
@ -124,7 +126,9 @@ fn test() {
(
r"<Link />",
None,
Some(serde_json::json!({ "jsx-a11y": { "components": { "Link": "a" } } })),
Some(
serde_json::json!({ "settings": { "jsx-a11y": { "components": { "Link": "a" } } } }),
),
),
];

View file

@ -295,7 +295,7 @@ fn test() {
r"<Link href='#foo' />",
Some(serde_json::json!({ "validHrefs": ["#foo"] })),
Some(
serde_json::json!({ "jsx-a11y": { "components": { "Anchor": "a", "Link": "a" } } }),
serde_json::json!({ "settings": { "jsx-a11y": { "components": { "Anchor": "a", "Link": "a" } } } }),
),
),
// (r#"<a {...props} />"#, Some(serde_json::json!(specialLink))),
@ -578,7 +578,7 @@ fn test() {
r"<Link href='#' onClick={() => void 0} />",
None,
Some(
serde_json::json!({ "jsx-a11y": { "components": { "Anchor": "a", "Link": "a" } } }),
serde_json::json!({ "settings": { "jsx-a11y": { "components": { "Anchor": "a", "Link": "a" } } } }),
),
),
// (r#"<a hrefLeft={undefined} />"#, Some(serde_json::json!(specialLink))),

View file

@ -108,11 +108,11 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"CustomComponent": "div",
}
}
} }
})
}

View file

@ -182,12 +182,12 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"polymorphicPropName": "asChild",
"components": {
"Div": "div",
}
}
} }
})
}

View file

@ -215,11 +215,11 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"Input": "input",
}
}
} }
})
}

View file

@ -137,11 +137,11 @@ fn test() {
r"<Footer onClick={doFoo} />",
None,
Some(serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"Footer": "footer",
}
}
} }
})),
None,
),

View file

@ -132,13 +132,13 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"CustomInput": "input",
"Title": "h1",
"Heading": "h2",
},
},
} }
})
}

View file

@ -102,11 +102,11 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"HTMLTop": "html",
}
}
} }
})
}

View file

@ -134,11 +134,11 @@ fn test() {
r"<FooComponent title='Unique title' />",
None,
Some(serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"FooComponent": "iframe",
},
},
}, }
})),
),
];
@ -159,11 +159,11 @@ fn test() {
r"<FooComponent />",
None,
Some(serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"FooComponent": "iframe",
},
},
}, }
})),
),
];

View file

@ -195,11 +195,11 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"Image": "img",
}
}
} }
})
}

View file

@ -112,12 +112,12 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"polymorphicPropName": "as",
"components": {
"Foo": "html",
}
}
} }
})
}

View file

@ -188,14 +188,14 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"polymorphicPropName": "as",
"components": {
"Audio": "audio",
"Video": "video",
"Track": "track",
},
}
} }
})
}

View file

@ -123,11 +123,11 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"Button": "button",
}
}
} }
})
}

View file

@ -78,12 +78,12 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"Blink": "blink",
"Marquee": "marquee"
}
}
} }
})
}

View file

@ -96,11 +96,11 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"Button": "button",
}
}
} }
})
}

View file

@ -89,11 +89,11 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"MyComponent": "div",
}
}
} }
})
}

View file

@ -1273,11 +1273,11 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"Link": "a"
}
},
} }
})
}

View file

@ -85,12 +85,12 @@ fn test() {
fn settings() -> serde_json::Value {
serde_json::json!({
"jsx-a11y": {
"settings": { "jsx-a11y": {
"components": {
"Foo": "div",
"TableHeader": "th"
}
}
} }
})
}

View file

@ -489,20 +489,20 @@ fn test() {
(
r#"<Link target="_blank" href={ dynamicLink }></Link>"#,
Some(serde_json::json!([{ "enforceDynamicLinks": "never" }])),
Some(serde_json::json!({ "react": { "linkComponents": ["Link"] } })),
Some(serde_json::json!({ "settings": { "react": { "linkComponents": ["Link"] } } })),
),
(
r#"<Link target="_blank" to={ dynamicLink }></Link>"#,
Some(serde_json::json!([{ "enforceDynamicLinks": "never" }])),
Some(
serde_json::json!({"react": { "linkComponents": [{ "name": "Link", "linkAttribute": "to" }] }}),
serde_json::json!({"settings": { "react": { "linkComponents": [{ "name": "Link", "linkAttribute": "to" }] } }}),
),
),
(
r#"<Link target="_blank" to={ dynamicLink }></Link>"#,
Some(serde_json::json!([{ "enforceDynamicLinks": "never" }])),
Some(
serde_json::json!({ "react": { "linkComponents": [{ "name": "Link", "linkAttribute": ["to"] }] }}),
serde_json::json!({ "settings": { "react": { "linkComponents": [{ "name": "Link", "linkAttribute": ["to"] }] } }}),
),
),
(
@ -683,13 +683,13 @@ fn test() {
(
r#"<Link target="_blank" href={ dynamicLink }></Link>"#,
Some(serde_json::json!([{ "enforceDynamicLinks": "always"}])),
Some(serde_json::json!({ "react": { "linkComponents": ["Link"] } })),
Some(serde_json::json!({ "settings": { "react": { "linkComponents": ["Link"] } } })),
),
(
r#"<Link target="_blank" to={ dynamicLink }></Link>"#,
Some(serde_json::json!([{ "enforceDynamicLinks": "always" }])),
Some(
serde_json::json!({ "react": { "linkComponents": [{ "name": "Link", "linkAttribute": "to" }] } }),
serde_json::json!({ "settings": { "react": { "linkComponents": [{ "name": "Link", "linkAttribute": "to" }] } } }),
),
),
(

View file

@ -10,7 +10,7 @@ use serde::Deserialize;
use serde_json::Value;
use crate::{
rules::RULES, ESLintSettings, Fixer, LintOptions, LintService, LintServiceOptions, Linter,
rules::RULES, ESLintConfig, Fixer, LintOptions, LintService, LintServiceOptions, Linter,
RuleEnum,
};
@ -24,8 +24,8 @@ enum TestResult {
#[derive(Debug, Clone, Default)]
pub struct TestCase {
source: String,
config: Option<Value>,
settings: Option<Value>,
rule_config: Option<Value>,
eslint_config: Option<Value>,
path: Option<PathBuf>,
}
@ -42,22 +42,27 @@ impl From<String> for TestCase {
}
impl From<(&str, Option<Value>)> for TestCase {
fn from((source, config): (&str, Option<Value>)) -> Self {
Self { source: source.to_string(), config, ..Self::default() }
fn from((source, rule_config): (&str, Option<Value>)) -> Self {
Self { source: source.to_string(), rule_config, ..Self::default() }
}
}
impl From<(&str, Option<Value>, Option<Value>)> for TestCase {
fn from((source, config, settings): (&str, Option<Value>, Option<Value>)) -> Self {
Self { source: source.to_string(), config, settings, ..Self::default() }
fn from((source, rule_config, eslint_config): (&str, Option<Value>, Option<Value>)) -> Self {
Self { source: source.to_string(), rule_config, eslint_config, ..Self::default() }
}
}
impl From<(&str, Option<Value>, Option<Value>, Option<PathBuf>)> for TestCase {
fn from(
(source, config, settings, path): (&str, Option<Value>, Option<Value>, Option<PathBuf>),
(source, rule_config, eslint_config, path): (
&str,
Option<Value>,
Option<Value>,
Option<PathBuf>,
),
) -> Self {
Self { source: source.to_string(), config, settings, path }
Self { source: source.to_string(), rule_config, eslint_config, path }
}
}
@ -159,16 +164,16 @@ impl Tester {
}
fn test_pass(&mut self) {
for TestCase { source, config, settings, path } in self.expect_pass.clone() {
let result = self.run(&source, config, false, &settings, &path);
for TestCase { source, rule_config, eslint_config, path } in self.expect_pass.clone() {
let result = self.run(&source, rule_config, &eslint_config, path, false);
let passed = result == TestResult::Passed;
assert!(passed, "expect test to pass: {source} {}", self.snapshot);
}
}
fn test_fail(&mut self) {
for TestCase { source, config, settings, path } in self.expect_fail.clone() {
let result = self.run(&source, config, false, &settings, &path);
for TestCase { source, rule_config, eslint_config, path } in self.expect_fail.clone() {
let result = self.run(&source, rule_config, &eslint_config, path, false);
let failed = result == TestResult::Failed;
assert!(failed, "expect test to fail: {source}");
}
@ -176,7 +181,7 @@ impl Tester {
fn test_fix(&mut self) {
for (test, expected, config) in self.expect_fix.clone() {
let result = self.run(&test, config, true, &None, &None);
let result = self.run(&test, config, &None, None, true);
if let TestResult::Fixed(fixed_str) = result {
assert_eq!(expected, fixed_str);
} else {
@ -188,16 +193,13 @@ impl Tester {
fn run(
&mut self,
source_text: &str,
config: Option<Value>,
rule_config: Option<Value>,
eslint_config: &Option<Value>,
path: Option<PathBuf>,
is_fix: bool,
settings: &Option<Value>,
path: &Option<PathBuf>,
) -> TestResult {
let allocator = Allocator::default();
let rule = self.find_rule().read_json(config);
let lint_settings: ESLintSettings = settings
.as_ref()
.map_or_else(ESLintSettings::default, |v| ESLintSettings::deserialize(v).unwrap());
let rule = self.find_rule().read_json(rule_config);
let options = LintOptions::default()
.with_fix(is_fix)
.with_import_plugin(self.import_plugin)
@ -205,10 +207,13 @@ impl Tester {
.with_jsx_a11y_plugin(self.jsx_a11y_plugin)
.with_nextjs_plugin(self.nextjs_plugin)
.with_react_perf_plugin(self.react_perf_plugin);
let eslint_config = eslint_config
.as_ref()
.map_or_else(ESLintConfig::default, |v| ESLintConfig::deserialize(v).unwrap());
let linter = Linter::from_options(options)
.unwrap()
.with_rules(vec![rule])
.with_settings(lint_settings);
.with_eslint_config(eslint_config);
let path_to_lint = if self.import_plugin {
assert!(path.is_none(), "import plugin does not support path");
self.current_working_directory.join(&self.rule_path)