Skip to content

Commit

Permalink
Merge pull request #130 from aaron-harvey/feature/file-annotation-spe…
Browse files Browse the repository at this point in the history
…ll-check

fix: annotation spell check, better @noflow support (fixes #106)
  • Loading branch information
danharper committed Nov 27, 2016
2 parents 35b18b5 + 0fe502b commit 6157b08
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 24 deletions.
34 changes: 17 additions & 17 deletions src/rules/requireValidFileAnnotation.js
@@ -1,15 +1,15 @@
import _ from 'lodash';
import {
isFlowFile,
isFlowFileAnnotation
isFlowFileAnnotation,
fuzzyStringMatch
} from './../utilities';

const defaults = {
annotationStyle: 'none'
};

const looksLikeFlowFileAnnotation = (comment) => {
return /@(?:no)?flow/i.test(comment);
return /@(?:no)?f/i.test(comment);
};

const isValidAnnotationStyle = (node, style) => {
Expand All @@ -20,20 +20,18 @@ const isValidAnnotationStyle = (node, style) => {
return style === node.type.toLowerCase();
};

const checkAnnotationSpelling = (comment) => {
return /@[a-z]+\b/.test(comment) && fuzzyStringMatch(comment.replace(/no/i, ''), '@flow', 0.20);
};

export const schema = [
{
enum: ['always']
}
];

export default (context) => {
const checkThisFile = !_.get(context, 'settings.flowtype.onlyFilesWithFlowAnnotation') || isFlowFile(context);

if (!checkThisFile) {
return {};
}

const always = context.options[0] === 'always';
const always = context.options[0] === 'always' && !_.get(context, 'settings.flowtype.onlyFilesWithFlowAnnotation', false);
const style = _.get(context, 'options[1].annotationStyle', defaults.annotationStyle);

return {
Expand All @@ -49,14 +47,16 @@ export default (context) => {
context.report(potentialFlowFileAnnotation, 'Flow file annotation not at the top of the file.');
}

if (!isFlowFileAnnotation(potentialFlowFileAnnotation.value)) {
context.report(potentialFlowFileAnnotation, 'Malformed flow file annotation.');
}

if (!isValidAnnotationStyle(potentialFlowFileAnnotation, style)) {
const str = style === 'line' ? '`// @flow`' : '`/* @flow */`';
if (isFlowFileAnnotation(potentialFlowFileAnnotation.value.trim())) {
if (!isValidAnnotationStyle(potentialFlowFileAnnotation, style)) {
const str = style === 'line' ? '`// ' + potentialFlowFileAnnotation.value.trim() + '`' : '`/* ' + potentialFlowFileAnnotation.value.trim() + ' */`';

context.report(potentialFlowFileAnnotation, 'Flow file annotation style must be ' + str);
context.report(potentialFlowFileAnnotation, 'Flow file annotation style must be ' + str);
}
} else if (checkAnnotationSpelling(potentialFlowFileAnnotation.value.trim())) {
context.report(potentialFlowFileAnnotation, 'Misspelled or malformed Flow file annotation.');
} else {
context.report(potentialFlowFileAnnotation, 'Malformed Flow file annotation.');
}
} else if (always) {
context.report(node, 'Flow file annotation is missing.');
Expand Down
47 changes: 47 additions & 0 deletions src/utilities/fuzzyStringMatch.js
@@ -0,0 +1,47 @@

import _ from 'lodash';
/**
* Creates an array of letter pairs from a given an array
* https://github.com/d3/d3-array/blob/master/src/pairs.js
*
* @param {any} array
* @returns array
*/
/* eslint-disable */
function d3ArrayPairs (array) {
var i = 0, n = array.length - 1, p = array[0], pairs = new Array(n < 0 ? 0 : n);
while (i < n) pairs[i] = [p, p = array[++i]];
return pairs;
};
/* eslint-enable */

export default (needle, haystack, weight = 0.5) => {
// Based on http://stackoverflow.com/a/23305385

const stringSimilarity = (str1, str2) => {
if (str1.length > 0 && str2.length > 0) {
const pairs1 = d3ArrayPairs(str1);
const pairs2 = d3ArrayPairs(str2);
const unionLen = pairs1.length + pairs2.length;
let hitCount;

hitCount = 0;

_.forIn(pairs1, (val1) => {
_.forIn(pairs2, (val2) => {
if (_.isEqual(val1, val2)) {
hitCount++;
}
});
});

if (hitCount > 0) {
return 2.0 * hitCount / unionLen;
}
}

return 0.0;
};

return stringSimilarity(needle, haystack) >= Number(weight);
};
1 change: 1 addition & 0 deletions src/utilities/index.js
Expand Up @@ -8,3 +8,4 @@ export * as spacingFixers from './spacingFixers';
export quoteName from './quoteName';
export getTokenBeforeParens from './getTokenBeforeParens';
export getTokenAfterParens from './getTokenAfterParens';
export fuzzyStringMatch from './fuzzyStringMatch';
2 changes: 1 addition & 1 deletion src/utilities/isFlowFile.js
Expand Up @@ -9,5 +9,5 @@ export default (context) => {

const firstComment = comments[0];

return isFlowFileAnnotation(firstComment.value);
return isFlowFileAnnotation(firstComment.value) && !/no/.test(firstComment.value);
};
48 changes: 42 additions & 6 deletions tests/rules/assertions/requireValidFileAnnotation.js
Expand Up @@ -20,31 +20,39 @@ export default {
code: '// @Flow',
errors: [
{
message: 'Malformed flow file annotation.'
message: 'Malformed Flow file annotation.'
}
]
},
{
code: '// @floweeeeeee',
code: '// @NoFlow',
errors: [
{
message: 'Malformed flow file annotation.'
message: 'Malformed Flow file annotation.'
}
]
},
{
code: '// @NoFlow',
code: '// @Noflow',
errors: [
{
message: 'Malformed flow file annotation.'
message: 'Malformed Flow file annotation.'
}
]
},
{
code: '// @floweeeeeee',
errors: [
{
message: 'Misspelled or malformed Flow file annotation.'
}
]
},
{
code: '// @nofloweeeeeee',
errors: [
{
message: 'Malformed flow file annotation.'
message: 'Misspelled or malformed Flow file annotation.'
}
]
},
Expand Down Expand Up @@ -86,6 +94,34 @@ export default {
annotationStyle: 'block'
}
]
},
{
code: '/* @noflow */',
errors: [
{
message: 'Flow file annotation style must be `// @noflow`'
}
],
options: [
'always',
{
annotationStyle: 'line'
}
]
},
{
code: '// @noflow',
errors: [
{
message: 'Flow file annotation style must be `/* @noflow */`'
}
],
options: [
'always',
{
annotationStyle: 'block'
}
]
}
],
valid: [
Expand Down

0 comments on commit 6157b08

Please sign in to comment.