Skip to content
Permalink
Browse files
Merge branch 'MichaelDeBoey-eslint-7'
  • Loading branch information
gajus committed May 15, 2020
2 parents 58d8f91 + a1f7920 commit 0b68853ee9528f5459e1b0f0bf0b85d397fd579b
Show file tree
Hide file tree
Showing 29 changed files with 182 additions and 280 deletions.
@@ -2,9 +2,7 @@

_The `--fix` option on the command line automatically fixes problems reported by this rule._

Enforces sorting of Object annotations.

This rule mirrors ESlint's [sort-keys](http://eslint.org/docs/rules/sort-keys) rule.
Enforces natural, case-insensitive sorting of Object annotations.

#### Options

@@ -13,20 +11,12 @@ The first option specifies sort order.
* `"asc"` (default) - enforce ascending sort order.
* `"desc"` - enforce descending sort order.

The second option takes an object with two possible properties.

* `caseSensitive` - if `true`, enforce case-sensitive sort order. Default is `true`.
* `natural` - if `true`, enforce [natural sort order](https://en.wikipedia.org/wiki/Natural_sort_order). Default is `false`.

```js
{
"rules": {
"flowtype/sort-keys": [
2,
"asc", {
"caseSensitive": true,
"natural": false
}
"asc"
]
}
}
@@ -1,14 +1,14 @@
{
"plugins": [
"transform-object-rest-spread",
"@babel/plugin-proposal-object-rest-spread",
"add-module-exports"
],
"presets": [
[
"env",
"@babel/preset-env",
{
"targets": {
"node": 4
"node": 10
}
}
]
@@ -1,7 +1,11 @@
language: node_js
node_js:
- node
- 8
- 14
- 12
- 12.0
- 10
- 10.12
before_install:
- npm config set depth 0
notifications:
@@ -5,31 +5,34 @@
"url": "http://gajus.com"
},
"dependencies": {
"lodash": "^4.17.15"
"lodash": "^4.17.15",
"string-natural-compare": "^3.0.1"
},
"description": "Flowtype linting rules for ESLint.",
"devDependencies": {
"ajv": "^6.10.2",
"babel-cli": "^6.26.0",
"babel-eslint": "^10.0.2",
"@babel/cli": "^7.8.4",
"@babel/core": "^7.9.6",
"@babel/node": "^7.8.7",
"@babel/plugin-proposal-object-rest-spread": "^7.9.6",
"@babel/preset-env": "^7.9.6",
"@babel/register": "^7.9.0",
"ajv": "^6.12.2",
"babel-eslint": "^10.1.0",
"babel-plugin-add-module-exports": "^1.0.2",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-register": "^6.26.0",
"chai": "^4.2.0",
"eclint": "^2.8.1",
"eslint": "^5.13.0",
"eslint-config-canonical": "^18.1.1",
"gitdown": "^3.1.1",
"glob": "^7.1.4",
"husky": "^3.0.3",
"eslint": "^7.0.0",
"eslint-config-canonical": "^19.0.5",
"gitdown": "^3.1.3",
"glob": "^7.1.6",
"husky": "^4.2.5",
"jsonlint": "^1.6.3",
"mocha": "^6.2.0",
"rimraf": "^3.0.0",
"semantic-release": "^15.13.19"
"mocha": "^7.1.2",
"rimraf": "^3.0.2",
"semantic-release": "^17.0.7"
},
"engines": {
"node": ">=4"
"node": "^10.12.0 || >=12.0.0"
},
"husky": {
"hooks": {
@@ -45,7 +48,7 @@
"main": "./dist/index.js",
"name": "eslint-plugin-flowtype",
"peerDependencies": {
"eslint": ">=6.1.0"
"eslint": "^7.0.0"
},
"repository": {
"type": "git",
@@ -59,7 +62,7 @@
"documentation-add-assertions": "babel-node ./src/bin/addAssertions",
"format-json": "jsonlint --sort-keys --in-place --indent \" \" ./src/configs/recommended.json",
"lint": "eslint ./src ./tests",
"test": "mocha --require babel-core/register ./tests/rules/index.js"
"test": "mocha --require @babel/register ./tests/rules/index.js"
},
"version": "2.30.1"
}
@@ -61,7 +61,7 @@ const updateDocuments = (assertions) => {

documentBody = fs.readFileSync(readmeDocumentPath, 'utf8');

documentBody = documentBody.replace(/<!-- assertions ([a-z]+?) -->/ig, (assertionsBlock) => {
documentBody = documentBody.replace(/<!-- assertions ([a-z]+?) -->/gi, (assertionsBlock) => {
let exampleBody;

const ruleName = assertionsBlock.match(/assertions ([a-z]+)/i)[1];
@@ -44,7 +44,7 @@ const getDocIndexRules = () => {
const hasCorrectAssertions = (docPath, name) => {
const content = fs.readFileSync(docPath, 'utf-8');

const match = /<!-- assertions ([a-zA-Z]+) -->/.exec(content);
const match = /<!-- assertions ([A-Za-z]+) -->/.exec(content);

if (match === null) {
return false;
@@ -90,7 +90,7 @@ const checkDocs = (rulesNames) => {
throw new Error(
'Docs checker encountered an error in: ' + invalidList + '. ' +
'Make sure that for every rule you created documentation file with assertions placeholder in camelCase ' +
'and included the file path in `.README/README.md` file.'
'and included the file path in `.README/README.md` file.',
);
}
};
@@ -10,6 +10,7 @@ import {
const getTestIndexRules = () => {
const content = fs.readFileSync(path.resolve(__dirname, '../../tests/rules/index.js'), 'utf-8');

// eslint-disable-next-line unicorn/no-reduce
const result = content.split('\n').reduce((acc, line) => {
if (acc.inRulesArray) {
if (line === '];') {
@@ -58,7 +59,7 @@ const checkTests = (rulesNames) => {

throw new Error(
'Tests checker encountered an error in: ' + invalidList + '. ' +
'Make sure that for every rule you created test suite and included the rule name in `tests/rules/index.js` file.'
'Make sure that for every rule you created test suite and included the rule name in `tests/rules/index.js` file.',
);
}
};
@@ -22,7 +22,7 @@ export const getRules = () => {
export const isFile = (filepath) => {
try {
return fs.statSync(filepath).isFile();
} catch (error) {
} catch {
return false;
}
};
@@ -1,7 +1,10 @@
import isSimpleType from './isSimpleType';

const complexTypesWithoutWrap = ['TupleTypeAnnotation', 'ObjectTypeAnnotation'];
const complexTypesWithoutWrap = new Set([
'TupleTypeAnnotation',
'ObjectTypeAnnotation',
]);

export default (node) => {
return !isSimpleType(node) && !complexTypesWithoutWrap.includes(node.type);
return !isSimpleType(node) && !complexTypesWithoutWrap.has(node.type);
};
@@ -10,7 +10,7 @@ const create = (context) => {

return {
BooleanTypeAnnotation (node) {
const diff = node.end - node.start;
const diff = node.range[1] - node.range[0];

if (longForm && diff === 4) {
context.report({
@@ -18,7 +18,7 @@ const create = (context) => {
globalScope.__defineGeneric(
ident.name,
globalScope.set,
globalScope.variables
globalScope.variables,
);
const variable = globalScope.set.get(ident.name);

@@ -26,12 +26,12 @@ const create = (context) => {
const [opener, firstInnerToken] = sourceCode.getFirstTokens(types, 2);
const [lastInnerToken, closer] = sourceCode.getLastTokens(types, 2);

const spacesBefore = firstInnerToken.start - opener.end;
const spacesAfter = closer.start - lastInnerToken.end;
const spacesBefore = firstInnerToken.range[0] - opener.range[1];
const spacesAfter = closer.range[0] - lastInnerToken.range[1];

if (never) {
if (spacesBefore) {
if (sourceCode.text[opener.end] !== '\n') {
if (sourceCode.text[opener.range[1]] !== '\n') {
context.report({
data: {name: node.id.name},
fix: spacingFixers.stripSpacesAfter(opener, spacesBefore),
@@ -42,7 +42,7 @@ const create = (context) => {
}

if (spacesAfter) {
if (sourceCode.text[closer.start - 1] !== '\n') {
if (sourceCode.text[closer.range[0] - 1] !== '\n') {
context.report({
data: {name: node.id.name},
fix: spacingFixers.stripSpacesAfter(lastInnerToken, spacesAfter),
@@ -25,7 +25,7 @@ const create = (context) => {
context.getAllComments(),
(comment) => {
return looksLikeFlowFileAnnotation(comment.value);
}
},
);

if (potentialFlowFileAnnotation) {
@@ -37,7 +37,7 @@ const create = (context) => {
fix: (fixer) => {
return fixer.insertTextAfter(
potentialFlowFileAnnotation,
newline
newline,
);
},
message: 'Expected newline after flow annotation',
@@ -48,16 +48,16 @@ const create = (context) => {
if (never && nextLineIsEmpty) {
context.report({
fix: (fixer) => {
const lineBreak = sourceCode.text[potentialFlowFileAnnotation.end];
const lineBreak = sourceCode.text[potentialFlowFileAnnotation.range[1]];

return fixer.replaceTextRange(
[
potentialFlowFileAnnotation.end,
potentialFlowFileAnnotation.end + (
potentialFlowFileAnnotation.range[1],
potentialFlowFileAnnotation.range[1] + (
lineBreak === '\r' ? 2 : 1
),
],
''
'',
);
},
message: 'Expected no newline after flow annotation',
@@ -52,7 +52,7 @@ const create = (context) => {
const element = analizeElement(
property.type === 'ObjectTypeSpreadProperty' ?
property.argument :
property.value
property.value,
);

return {
@@ -62,60 +62,51 @@ const create = (context) => {
return {
Program (node) {
const firstToken = node.tokens[0];
const addAnnotation = () => {
return (fixer) => {
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);
});

if (potentialFlowFileAnnotation) {
if (firstToken && firstToken.start < potentialFlowFileAnnotation.start) {
if (firstToken && firstToken.range[0] < potentialFlowFileAnnotation.range[0]) {
context.report(potentialFlowFileAnnotation, 'Flow file annotation not at the top of the file.');
}
const annotationValue = potentialFlowFileAnnotation.value.trim();

if (isFlowFileAnnotation(annotationValue)) {
if (!isValidAnnotationStyle(potentialFlowFileAnnotation, style)) {
const annotation = style === 'line' ? '// ' + annotationValue : '/* ' + annotationValue + ' */';

context.report({
fix: (fixer) => {
return fixer.replaceTextRange(
[potentialFlowFileAnnotation.start, potentialFlowFileAnnotation.end],
annotation
[
potentialFlowFileAnnotation.range[0],
potentialFlowFileAnnotation.range[1],
],
annotation,
);
},
message: 'Flow file annotation style must be `' + annotation + '`',
node: potentialFlowFileAnnotation,
});
}
if (!noFlowAnnotation(annotationValue) && flowStrict) {
if (!isFlowStrict(annotationValue)) {
const str = style === 'line' ? '`// @flow strict`' : '`/* @flow strict */`';
context.report({
fix: addStrictAnnotation(),
message: 'Strict Flow file annotation is required, should be ' + str,
node,
});
}

if (!noFlowAnnotation(annotationValue) && flowStrict && !isFlowStrict(annotationValue)) {
const str = style === 'line' ? '`// @flow strict`' : '`/* @flow strict */`';

context.report({
fix: (fixer) => {
const annotation = ['line', 'none'].includes(style) ? '// @flow strict' : '/* @flow strict */';

return fixer.replaceTextRange([
potentialFlowFileAnnotation.range[0],
potentialFlowFileAnnotation.range[1],
], annotation);
},
message: 'Strict Flow file annotation is required, should be ' + str,
node,
});
}
} else if (checkAnnotationSpelling(annotationValue)) {
context.report(potentialFlowFileAnnotation, 'Misspelled or malformed Flow file annotation.');
@@ -124,7 +115,24 @@ const create = (context) => {
}
} else if (always) {
context.report({
fix: addAnnotation(),
fix: (fixer) => {
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.range[0],
node.range[0],
],
annotation,
);
},
message: 'Flow file annotation is missing.',
node,
});

0 comments on commit 0b68853

Please sign in to comment.