Skip to content

Commit f37b1fc

Browse files
committed
Merge branch 'master' of github.com:gajus/eslint-plugin-flowtype
2 parents 1f70db2 + e0c328d commit f37b1fc

File tree

7 files changed

+141
-17
lines changed

7 files changed

+141
-17
lines changed

Diff for: .README/rules/no-mutable-array.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ General reasons for using immutable data structures:
1313
* They always have failure atomicity
1414
* They are much easier to cache
1515

16+
Note that initialization of a variable with an empty array is considered valid (e.g., `const values: Array<string> = [];`). This behavior resembles the behavior of Flow's [unsealed objects](https://flow.org/en/docs/types/objects/#toc-unsealed-objects), as it is assumed that empty array is intended to be mutated.
17+
1618
<!-- assertions noMutableArray -->

Diff for: src/rules/noFlowFixMeComments.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,24 @@ const create = (context) => {
3131
};
3232

3333
return {
34-
BlockComment: handleComment,
35-
GenericTypeAnnotation: (node) => {
34+
GenericTypeAnnotation (node) {
3635
if (isIdentifier(node.id, /\$FlowFixMe/)) {
3736
context.report({
3837
message,
3938
node: node.id
4039
});
4140
}
4241
},
43-
LineComment: handleComment
42+
43+
Program () {
44+
context
45+
.getSourceCode()
46+
.getAllComments()
47+
.filter((comment) => {
48+
return comment.type === 'Block' || comment.type === 'Line';
49+
})
50+
.forEach(handleComment);
51+
}
4452
};
4553
};
4654

Diff for: src/rules/noMutableArray.js

+39-10
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,49 @@
1+
import _ from 'lodash';
2+
13
const schema = [];
24

