Skip to content

Commit

Permalink
Update: introduce minKeys option to sort-keys rule (fixes #11624) (#1…
Browse files Browse the repository at this point in the history
…1625)

* Fix #11624 by adding minKeys option to sort-keys

* Update documentation for sort-keys with minKeys option
  • Loading branch information
csantos42 authored and platinumazure committed Jun 8, 2019
1 parent 87451f4 commit f403b07
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 6 deletions.
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 '_'."
]
}
]
});

0 comments on commit f403b07

Please sign in to comment.