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

Update: introduce minKeys option to sort-keys rule (fixes #11624) #11625

Merged
merged 2 commits into from Jun 8, 2019
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
51 changes: 49 additions & 2 deletions docs/rules/sort-keys.md
Expand Up @@ -61,7 +61,7 @@ let obj = {b: 1, ...c, a: 2};

```json
{
"sort-keys": ["error", "asc", {"caseSensitive": true, "natural": false}]
"sort-keys": ["error", "asc", {"caseSensitive": true, "natural": false, "minKeys": 2}]
}
```

Expand All @@ -70,9 +70,10 @@ The 1st option is `"asc"` or `"desc"`.
* `"asc"` (default) - enforce properties to be in ascending order.
* `"desc"` - enforce properties to be in descending order.

The 2nd option is an object which has 2 properties.
The 2nd option is an object which has 3 properties.

* `caseSensitive` - if `true`, enforce properties to be in case-sensitive order. Default is `true`.
* `minKeys` - Specifies the minimum number of keys that an object should have in order for the object's unsorted keys to produce an error. Default is `2`, which means by default all objects with unsorted keys will result in lint errors.
* `natural` - if `true`, enforce properties to be in natural order. Default is `false`. Natural Order compares strings containing combination of letters and numbers in the way a human being would sort. It basically sorts numerically, instead of sorting alphabetically. So the number 10 comes after the number 3 in Natural Sorting.

Example for a list:
Expand Down Expand Up @@ -167,6 +168,52 @@ Examples of **correct** code for the `{natural: true}` option:
let obj = {1: a, 2: b, 10: c};
```

### minKeys

Examples of **incorrect** code for the `{minKeys: 4}` option:

```js
/*eslint sort-keys: ["error", "asc", {minKeys: 4}]*/
/*eslint-env es6*/

// 4 keys
let obj = {
b: 2,
a: 1, // not sorted correctly (should be 1st key)
c: 3,
d: 4,
};

// 5 keys
let obj = {
2: 'a',
1: 'b', // not sorted correctly (should be 1st key)
3: 'c',
4: 'd',
5: 'e',
};
```

Examples of **correct** code for the `{minKeys: 4}` option:

```js
/*eslint sort-keys: ["error", "asc", {minKeys: 4}]*//
/*eslint-env es6*/

// 3 keys
let obj = {
b: 2,
a: 1,
c: 3,
};

// 2 keys
let obj = {
2: 'b',
1: 'a',
};
```

## When Not To Use It

If you don't want to notify about properties' order, then it's safe to disable this rule.
Expand Down
14 changes: 11 additions & 3 deletions lib/rules/sort-keys.js
Expand Up @@ -96,6 +96,11 @@ module.exports = {
natural: {
type: "boolean",
default: false
},
minKeys: {
type: "integer",
minimum: 2,
default: 2
}
},
additionalProperties: false
Expand All @@ -110,6 +115,7 @@ module.exports = {
const options = context.options[1];
const insensitive = options && options.caseSensitive === false;
const natual = options && options.natural;
const minKeys = options && options.minKeys;
const isValidOrder = isValidOrders[
order + (insensitive ? "I" : "") + (natual ? "N" : "")
];
Expand All @@ -118,10 +124,11 @@ module.exports = {
let stack = null;

return {
ObjectExpression() {
ObjectExpression(node) {
stack = {
upper: stack,
prevName: null
prevName: null,
numKeys: node.properties.length
};
},

Expand All @@ -141,11 +148,12 @@ module.exports = {
}

const prevName = stack.prevName;
const numKeys = stack.numKeys;
const thisName = getPropertyName(node);

stack.prevName = thisName || prevName;

if (!prevName || !thisName) {
if (!prevName || !thisName || numKeys < minKeys) {
return;
}

Expand Down
98 changes: 97 additions & 1 deletion tests/lib/rules/sort-keys.js
Expand Up @@ -65,6 +65,9 @@ ruleTester.run("sort-keys", rule, {
{ code: "var obj = {1:1, '11':2, 2:4, A:3}", options: ["asc"] },
{ code: "var obj = {'#':1, 'Z':2, À:3, è:4}", options: ["asc"] },

// asc, minKeys should ignore unsorted keys when number of keys is less than minKeys
{ code: "var obj = {a:1, c:2, b:3}", options: ["asc", { minKeys: 4 }] },

// asc, insensitive
{ code: "var obj = {_:2, a:1, b:3} // asc, insensitive", options: ["asc", { caseSensitive: false }] },
{ code: "var obj = {a:1, b:3, c:2}", options: ["asc", { caseSensitive: false }] },
Expand All @@ -75,6 +78,9 @@ ruleTester.run("sort-keys", rule, {
{ code: "var obj = {1:1, '11':2, 2:4, A:3}", options: ["asc", { caseSensitive: false }] },
{ code: "var obj = {'#':1, 'Z':2, À:3, è:4}", options: ["asc", { caseSensitive: false }] },

// asc, insensitive, minKeys should ignore unsorted keys when number of keys is less than minKeys
{ code: "var obj = {$:1, A:3, _:2, a:4}", options: ["asc", { caseSensitive: false, minKeys: 5 }] },

// asc, natural
{ code: "var obj = {_:2, a:1, b:3} // asc, natural", options: ["asc", { natural: true }] },
{ code: "var obj = {a:1, b:3, c:2}", options: ["asc", { natural: true }] },
Expand All @@ -84,6 +90,9 @@ ruleTester.run("sort-keys", rule, {
{ code: "var obj = {1:1, 2:4, '11':2, A:3}", options: ["asc", { natural: true }] },
{ code: "var obj = {'#':1, 'Z':2, À:3, è:4}", options: ["asc", { natural: true }] },

// asc, natural, minKeys should ignore unsorted keys when number of keys is less than minKeys
{ code: "var obj = {b_:1, a:2, b:3}", options: ["asc", { natural: true, minKeys: 4 }] },

// asc, natural, insensitive
{ code: "var obj = {_:2, a:1, b:3} // asc, natural, insensitive", options: ["asc", { natural: true, caseSensitive: false }] },
{ code: "var obj = {a:1, b:3, c:2}", options: ["asc", { natural: true, caseSensitive: false }] },
Expand All @@ -94,6 +103,9 @@ ruleTester.run("sort-keys", rule, {
{ code: "var obj = {1:1, 2:4, '11':2, A:3}", options: ["asc", { natural: true, caseSensitive: false }] },
{ code: "var obj = {'#':1, 'Z':2, À:3, è:4}", options: ["asc", { natural: true, caseSensitive: false }] },

// asc, natural, insensitive, minKeys should ignore unsorted keys when number of keys is less than minKeys
{ code: "var obj = {a:1, _:2, b:3}", options: ["asc", { natural: true, caseSensitive: false, minKeys: 4 }] },

// desc
{ code: "var obj = {b:3, a:1, _:2} // desc", options: ["desc"] },
{ code: "var obj = {c:2, b:3, a:1}", options: ["desc"] },
Expand All @@ -103,6 +115,9 @@ ruleTester.run("sort-keys", rule, {
{ code: "var obj = {A:3, 2:4, '11':2, 1:1}", options: ["desc"] },
{ code: "var obj = {è:4, À:3, 'Z':2, '#':1}", options: ["desc"] },

// desc, minKeys should ignore unsorted keys when number of keys is less than minKeys
{ code: "var obj = {a:1, c:2, b:3}", options: ["desc", { minKeys: 4 }] },

// desc, insensitive
{ code: "var obj = {b:3, a:1, _:2} // desc, insensitive", options: ["desc", { caseSensitive: false }] },
{ code: "var obj = {c:2, b:3, a:1}", options: ["desc", { caseSensitive: false }] },
Expand All @@ -113,6 +128,9 @@ ruleTester.run("sort-keys", rule, {
{ code: "var obj = {A:3, 2:4, '11':2, 1:1}", options: ["desc", { caseSensitive: false }] },
{ code: "var obj = {è:4, À:3, 'Z':2, '#':1}", options: ["desc", { caseSensitive: false }] },

// desc, insensitive, minKeys should ignore unsorted keys when number of keys is less than minKeys
{ code: "var obj = {$:1, _:2, A:3, a:4}", options: ["desc", { caseSensitive: false, minKeys: 5 }] },

// desc, natural
{ code: "var obj = {b:3, a:1, _:2} // desc, natural", options: ["desc", { natural: true }] },
{ code: "var obj = {c:2, b:3, a:1}", options: ["desc", { natural: true }] },
Expand All @@ -122,6 +140,9 @@ ruleTester.run("sort-keys", rule, {
{ code: "var obj = {A:3, '11':2, 2:4, 1:1}", options: ["desc", { natural: true }] },
{ code: "var obj = {è:4, À:3, 'Z':2, '#':1}", options: ["desc", { natural: true }] },

// desc, natural, minKeys should ignore unsorted keys when number of keys is less than minKeys
{ code: "var obj = {b_:1, a:2, b:3}", options: ["desc", { natural: true, minKeys: 4 }] },

// desc, natural, insensitive
{ code: "var obj = {b:3, a:1, _:2} // desc, natural, insensitive", options: ["desc", { natural: true, caseSensitive: false }] },
{ code: "var obj = {c:2, b:3, a:1}", options: ["desc", { natural: true, caseSensitive: false }] },
Expand All @@ -130,7 +151,10 @@ ruleTester.run("sort-keys", rule, {
{ code: "var obj = {C:2, c:3, b_:1}", options: ["desc", { natural: true, caseSensitive: false }] },
{ code: "var obj = {a:4, A:3, _:2, $:1}", options: ["desc", { natural: true, caseSensitive: false }] },
{ code: "var obj = {A:3, '11':2, 2:4, 1:1}", options: ["desc", { natural: true, caseSensitive: false }] },
{ code: "var obj = {è:4, À:3, 'Z':2, '#':1}", options: ["desc", { natural: true, caseSensitive: false }] }
{ code: "var obj = {è:4, À:3, 'Z':2, '#':1}", options: ["desc", { natural: true, caseSensitive: false }] },

// desc, natural, insensitive, minKeys should ignore unsorted keys when number of keys is less than minKeys
{ code: "var obj = {a:1, _:2, b:3}", options: ["desc", { natural: true, caseSensitive: false, minKeys: 4 }] }
],
invalid: [

Expand Down Expand Up @@ -278,6 +302,15 @@ ruleTester.run("sort-keys", rule, {
]
},

// asc, minKeys should error when number of keys is greater than or equal to minKeys
{
code: "var obj = {a:1, _:2, b:3}",
options: ["asc", { minKeys: 3 }],
errors: [
"Expected object keys to be in ascending order. '_' should be before 'a'."
]
},

// asc, insensitive
{
code: "var obj = {a:1, _:2, b:3} // asc, insensitive",
Expand Down Expand Up @@ -322,6 +355,15 @@ ruleTester.run("sort-keys", rule, {
]
},

// asc, insensitive, minKeys should error when number of keys is greater than or equal to minKeys
{
code: "var obj = {a:1, _:2, b:3}",
options: ["asc", { caseSensitive: false, minKeys: 3 }],
errors: [
"Expected object keys to be in insensitive ascending order. '_' should be before 'a'."
]
},

// asc, natural
{
code: "var obj = {a:1, _:2, b:3} // asc, natural",
Expand Down Expand Up @@ -373,6 +415,15 @@ ruleTester.run("sort-keys", rule, {
]
},

// asc, natural, minKeys should error when number of keys is greater than or equal to minKeys
{
code: "var obj = {a:1, _:2, b:3}",
options: ["asc", { natural: true, minKeys: 2 }],
errors: [
"Expected object keys to be in natural ascending order. '_' should be before 'a'."
]
},

// asc, natural, insensitive
{
code: "var obj = {a:1, _:2, b:3} // asc, natural, insensitive",
Expand Down Expand Up @@ -417,6 +468,15 @@ ruleTester.run("sort-keys", rule, {
]
},

// asc, natural, insensitive, minKeys should error when number of keys is greater than or equal to minKeys
{
code: "var obj = {a:1, _:2, b:3}",
options: ["asc", { natural: true, caseSensitive: false, minKeys: 3 }],
errors: [
"Expected object keys to be in natural insensitive ascending order. '_' should be before 'a'."
]
},

// desc
{
code: "var obj = {a:1, _:2, b:3} // desc",
Expand Down Expand Up @@ -471,6 +531,15 @@ ruleTester.run("sort-keys", rule, {
]
},

// desc, minKeys should error when number of keys is greater than or equal to minKeys
{
code: "var obj = {a:1, _:2, b:3}",
options: ["desc", { minKeys: 3 }],
errors: [
"Expected object keys to be in descending order. 'b' should be before '_'."
]
},

// desc, insensitive
{
code: "var obj = {a:1, _:2, b:3} // desc, insensitive",
Expand Down Expand Up @@ -525,6 +594,15 @@ ruleTester.run("sort-keys", rule, {
]
},

// desc, insensitive should error when number of keys is greater than or equal to minKeys
{
code: "var obj = {a:1, _:2, b:3}",
options: ["desc", { caseSensitive: false, minKeys: 2 }],
errors: [
"Expected object keys to be in insensitive descending order. 'b' should be before '_'."
]
},

// desc, natural
{
code: "var obj = {a:1, _:2, b:3} // desc, natural",
Expand Down Expand Up @@ -580,6 +658,15 @@ ruleTester.run("sort-keys", rule, {
]
},

// desc, natural should error when number of keys is greater than or equal to minKeys
{
code: "var obj = {a:1, _:2, b:3}",
options: ["desc", { natural: true, minKeys: 3 }],
errors: [
"Expected object keys to be in natural descending order. 'b' should be before '_'."
]
},

// desc, natural, insensitive
{
code: "var obj = {a:1, _:2, b:3} // desc, natural, insensitive",
Expand Down Expand Up @@ -633,6 +720,15 @@ ruleTester.run("sort-keys", rule, {
"Expected object keys to be in natural insensitive descending order. 'À' should be before '#'.",
"Expected object keys to be in natural insensitive descending order. 'è' should be before 'Z'."
]
},

// desc, natural, insensitive should error when number of keys is greater than or equal to minKeys
{
code: "var obj = {a:1, _:2, b:3}",
options: ["desc", { natural: true, caseSensitive: false, minKeys: 2 }],
errors: [
"Expected object keys to be in natural insensitive descending order. 'b' should be before '_'."
]
}
]
});