5+
// const x = [];
6+
const isEmptyArrayLiteral = (node) => {
7+
return _.get(node, 'init.type') === 'ArrayExpression' && _.get(node, 'init.elements.length') === 0;
8+
};
9+
10+
// const x = new Array(); const y = Array();
11+
const isEmptyArrayInstance = (node) => {
12+
if (_.get(node, 'init.type') === 'NewExpression' || _.get(node, 'init.type') === 'CallExpression') {
13+
return _.get(node, 'init.callee.name') === 'Array' && _.get(node, 'init.arguments.length') === 0;
14+
} else {
15+
return false;
16+
}
17+
};
18+
19+
const isAnnotationOfEmptyArrayInit = (node) => {
20+
if (_.has(node, 'parent.parent.parent')) {
21+
const parent = _.get(node, 'parent.parent.parent');
22+
const isVariableDeclaration = _.get(parent, 'type') === 'VariableDeclarator';
23+
24+
return isVariableDeclaration && (isEmptyArrayLiteral(parent) || isEmptyArrayInstance(parent));
25+
} else {
26+
return false;
27+
}
28+
};
29+
330
const create = (context) => {
431
return {
532
ArrayTypeAnnotation (node) {
6-
context.report({
7-
fix (fixer) {
8-
const rawElementType = context.getSourceCode().getText(node.elementType);
9-
10-
return fixer.replaceText(node, '$ReadOnlyArray<' + rawElementType + '>');
11-
},
12-
message: 'Use "$ReadOnlyArray" instead of array shorthand notation',
13-
node
14-
});
33+
if (!isAnnotationOfEmptyArrayInit(node)) {
34+
context.report({
35+
fix (fixer) {
36+
const rawElementType = context.getSourceCode().getText(node.elementType);
37+
38+
return fixer.replaceText(node, '$ReadOnlyArray<' + rawElementType + '>');
39+
},
40+
message: 'Use "$ReadOnlyArray" instead of array shorthand notation',
41+
node
42+
});
43+
}
1544
},
1645
GenericTypeAnnotation (node) {
17-
if (node.id.name === 'Array') {
46+
if (node.id.name === 'Array' && !isAnnotationOfEmptyArrayInit(node)) {
1847
context.report({
1948
fix (fixer) {
2049
return fixer.replaceText(node.id, '$ReadOnlyArray');

Diff for: src/rules/objectTypeDelimiter.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,18 @@ const create = (context) => {
2424
}
2525

2626
const requireProperPunctuation = (node) => {
27-
const tokens = context.getSourceCode().getTokens(node);
28-
const lastToken = tokens[tokens.length - 1];
27+
const sourceCode = context.getSourceCode();
28+
const tokens = sourceCode.getTokens(node);
29+
let lastToken;
30+
31+
lastToken = tokens[tokens.length - 1];
32+
if (lastToken.type !== 'Punctuator' ||
33+
!(lastToken.value === SEMICOLON.char ||
34+
lastToken.value === COMMA.char)) {
35+
const parentTokens = sourceCode.getTokens(node.parent);
36+
37+
lastToken = parentTokens[parentTokens.indexOf(lastToken) + 1];
38+
}
2939

3040
if (lastToken.type === 'Punctuator') {
3141
if (lastToken.value === BAD.char) {

Diff for: src/rules/sortKeys.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ const variances = {
7070
plus: '+'
7171
};
7272

73+
const getVariance = (node) => {
74+
if (_.isString(node.variance)) {
75+
return variances[node.variance] || '';
76+
} else if (_.get(node, 'variance.type') === 'Variance') {
77+
return variances[node.variance.kind] || '';
78+
} else {
79+
return '';
80+
}
81+
};
82+
7383
const generateOrderedList = (context, sort, properties) => {
7484
return properties.map((property) => {
7585
const name = getParameterName(property, context);
@@ -84,7 +94,7 @@ const generateOrderedList = (context, sort, properties) => {
8494
value = context.getSourceCode().getText(property.value);
8595
}
8696

87-
return [(variances[property.variance] || '') + name + (property.optional ? '?' : ''), value];
97+
return [name, getVariance(property) + name + (property.optional ? '?' : ''), value];
8898
})
8999
.sort((first, second) => {
90100
return sort(first[0], second[0]) ? -1 : 1;
@@ -94,7 +104,7 @@ const generateOrderedList = (context, sort, properties) => {
94104
return item[0];
95105
}
96106

97-
return item[0] + ': ' + item[1];
107+
return item[1] + ': ' + item[2];
98108
});
99109
};
100110

Diff for: tests/rules/assertions/noMutableArray.js

+25
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,36 @@ export default {
99
code: 'type X = string[]',
1010
errors: [{message: 'Use "$ReadOnlyArray" instead of array shorthand notation'}],
1111
output: 'type X = $ReadOnlyArray<string>'
12+
},
13+
{
14+
code: 'const values: Array<Array<string>> = [];',
15+
errors: [{message: 'Use "$ReadOnlyArray" instead of "Array"'}],
16+
output: 'const values: Array<$ReadOnlyArray<string>> = [];'
17+
},
18+
{
19+
code: 'let values: Array<Array<string>>;',
20+
errors: [
21+
{message: 'Use "$ReadOnlyArray" instead of "Array"'},
22+
{message: 'Use "$ReadOnlyArray" instead of "Array"'}
23+
],
24+
output: 'let values: $ReadOnlyArray<$ReadOnlyArray<string>>;'
1225
}
1326
],
1427
valid: [
1528
{
1629
code: 'type X = $ReadOnlyArray<string>'
30+
},
31+
{
32+
code: 'const values: Array<$ReadOnlyArray<string>> = [];'
33+
},
34+
{
35+
code: 'const values: $ReadOnlyArray<string>[] = [];'
36+
},
37+
{
38+
code: 'const values: Array<$ReadOnlyArray<string>> = new Array();'
39+
},
40+
{
41+
code: 'const values: Array<$ReadOnlyArray<string>> = Array();'
1742
}
1843
]
1944
};

Diff for: tests/rules/assertions/sortKeys.js

+40
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,46 @@ export default {
231231
},
232232
}
233233
`
234+
},
235+
{
236+
code: `
237+
type FooType = {
238+
+c: number,
239+
-b: number,
240+
a: number,
241+
}
242+
`,
243+
errors: [
244+
{message: 'Expected type annotations to be in ascending order. "b" should be before "c".'},
245+
{message: 'Expected type annotations to be in ascending order. "a" should be before "b".'}
246+
],
247+
output: `
248+
type FooType = {
249+
a: number,
250+
-b: number,
251+
+c: number,
252+
}
253+
`
254+
},
255+
{
256+
code: `
257+
type FooType = {|
258+
+c: number,
259+
-b: number,
260+
a: number,
261+
|}
262+
`,
263+
errors: [
264+
{message: 'Expected type annotations to be in ascending order. "b" should be before "c".'},
265+
{message: 'Expected type annotations to be in ascending order. "a" should be before "b".'}
266+
],
267+
output: `
268+
type FooType = {|
269+
a: number,
270+
-b: number,
271+
+c: number,
272+
|}
273+
`
234274
}
235275
/* eslint-enable no-restricted-syntax */
236276
],

0 commit comments

Comments
 (0)