-
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add sort-keys * adding tests for eslint/sort-keys * made eslint/sort-keys tests work with json * account for line skips * more semantic sort-keys code * Handle when object members are joined by a comment * formatting * add allowLineSeparatedGroups option * Update src/rules/sort-keys.js Co-authored-by: Nicholas C. Zakas <[email protected]> * Update readme * Add tests for nesting * Add type fix for sort-keys meta.type * Added some tests for json5 * mv natural-compare to dependencies * rm types from sort-keys because it makes dist grumpy * rm unneeded nullish check * make sort-keys handle multiline comments * tabs => space * Update README.md Co-authored-by: Nicholas C. Zakas <[email protected]> * stylistic updates * Commas do not count as group-separating lines * forget about commas, just check if empty line outside of comment * rm unused languageOptions --------- Co-authored-by: Nicholas C. Zakas <[email protected]>
- Loading branch information
1 parent
d09d8cf
commit e68c247
Showing
5 changed files
with
2,216 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
/** | ||
* @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 | ||
*/ | ||
|
||
import naturalCompare from "natural-compare"; | ||
|
||
const hasNonWhitespace = /\S/u; | ||
|
||
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, | ||
}, | ||
}, | ||
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), | ||
}, | ||
}, | ||
}; | ||
|
||
function getKey(member) { | ||
return member.name.type === `Identifier` | ||
? member.name.name | ||
: member.name.value; | ||
} | ||
|
||
export default { | ||
meta: { | ||
type: /** @type {const} */ ("suggestion"), | ||
|
||
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 {{sortName}} case-{{sensitivity}} {{direction}} 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, | ||
}, | ||
], | ||
}, | ||
|
||
create(context) { | ||
const [ | ||
directionShort, | ||
{ allowLineSeparatedGroups, 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]; | ||
|
||
// 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(); | ||
for (const comment of context.sourceCode.comments) { | ||
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 prevMemberEndLine = prevMember.loc.end.line; | ||
const thisStartLine = member.loc.start.line; | ||
if (thisStartLine - prevMemberEndLine < 2) { | ||
return false; | ||
} | ||
|
||
for ( | ||
let lineNum = prevMemberEndLine + 1; | ||
lineNum < thisStartLine; | ||
lineNum += 1 | ||
) { | ||
if ( | ||
!commentLineNums.has(lineNum) && | ||
!hasNonWhitespace.test( | ||
context.sourceCode.lines[lineNum - 1], | ||
) | ||
) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
return { | ||
Object(node) { | ||
let prevMember; | ||
let prevName; | ||
|
||
if (node.members.length < minKeys) { | ||
return; | ||
} | ||
|
||
for (const member of node.members) { | ||
const thisName = getKey(member); | ||
|
||
if ( | ||
prevMember && | ||
!isValidOrder(prevName, thisName) && | ||
(!allowLineSeparatedGroups || | ||
!isLineSeparated(prevMember, member)) | ||
) { | ||
context.report({ | ||
loc: member.name.loc, | ||
messageId: "sortKeys", | ||
data: { | ||
thisName, | ||
prevName, | ||
direction, | ||
sensitivity, | ||
sortName, | ||
}, | ||
}); | ||
} | ||
|
||
prevMember = member; | ||
prevName = thisName; | ||
} | ||
}, | ||
}; | ||
}, | ||
}; |
Oops, something went wrong.