Skip to content

Commit

Permalink
feat: migrate a new rule 'no-internal-flow-type' (#469)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mrtnzlml committed Mar 14, 2021
1 parent e1d5d04 commit fd838c3
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 0 deletions.
1 change: 1 addition & 0 deletions .README/README.md
Expand Up @@ -165,6 +165,7 @@ When `true`, only checks files with a [`@flow` annotation](http://flowtype.org/d
{"gitdown": "include", "file": "./rules/no-dupe-keys.md"}
{"gitdown": "include", "file": "./rules/no-existential-type.md"}
{"gitdown": "include", "file": "./rules/no-flow-fix-me-comments.md"}
{"gitdown": "include", "file": "./rules/no-internal-flow-type.md"}
{"gitdown": "include", "file": "./rules/no-mixed.md"}
{"gitdown": "include", "file": "./rules/no-mutable-array.md"}
{"gitdown": "include", "file": "./rules/no-primitive-constructor-types.md"}
Expand Down
5 changes: 5 additions & 0 deletions .README/rules/no-internal-flow-type.md
@@ -0,0 +1,5 @@
### `no-internal-flow-type`

Warns against using internal Flow types such as `React$Node`, `React$Ref` and others and suggests using public alternatives instead (`React.Node`, `React.Ref`, …).

<!-- assertions noInternalFlowType -->
2 changes: 2 additions & 0 deletions src/index.js
Expand Up @@ -16,6 +16,7 @@ import noPrimitiveConstructorTypes from './rules/noPrimitiveConstructorTypes';
import noTypesMissingFileAnnotation from './rules/noTypesMissingFileAnnotation';
import noUnusedExpressions from './rules/noUnusedExpressions';
import noWeakTypes from './rules/noWeakTypes';
import noInternalFlowType from './rules/noInternalFlowType';
import noMixed from './rules/noMixed';
import objectTypeCurlySpacing from './rules/objectTypeCurlySpacing';
import objectTypeDelimiter from './rules/objectTypeDelimiter';
Expand Down Expand Up @@ -55,6 +56,7 @@ const rules = {
'no-dupe-keys': noDupeKeys,
'no-existential-type': noExistentialType,
'no-flow-fix-me-comments': noFlowFixMeComments,
'no-internal-flow-type': noInternalFlowType,
'no-mixed': noMixed,
'no-mutable-array': noMutableArray,
'no-primitive-constructor-types': noPrimitiveConstructorTypes,
Expand Down
47 changes: 47 additions & 0 deletions src/rules/noInternalFlowType.js
@@ -0,0 +1,47 @@
// We enumerate here all the React components Flow patches internally. It's because we don't want
// to fail on otherwise valid type names (but rather take the actual implementation into account).
// See: https://github.com/facebook/flow/blob/e23278bc17e6a0b5a2c52719d24b6bc5bb716931/src/services/code_action/insert_type_utils.ml#L607-L610
const ReactComponents = [
'AbstractComponent',
'ChildrenArray',
'ComponentType',
'Config',
'Context',
'Element',
'ElementConfig',
'ElementProps',
'ElementRef',
'ElementType',
'Key',
'Node',
'Portal',
'Ref',
'StatelessFunctionalComponent',
];

const create = (context) => {
return {
Identifier (node) {
const match = node.name.match(/^React\$(?<internalTypeName>.+)/);
if (match !== null && match.groups !== null && match.groups !== undefined) {
const {internalTypeName} = match.groups;
if (ReactComponents.includes(internalTypeName)) {
const validName = `React.${internalTypeName}`;
context.report({
data: {
invalidName: node.name,
validName,
},
message:
'Type identifier \'{{invalidName}}\' is not allowed. Use \'{{validName}}\' instead.',
node,
});
}
}
},
};
};

export default {
create,
};
91 changes: 91 additions & 0 deletions tests/rules/assertions/noInternalFlowType.js
@@ -0,0 +1,91 @@
export default {
invalid: [
{
code: 'type X = React$AbstractComponent<Config, Instance>',
errors: [
'Type identifier \'React$AbstractComponent\' is not allowed. Use \'React.AbstractComponent\' instead.',
],
},
{
code: 'type X = React$ChildrenArray<string>',
errors: [
'Type identifier \'React$ChildrenArray\' is not allowed. Use \'React.ChildrenArray\' instead.',
],
},
{
code: 'type X = React$ComponentType<Props>',
errors: [
'Type identifier \'React$ComponentType\' is not allowed. Use \'React.ComponentType\' instead.',
],
},
{
code: 'type X = React$Config<Prosp, DefaultProps>',
errors: ['Type identifier \'React$Config\' is not allowed. Use \'React.Config\' instead.'],
},
{
code: 'type X = React$Element<typeof Component>',
errors: ['Type identifier \'React$Element\' is not allowed. Use \'React.Element\' instead.'],
},
{
code: 'type X = React$ElementConfig<typeof Component>',
errors: [
'Type identifier \'React$ElementConfig\' is not allowed. Use \'React.ElementConfig\' instead.',
],
},
{
code: 'type X = React$ElementProps<typeof Component>',
errors: [
'Type identifier \'React$ElementProps\' is not allowed. Use \'React.ElementProps\' instead.',
],
},
{
code: 'type X = React$ElementRef<typeof Component>',
errors: [
'Type identifier \'React$ElementRef\' is not allowed. Use \'React.ElementRef\' instead.',
],
},
{
code: 'type X = React$ElementType',
errors: [
'Type identifier \'React$ElementType\' is not allowed. Use \'React.ElementType\' instead.',
],
},
{
code: 'type X = React$Key',
errors: ['Type identifier \'React$Key\' is not allowed. Use \'React.Key\' instead.'],
},
{
code: 'type X = React$Node',
errors: ['Type identifier \'React$Node\' is not allowed. Use \'React.Node\' instead.'],
},
{
code: 'type X = React$Ref<typeof Component>',
errors: ['Type identifier \'React$Ref\' is not allowed. Use \'React.Ref\' instead.'],
},
{
code: 'type X = React$StatelessFunctionalComponent<Props>',
errors: [
'Type identifier \'React$StatelessFunctionalComponent\' is not allowed. Use \'React.StatelessFunctionalComponent\' instead.',
],
},
],

valid: [
{code: 'type X = React.AbstractComponent<Config, Instance>'},
{code: 'type X = React.ChildrenArray<string>'},
{code: 'type X = React.ComponentType<Props>'},
{code: 'type X = React.Config<Props, DefaultProps>'},
{code: 'type X = React.Element<typeof Component>'},
{code: 'type X = React.ElementConfig<typeof Component>'},
{code: 'type X = React.ElementProps<typeof Component>'},
{code: 'type X = React.ElementRef<typeof Component>'},
{code: 'type X = React.ElementType'},
{code: 'type X = React.Key'},
{code: 'type X = React.Node'},
{code: 'type X = React.Ref<typeof Component>'},
{code: 'type X = React.StatelessFunctionalComponent<Props>'},

// valid custom type:
{code: 'type X = React$Rocks'},
],
};
1 change: 1 addition & 0 deletions tests/rules/index.js
Expand Up @@ -27,6 +27,7 @@ const reportingRules = [
'no-types-missing-file-annotation',
'no-unused-expressions',
'no-weak-types',
'no-internal-flow-type',
'no-mixed',
'object-type-curly-spacing',
'object-type-delimiter',
Expand Down

0 comments on commit fd838c3

Please sign in to comment.