diff --git a/.README/README.md b/.README/README.md index 24da4b5..1910678 100644 --- a/.README/README.md +++ b/.README/README.md @@ -165,6 +165,7 @@ When `true`, only checks files with a [`@flow` annotation](http://flowtype.org/d {"gitdown": "include", "file": "./rules/define-flow-type.md"} {"gitdown": "include", "file": "./rules/delimiter-dangle.md"} {"gitdown": "include", "file": "./rules/enforce-line-break.md"} +{"gitdown": "include", "file": "./rules/enforce-suppression-code.md"} {"gitdown": "include", "file": "./rules/generic-spacing.md"} {"gitdown": "include", "file": "./rules/interface-id-match.md"} {"gitdown": "include", "file": "./rules/newline-after-flow-annotation.md"} diff --git a/.README/rules/enforce-suppression-code.md b/.README/rules/enforce-suppression-code.md new file mode 100644 index 0000000..bc694c0 --- /dev/null +++ b/.README/rules/enforce-suppression-code.md @@ -0,0 +1,5 @@ +### `enforce-suppression-code` + +This rule enforces a suppression code on flow suppression comments such as `$FlowFixMe` and `$FlowExpectedError`. + + diff --git a/src/index.js b/src/index.js index a6e5746..670b34a 100644 --- a/src/index.js +++ b/src/index.js @@ -7,6 +7,7 @@ import booleanStyle from './rules/booleanStyle'; import defineFlowType from './rules/defineFlowType'; import delimiterDangle from './rules/delimiterDangle'; import enforceLineBreak from './rules/enforceLineBreak'; +import enforceSuppressionCode from './rules/enforceSuppressionCode'; import genericSpacing from './rules/genericSpacing'; import interfaceIdMatch from './rules/interfaceIdMatch'; import newlineAfterFlowAnnotation from './rules/newlineAfterFlowAnnotation'; @@ -59,6 +60,7 @@ const rules = { 'define-flow-type': defineFlowType, 'delimiter-dangle': delimiterDangle, 'enforce-line-break': enforceLineBreak, + 'enforce-suppression-code': enforceSuppressionCode, 'generic-spacing': genericSpacing, 'interface-id-match': interfaceIdMatch, 'newline-after-flow-annotation': newlineAfterFlowAnnotation, diff --git a/src/rules/enforceSuppressionCode.js b/src/rules/enforceSuppressionCode.js new file mode 100644 index 0000000..fd8fb84 --- /dev/null +++ b/src/rules/enforceSuppressionCode.js @@ -0,0 +1,57 @@ +const schema = [ + { + type: 'string', + }, +]; + +const message = (suppression = '') => { + return `${suppression} is missing a suppression error code. Please update this suppression to use an error code: ${suppression}[…]`; +}; + +const create = (context) => { + const isMissingSuppressionCode = function (value) { + const suppressionTypes = ['$FlowFixMe', '$FlowExpectedError', '$FlowIssue', '$FlowIgnore']; + + let failedType; + suppressionTypes.forEach((cur) => { + if (value.startsWith(cur) && + !value.startsWith(`${cur}[`) && + !value.endsWith(']')) { + failedType = cur; + } + }); + + return failedType; + }; + + const handleComment = function (comment) { + const value = comment.type === 'Block' ? + comment.value.replace(/\*/g, '') : + comment.value; + const suppression = value.trim().split(' ').filter((arg) => { + return arg.length > 0; + })[0]; + const failedType = isMissingSuppressionCode(suppression); + + if (failedType) { + context.report(comment, message(failedType)); + } + }; + + return { + Program () { + context + .getSourceCode() + .getAllComments() + .filter((comment) => { + return comment.type === 'Block' || comment.type === 'Line'; + }) + .forEach(handleComment); + }, + }; +}; + +export default { + create, + schema, +}; diff --git a/tests/rules/assertions/enforceSuppressionCode.js b/tests/rules/assertions/enforceSuppressionCode.js new file mode 100644 index 0000000..3f64f4d --- /dev/null +++ b/tests/rules/assertions/enforceSuppressionCode.js @@ -0,0 +1,90 @@ +export default { + invalid: [ + { + code: '// $FlowFixMe I am doing something evil here\nconst text = \'HELLO\';', + errors: [ + { + message: '$FlowFixMe is missing a suppression error code. Please update this suppression to use an error code: $FlowFixMe[…]', + }, + ], + }, + { + code: '// $FlowExpectedError I am doing something evil here\nconst text = \'HELLO\';', + errors: [ + { + message: '$FlowExpectedError is missing a suppression error code. Please update this suppression to use an error code: $FlowExpectedError[…]', + }, + ], + }, + { + code: '// $FlowIssue I am doing something evil here\nconst text = \'HELLO\';', + errors: [ + { + message: '$FlowIssue is missing a suppression error code. Please update this suppression to use an error code: $FlowIssue[…]', + }, + ], + }, + { + code: '// $FlowIgnore I am doing something evil here\nconst text = \'HELLO\';', + errors: [ + { + message: '$FlowIgnore is missing a suppression error code. Please update this suppression to use an error code: $FlowIgnore[…]', + }, + ], + }, + { + code: '/* $FlowIgnore I am doing something evil here */', + errors: [ + { + message: '$FlowIgnore is missing a suppression error code. Please update this suppression to use an error code: $FlowIgnore[…]', + }, + ], + }, + { + code: '{ /* $FlowIgnore I am doing something evil here */ }', + errors: [ + { + message: '$FlowIgnore is missing a suppression error code. Please update this suppression to use an error code: $FlowIgnore[…]', + }, + ], + }, + { + code: `/** + * $FlowIgnore I am doing something evil here + */`, + errors: [ + { + message: '$FlowIgnore is missing a suppression error code. Please update this suppression to use an error code: $FlowIgnore[…]', + }, + ], + }, + ], + valid: [ + { + code: 'const text = \'HELLO\';', + }, + { + code: '// $FlowFixMe[incompatible-call] TODO 48\nconst text = \'HELLO\';', + }, + { + code: '// $FlowExpectedError[incompatible-call] TODO 48\nconst text = \'HELLO\';', + }, + { + code: '// $FlowIssue[incompatible-call] TODO 48\nconst text = \'HELLO\';', + }, + { + code: '// $FlowIgnore[incompatible-call] TODO 48\nconst text = \'HELLO\';', + }, + { + code: '/* $FlowIgnore[incompatible-call] TODO 48 */', + }, + { + code: `/** + * $FlowIgnore[incompatible-call] TODO 48 + */`, + }, + { + code: '/* $FlowIgnore[incompatible-call] TODO 48 */', + }, + ], +}; diff --git a/tests/rules/index.js b/tests/rules/index.js index 87d0a7a..7a8efda 100644 --- a/tests/rules/index.js +++ b/tests/rules/index.js @@ -18,6 +18,7 @@ const reportingRules = [ 'define-flow-type', 'delimiter-dangle', 'enforce-line-break', + 'enforce-suppression-code', 'generic-spacing', 'interface-id-match', 'newline-after-flow-annotation',