Skip to content

Commit

Permalink
Merge branch 'pnevyk-feat/array-style'
Browse files Browse the repository at this point in the history
  • Loading branch information
gajus committed May 22, 2018
2 parents 8f86c4b + 1d664d7 commit 45e86d8
Show file tree
Hide file tree
Showing 12 changed files with 434 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .README/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ When `true`, only checks files with a [`@flow` annotation](http://flowtype.org/d

<!-- Rules are sorted alphabetically. -->

{"gitdown": "include", "file": "./rules/array-style-complex-type.md"}
{"gitdown": "include", "file": "./rules/array-style-simple-type.md"}
{"gitdown": "include", "file": "./rules/boolean-style.md"}
{"gitdown": "include", "file": "./rules/define-flow-type.md"}
{"gitdown": "include", "file": "./rules/delimiter-dangle.md"}
Expand Down
24 changes: 24 additions & 0 deletions .README/rules/array-style-complex-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
### `array-style-complex-type`

_The `--fix` option on the command line automatically fixes problems reported by this rule._

Enforces a particular annotation style of complex types.

Type is considered complex in these cases:

* [Maybe type](https://flow.org/en/docs/types/maybe/)
* [Function type](https://flow.org/en/docs/types/functions/)
* [Object type](https://flow.org/en/docs/types/objects/)
* [Tuple type](https://flow.org/en/docs/types/tuples/)
* [Union type](https://flow.org/en/docs/types/unions/)
* [Intersection type](https://flow.org/en/docs/types/intersections/)

This rule takes one argument.

If it is `'verbose'` then a problem is raised when using `Type[]` instead of `Array<Type>`.

If it is `'shorthand'` then a problem is raised when using `Array<Type>` instead of `Type[]`.

The default value is `'verbose'`.

<!-- assertions arrayStyleComplexType -->
25 changes: 25 additions & 0 deletions .README/rules/array-style-simple-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
### `array-style-simple-type`

_The `--fix` option on the command line automatically fixes problems reported by this rule._

Enforces a particular array type annotation style of simple types.

Type is considered simple in these cases:

* [Primitive types](https://flow.org/en/docs/types/primitives/)
* [Literal types](https://flow.org/en/docs/types/literals/)
* [Mixed type](https://flow.org/en/docs/types/mixed/)
* [Any type](https://flow.org/en/docs/types/any/)
* [Class type](https://flow.org/en/docs/types/classes/)
* [Generic type](https://flow.org/en/docs/types/generics/)
* Array type [shorthand notation](https://flow.org/en/docs/types/arrays/#toc-array-type-shorthand-syntax)

This rule takes one argument.

If it is `'verbose'` then a problem is raised when using `Type[]` instead of `Array<Type>`.

If it is `'shorthand'` then a problem is raised when using `Array<Type>` instead of `Type[]`.

The default value is `'verbose'`.

<!-- assertions arrayStyleSimpleType -->
4 changes: 4 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import _ from 'lodash';
import recommended from './configs/recommended.json';
import arrayStyleComplexType from './rules/arrayStyleComplexType';
import arrayStyleSimpleType from './rules/arrayStyleSimpleType';
import booleanStyle from './rules/booleanStyle';
import defineFlowType from './rules/defineFlowType';
import delimiterDangle from './rules/delimiterDangle';
Expand Down Expand Up @@ -33,6 +35,8 @@ import validSyntax from './rules/validSyntax';
import {checkFlowFileAnnotation} from './utilities';

const rules = {
'array-style-complex-type': arrayStyleComplexType,
'array-style-simple-type': arrayStyleSimpleType,
'boolean-style': booleanStyle,
'define-flow-type': defineFlowType,
'delimiter-dangle': delimiterDangle,
Expand Down
82 changes: 82 additions & 0 deletions src/rules/arrayStyle/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import isSimpleType from './isSimpleType';
import needWrap from './needWrap';

const schema = [
{
enum: ['verbose', 'shorthand'],
type: 'string'
}
];

const inlineType = (type) => {
const inlined = type.replace(/\s+/g, ' ');

if (inlined.length <= 50) {
return inlined;
} else {
return 'Type';
}
};

export default (defaultConfig, simpleType) => {
const create = (context) => {
const verbose = (context.options[0] || defaultConfig) === 'verbose';

return {
// shorthand
ArrayTypeAnnotation (node) {
const rawElementType = context.getSourceCode().getText(node.elementType);
const inlinedType = inlineType(rawElementType);
const wrappedInlinedType = needWrap(node.elementType) ? '(' + inlinedType + ')' : inlinedType;

if (isSimpleType(node.elementType) === simpleType && verbose) {
context.report({
data: {
type: inlinedType,
wrappedType: wrappedInlinedType
},
fix (fixer) {
return fixer.replaceText(node, 'Array<' + rawElementType + '>');
},
message: 'Use "Array<{{ type }}>", not "{{ wrappedType }}[]"',
node
});
}
},
// verbose
GenericTypeAnnotation (node) {
if (node.id.name === 'Array') {
if (node.typeParameters.params.length === 1) {
const elementTypeNode = node.typeParameters.params[0];
const rawElementType = context.getSourceCode().getText(elementTypeNode);
const inlinedType = inlineType(rawElementType);
const wrappedInlinedType = needWrap(elementTypeNode) ? '(' + inlinedType + ')' : inlinedType;

if (isSimpleType(elementTypeNode) === simpleType && !verbose) {
context.report({
data: {
type: inlinedType,
wrappedType: wrappedInlinedType
},
fix (fixer) {
if (needWrap(elementTypeNode)) {
return fixer.replaceText(node, '(' + rawElementType + ')[]');
} else {
return fixer.replaceText(node, rawElementType + '[]');
}
},
message: 'Use "{{ wrappedType }}[]", not "Array<{{ type }}>"',
node
});
}
}
}
}
};
};

return {
create,
schema
};
};
30 changes: 30 additions & 0 deletions src/rules/arrayStyle/isSimpleType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Types considered simple:
*
* - primitive types
* - literal types
* - mixed and any types
* - generic types (such as Date, Promise<string>, $Keys<T>, etc.)
* - array type written in shorthand notation
*
* Types not considered simple:
*
* - maybe type
* - function type
* - object type
* - tuple type
* - union and intersection types
*
* Reminder: if you change these semantics, don't forget to modify documentation of `array-style-...` rules
*/

const simpleTypePatterns = [
/^(?:Any|Array|Boolean|Generic|Mixed|Number|String|Void)TypeAnnotation$/,
/.+LiteralTypeAnnotation$/
];

export default (node) => {
return simpleTypePatterns.some((pattern) => {
return pattern.test(node.type);
});
};
7 changes: 7 additions & 0 deletions src/rules/arrayStyle/needWrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import isSimpleType from './isSimpleType';

const complexTypesWithoutWrap = ['TupleTypeAnnotation', 'ObjectTypeAnnotation'];

export default (node) => {
return !isSimpleType(node) && complexTypesWithoutWrap.indexOf(node.type) === -1;
};
3 changes: 3 additions & 0 deletions src/rules/arrayStyleComplexType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import makeArrayStyleRule from './arrayStyle';

export default makeArrayStyleRule('verbose', false);
3 changes: 3 additions & 0 deletions src/rules/arrayStyleSimpleType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import makeArrayStyleRule from './arrayStyle';

export default makeArrayStyleRule('verbose', true);
119 changes: 119 additions & 0 deletions tests/rules/assertions/arrayStyleComplexType.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
export default {
invalid: [
{
code: 'type X = (?string)[]',
errors: [{message: 'Use "Array<?string>", not "(?string)[]"'}],
output: 'type X = Array<?string>'
},
{
code: 'type X = (?string)[]',
errors: [{message: 'Use "Array<?string>", not "(?string)[]"'}],
options: ['verbose'],
output: 'type X = Array<?string>'
},
{
code: 'type X = Array<?string>',
errors: [{message: 'Use "(?string)[]", not "Array<?string>"'}],
options: ['shorthand'],
output: 'type X = (?string)[]'
},
{
code: 'type X = Array<{foo: string}>',
errors: [{message: 'Use "{foo: string}[]", not "Array<{foo: string}>"'}],
options: ['shorthand'],
output: 'type X = {foo: string}[]'
},
{
code: 'type X = (string | number)[]',
errors: [{message: 'Use "Array<string | number>", not "(string | number)[]"'}],
output: 'type X = Array<string | number>'
},
{
code: 'type X = (string & number)[]',
errors: [{message: 'Use "Array<string & number>", not "(string & number)[]"'}],
output: 'type X = Array<string & number>'
},
{
code: 'type X = [string, number][]',
errors: [{message: 'Use "Array<[string, number]>", not "[string, number][]"'}],
output: 'type X = Array<[string, number]>'
},
{
code: 'type X = {foo: string}[]',
errors: [{message: 'Use "Array<{foo: string}>", not "{foo: string}[]"'}],
output: 'type X = Array<{foo: string}>'
},
{
code: 'type X = (string => number)[]',
errors: [{message: 'Use "Array<string => number>", not "(string => number)[]"'}],
output: 'type X = Array<string => number>'
},
{
code: 'type X = {\n foo: string,\n bar: number\n}[]',
errors: [{message: 'Use "Array<{ foo: string, bar: number }>", not "{ foo: string, bar: number }[]"'}],
output: 'type X = Array<{\n foo: string,\n bar: number\n}>'
},
{
code: 'type X = {\n foo: string,\n bar: number,\n quo: boolean,\n hey: Date\n}[]',
errors: [{message: 'Use "Array<Type>", not "Type[]"'}],
output: 'type X = Array<{\n foo: string,\n bar: number,\n quo: boolean,\n hey: Date\n}>'
}
],
misconfigured: [
{
errors: [
{
data: 'normal',
dataPath: '[0]',
keyword: 'enum',
message: 'should be equal to one of the allowed values',
params: {
allowedValues: [
'verbose',
'shorthand'
]
},
parentSchema: {
enum: [
'verbose',
'shorthand'
],
type: 'string'
},
schema: [
'verbose',
'shorthand'
],
schemaPath: '#/items/0/enum'
}
],
options: ['normal']
}
],
valid: [
{
code: 'type X = Array<?string>'
},
{
code: 'type X = Array<?string>',
options: ['verbose']
},
{
code: 'type X = (?string)[]',
options: ['shorthand']
},
{
code: 'type X = Array<string>',
options: ['shorthand']
},
{
code: 'type X = Array<?string>',
options: ['shorthand'],
settings: {
flowtype: {
onlyFilesWithFlowAnnotation: true
}
}
}
]
};

0 comments on commit 45e86d8

Please sign in to comment.