Skip to content

Commit afd4210

Browse files
committedSep 29, 2017
feat: Implement array style rules
This commit adds two new rules: array-style-complex-type and array-style-simple-type. In Flow, there are two array notation styles, verbose (Array<Type>) and shorthand (Type[]). These rules enforce consistent notation. Array element types are also divided into two categories: complex and simple. This is useful for e.g. using shorthand notation for simple types and and verbose notation for complex types. What "simple" and "complex" means is explained in rules' documentation.
1 parent 1232069 commit afd4210

File tree

8 files changed

+317
-0
lines changed

8 files changed

+317
-0
lines changed
 

‎src/index.js

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import _ from 'lodash';
22
import recommended from './configs/recommended.json';
3+
import arrayStyleComplexType from './rules/arrayStyleComplexType';
4+
import arrayStyleSimpleType from './rules/arrayStyleSimpleType';
35
import booleanStyle from './rules/booleanStyle';
46
import defineFlowType from './rules/defineFlowType';
57
import delimiterDangle from './rules/delimiterDangle';
@@ -26,6 +28,8 @@ import validSyntax from './rules/validSyntax';
2628
import {checkFlowFileAnnotation} from './utilities';
2729

2830
const rules = {
31+
'array-style-complex-type': arrayStyleComplexType,
32+
'array-style-simple-type': arrayStyleSimpleType,
2933
'boolean-style': booleanStyle,
3034
'define-flow-type': defineFlowType,
3135
'delimiter-dangle': delimiterDangle,

‎src/rules/arrayStyle/index.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import isSimpleType from './isSimpleType';
2+
3+
const schema = [
4+
{
5+
enum: ['verbose', 'shorthand'],
6+
type: 'string'
7+
}
8+
];
9+
10+
export default (defaultConfig, shorthandHandler, verboseHandler) => {
11+
const create = (context) => {
12+
const verbose = (context.options[0] || defaultConfig) === 'verbose';
13+
14+
return {
15+
// shorthand
16+
ArrayTypeAnnotation (node) {
17+
shorthandHandler(isSimpleType(node.elementType), verbose, context, node);
18+
},
19+
// verbose
20+
GenericTypeAnnotation (node) {
21+
if (node.id.name === 'Array') {
22+
if (node.typeParameters.params.length === 1) {
23+
verboseHandler(isSimpleType(node.typeParameters.params[0]), verbose, context, node);
24+
}
25+
}
26+
}
27+
};
28+
};
29+
30+
return {
31+
create,
32+
schema
33+
};
34+
};

‎src/rules/arrayStyle/isSimpleType.js

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Types considered simple:
3+
*
4+
* - primitive types
5+
* - literal types
6+
* - mixed and any types
7+
* - generic types (such as Date, Promise<string>, $Keys<T>, etc.)
8+
* - array type written in shorthand notation
9+
*
10+
* Types not considered simple:
11+
*
12+
* - maybe type
13+
* - function type
14+
* - object type
15+
* - tuple type
16+
* - union and intersection types
17+
*
18+
* Reminder: if you change these semantics, don't forget to modify documentation of `array-style-...` rules
19+
*/
20+
21+
const simpleTypePatterns = [
22+
/^(?:Any|Array|Boolean|Generic|Mixed|Number|String|Void)TypeAnnotation$/,
23+
/.+LiteralTypeAnnotation$/
24+
];
25+
26+
export default (node) => {
27+
return simpleTypePatterns.some((pattern) => {
28+
return pattern.test(node.type);
29+
});
30+
};

‎src/rules/arrayStyleComplexType.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import makeArrayStyleRule from './arrayStyle';
2+
3+
const shorthandHandler = (isSimpleType, verbose, context, node) => {
4+
if (!isSimpleType && verbose) {
5+
context.report({
6+
message: 'Use "Array<ComplexType>", not "ComplexType[]"',
7+
node
8+
});
9+
}
10+
};
11+
12+
const verboseHandler = (isSimpleType, verbose, context, node) => {
13+
if (!isSimpleType && !verbose) {
14+
context.report({
15+
message: 'Use "ComplexType[]", not "Array<ComplexType>"',
16+
node
17+
});
18+
}
19+
};
20+
21+
export default makeArrayStyleRule('verbose', shorthandHandler, verboseHandler);

‎src/rules/arrayStyleSimpleType.js

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import makeArrayStyleRule from './arrayStyle';
2+
3+
const shorthandHandler = (isSimpleType, verbose, context, node) => {
4+
if (isSimpleType && verbose) {
5+
context.report({
6+
message: 'Use "Array<SimpleType>", not "SimpleType[]"',
7+
node
8+
});
9+
}
10+
};
11+
12+
const verboseHandler = (isSimpleType, verbose, context, node) => {
13+
if (isSimpleType && !verbose) {
14+
context.report({
15+
message: 'Use "SimpleType[]", not "Array<SimpleType>"',
16+
node
17+
});
18+
}
19+
};
20+
21+
export default makeArrayStyleRule('shorthand', shorthandHandler, verboseHandler);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
export default {
2+
invalid: [
3+
{
4+
code: 'type X = (?string)[]',
5+
errors: [{message: 'Use "Array<ComplexType>", not "ComplexType[]"'}]
6+
},
7+
{
8+
code: 'type X = (?string)[]',
9+
errors: [{message: 'Use "Array<ComplexType>", not "ComplexType[]"'}],
10+
options: ['verbose']
11+
},
12+
{
13+
code: 'type X = Array<?string>',
14+
errors: [{message: 'Use "ComplexType[]", not "Array<ComplexType>"'}],
15+
options: ['shorthand']
16+
},
17+
{
18+
code: 'type X = (string | number)[]',
19+
errors: [{message: 'Use "Array<ComplexType>", not "ComplexType[]"'}]
20+
},
21+
{
22+
code: 'type X = (string & number)[]',
23+
errors: [{message: 'Use "Array<ComplexType>", not "ComplexType[]"'}]
24+
},
25+
{
26+
code: 'type X = [string, number][]',
27+
errors: [{message: 'Use "Array<ComplexType>", not "ComplexType[]"'}]
28+
},
29+
{
30+
code: 'type X = ({foo: string})[]',
31+
errors: [{message: 'Use "Array<ComplexType>", not "ComplexType[]"'}]
32+
},
33+
{
34+
code: 'type X = (string => number)[]',
35+
errors: [{message: 'Use "Array<ComplexType>", not "ComplexType[]"'}]
36+
}
37+
],
38+
misconfigured: [
39+
{
40+
errors: [
41+
{
42+
data: 'normal',
43+
dataPath: '[0]',
44+
keyword: 'enum',
45+
message: 'should be equal to one of the allowed values',
46+
params: {
47+
allowedValues: [
48+
'verbose',
49+
'shorthand'
50+
]
51+
},
52+
parentSchema: {
53+
enum: [
54+
'verbose',
55+
'shorthand'
56+
],
57+
type: 'string'
58+
},
59+
schema: [
60+
'verbose',
61+
'shorthand'
62+
],
63+
schemaPath: '#/items/0/enum'
64+
}
65+
],
66+
options: ['normal']
67+
}
68+
],
69+
valid: [
70+
{
71+
code: 'type X = Array<?string>'
72+
},
73+
{
74+
code: 'type X = Array<?string>',
75+
options: ['verbose']
76+
},
77+
{
78+
code: 'type X = (?string)[]',
79+
options: ['shorthand']
80+
},
81+
{
82+
code: 'type X = Array<string>',
83+
options: ['shorthand']
84+
},
85+
{
86+
code: 'type X = Array<?string>',
87+
options: ['shorthand'],
88+
settings: {
89+
flowtype: {
90+
onlyFilesWithFlowAnnotation: true
91+
}
92+
}
93+
}
94+
]
95+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
export default {
2+
invalid: [
3+
{
4+
code: 'type X = Array<string>',
5+
errors: [{message: 'Use "SimpleType[]", not "Array<SimpleType>"'}]
6+
},
7+
{
8+
code: 'type X = string[]',
9+
errors: [{message: 'Use "Array<SimpleType>", not "SimpleType[]"'}],
10+
options: ['verbose']
11+
},
12+
{
13+
code: 'type X = Array<string>',
14+
errors: [{message: 'Use "SimpleType[]", not "Array<SimpleType>"'}],
15+
options: ['shorthand']
16+
},
17+
{
18+
code: 'type X = Array<Date>',
19+
errors: [{message: 'Use "SimpleType[]", not "Array<SimpleType>"'}]
20+
},
21+
{
22+
code: 'type X = Array<Promise<string>>',
23+
errors: [{message: 'Use "SimpleType[]", not "Array<SimpleType>"'}]
24+
},
25+
{
26+
code: 'type X = Array<$Keys<{ foo: string }>>',
27+
errors: [{message: 'Use "SimpleType[]", not "Array<SimpleType>"'}]
28+
},
29+
{
30+
code: 'type X = Array<any>',
31+
errors: [{message: 'Use "SimpleType[]", not "Array<SimpleType>"'}]
32+
},
33+
{
34+
code: 'type X = Array<mixed>',
35+
errors: [{message: 'Use "SimpleType[]", not "Array<SimpleType>"'}]
36+
},
37+
{
38+
code: 'type X = Array<void>',
39+
errors: [{message: 'Use "SimpleType[]", not "Array<SimpleType>"'}]
40+
},
41+
{
42+
code: 'type X = Array<null>',
43+
errors: [{message: 'Use "SimpleType[]", not "Array<SimpleType>"'}]
44+
},
45+
{
46+
code: 'type X = Array<string[]>',
47+
errors: [{message: 'Use "SimpleType[]", not "Array<SimpleType>"'}]
48+
}
49+
],
50+
misconfigured: [
51+
{
52+
errors: [
53+
{
54+
data: 'normal',
55+
dataPath: '[0]',
56+
keyword: 'enum',
57+
message: 'should be equal to one of the allowed values',
58+
params: {
59+
allowedValues: [
60+
'verbose',
61+
'shorthand'
62+
]
63+
},
64+
parentSchema: {
65+
enum: [
66+
'verbose',
67+
'shorthand'
68+
],
69+
type: 'string'
70+
},
71+
schema: [
72+
'verbose',
73+
'shorthand'
74+
],
75+
schemaPath: '#/items/0/enum'
76+
}
77+
],
78+
options: ['normal']
79+
}
80+
],
81+
valid: [
82+
{
83+
code: 'type X = string[]'
84+
},
85+
{
86+
code: 'type X = Array<string>',
87+
options: ['verbose']
88+
},
89+
{
90+
code: 'type X = string[]',
91+
options: ['shorthand']
92+
},
93+
{
94+
code: 'type X = string[][]'
95+
},
96+
{
97+
code: 'type X = (?string)[]',
98+
options: ['verbose']
99+
},
100+
{
101+
code: 'type X = string[]',
102+
options: ['verbose'],
103+
settings: {
104+
flowtype: {
105+
onlyFilesWithFlowAnnotation: true
106+
}
107+
}
108+
}
109+
]
110+
};

‎tests/rules/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ rules.importPlugin(plugin, 'flowtype');
1111
const ruleTester = new RuleTester();
1212

1313
const reportingRules = [
14+
'array-style-complex-type',
15+
'array-style-simple-type',
1416
'boolean-style',
1517
'define-flow-type',
1618
'delimiter-dangle',

0 commit comments

Comments
 (0)
Please sign in to comment.