diff --git a/.README/README.md b/.README/README.md index e6c3729..ec7c293 100644 --- a/.README/README.md +++ b/.README/README.md @@ -174,6 +174,7 @@ When `true`, only checks files with a [`@flow` annotation](http://flowtype.org/d {"gitdown": "include", "file": "./rules/no-weak-types.md"} {"gitdown": "include", "file": "./rules/object-type-curly-spacing.md"} {"gitdown": "include", "file": "./rules/object-type-delimiter.md"} +{"gitdown": "include", "file": "./rules/quotes.md"} {"gitdown": "include", "file": "./rules/require-compound-type-alias.md"} {"gitdown": "include", "file": "./rules/require-exact-type.md"} {"gitdown": "include", "file": "./rules/require-indexer-name.md"} diff --git a/.README/rules/quotes.md b/.README/rules/quotes.md new file mode 100644 index 0000000..fc803fe --- /dev/null +++ b/.README/rules/quotes.md @@ -0,0 +1,12 @@ +### `quotes` + +Enforces single quotes or double quotes around string literals. + +#### Options + +The rule has string options of: + +* `"double"` (default) requires double quotes around string literals. +* `"single"` requires single quotes around string literals. + + diff --git a/README.md b/README.md index 276ceb1..aca2fd1 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ * [`no-weak-types`](#eslint-plugin-flowtype-rules-no-weak-types) * [`object-type-curly-spacing`](#eslint-plugin-flowtype-rules-object-type-curly-spacing) * [`object-type-delimiter`](#eslint-plugin-flowtype-rules-object-type-delimiter) + * [`quotes`](#eslint-plugin-flowtype-rules-quotes) * [`require-compound-type-alias`](#eslint-plugin-flowtype-rules-require-compound-type-alias) * [`require-exact-type`](#eslint-plugin-flowtype-rules-require-exact-type) * [`require-indexer-name`](#eslint-plugin-flowtype-rules-require-indexer-name) @@ -2836,12 +2837,79 @@ type Foo = { a: Foo, b: Bar } + +### quotes + +Enforces single quotes or double quotes around string literals. + + +#### Options + +The rule has string options of: + +* `"double"` (default) requires double quotes around string literals. +* `"single"` requires single quotes around string literals. + +The following patterns are considered problems: + +```js +type T = 'hi' +// Message: String literals must use double quote. + +// Options: ["double"] +type T = { test: 'hello' | 'test' } +// Message: String literals must use double quote. +// Message: String literals must use double quote. + +// Options: ["double"] +type T = { test: "hello" | 'test', t: 'hello' } +// Message: String literals must use double quote. +// Message: String literals must use double quote. + +// Options: ["single"] +type T = "hi" +// Message: String literals must use single quote. + +// Options: ["single"] +type T = { test: "hello" | "test" } +// Message: String literals must use single quote. +// Message: String literals must use single quote. + +// Options: ["single"] +type T = { test: "hello" | 'test', t: 'hello' } +// Message: String literals must use single quote. +``` + +The following patterns are not considered problems: + +```js +// Options: ["double"] +type T = "hi" + +// Options: ["double"] +type T = { test: "hello" | "test" } + +// Options: ["double"] +type T = { test: "hello" | "test", t: "hello" } + +// Options: ["single"] +type FooType = 'hi' + +// Options: ["single"] +type T = { test: 'hello' | 'test' } + +// Options: ["single"] +type T = { test: 'hello' | 'test', t: 'hello' } +``` + + + ### require-compound-type-alias 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. - + #### Options The rule has two options: @@ -2935,7 +3003,7 @@ _The `--fix` option on the command line automatically fixes problems reported by This rule enforces [exact object types](https://flow.org/en/docs/types/objects/#toc-exact-object-types). - + #### Options The rule has one string option: @@ -3088,7 +3156,7 @@ _The `--fix` option on the command line automatically fixes problems reported by This rule validates Flow object indexer name. - + #### Options The rule has a string option: @@ -3133,7 +3201,7 @@ type foo = { [string]: number }; This rule enforces explicit inexact object types. - + #### Options The rule has one string option: @@ -3242,7 +3310,7 @@ type foo = number; Requires that all function parameters have type annotations. - + #### Options You can skip all arrow functions by providing the `excludeArrowFunctions` option with `true`. @@ -3602,7 +3670,7 @@ function Foo(props: {}) { return

} Requires that functions have return type annotation. - + #### Options You can skip all arrow functions by providing the `excludeArrowFunctions` option with `true`. @@ -3964,7 +4032,7 @@ async function * foo(): AsyncIterable { yield 2; } Requires all type declarations to be at the top of the file, after any import declarations. - + #### Options The rule has a string option: @@ -4041,7 +4109,7 @@ This rule validates Flow file annotations. This rule can optionally report missing or missed placed annotations, common typos (e.g. `// @floww`), and enforce a consistent annotation style. - + #### Options The rule has a string option: @@ -4234,7 +4302,7 @@ a; Requires that all variable declarators have type annotations. - + #### Options 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 Enforces natural, case-insensitive sorting of Object annotations. - + #### Options The first option specifies sort order. @@ -4753,7 +4821,7 @@ _The `--fix` option on the command line automatically fixes problems reported by Enforces consistent spacing after the type annotation colon. - + #### Options This rule has a string argument. @@ -6122,7 +6190,7 @@ type foo = {test: number}; type bar = {...$Exact} Enforces a consistent naming pattern for type aliases. - + #### Options This rule requires a text RegExp: @@ -6183,7 +6251,7 @@ import {type T, type U, type V} from '...'; import type {T, U, V} from '...'; ``` - + #### Options The rule has a string option: diff --git a/src/index.js b/src/index.js index 06c251e..51292ea 100644 --- a/src/index.js +++ b/src/index.js @@ -20,6 +20,7 @@ import noInternalFlowType from './rules/noInternalFlowType'; import noMixed from './rules/noMixed'; import objectTypeCurlySpacing from './rules/objectTypeCurlySpacing'; import objectTypeDelimiter from './rules/objectTypeDelimiter'; +import quotes from './rules/quotes'; import requireIndexerName from './rules/requireIndexerName'; import requireCompoundTypeAlias from './rules/requireCompoundTypeAlias'; import requireInexactType from './rules/requireInexactType'; @@ -66,6 +67,7 @@ const rules = { 'no-weak-types': noWeakTypes, 'object-type-curly-spacing': objectTypeCurlySpacing, 'object-type-delimiter': objectTypeDelimiter, + quotes, 'require-compound-type-alias': requireCompoundTypeAlias, 'require-exact-type': requireExactType, 'require-indexer-name': requireIndexerName, @@ -117,6 +119,7 @@ export default { 'no-weak-types': 0, 'object-type-curly-spacing': 0, 'object-type-delimiter': 0, + quotes: 0, 'require-compound-type-alias': 0, 'require-exact-type': 0, 'require-parameter-type': 0, diff --git a/src/rules/quotes.js b/src/rules/quotes.js new file mode 100644 index 0000000..3ee2216 --- /dev/null +++ b/src/rules/quotes.js @@ -0,0 +1,51 @@ +const schema = [ + { + enum: ['double', 'single'], + type: 'string', + }, +]; + +const meta = { + fixable: 'code', +}; + +const create = (context) => { + const double = (context.options[0] || 'double') === 'double'; + const sourceCode = context.getSourceCode(); + + return { + StringLiteralTypeAnnotation (node) { + if (double && sourceCode.text[node.range[0]] !== '"') { + // double + context.report({ + fix: (fixer) => { + return [ + fixer.replaceTextRange([node.range[0], node.range[0] + 1], '"'), + fixer.replaceTextRange([node.range[1] - 1, node.range[1]], '"'), + ]; + }, + message: 'String literals must use double quote.', + node, + }); + } else if (!double && sourceCode.text[node.range[0]] !== '\'') { + // single + context.report({ + fix: (fixer) => { + return [ + fixer.replaceTextRange([node.range[0], node.range[0] + 1], '\''), + fixer.replaceTextRange([node.range[1] - 1, node.range[1]], '\''), + ]; + }, + message: 'String literals must use single quote.', + node, + }); + } + }, + }; +}; + +export default { + create, + meta, + schema, +}; diff --git a/tests/rules/assertions/quotes.js b/tests/rules/assertions/quotes.js new file mode 100644 index 0000000..49f1cb8 --- /dev/null +++ b/tests/rules/assertions/quotes.js @@ -0,0 +1,129 @@ +export default { + invalid: [ + { + code: 'type T = \'hi\'', + errors: [ + { + message: 'String literals must use double quote.', + }, + ], + output: 'type T = "hi"', + }, + { + code: 'type T = { test: \'hello\' | \'test\' }', + errors: [ + { + message: 'String literals must use double quote.', + }, + { + message: 'String literals must use double quote.', + }, + ], + options: ['double'], + output: 'type T = { test: "hello" | "test" }', + }, + { + code: 'type T = { test: "hello" | \'test\', t: \'hello\' }', + errors: [ + { + message: 'String literals must use double quote.', + }, + { + message: 'String literals must use double quote.', + }, + ], + options: ['double'], + output: 'type T = { test: "hello" | "test", t: "hello" }', + }, + { + code: 'type T = "hi"', + errors: [ + { + message: 'String literals must use single quote.', + }, + ], + options: ['single'], + output: 'type T = \'hi\'', + }, + { + code: 'type T = { test: "hello" | "test" }', + errors: [ + { + message: 'String literals must use single quote.', + }, + { + message: 'String literals must use single quote.', + }, + ], + options: ['single'], + output: 'type T = { test: \'hello\' | \'test\' }', + }, + { + code: 'type T = { test: "hello" | \'test\', t: \'hello\' }', + errors: [ + { + message: 'String literals must use single quote.', + }, + ], + options: ['single'], + output: 'type T = { test: \'hello\' | \'test\', t: \'hello\' }', + }, + ], + misconfigured: [ + { + errors: [ + { + data: 'temporarily', + dataPath: '[0]', + keyword: 'enum', + message: 'should be equal to one of the allowed values', + params: { + allowedValues: [ + 'double', + 'single', + ], + }, + parentSchema: { + enum: [ + 'double', + 'single', + ], + type: 'string', + }, + schema: [ + 'double', + 'single', + ], + schemaPath: '#/items/0/enum', + }, + ], + options: ['temporarily'], + }, + ], + valid: [ + { + code: 'type T = "hi"', + options: ['double'], + }, + { + code: 'type T = { test: "hello" | "test" }', + options: ['double'], + }, + { + code: 'type T = { test: "hello" | "test", t: "hello" }', + options: ['double'], + }, + { + code: 'type FooType = \'hi\'', + options: ['single'], + }, + { + code: 'type T = { test: \'hello\' | \'test\' }', + options: ['single'], + }, + { + code: 'type T = { test: \'hello\' | \'test\', t: \'hello\' }', + options: ['single'], + }, + ], +}; diff --git a/tests/rules/index.js b/tests/rules/index.js index 0383654..b030073 100644 --- a/tests/rules/index.js +++ b/tests/rules/index.js @@ -31,6 +31,7 @@ const reportingRules = [ 'no-mixed', 'object-type-curly-spacing', 'object-type-delimiter', + 'quotes', 'require-compound-type-alias', 'require-inexact-type', 'require-indexer-name',