From f403b07283f91f1285d8318d6acea851dd765755 Mon Sep 17 00:00:00 2001 From: Christian Date: Sat, 8 Jun 2019 16:06:29 -0400 Subject: [PATCH] Update: introduce minKeys option to sort-keys rule (fixes #11624) (#11625) * Fix #11624 by adding minKeys option to sort-keys * Update documentation for sort-keys with minKeys option --- docs/rules/sort-keys.md | 51 ++++++++++++++++++- lib/rules/sort-keys.js | 14 ++++-- tests/lib/rules/sort-keys.js | 98 +++++++++++++++++++++++++++++++++++- 3 files changed, 157 insertions(+), 6 deletions(-) diff --git a/docs/rules/sort-keys.md b/docs/rules/sort-keys.md index e6c5155688e..199d7bbc8a8 100644 --- a/docs/rules/sort-keys.md +++ b/docs/rules/sort-keys.md @@ -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}] } ``` @@ -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: @@ -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. diff --git a/lib/rules/sort-keys.js b/lib/rules/sort-keys.js index 23a39108181..beda42f1e3e 100644 --- a/lib/rules/sort-keys.js +++ b/lib/rules/sort-keys.js @@ -96,6 +96,11 @@ module.exports = { natural: { type: "boolean", default: false + }, + minKeys: { + type: "integer", + minimum: 2, + default: 2 } }, additionalProperties: false @@ -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" : "") ]; @@ -118,10 +124,11 @@ module.exports = { let stack = null; return { - ObjectExpression() { + ObjectExpression(node) { stack = { upper: stack, - prevName: null + prevName: null, + numKeys: node.properties.length }; }, @@ -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; } diff --git a/tests/lib/rules/sort-keys.js b/tests/lib/rules/sort-keys.js index 47a13f80468..de2eeef1eee 100644 --- a/tests/lib/rules/sort-keys.js +++ b/tests/lib/rules/sort-keys.js @@ -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 }] }, @@ -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 }] }, @@ -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 }] }, @@ -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"] }, @@ -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 }] }, @@ -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 }] }, @@ -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 }] }, @@ -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: [ @@ -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", @@ -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", @@ -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", @@ -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", @@ -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", @@ -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", @@ -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", @@ -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 '_'." + ] } ] });