-
-
Notifications
You must be signed in to change notification settings - Fork 474
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(linter/eslint-plugin-react): Implement prefer-es6-class
Rule Detail: [link](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md)
- Loading branch information
Showing
3 changed files
with
223 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
use oxc_ast::AstKind; | ||
use oxc_diagnostics::OxcDiagnostic; | ||
use oxc_macros::declare_oxc_lint; | ||
use oxc_span::Span; | ||
|
||
use crate::{ | ||
context::LintContext, | ||
rule::Rule, | ||
utils::{is_es5_component, is_es6_component}, | ||
AstNode, | ||
}; | ||
|
||
fn unexpected_es6_class_diagnostic(span0: Span) -> OxcDiagnostic { | ||
OxcDiagnostic::warn("eslint-plugin-react(prefer-es6-class): Component should use createClass instead of es6 class") | ||
.with_label(span0) | ||
} | ||
|
||
fn expected_es6_class_diagnostic(span0: Span) -> OxcDiagnostic { | ||
OxcDiagnostic::warn("eslint-plugin-react(prefer-es6-class): Component should use es6 class instead of createClass") | ||
.with_label(span0) | ||
} | ||
|
||
#[derive(Debug, Default, Clone)] | ||
pub struct PreferEs6Class { | ||
prefer_es6_class_option: PreferES6ClassOptionType, | ||
} | ||
|
||
declare_oxc_lint!( | ||
/// ### What it does | ||
/// | ||
/// React offers you two ways to create traditional components: using the ES5 | ||
/// create-react-class module or the new ES6 class system. | ||
/// | ||
/// ### Why is this bad? | ||
/// | ||
/// This rule enforces a consistent React class style. | ||
/// | ||
/// ### Example | ||
/// ```javascript | ||
/// var Hello = createReactClass({ | ||
/// render: function() { | ||
/// return <div>Hello {this.props.name}</div>; | ||
/// } | ||
/// }); | ||
/// ``` | ||
PreferEs6Class, | ||
style, | ||
); | ||
|
||
impl Rule for PreferEs6Class { | ||
fn from_configuration(value: serde_json::Value) -> Self { | ||
let obj = value.get(0); | ||
|
||
Self { | ||
prefer_es6_class_option: obj | ||
.and_then(serde_json::Value::as_str) | ||
.map(PreferES6ClassOptionType::from) | ||
.unwrap_or_default(), | ||
} | ||
} | ||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { | ||
if matches!(self.prefer_es6_class_option, PreferES6ClassOptionType::Always) { | ||
if is_es5_component(node) { | ||
let AstKind::CallExpression(call_expr) = node.kind() else { | ||
return; | ||
}; | ||
ctx.diagnostic(expected_es6_class_diagnostic(call_expr.span)); | ||
} | ||
} else if is_es6_component(node) { | ||
let AstKind::Class(class_expr) = node.kind() else { | ||
return; | ||
}; | ||
ctx.diagnostic(unexpected_es6_class_diagnostic(class_expr.span)); | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug, Default, Clone)] | ||
enum PreferES6ClassOptionType { | ||
#[default] | ||
Always, | ||
Never, | ||
} | ||
|
||
impl PreferES6ClassOptionType { | ||
pub fn from(raw: &str) -> Self { | ||
match raw { | ||
"always" => Self::Always, | ||
_ => Self::Never, | ||
} | ||
} | ||
} | ||
|
||
#[test] | ||
fn test() { | ||
use crate::tester::Tester; | ||
|
||
let pass = vec![ | ||
( | ||
r" | ||
class Hello extends React.Component { | ||
render() { | ||
return <div>Hello {this.props.name}</div>; | ||
} | ||
} | ||
Hello.displayName = 'Hello' | ||
", | ||
None, | ||
), | ||
( | ||
r" | ||
export default class Hello extends React.Component { | ||
render() { | ||
return <div>Hello {this.props.name}</div>; | ||
} | ||
} | ||
Hello.displayName = 'Hello' | ||
", | ||
None, | ||
), | ||
( | ||
r" | ||
var Hello = 'foo'; | ||
module.exports = {}; | ||
", | ||
None, | ||
), | ||
( | ||
r" | ||
var Hello = createReactClass({ | ||
render: function() { | ||
return <div>Hello {this.props.name}</div>; | ||
} | ||
}); | ||
", | ||
Some(serde_json::json!(["never"])), | ||
), | ||
( | ||
r" | ||
class Hello extends React.Component { | ||
render() { | ||
return <div>Hello {this.props.name}</div>; | ||
} | ||
} | ||
", | ||
Some(serde_json::json!(["always"])), | ||
), | ||
]; | ||
|
||
let fail = vec![ | ||
( | ||
r" | ||
var Hello = createReactClass({ | ||
displayName: 'Hello', | ||
render: function() { | ||
return <div>Hello {this.props.name}</div>; | ||
} | ||
}); | ||
", | ||
None, | ||
), | ||
( | ||
r" | ||
var Hello = createReactClass({ | ||
render: function() { | ||
return <div>Hello {this.props.name}</div>; | ||
} | ||
}); | ||
", | ||
Some(serde_json::json!(["always"])), | ||
), | ||
( | ||
r" | ||
class Hello extends React.Component { | ||
render() { | ||
return <div>Hello {this.props.name}</div>; | ||
} | ||
} | ||
", | ||
Some(serde_json::json!(["never"])), | ||
), | ||
]; | ||
|
||
Tester::new(PreferEs6Class::NAME, pass, fail).test_and_snapshot(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
--- | ||
source: crates/oxc_linter/src/tester.rs | ||
--- | ||
⚠ eslint-plugin-react(prefer-es6-class): Component should use es6 class instead of createClass | ||
╭─[prefer_es_6_class.tsx:2:25] | ||
1 │ | ||
2 │ ╭─▶ var Hello = createReactClass({ | ||
3 │ │ displayName: 'Hello', | ||
4 │ │ render: function() { | ||
5 │ │ return <div>Hello {this.props.name}</div>; | ||
6 │ │ } | ||
7 │ ╰─▶ }); | ||
8 │ | ||
╰──── | ||
|
||
⚠ eslint-plugin-react(prefer-es6-class): Component should use es6 class instead of createClass | ||
╭─[prefer_es_6_class.tsx:2:25] | ||
1 │ | ||
2 │ ╭─▶ var Hello = createReactClass({ | ||
3 │ │ render: function() { | ||
4 │ │ return <div>Hello {this.props.name}</div>; | ||
5 │ │ } | ||
6 │ ╰─▶ }); | ||
7 │ | ||
╰──── | ||
|
||
⚠ eslint-plugin-react(prefer-es6-class): Component should use createClass instead of es6 class | ||
╭─[prefer_es_6_class.tsx:2:13] | ||
1 │ | ||
2 │ ╭─▶ class Hello extends React.Component { | ||
3 │ │ render() { | ||
4 │ │ return <div>Hello {this.props.name}</div>; | ||
5 │ │ } | ||
6 │ ╰─▶ } | ||
7 │ | ||
╰──── |