Skip to content

Commit 3073214

Browse files
Efimenkoljharb
authored andcommitted
[New] forbid-component-props: add allowedForPatterns/disallowedForPatterns options
1 parent 95297ed commit 3073214

File tree

4 files changed

+299
-16
lines changed

4 files changed

+299
-16
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
99
### Added
1010
* add type generation ([#3830][] @voxpelli)
1111
* [`no-unescaped-entities`]: add suggestions ([#3831][] @StyleShit)
12+
* [`forbid-component-props`]: add `allowedForPatterns`/`disallowedForPatterns` options ([#3805][] @Efimenko)
1213

1314
[#3831]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3831
1415
[#3830]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3830
16+
[#3805]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3805
1517

1618
## [7.36.1] - 2024.09.12
1719

docs/rules/forbid-component-props.md

+37-8
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,17 @@ custom message, and a component allowlist:
5555
}
5656
```
5757

58-
For glob string patterns:
58+
Use `disallowedFor` as an exclusion list to warn on props for specific components. `disallowedFor` must 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"
65+
}
66+
```
67+
68+
For `propNamePattern` glob string patterns:
5969

6070
```js
6171
{
@@ -65,23 +75,42 @@ For glob string patterns:
6575
}
6676
```
6777

68-
Use `disallowedFor` as an exclusion list to warn on props for specific components. `disallowedFor` must have at least one item.
78+
```js
79+
{
80+
"propNamePattern": '**-**',
81+
"allowedForPatterns": ["*Component"],
82+
"message": "Avoid using kebab-case except components that match the `*Component` pattern"
83+
}
84+
```
85+
86+
Use `allowedForPatterns` for glob string patterns:
6987

7088
```js
7189
{
7290
"propName": "someProp",
73-
"disallowedFor": ["SomeComponent", "AnotherComponent"],
74-
"message": "Avoid using someProp for SomeComponent and AnotherComponent"
91+
"allowedForPatterns": ["*Component"],
92+
"message": "Avoid using `someProp` except components that match the `*Component` pattern"
93+
}
94+
```
95+
96+
Use `disallowedForPatterns` for glob string patterns:
97+
98+
```js
99+
{
100+
"propName": "someProp",
101+
"disallowedForPatterns": ["*Component"],
102+
"message": "Avoid using `someProp` for components that match the `*Component` pattern"
75103
}
76104
```
77105

78-
For glob string patterns:
106+
Combine several properties to cover more cases:
79107

80108
```js
81109
{
82-
"propNamePattern": "**-**",
83-
"disallowedFor": ["MyComponent"],
84-
"message": "Avoid using kebab-case for MyComponent"
110+
"propName": "someProp",
111+
"allowedFor": ['div'],
112+
"allowedForPatterns": ["*Component"],
113+
"message": "Avoid using `someProp` except `div` and components that match the `*Component` pattern"
85114
}
86115
```
87116

lib/rules/forbid-component-props.js

+67-8
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ module.exports = {
5252
uniqueItems: true,
5353
items: { type: 'string' },
5454
},
55+
allowedForPatterns: {
56+
type: 'array',
57+
uniqueItems: true,
58+
items: { type: 'string' },
59+
},
5560
message: { type: 'string' },
5661
},
5762
additionalProperties: false,
@@ -66,12 +71,20 @@ module.exports = {
6671
minItems: 1,
6772
items: { type: 'string' },
6873
},
74+
disallowedForPatterns: {
75+
type: 'array',
76+
uniqueItems: true,
77+
minItems: 1,
78+
items: { type: 'string' },
79+
},
6980
message: { type: 'string' },
7081
},
71-
required: ['disallowedFor'],
82+
anyOf: [
83+
{ required: ['disallowedFor'] },
84+
{ required: ['disallowedForPatterns'] },
85+
],
7286
additionalProperties: false,
7387
},
74-
7588
{
7689
type: 'object',
7790
properties: {
@@ -81,6 +94,11 @@ module.exports = {
8194
uniqueItems: true,
8295
items: { type: 'string' },
8396
},
97+
allowedForPatterns: {
98+
type: 'array',
99+
uniqueItems: true,
100+
items: { type: 'string' },
101+
},
84102
message: { type: 'string' },
85103
},
86104
additionalProperties: false,
@@ -95,9 +113,18 @@ module.exports = {
95113
minItems: 1,
96114
items: { type: 'string' },
97115
},
116+
disallowedForPatterns: {
117+
type: 'array',
118+
uniqueItems: true,
119+
minItems: 1,
120+
items: { type: 'string' },
121+
},
98122
message: { type: 'string' },
99123
},
100-
required: ['disallowedFor'],
124+
anyOf: [
125+
{ required: ['disallowedFor'] },
126+
{ required: ['disallowedForPatterns'] },
127+
],
101128
additionalProperties: false,
102129
},
103130
],
@@ -114,8 +141,10 @@ module.exports = {
114141
const propPattern = value.propNamePattern;
115142
const prop = propName || propPattern;
116143
const options = {
117-
allowList: typeof value === 'string' ? [] : (value.allowedFor || []),
118-
disallowList: typeof value === 'string' ? [] : (value.disallowedFor || []),
144+
allowList: [].concat(value.allowedFor || []),
145+
allowPatternList: [].concat(value.allowedForPatterns || []),
146+
disallowList: [].concat(value.disallowedFor || []),
147+
disallowPatternList: [].concat(value.disallowedForPatterns || []),
119148
message: typeof value === 'string' ? null : value.message,
120149
isPattern: !!value.propNamePattern,
121150
};
@@ -140,10 +169,40 @@ module.exports = {
140169
return false;
141170
}
142171

172+
function checkIsTagForbiddenByAllowOptions() {
173+
if (options.allowList.indexOf(tagName) !== -1) {
174+
return false;
175+
}
176+
177+
if (options.allowPatternList.length === 0) {
178+
return true;
179+
}
180+
181+
return options.allowPatternList.every(
182+
(pattern) => !minimatch(tagName, pattern)
183+
);
184+
}
185+
186+
function checkIsTagForbiddenByDisallowOptions() {
187+
if (options.disallowList.indexOf(tagName) !== -1) {
188+
return true;
189+
}
190+
191+
if (options.disallowPatternList.length === 0) {
192+
return false;
193+
}
194+
195+
return options.disallowPatternList.some(
196+
(pattern) => minimatch(tagName, pattern)
197+
);
198+
}
199+
200+
const hasDisallowOptions = options.disallowList.length > 0 || options.disallowPatternList.length > 0;
201+
143202
// disallowList should have a least one item (schema configuration)
144-
const isTagForbidden = options.disallowList.length > 0
145-
? options.disallowList.indexOf(tagName) !== -1
146-
: options.allowList.indexOf(tagName) === -1;
203+
const isTagForbidden = hasDisallowOptions
204+
? checkIsTagForbiddenByDisallowOptions()
205+
: checkIsTagForbiddenByAllowOptions();
147206

148207
// if the tagName is undefined (`<this.something>`), we assume it's a forbidden element
149208
return typeof tagName === 'undefined' || isTagForbidden;

0 commit comments

Comments
 (0)