Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix some issues with the sortKeys autoFix #280

Merged
merged 3 commits into from
Oct 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .README/rules/sort-keys.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
### `sort-keys`

_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.
Expand Down
63 changes: 40 additions & 23 deletions src/rules/sortKeys.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,54 @@ const isValidOrders = {
}
};

const variances = {
minus: '-',
plus: '+'
};

const generateOrderedList = (context, sort, properties) => {
return properties.map((property) => {
const name = getParameterName(property, context);
const value = context.getSourceCode().getText(property.value);
let value;

return [name + (property.optional ? '?' : ''), value];
if (property.value.type === 'ObjectTypeAnnotation') {
value = generateFix(property.value, context, sort); // eslint-disable-line no-use-before-define
} else {
value = context.getSourceCode().getText(property.value);
}

return [(variances[property.variance] || '') + name + (property.optional ? '?' : ''), value];
})
.sort((first, second) => { return sort(first[0], second[0]) ? -1 : 1; })
.map((item) => { return item[0] + ': ' + item[1]; });
};

const generateFix = (node, context, sort) => {
// this could be done much more cleanly in ESLint >=4
// as we can apply multiple fixes. That also means we can
// maintain code style in a much nicer way
let nodeText;
const newTypes = generateOrderedList(context, sort, node.properties);
const source = context.getSourceCode(node);

const originalSubstring = source.getText(node);

nodeText = originalSubstring;

node.properties.forEach((property, index) => {
const subString = source.getText(property);
const addComma = subString[subString.length - 1] === ',';

nodeText = nodeText.replace(subString, '$' + index + (addComma ? ',' : ''));
});

newTypes.forEach((item, index) => {
nodeText = nodeText.replace('$' + index, item);
});

return nodeText;
};

const create = (context) => {
const order = _.get(context, ['options', 0], 'asc');
const {natural, caseSensitive} = _.get(context, ['options', 1], defaults);
Expand Down Expand Up @@ -108,27 +145,7 @@ const create = (context) => {
order
},
fix (fixer) {
// this could be done much more cleanly in ESLint >=4
// as we can apply multiple fixes. That also means we can
// maintain code style in a much nicer way
let nodeText;
const newTypes = generateOrderedList(context, isValidOrder, node.properties);
const source = context.getSourceCode(node);

const originalSubstring = source.getText(node);

nodeText = originalSubstring;

node.properties.forEach((property, index) => {
const subString = source.getText(property);
const addComma = subString[subString.length - 1] === ',';

nodeText = nodeText.replace(subString, '$' + index + (addComma ? ',' : ''));
});

newTypes.forEach((item, index) => {
nodeText = nodeText.replace('$' + index, item);
});
const nodeText = generateFix(node, context, isValidOrder);

return fixer.replaceText(node, nodeText);
},
Expand Down
65 changes: 65 additions & 0 deletions tests/rules/assertions/sortKeys.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,71 @@ export default {
c: number,
}
`
},
{
code: `
type FooType = {
c: {
z: number,
x: string,
y: boolean,
},
a: number | string | boolean,
b: (param: string) => number,
}
`,
errors: [
{message: 'Expected type annotations to be in ascending order. "x" should be before "z".'},
{message: 'Expected type annotations to be in ascending order. "a" should be before "c".'}
],
output: `
type FooType = {
a: number | string | boolean,
b: (param: string) => number,
c: {
x: string,
y: boolean,
z: number,
},
}
`
},
{
code: `
type FooType = {
c: {
z: {
j: string,
l: number,
k: boolean,
},
x: string,
y: boolean,
},
a: number | string | boolean,
b: (param: string) => number,
}
`,
errors: [
{message: 'Expected type annotations to be in ascending order. "k" should be before "l".'},
{message: 'Expected type annotations to be in ascending order. "x" should be before "z".'},
{message: 'Expected type annotations to be in ascending order. "a" should be before "c".'}
],
output: `
type FooType = {
a: number | string | boolean,
b: (param: string) => number,
c: {
x: string,
y: boolean,
z: {
j: string,
k: boolean,
l: number,
},
},
}
`
}
/* eslint-enable no-restricted-syntax */
],
Expand Down