Skip to content

Commit ad4cf82

Browse files
danwanggajus
authored andcommitted
feat: Add newline-after-flow-annotation rule (#312)
1 parent 2008687 commit ad4cf82

6 files changed

Lines changed: 148 additions & 0 deletions

File tree

.README/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ When `true`, only checks files with a [`@flow` annotation](http://flowtype.org/d
146146
{"gitdown": "include", "file": "./rules/define-flow-type.md"}
147147
{"gitdown": "include", "file": "./rules/delimiter-dangle.md"}
148148
{"gitdown": "include", "file": "./rules/generic-spacing.md"}
149+
{"gitdown": "include", "file": "./rules/newline-after-flow-annotation"}
149150
{"gitdown": "include", "file": "./rules/no-dupe-keys.md"}
150151
{"gitdown": "include", "file": "./rules/no-flow-fix-me-comments.md"}
151152
{"gitdown": "include", "file": "./rules/no-mutable-array.md"}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
### `newline-after-flow-annotation`
2+
3+
This rule requires an empty line after the Flow annotation.
4+
5+
#### Options
6+
7+
The rule has a string option:
8+
9+
* `"always"` (default): Enforces that `@flow` annotations be followed by an empty line, separated by newline (LF)
10+
* `"always-windows"`: Identical to "always", but will use a CRLF when autofixing
11+
* `"never"`: Enforces that `@flow` annotations are not followed by empty lines
12+
13+
```js
14+
{
15+
"rules": {
16+
"flowtype/newline-after-flow-annotation": [
17+
2,
18+
"always"
19+
]
20+
}
21+
}
22+
```
23+
24+
25+
<!-- assertions newlineAfterFlowAnnotation -->

src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import booleanStyle from './rules/booleanStyle';
44
import defineFlowType from './rules/defineFlowType';
55
import delimiterDangle from './rules/delimiterDangle';
66
import genericSpacing from './rules/genericSpacing';
7+
import newlineAfterFlowAnnotation from './rules/newlineAfterFlowAnnotation';
78
import noDupeKeys from './rules/noDupeKeys';
89
import noFlowFixMeComments from './rules/noFlowFixMeComments';
910
import noMutableArray from './rules/noMutableArray';
@@ -33,6 +34,7 @@ const rules = {
3334
'define-flow-type': defineFlowType,
3435
'delimiter-dangle': delimiterDangle,
3536
'generic-spacing': genericSpacing,
37+
'newline-after-flow-annotation': newlineAfterFlowAnnotation,
3638
'no-dupe-keys': noDupeKeys,
3739
'no-flow-fix-me-comments': noFlowFixMeComments,
3840
'no-mutable-array': noMutableArray,
@@ -76,6 +78,7 @@ export default {
7678
'define-flow-type': 0,
7779
'delimiter-dangle': 0,
7880
'generic-spacing': 0,
81+
'newline-after-flow-annotation': 0,
7982
'no-dupe-keys': 0,
8083
'no-flow-fix-me-comments': 0,
8184
'no-mutable-array': 0,
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import _ from 'lodash';
2+
3+
const looksLikeFlowFileAnnotation = (comment) => {
4+
return /@(?:no)?flo/i.test(comment);
5+
};
6+
7+
const schema = [
8+
{
9+
enum: ['always', 'always-windows', 'never'],
10+
type: 'string'
11+
}
12+
];
13+
14+
const create = (context) => {
15+
const mode = context.options[0];
16+
const never = mode === 'never';
17+
18+
const newline = mode === 'always-windows' ? '\r\n' : '\n';
19+
20+
return {
21+
Program (node) {
22+
const sourceCode = context.getSourceCode();
23+
24+
const potentialFlowFileAnnotation = _.find(
25+
context.getAllComments(),
26+
(comment) => {
27+
return looksLikeFlowFileAnnotation(comment.value);
28+
}
29+
);
30+
31+
if (potentialFlowFileAnnotation) {
32+
const line = potentialFlowFileAnnotation.loc.end.line;
33+
const nextLineIsEmpty = sourceCode.lines[line] === '';
34+
35+
if (!never && !nextLineIsEmpty) {
36+
context.report({
37+
fix: (fixer) => {
38+
return fixer.insertTextAfter(
39+
potentialFlowFileAnnotation,
40+
newline
41+
);
42+
},
43+
message: 'Expected newline after flow annotation',
44+
node
45+
});
46+
}
47+
48+
if (never && nextLineIsEmpty) {
49+
context.report({
50+
fix: (fixer) => {
51+
const lineBreak = sourceCode.text[potentialFlowFileAnnotation.end];
52+
53+
return fixer.replaceTextRange(
54+
[
55+
potentialFlowFileAnnotation.end,
56+
potentialFlowFileAnnotation.end + (
57+
lineBreak === '\r' ? 2 : 1
58+
)
59+
],
60+
''
61+
);
62+
},
63+
message: 'Expected no newline after flow annotation',
64+
node
65+
});
66+
}
67+
}
68+
}
69+
};
70+
};
71+
72+
export default {
73+
create,
74+
schema
75+
};
76+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
export default {
2+
invalid: [
3+
{
4+
code: '// @flow\nimport Foo from \'./foo\';',
5+
errors: [{message: 'Expected newline after flow annotation'}],
6+
output: '// @flow\n\nimport Foo from \'./foo\';'
7+
},
8+
{
9+
code: '// @flow\nimport Foo from \'./foo\';',
10+
errors: [{message: 'Expected newline after flow annotation'}],
11+
options: ['always'],
12+
output: '// @flow\n\nimport Foo from \'./foo\';'
13+
},
14+
{
15+
code: '// @flow\r\nimport Foo from \'./foo\';',
16+
errors: [{message: 'Expected newline after flow annotation'}],
17+
options: ['always-windows'],
18+
output: '// @flow\r\n\r\nimport Foo from \'./foo\';'
19+
},
20+
{
21+
code: '// @flow\n\n',
22+
errors: [{message: 'Expected no newline after flow annotation'}],
23+
options: ['never'],
24+
output: '// @flow\n'
25+
}
26+
],
27+
valid: [
28+
{
29+
code: '// @flow\n\nimport Foo from \'./foo\';',
30+
options: ['always']
31+
},
32+
{
33+
code: '// @flow\r\n\r\nimport Foo from \'./foo\';',
34+
options: ['always-windows']
35+
},
36+
{
37+
code: '// @flow\nimport Foo from \'./foo\';',
38+
options: ['never']
39+
}
40+
]
41+
};
42+

tests/rules/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const reportingRules = [
1515
'define-flow-type',
1616
'delimiter-dangle',
1717
'generic-spacing',
18+
'newline-after-flow-annotation',
1819
'no-dupe-keys',
1920
'no-flow-fix-me-comments',
2021
'no-mutable-array',

0 commit comments

Comments
 (0)