From e36b920d842bc0b03e81893b66c5259e52a50c14 Mon Sep 17 00:00:00 2001 From: Clark Date: Mon, 24 Jun 2019 15:11:32 +0800 Subject: [PATCH] feat: support valid flow strict annotation (#414) * Support flow strict * Fix replacement fixer * docs: generate docs * Update document * docs: generate docs * Update README for strict flow annotation * docs: generate docs --- .../rules/require-valid-file-annotation.md | 5 ++ README.md | 26 +++++++ src/rules/requireValidFileAnnotation.js | 38 +++++++++- .../assertions/requireValidFileAnnotation.js | 71 +++++++++++++++++++ 4 files changed, 137 insertions(+), 3 deletions(-) diff --git a/.README/rules/require-valid-file-annotation.md b/.README/rules/require-valid-file-annotation.md index 02b973fe..0970af5f 100644 --- a/.README/rules/require-valid-file-annotation.md +++ b/.README/rules/require-valid-file-annotation.md @@ -18,6 +18,10 @@ This rule has an object option: * `"line"`: Require single line annotations (i.e. `// @flow`). * `"block"`: Require block annotations (i.e. `/* @flow */`). +* `"strict"` - Enforce a strict flow file annotation. + * `false` (default): strict flow annotation is not required. + * `true`: Require strict flow annotation (i.e. `// @flow strict`). + ```js { "rules": { @@ -34,6 +38,7 @@ This rule has an object option: 2, "always", { "annotationStyle": "block" + "strict": true, } ] } diff --git a/README.md b/README.md index f9575076..4ae90d17 100644 --- a/README.md +++ b/README.md @@ -3028,6 +3028,10 @@ This rule has an object option: * `"line"`: Require single line annotations (i.e. `// @flow`). * `"block"`: Require block annotations (i.e. `/* @flow */`). +* `"strict"` - Enforce a strict flow file annotation. + * `false` (default): strict flow annotation is not required. + * `true`: Require strict flow annotation (i.e. `// @flow strict`). + ```js { "rules": { @@ -3044,6 +3048,7 @@ This rule has an object option: 2, "always", { "annotationStyle": "block" + "strict": true, } ] } @@ -3087,6 +3092,14 @@ a; // @flow // Message: Flow file annotation style must be `/* @flow */` +// Options: ["always",{"annotationStyle":"block"}] +// @flow +// Message: Flow file annotation style must be `/* @flow */` + +// Options: ["always",{"annotationStyle":"line","strict":true}] +// @flow +// Message: Strict Flow file annotation is required, should be `// @flow strict` + // Options: ["always",{"annotationStyle":"line"}] /* @noflow */ // Message: Flow file annotation style must be `// @noflow` @@ -3102,6 +3115,16 @@ a; // Options: ["always",{"annotationStyle":"block"}] a; // Message: Flow file annotation is missing. + +// Options: ["always",{"annotationStyle":"line","strict":true}] +a; +// Message: Flow file annotation is missing. + +// Options: ["always",{"annotationStyle":"line","strict":true}] +// @flow +a; +b; +// Message: Strict Flow file annotation is required, should be `// @flow strict` ``` The following patterns are not considered problems: @@ -3139,6 +3162,9 @@ a; // Options: ["always",{"annotationStyle":"line"}] // @flow +// Options: ["always",{"annotationStyle":"line","strict":true}] +// @flow strict + // Options: ["never",{"annotationStyle":"none"}] // @function diff --git a/src/rules/requireValidFileAnnotation.js b/src/rules/requireValidFileAnnotation.js index 2019de0d..9ea41fff 100644 --- a/src/rules/requireValidFileAnnotation.js +++ b/src/rules/requireValidFileAnnotation.js @@ -5,7 +5,8 @@ import { } from '../utilities'; const defaults = { - annotationStyle: 'none' + annotationStyle: 'none', + strict: false }; const looksLikeFlowFileAnnotation = (comment) => { @@ -24,6 +25,10 @@ const checkAnnotationSpelling = (comment) => { return /@[a-z]+\b/.test(comment) && fuzzyStringMatch(comment.replace(/no/i, ''), '@flow', 0.2); }; +const isFlowStrict = (comment) => { + return /@flow\sstrict\b/.test(comment); +}; + const schema = [ { enum: ['always', 'never'], @@ -35,6 +40,10 @@ const schema = [ annotationStyle: { enum: ['none', 'line', 'block'], type: 'string' + }, + strict: { + enum: [true, false], + type: 'boolean' } }, type: 'object' @@ -44,19 +53,32 @@ const schema = [ const create = (context) => { const always = context.options[0] === 'always'; const style = _.get(context, 'options[1].annotationStyle', defaults.annotationStyle); + const flowStrict = _.get(context, 'options[1].strict', defaults.strict); return { Program (node) { const firstToken = node.tokens[0]; - const addAnnotation = () => { return (fixer) => { - const annotation = ['line', 'none'].includes(style) ? '// @flow\n' : '/* @flow */\n'; + let annotation; + if (flowStrict) { + annotation = ['line', 'none'].includes(style) ? '// @flow strict\n' : '/* @flow strict */\n'; + } else { + annotation = ['line', 'none'].includes(style) ? '// @flow\n' : '/* @flow */\n'; + } return fixer.replaceTextRange([node.start, node.start], annotation); }; }; + const addStrictAnnotation = () => { + return (fixer) => { + const annotation = ['line', 'none'].includes(style) ? '// @flow strict\n' : '/* @flow strict */\n'; + + return fixer.replaceTextRange([node.start, node.range[0]], annotation); + }; + }; + const potentialFlowFileAnnotation = _.find(context.getAllComments(), (comment) => { return looksLikeFlowFileAnnotation(comment.value); }); @@ -72,6 +94,16 @@ const create = (context) => { context.report(potentialFlowFileAnnotation, 'Flow file annotation style must be ' + str); } + if (flowStrict) { + if (!isFlowStrict(potentialFlowFileAnnotation.value.trim())) { + const str = style === 'line' ? '`// @flow strict`' : '`/* @flow strict */`'; + context.report({ + fix: addStrictAnnotation(), + message: 'Strict Flow file annotation is required, should be ' + str, + node + }); + } + } } else if (checkAnnotationSpelling(potentialFlowFileAnnotation.value.trim())) { context.report(potentialFlowFileAnnotation, 'Misspelled or malformed Flow file annotation.'); } else { diff --git a/tests/rules/assertions/requireValidFileAnnotation.js b/tests/rules/assertions/requireValidFileAnnotation.js index 57c5c1a3..7e575e2b 100644 --- a/tests/rules/assertions/requireValidFileAnnotation.js +++ b/tests/rules/assertions/requireValidFileAnnotation.js @@ -95,6 +95,35 @@ export default { } ] }, + { + code: '// @flow', + errors: [ + { + message: 'Flow file annotation style must be `/* @flow */`' + } + ], + options: [ + 'always', + { + annotationStyle: 'block' + } + ] + }, + { + code: '// @flow', + errors: [ + { + message: 'Strict Flow file annotation is required, should be `// @flow strict`' + } + ], + options: [ + 'always', + { + annotationStyle: 'line', + strict: true + } + ] + }, { code: '/* @noflow */', errors: [ @@ -149,6 +178,38 @@ export default { } ], output: '/* @flow */\na;' + }, + { + code: 'a;', + errors: [ + { + message: 'Flow file annotation is missing.' + } + ], + options: [ + 'always', + { + annotationStyle: 'line', + strict: true + } + ], + output: '// @flow strict\na;' + }, + { + code: '// @flow\na;\nb;', + errors: [ + { + message: 'Strict Flow file annotation is required, should be `// @flow strict`' + } + ], + options: [ + 'always', + { + annotationStyle: 'line', + strict: true + } + ], + output: '// @flow strict\na;\nb;' } ], misconfigured: [ @@ -257,6 +318,16 @@ export default { } ] }, + { + code: '// @flow strict', + options: [ + 'always', + { + annotationStyle: 'line', + strict: true + } + ] + }, { code: '// @function', options: [