diff --git a/src/index.js b/src/index.js index 33c460dc..6b3fcb77 100644 --- a/src/index.js +++ b/src/index.js @@ -88,7 +88,7 @@ export default { recommended, }, rules: _.mapValues(rules, (rule, key) => { - if (key === 'no-types-missing-file-annotation') { + if (['no-types-missing-file-annotation', 'require-valid-file-annotation'].includes(key)) { return rule; } diff --git a/src/rules/requireValidFileAnnotation.js b/src/rules/requireValidFileAnnotation.js index 51265988..fb0f6ae7 100644 --- a/src/rules/requireValidFileAnnotation.js +++ b/src/rules/requireValidFileAnnotation.js @@ -113,7 +113,7 @@ const create = (context) => { } else { context.report(potentialFlowFileAnnotation, 'Malformed Flow file annotation.'); } - } else if (always) { + } else if (always && !_.get(context, 'settings.flowtype.onlyFilesWithFlowAnnotation')) { context.report({ fix: (fixer) => { let annotation; diff --git a/src/utilities/checkFlowFileAnnotation.js b/src/utilities/checkFlowFileAnnotation.js index 7e923b63..48a32245 100644 --- a/src/utilities/checkFlowFileAnnotation.js +++ b/src/utilities/checkFlowFileAnnotation.js @@ -1,8 +1,9 @@ import _ from 'lodash'; import isFlowFile from './isFlowFile'; +import isNoFlowFile from './isNoFlowFile'; export default (cb, context) => { - const checkThisFile = !_.get(context, 'settings.flowtype.onlyFilesWithFlowAnnotation') || isFlowFile(context); + const checkThisFile = (!_.get(context, 'settings.flowtype.onlyFilesWithFlowAnnotation') && !isNoFlowFile(context)) || isFlowFile(context); // eslint-disable-line no-extra-parens, max-len if (!checkThisFile) { return () => {}; diff --git a/src/utilities/index.js b/src/utilities/index.js index 23c7b2ec..64d80c5f 100644 --- a/src/utilities/index.js +++ b/src/utilities/index.js @@ -9,6 +9,7 @@ export {default as getParameterName} from './getParameterName'; export {default as getTokenAfterParens} from './getTokenAfterParens'; export {default as getTokenBeforeParens} from './getTokenBeforeParens'; export {default as isFlowFile} from './isFlowFile'; +export {default as isNoFlowFile} from './isNoFlowFile'; export {default as isFlowFileAnnotation} from './isFlowFileAnnotation'; export {default as iterateFunctionNodes} from './iterateFunctionNodes'; export {default as quoteName} from './quoteName'; diff --git a/src/utilities/isNoFlowFile.js b/src/utilities/isNoFlowFile.js new file mode 100644 index 00000000..16f905f6 --- /dev/null +++ b/src/utilities/isNoFlowFile.js @@ -0,0 +1,21 @@ +import isNoFlowFileAnnotation from './isNoFlowFileAnnotation'; + +/** + * Checks whether a file has an @flow or @noflow annotation. + * + * @param context + * @param [strict] - By default, the function returns true if the file starts with @flow but not if it + * starts by @noflow. When the strict flag is set to false, the function returns true if the flag has @noflow also. + */ + +export default (context, strict = true) => { + const comments = context.getAllComments(); + + if (!comments.length) { + return false; + } + + return comments.some((comment) => { + return isNoFlowFileAnnotation(comment.value, strict); + }); +}; diff --git a/src/utilities/isNoFlowFileAnnotation.js b/src/utilities/isNoFlowFileAnnotation.js new file mode 100644 index 00000000..027ba515 --- /dev/null +++ b/src/utilities/isNoFlowFileAnnotation.js @@ -0,0 +1,17 @@ +import _ from 'lodash'; + +const FLOW_MATCHER = /^@noflow$/; + +export default (comment, strict) => { + // The flow parser splits comments with the following regex to look for the @flow flag. + // See https://github.com/facebook/flow/blob/a96249b93541f2f7bfebd8d62085bf7a75de02f2/src/parsing/docblock.ml#L39 + return _.some(comment.split(/[\t\n\r */\\]+/), (commentPart) => { + const match = commentPart.match(FLOW_MATCHER); + + if (match === null) { + return false; + } + + return !strict || match[0] === '@noflow'; + }); +}; diff --git a/tests/rules/assertions/requireReturnType.js b/tests/rules/assertions/requireReturnType.js index 1f6a6daa..b17d2a05 100644 --- a/tests/rules/assertions/requireReturnType.js +++ b/tests/rules/assertions/requireReturnType.js @@ -38,6 +38,14 @@ export default { }, ], }, + { + code: '/* @flow */\n(foo) => { return 1; }', + errors: [ + { + message: 'Missing return type annotation.', + }, + ], + }, { code: '(foo): undefined => { return; }', errors: [ @@ -692,6 +700,12 @@ export default { }, }, }, + { + code: '/* @noflow */\n(foo) => { return 1; }', + options: [ + 'always', + ], + }, { code: '(foo) => { return undefined; }', options: [