From 84c669869435a7fa378923f33c86a85add6a90e2 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Wed, 8 Jan 2025 21:16:04 -0600 Subject: [PATCH 01/23] add sort-keys --- package.json | 3 + src/index.js | 2 + src/rules/sort-keys.js | 154 ++++++++++++++++++++++++++++++++++ tests/rules/sort-keys.test.js | 136 ++++++++++++++++++++++++++++++ 4 files changed, 295 insertions(+) create mode 100644 src/rules/sort-keys.js create mode 100644 tests/rules/sort-keys.test.js diff --git a/package.json b/package.json index 1c4fb25..6aa3cd9 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,9 @@ "typescript": "^5.4.5", "yorkie": "^2.0.0" }, + "peerDependencies": { + "natural-compare": "*" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } diff --git a/src/index.js b/src/index.js index db9dbca..7ba3504 100644 --- a/src/index.js +++ b/src/index.js @@ -13,6 +13,7 @@ import noDuplicateKeys from "./rules/no-duplicate-keys.js"; import noEmptyKeys from "./rules/no-empty-keys.js"; import noUnsafeValues from "./rules/no-unsafe-values.js"; import noUnnormalizedKeys from "./rules/no-unnormalized-keys.js"; +import sortKeys from "./rules/sort-keys.js"; import topLevelInterop from "./rules/top-level-interop.js"; //----------------------------------------------------------------------------- @@ -34,6 +35,7 @@ const plugin = { "no-empty-keys": noEmptyKeys, "no-unsafe-values": noUnsafeValues, "no-unnormalized-keys": noUnnormalizedKeys, + "sort-keys": sortKeys, "top-level-interop": topLevelInterop, }, configs: { diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js new file mode 100644 index 0000000..2a9c293 --- /dev/null +++ b/src/rules/sort-keys.js @@ -0,0 +1,154 @@ +/** + * @fileoverview Rule to require JSON object keys to be sorted. Cribbed largely from https://github.com/eslint/eslint/blob/main/lib/rules/sort-keys.js + * @author Robin Thomas + */ + +import naturalCompare from "natural-compare"; + +/** + * Functions which check that the given 2 names are in specific order. + * + * Postfix `I` is meant insensitive. + * Postfix `N` is meant natural. + * @private + */ +const isValidOrders = { + asc(a, b) { + return a <= b; + }, + ascI(a, b) { + return a.toLowerCase() <= b.toLowerCase(); + }, + ascN(a, b) { + return naturalCompare(a, b) <= 0; + }, + ascIN(a, b) { + return naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0; + }, + desc(a, b) { + return isValidOrders.asc(b, a); + }, + descI(a, b) { + return isValidOrders.ascI(b, a); + }, + descN(a, b) { + return isValidOrders.ascN(b, a); + }, + descIN(a, b) { + return isValidOrders.ascIN(b, a); + }, +}; + +export default { + meta: { + defaultOptions: [ + "asc", + { + allowLineSeparatedGroups: false, + caseSensitive: true, + minKeys: 2, + natural: false, + }, + ], + + docs: { + description: `Require JSON object keys to be sorted`, + }, + + messages: { + sortKeys: + "Expected object keys to be in {{natural}}{{insensitive}}{{order}}ending order. '{{thisName}}' should be before '{{prevName}}'.", + }, + + schema: [ + { + enum: ["asc", "desc"], + }, + { + type: "object", + properties: { + caseSensitive: { + type: "boolean", + }, + natural: { + type: "boolean", + }, + minKeys: { + type: "integer", + minimum: 2, + }, + allowLineSeparatedGroups: { + type: "boolean", + }, + }, + additionalProperties: false, + }, + ], + + type: "suggestion", + }, + + create(context) { + const [order, { caseSensitive, natural, minKeys }] = context.options; + const insensitive = !caseSensitive; + const isValidOrder = + isValidOrders[ + order + (insensitive ? "I" : "") + (natural ? "N" : "") + ]; + + // The stack to save the previous property's name for each object literals. + let stack = null; + + return { + Object(node) { + stack = { + upper: stack, + prevNode: null, + prevBlankLine: false, + prevName: null, + numKeys: node.members.length, + }; + }, + + Member(node) { + const prevName = stack.prevName; + const numKeys = stack.numKeys; + const thisName = node.name.value; + + stack.prevNode = node; + + if (thisName !== null) { + stack.prevName = thisName; + } + + // if (allowLineSeparatedGroups && isBlankLineBetweenNodes) { + // stack.prevBlankLine = thisName === null; + // return; + // } + + if ( + prevName === null || + thisName === null || + numKeys < minKeys + ) { + return; + } + + if (!isValidOrder(prevName, thisName)) { + context.report({ + node, + loc: node.name.loc, + messageId: "sortKeys", + data: { + thisName, + prevName, + order, + insensitive: insensitive ? "insensitive " : "", + natural: natural ? "natural " : "", + }, + }); + } + }, + }; + }, +}; diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js new file mode 100644 index 0000000..32ebffa --- /dev/null +++ b/tests/rules/sort-keys.test.js @@ -0,0 +1,136 @@ +/** + * @fileoverview Tests for sort-keys rule. + * @author Robin Thomas + */ + +import rule from "../../src/rules/sort-keys.js"; +import json from "../../src/index.js"; +import { RuleTester } from "eslint"; + +const ruleTester = new RuleTester({ + plugins: { + json, + }, + language: "json/json", +}); + +ruleTester.run("sort-keys", rule, { + valid: [ + "[]", + "{}", + + //#region ascending + '{"a": 1, "b": 1}', + '{"a": 1, "a": 1}', + '{"a": 1, "b": 1, "b": 1}', + '{"a": 1, "b": {"a": 1, "b": {"a": 1, "b": 1}}}', + //#endregion + + //#region descending + { + code: '{"b": 1, "a": 1}', + options: ["desc"], + }, + { + code: '{"a": 1, "a": 1}', + options: ["desc"], + }, + { + code: '{"b": 1, "a": {"b": 1, "a": {"b": 1, "a": 1}}}', + options: ["desc"], + }, + //#endregion + + //#region minKeys + { + code: '{"c": 1, "b": 1, "a": 1}', + options: [ + "asc", + { + minKeys: 4, + }, + ], + }, + //#endregion + + //#region case sensitivity + '{"A": 1, "B": 1, "a": 1, "b": 1}', + { + code: '{"b": 1, "a": 1, "B": 1, "A": 1}', + options: ["desc"], + }, + { + code: '{"bet": 1, "bat": 1, "BET": 1, "BAT": 1}', + options: ["desc"], + }, + { + code: '{"A": 1, "a": 1, "b": 1, "B": 1}', + options: [ + "asc", + { + caseSensitive: false, + }, + ], + }, + { + code: '{"Aa": 1, "ab": 1, "ba": 1, "Bb": 1}', + options: [ + "asc", + { + caseSensitive: false, + }, + ], + }, + { + code: '{"BOT": 1, "bet": 1, "bAt": 1}', + options: [ + "desc", + { + caseSensitive: false, + }, + ], + }, + //#endregion + ], + invalid: [ + { + code: '{"b": 1, "a": 2}', + errors: [ + { + messageId: "sortKeys", + data: { + insensitive: "", + natural: "", + order: "asc", + thisName: "a", + prevName: "b", + }, + line: 1, + column: 10, + endLine: 1, + endColumn: 13, + }, + ], + }, + + { + code: '{"a": 1, "b": {"a": 1, "b": { "b": 1, "a": 2}}}', + errors: [ + { + messageId: "sortKeys", + data: { + insensitive: "", + natural: "", + order: "asc", + thisName: "a", + prevName: "b", + }, + line: 1, + column: 39, + endLine: 1, + endColumn: 42, + }, + ], + }, + ], +}); From f0586afa5795933a40b6fa33749aae3c7b153f05 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Wed, 8 Jan 2025 21:19:11 -0600 Subject: [PATCH 02/23] adding tests for eslint/sort-keys --- tests/rules/sort-keys.test.js | 2520 ++++++++++++++++++++++++++++++++- 1 file changed, 2468 insertions(+), 52 deletions(-) diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js index 32ebffa..556d7ca 100644 --- a/tests/rules/sort-keys.test.js +++ b/tests/rules/sort-keys.test.js @@ -16,119 +16,2535 @@ const ruleTester = new RuleTester({ ruleTester.run("sort-keys", rule, { valid: [ - "[]", - "{}", + // default (asc) + { + code: "var obj = {'':1, [``]:2}", + options: [], + languageOptions: { ecmaVersion: 6 }, + }, + { + code: "var obj = {[``]:1, '':2}", + options: [], + languageOptions: { ecmaVersion: 6 }, + }, + { code: "var obj = {'':1, a:2}", options: [] }, + { + code: "var obj = {[``]:1, a:2}", + options: [], + languageOptions: { ecmaVersion: 6 }, + }, + { code: "var obj = {_:2, a:1, b:3} // default", options: [] }, + { code: "var obj = {a:1, b:3, c:2}", options: [] }, + { code: "var obj = {a:2, b:3, b_:1}", options: [] }, + { code: "var obj = {C:3, b_:1, c:2}", options: [] }, + { code: "var obj = {$:1, A:3, _:2, a:4}", options: [] }, + { code: "var obj = {1:1, '11':2, 2:4, A:3}", options: [] }, + { code: "var obj = {'#':1, 'Z':2, À:3, è:4}", options: [] }, + { + code: "var obj = { [/(?0)/]: 1, '/(?0)/': 2 }", + options: [], + languageOptions: { ecmaVersion: 2018 }, + }, + + // ignore non-simple computed properties. + { + code: "var obj = {a:1, b:3, [a + b]: -1, c:2}", + options: [], + languageOptions: { ecmaVersion: 6 }, + }, + { + code: "var obj = {'':1, [f()]:2, a:3}", + options: [], + languageOptions: { ecmaVersion: 6 }, + }, + { + code: "var obj = {a:1, [b++]:2, '':3}", + options: ["desc"], + languageOptions: { ecmaVersion: 6 }, + }, + + // ignore properties separated by spread properties + { + code: "var obj = {a:1, ...z, b:1}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + }, + { + code: "var obj = {b:1, ...z, a:1}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + }, + { + code: "var obj = {...a, b:1, ...c, d:1}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + }, + { + code: "var obj = {...a, b:1, ...d, ...c, e:2, z:5}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + }, + { + code: "var obj = {b:1, ...c, ...d, e:2}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + }, + { + code: "var obj = {a:1, ...z, '':2}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + }, + { + code: "var obj = {'':1, ...z, 'a':2}", + options: ["desc"], + languageOptions: { ecmaVersion: 2018 }, + }, + + // not ignore properties not separated by spread properties + { + code: "var obj = {...z, a:1, b:1}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + }, + { + code: "var obj = {...z, ...c, a:1, b:1}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + }, + { + code: "var obj = {a:1, b:1, ...z}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + }, + { + code: "var obj = {...z, ...x, a:1, ...c, ...d, f:5, e:4}", + options: ["desc"], + languageOptions: { ecmaVersion: 2018 }, + }, + + // works when spread occurs somewhere other than an object literal + { + code: "function fn(...args) { return [...args].length; }", + options: [], + languageOptions: { ecmaVersion: 2018 }, + }, + { + code: "function g() {}; function f(...args) { return g(...args); }", + options: [], + languageOptions: { ecmaVersion: 2018 }, + }, + + // ignore destructuring patterns. + { + code: "let {a, b} = {}", + options: [], + languageOptions: { ecmaVersion: 6 }, + }, + + // nested + { code: "var obj = {a:1, b:{x:1, y:1}, c:1}", options: [] }, + + // asc + { code: "var obj = {_:2, a:1, b:3} // asc", options: ["asc"] }, + { code: "var obj = {a:1, b:3, c:2}", options: ["asc"] }, + { code: "var obj = {a:2, b:3, b_:1}", options: ["asc"] }, + { code: "var obj = {C:3, b_:1, c:2}", options: ["asc"] }, + { code: "var obj = {$:1, A:3, _:2, a:4}", options: ["asc"] }, + { 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 }], + }, + { + code: "var obj = {a:2, b:3, b_:1}", + options: ["asc", { caseSensitive: false }], + }, + { + code: "var obj = {b_:1, C:3, c:2}", + options: ["asc", { caseSensitive: false }], + }, + { + code: "var obj = {b_:1, c:3, C:2}", + options: ["asc", { caseSensitive: false }], + }, + { + code: "var obj = {$:1, _:2, A:3, a:4}", + options: ["asc", { caseSensitive: false }], + }, + { + 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 }], + }, + { + code: "var obj = {a:2, b:3, b_:1}", + options: ["asc", { natural: true }], + }, + { + code: "var obj = {C:3, b_:1, c:2}", + options: ["asc", { natural: true }], + }, + { + code: "var obj = {$:1, _:2, A:3, a:4}", + options: ["asc", { natural: true }], + }, + { + 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 }], + }, + { + code: "var obj = {a:2, b:3, b_:1}", + options: ["asc", { natural: true, caseSensitive: false }], + }, + { + code: "var obj = {b_:1, C:3, c:2}", + options: ["asc", { natural: true, caseSensitive: false }], + }, + { + code: "var obj = {b_:1, c:3, C:2}", + options: ["asc", { natural: true, caseSensitive: false }], + }, + { + code: "var obj = {$:1, _:2, A:3, a:4}", + options: ["asc", { natural: true, caseSensitive: false }], + }, + { + 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"] }, + { code: "var obj = {b_:1, b:3, a:2}", options: ["desc"] }, + { code: "var obj = {c:2, b_:1, C:3}", options: ["desc"] }, + { code: "var obj = {a:4, _:2, A:3, $:1}", options: ["desc"] }, + { 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 }], + }, + { + code: "var obj = {b_:1, b:3, a:2}", + options: ["desc", { caseSensitive: false }], + }, + { + code: "var obj = {c:2, C:3, b_:1}", + options: ["desc", { caseSensitive: false }], + }, + { + code: "var obj = {C:2, c:3, b_:1}", + options: ["desc", { caseSensitive: false }], + }, + { + code: "var obj = {a:4, A:3, _:2, $:1}", + options: ["desc", { caseSensitive: false }], + }, + { + 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 }], + }, + { + code: "var obj = {b_:1, b:3, a:2}", + options: ["desc", { natural: true }], + }, + { + code: "var obj = {c:2, b_:1, C:3}", + options: ["desc", { natural: true }], + }, + { + code: "var obj = {a:4, A:3, _:2, $:1}", + options: ["desc", { natural: true }], + }, + { + 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 }], + }, + { + code: "var obj = {b_:1, b:3, a:2}", + options: ["desc", { natural: true, caseSensitive: false }], + }, + { + code: "var obj = {c:2, C:3, b_:1}", + options: ["desc", { natural: true, caseSensitive: false }], + }, + { + 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 }], + }, + + // 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 }, + ], + }, + + // allowLineSeparatedGroups option + { + code: ` + var obj = { + e: 1, + f: 2, + g: 3, + + a: 4, + b: 5, + c: 6 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + }, + { + code: ` + var obj = { + b: 1, + + // comment + a: 2, + c: 3 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + }, + { + code: ` + var obj = { + b: 1 + + , + + // comment + a: 2, + c: 3 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + }, + { + code: ` + var obj = { + c: 1, + d: 2, + + b() { + }, + e: 4 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + }, + { + code: ` + var obj = { + c: 1, + d: 2, + // comment + + // comment + b() { + }, + e: 4 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + }, + { + code: ` + var obj = { + b, + + [a+b]: 1, + a + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + }, + { + code: ` + var obj = { + c: 1, + d: 2, + + a() { + + }, + + // abce + f: 3, + + /* + + */ + [a+b]: 1, + cc: 1, + e: 2 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + }, + { + code: ` + var obj = { + b: "/*", + + a: "*/", + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + }, + { + code: ` + var obj = { + b, + /* + */ // + + a + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + }, + { + code: ` + var obj = { + b, + + /* + */ // + a + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + }, + { + code: ` + var obj = { + b: 1 + + ,a: 2 + }; + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + }, + { + code: ` + var obj = { + b: 1 + // comment before comma + + , + a: 2 + }; + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + }, + { + code: ` + var obj = { + b, + + a, + ...z, + c + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 2018 }, + }, + { + code: ` + var obj = { + b, + + [foo()]: [ + + ], + a + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 2018 }, + }, + + // ignoreComputedKeys + { + code: "var obj = { ['b']: 1, a: 2 }", + options: ["asc", { ignoreComputedKeys: true }], + }, + { + code: "var obj = { a: 1, [c]: 2, b: 3 }", + options: ["asc", { ignoreComputedKeys: true }], + }, + { + code: "var obj = { c: 1, ['b']: 2, a: 3 }", + options: ["asc", { ignoreComputedKeys: true }], + }, + ], + invalid: [ + // default (asc) + { + code: "var obj = {a:1, '':2} // default", + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {a:1, [``]:2} // default", + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {a:1, _:2, b:3} // default", + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "_", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {a:1, c:2, b:3}", + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "b", + prevName: "c", + }, + }, + ], + }, + { + code: "var obj = {b_:1, a:2, b:3}", + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "a", + prevName: "b_", + }, + }, + ], + }, + { + code: "var obj = {b_:1, c:2, C:3}", + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "C", + prevName: "c", + }, + }, + ], + }, + { + code: "var obj = {$:1, _:2, A:3, a:4}", + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "A", + prevName: "_", + }, + }, + ], + }, + { + code: "var obj = {1:1, 2:4, A:3, '11':2}", + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "11", + prevName: "A", + }, + }, + ], + }, + { + code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "Z", + prevName: "À", + }, + }, + ], + }, + { + code: "var obj = { null: 1, [/(?0)/]: 2 }", + languageOptions: { ecmaVersion: 2018 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "/(?0)/", + prevName: "null", + }, + }, + ], + }, + + // not ignore properties not separated by spread properties + { + code: "var obj = {...z, c:1, b:1}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "b", + prevName: "c", + }, + }, + ], + }, + { + code: "var obj = {...z, ...c, d:4, b:1, ...y, ...f, e:2, a:1}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "b", + prevName: "d", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "a", + prevName: "e", + }, + }, + ], + }, + { + code: "var obj = {c:1, b:1, ...a}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "b", + prevName: "c", + }, + }, + ], + }, + { + code: "var obj = {...z, ...a, c:1, b:1}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "b", + prevName: "c", + }, + }, + ], + }, + { + code: "var obj = {...z, b:1, a:1, ...d, ...c}", + options: [], + languageOptions: { ecmaVersion: 2018 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "a", + prevName: "b", + }, + }, + ], + }, + { + code: "var obj = {...z, a:2, b:0, ...x, ...c}", + options: ["desc"], + languageOptions: { ecmaVersion: 2018 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "b", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {...z, a:2, b:0, ...x}", + options: ["desc"], + languageOptions: { ecmaVersion: 2018 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "b", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {...z, '':1, a:2}", + options: ["desc"], + languageOptions: { ecmaVersion: 2018 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "a", + prevName: "", + }, + }, + ], + }, + + // ignore non-simple computed properties, but their position shouldn't affect other comparisons. + { + code: "var obj = {a:1, [b+c]:2, '':3}", + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {'':1, [b+c]:2, a:3}", + options: ["desc"], + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "a", + prevName: "", + }, + }, + ], + }, + { + code: "var obj = {b:1, [f()]:2, '':3, a:4}", + options: ["desc"], + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "a", + prevName: "", + }, + }, + ], + }, + + // not ignore simple computed properties. + { + code: "var obj = {a:1, b:3, [a]: -1, c:2}", + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "a", + prevName: "b", + }, + }, + ], + }, + + // nested + { + code: "var obj = {a:1, c:{y:1, x:1}, b:1}", + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "x", + prevName: "y", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "b", + prevName: "c", + }, + }, + ], + }, + + // asc + { + code: "var obj = {a:1, _:2, b:3} // asc", + options: ["asc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "_", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {a:1, c:2, b:3}", + options: ["asc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "b", + prevName: "c", + }, + }, + ], + }, + { + code: "var obj = {b_:1, a:2, b:3}", + options: ["asc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "a", + prevName: "b_", + }, + }, + ], + }, + { + code: "var obj = {b_:1, c:2, C:3}", + options: ["asc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "C", + prevName: "c", + }, + }, + ], + }, + { + code: "var obj = {$:1, _:2, A:3, a:4}", + options: ["asc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "A", + prevName: "_", + }, + }, + ], + }, + { + code: "var obj = {1:1, 2:4, A:3, '11':2}", + options: ["asc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "11", + prevName: "A", + }, + }, + ], + }, + { + code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + options: ["asc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "Z", + prevName: "À", + }, + }, + ], + }, + + // 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: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "_", + prevName: "a", + }, + }, + ], + }, + + // asc, insensitive + { + code: "var obj = {a:1, _:2, b:3} // asc, insensitive", + options: ["asc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "asc", + thisName: "_", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {a:1, c:2, b:3}", + options: ["asc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "asc", + thisName: "b", + prevName: "c", + }, + }, + ], + }, + { + code: "var obj = {b_:1, a:2, b:3}", + options: ["asc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "asc", + thisName: "a", + prevName: "b_", + }, + }, + ], + }, + { + code: "var obj = {$:1, A:3, _:2, a:4}", + options: ["asc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "asc", + thisName: "_", + prevName: "A", + }, + }, + ], + }, + { + code: "var obj = {1:1, 2:4, A:3, '11':2}", + options: ["asc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "asc", + thisName: "11", + prevName: "A", + }, + }, + ], + }, + { + code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + options: ["asc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "asc", + thisName: "Z", + prevName: "À", + }, + }, + ], + }, + + // 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: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "asc", + thisName: "_", + prevName: "a", + }, + }, + ], + }, + + // asc, natural + { + code: "var obj = {a:1, _:2, b:3} // asc, natural", + options: ["asc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "asc", + thisName: "_", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {a:1, c:2, b:3}", + options: ["asc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "asc", + thisName: "b", + prevName: "c", + }, + }, + ], + }, + { + code: "var obj = {b_:1, a:2, b:3}", + options: ["asc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "asc", + thisName: "a", + prevName: "b_", + }, + }, + ], + }, + { + code: "var obj = {b_:1, c:2, C:3}", + options: ["asc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "asc", + thisName: "C", + prevName: "c", + }, + }, + ], + }, + { + code: "var obj = {$:1, A:3, _:2, a:4}", + options: ["asc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "asc", + thisName: "_", + prevName: "A", + }, + }, + ], + }, + { + code: "var obj = {1:1, 2:4, A:3, '11':2}", + options: ["asc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "asc", + thisName: "11", + prevName: "A", + }, + }, + ], + }, + { + code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + options: ["asc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "asc", + thisName: "Z", + prevName: "À", + }, + }, + ], + }, + + // 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: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "asc", + thisName: "_", + prevName: "a", + }, + }, + ], + }, + + // asc, natural, insensitive + { + code: "var obj = {a:1, _:2, b:3} // asc, natural, insensitive", + options: ["asc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "asc", + thisName: "_", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {a:1, c:2, b:3}", + options: ["asc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "asc", + thisName: "b", + prevName: "c", + }, + }, + ], + }, + { + code: "var obj = {b_:1, a:2, b:3}", + options: ["asc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "asc", + thisName: "a", + prevName: "b_", + }, + }, + ], + }, + { + code: "var obj = {$:1, A:3, _:2, a:4}", + options: ["asc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "asc", + thisName: "_", + prevName: "A", + }, + }, + ], + }, + { + code: "var obj = {1:1, '11':2, 2:4, A:3}", + options: ["asc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "asc", + thisName: "2", + prevName: "11", + }, + }, + ], + }, + { + code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + options: ["asc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "asc", + thisName: "Z", + prevName: "À", + }, + }, + ], + }, + + // 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: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "asc", + thisName: "_", + prevName: "a", + }, + }, + ], + }, + + // desc + { + code: "var obj = {'':1, a:'2'} // desc", + options: ["desc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "a", + prevName: "", + }, + }, + ], + }, + { + code: "var obj = {[``]:1, a:'2'} // desc", + options: ["desc"], + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "a", + prevName: "", + }, + }, + ], + }, + { + code: "var obj = {a:1, _:2, b:3} // desc", + options: ["desc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "b", + prevName: "_", + }, + }, + ], + }, + { + code: "var obj = {a:1, c:2, b:3}", + options: ["desc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "c", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {b_:1, a:2, b:3}", + options: ["desc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "b", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {b_:1, c:2, C:3}", + options: ["desc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "c", + prevName: "b_", + }, + }, + ], + }, + { + code: "var obj = {$:1, _:2, A:3, a:4}", + options: ["desc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "_", + prevName: "$", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "a", + prevName: "A", + }, + }, + ], + }, + { + code: "var obj = {1:1, 2:4, A:3, '11':2}", + options: ["desc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "2", + prevName: "1", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "A", + prevName: "2", + }, + }, + ], + }, + { + code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + options: ["desc"], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "À", + prevName: "#", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "è", + prevName: "Z", + }, + }, + ], + }, + + // 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: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "desc", + thisName: "b", + prevName: "_", + }, + }, + ], + }, + + // desc, insensitive + { + code: "var obj = {a:1, _:2, b:3} // desc, insensitive", + options: ["desc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "desc", + thisName: "b", + prevName: "_", + }, + }, + ], + }, + { + code: "var obj = {a:1, c:2, b:3}", + options: ["desc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "desc", + thisName: "c", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {b_:1, a:2, b:3}", + options: ["desc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "desc", + thisName: "b", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {b_:1, c:2, C:3}", + options: ["desc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "desc", + thisName: "c", + prevName: "b_", + }, + }, + ], + }, + { + code: "var obj = {$:1, _:2, A:3, a:4}", + options: ["desc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "desc", + thisName: "_", + prevName: "$", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "desc", + thisName: "A", + prevName: "_", + }, + }, + ], + }, + { + code: "var obj = {1:1, 2:4, A:3, '11':2}", + options: ["desc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "desc", + thisName: "2", + prevName: "1", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "desc", + thisName: "A", + prevName: "2", + }, + }, + ], + }, + { + code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + options: ["desc", { caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "desc", + thisName: "À", + prevName: "#", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "desc", + thisName: "è", + prevName: "Z", + }, + }, + ], + }, + + // 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: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "insensitive ", + order: "desc", + thisName: "b", + prevName: "_", + }, + }, + ], + }, + + // desc, natural + { + code: "var obj = {a:1, _:2, b:3} // desc, natural", + options: ["desc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "desc", + thisName: "b", + prevName: "_", + }, + }, + ], + }, + { + code: "var obj = {a:1, c:2, b:3}", + options: ["desc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "desc", + thisName: "c", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {b_:1, a:2, b:3}", + options: ["desc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "desc", + thisName: "b", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {b_:1, c:2, C:3}", + options: ["desc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "desc", + thisName: "c", + prevName: "b_", + }, + }, + ], + }, + { + code: "var obj = {$:1, _:2, A:3, a:4}", + options: ["desc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "desc", + thisName: "_", + prevName: "$", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "desc", + thisName: "A", + prevName: "_", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "desc", + thisName: "a", + prevName: "A", + }, + }, + ], + }, + { + code: "var obj = {1:1, 2:4, A:3, '11':2}", + options: ["desc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "desc", + thisName: "2", + prevName: "1", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "desc", + thisName: "A", + prevName: "2", + }, + }, + ], + }, + { + code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + options: ["desc", { natural: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "desc", + thisName: "À", + prevName: "#", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "desc", + thisName: "è", + prevName: "Z", + }, + }, + ], + }, - //#region ascending - '{"a": 1, "b": 1}', - '{"a": 1, "a": 1}', - '{"a": 1, "b": 1, "b": 1}', - '{"a": 1, "b": {"a": 1, "b": {"a": 1, "b": 1}}}', - //#endregion + // 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: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "", + order: "desc", + thisName: "b", + prevName: "_", + }, + }, + ], + }, - //#region descending + // desc, natural, insensitive { - code: '{"b": 1, "a": 1}', - options: ["desc"], + code: "var obj = {a:1, _:2, b:3} // desc, natural, insensitive", + options: ["desc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "desc", + thisName: "b", + prevName: "_", + }, + }, + ], }, { - code: '{"a": 1, "a": 1}', - options: ["desc"], + code: "var obj = {a:1, c:2, b:3}", + options: ["desc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "desc", + thisName: "c", + prevName: "a", + }, + }, + ], }, { - code: '{"b": 1, "a": {"b": 1, "a": {"b": 1, "a": 1}}}', - options: ["desc"], + code: "var obj = {b_:1, a:2, b:3}", + options: ["desc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "desc", + thisName: "b", + prevName: "a", + }, + }, + ], + }, + { + code: "var obj = {b_:1, c:2, C:3}", + options: ["desc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "desc", + thisName: "c", + prevName: "b_", + }, + }, + ], + }, + { + code: "var obj = {$:1, _:2, A:3, a:4}", + options: ["desc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "desc", + thisName: "_", + prevName: "$", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "desc", + thisName: "A", + prevName: "_", + }, + }, + ], + }, + { + code: "var obj = {1:1, 2:4, '11':2, A:3}", + options: ["desc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "desc", + thisName: "2", + prevName: "1", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "desc", + thisName: "11", + prevName: "2", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "desc", + thisName: "A", + prevName: "11", + }, + }, + ], + }, + { + code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + options: ["desc", { natural: true, caseSensitive: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "desc", + thisName: "À", + prevName: "#", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "desc", + thisName: "è", + prevName: "Z", + }, + }, + ], }, - //#endregion - //#region minKeys + // desc, natural, insensitive should error when number of keys is greater than or equal to minKeys { - code: '{"c": 1, "b": 1, "a": 1}', + code: "var obj = {a:1, _:2, b:3}", options: [ - "asc", + "desc", + { natural: true, caseSensitive: false, minKeys: 2 }, + ], + errors: [ { - minKeys: 4, + messageId: "sortKeys", + data: { + natural: "natural ", + insensitive: "insensitive ", + order: "desc", + thisName: "b", + prevName: "_", + }, }, ], }, - //#endregion - //#region case sensitivity - '{"A": 1, "B": 1, "a": 1, "b": 1}', + // When allowLineSeparatedGroups option is false { - code: '{"b": 1, "a": 1, "B": 1, "A": 1}', - options: ["desc"], + code: ` + var obj = { + b: 1, + c: 2, + a: 3 + } + `, + options: ["asc", { allowLineSeparatedGroups: false }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "a", + prevName: "c", + }, + }, + ], }, { - code: '{"bet": 1, "bat": 1, "BET": 1, "BAT": 1}', - options: ["desc"], + code: ` + let obj = { + b + + ,a + } + `, + options: ["asc", { allowLineSeparatedGroups: false }], + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "a", + prevName: "b", + }, + }, + ], }, { - code: '{"A": 1, "a": 1, "b": 1, "B": 1}', - options: [ - "asc", + code: ` + let obj = { + b + + ,a + } + `, + languageOptions: { ecmaVersion: 6 }, + errors: [ { - caseSensitive: false, + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "a", + prevName: "b", + }, }, ], }, + + // When allowLineSeparatedGroups option is true { - code: '{"Aa": 1, "ab": 1, "ba": 1, "Bb": 1}', - options: [ - "asc", + code: ` + var obj = { + b: 1, + c () { + + }, + a: 3 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + errors: [ { - caseSensitive: false, + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "a", + prevName: "c", + }, }, ], }, { - code: '{"BOT": 1, "bet": 1, "bAt": 1}', - options: [ - "desc", + code: ` + var obj = { + a: 1, + b: 2, + + z () { + + }, + y: 3 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + errors: [ { - caseSensitive: false, + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "y", + prevName: "z", + }, }, ], }, - //#endregion - ], - invalid: [ { - code: '{"b": 1, "a": 2}', + code: ` + var obj = { + b: 1, + c () { + }, + // comment + a: 3 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, errors: [ { messageId: "sortKeys", data: { + natural: "", insensitive: "", + order: "asc", + thisName: "a", + prevName: "c", + }, + }, + ], + }, + { + code: ` + var obj = { + b, + [a+b]: 1, + a // sort-keys: 'a' should be before 'b' + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { natural: "", + insensitive: "", order: "asc", thisName: "a", prevName: "b", }, - line: 1, - column: 10, - endLine: 1, - endColumn: 13, }, ], }, + { + code: ` + var obj = { + c: 1, + d: 2, + // comment + // comment + b() { + }, + e: 4 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "b", + prevName: "d", + }, + }, + ], + }, + { + code: ` + var obj = { + c: 1, + d: 2, + + z() { + }, + f: 3, + /* + + + */ + [a+b]: 1, + b: 1, + e: 2 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "f", + prevName: "z", + }, + }, + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "b", + prevName: "f", + }, + }, + ], + }, + { + code: ` + var obj = { + b: "/*", + a: "*/", + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "a", + prevName: "b", + }, + }, + ], + }, + { + code: ` + var obj = { + b: 1 + // comment before comma + , a: 2 + }; + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { + natural: "", + insensitive: "", + order: "asc", + thisName: "a", + prevName: "b", + }, + }, + ], + }, { - code: '{"a": 1, "b": {"a": 1, "b": { "b": 1, "a": 2}}}', + code: ` + let obj = { + b, + [foo()]: [ + // ↓ this blank is inside a property and therefore should not count + + ], + a + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 2018 }, errors: [ { messageId: "sortKeys", data: { + natural: "", insensitive: "", + order: "asc", + thisName: "a", + prevName: "b", + }, + }, + ], + }, + { + code: "var obj = { d: 1, ['c']: 2, b: 3, a: 4 }", + options: ["asc", { ignoreComputedKeys: true, minKeys: 4 }], + errors: [ + { + messageId: "sortKeys", + data: { natural: "", + insensitive: "", order: "asc", thisName: "a", prevName: "b", }, - line: 1, - column: 39, - endLine: 1, - endColumn: 42, }, ], }, From edfa0f80fdcd36f9c261a312e5a2c9cc36721a3c Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Thu, 9 Jan 2025 00:02:24 -0600 Subject: [PATCH 03/23] made eslint/sort-keys tests work with json --- src/rules/sort-keys.js | 118 ++-- tests/rules/sort-keys.test.js | 1239 +++++++-------------------------- 2 files changed, 309 insertions(+), 1048 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index 2a9c293..ffb79e8 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -97,58 +97,90 @@ export default { ]; // The stack to save the previous property's name for each object literals. - let stack = null; + // let stack = null; return { Object(node) { - stack = { - upper: stack, - prevNode: null, - prevBlankLine: false, - prevName: null, - numKeys: node.members.length, - }; - }, - - Member(node) { - const prevName = stack.prevName; - const numKeys = stack.numKeys; - const thisName = node.name.value; + let prevMember; + let prevName; - stack.prevNode = node; - - if (thisName !== null) { - stack.prevName = thisName; - } - - // if (allowLineSeparatedGroups && isBlankLineBetweenNodes) { - // stack.prevBlankLine = thisName === null; - // return; - // } - - if ( - prevName === null || - thisName === null || - numKeys < minKeys - ) { + if (node.members.length < minKeys) { return; } - if (!isValidOrder(prevName, thisName)) { - context.report({ - node, - loc: node.name.loc, - messageId: "sortKeys", - data: { - thisName, - prevName, - order, - insensitive: insensitive ? "insensitive " : "", - natural: natural ? "natural " : "", - }, - }); + for (const member of node.members) { + const thisName = member.name.value; + + if (prevMember) { + if (!isValidOrder(prevName, thisName)) { + context.report({ + node, + loc: member.name.loc, + messageId: "sortKeys", + data: { + thisName, + prevName, + order, + insensitive: insensitive + ? "insensitive " + : "", + natural: natural ? "natural " : "", + }, + }); + } + } + + prevMember = member; + prevName = thisName; } + // stack = { + // upper: stack, + // prevNode: null, + // prevBlankLine: false, + // prevName: null, + // numKeys: node.members.length, + // }; }, + + // Member(node) { + // const prevName = stack.prevName; + // const numKeys = stack.numKeys; + // const thisName = node.name.value; + + // stack.prevNode = node; + + // if (thisName !== null) { + // stack.prevName = thisName; + // } + + // // if (allowLineSeparatedGroups && isBlankLineBetweenNodes) { + // // stack.prevBlankLine = thisName === null; + // // return; + // // } + + // if ( + // prevName === null || + // thisName === null || + // numKeys < minKeys + // ) { + // return; + // } + + // if (!isValidOrder(prevName, thisName)) { + // context.report({ + // node, + // loc: node.name.loc, + // messageId: "sortKeys", + // data: { + // thisName, + // prevName, + // order, + // insensitive: insensitive ? "insensitive " : "", + // natural: natural ? "natural " : "", + // }, + // }); + // } + // }, }; }, }; diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js index 556d7ca..02800e8 100644 --- a/tests/rules/sort-keys.test.js +++ b/tests/rules/sort-keys.test.js @@ -17,258 +17,150 @@ const ruleTester = new RuleTester({ ruleTester.run("sort-keys", rule, { valid: [ // default (asc) - { - code: "var obj = {'':1, [``]:2}", - options: [], - languageOptions: { ecmaVersion: 6 }, - }, - { - code: "var obj = {[``]:1, '':2}", - options: [], - languageOptions: { ecmaVersion: 6 }, - }, - { code: "var obj = {'':1, a:2}", options: [] }, - { - code: "var obj = {[``]:1, a:2}", - options: [], - languageOptions: { ecmaVersion: 6 }, - }, - { code: "var obj = {_:2, a:1, b:3} // default", options: [] }, - { code: "var obj = {a:1, b:3, c:2}", options: [] }, - { code: "var obj = {a:2, b:3, b_:1}", options: [] }, - { code: "var obj = {C:3, b_:1, c:2}", options: [] }, - { code: "var obj = {$:1, A:3, _:2, a:4}", options: [] }, - { code: "var obj = {1:1, '11':2, 2:4, A:3}", options: [] }, - { code: "var obj = {'#':1, 'Z':2, À:3, è:4}", options: [] }, - { - code: "var obj = { [/(?0)/]: 1, '/(?0)/': 2 }", - options: [], - languageOptions: { ecmaVersion: 2018 }, - }, - - // ignore non-simple computed properties. - { - code: "var obj = {a:1, b:3, [a + b]: -1, c:2}", - options: [], - languageOptions: { ecmaVersion: 6 }, - }, - { - code: "var obj = {'':1, [f()]:2, a:3}", - options: [], - languageOptions: { ecmaVersion: 6 }, - }, - { - code: "var obj = {a:1, [b++]:2, '':3}", - options: ["desc"], - languageOptions: { ecmaVersion: 6 }, - }, - - // ignore properties separated by spread properties - { - code: "var obj = {a:1, ...z, b:1}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - }, - { - code: "var obj = {b:1, ...z, a:1}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - }, - { - code: "var obj = {...a, b:1, ...c, d:1}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - }, - { - code: "var obj = {...a, b:1, ...d, ...c, e:2, z:5}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - }, - { - code: "var obj = {b:1, ...c, ...d, e:2}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - }, - { - code: "var obj = {a:1, ...z, '':2}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - }, - { - code: "var obj = {'':1, ...z, 'a':2}", - options: ["desc"], - languageOptions: { ecmaVersion: 2018 }, - }, - - // not ignore properties not separated by spread properties - { - code: "var obj = {...z, a:1, b:1}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - }, - { - code: "var obj = {...z, ...c, a:1, b:1}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - }, - { - code: "var obj = {a:1, b:1, ...z}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - }, - { - code: "var obj = {...z, ...x, a:1, ...c, ...d, f:5, e:4}", - options: ["desc"], - languageOptions: { ecmaVersion: 2018 }, - }, - - // works when spread occurs somewhere other than an object literal - { - code: "function fn(...args) { return [...args].length; }", - options: [], - languageOptions: { ecmaVersion: 2018 }, - }, - { - code: "function g() {}; function f(...args) { return g(...args); }", - options: [], - languageOptions: { ecmaVersion: 2018 }, - }, - - // ignore destructuring patterns. - { - code: "let {a, b} = {}", - options: [], - languageOptions: { ecmaVersion: 6 }, - }, + { code: '{"":1, "a":2}', options: [] }, + { code: '{"_":2, "a":1, "b":3}', options: [] }, + { code: '{"a":1, "b":3, "c":2}', options: [] }, + { code: '{"a":2, "b":3, "b_":1}', options: [] }, + { code: '{"C":3, "b_":1, "c":2}', options: [] }, + { code: '{"$":1, "A":3, "_":2, "a":4}', options: [] }, + { code: '{"1":1, "11":2, "2":4, "A":3}', options: [] }, + { code: '{"#":1, "Z":2, "À":3, "è":4}', options: [] }, // nested - { code: "var obj = {a:1, b:{x:1, y:1}, c:1}", options: [] }, + { code: '{"a":1, "b":{"x":1, "y":1}, "c":1}', options: [] }, // asc - { code: "var obj = {_:2, a:1, b:3} // asc", options: ["asc"] }, - { code: "var obj = {a:1, b:3, c:2}", options: ["asc"] }, - { code: "var obj = {a:2, b:3, b_:1}", options: ["asc"] }, - { code: "var obj = {C:3, b_:1, c:2}", options: ["asc"] }, - { code: "var obj = {$:1, A:3, _:2, a:4}", options: ["asc"] }, - { code: "var obj = {1:1, '11':2, 2:4, A:3}", options: ["asc"] }, - { code: "var obj = {'#':1, 'Z':2, À:3, è:4}", options: ["asc"] }, + { + code: '{"_":2, "a":1, "b":3} // asc"', + language: "json/jsonc", + options: ["asc"], + }, + { code: '{"a":1, "b":3, "c":2}', options: ["asc"] }, + { code: '{"a":2, "b":3, "b_":1}', options: ["asc"] }, + { code: '{"C":3, "b_":1, "c":2}', options: ["asc"] }, + { code: '{"$":1, "A":3, "_":2, "a":4}', options: ["asc"] }, + { code: '{"1":1, "11":2, "2":4, "A":3}', options: ["asc"] }, + { code: '{"#":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 }] }, + { code: '{"a":1, "c":2, "b":3}', options: ["asc", { minKeys: 4 }] }, // asc, insensitive { - code: "var obj = {_:2, a:1, b:3} // asc, insensitive", + code: '{"_":2, "a":1, "b":3} // asc, insensitive', + language: "json/jsonc", options: ["asc", { caseSensitive: false }], }, { - code: "var obj = {a:1, b:3, c:2}", + code: '{"a":1, "b":3, "c":2}', options: ["asc", { caseSensitive: false }], }, { - code: "var obj = {a:2, b:3, b_:1}", + code: '{"a":2, "b":3, "b_":1}', options: ["asc", { caseSensitive: false }], }, { - code: "var obj = {b_:1, C:3, c:2}", + code: '{"b_":1, "C":3, "c":2}', options: ["asc", { caseSensitive: false }], }, { - code: "var obj = {b_:1, c:3, C:2}", + code: '{"b_":1, "c":3, "C":2}', options: ["asc", { caseSensitive: false }], }, { - code: "var obj = {$:1, _:2, A:3, a:4}", + code: '{"$":1, "_":2, "A":3, "a":4}', options: ["asc", { caseSensitive: false }], }, { - code: "var obj = {1:1, '11':2, 2:4, A:3}", + code: '{"1":1, "11":2, "2":4, "A":3}', options: ["asc", { caseSensitive: false }], }, { - code: "var obj = {'#':1, 'Z':2, À:3, è:4}", + code: '{"#":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}", + code: '{"$":1, "A":3, "_":2, "a":4}', options: ["asc", { caseSensitive: false, minKeys: 5 }], }, // asc, natural { - code: "var obj = {_:2, a:1, b:3} // asc, natural", + code: '{"_":2, "a":1, "b":3} // asc, natural', + language: "json/jsonc", options: ["asc", { natural: true }], }, { - code: "var obj = {a:1, b:3, c:2}", + code: '{"a":1, "b":3, "c":2}', options: ["asc", { natural: true }], }, { - code: "var obj = {a:2, b:3, b_:1}", + code: '{"a":2, "b":3, "b_":1}', options: ["asc", { natural: true }], }, { - code: "var obj = {C:3, b_:1, c:2}", + code: '{"C":3, "b_":1, "c":2}', options: ["asc", { natural: true }], }, { - code: "var obj = {$:1, _:2, A:3, a:4}", + code: '{"$":1, "_":2, "A":3, "a":4}', options: ["asc", { natural: true }], }, { - code: "var obj = {1:1, 2:4, '11':2, A:3}", + code: '{"1":1, "2":4, "11":2, "A":3}', options: ["asc", { natural: true }], }, { - code: "var obj = {'#':1, 'Z':2, À:3, è:4}", + code: '{"#":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}", + code: '{"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", + code: '{"_":2, "a":1, "b":3} // asc, natural, insensitive', + language: "json/jsonc", options: ["asc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {a:1, b:3, c:2}", + code: '{"a":1, "b":3, "c":2}', options: ["asc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {a:2, b:3, b_:1}", + code: '{"a":2, "b":3, "b_":1}', options: ["asc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {b_:1, C:3, c:2}", + code: '{"b_":1, "C":3, "c":2}', options: ["asc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {b_:1, c:3, C:2}", + code: '{"b_":1, "c":3, "C":2}', options: ["asc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {$:1, _:2, A:3, a:4}", + code: '{"$":1, "_":2, "A":3, "a":4}', options: ["asc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {1:1, 2:4, '11':2, A:3}", + code: '{"1":1, "2":4, "11":2, "A":3}', options: ["asc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {'#':1, 'Z':2, À:3, è:4}", + code: '{"#":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}", + code: '{"a":1, "_":2, "b":3}', options: [ "asc", { natural: true, caseSensitive: false, minKeys: 4 }, @@ -276,133 +168,140 @@ ruleTester.run("sort-keys", rule, { }, // desc - { code: "var obj = {b:3, a:1, _:2} // desc", options: ["desc"] }, - { code: "var obj = {c:2, b:3, a:1}", options: ["desc"] }, - { code: "var obj = {b_:1, b:3, a:2}", options: ["desc"] }, - { code: "var obj = {c:2, b_:1, C:3}", options: ["desc"] }, - { code: "var obj = {a:4, _:2, A:3, $:1}", options: ["desc"] }, - { code: "var obj = {A:3, 2:4, '11':2, 1:1}", options: ["desc"] }, - { code: "var obj = {è:4, À:3, 'Z':2, '#':1}", options: ["desc"] }, + { + code: '{"b":3, "a":1, "_":2} // desc', + language: "json/jsonc", + options: ["desc"], + }, + { code: '{"c":2, "b":3, "a":1}', options: ["desc"] }, + { code: '{"b_":1, "b":3, "a":2}', options: ["desc"] }, + { code: '{"c":2, "b_":1, "C":3}', options: ["desc"] }, + { code: '{"a":4, "_":2, "A":3, "$":1}', options: ["desc"] }, + { code: '{"A":3, "2":4, "11":2, "1":1}', options: ["desc"] }, + { code: '{"è":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}", + code: '{"a":1, "c":2, "b":3}', options: ["desc", { minKeys: 4 }], }, // desc, insensitive { - code: "var obj = {b:3, a:1, _:2} // desc, insensitive", + code: '{"b":3, "a":1, "_":2} // desc, insensitive', + language: "json/jsonc", options: ["desc", { caseSensitive: false }], }, { - code: "var obj = {c:2, b:3, a:1}", + code: '{"c":2, "b":3, "a":1}', options: ["desc", { caseSensitive: false }], }, { - code: "var obj = {b_:1, b:3, a:2}", + code: '{"b_":1, "b":3, "a":2}', options: ["desc", { caseSensitive: false }], }, { - code: "var obj = {c:2, C:3, b_:1}", + code: '{"c":2, "C":3, "b_":1}', options: ["desc", { caseSensitive: false }], }, { - code: "var obj = {C:2, c:3, b_:1}", + code: '{"C":2, "c":3, "b_":1}', options: ["desc", { caseSensitive: false }], }, { - code: "var obj = {a:4, A:3, _:2, $:1}", + code: '{"a":4, "A":3, "_":2, "$":1}', options: ["desc", { caseSensitive: false }], }, { - code: "var obj = {A:3, 2:4, '11':2, 1:1}", + code: '{"A":3, "2":4, "11":2, "1":1}', options: ["desc", { caseSensitive: false }], }, { - code: "var obj = {è:4, À:3, 'Z':2, '#':1}", + code: '{"è":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}", + code: '{"$":1, "_":2, "A":3, "a":4}', options: ["desc", { caseSensitive: false, minKeys: 5 }], }, // desc, natural { - code: "var obj = {b:3, a:1, _:2} // desc, natural", + code: '{"b":3, "a":1, "_":2} // desc, natural', + language: "json/jsonc", options: ["desc", { natural: true }], }, { - code: "var obj = {c:2, b:3, a:1}", + code: '{"c":2, "b":3, "a":1}', options: ["desc", { natural: true }], }, { - code: "var obj = {b_:1, b:3, a:2}", + code: '{"b_":1, "b":3, "a":2}', options: ["desc", { natural: true }], }, { - code: "var obj = {c:2, b_:1, C:3}", + code: '{"c":2, "b_":1, "C":3}', options: ["desc", { natural: true }], }, { - code: "var obj = {a:4, A:3, _:2, $:1}", + code: '{"a":4, "A":3, "_":2, "$":1}', options: ["desc", { natural: true }], }, { - code: "var obj = {A:3, '11':2, 2:4, 1:1}", + code: '{"A":3, "11":2, "2":4, "1":1}', options: ["desc", { natural: true }], }, { - code: "var obj = {è:4, À:3, 'Z':2, '#':1}", + code: '{"è":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}", + code: '{"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", + code: '{"b":3, "a":1, "_":2} // desc, natural, insensitive', + language: "json/jsonc", options: ["desc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {c:2, b:3, a:1}", + code: '{"c":2, "b":3, "a":1}', options: ["desc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {b_:1, b:3, a:2}", + code: '{"b_":1, "b":3, "a":2}', options: ["desc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {c:2, C:3, b_:1}", + code: '{"c":2, "C":3, "b_":1}', options: ["desc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {C:2, c:3, b_:1}", + code: '{"C":2, "c":3, "b_":1}', options: ["desc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {a:4, A:3, _:2, $:1}", + code: '{"a":4, "A":3, "_":2, "$":1}', options: ["desc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {A:3, '11':2, 2:4, 1:1}", + code: '{"A":3, "11":2, "2":4, "1":1}', options: ["desc", { natural: true, caseSensitive: false }], }, { - code: "var obj = {è:4, À:3, 'Z':2, '#':1}", + code: '{"è":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}", + code: '{"a":1, "_":2, "b":3}', options: [ "desc", { natural: true, caseSensitive: false, minKeys: 4 }, @@ -412,69 +311,62 @@ ruleTester.run("sort-keys", rule, { // allowLineSeparatedGroups option { code: ` - var obj = { - e: 1, - f: 2, - g: 3, - - a: 4, - b: 5, - c: 6 + { + "e": 1, + "f": 2, + "g": 3, + + "a": 4, + "b": 5, + "c": 6 } `, options: ["asc", { allowLineSeparatedGroups: true }], }, { code: ` - var obj = { - b: 1, + { + "b": 1, // comment - a: 2, - c: 3 + "a": 2, + "c": 3 } `, + language: "json/jsonc", options: ["asc", { allowLineSeparatedGroups: true }], }, { code: ` - var obj = { - b: 1 + { + "b": 1 , // comment - a: 2, - c: 3 + "a": 2, + "c": 3 } `, + language: "json/jsonc", options: ["asc", { allowLineSeparatedGroups: true }], }, { code: ` - var obj = { - c: 1, - d: 2, + { + "b": "/*", - b() { - }, - e: 4 + "a": "*/" } `, options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, }, { code: ` - var obj = { - c: 1, - d: 2, - // comment + { + "b": 1 - // comment - b() { - }, - e: 4 + ,"a": 2 } `, options: ["asc", { allowLineSeparatedGroups: true }], @@ -482,146 +374,24 @@ ruleTester.run("sort-keys", rule, { }, { code: ` - var obj = { - b, - - [a+b]: 1, - a - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - }, - { - code: ` - var obj = { - c: 1, - d: 2, - - a() { - - }, - - // abce - f: 3, - - /* - - */ - [a+b]: 1, - cc: 1, - e: 2 - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - }, - { - code: ` - var obj = { - b: "/*", - - a: "*/", - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - }, - { - code: ` - var obj = { - b, - /* - */ // - - a - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - }, - { - code: ` - var obj = { - b, - - /* - */ // - a - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - }, - { - code: ` - var obj = { - b: 1 - - ,a: 2 - }; - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - }, - { - code: ` - var obj = { - b: 1 + { + "b": 1 // comment before comma , - a: 2 - }; - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - }, - { - code: ` - var obj = { - b, - - a, - ...z, - c + "a": 2 } `, + language: "json/jsonc", options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 2018 }, - }, - { - code: ` - var obj = { - b, - - [foo()]: [ - - ], - a - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 2018 }, - }, - - // ignoreComputedKeys - { - code: "var obj = { ['b']: 1, a: 2 }", - options: ["asc", { ignoreComputedKeys: true }], - }, - { - code: "var obj = { a: 1, [c]: 2, b: 3 }", - options: ["asc", { ignoreComputedKeys: true }], - }, - { - code: "var obj = { c: 1, ['b']: 2, a: 3 }", - options: ["asc", { ignoreComputedKeys: true }], + languageOptions: { ecmaVersion: 6 }, }, ], invalid: [ // default (asc) { - code: "var obj = {a:1, '':2} // default", + code: '{"a":1, "":2} // default', + language: "json/jsonc", errors: [ { messageId: "sortKeys", @@ -636,295 +406,23 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {a:1, [``]:2} // default", - languageOptions: { ecmaVersion: 6 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "", - prevName: "a", - }, - }, - ], - }, - { - code: "var obj = {a:1, _:2, b:3} // default", - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "_", - prevName: "a", - }, - }, - ], - }, - { - code: "var obj = {a:1, c:2, b:3}", - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "b", - prevName: "c", - }, - }, - ], - }, - { - code: "var obj = {b_:1, a:2, b:3}", - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "a", - prevName: "b_", - }, - }, - ], - }, - { - code: "var obj = {b_:1, c:2, C:3}", - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "C", - prevName: "c", - }, - }, - ], - }, - { - code: "var obj = {$:1, _:2, A:3, a:4}", - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "A", - prevName: "_", - }, - }, - ], - }, - { - code: "var obj = {1:1, 2:4, A:3, '11':2}", - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "11", - prevName: "A", - }, - }, - ], - }, - { - code: "var obj = {'#':1, À:3, 'Z':2, è:4}", - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "Z", - prevName: "À", - }, - }, - ], - }, - { - code: "var obj = { null: 1, [/(?0)/]: 2 }", - languageOptions: { ecmaVersion: 2018 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "/(?0)/", - prevName: "null", - }, - }, - ], - }, - - // not ignore properties not separated by spread properties - { - code: "var obj = {...z, c:1, b:1}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "b", - prevName: "c", - }, - }, - ], - }, - { - code: "var obj = {...z, ...c, d:4, b:1, ...y, ...f, e:2, a:1}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "b", - prevName: "d", - }, - }, - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "a", - prevName: "e", - }, - }, - ], - }, - { - code: "var obj = {c:1, b:1, ...a}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "b", - prevName: "c", - }, - }, - ], - }, - { - code: "var obj = {...z, ...a, c:1, b:1}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "b", - prevName: "c", - }, - }, - ], - }, - { - code: "var obj = {...z, b:1, a:1, ...d, ...c}", - options: [], - languageOptions: { ecmaVersion: 2018 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "a", - prevName: "b", - }, - }, - ], - }, - { - code: "var obj = {...z, a:2, b:0, ...x, ...c}", - options: ["desc"], - languageOptions: { ecmaVersion: 2018 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "desc", - thisName: "b", - prevName: "a", - }, - }, - ], - }, - { - code: "var obj = {...z, a:2, b:0, ...x}", - options: ["desc"], - languageOptions: { ecmaVersion: 2018 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "desc", - thisName: "b", - prevName: "a", - }, - }, - ], - }, - { - code: "var obj = {...z, '':1, a:2}", - options: ["desc"], - languageOptions: { ecmaVersion: 2018 }, + code: '{"a":1, "_":2, "b":3} // default', + language: "json/jsonc", errors: [ { messageId: "sortKeys", data: { natural: "", insensitive: "", - order: "desc", - thisName: "a", - prevName: "", + order: "asc", + thisName: "_", + prevName: "a", }, }, ], }, - - // ignore non-simple computed properties, but their position shouldn't affect other comparisons. { - code: "var obj = {a:1, [b+c]:2, '':3}", - languageOptions: { ecmaVersion: 6 }, + code: '{"a":1, "c":2, "b":3}', errors: [ { messageId: "sortKeys", @@ -932,51 +430,44 @@ ruleTester.run("sort-keys", rule, { natural: "", insensitive: "", order: "asc", - thisName: "", - prevName: "a", + thisName: "b", + prevName: "c", }, }, ], }, { - code: "var obj = {'':1, [b+c]:2, a:3}", - options: ["desc"], - languageOptions: { ecmaVersion: 6 }, + code: '{"b_":1, "a":2, "b":3}', errors: [ { messageId: "sortKeys", data: { natural: "", insensitive: "", - order: "desc", + order: "asc", thisName: "a", - prevName: "", + prevName: "b_", }, }, ], }, { - code: "var obj = {b:1, [f()]:2, '':3, a:4}", - options: ["desc"], - languageOptions: { ecmaVersion: 6 }, + code: '{"b_":1, "c":2, "C":3}', errors: [ { messageId: "sortKeys", data: { natural: "", insensitive: "", - order: "desc", - thisName: "a", - prevName: "", + order: "asc", + thisName: "C", + prevName: "c", }, }, ], }, - - // not ignore simple computed properties. { - code: "var obj = {a:1, b:3, [a]: -1, c:2}", - languageOptions: { ecmaVersion: 6 }, + code: '{"$":1, "_":2, "A":3, "a":4}', errors: [ { messageId: "sortKeys", @@ -984,16 +475,14 @@ ruleTester.run("sort-keys", rule, { natural: "", insensitive: "", order: "asc", - thisName: "a", - prevName: "b", + thisName: "A", + prevName: "_", }, }, ], }, - - // nested { - code: "var obj = {a:1, c:{y:1, x:1}, b:1}", + code: '{"1":1, "2":4, "A":3, "11":2}', errors: [ { messageId: "sortKeys", @@ -1001,18 +490,23 @@ ruleTester.run("sort-keys", rule, { natural: "", insensitive: "", order: "asc", - thisName: "x", - prevName: "y", + thisName: "11", + prevName: "A", }, }, + ], + }, + { + code: '{"#":1, "À":3, "Z":2, "è":4}', + errors: [ { messageId: "sortKeys", data: { natural: "", insensitive: "", order: "asc", - thisName: "b", - prevName: "c", + thisName: "Z", + prevName: "À", }, }, ], @@ -1020,7 +514,8 @@ ruleTester.run("sort-keys", rule, { // asc { - code: "var obj = {a:1, _:2, b:3} // asc", + code: '{"a":1, "_":2, "b":3} // asc', + language: "json/jsonc", options: ["asc"], errors: [ { @@ -1036,7 +531,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {a:1, c:2, b:3}", + code: '{"a":1, "c":2, "b":3}', options: ["asc"], errors: [ { @@ -1052,7 +547,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, a:2, b:3}", + code: '{"b_":1, "a":2, "b":3}', options: ["asc"], errors: [ { @@ -1068,7 +563,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, c:2, C:3}", + code: '{"b_":1, "c":2, "C":3}', options: ["asc"], errors: [ { @@ -1084,7 +579,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {$:1, _:2, A:3, a:4}", + code: '{"$":1, "_":2, "A":3, "a":4}', options: ["asc"], errors: [ { @@ -1100,7 +595,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {1:1, 2:4, A:3, '11':2}", + code: '{"1":1, "2":4, "A":3, "11":2}', options: ["asc"], errors: [ { @@ -1116,7 +611,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + code: '{"#":1, "À":3, "Z":2, "è":4}', options: ["asc"], errors: [ { @@ -1134,7 +629,7 @@ 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}", + code: '{"a":1, "_":2, "b":3}', options: ["asc", { minKeys: 3 }], errors: [ { @@ -1152,7 +647,8 @@ ruleTester.run("sort-keys", rule, { // asc, insensitive { - code: "var obj = {a:1, _:2, b:3} // asc, insensitive", + code: '{"a":1, "_":2, "b":3} // asc, insensitive', + language: "json/jsonc", options: ["asc", { caseSensitive: false }], errors: [ { @@ -1168,7 +664,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {a:1, c:2, b:3}", + code: '{"a":1, "c":2, "b":3}', options: ["asc", { caseSensitive: false }], errors: [ { @@ -1184,7 +680,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, a:2, b:3}", + code: '{"b_":1, "a":2, "b":3}', options: ["asc", { caseSensitive: false }], errors: [ { @@ -1200,7 +696,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {$:1, A:3, _:2, a:4}", + code: '{"$":1, "A":3, "_":2, "a":4}', options: ["asc", { caseSensitive: false }], errors: [ { @@ -1216,7 +712,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {1:1, 2:4, A:3, '11':2}", + code: '{"1":1, "2":4, "A":3, "11":2}', options: ["asc", { caseSensitive: false }], errors: [ { @@ -1232,7 +728,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + code: '{"#":1, "À":3, "Z":2, "è":4}', options: ["asc", { caseSensitive: false }], errors: [ { @@ -1250,7 +746,7 @@ 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}", + code: '{"a":1, "_":2, "b":3}', options: ["asc", { caseSensitive: false, minKeys: 3 }], errors: [ { @@ -1268,7 +764,8 @@ ruleTester.run("sort-keys", rule, { // asc, natural { - code: "var obj = {a:1, _:2, b:3} // asc, natural", + code: '{"a":1, "_":2, "b":3} // asc, natural', + language: "json/jsonc", options: ["asc", { natural: true }], errors: [ { @@ -1284,7 +781,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {a:1, c:2, b:3}", + code: '{"a":1, "c":2, "b":3}', options: ["asc", { natural: true }], errors: [ { @@ -1300,7 +797,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, a:2, b:3}", + code: '{"b_":1, "a":2, "b":3}', options: ["asc", { natural: true }], errors: [ { @@ -1316,7 +813,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, c:2, C:3}", + code: '{"b_":1, "c":2, "C":3}', options: ["asc", { natural: true }], errors: [ { @@ -1332,7 +829,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {$:1, A:3, _:2, a:4}", + code: '{"$":1, "A":3, "_":2, "a":4}', options: ["asc", { natural: true }], errors: [ { @@ -1348,7 +845,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {1:1, 2:4, A:3, '11':2}", + code: '{"1":1, "2":4, "A":3, "11":2}', options: ["asc", { natural: true }], errors: [ { @@ -1364,7 +861,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + code: '{"#":1, "À":3, "Z":2, "è":4}', options: ["asc", { natural: true }], errors: [ { @@ -1382,7 +879,7 @@ 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}", + code: '{"a":1, "_":2, "b":3}', options: ["asc", { natural: true, minKeys: 2 }], errors: [ { @@ -1400,7 +897,8 @@ ruleTester.run("sort-keys", rule, { // asc, natural, insensitive { - code: "var obj = {a:1, _:2, b:3} // asc, natural, insensitive", + code: '{"a":1, "_":2, "b":3} // asc, natural, insensitive', + language: "json/jsonc", options: ["asc", { natural: true, caseSensitive: false }], errors: [ { @@ -1416,7 +914,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {a:1, c:2, b:3}", + code: '{"a":1, "c":2, "b":3}', options: ["asc", { natural: true, caseSensitive: false }], errors: [ { @@ -1432,7 +930,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, a:2, b:3}", + code: '{"b_":1, "a":2, "b":3}', options: ["asc", { natural: true, caseSensitive: false }], errors: [ { @@ -1448,7 +946,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {$:1, A:3, _:2, a:4}", + code: '{"$":1, "A":3, "_":2, "a":4}', options: ["asc", { natural: true, caseSensitive: false }], errors: [ { @@ -1464,7 +962,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {1:1, '11':2, 2:4, A:3}", + code: '{"1":1, "11":2, "2":4, "A":3}', options: ["asc", { natural: true, caseSensitive: false }], errors: [ { @@ -1480,7 +978,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + code: '{"#":1, "À":3, "Z":2, "è":4}', options: ["asc", { natural: true, caseSensitive: false }], errors: [ { @@ -1498,7 +996,7 @@ 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}", + code: '{"a":1, "_":2, "b":3}', options: [ "asc", { natural: true, caseSensitive: false, minKeys: 3 }, @@ -1519,25 +1017,9 @@ ruleTester.run("sort-keys", rule, { // desc { - code: "var obj = {'':1, a:'2'} // desc", - options: ["desc"], - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "desc", - thisName: "a", - prevName: "", - }, - }, - ], - }, - { - code: "var obj = {[``]:1, a:'2'} // desc", + code: '{"":1, "a":2} // desc', + language: "json/jsonc", options: ["desc"], - languageOptions: { ecmaVersion: 6 }, errors: [ { messageId: "sortKeys", @@ -1552,7 +1034,8 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {a:1, _:2, b:3} // desc", + code: '{"a":1, "_":2, "b":3} // desc', + language: "json/jsonc", options: ["desc"], errors: [ { @@ -1568,7 +1051,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {a:1, c:2, b:3}", + code: '{"a":1, "c":2, "b":3}', options: ["desc"], errors: [ { @@ -1584,7 +1067,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, a:2, b:3}", + code: '{"b_":1, "a":2, "b":3}', options: ["desc"], errors: [ { @@ -1600,7 +1083,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, c:2, C:3}", + code: '{"b_":1, "c":2, "C":3}', options: ["desc"], errors: [ { @@ -1616,7 +1099,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {$:1, _:2, A:3, a:4}", + code: '{"$":1, "_":2, "A":3, "a":4}', options: ["desc"], errors: [ { @@ -1642,7 +1125,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {1:1, 2:4, A:3, '11':2}", + code: '{"1":1, "2":4, "A":3, "11":2}', options: ["desc"], errors: [ { @@ -1668,7 +1151,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + code: '{"#":1, "À":3, "Z":2, "è":4}', options: ["desc"], errors: [ { @@ -1696,7 +1179,7 @@ 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}", + code: '{"a":1, "_":2, "b":3}', options: ["desc", { minKeys: 3 }], errors: [ { @@ -1714,7 +1197,8 @@ ruleTester.run("sort-keys", rule, { // desc, insensitive { - code: "var obj = {a:1, _:2, b:3} // desc, insensitive", + code: '{"a":1, "_":2, "b":3} // desc, insensitive', + language: "json/jsonc", options: ["desc", { caseSensitive: false }], errors: [ { @@ -1730,7 +1214,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {a:1, c:2, b:3}", + code: '{"a":1, "c":2, "b":3}', options: ["desc", { caseSensitive: false }], errors: [ { @@ -1746,7 +1230,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, a:2, b:3}", + code: '{"b_":1, "a":2, "b":3}', options: ["desc", { caseSensitive: false }], errors: [ { @@ -1762,7 +1246,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, c:2, C:3}", + code: '{"b_":1, "c":2, "C":3}', options: ["desc", { caseSensitive: false }], errors: [ { @@ -1778,7 +1262,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {$:1, _:2, A:3, a:4}", + code: '{"$":1, "_":2, "A":3, "a":4}', options: ["desc", { caseSensitive: false }], errors: [ { @@ -1804,7 +1288,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {1:1, 2:4, A:3, '11':2}", + code: '{"1":1, "2":4, "A":3, "11":2}', options: ["desc", { caseSensitive: false }], errors: [ { @@ -1830,7 +1314,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + code: '{"#":1, "À":3, "Z":2, "è":4}', options: ["desc", { caseSensitive: false }], errors: [ { @@ -1858,7 +1342,7 @@ 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}", + code: '{"a":1, "_":2, "b":3}', options: ["desc", { caseSensitive: false, minKeys: 2 }], errors: [ { @@ -1876,7 +1360,8 @@ ruleTester.run("sort-keys", rule, { // desc, natural { - code: "var obj = {a:1, _:2, b:3} // desc, natural", + code: '{"a":1, "_":2, "b":3} // desc, natural', + language: "json/jsonc", options: ["desc", { natural: true }], errors: [ { @@ -1892,7 +1377,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {a:1, c:2, b:3}", + code: '{"a":1, "c":2, "b":3}', options: ["desc", { natural: true }], errors: [ { @@ -1908,7 +1393,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, a:2, b:3}", + code: '{"b_":1, "a":2, "b":3}', options: ["desc", { natural: true }], errors: [ { @@ -1924,7 +1409,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, c:2, C:3}", + code: '{"b_":1, "c":2, "C":3}', options: ["desc", { natural: true }], errors: [ { @@ -1940,7 +1425,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {$:1, _:2, A:3, a:4}", + code: '{"$":1, "_":2, "A":3, "a":4}', options: ["desc", { natural: true }], errors: [ { @@ -1976,7 +1461,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {1:1, 2:4, A:3, '11':2}", + code: '{"1":1, "2":4, "A":3, "11":2}', options: ["desc", { natural: true }], errors: [ { @@ -2002,7 +1487,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + code: '{"#":1, "À":3, "Z":2, "è":4}', options: ["desc", { natural: true }], errors: [ { @@ -2030,7 +1515,7 @@ 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}", + code: '{"a":1, "_":2, "b":3}', options: ["desc", { natural: true, minKeys: 3 }], errors: [ { @@ -2048,7 +1533,8 @@ ruleTester.run("sort-keys", rule, { // desc, natural, insensitive { - code: "var obj = {a:1, _:2, b:3} // desc, natural, insensitive", + code: '{"a":1, "_":2, "b":3} // desc, natural, insensitive', + language: "json/jsonc", options: ["desc", { natural: true, caseSensitive: false }], errors: [ { @@ -2064,7 +1550,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {a:1, c:2, b:3}", + code: '{"a":1, "c":2, "b":3}', options: ["desc", { natural: true, caseSensitive: false }], errors: [ { @@ -2080,7 +1566,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, a:2, b:3}", + code: '{"b_":1, "a":2, "b":3}', options: ["desc", { natural: true, caseSensitive: false }], errors: [ { @@ -2096,7 +1582,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {b_:1, c:2, C:3}", + code: '{"b_":1, "c":2, "C":3}', options: ["desc", { natural: true, caseSensitive: false }], errors: [ { @@ -2112,7 +1598,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {$:1, _:2, A:3, a:4}", + code: '{"$":1, "_":2, "A":3, "a":4}', options: ["desc", { natural: true, caseSensitive: false }], errors: [ { @@ -2138,7 +1624,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {1:1, 2:4, '11':2, A:3}", + code: '{"1":1, "2":4, "11":2, "A":3}', options: ["desc", { natural: true, caseSensitive: false }], errors: [ { @@ -2174,7 +1660,7 @@ ruleTester.run("sort-keys", rule, { ], }, { - code: "var obj = {'#':1, À:3, 'Z':2, è:4}", + code: '{"#":1, "À":3, "Z":2, "è":4}', options: ["desc", { natural: true, caseSensitive: false }], errors: [ { @@ -2202,7 +1688,7 @@ ruleTester.run("sort-keys", rule, { // desc, natural, insensitive should error when number of keys is greater than or equal to minKeys { - code: "var obj = {a:1, _:2, b:3}", + code: '{"a":1, "_":2, "b":3}', options: [ "desc", { natural: true, caseSensitive: false, minKeys: 2 }, @@ -2224,10 +1710,10 @@ ruleTester.run("sort-keys", rule, { // When allowLineSeparatedGroups option is false { code: ` - var obj = { - b: 1, - c: 2, - a: 3 + { + "b": 1, + "c": 2, + "a": 3 } `, options: ["asc", { allowLineSeparatedGroups: false }], @@ -2244,229 +1730,13 @@ ruleTester.run("sort-keys", rule, { }, ], }, - { - code: ` - let obj = { - b - - ,a - } - `, - options: ["asc", { allowLineSeparatedGroups: false }], - languageOptions: { ecmaVersion: 6 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "a", - prevName: "b", - }, - }, - ], - }, - { - code: ` - let obj = { - b - - ,a - } - `, - languageOptions: { ecmaVersion: 6 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "a", - prevName: "b", - }, - }, - ], - }, // When allowLineSeparatedGroups option is true { code: ` - var obj = { - b: 1, - c () { - - }, - a: 3 - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "a", - prevName: "c", - }, - }, - ], - }, - { - code: ` - var obj = { - a: 1, - b: 2, - - z () { - - }, - y: 3 - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "y", - prevName: "z", - }, - }, - ], - }, - { - code: ` - var obj = { - b: 1, - c () { - }, - // comment - a: 3 - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "a", - prevName: "c", - }, - }, - ], - }, - { - code: ` - var obj = { - b, - [a+b]: 1, - a // sort-keys: 'a' should be before 'b' - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "a", - prevName: "b", - }, - }, - ], - }, - { - code: ` - var obj = { - c: 1, - d: 2, - // comment - // comment - b() { - }, - e: 4 - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "b", - prevName: "d", - }, - }, - ], - }, - { - code: ` - var obj = { - c: 1, - d: 2, - - z() { - - }, - f: 3, - /* - - - */ - [a+b]: 1, - b: 1, - e: 2 - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "f", - prevName: "z", - }, - }, - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "b", - prevName: "f", - }, - }, - ], - }, - { - code: ` - var obj = { - b: "/*", - a: "*/", + { + "b": "/*", + "a": "*/" } `, options: ["asc", { allowLineSeparatedGroups: true }], @@ -2485,56 +1755,15 @@ ruleTester.run("sort-keys", rule, { }, { code: ` - var obj = { - b: 1 + { + "b": 1 // comment before comma - , a: 2 - }; - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "a", - prevName: "b", - }, - }, - ], - }, - { - code: ` - let obj = { - b, - [foo()]: [ - // ↓ this blank is inside a property and therefore should not count - - ], - a + , "a": 2 } `, + language: "json/jsonc", options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 2018 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "a", - prevName: "b", - }, - }, - ], - }, - { - code: "var obj = { d: 1, ['c']: 2, b: 3, a: 4 }", - options: ["asc", { ignoreComputedKeys: true, minKeys: 4 }], + languageOptions: { ecmaVersion: 6 }, errors: [ { messageId: "sortKeys", From 162b1b5620fb14f375cf8a9c0c59533601aed980 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Thu, 9 Jan 2025 00:33:47 -0600 Subject: [PATCH 04/23] account for line skips --- src/rules/sort-keys.js | 55 +++-------------------------------- tests/rules/sort-keys.test.js | 51 ++++++++++++++++---------------- 2 files changed, 30 insertions(+), 76 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index ffb79e8..6ee9b70 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -96,9 +96,6 @@ export default { order + (insensitive ? "I" : "") + (natural ? "N" : "") ]; - // The stack to save the previous property's name for each object literals. - // let stack = null; - return { Object(node) { let prevMember; @@ -111,7 +108,10 @@ export default { for (const member of node.members) { const thisName = member.name.value; - if (prevMember) { + if ( + prevMember && + member.loc.start.line - prevMember.loc.end.line < 2 + ) { if (!isValidOrder(prevName, thisName)) { context.report({ node, @@ -133,54 +133,7 @@ export default { prevMember = member; prevName = thisName; } - // stack = { - // upper: stack, - // prevNode: null, - // prevBlankLine: false, - // prevName: null, - // numKeys: node.members.length, - // }; }, - - // Member(node) { - // const prevName = stack.prevName; - // const numKeys = stack.numKeys; - // const thisName = node.name.value; - - // stack.prevNode = node; - - // if (thisName !== null) { - // stack.prevName = thisName; - // } - - // // if (allowLineSeparatedGroups && isBlankLineBetweenNodes) { - // // stack.prevBlankLine = thisName === null; - // // return; - // // } - - // if ( - // prevName === null || - // thisName === null || - // numKeys < minKeys - // ) { - // return; - // } - - // if (!isValidOrder(prevName, thisName)) { - // context.report({ - // node, - // loc: node.name.loc, - // messageId: "sortKeys", - // data: { - // thisName, - // prevName, - // order, - // insensitive: insensitive ? "insensitive " : "", - // natural: natural ? "natural " : "", - // }, - // }); - // } - // }, }; }, }; diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js index 02800e8..13d6789 100644 --- a/tests/rules/sort-keys.test.js +++ b/tests/rules/sort-keys.test.js @@ -1,5 +1,5 @@ /** - * @fileoverview Tests for sort-keys rule. + * @fileoverview Tests for sort-keys rule. Cribbed from https://github.com/eslint/eslint/blob/main/tests/lib/rules/sort-keys.js. TODO: How to maintain parity with eslint/sort-keys? * @author Robin Thomas */ @@ -1753,29 +1753,30 @@ ruleTester.run("sort-keys", rule, { }, ], }, - { - code: ` - { - "b": 1 - // comment before comma - , "a": 2 - } - `, - language: "json/jsonc", - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - errors: [ - { - messageId: "sortKeys", - data: { - natural: "", - insensitive: "", - order: "asc", - thisName: "a", - prevName: "b", - }, - }, - ], - }, + // TODO: This reports in eslint/sort-keys but does not here. Would need to check whether each member is preceded by a comment, which would require mapping members to specific tokens in the AST tree, and there isn't much support for that in the JSON language service right now + // { + // code: ` + // { + // "b": 1 + // // comment before comma + // , "a": 2 + // } + // `, + // language: "json/jsonc", + // options: ["asc", { allowLineSeparatedGroups: true }], + // languageOptions: { ecmaVersion: 6 }, + // errors: [ + // { + // messageId: "sortKeys", + // data: { + // natural: "", + // insensitive: "", + // order: "asc", + // thisName: "a", + // prevName: "b", + // }, + // }, + // ], + // }, ], }); From a4aad1dfc7e9e2c8eb33fc9a284b56a1173f7543 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Thu, 9 Jan 2025 08:14:10 -0600 Subject: [PATCH 05/23] more semantic sort-keys code --- src/rules/sort-keys.js | 78 +++-- tests/rules/sort-keys.test.js | 528 +++++++++++++++++----------------- 2 files changed, 299 insertions(+), 307 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index 6ee9b70..9f558e2 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -5,37 +5,30 @@ import naturalCompare from "natural-compare"; -/** - * Functions which check that the given 2 names are in specific order. - * - * Postfix `I` is meant insensitive. - * Postfix `N` is meant natural. - * @private - */ -const isValidOrders = { - asc(a, b) { - return a <= b; - }, - ascI(a, b) { - return a.toLowerCase() <= b.toLowerCase(); - }, - ascN(a, b) { - return naturalCompare(a, b) <= 0; - }, - ascIN(a, b) { - return naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0; - }, - desc(a, b) { - return isValidOrders.asc(b, a); - }, - descI(a, b) { - return isValidOrders.ascI(b, a); - }, - descN(a, b) { - return isValidOrders.ascN(b, a); +const comparators = { + ascending: { + alphanumeric: { + sensitive: (a, b) => a <= b, + insensitive: (a, b) => a.toLowerCase() <= b.toLowerCase(), + }, + natural: { + sensitive: (a, b) => naturalCompare(a, b) <= 0, + insensitive: (a, b) => + naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0, + }, }, - descIN(a, b) { - return isValidOrders.ascIN(b, a); + descending: { + alphanumeric: { + sensitive: (a, b) => + comparators.ascending.alphanumeric.sensitive(b, a), + insensitive: (a, b) => + comparators.ascending.alphanumeric.insensitive(b, a), + }, + natural: { + sensitive: (a, b) => comparators.ascending.natural.sensitive(b, a), + insensitive: (a, b) => + comparators.ascending.natural.insensitive(b, a), + }, }, }; @@ -57,7 +50,7 @@ export default { messages: { sortKeys: - "Expected object keys to be in {{natural}}{{insensitive}}{{order}}ending order. '{{thisName}}' should be before '{{prevName}}'.", + "Expected object keys to be in {{sortName}} case-{{sensitivity}} {{direction}} order. '{{thisName}}' should be before '{{prevName}}'.", }, schema: [ @@ -89,12 +82,13 @@ export default { }, create(context) { - const [order, { caseSensitive, natural, minKeys }] = context.options; - const insensitive = !caseSensitive; - const isValidOrder = - isValidOrders[ - order + (insensitive ? "I" : "") + (natural ? "N" : "") - ]; + const [directionShort, { caseSensitive, natural, minKeys }] = + context.options; + + const direction = directionShort === "asc" ? "ascending" : "descending"; + const sortName = natural ? "natural" : "alphanumeric"; + const sensitivity = caseSensitive ? "sensitive" : "insensitive"; + const isValidOrder = comparators[direction][sortName][sensitivity]; return { Object(node) { @@ -112,7 +106,7 @@ export default { prevMember && member.loc.start.line - prevMember.loc.end.line < 2 ) { - if (!isValidOrder(prevName, thisName)) { + if (isValidOrder(prevName, thisName) === false) { context.report({ node, loc: member.name.loc, @@ -120,11 +114,9 @@ export default { data: { thisName, prevName, - order, - insensitive: insensitive - ? "insensitive " - : "", - natural: natural ? "natural " : "", + direction, + sensitivity, + sortName, }, }); } diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js index 13d6789..0f115db 100644 --- a/tests/rules/sort-keys.test.js +++ b/tests/rules/sort-keys.test.js @@ -396,9 +396,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "", prevName: "a", }, @@ -412,9 +412,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "_", prevName: "a", }, @@ -427,9 +427,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "b", prevName: "c", }, @@ -442,9 +442,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "a", prevName: "b_", }, @@ -457,9 +457,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "C", prevName: "c", }, @@ -472,9 +472,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "A", prevName: "_", }, @@ -487,9 +487,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "11", prevName: "A", }, @@ -502,9 +502,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "Z", prevName: "À", }, @@ -521,9 +521,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "_", prevName: "a", }, @@ -537,9 +537,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "b", prevName: "c", }, @@ -553,9 +553,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "a", prevName: "b_", }, @@ -569,9 +569,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "C", prevName: "c", }, @@ -585,9 +585,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "A", prevName: "_", }, @@ -601,9 +601,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "11", prevName: "A", }, @@ -617,9 +617,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "Z", prevName: "À", }, @@ -635,9 +635,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "_", prevName: "a", }, @@ -654,9 +654,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "asc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "ascending", thisName: "_", prevName: "a", }, @@ -670,9 +670,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "asc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "ascending", thisName: "b", prevName: "c", }, @@ -686,9 +686,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "asc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "ascending", thisName: "a", prevName: "b_", }, @@ -702,9 +702,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "asc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "ascending", thisName: "_", prevName: "A", }, @@ -718,9 +718,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "asc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "ascending", thisName: "11", prevName: "A", }, @@ -734,9 +734,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "asc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "ascending", thisName: "Z", prevName: "À", }, @@ -752,9 +752,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "asc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "ascending", thisName: "_", prevName: "a", }, @@ -771,9 +771,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "asc", + sortName: "natural", + sensitivity: "sensitive", + direction: "ascending", thisName: "_", prevName: "a", }, @@ -787,9 +787,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "asc", + sortName: "natural", + sensitivity: "sensitive", + direction: "ascending", thisName: "b", prevName: "c", }, @@ -803,9 +803,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "asc", + sortName: "natural", + sensitivity: "sensitive", + direction: "ascending", thisName: "a", prevName: "b_", }, @@ -819,9 +819,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "asc", + sortName: "natural", + sensitivity: "sensitive", + direction: "ascending", thisName: "C", prevName: "c", }, @@ -835,9 +835,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "asc", + sortName: "natural", + sensitivity: "sensitive", + direction: "ascending", thisName: "_", prevName: "A", }, @@ -851,9 +851,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "asc", + sortName: "natural", + sensitivity: "sensitive", + direction: "ascending", thisName: "11", prevName: "A", }, @@ -867,9 +867,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "asc", + sortName: "natural", + sensitivity: "sensitive", + direction: "ascending", thisName: "Z", prevName: "À", }, @@ -885,9 +885,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "asc", + sortName: "natural", + sensitivity: "sensitive", + direction: "ascending", thisName: "_", prevName: "a", }, @@ -904,9 +904,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "asc", + sortName: "natural", + sensitivity: "insensitive", + direction: "ascending", thisName: "_", prevName: "a", }, @@ -920,9 +920,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "asc", + sortName: "natural", + sensitivity: "insensitive", + direction: "ascending", thisName: "b", prevName: "c", }, @@ -936,9 +936,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "asc", + sortName: "natural", + sensitivity: "insensitive", + direction: "ascending", thisName: "a", prevName: "b_", }, @@ -952,9 +952,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "asc", + sortName: "natural", + sensitivity: "insensitive", + direction: "ascending", thisName: "_", prevName: "A", }, @@ -968,9 +968,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "asc", + sortName: "natural", + sensitivity: "insensitive", + direction: "ascending", thisName: "2", prevName: "11", }, @@ -984,9 +984,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "asc", + sortName: "natural", + sensitivity: "insensitive", + direction: "ascending", thisName: "Z", prevName: "À", }, @@ -1005,9 +1005,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "asc", + sortName: "natural", + sensitivity: "insensitive", + direction: "ascending", thisName: "_", prevName: "a", }, @@ -1024,9 +1024,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "desc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", thisName: "a", prevName: "", }, @@ -1041,9 +1041,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "desc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", thisName: "b", prevName: "_", }, @@ -1057,9 +1057,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "desc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", thisName: "c", prevName: "a", }, @@ -1073,9 +1073,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "desc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", thisName: "b", prevName: "a", }, @@ -1089,9 +1089,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "desc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", thisName: "c", prevName: "b_", }, @@ -1105,9 +1105,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "desc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", thisName: "_", prevName: "$", }, @@ -1115,9 +1115,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "desc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", thisName: "a", prevName: "A", }, @@ -1131,9 +1131,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "desc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", thisName: "2", prevName: "1", }, @@ -1141,9 +1141,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "desc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", thisName: "A", prevName: "2", }, @@ -1157,9 +1157,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "desc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", thisName: "À", prevName: "#", }, @@ -1167,9 +1167,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "desc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", thisName: "è", prevName: "Z", }, @@ -1185,9 +1185,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "desc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", thisName: "b", prevName: "_", }, @@ -1204,9 +1204,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "desc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "descending", thisName: "b", prevName: "_", }, @@ -1220,9 +1220,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "desc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "descending", thisName: "c", prevName: "a", }, @@ -1236,9 +1236,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "desc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "descending", thisName: "b", prevName: "a", }, @@ -1252,9 +1252,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "desc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "descending", thisName: "c", prevName: "b_", }, @@ -1268,9 +1268,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "desc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "descending", thisName: "_", prevName: "$", }, @@ -1278,9 +1278,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "desc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "descending", thisName: "A", prevName: "_", }, @@ -1294,9 +1294,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "desc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "descending", thisName: "2", prevName: "1", }, @@ -1304,9 +1304,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "desc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "descending", thisName: "A", prevName: "2", }, @@ -1320,9 +1320,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "desc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "descending", thisName: "À", prevName: "#", }, @@ -1330,9 +1330,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "desc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "descending", thisName: "è", prevName: "Z", }, @@ -1348,9 +1348,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "insensitive ", - order: "desc", + sortName: "alphanumeric", + sensitivity: "insensitive", + direction: "descending", thisName: "b", prevName: "_", }, @@ -1367,9 +1367,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "desc", + sortName: "natural", + sensitivity: "sensitive", + direction: "descending", thisName: "b", prevName: "_", }, @@ -1383,9 +1383,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "desc", + sortName: "natural", + sensitivity: "sensitive", + direction: "descending", thisName: "c", prevName: "a", }, @@ -1399,9 +1399,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "desc", + sortName: "natural", + sensitivity: "sensitive", + direction: "descending", thisName: "b", prevName: "a", }, @@ -1415,9 +1415,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "desc", + sortName: "natural", + sensitivity: "sensitive", + direction: "descending", thisName: "c", prevName: "b_", }, @@ -1431,9 +1431,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "desc", + sortName: "natural", + sensitivity: "sensitive", + direction: "descending", thisName: "_", prevName: "$", }, @@ -1441,9 +1441,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "desc", + sortName: "natural", + sensitivity: "sensitive", + direction: "descending", thisName: "A", prevName: "_", }, @@ -1451,9 +1451,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "desc", + sortName: "natural", + sensitivity: "sensitive", + direction: "descending", thisName: "a", prevName: "A", }, @@ -1467,9 +1467,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "desc", + sortName: "natural", + sensitivity: "sensitive", + direction: "descending", thisName: "2", prevName: "1", }, @@ -1477,9 +1477,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "desc", + sortName: "natural", + sensitivity: "sensitive", + direction: "descending", thisName: "A", prevName: "2", }, @@ -1493,9 +1493,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "desc", + sortName: "natural", + sensitivity: "sensitive", + direction: "descending", thisName: "À", prevName: "#", }, @@ -1503,9 +1503,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "desc", + sortName: "natural", + sensitivity: "sensitive", + direction: "descending", thisName: "è", prevName: "Z", }, @@ -1521,9 +1521,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "", - order: "desc", + sortName: "natural", + sensitivity: "sensitive", + direction: "descending", thisName: "b", prevName: "_", }, @@ -1540,9 +1540,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "desc", + sortName: "natural", + sensitivity: "insensitive", + direction: "descending", thisName: "b", prevName: "_", }, @@ -1556,9 +1556,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "desc", + sortName: "natural", + sensitivity: "insensitive", + direction: "descending", thisName: "c", prevName: "a", }, @@ -1572,9 +1572,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "desc", + sortName: "natural", + sensitivity: "insensitive", + direction: "descending", thisName: "b", prevName: "a", }, @@ -1588,9 +1588,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "desc", + sortName: "natural", + sensitivity: "insensitive", + direction: "descending", thisName: "c", prevName: "b_", }, @@ -1604,9 +1604,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "desc", + sortName: "natural", + sensitivity: "insensitive", + direction: "descending", thisName: "_", prevName: "$", }, @@ -1614,9 +1614,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "desc", + sortName: "natural", + sensitivity: "insensitive", + direction: "descending", thisName: "A", prevName: "_", }, @@ -1630,9 +1630,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "desc", + sortName: "natural", + sensitivity: "insensitive", + direction: "descending", thisName: "2", prevName: "1", }, @@ -1640,9 +1640,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "desc", + sortName: "natural", + sensitivity: "insensitive", + direction: "descending", thisName: "11", prevName: "2", }, @@ -1650,9 +1650,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "desc", + sortName: "natural", + sensitivity: "insensitive", + direction: "descending", thisName: "A", prevName: "11", }, @@ -1666,9 +1666,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "desc", + sortName: "natural", + sensitivity: "insensitive", + direction: "descending", thisName: "À", prevName: "#", }, @@ -1676,9 +1676,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "desc", + sortName: "natural", + sensitivity: "insensitive", + direction: "descending", thisName: "è", prevName: "Z", }, @@ -1697,9 +1697,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "natural ", - insensitive: "insensitive ", - order: "desc", + sortName: "natural", + sensitivity: "insensitive", + direction: "descending", thisName: "b", prevName: "_", }, @@ -1721,9 +1721,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "a", prevName: "c", }, @@ -1744,9 +1744,9 @@ ruleTester.run("sort-keys", rule, { { messageId: "sortKeys", data: { - natural: "", - insensitive: "", - order: "asc", + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", thisName: "a", prevName: "b", }, @@ -1769,9 +1769,9 @@ ruleTester.run("sort-keys", rule, { // { // messageId: "sortKeys", // data: { - // natural: "", - // insensitive: "", - // order: "asc", + // sortName: "alphanumeric", + // sensitivity: "sensitive", + // direction: "ascending", // thisName: "a", // prevName: "b", // }, From 357aacfa1424fc5935fe5d3ad954a35cfc3ce218 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Thu, 9 Jan 2025 08:34:37 -0600 Subject: [PATCH 06/23] Handle when object members are joined by a comment --- src/rules/sort-keys.js | 36 ++++++++++++++++++++----- tests/rules/sort-keys.test.js | 49 +++++++++++++++++------------------ 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index 9f558e2..b50a827 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -90,10 +90,16 @@ export default { const sensitivity = caseSensitive ? "sensitive" : "insensitive"; const isValidOrder = comparators[direction][sortName][sensitivity]; + const commentLineRanges = new Set(); + for (const comment of context.sourceCode.comments) { + commentLineRanges.add( + `${comment.loc.start.line}:${comment.loc.end.line}`, + ); + } + return { Object(node) { let prevMember; - let prevName; if (node.members.length < minKeys) { return; @@ -102,11 +108,28 @@ export default { for (const member of node.members) { const thisName = member.name.value; - if ( - prevMember && - member.loc.start.line - prevMember.loc.end.line < 2 - ) { - if (isValidOrder(prevName, thisName) === false) { + if (prevMember) { + const prevName = prevMember?.name.value; + const prevLine = prevMember?.loc.end.line; + const thisLine = member.loc.start.line; + + const membersAreJoinedByComment = + commentLineRanges.has(`${prevLine}:${thisLine}`) || + commentLineRanges.has( + `${prevLine + 1}:${thisLine}`, + ) || + commentLineRanges.has( + `${prevLine}:${thisLine - 1}`, + ) || + commentLineRanges.has( + `${prevLine + 1}:${thisLine - 1}`, + ); + + if ( + (thisLine - prevLine < 2 || + membersAreJoinedByComment) && + isValidOrder(prevName, thisName) === false + ) { context.report({ node, loc: member.name.loc, @@ -123,7 +146,6 @@ export default { } prevMember = member; - prevName = thisName; } }, }; diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js index 0f115db..ca4ee28 100644 --- a/tests/rules/sort-keys.test.js +++ b/tests/rules/sort-keys.test.js @@ -1753,30 +1753,29 @@ ruleTester.run("sort-keys", rule, { }, ], }, - // TODO: This reports in eslint/sort-keys but does not here. Would need to check whether each member is preceded by a comment, which would require mapping members to specific tokens in the AST tree, and there isn't much support for that in the JSON language service right now - // { - // code: ` - // { - // "b": 1 - // // comment before comma - // , "a": 2 - // } - // `, - // language: "json/jsonc", - // options: ["asc", { allowLineSeparatedGroups: true }], - // languageOptions: { ecmaVersion: 6 }, - // errors: [ - // { - // messageId: "sortKeys", - // data: { - // sortName: "alphanumeric", - // sensitivity: "sensitive", - // direction: "ascending", - // thisName: "a", - // prevName: "b", - // }, - // }, - // ], - // }, + { + code: ` + { + "b": 1 + // comment before comma + , "a": 2 + } + `, + language: "json/jsonc", + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", + thisName: "a", + prevName: "b", + }, + }, + ], + }, ], }); From 2535bac324c0359b73bea04e8ad90846c114ed8c Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Thu, 9 Jan 2025 08:45:00 -0600 Subject: [PATCH 07/23] formatting --- src/rules/sort-keys.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index b50a827..c489367 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -90,9 +90,9 @@ export default { const sensitivity = caseSensitive ? "sensitive" : "insensitive"; const isValidOrder = comparators[direction][sortName][sensitivity]; - const commentLineRanges = new Set(); + const commentLines = new Set(); for (const comment of context.sourceCode.comments) { - commentLineRanges.add( + commentLines.add( `${comment.loc.start.line}:${comment.loc.end.line}`, ); } @@ -113,21 +113,15 @@ export default { const prevLine = prevMember?.loc.end.line; const thisLine = member.loc.start.line; - const membersAreJoinedByComment = - commentLineRanges.has(`${prevLine}:${thisLine}`) || - commentLineRanges.has( - `${prevLine + 1}:${thisLine}`, - ) || - commentLineRanges.has( - `${prevLine}:${thisLine - 1}`, - ) || - commentLineRanges.has( - `${prevLine + 1}:${thisLine - 1}`, - ); + const membersAreAdjacent = + thisLine - prevLine < 2 || + commentLines.has(`${prevLine}:${thisLine}`) || + commentLines.has(`${prevLine + 1}:${thisLine}`) || + commentLines.has(`${prevLine}:${thisLine - 1}`) || + commentLines.has(`${prevLine + 1}:${thisLine - 1}`); if ( - (thisLine - prevLine < 2 || - membersAreJoinedByComment) && + membersAreAdjacent && isValidOrder(prevName, thisName) === false ) { context.report({ From d87ad4a9c8a19e3a2e4d6d7f1116ce27befab4cb Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Fri, 10 Jan 2025 08:34:42 -0600 Subject: [PATCH 08/23] add allowLineSeparatedGroups option --- src/rules/sort-keys.js | 9 ++++++--- tests/rules/sort-keys.test.js | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index c489367..6ec15db 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -82,8 +82,10 @@ export default { }, create(context) { - const [directionShort, { caseSensitive, natural, minKeys }] = - context.options; + const [ + directionShort, + { allowLineSeparatedGroups, caseSensitive, natural, minKeys }, + ] = context.options; const direction = directionShort === "asc" ? "ascending" : "descending"; const sortName = natural ? "natural" : "alphanumeric"; @@ -121,7 +123,8 @@ export default { commentLines.has(`${prevLine + 1}:${thisLine - 1}`); if ( - membersAreAdjacent && + (membersAreAdjacent || + allowLineSeparatedGroups === false) && isValidOrder(prevName, thisName) === false ) { context.report({ diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js index ca4ee28..fc9cf85 100644 --- a/tests/rules/sort-keys.test.js +++ b/tests/rules/sort-keys.test.js @@ -309,6 +309,19 @@ ruleTester.run("sort-keys", rule, { }, // allowLineSeparatedGroups option + { + code: ` + { + "a": 1, + "b": 2, + "c": 3, + "e": 4, + "f": 5, + "g": 6 + } + `, + options: ["asc", { allowLineSeparatedGroups: false }], + }, { code: ` { @@ -1730,6 +1743,30 @@ ruleTester.run("sort-keys", rule, { }, ], }, + { + code: ` + { + "b": 1, + + "c": 2, + + "a": 3 + } + `, + options: ["asc", { allowLineSeparatedGroups: false }], + errors: [ + { + messageId: "sortKeys", + data: { + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", + thisName: "a", + prevName: "c", + }, + }, + ], + }, // When allowLineSeparatedGroups option is true { From 7688cc3f42281c4c7a5a844861b1f0d222df69dd Mon Sep 17 00:00:00 2001 From: "Robin (Robert) Thomas" Date: Fri, 10 Jan 2025 11:47:51 -0600 Subject: [PATCH 09/23] Update src/rules/sort-keys.js Co-authored-by: Nicholas C. Zakas --- src/rules/sort-keys.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index 6ec15db..3787f54 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -1,5 +1,5 @@ /** - * @fileoverview Rule to require JSON object keys to be sorted. Cribbed largely from https://github.com/eslint/eslint/blob/main/lib/rules/sort-keys.js + * @fileoverview Rule to require JSON object keys to be sorted. Copied largely from https://github.com/eslint/eslint/blob/main/lib/rules/sort-keys.js * @author Robin Thomas */ From 67615ba0cd440c3cde7b1744524f4adbfff36422 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Fri, 10 Jan 2025 11:56:59 -0600 Subject: [PATCH 10/23] Update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e9a1ebb..f044d94 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,7 @@ export default [ like integers but are too large, and [subnormal numbers](https://en.wikipedia.org/wiki/Subnormal_number). - `no-unnormalized-keys` - warns on keys containing [unnormalized characters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize#description). You can optionally specify the normalization form via `{ form: "form_name" }`, where `form_name` can be any of `"NFC"`, `"NFD"`, `"NFKC"`, or `"NFKD"`. +- `sort-keys` - warns when keys are not in the specified order. Based on [eslint/sort-keys](https://eslint.org/docs/latest/rules/sort-keys). - `top-level-interop` - warns when the top-level item in the document is neither an array nor an object. This can be enabled to ensure maximal interoperability with the oldest JSON parsers. ## Configuration Comments From b77c64bbe672c52418920838627a0dad95dc7f96 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Fri, 10 Jan 2025 12:16:18 -0600 Subject: [PATCH 11/23] Add tests for nesting --- tests/rules/sort-keys.test.js | 93 +++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js index fc9cf85..130ee6d 100644 --- a/tests/rules/sort-keys.test.js +++ b/tests/rules/sort-keys.test.js @@ -28,6 +28,37 @@ ruleTester.run("sort-keys", rule, { // nested { code: '{"a":1, "b":{"x":1, "y":1}, "c":1}', options: [] }, + { + code: ` + { + "a":1, + "b": { + "x":1, + "y":1 + }, + "c":1 + } + `, + options: [], + }, + { + code: ` + [ + { + "a":1, + "b": { + "x":1, + "y":1 + } + }, + { + "c":1, + "d":1 + } + ] + `, + options: [], + }, // asc { @@ -399,6 +430,39 @@ ruleTester.run("sort-keys", rule, { options: ["asc", { allowLineSeparatedGroups: true }], languageOptions: { ecmaVersion: 6 }, }, + { + code: ` + { + "b":1, + "c": { + "y":1, + "z":1, + + "x":1 + }, + + "a":1 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + }, + + { + code: ` + { + "b":1, + "a": { + "y":1, + "x":1, + + "z":1 + }, + + "c":1 + } + `, + options: ["desc", { allowLineSeparatedGroups: true }], + }, ], invalid: [ // default (asc) @@ -1814,5 +1878,34 @@ ruleTester.run("sort-keys", rule, { }, ], }, + { + code: ` + [ + { + "b":1, + "a": { + "x":1, + "y":1 + }, + + "d":1, + "c":1 + } + ] + `, + options: ["desc", { allowLineSeparatedGroups: true }], + errors: [ + { + messageId: "sortKeys", + data: { + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "descending", + thisName: "y", + prevName: "x", + }, + }, + ], + }, ], }); From 559fa63ce93893f9993e8cb4252e5eab2777f75e Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Fri, 10 Jan 2025 12:18:19 -0600 Subject: [PATCH 12/23] Add type fix for sort-keys meta.type --- src/rules/sort-keys.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index 3787f54..b4c6aa4 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -34,6 +34,8 @@ const comparators = { export default { meta: { + type: /** @type {const} */ ("suggestion"), + defaultOptions: [ "asc", { @@ -77,8 +79,6 @@ export default { additionalProperties: false, }, ], - - type: "suggestion", }, create(context) { From 2cf5d8f68aa539e50282061f8dbedcd3a514fe4e Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Sat, 11 Jan 2025 09:40:18 -0600 Subject: [PATCH 13/23] Added some tests for json5 --- src/rules/sort-keys.js | 42 ++++++++++++++++++++++++++--------- tests/rules/sort-keys.test.js | 42 ++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index b4c6aa4..17fdae3 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -3,35 +3,53 @@ * @author Robin Thomas */ +/** + * @typedef {import("@humanwhocodes/momoa").MemberNode} MemberNode + * @typedef {import("@humanwhocodes/momoa").ObjectNode} ObjectNode + * @typedef {(a: string, b:string) => boolean} Comparator + */ + import naturalCompare from "natural-compare"; const comparators = { ascending: { alphanumeric: { - sensitive: (a, b) => a <= b, - insensitive: (a, b) => a.toLowerCase() <= b.toLowerCase(), + sensitive: /** @type {Comparator} **/ (a, b) => a <= b, + insensitive: /** @type {Comparator} **/ (a, b) => + a.toLowerCase() <= b.toLowerCase(), }, natural: { - sensitive: (a, b) => naturalCompare(a, b) <= 0, - insensitive: (a, b) => + sensitive: /** @type {Comparator} **/ (a, b) => + naturalCompare(a, b) <= 0, + insensitive: /** @type {Comparator} **/ (a, b) => naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0, }, }, descending: { alphanumeric: { - sensitive: (a, b) => + sensitive: /** @type {Comparator} **/ (a, b) => comparators.ascending.alphanumeric.sensitive(b, a), - insensitive: (a, b) => + insensitive: /** @type {Comparator} **/ (a, b) => comparators.ascending.alphanumeric.insensitive(b, a), }, natural: { - sensitive: (a, b) => comparators.ascending.natural.sensitive(b, a), - insensitive: (a, b) => + sensitive: /** @type {Comparator} **/ (a, b) => + comparators.ascending.natural.sensitive(b, a), + insensitive: /** @type {Comparator} **/ (a, b) => comparators.ascending.natural.insensitive(b, a), }, }, }; +/** + * @param {MemberNode} member + */ +function getKey(member) { + return member.name.type === `Identifier` + ? member.name.name + : member.name.value; +} + export default { meta: { type: /** @type {const} */ ("suggestion"), @@ -100,18 +118,21 @@ export default { } return { + /** + * @param {ObjectNode} node + */ Object(node) { let prevMember; + let prevName; if (node.members.length < minKeys) { return; } for (const member of node.members) { - const thisName = member.name.value; + const thisName = getKey(member); if (prevMember) { - const prevName = prevMember?.name.value; const prevLine = prevMember?.loc.end.line; const thisLine = member.loc.start.line; @@ -143,6 +164,7 @@ export default { } prevMember = member; + prevName = thisName; } }, }; diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js index 130ee6d..e144084 100644 --- a/tests/rules/sort-keys.test.js +++ b/tests/rules/sort-keys.test.js @@ -23,8 +23,14 @@ ruleTester.run("sort-keys", rule, { { code: '{"a":2, "b":3, "b_":1}', options: [] }, { code: '{"C":3, "b_":1, "c":2}', options: [] }, { code: '{"$":1, "A":3, "_":2, "a":4}', options: [] }, + { + code: `{$:1, 'A':3, "_":2, a:4}`, + language: `json/json5`, + options: [], + }, { code: '{"1":1, "11":2, "2":4, "A":3}', options: [] }, { code: '{"#":1, "Z":2, "À":3, "è":4}', options: [] }, + { code: '{"#":1, Z:2, À:3, è:4}', language: `json/json5`, options: [] }, // nested { code: '{"a":1, "b":{"x":1, "y":1}, "c":1}', options: [] }, @@ -59,6 +65,30 @@ ruleTester.run("sort-keys", rule, { `, options: [], }, + { + code: ` + [ + { + "a":1, + b: { + "x":1, + y:1 + } + }, + { + "c":1, + d:1 + } + ] + `, + language: "json/json5", + options: [], + errors: [ + { + messageId: "sortKeys", + }, + ], + }, // asc { @@ -204,6 +234,11 @@ ruleTester.run("sort-keys", rule, { language: "json/jsonc", options: ["desc"], }, + { + code: `{b:3, "a":1, '_':2} // desc`, + language: "json/json5", + options: ["desc"], + }, { code: '{"c":2, "b":3, "a":1}', options: ["desc"] }, { code: '{"b_":1, "b":3, "a":2}', options: ["desc"] }, { code: '{"c":2, "b_":1, "C":3}', options: ["desc"] }, @@ -451,16 +486,17 @@ ruleTester.run("sort-keys", rule, { code: ` { "b":1, - "a": { + a: { "y":1, - "x":1, + x:1, "z":1 }, - "c":1 + c:1 } `, + language: `json/json5`, options: ["desc", { allowLineSeparatedGroups: true }], }, ], From 7caf0c7be63cf812fdd4cba362eb7095679e6d66 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Sat, 11 Jan 2025 10:08:39 -0600 Subject: [PATCH 14/23] mv natural-compare to dependencies --- package.json | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 6aa3cd9..b1ea32d 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,8 @@ "dependencies": { "@eslint/core": "^0.10.0", "@eslint/plugin-kit": "^0.2.5", - "@humanwhocodes/momoa": "^3.3.4" + "@humanwhocodes/momoa": "^3.3.4", + "natural-compare": "^1.4.0" }, "devDependencies": { "@types/eslint": "^8.56.10", @@ -83,9 +84,6 @@ "typescript": "^5.4.5", "yorkie": "^2.0.0" }, - "peerDependencies": { - "natural-compare": "*" - }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } From d7d79a0bcfa08a9cead385a0c45f14677a4c3794 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Sat, 11 Jan 2025 10:08:59 -0600 Subject: [PATCH 15/23] rm types from sort-keys because it makes dist grumpy --- src/rules/sort-keys.js | 31 ++++++++----------------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index 17fdae3..6344d6b 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -3,47 +3,35 @@ * @author Robin Thomas */ -/** - * @typedef {import("@humanwhocodes/momoa").MemberNode} MemberNode - * @typedef {import("@humanwhocodes/momoa").ObjectNode} ObjectNode - * @typedef {(a: string, b:string) => boolean} Comparator - */ - import naturalCompare from "natural-compare"; const comparators = { ascending: { alphanumeric: { - sensitive: /** @type {Comparator} **/ (a, b) => a <= b, - insensitive: /** @type {Comparator} **/ (a, b) => - a.toLowerCase() <= b.toLowerCase(), + sensitive: (a, b) => a <= b, + insensitive: (a, b) => a.toLowerCase() <= b.toLowerCase(), }, natural: { - sensitive: /** @type {Comparator} **/ (a, b) => - naturalCompare(a, b) <= 0, - insensitive: /** @type {Comparator} **/ (a, b) => + sensitive: (a, b) => naturalCompare(a, b) <= 0, + insensitive: (a, b) => naturalCompare(a.toLowerCase(), b.toLowerCase()) <= 0, }, }, descending: { alphanumeric: { - sensitive: /** @type {Comparator} **/ (a, b) => + sensitive: (a, b) => comparators.ascending.alphanumeric.sensitive(b, a), - insensitive: /** @type {Comparator} **/ (a, b) => + insensitive: (a, b) => comparators.ascending.alphanumeric.insensitive(b, a), }, natural: { - sensitive: /** @type {Comparator} **/ (a, b) => - comparators.ascending.natural.sensitive(b, a), - insensitive: /** @type {Comparator} **/ (a, b) => + sensitive: (a, b) => comparators.ascending.natural.sensitive(b, a), + insensitive: (a, b) => comparators.ascending.natural.insensitive(b, a), }, }, }; -/** - * @param {MemberNode} member - */ function getKey(member) { return member.name.type === `Identifier` ? member.name.name @@ -118,9 +106,6 @@ export default { } return { - /** - * @param {ObjectNode} node - */ Object(node) { let prevMember; let prevName; From 273389b900ec3d2a67268c3e1d8a1b43b0e66516 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Sun, 12 Jan 2025 10:26:19 -0600 Subject: [PATCH 16/23] rm unneeded nullish check --- src/rules/sort-keys.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index 6344d6b..b25f74f 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -118,7 +118,7 @@ export default { const thisName = getKey(member); if (prevMember) { - const prevLine = prevMember?.loc.end.line; + const prevLine = prevMember.loc.end.line; const thisLine = member.loc.start.line; const membersAreAdjacent = From 1d6fc14a33b5eac48cdbdb73bc63f7f0fd889757 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Tue, 14 Jan 2025 12:22:35 -0600 Subject: [PATCH 17/23] make sort-keys handle multiline comments --- src/rules/sort-keys.js | 85 +++++++++++++++++++++-------------- tests/rules/sort-keys.test.js | 49 ++++++++++++++++++++ 2 files changed, 101 insertions(+), 33 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index b25f74f..2b33d68 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -87,6 +87,9 @@ export default { ], }, + /** + * Note that the comma after a member is *not* included in `member.loc`, therefore the comma position is irrelevant + */ create(context) { const [ directionShort, @@ -98,11 +101,38 @@ export default { const sensitivity = caseSensitive ? "sensitive" : "insensitive"; const isValidOrder = comparators[direction][sortName][sensitivity]; - const commentLines = new Set(); + // Note that @humanwhocodes/momoa doesn't include comments in the object.members tree, so we can't just see if a member is preceded by a comment + const commentLineNums = new Set(); + // TODO: Only invoke this if the language supports comments for (const comment of context.sourceCode.comments) { - commentLines.add( - `${comment.loc.start.line}:${comment.loc.end.line}`, - ); + for ( + let lineNum = comment.loc.start.line; + lineNum <= comment.loc.end.line; + lineNum += 1 + ) { + commentLineNums.add(lineNum); + } + } + + // Note that there can be comments *inside* members, e.g. `{"foo: /* comment *\/ "bar"}`, but these are ignored when calculating line-separated groups + function isLineSeparated(prevMember, member) { + const prevLine = prevMember.loc.end.line; + const thisLine = member.loc.start.line; + + if (thisLine - prevLine < 2) { + return false; + } + + let lineNum = prevLine + 1; + while (lineNum < thisLine) { + if (commentLineNums.has(lineNum) === false) { + return true; + } + + lineNum += 1; + } + + return false; } return { @@ -117,35 +147,24 @@ export default { for (const member of node.members) { const thisName = getKey(member); - if (prevMember) { - const prevLine = prevMember.loc.end.line; - const thisLine = member.loc.start.line; - - const membersAreAdjacent = - thisLine - prevLine < 2 || - commentLines.has(`${prevLine}:${thisLine}`) || - commentLines.has(`${prevLine + 1}:${thisLine}`) || - commentLines.has(`${prevLine}:${thisLine - 1}`) || - commentLines.has(`${prevLine + 1}:${thisLine - 1}`); - - if ( - (membersAreAdjacent || - allowLineSeparatedGroups === false) && - isValidOrder(prevName, thisName) === false - ) { - context.report({ - node, - loc: member.name.loc, - messageId: "sortKeys", - data: { - thisName, - prevName, - direction, - sensitivity, - sortName, - }, - }); - } + if ( + prevMember && + isValidOrder(prevName, thisName) === false && + (allowLineSeparatedGroups === false || + isLineSeparated(prevMember, member) === false) + ) { + context.report({ + node, + loc: member.name.loc, + messageId: "sortKeys", + data: { + thisName, + prevName, + direction, + sensitivity, + sortName, + }, + }); } prevMember = member; diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js index e144084..910e4af 100644 --- a/tests/rules/sort-keys.test.js +++ b/tests/rules/sort-keys.test.js @@ -1943,5 +1943,54 @@ ruleTester.run("sort-keys", rule, { }, ], }, + { + code: ` + { + "b": /*foo */ 1, + // some multiline comment + // using line comment style + "a": 2 // "a" and "b" are not line separated + } + `, + language: "json/jsonc", + options: ["asc", { allowLineSeparatedGroups: true }], + errors: [ + { + messageId: "sortKeys", + data: { + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", + thisName: "a", + prevName: "b", + }, + }, + ], + }, + { + code: ` + { + "b": 1, + /* some multiline comment + using block comment style */ + /* here's another for good measure */ + "a": 2 // "a" and "b" are not line separated + } + `, + language: "json/jsonc", + options: ["asc", { allowLineSeparatedGroups: true }], + errors: [ + { + messageId: "sortKeys", + data: { + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", + thisName: "a", + prevName: "b", + }, + }, + ], + }, ], }); From 0b2a425b253cec01459a171af4c11e4ab70dd6e6 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Tue, 14 Jan 2025 12:26:53 -0600 Subject: [PATCH 18/23] tabs => space --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b1ea32d..a106fc8 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@eslint/core": "^0.10.0", "@eslint/plugin-kit": "^0.2.5", "@humanwhocodes/momoa": "^3.3.4", - "natural-compare": "^1.4.0" + "natural-compare": "^1.4.0" }, "devDependencies": { "@types/eslint": "^8.56.10", From 933539a9b9a429d4d01605f968260c082be2f456 Mon Sep 17 00:00:00 2001 From: "Robin (Robert) Thomas" Date: Wed, 15 Jan 2025 13:09:54 -0600 Subject: [PATCH 19/23] Update README.md Co-authored-by: Nicholas C. Zakas --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f044d94..df5c3fd 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ export default [ like integers but are too large, and [subnormal numbers](https://en.wikipedia.org/wiki/Subnormal_number). - `no-unnormalized-keys` - warns on keys containing [unnormalized characters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize#description). You can optionally specify the normalization form via `{ form: "form_name" }`, where `form_name` can be any of `"NFC"`, `"NFD"`, `"NFKC"`, or `"NFKD"`. -- `sort-keys` - warns when keys are not in the specified order. Based on [eslint/sort-keys](https://eslint.org/docs/latest/rules/sort-keys). +- `sort-keys` - warns when keys are not in the specified order. Based on the ESLint [`sort-keys`](https://eslint.org/docs/latest/rules/sort-keys) rule. - `top-level-interop` - warns when the top-level item in the document is neither an array nor an object. This can be enabled to ensure maximal interoperability with the oldest JSON parsers. ## Configuration Comments From 05f069d5514846dbc8b8d5ef5f4519daccbda714 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Fri, 17 Jan 2025 13:03:24 -0600 Subject: [PATCH 20/23] stylistic updates --- src/rules/sort-keys.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index 2b33d68..9454560 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -103,7 +103,6 @@ export default { // Note that @humanwhocodes/momoa doesn't include comments in the object.members tree, so we can't just see if a member is preceded by a comment const commentLineNums = new Set(); - // TODO: Only invoke this if the language supports comments for (const comment of context.sourceCode.comments) { for ( let lineNum = comment.loc.start.line; @@ -125,7 +124,7 @@ export default { let lineNum = prevLine + 1; while (lineNum < thisLine) { - if (commentLineNums.has(lineNum) === false) { + if (!commentLineNums.has(lineNum)) { return true; } @@ -149,12 +148,11 @@ export default { if ( prevMember && - isValidOrder(prevName, thisName) === false && - (allowLineSeparatedGroups === false || - isLineSeparated(prevMember, member) === false) + !isValidOrder(prevName, thisName) && + (!allowLineSeparatedGroups || + !isLineSeparated(prevMember, member)) ) { context.report({ - node, loc: member.name.loc, messageId: "sortKeys", data: { From 708311c9e01a542aff172e75f0b792378ebee161 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Fri, 17 Jan 2025 14:02:31 -0600 Subject: [PATCH 21/23] Commas do not count as group-separating lines --- src/rules/sort-keys.js | 29 ++++++++-- tests/rules/sort-keys.test.js | 99 ++++++++++++++++++++++++++--------- 2 files changed, 98 insertions(+), 30 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index 9454560..63e4cc4 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -113,17 +113,36 @@ export default { } } + const commaLinesByPosition = {}; + for (const token of context.sourceCode.ast.tokens) { + if (token.type === `Comma`) { + commaLinesByPosition[token.range[0]] = token.loc.start.line; // Assume commas are always a single character + } + } + // Note that there can be comments *inside* members, e.g. `{"foo: /* comment *\/ "bar"}`, but these are ignored when calculating line-separated groups function isLineSeparated(prevMember, member) { - const prevLine = prevMember.loc.end.line; - const thisLine = member.loc.start.line; + let prevMemberEndLine = prevMember.loc.end.line; + + const prevMemberEndPosition = prevMember.range[1]; + const memberStartPosition = member.range[0]; + let position = prevMemberEndPosition + 1; + while (position < memberStartPosition) { + if (position in commaLinesByPosition) { + prevMemberEndLine = commaLinesByPosition[position]; + break; + } + + position += 1; + } - if (thisLine - prevLine < 2) { + const thisStartLine = member.loc.start.line; + if (thisStartLine - prevMemberEndLine < 2) { return false; } - let lineNum = prevLine + 1; - while (lineNum < thisLine) { + let lineNum = prevMemberEndLine + 1; + while (lineNum < thisStartLine) { if (!commentLineNums.has(lineNum)) { return true; } diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js index 910e4af..e8c886a 100644 --- a/tests/rules/sort-keys.test.js +++ b/tests/rules/sort-keys.test.js @@ -440,31 +440,6 @@ ruleTester.run("sort-keys", rule, { `, options: ["asc", { allowLineSeparatedGroups: true }], }, - { - code: ` - { - "b": 1 - - ,"a": 2 - } - `, - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - }, - { - code: ` - { - "b": 1 - // comment before comma - - , - "a": 2 - } - `, - language: "json/jsonc", - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - }, { code: ` { @@ -499,6 +474,32 @@ ruleTester.run("sort-keys", rule, { language: `json/json5`, options: ["desc", { allowLineSeparatedGroups: true }], }, + + // Commas are not considered separating lines + { + code: ` + { + "b": 1 + + , + + "a": 2 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + }, + { + code: ` + { + "a": 1 + + + , + "b": 2 + } + `, + options: ["asc", { allowLineSeparatedGroups: false }], + }, ], invalid: [ // default (asc) @@ -1914,6 +1915,32 @@ ruleTester.run("sort-keys", rule, { }, ], }, + { + code: ` + { + "b": 1 + // comment before comma + + , + "a": 2 + } + `, + language: "json/jsonc", + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + errors: [ + { + messageId: "sortKeys", + data: { + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", + thisName: "a", + prevName: "b", + }, + }, + ], + }, { code: ` [ @@ -1992,5 +2019,27 @@ ruleTester.run("sort-keys", rule, { }, ], }, + { + code: ` + { + "b": 1 + , + "a": 2 + } + `, + options: ["asc", { allowLineSeparatedGroups: true }], + errors: [ + { + messageId: "sortKeys", + data: { + sortName: "alphanumeric", + sensitivity: "sensitive", + direction: "ascending", + thisName: "a", + prevName: "b", + }, + }, + ], + }, ], }); From 5f446f2bda308dfbbaefbd9ddedf857bf27fbcf5 Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Fri, 17 Jan 2025 14:26:17 -0600 Subject: [PATCH 22/23] forget about commas, just check if empty line outside of comment --- src/rules/sort-keys.js | 43 +++++++++++----------------------- tests/rules/sort-keys.test.js | 44 ++++++++++++++--------------------- 2 files changed, 31 insertions(+), 56 deletions(-) diff --git a/src/rules/sort-keys.js b/src/rules/sort-keys.js index 63e4cc4..d779d63 100644 --- a/src/rules/sort-keys.js +++ b/src/rules/sort-keys.js @@ -5,6 +5,8 @@ import naturalCompare from "natural-compare"; +const hasNonWhitespace = /\S/u; + const comparators = { ascending: { alphanumeric: { @@ -87,9 +89,6 @@ export default { ], }, - /** - * Note that the comma after a member is *not* included in `member.loc`, therefore the comma position is irrelevant - */ create(context) { const [ directionShort, @@ -113,41 +112,27 @@ export default { } } - const commaLinesByPosition = {}; - for (const token of context.sourceCode.ast.tokens) { - if (token.type === `Comma`) { - commaLinesByPosition[token.range[0]] = token.loc.start.line; // Assume commas are always a single character - } - } - // Note that there can be comments *inside* members, e.g. `{"foo: /* comment *\/ "bar"}`, but these are ignored when calculating line-separated groups function isLineSeparated(prevMember, member) { - let prevMemberEndLine = prevMember.loc.end.line; - - const prevMemberEndPosition = prevMember.range[1]; - const memberStartPosition = member.range[0]; - let position = prevMemberEndPosition + 1; - while (position < memberStartPosition) { - if (position in commaLinesByPosition) { - prevMemberEndLine = commaLinesByPosition[position]; - break; - } - - position += 1; - } - + const prevMemberEndLine = prevMember.loc.end.line; const thisStartLine = member.loc.start.line; if (thisStartLine - prevMemberEndLine < 2) { return false; } - let lineNum = prevMemberEndLine + 1; - while (lineNum < thisStartLine) { - if (!commentLineNums.has(lineNum)) { + for ( + let lineNum = prevMemberEndLine + 1; + lineNum < thisStartLine; + lineNum += 1 + ) { + if ( + !commentLineNums.has(lineNum) && + !hasNonWhitespace.test( + context.sourceCode.lines[lineNum - 1], + ) + ) { return true; } - - lineNum += 1; } return false; diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js index e8c886a..2cd15f0 100644 --- a/tests/rules/sort-keys.test.js +++ b/tests/rules/sort-keys.test.js @@ -500,6 +500,20 @@ ruleTester.run("sort-keys", rule, { `, options: ["asc", { allowLineSeparatedGroups: false }], }, + { + code: ` + { + "b": 1 + // comment before comma + + , + "a": 2 + } + `, + language: "json/jsonc", + options: ["asc", { allowLineSeparatedGroups: true }], + languageOptions: { ecmaVersion: 6 }, + }, ], invalid: [ // default (asc) @@ -1915,32 +1929,6 @@ ruleTester.run("sort-keys", rule, { }, ], }, - { - code: ` - { - "b": 1 - // comment before comma - - , - "a": 2 - } - `, - language: "json/jsonc", - options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, - errors: [ - { - messageId: "sortKeys", - data: { - sortName: "alphanumeric", - sensitivity: "sensitive", - direction: "ascending", - thisName: "a", - prevName: "b", - }, - }, - ], - }, { code: ` [ @@ -2000,7 +1988,9 @@ ruleTester.run("sort-keys", rule, { "b": 1, /* some multiline comment using block comment style */ - /* here's another for good measure */ + /* the empty line... + + ...in this one doesn't count */ "a": 2 // "a" and "b" are not line separated } `, From 765cbde30b90c868cac9315a59072800a0c697be Mon Sep 17 00:00:00 2001 From: RobertAKARobin Date: Sat, 18 Jan 2025 09:17:38 -0600 Subject: [PATCH 23/23] rm unused languageOptions --- tests/rules/sort-keys.test.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/rules/sort-keys.test.js b/tests/rules/sort-keys.test.js index 2cd15f0..c15bb15 100644 --- a/tests/rules/sort-keys.test.js +++ b/tests/rules/sort-keys.test.js @@ -512,7 +512,6 @@ ruleTester.run("sort-keys", rule, { `, language: "json/jsonc", options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, }, ], invalid: [ @@ -1915,7 +1914,6 @@ ruleTester.run("sort-keys", rule, { `, language: "json/jsonc", options: ["asc", { allowLineSeparatedGroups: true }], - languageOptions: { ecmaVersion: 6 }, errors: [ { messageId: "sortKeys",