Skip to content

Commit

Permalink
feat: add jsx-no-danger-with-children rule (#1349)
Browse files Browse the repository at this point in the history
* feat: add jsx-no-danger-with-children rule

* fix: clippy

* fix: update schema

* fix: update docs

* remove code comment
  • Loading branch information
marvinhagemeister authored Nov 29, 2024
1 parent dcdbb1a commit 1e76fc0
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 0 deletions.
16 changes: 16 additions & 0 deletions docs/rules/jsx_no_danger_with_children.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Using JSX children together with `dangerouslySetInnerHTML` is invalid as they
will be ignored.

### Invalid:

```tsx
<div dangerouslySetInnerHTML={{ __html: "<h1>hello</h1>" }}>
<h1>this will never be rendered</h1>
</div>;
```

### Valid:

```tsx
<div dangerouslySetInnerHTML={{ __html: "<h1>hello</h1>" }} />;
```
1 change: 1 addition & 0 deletions schemas/rules.v1.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"jsx-boolean-value",
"jsx-curly-braces",
"jsx-no-children-prop",
"jsx-no-danger-with-children",
"jsx-no-duplicate-props",
"jsx-no-useless-fragment",
"jsx-props-no-spread-multi",
Expand Down
2 changes: 2 additions & 0 deletions src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ pub mod guard_for_in;
pub mod jsx_boolean_value;
pub mod jsx_curly_braces;
pub mod jsx_no_children_prop;
pub mod jsx_no_danger_with_children;
pub mod jsx_no_duplicate_props;
pub mod jsx_no_useless_fragment;
pub mod jsx_props_no_spread_multi;
Expand Down Expand Up @@ -270,6 +271,7 @@ fn get_all_rules_raw() -> Vec<Box<dyn LintRule>> {
Box::new(jsx_boolean_value::JSXBooleanValue),
Box::new(jsx_curly_braces::JSXCurlyBraces),
Box::new(jsx_no_children_prop::JSXNoChildrenProp),
Box::new(jsx_no_danger_with_children::JSXNoDangerWithChildren),
Box::new(jsx_no_duplicate_props::JSXNoDuplicateProps),
Box::new(jsx_no_useless_fragment::JSXNoUselessFragment),
Box::new(jsx_props_no_spread_multi::JSXPropsNoSpreadMulti),
Expand Down
86 changes: 86 additions & 0 deletions src/rules/jsx_no_danger_with_children.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

use super::{Context, LintRule};
use crate::handler::{Handler, Traverse};
use crate::tags::{self, Tags};
use crate::Program;
use deno_ast::view::{JSXAttrName, JSXAttrOrSpread, JSXElement};
use deno_ast::SourceRanged;

#[derive(Debug)]
pub struct JSXNoDangerWithChildren;

const CODE: &str = "jsx-no-danger-with-children";

impl LintRule for JSXNoDangerWithChildren {
fn tags(&self) -> Tags {
&[tags::RECOMMENDED, tags::REACT, tags::JSX, tags::FRESH]
}

fn code(&self) -> &'static str {
CODE
}

fn lint_program_with_ast_view(
&self,
context: &mut Context,
program: Program,
) {
JSXNoDangerWithChildrenHandler.traverse(program, context);
}

#[cfg(feature = "docs")]
fn docs(&self) -> &'static str {
include_str!("../../docs/rules/jsx_no_danger_with_children.md")
}
}

const MESSAGE: &str =
"Using JSX children together with 'dangerouslySetInnerHTML' is invalid";
const HINT: &str = "Remove the JSX children";

struct JSXNoDangerWithChildrenHandler;

impl Handler for JSXNoDangerWithChildrenHandler {
fn jsx_element(&mut self, node: &JSXElement, ctx: &mut Context) {
for attr in node.opening.attrs {
if let JSXAttrOrSpread::JSXAttr(attr) = attr {
if let JSXAttrName::Ident(id) = attr.name {
if id.sym() == "dangerouslySetInnerHTML" && !node.children.is_empty()
{
ctx.add_diagnostic_with_hint(node.range(), CODE, MESSAGE, HINT);
}
}
}
}
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn jsx_no_danger_with_children_valid() {
assert_lint_ok! {
JSXNoDangerWithChildren,
filename: "file:///foo.jsx",
r#"<div dangerouslySetInnerHTML={{ __html: "foo" }} />"#,
};
}

#[test]
fn jsx_no_danger_with_children_invalid() {
assert_lint_err! {
JSXNoDangerWithChildren,
filename: "file:///foo.jsx",
r#"<div dangerouslySetInnerHTML={{ __html: "foo" }}>foo</div>"#: [
{
col: 0,
message: MESSAGE,
hint: HINT
}
]
};
}
}
10 changes: 10 additions & 0 deletions www/static/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,16 @@
"fresh"
]
},
{
"code": "jsx-no-danger-with-children",
"docs": "Using JSX children together with `dangerouslySetInnerHTML` is invalid as they\nwill be ignored.\n\n### Invalid:\n\n```tsx\n<div dangerouslySetInnerHTML={{ __html: \"<h1>hello</h1>\" }}>\n <h1>this will never be rendered</h1>\n</div>;\n```\n\n### Valid:\n\n```tsx\n<div dangerouslySetInnerHTML={{ __html: \"<h1>hello</h1>\" }} />;\n```\n",
"tags": [
"recommended",
"react",
"jsx",
"fresh"
]
},
{
"code": "jsx-no-duplicate-props",
"docs": "Disallow duplicated JSX props. Later props will always overwrite earlier props\noften leading to unexpected results.\n\n### Invalid:\n\n```tsx\n<div id=\"1\" id=\"2\" />;\n<App a a />;\n<App a {...b} a />;\n```\n\n### Valid:\n\n```tsx\n<div id=\"1\" />\n<App a />\n<App a {...b} />\n<App {...b} b />\n```\n",
Expand Down

0 comments on commit 1e76fc0

Please sign in to comment.