Skip to content

Commit 597553d

Browse files
akulsr0ljharb
authored andcommitted
[New] no-danger: add customComponentNames option
1 parent c58f04b commit 597553d

File tree

4 files changed

+126
-4
lines changed

4 files changed

+126
-4
lines changed

CHANGELOG.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,18 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1313
* [`jsx-handler-names`]: support ignoring component names ([#3772][] @akulsr0)
1414
* version settings: Allow react defaultVersion to be configurable ([#3771][] @onlywei)
1515
* [`jsx-closing-tag-location`]: add `line-aligned` option ([#3777] @kimtaejin3)
16+
* [`no-danger`]: add `customComponentNames` option ([#3748][] @akulsr0)
1617

1718
### Changed
1819
* [Refactor] `variableUtil`: Avoid creating a single flat variable scope for each lookup ([#3782][] @DanielRosenwasser)
1920

20-
e[#3782]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3782
21+
[#3782]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3782
2122
[#3777]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3777
2223
[#3774]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3774
2324
[#3772]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3772
2425
[#3771]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3771
2526
[#3759]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3759
27+
[#3748]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3748
2628
[#3724]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3724
2729
[#3694]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3694
2830

@@ -60,7 +62,7 @@ e[#3782]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3782
6062

6163
### Fixed
6264
* [`boolean-prop-naming`]: avoid a crash with a non-TSTypeReference type ([#3718][] @developer-bandi)
63-
* [`jsx-no-leaked-render`]: invalid report if left side is boolean ([#3746][] @akulsr0)
65+
* [`jsx-no-leaked-render`]: invalid report if left eside is boolean ([#3746][] @akulsr0)
6466
* [`jsx-closing-bracket-location`]: message shows `{{details}}` when there are no details ([#3759][] @mdjermanovic)
6567
* [`no-invalid-html-attribute`]: ensure error messages are correct ([#3759][] @mdjermanovic, @ljharb)
6668

docs/rules/no-danger.md

+14
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,20 @@ var React = require('react');
2424
var Hello = <div>Hello World</div>;
2525
```
2626

27+
## Rule Options
28+
29+
```js
30+
...
31+
"react/no-danger": [<enabled>, {
32+
"customComponentNames": Array<string>,
33+
}]
34+
...
35+
```
36+
37+
### customComponentNames
38+
39+
Defaults to `[]`, if you want to enable this rule for all custom components you can pass `customComponentNames` as `['*']`, or else you can pass specific components name to the array.
40+
2741
## When Not To Use It
2842

2943
If you are certain the content passed to dangerouslySetInnerHTML is sanitized HTML you can disable this rule.

lib/rules/no-danger.js

+22-2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
const has = require('hasown');
99
const fromEntries = require('object.fromentries/polyfill')();
10+
const minimatch = require('minimatch');
1011

1112
const docsUrl = require('../util/docsUrl');
1213
const jsxUtil = require('../util/jsx');
@@ -55,13 +56,32 @@ module.exports = {
5556

5657
messages,
5758

58-
schema: [],
59+
schema: [{
60+
type: 'object',
61+
properties: {
62+
customComponentNames: {
63+
items: {
64+
type: 'string',
65+
},
66+
minItems: 0,
67+
type: 'array',
68+
uniqueItems: true,
69+
},
70+
},
71+
}],
5972
},
6073

6174
create(context) {
75+
const configuration = context.options[0] || {};
76+
const customComponentNames = configuration.customComponentNames || [];
77+
6278
return {
6379
JSXAttribute(node) {
64-
if (jsxUtil.isDOMComponent(node.parent) && isDangerous(node.name.name)) {
80+
const functionName = node.parent.name.name;
81+
82+
const enableCheckingCustomComponent = customComponentNames.some((name) => minimatch(functionName, name));
83+
84+
if ((enableCheckingCustomComponent || jsxUtil.isDOMComponent(node.parent)) && isDangerous(node.name.name)) {
6585
report(context, messages.dangerousProp, 'dangerousProp', {
6686
node,
6787
data: {

tests/lib/rules/no-danger.js

+86
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,26 @@ ruleTester.run('no-danger', rule, {
3232
{ code: '<App />;' },
3333
{ code: '<App dangerouslySetInnerHTML={{ __html: "" }} />;' },
3434
{ code: '<div className="bar"></div>;' },
35+
{
36+
code: '<div className="bar"></div>;',
37+
options: [{ customComponentNames: ['*'] }],
38+
},
39+
{
40+
code: `
41+
function App() {
42+
return <Title dangerouslySetInnerHTML={{ __html: "<span>hello</span>" }} />;
43+
}
44+
`,
45+
options: [{ customComponentNames: ['Home'] }],
46+
},
47+
{
48+
code: `
49+
function App() {
50+
return <TextMUI dangerouslySetInnerHTML={{ __html: "<span>hello</span>" }} />;
51+
}
52+
`,
53+
options: [{ customComponentNames: ['MUI*'] }],
54+
},
3555
]),
3656
invalid: parsers.all([
3757
{
@@ -43,5 +63,71 @@ ruleTester.run('no-danger', rule, {
4363
},
4464
],
4565
},
66+
{
67+
code: '<App dangerouslySetInnerHTML={{ __html: "<span>hello</span>" }} />;',
68+
options: [{ customComponentNames: ['*'] }],
69+
errors: [
70+
{
71+
messageId: 'dangerousProp',
72+
data: { name: 'dangerouslySetInnerHTML' },
73+
},
74+
],
75+
},
76+
{
77+
code: `
78+
function App() {
79+
return <Title dangerouslySetInnerHTML={{ __html: "<span>hello</span>" }} />;
80+
}
81+
`,
82+
options: [{ customComponentNames: ['Title'] }],
83+
errors: [
84+
{
85+
messageId: 'dangerousProp',
86+
data: { name: 'dangerouslySetInnerHTML' },
87+
},
88+
],
89+
},
90+
{
91+
code: `
92+
function App() {
93+
return <TextFoo dangerouslySetInnerHTML={{ __html: "<span>hello</span>" }} />;
94+
}
95+
`,
96+
options: [{ customComponentNames: ['*Foo'] }],
97+
errors: [
98+
{
99+
messageId: 'dangerousProp',
100+
data: { name: 'dangerouslySetInnerHTML' },
101+
},
102+
],
103+
},
104+
{
105+
code: `
106+
function App() {
107+
return <FooText dangerouslySetInnerHTML={{ __html: "<span>hello</span>" }} />;
108+
}
109+
`,
110+
options: [{ customComponentNames: ['Foo*'] }],
111+
errors: [
112+
{
113+
messageId: 'dangerousProp',
114+
data: { name: 'dangerouslySetInnerHTML' },
115+
},
116+
],
117+
},
118+
{
119+
code: `
120+
function App() {
121+
return <TextMUI dangerouslySetInnerHTML={{ __html: "<span>hello</span>" }} />;
122+
}
123+
`,
124+
options: [{ customComponentNames: ['*MUI'] }],
125+
errors: [
126+
{
127+
messageId: 'dangerousProp',
128+
data: { name: 'dangerouslySetInnerHTML' },
129+
},
130+
],
131+
},
46132
]),
47133
});

0 commit comments

Comments
 (0)