Skip to content

Commit 5950e3d

Browse files
authored
feat: improve auto-fix of sort rules (#390)
1 parent 68f8650 commit 5950e3d

File tree

6 files changed

+511
-91
lines changed

6 files changed

+511
-91
lines changed

.changeset/green-bugs-scream.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"eslint-plugin-jsonc": minor
3+
---
4+
5+
feat: improve auto-fix of sort rules

lib/rules/sort-array-values.ts

+21-50
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import naturalCompare from "natural-compare";
22
import { createRule } from "../utils";
3-
import { isCommaToken } from "@eslint-community/eslint-utils";
43
import type { AST } from "jsonc-eslint-parser";
54
import { getStaticJSONValue } from "jsonc-eslint-parser";
6-
import type { SourceCode, AST as ESLintAST } from "eslint";
5+
import type { SourceCode } from "eslint";
6+
import type { AroundTarget } from "../utils/fix-sort-elements";
7+
import { fixForSorting } from "../utils/fix-sort-elements";
78

89
type JSONValue = ReturnType<typeof getStaticJSONValue>;
910

@@ -40,7 +41,6 @@ type ParsedOption = {
4041
type Validator = (a: JSONElementData, b: JSONElementData) => boolean;
4142

4243
type JSONElement = AST.JSONArrayExpression["elements"][number];
43-
type AroundTokens = { before: ESLintAST.Token; after: ESLintAST.Token };
4444
class JSONElementData {
4545
public readonly array: JSONArrayData;
4646

@@ -50,52 +50,37 @@ class JSONElementData {
5050

5151
private cached: { value: JSONValue } | null = null;
5252

53-
private cachedRange: [number, number] | null = null;
54-
55-
private cachedAroundTokens: AroundTokens | null = null;
53+
private cachedAround: AroundTarget | null = null;
5654

5755
public get reportLoc() {
5856
if (this.node) {
5957
return this.node.loc;
6058
}
61-
const aroundTokens = this.aroundTokens;
59+
const around = this.around;
6260
return {
63-
start: aroundTokens.before.loc.end,
64-
end: aroundTokens.after.loc.start,
61+
start: around.before.loc.end,
62+
end: around.after.loc.start,
6563
};
6664
}
6765

68-
public get range(): [number, number] {
69-
if (this.node) {
70-
return this.node.range;
71-
}
72-
if (this.cachedRange) {
73-
return this.cachedRange;
74-
}
75-
const aroundTokens = this.aroundTokens;
76-
return (this.cachedRange = [
77-
aroundTokens.before.range[1],
78-
aroundTokens.after.range[0],
79-
]);
80-
}
81-
82-
public get aroundTokens(): AroundTokens {
83-
if (this.cachedAroundTokens) {
84-
return this.cachedAroundTokens;
66+
public get around(): AroundTarget {
67+
if (this.cachedAround) {
68+
return this.cachedAround;
8569
}
8670
const sourceCode = this.array.sourceCode;
8771
if (this.node) {
88-
return (this.cachedAroundTokens = {
72+
return (this.cachedAround = {
73+
node: this.node,
8974
before: sourceCode.getTokenBefore(this.node as never)!,
9075
after: sourceCode.getTokenAfter(this.node as never)!,
9176
});
9277
}
9378
const before =
9479
this.index > 0
95-
? this.array.elements[this.index - 1].aroundTokens.after
80+
? this.array.elements[this.index - 1].around.after
9681
: sourceCode.getFirstToken(this.array.node as never)!;
9782
const after = sourceCode.getTokenAfter(before)!;
98-
return (this.cachedAroundTokens = { before, after });
83+
return (this.cachedAround = { before, after });
9984
}
10085

10186
public constructor(array: JSONArrayData, node: JSONElement, index: number) {
@@ -426,7 +411,7 @@ export default createRule("sort-array-values", {
426411
prevValue: toText(prev),
427412
orderText: option.orderText(data),
428413
},
429-
*fix(fixer) {
414+
fix(fixer) {
430415
let moveTarget = prevList[0];
431416
for (const prev of prevList) {
432417
if (option.isValidOrder(prev, data)) {
@@ -435,26 +420,12 @@ export default createRule("sort-array-values", {
435420
moveTarget = prev;
436421
}
437422
}
438-
439-
const beforeToken = data.aroundTokens.before;
440-
const afterToken = data.aroundTokens.after;
441-
const hasAfterComma = isCommaToken(afterToken);
442-
const codeStart = beforeToken.range[1]; // to include comments
443-
const codeEnd = hasAfterComma
444-
? afterToken.range[1] // |/**/ value,|
445-
: data.range[1]; // |/**/ value|
446-
const removeStart = hasAfterComma
447-
? codeStart // |/**/ value,|
448-
: beforeToken.range[0]; // |,/**/ value|
449-
450-
const insertCode =
451-
sourceCode.text.slice(codeStart, codeEnd) +
452-
(hasAfterComma ? "" : ",");
453-
454-
const insertTarget = moveTarget.aroundTokens.before;
455-
yield fixer.insertTextAfterRange(insertTarget.range, insertCode);
456-
457-
yield fixer.removeRange([removeStart, codeEnd]);
423+
return fixForSorting(
424+
fixer,
425+
sourceCode,
426+
data.around,
427+
moveTarget.around,
428+
);
458429
},
459430
});
460431
}

lib/rules/sort-keys.ts

+3-35
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import naturalCompare from "natural-compare";
22
import { createRule } from "../utils";
3-
import { isCommaToken } from "@eslint-community/eslint-utils";
43
import type { AST } from "jsonc-eslint-parser";
54
import { getStaticJSONValue } from "jsonc-eslint-parser";
5+
import { fixForSorting } from "../utils/fix-sort-elements";
66

77
//------------------------------------------------------------------------------
88
// Helpers
@@ -451,7 +451,7 @@ export default createRule("sort-keys", {
451451
prevName: prev.name,
452452
orderText: option.orderText,
453453
},
454-
*fix(fixer) {
454+
fix(fixer) {
455455
let moveTarget = prevList[0];
456456
for (const prev of prevList) {
457457
if (option.isValidOrder(prev, data)) {
@@ -460,39 +460,7 @@ export default createRule("sort-keys", {
460460
moveTarget = prev;
461461
}
462462
}
463-
464-
const beforeToken = sourceCode.getTokenBefore(data.node as never)!;
465-
const afterToken = sourceCode.getTokenAfter(data.node as never)!;
466-
const hasAfterComma = isCommaToken(afterToken);
467-
const codeStart = beforeToken.range[1]; // to include comments
468-
const codeEnd = hasAfterComma
469-
? afterToken.range[1] // |/**/ key: value,|
470-
: data.node.range[1]; // |/**/ key: value|
471-
const removeStart = hasAfterComma
472-
? codeStart // |/**/ key: value,|
473-
: beforeToken.range[0]; // |,/**/ key: value|
474-
475-
const insertCode =
476-
sourceCode.text.slice(codeStart, codeEnd) +
477-
(hasAfterComma ? "" : ",");
478-
479-
const insertTarget = sourceCode.getTokenBefore(
480-
moveTarget.node as never,
481-
)!;
482-
let insertRange = insertTarget.range;
483-
const insertNext = sourceCode.getTokenAfter(insertTarget, {
484-
includeComments: true,
485-
})!;
486-
if (insertNext.loc!.start.line - insertTarget.loc.end.line > 1) {
487-
const offset = sourceCode.getIndexFromLoc({
488-
line: insertNext.loc!.start.line - 1,
489-
column: 0,
490-
});
491-
insertRange = [offset, offset];
492-
}
493-
yield fixer.insertTextAfterRange(insertRange, insertCode);
494-
495-
yield fixer.removeRange([removeStart, codeEnd]);
463+
return fixForSorting(fixer, sourceCode, data, moveTarget);
496464
},
497465
});
498466
}

0 commit comments

Comments
 (0)