diff --git a/.README/README.md b/.README/README.md index 1a54cbec..2fb65f19 100644 --- a/.README/README.md +++ b/.README/README.md @@ -168,6 +168,7 @@ When `true`, only checks files with a [`@flow` annotation](http://flowtype.org/d {"gitdown": "include", "file": "./rules/space-before-generic-bracket.md"} {"gitdown": "include", "file": "./rules/space-before-type-colon.md"} {"gitdown": "include", "file": "./rules/type-id-match.md"} +{"gitdown": "include", "file": "./rules/type-import-style.md"} {"gitdown": "include", "file": "./rules/union-intersection-spacing.md"} {"gitdown": "include", "file": "./rules/use-flow-type.md"} {"gitdown": "include", "file": "./rules/valid-syntax.md"} diff --git a/.README/rules/type-import-style.md b/.README/rules/type-import-style.md new file mode 100644 index 00000000..1e61dfda --- /dev/null +++ b/.README/rules/type-import-style.md @@ -0,0 +1,22 @@ +### `type-import-style` + +_The `--fix` option on the command line automatically fixes problems reported by this rule._ + +Enforces a particular style for type imports: + +``` +// 'identifier' style +import {type T, type U, type V} from '...'; + +// 'declaration' style +import type {T, U, V} from '...'; +``` + +The rule has a string option: + +* `"identifier"` (default): Enforces that type imports are all in the + 'identifier' style. +* `"declaration"`: Enforces that type imports are all in the 'declaration' + style. + + diff --git a/src/index.js b/src/index.js index 554bde4a..381e818f 100644 --- a/src/index.js +++ b/src/index.js @@ -26,6 +26,7 @@ import spaceAfterTypeColon from './rules/spaceAfterTypeColon'; import spaceBeforeGenericBracket from './rules/spaceBeforeGenericBracket'; import spaceBeforeTypeColon from './rules/spaceBeforeTypeColon'; import typeIdMatch from './rules/typeIdMatch'; +import typeImportStyle from './rules/typeImportStyle'; import unionIntersectionSpacing from './rules/unionIntersectionSpacing'; import useFlowType from './rules/useFlowType'; import validSyntax from './rules/validSyntax'; @@ -58,6 +59,7 @@ const rules = { 'space-before-generic-bracket': spaceBeforeGenericBracket, 'space-before-type-colon': spaceBeforeTypeColon, 'type-id-match': typeIdMatch, + 'type-import-style': typeImportStyle, 'union-intersection-spacing': unionIntersectionSpacing, 'use-flow-type': useFlowType, 'valid-syntax': validSyntax @@ -98,6 +100,7 @@ export default { 'space-before-generic-bracket': 0, 'space-before-type-colon': 0, 'type-id-match': 0, + 'type-import-style': 0, 'union-intersection-spacing': 0, 'use-flow-type': 0, 'valid-syntax': 0 diff --git a/src/rules/typeImportStyle.js b/src/rules/typeImportStyle.js new file mode 100644 index 00000000..1d14eaa4 --- /dev/null +++ b/src/rules/typeImportStyle.js @@ -0,0 +1,53 @@ +const schema = [ + { + enum: ['declaration', 'identifier'], + type: 'string' + } +]; + +const create = (context) => { + if (context.options[0] === 'declaration') { + return { + ImportDeclaration (node) { + if (node.importKind !== 'type') { + node.specifiers.forEach((specifier) => { + if (specifier.importKind === 'type') { + context.report({ + message: 'Unexpected type import', + node + }); + } + }); + } + } + }; + } else { + // Default to 'identifier' + return { + ImportDeclaration (node) { + if (node.importKind === 'type') { + context.report({ + fix (fixer) { + const imports = node.specifiers.map((specifier) => { + return 'type ' + specifier.local.name; + }); + const source = node.source.value; + + return fixer.replaceText(node, + 'import {' + imports.join(', ') + '} from \'' + source + '\';' + ); + }, + message: 'Unexpected "import type"', + node + }); + } + } + }; + } +}; + +export default { + create, + schema +}; + diff --git a/tests/rules/assertions/typeImportStyle.js b/tests/rules/assertions/typeImportStyle.js new file mode 100644 index 00000000..46d5e85a --- /dev/null +++ b/tests/rules/assertions/typeImportStyle.js @@ -0,0 +1,38 @@ +export default { + invalid: [ + { + code: 'import type {A, B} from \'a\';', + errors: [{message: 'Unexpected "import type"'}], + output: 'import {type A, type B} from \'a\';' + }, + { + code: 'import type {A, B} from \'a\';', + errors: [{message: 'Unexpected "import type"'}], + options: ['identifier'], + output: 'import {type A, type B} from \'a\';' + }, + { + code: 'import {type A, type B} from \'a\';', + errors: [ + {message: 'Unexpected type import'}, + {message: 'Unexpected type import'} + ], + options: ['declaration'] + } + ], + valid: [ + { + code: 'import {type A, type B} from \'a\';' + }, + { + code: 'import {type A, type B} from \'a\';', + options: ['identifier'] + }, + { + code: 'import type {A, B} from \'a\';', + options: ['declaration'] + } + ] +}; + + diff --git a/tests/rules/index.js b/tests/rules/index.js index 9b410dc1..dbc5d490 100644 --- a/tests/rules/index.js +++ b/tests/rules/index.js @@ -36,6 +36,7 @@ const reportingRules = [ 'space-before-generic-bracket', 'space-before-type-colon', 'type-id-match', + 'type-import-style', 'union-intersection-spacing', 'use-flow-type', 'valid-syntax'