use std::collections::HashSet;
use super::{Context, LintRule};
use crate::handler::{Handler, Traverse};
use crate::tags::{self, Tags};
use crate::Program;
use deno_ast::view::{JSXAttrName, JSXAttrOrSpread, JSXOpeningElement};
use deno_ast::SourceRanged;
#[derive(Debug)]
pub struct JSXNoDuplicateProps;
const CODE: &str = "jsx-no-duplicate-props";
impl LintRule for JSXNoDuplicateProps {
fn tags(&self) -> Tags {
&[tags::RECOMMENDED, tags::REACT, tags::JSX]
}
fn code(&self) -> &'static str {
CODE
}
fn lint_program_with_ast_view(
&self,
context: &mut Context,
program: Program,
) {
JSXNoDuplicatedPropsHandler.traverse(program, context);
}
}
const MESSAGE: &str = "Duplicate JSX attribute found.";
const HINT: &str = "Remove the duplicated attribute.";
struct JSXNoDuplicatedPropsHandler;
impl Handler for JSXNoDuplicatedPropsHandler {
fn jsx_opening_element(
&mut self,
node: &JSXOpeningElement,
ctx: &mut Context,
) {
let mut seen: HashSet<&'_ str> = HashSet::new();
for attr in node.attrs {
if let JSXAttrOrSpread::JSXAttr(attr_name) = attr {
if let JSXAttrName::Ident(id) = attr_name.name {
let name = id.sym().as_str();
if seen.contains(name) {
ctx.add_diagnostic_with_hint(id.range(), CODE, MESSAGE, HINT);
}
seen.insert(name);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn jsx_no_duplicate_props_valid() {
assert_lint_ok! {
JSXNoDuplicateProps,
filename: "file:///foo.jsx",
"<App a b />",
"<div a b />",
};
}
#[test]
fn jsx_no_duplicate_props_invalid() {
assert_lint_err! {
JSXNoDuplicateProps,
filename: "file:///foo.jsx",
"<div a a />": [
{
col: 7,
message: MESSAGE,
hint: HINT,
}
],
"<App a a />": [
{
col: 7,
message: MESSAGE,
hint: HINT,
}
],
"<App a {...b} a />": [
{
col: 14,
message: MESSAGE,
hint: HINT,
}
],
"<div a {...b} a />": [
{
col: 14,
message: MESSAGE,
hint: HINT,
}
]
};
}
}