Skip to content

Commit 8008f78

Browse files
ab-angelicaAngelica Bocanegra
andauthored
feat: Added rule to enforce quotes type (#475)
* feat: new quotes rule * chore: change error messaging to specifying string literals Co-authored-by: Angelica Bocanegra <[email protected]>
1 parent 998eb5a commit 8008f78

File tree

7 files changed

+278
-13
lines changed

7 files changed

+278
-13
lines changed

.README/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ When `true`, only checks files with a [`@flow` annotation](http://flowtype.org/d
174174
{"gitdown": "include", "file": "./rules/no-weak-types.md"}
175175
{"gitdown": "include", "file": "./rules/object-type-curly-spacing.md"}
176176
{"gitdown": "include", "file": "./rules/object-type-delimiter.md"}
177+
{"gitdown": "include", "file": "./rules/quotes.md"}
177178
{"gitdown": "include", "file": "./rules/require-compound-type-alias.md"}
178179
{"gitdown": "include", "file": "./rules/require-exact-type.md"}
179180
{"gitdown": "include", "file": "./rules/require-indexer-name.md"}

.README/rules/quotes.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
### `quotes`
2+
3+
Enforces single quotes or double quotes around string literals.
4+
5+
#### Options
6+
7+
The rule has string options of:
8+
9+
* `"double"` (default) requires double quotes around string literals.
10+
* `"single"` requires single quotes around string literals.
11+
12+
<!-- assertions quotes -->

README.md

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
* [`no-weak-types`](#eslint-plugin-flowtype-rules-no-weak-types)
3737
* [`object-type-curly-spacing`](#eslint-plugin-flowtype-rules-object-type-curly-spacing)
3838
* [`object-type-delimiter`](#eslint-plugin-flowtype-rules-object-type-delimiter)
39+
* [`quotes`](#eslint-plugin-flowtype-rules-quotes)
3940
* [`require-compound-type-alias`](#eslint-plugin-flowtype-rules-require-compound-type-alias)
4041
* [`require-exact-type`](#eslint-plugin-flowtype-rules-require-exact-type)
4142
* [`require-indexer-name`](#eslint-plugin-flowtype-rules-require-indexer-name)
@@ -2836,12 +2837,79 @@ type Foo = { a: Foo, b: Bar }
28362837
28372838
28382839
2840+
<a name="eslint-plugin-flowtype-rules-quotes"></a>
2841+
### <code>quotes</code>
2842+
2843+
Enforces single quotes or double quotes around string literals.
2844+
2845+
<a name="eslint-plugin-flowtype-rules-quotes-options-3"></a>
2846+
#### Options
2847+
2848+
The rule has string options of:
2849+
2850+
* `"double"` (default) requires double quotes around string literals.
2851+
* `"single"` requires single quotes around string literals.
2852+
2853+
The following patterns are considered problems:
2854+
2855+
```js
2856+
type T = 'hi'
2857+
// Message: String literals must use double quote.
2858+
2859+
// Options: ["double"]
2860+
type T = { test: 'hello' | 'test' }
2861+
// Message: String literals must use double quote.
2862+
// Message: String literals must use double quote.
2863+
2864+
// Options: ["double"]
2865+
type T = { test: "hello" | 'test', t: 'hello' }
2866+
// Message: String literals must use double quote.
2867+
// Message: String literals must use double quote.
2868+
2869+
// Options: ["single"]
2870+
type T = "hi"
2871+
// Message: String literals must use single quote.
2872+
2873+
// Options: ["single"]
2874+
type T = { test: "hello" | "test" }
2875+
// Message: String literals must use single quote.
2876+
// Message: String literals must use single quote.
2877+
2878+
// Options: ["single"]
2879+
type T = { test: "hello" | 'test', t: 'hello' }
2880+
// Message: String literals must use single quote.
2881+
```
2882+
2883+
The following patterns are not considered problems:
2884+
2885+
```js
2886+
// Options: ["double"]
2887+
type T = "hi"
2888+
2889+
// Options: ["double"]
2890+
type T = { test: "hello" | "test" }
2891+
2892+
// Options: ["double"]
2893+
type T = { test: "hello" | "test", t: "hello" }
2894+
2895+
// Options: ["single"]
2896+
type FooType = 'hi'
2897+
2898+
// Options: ["single"]
2899+
type T = { test: 'hello' | 'test' }
2900+
2901+
// Options: ["single"]
2902+
type T = { test: 'hello' | 'test', t: 'hello' }
2903+
```
2904+
2905+
2906+
28392907
<a name="eslint-plugin-flowtype-rules-require-compound-type-alias"></a>
28402908
### <code>require-compound-type-alias</code>
28412909
28422910
Requires to make a type alias for all [union](https://flow.org/en/docs/types/unions/) and [intersection](https://flow.org/en/docs/types/intersections/) types. If these are used in "raw" forms it might be tempting to just copy & paste them around the code. However, this brings sort of a source code pollution and unnecessary changes on several parts when these compound types need to be changed.
28432911
2844-
<a name="eslint-plugin-flowtype-rules-require-compound-type-alias-options-3"></a>
2912+
<a name="eslint-plugin-flowtype-rules-require-compound-type-alias-options-4"></a>
28452913
#### Options
28462914
28472915
The rule has two options:
@@ -2935,7 +3003,7 @@ _The `--fix` option on the command line automatically fixes problems reported by
29353003
29363004
This rule enforces [exact object types](https://flow.org/en/docs/types/objects/#toc-exact-object-types).
29373005
2938-
<a name="eslint-plugin-flowtype-rules-require-exact-type-options-4"></a>
3006+
<a name="eslint-plugin-flowtype-rules-require-exact-type-options-5"></a>
29393007
#### Options
29403008
29413009
The rule has one string option:
@@ -3088,7 +3156,7 @@ _The `--fix` option on the command line automatically fixes problems reported by
30883156

30893157
This rule validates Flow object indexer name.
30903158

3091-
<a name="eslint-plugin-flowtype-rules-require-indexer-name-options-5"></a>
3159+
<a name="eslint-plugin-flowtype-rules-require-indexer-name-options-6"></a>
30923160
#### Options
30933161

30943162
The rule has a string option:
@@ -3133,7 +3201,7 @@ type foo = { [string]: number };
31333201
31343202
This rule enforces explicit inexact object types.
31353203
3136-
<a name="eslint-plugin-flowtype-rules-require-inexact-type-options-6"></a>
3204+
<a name="eslint-plugin-flowtype-rules-require-inexact-type-options-7"></a>
31373205
#### Options
31383206
31393207
The rule has one string option:
@@ -3242,7 +3310,7 @@ type foo = number;
32423310
32433311
Requires that all function parameters have type annotations.
32443312
3245-
<a name="eslint-plugin-flowtype-rules-require-parameter-type-options-7"></a>
3313+
<a name="eslint-plugin-flowtype-rules-require-parameter-type-options-8"></a>
32463314
#### Options
32473315
32483316
You can skip all arrow functions by providing the `excludeArrowFunctions` option with `true`.
@@ -3602,7 +3670,7 @@ function Foo(props: {}) { return <p /> }
36023670
36033671
Requires that functions have return type annotation.
36043672
3605-
<a name="eslint-plugin-flowtype-rules-require-return-type-options-8"></a>
3673+
<a name="eslint-plugin-flowtype-rules-require-return-type-options-9"></a>
36063674
#### Options
36073675
36083676
You can skip all arrow functions by providing the `excludeArrowFunctions` option with `true`.
@@ -3964,7 +4032,7 @@ async function * foo(): AsyncIterable<number> { yield 2; }
39644032
39654033
Requires all type declarations to be at the top of the file, after any import declarations.
39664034
3967-
<a name="eslint-plugin-flowtype-rules-require-types-at-top-options-9"></a>
4035+
<a name="eslint-plugin-flowtype-rules-require-types-at-top-options-10"></a>
39684036
#### Options
39694037
39704038
The rule has a string option:
@@ -4041,7 +4109,7 @@ This rule validates Flow file annotations.
40414109
40424110
This rule can optionally report missing or missed placed annotations, common typos (e.g. `// @floww`), and enforce a consistent annotation style.
40434111

4044-
<a name="eslint-plugin-flowtype-rules-require-valid-file-annotation-options-10"></a>
4112+
<a name="eslint-plugin-flowtype-rules-require-valid-file-annotation-options-11"></a>
40454113
#### Options
40464114

40474115
The rule has a string option:
@@ -4234,7 +4302,7 @@ a;
42344302
42354303
Requires that all variable declarators have type annotations.
42364304
4237-
<a name="eslint-plugin-flowtype-rules-require-variable-type-options-11"></a>
4305+
<a name="eslint-plugin-flowtype-rules-require-variable-type-options-12"></a>
42384306
#### Options
42394307
42404308
You can exclude variables that match a certain regex by using `excludeVariableMatch`.
@@ -4401,7 +4469,7 @@ _The `--fix` option on the command line automatically fixes problems reported by
44014469

44024470
Enforces natural, case-insensitive sorting of Object annotations.
44034471

4404-
<a name="eslint-plugin-flowtype-rules-sort-keys-options-12"></a>
4472+
<a name="eslint-plugin-flowtype-rules-sort-keys-options-13"></a>
44054473
#### Options
44064474

44074475
The first option specifies sort order.
@@ -4753,7 +4821,7 @@ _The `--fix` option on the command line automatically fixes problems reported by
47534821

47544822
Enforces consistent spacing after the type annotation colon.
47554823

4756-
<a name="eslint-plugin-flowtype-rules-space-after-type-colon-options-13"></a>
4824+
<a name="eslint-plugin-flowtype-rules-space-after-type-colon-options-14"></a>
47574825
#### Options
47584826

47594827
This rule has a string argument.
@@ -6122,7 +6190,7 @@ type foo = {test: number}; type bar = {...$Exact<foo>}
61226190

61236191
Enforces a consistent naming pattern for type aliases.
61246192

6125-
<a name="eslint-plugin-flowtype-rules-type-id-match-options-14"></a>
6193+
<a name="eslint-plugin-flowtype-rules-type-id-match-options-15"></a>
61266194
#### Options
61276195

61286196
This rule requires a text RegExp:
@@ -6183,7 +6251,7 @@ import {type T, type U, type V} from '...';
61836251
import type {T, U, V} from '...';
61846252
```
61856253

6186-
<a name="eslint-plugin-flowtype-rules-type-import-style-options-15"></a>
6254+
<a name="eslint-plugin-flowtype-rules-type-import-style-options-16"></a>
61876255
#### Options
61886256

61896257
The rule has a string option:

src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import noInternalFlowType from './rules/noInternalFlowType';
2020
import noMixed from './rules/noMixed';
2121
import objectTypeCurlySpacing from './rules/objectTypeCurlySpacing';
2222
import objectTypeDelimiter from './rules/objectTypeDelimiter';
23+
import quotes from './rules/quotes';
2324
import requireIndexerName from './rules/requireIndexerName';
2425
import requireCompoundTypeAlias from './rules/requireCompoundTypeAlias';
2526
import requireInexactType from './rules/requireInexactType';
@@ -66,6 +67,7 @@ const rules = {
6667
'no-weak-types': noWeakTypes,
6768
'object-type-curly-spacing': objectTypeCurlySpacing,
6869
'object-type-delimiter': objectTypeDelimiter,
70+
quotes,
6971
'require-compound-type-alias': requireCompoundTypeAlias,
7072
'require-exact-type': requireExactType,
7173
'require-indexer-name': requireIndexerName,
@@ -117,6 +119,7 @@ export default {
117119
'no-weak-types': 0,
118120
'object-type-curly-spacing': 0,
119121
'object-type-delimiter': 0,
122+
quotes: 0,
120123
'require-compound-type-alias': 0,
121124
'require-exact-type': 0,
122125
'require-parameter-type': 0,

src/rules/quotes.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const schema = [
2+
{
3+
enum: ['double', 'single'],
4+
type: 'string',
5+
},
6+
];
7+
8+
const meta = {
9+
fixable: 'code',
10+
};
11+
12+
const create = (context) => {
13+
const double = (context.options[0] || 'double') === 'double';
14+
const sourceCode = context.getSourceCode();
15+
16+
return {
17+
StringLiteralTypeAnnotation (node) {
18+
if (double && sourceCode.text[node.range[0]] !== '"') {
19+
// double
20+
context.report({
21+
fix: (fixer) => {
22+
return [
23+
fixer.replaceTextRange([node.range[0], node.range[0] + 1], '"'),
24+
fixer.replaceTextRange([node.range[1] - 1, node.range[1]], '"'),
25+
];
26+
},
27+
message: 'String literals must use double quote.',
28+
node,
29+
});
30+
} else if (!double && sourceCode.text[node.range[0]] !== '\'') {
31+
// single
32+
context.report({
33+
fix: (fixer) => {
34+
return [
35+
fixer.replaceTextRange([node.range[0], node.range[0] + 1], '\''),
36+
fixer.replaceTextRange([node.range[1] - 1, node.range[1]], '\''),
37+
];
38+
},
39+
message: 'String literals must use single quote.',
40+
node,
41+
});
42+
}
43+
},
44+
};
45+
};
46+
47+
export default {
48+
create,
49+
meta,
50+
schema,
51+
};

0 commit comments

Comments
 (0)