Skip to content

Commit 13cfdc7

Browse files
dmason30ljharb
authored andcommitted
[New] add static-property-placement rule
1 parent f6becb1 commit 13cfdc7

File tree

7 files changed

+2170
-22
lines changed

7 files changed

+2170
-22
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ Enable the rules that you would like to use.
142142
* [react/sort-comp](docs/rules/sort-comp.md): Enforce component methods order (fixable)
143143
* [react/sort-prop-types](docs/rules/sort-prop-types.md): Enforce propTypes declarations alphabetical sorting
144144
* [react/state-in-constructor](docs/rules/state-in-constructor.md): Enforce the state initialization style to be either in a constructor or with a class property
145+
* [react/static-property-placement](docs/rules/static-property-placement.md): Defines where React component static properties should be positioned.
145146
* [react/style-prop-object](docs/rules/style-prop-object.md): Enforce style prop value being an object
146147
* [react/void-dom-elements-no-children](docs/rules/void-dom-elements-no-children.md): Prevent void DOM elements (e.g. `<img />`, `<br />`) from receiving children
147148

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
# Enforces where React component static properties should be positioned. (static-property-placement)
2+
3+
This rule allows you to enforce where `childContextTypes`, `contextTypes`, `contextType`, `defaultProps`, `displayName`,
4+
and `propTypes` are declared in an ES6 class.
5+
6+
7+
## Rule Details
8+
9+
By default, this rule will check for and warn about declaring any of the above properties outside of the class body.
10+
11+
There are three key options are `static public field`, `static getter`, and `property assignment`.
12+
13+
### When `static public field` is enabled (default):
14+
15+
Examples of **incorrect** code for this rule:
16+
17+
```js
18+
class MyComponent extends React.Component {
19+
static get childContextTypes() { /*...*/ }
20+
static get contextTypes() { /*...*/ }
21+
static get contextType() { /*...*/ }
22+
static get displayName() { /*...*/ }
23+
static get defaultProps() { /*...*/ }
24+
static get propTypes() { /*...*/ }
25+
}
26+
```
27+
28+
```js
29+
class MyComponent extends React.Component { /*...*/ }
30+
MyComponent.childContextTypes = { /*...*/ };
31+
MyComponent.contextTypes = { /*...*/ };
32+
MyComponent.contextType = { /*...*/ };
33+
MyComponent.displayName = "Hello";
34+
MyComponent.defaultProps = { /*...*/ };
35+
MyComponent.propTypes = { /*...*/ };
36+
```
37+
38+
Examples of **correct** code for this rule:
39+
40+
```js
41+
class MyComponent extends React.Component {
42+
static childContextTypes = { /*...*/ };
43+
static contextTypes = { /*...*/ };
44+
static contextType = { /*...*/ };
45+
static displayName = "Hello";
46+
static defaultProps = { /*...*/ };
47+
static propTypes = { /*...*/ };
48+
}
49+
```
50+
51+
### When `static getter` is enabled:
52+
53+
Examples of **incorrect** code for this rule:
54+
55+
```js
56+
class MyComponent extends React.Component {
57+
static childContextTypes = { /*...*/ };
58+
static contextTypes = { /*...*/ };
59+
static contextType = { /*...*/ };
60+
static displayName = "Hello";
61+
static defaultProps = { /*...*/ };
62+
static propTypes = { /*...*/ };
63+
}
64+
```
65+
66+
```js
67+
class MyComponent extends React.Component { /*...*/ }
68+
MyComponent.childContextTypes = { /*...*/ };
69+
MyComponent.contextTypes = { /*...*/ };
70+
MyComponent.contextType = { /*...*/ };
71+
MyComponent.displayName = "Hello";
72+
MyComponent.defaultProps = { /*...*/ };
73+
MyComponent.propTypes = { /*...*/ };
74+
```
75+
76+
Examples of **correct** code for this rule:
77+
78+
```js
79+
class MyComponent extends React.Component {
80+
static get childContextTypes() { /*...*/ }
81+
static get contextTypes() { /*...*/ }
82+
static get contextType() { /*...*/ }
83+
static get displayName() { /*...*/ }
84+
static get defaultProps() { /*...*/ }
85+
static get propTypes() { /*...*/ }
86+
}
87+
```
88+
89+
### When `property assignment` is enabled:
90+
91+
Examples of **incorrect** code for this rule:
92+
93+
```js
94+
class MyComponent extends React.Component {
95+
static childContextTypes = { /*...*/ };
96+
static contextTypes = { /*...*/ };
97+
static contextType = { /*...*/ };
98+
static displayName = "Hello";
99+
static defaultProps = { /*...*/ };
100+
static propTypes = { /*...*/ };
101+
}
102+
```
103+
104+
```js
105+
class MyComponent extends React.Component {
106+
static get childContextTypes() { /*...*/ }
107+
static get contextTypes() { /*...*/ }
108+
static get contextType() { /*...*/ }
109+
static get displayName() { /*...*/ }
110+
static get defaultProps() { /*...*/ }
111+
static get propTypes() { /*...*/ }
112+
}
113+
```
114+
115+
Examples of **correct** code for this rule:
116+
117+
```js
118+
class MyComponent extends React.Component { /*...*/ }
119+
MyComponent.childContextTypes = { /*...*/ };
120+
MyComponent.contextTypes = { /*...*/ };
121+
MyComponent.contextType = { /*...*/ };
122+
MyComponent.displayName = "Hello";
123+
MyComponent.defaultProps = { /*...*/ };
124+
MyComponent.propTypes = { /*...*/ };
125+
```
126+
127+
### Options
128+
129+
```
130+
...
131+
"react/static-property-placement": [<enabled>] // `static public field` enabled
132+
...
133+
```
134+
135+
or alternatively:
136+
137+
```
138+
...
139+
"react/static-property-placement": [<enabled>, <string>]
140+
...
141+
```
142+
143+
or alternatively:
144+
145+
```
146+
...
147+
"react/static-property-placement": [<enabled>, <string>, {
148+
childContextTypes: <string>,
149+
contextTypes: <string>,
150+
contextType: <string>,
151+
defaultProps: <string>,
152+
displayName: <string>,
153+
propTypes: <string>,
154+
}]
155+
...
156+
```
157+
The `<string>` value must be one these options:
158+
* `static public field`
159+
* `static getter`
160+
* `property assignment`
161+
162+
The `options` schema defined above allows you to specify different rules for the different property fields available.
163+
164+
##### Example configuration:
165+
_This is only an example, we do not recommend this as a configuration._
166+
```
167+
...
168+
"react/static-property-placement": ["warn", "property assignment", {
169+
childContextTypes: "static getter",
170+
contextTypes: "static public field",
171+
contextType: "static public field",
172+
displayName: "static public field",
173+
}]
174+
...
175+
```
176+
177+
Based on the above configuration:
178+
* `defaultProps` and `propTypes` will both enforce the `property assignment` rule.
179+
* `childContextTypes` will enforce the `static getter` rule.
180+
* `contextTypes`, `contextType`, and `displayName` will enforce the `static public field` rule.
181+
182+
## When Not To Use It
183+
184+
If you have no placement preference for React's static class properties.
185+

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ const allRules = {
8282
'sort-comp': require('./lib/rules/sort-comp'),
8383
'sort-prop-types': require('./lib/rules/sort-prop-types'),
8484
'state-in-constructor': require('./lib/rules/state-in-constructor'),
85+
'static-property-placement': require('./lib/rules/static-property-placement'),
8586
'style-prop-object': require('./lib/rules/style-prop-object'),
8687
'void-dom-elements-no-children': require('./lib/rules/void-dom-elements-no-children')
8788
};

