Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions src/rules/sort-keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ const rule = {
meta: {
type: "suggestion",

fixable: "code",

defaultOptions: [
"asc",
{
Expand Down Expand Up @@ -198,6 +200,33 @@ const rule = {
return false;
}

/**
* Compares two key names according to the rule options and returns a number
* suitable for Array.prototype.sort.
* @param {string} a First key to compare.
* @param {string} b Second key to compare.
* @returns {number} Negative if a < b, positive if a > b, 0 if equal.
*/
function compareKeys(a, b) {
let a1 = a;
let b1 = b;
if (!caseSensitive) {
a1 = a1.toLowerCase();
b1 = b1.toLowerCase();
}
if (natural) {
const r = naturalCompare(a1, b1);
return directionShort === "asc" ? r : -r;
}
if (a1 < b1) {
return directionShort === "asc" ? -1 : 1;
}
if (a1 > b1) {
return directionShort === "asc" ? 1 : -1;
}
return 0;
}

return {
Object(node) {
let prevMember;
Expand Down Expand Up @@ -226,6 +255,126 @@ const rule = {
sensitivity,
sortName,
},
fix(fixer) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This aims to sort the entire object at once, not just the reported property?

const text = context.sourceCode.text;

// If there are any comment tokens within this object, do not attempt to autofix
const hasComments =
context.sourceCode.comments.some(
comment =>
comment.range[0] >= node.range[0] &&
comment.range[1] <= node.range[1],
);

if (hasComments) {
Copy link
Preview

Copilot AI Aug 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment detection logic should be documented to explain why autofix is disabled when comments are present within the object, as this might not be obvious to maintainers.

Copilot uses AI. Check for mistakes.

return null;
}

const groups = [];
let groupStart = 0;
for (let i = 1; i < node.members.length; i++) {
if (
allowLineSeparatedGroups &&
isLineSeparated(
node.members[i - 1],
node.members[i],
)
) {
groups.push({
start: groupStart,
end: i - 1,
});
groupStart = i;
}
}
groups.push({
start: groupStart,
end: node.members.length - 1,
});

const fixes = [];
for (const group of groups) {
const groupLen =
group.end - group.start + 1;
if (groupLen <= 1) {
continue;
}

let isSorted = true;
for (
let i = group.start + 1;
i <= group.end;
i++
) {
const prevKey = getKey(
node.members[i - 1],
);
const thisKey = getKey(node.members[i]);
if (compareKeys(prevKey, thisKey) > 0) {
isSorted = false;
break;
}
}
if (isSorted) {
continue;
}

const items = [];
const seps = [];
for (
let i = group.start;
i <= group.end;
i++
) {
const m = node.members[i];
items.push({
key: getKey(m),
start: m.range[0],
end: m.range[1],
text: text.slice(
m.range[0],
m.range[1],
),
});

if (i < group.end) {
seps.push(
text.slice(
node.members[i].range[1],
node.members[i + 1]
.range[0],
),
);
}
}

const replaceStart = items[0].start;
const replaceEnd = items.at(-1).end;

const rebuilt = items
.slice()
.sort((a, b) =>
compareKeys(a.key, b.key),
)
.map(
(item, i) =>
item.text +
(i < seps.length
? seps[i]
: ""),
)
.join("");

fixes.push(
fixer.replaceTextRange(
[replaceStart, replaceEnd],
rebuilt,
),
);
}

return fixes;
},
});
}

Expand Down
Loading
Loading