Skip to content

Commit 83893d4

Browse files
author
jilianfeng
committed
[New] forbid-component-props: add disallowedFor option
1 parent 1fa0888 commit 83893d4

File tree

4 files changed

+170
-19
lines changed

4 files changed

+170
-19
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
55

66
## Unreleased
77

8+
### Added
9+
* [`forbid-component-props`]: add `disallowedFor` option ([#xxxx][] @jacketwpbb)
10+
811
### Fixed
912
* [`no-unknown-property`]: add `viewBox` on `marker` ([#3416][] @ljharb)
1013
* [`no-unknown-property`]: add `noModule` on `script` ([#3414][] @ljharb)

docs/rules/forbid-component-props.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,18 @@ custom message, and a component whitelist:
5050
```js
5151
{
5252
"propName": "someProp",
53-
"allowedFor": [SomeComponent, AnotherComponent],
54-
"message": "Avoid using someProp"
53+
"allowedFor": ["SomeComponent", "AnotherComponent"],
54+
"message": "Avoid using someProp except SomeComponent and AnotherComponent"
55+
}
56+
```
57+
58+
Use `disallowedFor` as a blacklist to lint props for specific components. `disallowedFor` should have at least one item.
59+
60+
```js
61+
{
62+
"propName": "someProp",
63+
"disallowedFor": ["SomeComponent", "AnotherComponent"],
64+
"message": "Avoid using someProp for SomeComponent and AnotherComponent"
5565
}
5666
```
5767

lib/rules/forbid-component-props.js

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,26 +39,51 @@ module.exports = {
3939
forbid: {
4040
type: 'array',
4141
items: {
42-
oneOf: [{
43-
type: 'string',
44-
}, {
45-
type: 'object',
46-
properties: {
47-
propName: {
48-
type: 'string',
49-
},
50-
allowedFor: {
51-
type: 'array',
52-
uniqueItems: true,
53-
items: {
42+
oneOf: [
43+
{
44+
type: 'string',
45+
},
46+
{
47+
type: 'object',
48+
properties: {
49+
propName: {
50+
type: 'string',
51+
},
52+
allowedFor: {
53+
type: 'array',
54+
uniqueItems: true,
55+
items: {
56+
type: 'string',
57+
},
58+
},
59+
message: {
5460
type: 'string',
5561
},
5662
},
57-
message: {
58-
type: 'string',
63+
additionalProperties: false,
64+
},
65+
{
66+
type: 'object',
67+
properties: {
68+
propName: {
69+
type: 'string',
70+
},
71+
disallowedFor: {
72+
type: 'array',
73+
uniqueItems: true,
74+
minItems: 1,
75+
items: {
76+
type: 'string',
77+
},
78+
},
79+
message: {
80+
type: 'string',
81+
},
5982
},
83+
required: ['disallowedFor'],
84+
additionalProperties: false,
6085
},
61-
}],
86+
],
6287
},
6388
},
6489
},
@@ -71,16 +96,25 @@ module.exports = {
7196
const propName = typeof value === 'string' ? value : value.propName;
7297
const options = {
7398
allowList: typeof value === 'string' ? [] : (value.allowedFor || []),
99+
disallowList: typeof value === 'string' ? [] : (value.disallowedFor || []),
74100
message: typeof value === 'string' ? null : value.message,
75101
};
76102
return [propName, options];
77103
}));
78104

79105
function isForbidden(prop, tagName) {
80106
const options = forbid.get(prop);
81-
const allowList = options ? options.allowList : undefined;
107+
if (!options) {
108+
return false;
109+
}
110+
111+
// disallowList should have a least one item (schema configuration)
112+
const isTagForbidden = options.disallowList.length
113+
? options.disallowList.indexOf(tagName) !== -1
114+
: options.allowList.indexOf(tagName) === -1;
115+
82116
// if the tagName is undefined (`<this.something>`), we assume it's a forbidden element
83-
return typeof allowList !== 'undefined' && (typeof tagName === 'undefined' || allowList.indexOf(tagName) === -1);
117+
return typeof tagName === 'undefined' || isTagForbidden;
84118
}
85119

86120
return {

tests/lib/rules/forbid-component-props.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,67 @@ ruleTester.run('forbid-component-props', rule, {
157157
`,
158158
features: ['jsx namespace'],
159159
},
160+
{
161+
code: `
162+
const item = (
163+
<Foo className="bar">
164+
<ReactModal style={{color: "red"}} />
165+
</Foo>
166+
);
167+
`,
168+
options: [
169+
{
170+
forbid: [
171+
{
172+
propName: 'className',
173+
disallowedFor: ['OtherModal', 'ReactModal'],
174+
},
175+
{
176+
propName: 'style',
177+
disallowedFor: ['Foo'],
178+
},
179+
],
180+
},
181+
],
182+
},
183+
{
184+
code: `
185+
const item = (
186+
<Foo className="bar">
187+
<ReactModal style={{color: "red"}} />
188+
</Foo>
189+
);
190+
`,
191+
options: [
192+
{
193+
forbid: [
194+
{
195+
propName: 'className',
196+
disallowedFor: ['OtherModal', 'ReactModal'],
197+
},
198+
{
199+
propName: 'style',
200+
allowedFor: ['ReactModal'],
201+
},
202+
],
203+
},
204+
],
205+
},
206+
{
207+
code: `
208+
const item = (<this.ReactModal className="foo" />);
209+
`,
210+
options: [
211+
{
212+
forbid: [
213+
{
214+
propName: 'className',
215+
disallowedFor: ['ReactModal'],
216+
},
217+
],
218+
},
219+
],
220+
},
160221
]),
161222

162223
invalid: parsers.all([
@@ -235,6 +296,25 @@ ruleTester.run('forbid-component-props', rule, {
235296
},
236297
],
237298
},
299+
{
300+
code: `
301+
var First = createReactClass({
302+
propTypes: externalPropTypes,
303+
render: function() {
304+
return <Foo style={{color: "red"}} />;
305+
}
306+
});
307+
`,
308+
options: [{ forbid: [{ propName: 'style', disallowedFor: ['Foo'] }] }],
309+
errors: [
310+
{
311+
messageId: 'propIsForbidden',
312+
data: { prop: 'style' },
313+
line: 5,
314+
type: 'JSXAttribute',
315+
},
316+
],
317+
},
238318
{
239319
code: `
240320
const item = (<Foo className="foo" />);
@@ -282,6 +362,30 @@ ruleTester.run('forbid-component-props', rule, {
282362
},
283363
],
284364
},
365+
{
366+
code: `
367+
const item = (<this.ReactModal className="foo" />);
368+
`,
369+
options: [
370+
{
371+
forbid: [
372+
{
373+
propName: 'className',
374+
disallowedFor: ['this.ReactModal'],
375+
},
376+
],
377+
},
378+
],
379+
errors: [
380+
{
381+
messageId: 'propIsForbidden',
382+
data: { prop: 'className' },
383+
line: 2,
384+
column: 40,
385+
type: 'JSXAttribute',
386+
},
387+
],
388+
},
285389
{
286390
code: `
287391
const item = (<Foo className="foo" />);

0 commit comments

Comments
 (0)