lib/rules/display-name.js

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
const Components = require('../util/Components');
88
const astUtil = require('../util/ast');
99
const docsUrl = require('../util/docsUrl');
10+
const propsUtil = require('../util/props');
1011

1112
// ------------------------------------------------------------------------------
1213
// Rule Definition
@@ -38,24 +39,6 @@ module.exports = {
3839

3940
const MISSING_MESSAGE = 'Component definition is missing display name';
4041

41-
/**
42-
* Checks if we are declaring a display name
43-
* @param {ASTNode} node The AST node being checked.
44-
* @returns {Boolean} True if we are declaring a display name, false if not.
45-
*/
46-
function isDisplayNameDeclaration(node) {
47-
switch (node.type) {
48-
case 'ClassProperty':
49-
return node.key && node.key.name === 'displayName';
50-
case 'Identifier':
51-
return node.name === 'displayName';
52-
case 'Literal':
53-
return node.value === 'displayName';
54-
default:
55-
return false;
56-
}
57-
}
58-
5942
/**
6043
* Mark a prop type as declared
6144
* @param {ASTNode} node The AST node being checked.
@@ -139,14 +122,14 @@ module.exports = {
139122
return {
140123

141124
ClassProperty: function(node) {
142-
if (!isDisplayNameDeclaration(node)) {
125+
if (!propsUtil.isDisplayNameDeclaration(node)) {
143126
return;
144127
}
145128
markDisplayNameAsDeclared(node);
146129
},
147130

148131
MemberExpression: function(node) {
149-
if (!isDisplayNameDeclaration(node.property)) {
132+
if (!propsUtil.isDisplayNameDeclaration(node.property)) {
150133
return;
151134
}
152135
const component = utils.getRelatedComponent(node);
@@ -184,7 +167,7 @@ module.exports = {
184167
},
185168

186169
MethodDefinition: function(node) {
187-
if (!isDisplayNameDeclaration(node.key)) {
170+
if (!propsUtil.isDisplayNameDeclaration(node.key)) {
188171
return;
189172
}
190173
markDisplayNameAsDeclared(node);
@@ -208,7 +191,7 @@ module.exports = {
208191
if (ignoreTranspilerName || !hasTranspilerName(node)) {
209192
// Search for the displayName declaration
210193
node.properties.forEach(property => {
211-
if (!property.key || !isDisplayNameDeclaration(property.key)) {
194+
if (!property.key || !propsUtil.isDisplayNameDeclaration(property.key)) {
212195
return;
213196
}
214197
markDisplayNameAsDeclared(node);

0 commit comments

Comments
 (0)