Skip to content

Commit fd838c3

Browse files
authored
feat: migrate a new rule 'no-internal-flow-type' (#469)
This commit adds a new rule 'no-internal-flow-type' originally developed and used here: https://github.com/adeira/universe/blob/ce3a89d0d845a7bf5aa6086adef9493f5e0effda/src/eslint-plugin-adeira/src/rules/no-internal-flow-type.js It prevents users from using internal Flow types such as `React$Node` and suggests public Flow types such as `React.Node` instead.
1 parent e1d5d04 commit fd838c3

File tree

6 files changed

+147
-0
lines changed

6 files changed

+147
-0
lines changed

.README/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ When `true`, only checks files with a [`@flow` annotation](http://flowtype.org/d
165165
{"gitdown": "include", "file": "./rules/no-dupe-keys.md"}
166166
{"gitdown": "include", "file": "./rules/no-existential-type.md"}
167167
{"gitdown": "include", "file": "./rules/no-flow-fix-me-comments.md"}
168+
{"gitdown": "include", "file": "./rules/no-internal-flow-type.md"}
168169
{"gitdown": "include", "file": "./rules/no-mixed.md"}
169170
{"gitdown": "include", "file": "./rules/no-mutable-array.md"}
170171
{"gitdown": "include", "file": "./rules/no-primitive-constructor-types.md"}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
### `no-internal-flow-type`
2+
3+
Warns against using internal Flow types such as `React$Node`, `React$Ref` and others and suggests using public alternatives instead (`React.Node`, `React.Ref`, …).
4+
5+
<!-- assertions noInternalFlowType -->

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import noPrimitiveConstructorTypes from './rules/noPrimitiveConstructorTypes';
1616
import noTypesMissingFileAnnotation from './rules/noTypesMissingFileAnnotation';
1717
import noUnusedExpressions from './rules/noUnusedExpressions';
1818
import noWeakTypes from './rules/noWeakTypes';
19+
import noInternalFlowType from './rules/noInternalFlowType';
1920
import noMixed from './rules/noMixed';
2021
import objectTypeCurlySpacing from './rules/objectTypeCurlySpacing';
2122
import objectTypeDelimiter from './rules/objectTypeDelimiter';
@@ -55,6 +56,7 @@ const rules = {
5556
'no-dupe-keys': noDupeKeys,
5657
'no-existential-type': noExistentialType,
5758
'no-flow-fix-me-comments': noFlowFixMeComments,
59+
'no-internal-flow-type': noInternalFlowType,
5860
'no-mixed': noMixed,
5961
'no-mutable-array': noMutableArray,
6062
'no-primitive-constructor-types': noPrimitiveConstructorTypes,

src/rules/noInternalFlowType.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// We enumerate here all the React components Flow patches internally. It's because we don't want
2+
// to fail on otherwise valid type names (but rather take the actual implementation into account).
3+
// See: https://github.com/facebook/flow/blob/e23278bc17e6a0b5a2c52719d24b6bc5bb716931/src/services/code_action/insert_type_utils.ml#L607-L610
4+
const ReactComponents = [
5+
'AbstractComponent',
6+
'ChildrenArray',
7+
'ComponentType',
8+
'Config',
9+
'Context',
10+
'Element',
11+
'ElementConfig',
12+
'ElementProps',
13+
'ElementRef',
14+
'ElementType',
15+
'Key',
16+
'Node',
17+
'Portal',
18+
'Ref',
19+
'StatelessFunctionalComponent',
20+
];
21+
22+
const create = (context) => {
23+
return {
24+
Identifier (node) {
25+
const match = node.name.match(/^React\$(?<internalTypeName>.+)/);
26+
if (match !== null && match.groups !== null && match.groups !== undefined) {
27+
const {internalTypeName} = match.groups;
28+
if (ReactComponents.includes(internalTypeName)) {
29+
const validName = `React.${internalTypeName}`;
30+
context.report({
31+
data: {
32+
invalidName: node.name,
33+
validName,
34+
},
35+
message:
36+
'Type identifier \'{{invalidName}}\' is not allowed. Use \'{{validName}}\' instead.',
37+
node,
38+
});
39+
}
40+
}
41+
},
42+
};
43+
};
44+
45+
export default {
46+
create,
47+
};
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
export default {
2+
invalid: [
3+
{
4+
code: 'type X = React$AbstractComponent<Config, Instance>',
5+
errors: [
6+
'Type identifier \'React$AbstractComponent\' is not allowed. Use \'React.AbstractComponent\' instead.',
7+
],
8+
},
9+
{
10+
code: 'type X = React$ChildrenArray<string>',
11+
errors: [
12+
'Type identifier \'React$ChildrenArray\' is not allowed. Use \'React.ChildrenArray\' instead.',
13+
],
14+
},
15+
{
16+
code: 'type X = React$ComponentType<Props>',
17+
errors: [
18+
'Type identifier \'React$ComponentType\' is not allowed. Use \'React.ComponentType\' instead.',
19+
],
20+
},
21+
{
22+
code: 'type X = React$Config<Prosp, DefaultProps>',
23+
errors: ['Type identifier \'React$Config\' is not allowed. Use \'React.Config\' instead.'],
24+
},
25+
{
26+
code: 'type X = React$Element<typeof Component>',
27+
errors: ['Type identifier \'React$Element\' is not allowed. Use \'React.Element\' instead.'],
28+
},
29+
{
30+
code: 'type X = React$ElementConfig<typeof Component>',
31+
errors: [
32+
'Type identifier \'React$ElementConfig\' is not allowed. Use \'React.ElementConfig\' instead.',
33+
],
34+
},
35+
{
36+
code: 'type X = React$ElementProps<typeof Component>',
37+
errors: [
38+
'Type identifier \'React$ElementProps\' is not allowed. Use \'React.ElementProps\' instead.',
39+
],
40+
},
41+
{
42+
code: 'type X = React$ElementRef<typeof Component>',
43+
errors: [
44+
'Type identifier \'React$ElementRef\' is not allowed. Use \'React.ElementRef\' instead.',
45+
],
46+
},
47+
{
48+
code: 'type X = React$ElementType',
49+
errors: [
50+
'Type identifier \'React$ElementType\' is not allowed. Use \'React.ElementType\' instead.',
51+
],
52+
},
53+
{
54+
code: 'type X = React$Key',
55+
errors: ['Type identifier \'React$Key\' is not allowed. Use \'React.Key\' instead.'],
56+
},
57+
{
58+
code: 'type X = React$Node',
59+
errors: ['Type identifier \'React$Node\' is not allowed. Use \'React.Node\' instead.'],
60+
},
61+
{
62+
code: 'type X = React$Ref<typeof Component>',
63+
errors: ['Type identifier \'React$Ref\' is not allowed. Use \'React.Ref\' instead.'],
64+
},
65+
{
66+
code: 'type X = React$StatelessFunctionalComponent<Props>',
67+
errors: [
68+
'Type identifier \'React$StatelessFunctionalComponent\' is not allowed. Use \'React.StatelessFunctionalComponent\' instead.',
69+
],
70+
},
71+
],
72+
73+
valid: [
74+
{code: 'type X = React.AbstractComponent<Config, Instance>'},
75+
{code: 'type X = React.ChildrenArray<string>'},
76+
{code: 'type X = React.ComponentType<Props>'},
77+
{code: 'type X = React.Config<Props, DefaultProps>'},
78+
{code: 'type X = React.Element<typeof Component>'},
79+
{code: 'type X = React.ElementConfig<typeof Component>'},
80+
{code: 'type X = React.ElementProps<typeof Component>'},
81+
{code: 'type X = React.ElementRef<typeof Component>'},
82+
{code: 'type X = React.ElementType'},
83+
{code: 'type X = React.Key'},
84+
{code: 'type X = React.Node'},
85+
{code: 'type X = React.Ref<typeof Component>'},
86+
{code: 'type X = React.StatelessFunctionalComponent<Props>'},
87+
88+
// valid custom type:
89+
{code: 'type X = React$Rocks'},
90+
],
91+
};

tests/rules/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const reportingRules = [
2727
'no-types-missing-file-annotation',
2828
'no-unused-expressions',
2929
'no-weak-types',
30+
'no-internal-flow-type',
3031
'no-mixed',
3132
'object-type-curly-spacing',
3233
'object-type-delimiter',

0 commit comments

Comments
 (0)