@tai-kun/vp-lint-plugins 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/rules/object-sort-utils.d.ts +12 -0
- package/dist/src/rules/object-sort-utils.d.ts.map +1 -1
- package/dist/src/rules/object-sort-utils.js +100 -47
- package/dist/src/rules/sort-object-properties.d.ts +1 -1
- package/dist/src/rules/sort-object-properties.d.ts.map +1 -1
- package/dist/src/rules/sort-object-properties.js +7 -1
- package/package.json +1 -1
- package/src/rules/object-sort-utils.ts +132 -58
- package/src/rules/sort-object-properties.ts +13 -1
|
@@ -10,12 +10,23 @@ interface SortPlanOptions {
|
|
|
10
10
|
*/
|
|
11
11
|
forceMultiline?: boolean;
|
|
12
12
|
}
|
|
13
|
+
interface SortEdit {
|
|
14
|
+
end: number;
|
|
15
|
+
start: number;
|
|
16
|
+
text: string;
|
|
17
|
+
}
|
|
13
18
|
type SortPlan = {
|
|
19
|
+
edits: null;
|
|
14
20
|
needsFix: false;
|
|
15
21
|
replacement: null;
|
|
16
22
|
} | {
|
|
23
|
+
edits: null;
|
|
17
24
|
needsFix: true;
|
|
18
25
|
replacement: string;
|
|
26
|
+
} | {
|
|
27
|
+
edits: SortEdit[];
|
|
28
|
+
needsFix: true;
|
|
29
|
+
replacement: null;
|
|
19
30
|
};
|
|
20
31
|
declare function compareNames(left: string, right: string): number;
|
|
21
32
|
/**
|
|
@@ -24,6 +35,7 @@ declare function compareNames(left: string, right: string): number;
|
|
|
24
35
|
* - プロパティー名の長さが短い順、同じ長さなら名前順
|
|
25
36
|
* - 空行で区切られたグループ内だけで並び替え
|
|
26
37
|
* - プロパティー直上のコメントはプロパティーと一緒に移動
|
|
38
|
+
* - 空行を挟んだコメントはグループ見出しとしてその場に残す
|
|
27
39
|
*
|
|
28
40
|
* 並び替えが安全でない場合は null を返します。
|
|
29
41
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"object-sort-utils.d.ts","sourceRoot":"","sources":["../../../src/rules/object-sort-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAW,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE1E,KAAK,cAAc,GAAG,MAAM,CAAC,gBAAgB,GAAG,MAAM,CAAC,aAAa,CAAC;AAIrE,UAAU,eAAe;IACvB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAYD,KAAK,QAAQ,GACT;IACE,QAAQ,EAAE,KAAK,CAAC;IAChB,WAAW,EAAE,IAAI,CAAC;CACnB,GACD;IACE,QAAQ,EAAE,IAAI,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAuEN,iBAAS,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBzD;
|
|
1
|
+
{"version":3,"file":"object-sort-utils.d.ts","sourceRoot":"","sources":["../../../src/rules/object-sort-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAW,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAE1E,KAAK,cAAc,GAAG,MAAM,CAAC,gBAAgB,GAAG,MAAM,CAAC,aAAa,CAAC;AAIrE,UAAU,eAAe;IACvB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAYD,UAAU,QAAQ;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAID,KAAK,QAAQ,GACT;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,QAAQ,EAAE,KAAK,CAAC;IAChB,WAAW,EAAE,IAAI,CAAC;CACnB,GACD;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,QAAQ,EAAE,IAAI,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,GACD;IACE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,QAAQ,EAAE,IAAI,CAAC;IACf,WAAW,EAAE,IAAI,CAAC;CACnB,CAAC;AAuEN,iBAAS,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBzD;AAyCD;;;;;;;;;GASG;AACH,iBAAS,WAAW,CAClB,IAAI,EAAE,cAAc,EACpB,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,eAAoB,GAC5B,QAAQ,GAAG,IAAI,CAuNjB;AAED,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -73,12 +73,33 @@ function sortGroup(group) {
|
|
|
73
73
|
const rest = group.filter((unit) => unit.name === null);
|
|
74
74
|
return [...sortable, ...rest];
|
|
75
75
|
}
|
|
76
|
+
function splitGroups(units) {
|
|
77
|
+
const groups = [];
|
|
78
|
+
let currentGroup = [];
|
|
79
|
+
let previousEndLine = null;
|
|
80
|
+
for (const unit of units) {
|
|
81
|
+
if (previousEndLine !== null && unit.startLine > previousEndLine + 1) {
|
|
82
|
+
groups.push(currentGroup);
|
|
83
|
+
currentGroup = [];
|
|
84
|
+
}
|
|
85
|
+
currentGroup.push(unit);
|
|
86
|
+
previousEndLine = unit.endLine;
|
|
87
|
+
}
|
|
88
|
+
if (currentGroup.length > 0) {
|
|
89
|
+
groups.push(currentGroup);
|
|
90
|
+
}
|
|
91
|
+
return groups;
|
|
92
|
+
}
|
|
93
|
+
function getEditText(units) {
|
|
94
|
+
return units.map((unit) => unit.text).join(",\n");
|
|
95
|
+
}
|
|
76
96
|
/**
|
|
77
97
|
* オブジェクトリテラル/オブジェクトパターンの並び替え計画を返します。
|
|
78
98
|
*
|
|
79
99
|
* - プロパティー名の長さが短い順、同じ長さなら名前順
|
|
80
100
|
* - 空行で区切られたグループ内だけで並び替え
|
|
81
101
|
* - プロパティー直上のコメントはプロパティーと一緒に移動
|
|
102
|
+
* - 空行を挟んだコメントはグループ見出しとしてその場に残す
|
|
82
103
|
*
|
|
83
104
|
* 並び替えが安全でない場合は null を返します。
|
|
84
105
|
*/
|
|
@@ -89,7 +110,6 @@ function getSortPlan(node, sourceCode, options = {}) {
|
|
|
89
110
|
}
|
|
90
111
|
const source = sourceCode.text;
|
|
91
112
|
const lineStarts = getLineStarts(source);
|
|
92
|
-
const lineIndent = getLineIndent(source, lineStarts, node.loc.start.line);
|
|
93
113
|
const comments = sourceCode
|
|
94
114
|
.getAllComments()
|
|
95
115
|
.filter((comment) => comment.range[0] >= node.range[0] && comment.range[1] <= node.range[1]);
|
|
@@ -111,7 +131,9 @@ function getSortPlan(node, sourceCode, options = {}) {
|
|
|
111
131
|
text: source.slice(property.range[0], property.range[1]),
|
|
112
132
|
endLine: property.loc.end.line,
|
|
113
133
|
property,
|
|
134
|
+
endOffset: property.range[1],
|
|
114
135
|
startLine: property.loc.start.line,
|
|
136
|
+
startOffset: property.range[0],
|
|
115
137
|
});
|
|
116
138
|
}
|
|
117
139
|
}
|
|
@@ -122,9 +144,10 @@ function getSortPlan(node, sourceCode, options = {}) {
|
|
|
122
144
|
const boundary = previous === null ? node.range[0] + 1 : previous.range[1];
|
|
123
145
|
const candidates = comments.filter((comment) => comment.range[0] >= boundary && comment.range[1] <= property.range[0]);
|
|
124
146
|
if (previous !== null &&
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
147
|
+
candidates.some((comment) => comment.loc.start.line === previous.loc.end.line)) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
if (candidates.some((comment) => comment.loc.start.line === node.loc.start.line)) {
|
|
128
151
|
return null;
|
|
129
152
|
}
|
|
130
153
|
const attached = [];
|
|
@@ -134,31 +157,47 @@ function getSortPlan(node, sourceCode, options = {}) {
|
|
|
134
157
|
if (comment === undefined) {
|
|
135
158
|
continue;
|
|
136
159
|
}
|
|
137
|
-
if (comment.loc.end.line
|
|
160
|
+
if (comment.loc.end.line === expectedLine) {
|
|
138
161
|
return null;
|
|
139
162
|
}
|
|
140
|
-
if (comment.loc.
|
|
141
|
-
|
|
163
|
+
if (comment.loc.end.line !== expectedLine - 1) {
|
|
164
|
+
break;
|
|
142
165
|
}
|
|
143
|
-
if (comment.loc.start.line ===
|
|
166
|
+
if (comment.loc.start.line === expectedLine) {
|
|
144
167
|
return null;
|
|
145
168
|
}
|
|
146
169
|
attached.unshift(comment);
|
|
147
170
|
expectedLine = comment.loc.start.line;
|
|
148
171
|
}
|
|
149
|
-
if (attached.length !== candidates.length) {
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
172
|
const first = attached[0] ?? property;
|
|
153
173
|
const startLine = first.loc.start.line;
|
|
154
|
-
|
|
174
|
+
let startOffset;
|
|
175
|
+
if (attached.length > 0) {
|
|
176
|
+
startOffset = lineStarts[startLine - 1] ?? first.range[0];
|
|
177
|
+
}
|
|
178
|
+
else if (startLine === previous?.loc.end.line) {
|
|
179
|
+
startOffset = property.range[0];
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
startOffset = lineStarts[startLine - 1] ?? property.range[0];
|
|
183
|
+
}
|
|
184
|
+
let endOffset = property.range[1];
|
|
185
|
+
while (endOffset > property.range[0]) {
|
|
186
|
+
const character = source[endOffset - 1];
|
|
187
|
+
if (character !== "," && character !== ";") {
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
endOffset -= 1;
|
|
191
|
+
}
|
|
155
192
|
const isRest = property.type === "RestElement";
|
|
156
193
|
units.push({
|
|
157
194
|
name: isRest ? null : getPropertyName(property),
|
|
158
|
-
text: source.slice(
|
|
195
|
+
text: source.slice(startOffset, endOffset),
|
|
159
196
|
endLine: property.loc.end.line,
|
|
160
197
|
property,
|
|
198
|
+
endOffset,
|
|
161
199
|
startLine,
|
|
200
|
+
startOffset,
|
|
162
201
|
});
|
|
163
202
|
previousProperty = property;
|
|
164
203
|
}
|
|
@@ -170,52 +209,66 @@ function getSortPlan(node, sourceCode, options = {}) {
|
|
|
170
209
|
return null;
|
|
171
210
|
}
|
|
172
211
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
212
|
+
if (inlineStart) {
|
|
213
|
+
const lineIndent = getLineIndent(source, lineStarts, node.loc.start.line);
|
|
214
|
+
const groups = splitGroups(units);
|
|
215
|
+
const needsFormat = forceMultiline;
|
|
216
|
+
const needsSort = groups.some((group) => {
|
|
217
|
+
const sorted = sortGroup(group);
|
|
218
|
+
return sorted.some((unit, index) => unit !== group[index]);
|
|
219
|
+
});
|
|
220
|
+
if (!needsSort && !needsFormat) {
|
|
221
|
+
return {
|
|
222
|
+
edits: null,
|
|
223
|
+
needsFix: false,
|
|
224
|
+
replacement: null,
|
|
225
|
+
};
|
|
180
226
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
227
|
+
if (singleLine && !forceMultiline) {
|
|
228
|
+
const sortedUnits = sortGroup(units);
|
|
229
|
+
return {
|
|
230
|
+
edits: null,
|
|
231
|
+
needsFix: true,
|
|
232
|
+
replacement: `{ ${sortedUnits.map((unit) => unit.text).join(", ")} }`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const groupsText = groups.map((group) => getEditText(sortGroup(group))).join(",\n\n");
|
|
236
|
+
const lastUnit = groups.flatMap((group) => sortGroup(group)).at(-1);
|
|
237
|
+
const trailingComma = lastUnit?.property.type === "RestElement" ? "" : ",";
|
|
238
|
+
return {
|
|
239
|
+
edits: null,
|
|
240
|
+
needsFix: true,
|
|
241
|
+
replacement: `{\n${groupsText}${trailingComma}\n${lineIndent}}`,
|
|
242
|
+
};
|
|
186
243
|
}
|
|
187
|
-
|
|
188
|
-
for (const group of
|
|
244
|
+
const edits = [];
|
|
245
|
+
for (const group of splitGroups(units)) {
|
|
189
246
|
const sorted = sortGroup(group);
|
|
190
|
-
if (sorted.
|
|
191
|
-
|
|
192
|
-
break;
|
|
247
|
+
if (sorted.every((unit, index) => unit === group[index])) {
|
|
248
|
+
continue;
|
|
193
249
|
}
|
|
250
|
+
const first = group[0];
|
|
251
|
+
const last = group.at(-1);
|
|
252
|
+
if (first === undefined || last === undefined) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
edits.push({
|
|
256
|
+
end: last.endOffset,
|
|
257
|
+
text: getEditText(sorted),
|
|
258
|
+
start: first.startOffset,
|
|
259
|
+
});
|
|
194
260
|
}
|
|
195
|
-
|
|
196
|
-
if (!needsSort && !needsFormat) {
|
|
261
|
+
if (edits.length === 0) {
|
|
197
262
|
return {
|
|
263
|
+
edits: null,
|
|
198
264
|
needsFix: false,
|
|
199
265
|
replacement: null,
|
|
200
266
|
};
|
|
201
267
|
}
|
|
202
|
-
if (singleLine && !forceMultiline) {
|
|
203
|
-
const sortedUnits = sortGroup(units);
|
|
204
|
-
return {
|
|
205
|
-
needsFix: true,
|
|
206
|
-
replacement: `{ ${sortedUnits.map((unit) => unit.text).join(", ")} }`,
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
const groupsText = groups
|
|
210
|
-
.map((group) => sortGroup(group)
|
|
211
|
-
.map((unit) => unit.text)
|
|
212
|
-
.join(",\n"))
|
|
213
|
-
.join(",\n\n");
|
|
214
|
-
const lastUnit = groups.flatMap((group) => sortGroup(group)).at(-1);
|
|
215
|
-
const trailingComma = lastUnit?.property.type === "RestElement" ? "" : ",";
|
|
216
268
|
return {
|
|
269
|
+
edits,
|
|
217
270
|
needsFix: true,
|
|
218
|
-
replacement:
|
|
271
|
+
replacement: null,
|
|
219
272
|
};
|
|
220
273
|
}
|
|
221
274
|
export { getSortPlan, compareNames };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* オブジェクトリテラルのプロパティーを、名前の長さが短い順、同じ長さなら名前順に並べます。
|
|
3
3
|
*
|
|
4
4
|
* 空行で区切られたグループは意図的なまとまりとみなし、グループ内だけで並べ替えます。
|
|
5
|
-
*
|
|
5
|
+
* プロパティー直上のコメントはプロパティーと一緒に移動し、空行を挟んだコメントはグループ見出しとして残します。
|
|
6
6
|
*/
|
|
7
7
|
declare const sortObjectProperties: import("vite-plus/lint/plugins").Rule;
|
|
8
8
|
export default sortObjectProperties;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sort-object-properties.d.ts","sourceRoot":"","sources":["../../../src/rules/sort-object-properties.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,QAAA,MAAM,oBAAoB,
|
|
1
|
+
{"version":3,"file":"sort-object-properties.d.ts","sourceRoot":"","sources":["../../../src/rules/sort-object-properties.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,QAAA,MAAM,oBAAoB,uCA6DxB,CAAC;eAEY,oBAAoB"}
|
|
@@ -4,7 +4,7 @@ import { getSortPlan } from "./object-sort-utils.js";
|
|
|
4
4
|
* オブジェクトリテラルのプロパティーを、名前の長さが短い順、同じ長さなら名前順に並べます。
|
|
5
5
|
*
|
|
6
6
|
* 空行で区切られたグループは意図的なまとまりとみなし、グループ内だけで並べ替えます。
|
|
7
|
-
*
|
|
7
|
+
* プロパティー直上のコメントはプロパティーと一緒に移動し、空行を挟んだコメントはグループ見出しとして残します。
|
|
8
8
|
*/
|
|
9
9
|
const sortObjectProperties = defineRule({
|
|
10
10
|
meta: {
|
|
@@ -29,6 +29,9 @@ const sortObjectProperties = defineRule({
|
|
|
29
29
|
}
|
|
30
30
|
context.report({
|
|
31
31
|
fix(fixer) {
|
|
32
|
+
if (plan.edits !== null) {
|
|
33
|
+
return plan.edits.map((edit) => fixer.replaceTextRange([edit.start, edit.end], edit.text));
|
|
34
|
+
}
|
|
32
35
|
return fixer.replaceText(node, plan.replacement);
|
|
33
36
|
},
|
|
34
37
|
node,
|
|
@@ -42,6 +45,9 @@ const sortObjectProperties = defineRule({
|
|
|
42
45
|
}
|
|
43
46
|
context.report({
|
|
44
47
|
fix(fixer) {
|
|
48
|
+
if (plan.edits !== null) {
|
|
49
|
+
return plan.edits.map((edit) => fixer.replaceTextRange([edit.start, edit.end], edit.text));
|
|
50
|
+
}
|
|
45
51
|
return fixer.replaceText(node, plan.replacement);
|
|
46
52
|
},
|
|
47
53
|
node,
|
package/package.json
CHANGED
|
@@ -18,9 +18,17 @@ interface SortPlanOptions {
|
|
|
18
18
|
|
|
19
19
|
interface SortUnit {
|
|
20
20
|
endLine: number;
|
|
21
|
+
endOffset: number;
|
|
21
22
|
name: string | null;
|
|
22
23
|
property: SortableProperty;
|
|
23
24
|
startLine: number;
|
|
25
|
+
startOffset: number;
|
|
26
|
+
text: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface SortEdit {
|
|
30
|
+
end: number;
|
|
31
|
+
start: number;
|
|
24
32
|
text: string;
|
|
25
33
|
}
|
|
26
34
|
|
|
@@ -28,12 +36,19 @@ type NamedSortUnit = SortUnit & { name: string };
|
|
|
28
36
|
|
|
29
37
|
type SortPlan =
|
|
30
38
|
| {
|
|
39
|
+
edits: null;
|
|
31
40
|
needsFix: false;
|
|
32
41
|
replacement: null;
|
|
33
42
|
}
|
|
34
43
|
| {
|
|
44
|
+
edits: null;
|
|
35
45
|
needsFix: true;
|
|
36
46
|
replacement: string;
|
|
47
|
+
}
|
|
48
|
+
| {
|
|
49
|
+
edits: SortEdit[];
|
|
50
|
+
needsFix: true;
|
|
51
|
+
replacement: null;
|
|
37
52
|
};
|
|
38
53
|
|
|
39
54
|
function getLineStarts(text: string): number[] {
|
|
@@ -137,12 +152,39 @@ function sortGroup(group: readonly SortUnit[]): SortUnit[] {
|
|
|
137
152
|
return [...sortable, ...rest];
|
|
138
153
|
}
|
|
139
154
|
|
|
155
|
+
function splitGroups(units: readonly SortUnit[]): SortUnit[][] {
|
|
156
|
+
const groups: SortUnit[][] = [];
|
|
157
|
+
let currentGroup: SortUnit[] = [];
|
|
158
|
+
let previousEndLine: number | null = null;
|
|
159
|
+
|
|
160
|
+
for (const unit of units) {
|
|
161
|
+
if (previousEndLine !== null && unit.startLine > previousEndLine + 1) {
|
|
162
|
+
groups.push(currentGroup);
|
|
163
|
+
currentGroup = [];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
currentGroup.push(unit);
|
|
167
|
+
previousEndLine = unit.endLine;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (currentGroup.length > 0) {
|
|
171
|
+
groups.push(currentGroup);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return groups;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function getEditText(units: readonly SortUnit[]): string {
|
|
178
|
+
return units.map((unit) => unit.text).join(",\n");
|
|
179
|
+
}
|
|
180
|
+
|
|
140
181
|
/**
|
|
141
182
|
* オブジェクトリテラル/オブジェクトパターンの並び替え計画を返します。
|
|
142
183
|
*
|
|
143
184
|
* - プロパティー名の長さが短い順、同じ長さなら名前順
|
|
144
185
|
* - 空行で区切られたグループ内だけで並び替え
|
|
145
186
|
* - プロパティー直上のコメントはプロパティーと一緒に移動
|
|
187
|
+
* - 空行を挟んだコメントはグループ見出しとしてその場に残す
|
|
146
188
|
*
|
|
147
189
|
* 並び替えが安全でない場合は null を返します。
|
|
148
190
|
*/
|
|
@@ -159,7 +201,6 @@ function getSortPlan(
|
|
|
159
201
|
|
|
160
202
|
const source = sourceCode.text;
|
|
161
203
|
const lineStarts = getLineStarts(source);
|
|
162
|
-
const lineIndent = getLineIndent(source, lineStarts, node.loc.start.line);
|
|
163
204
|
const comments = sourceCode
|
|
164
205
|
.getAllComments()
|
|
165
206
|
.filter((comment) => comment.range[0] >= node.range[0] && comment.range[1] <= node.range[1]);
|
|
@@ -171,7 +212,6 @@ function getSortPlan(
|
|
|
171
212
|
|
|
172
213
|
const inlineStart = firstProperty.loc.start.line === node.loc.start.line;
|
|
173
214
|
const singleLine = node.loc.start.line === node.loc.end.line;
|
|
174
|
-
|
|
175
215
|
const units: SortUnit[] = [];
|
|
176
216
|
|
|
177
217
|
if (inlineStart) {
|
|
@@ -186,7 +226,9 @@ function getSortPlan(
|
|
|
186
226
|
text: source.slice(property.range[0], property.range[1]),
|
|
187
227
|
endLine: property.loc.end.line,
|
|
188
228
|
property,
|
|
229
|
+
endOffset: property.range[1],
|
|
189
230
|
startLine: property.loc.start.line,
|
|
231
|
+
startOffset: property.range[0],
|
|
190
232
|
});
|
|
191
233
|
}
|
|
192
234
|
} else {
|
|
@@ -201,16 +243,15 @@ function getSortPlan(
|
|
|
201
243
|
|
|
202
244
|
if (
|
|
203
245
|
previous !== null &&
|
|
204
|
-
|
|
205
|
-
(comment) =>
|
|
206
|
-
comment.loc.start.line === previous.loc.end.line &&
|
|
207
|
-
comment.range[0] >= previous.range[1] &&
|
|
208
|
-
comment.range[1] <= property.range[0],
|
|
209
|
-
)
|
|
246
|
+
candidates.some((comment) => comment.loc.start.line === previous.loc.end.line)
|
|
210
247
|
) {
|
|
211
248
|
return null;
|
|
212
249
|
}
|
|
213
250
|
|
|
251
|
+
if (candidates.some((comment) => comment.loc.start.line === node.loc.start.line)) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
|
|
214
255
|
const attached: Comment[] = [];
|
|
215
256
|
let expectedLine = property.loc.start.line;
|
|
216
257
|
|
|
@@ -221,15 +262,15 @@ function getSortPlan(
|
|
|
221
262
|
continue;
|
|
222
263
|
}
|
|
223
264
|
|
|
224
|
-
if (comment.loc.end.line
|
|
265
|
+
if (comment.loc.end.line === expectedLine) {
|
|
225
266
|
return null;
|
|
226
267
|
}
|
|
227
268
|
|
|
228
|
-
if (comment.loc.
|
|
229
|
-
|
|
269
|
+
if (comment.loc.end.line !== expectedLine - 1) {
|
|
270
|
+
break;
|
|
230
271
|
}
|
|
231
272
|
|
|
232
|
-
if (comment.loc.start.line ===
|
|
273
|
+
if (comment.loc.start.line === expectedLine) {
|
|
233
274
|
return null;
|
|
234
275
|
}
|
|
235
276
|
|
|
@@ -237,20 +278,39 @@ function getSortPlan(
|
|
|
237
278
|
expectedLine = comment.loc.start.line;
|
|
238
279
|
}
|
|
239
280
|
|
|
240
|
-
if (attached.length !== candidates.length) {
|
|
241
|
-
return null;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
281
|
const first = attached[0] ?? property;
|
|
245
282
|
const startLine = first.loc.start.line;
|
|
246
|
-
|
|
283
|
+
let startOffset: number;
|
|
284
|
+
|
|
285
|
+
if (attached.length > 0) {
|
|
286
|
+
startOffset = lineStarts[startLine - 1] ?? first.range[0];
|
|
287
|
+
} else if (startLine === previous?.loc.end.line) {
|
|
288
|
+
startOffset = property.range[0];
|
|
289
|
+
} else {
|
|
290
|
+
startOffset = lineStarts[startLine - 1] ?? property.range[0];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
let endOffset = property.range[1];
|
|
294
|
+
|
|
295
|
+
while (endOffset > property.range[0]) {
|
|
296
|
+
const character = source[endOffset - 1];
|
|
297
|
+
|
|
298
|
+
if (character !== "," && character !== ";") {
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
endOffset -= 1;
|
|
303
|
+
}
|
|
304
|
+
|
|
247
305
|
const isRest = property.type === "RestElement";
|
|
248
306
|
units.push({
|
|
249
307
|
name: isRest ? null : getPropertyName(property),
|
|
250
|
-
text: source.slice(
|
|
308
|
+
text: source.slice(startOffset, endOffset),
|
|
251
309
|
endLine: property.loc.end.line,
|
|
252
310
|
property,
|
|
311
|
+
endOffset,
|
|
253
312
|
startLine,
|
|
313
|
+
startOffset,
|
|
254
314
|
});
|
|
255
315
|
|
|
256
316
|
previousProperty = property;
|
|
@@ -272,66 +332,80 @@ function getSortPlan(
|
|
|
272
332
|
}
|
|
273
333
|
}
|
|
274
334
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
335
|
+
if (inlineStart) {
|
|
336
|
+
const lineIndent = getLineIndent(source, lineStarts, node.loc.start.line);
|
|
337
|
+
const groups = splitGroups(units);
|
|
338
|
+
const needsFormat = forceMultiline;
|
|
339
|
+
const needsSort = groups.some((group) => {
|
|
340
|
+
const sorted = sortGroup(group);
|
|
341
|
+
|
|
342
|
+
return sorted.some((unit, index) => unit !== group[index]);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
if (!needsSort && !needsFormat) {
|
|
346
|
+
return {
|
|
347
|
+
edits: null,
|
|
348
|
+
needsFix: false,
|
|
349
|
+
replacement: null,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
278
352
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
353
|
+
if (singleLine && !forceMultiline) {
|
|
354
|
+
const sortedUnits = sortGroup(units);
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
edits: null,
|
|
358
|
+
needsFix: true,
|
|
359
|
+
replacement: `{ ${sortedUnits.map((unit) => unit.text).join(", ")} }`,
|
|
360
|
+
};
|
|
283
361
|
}
|
|
284
362
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
363
|
+
const groupsText = groups.map((group) => getEditText(sortGroup(group))).join(",\n\n");
|
|
364
|
+
const lastUnit = groups.flatMap((group) => sortGroup(group)).at(-1);
|
|
365
|
+
const trailingComma = lastUnit?.property.type === "RestElement" ? "" : ",";
|
|
288
366
|
|
|
289
|
-
|
|
290
|
-
|
|
367
|
+
return {
|
|
368
|
+
edits: null,
|
|
369
|
+
needsFix: true,
|
|
370
|
+
replacement: `{\n${groupsText}${trailingComma}\n${lineIndent}}`,
|
|
371
|
+
};
|
|
291
372
|
}
|
|
292
373
|
|
|
293
|
-
|
|
374
|
+
const edits: SortEdit[] = [];
|
|
294
375
|
|
|
295
|
-
for (const group of
|
|
376
|
+
for (const group of splitGroups(units)) {
|
|
296
377
|
const sorted = sortGroup(group);
|
|
297
378
|
|
|
298
|
-
if (sorted.
|
|
299
|
-
|
|
300
|
-
break;
|
|
379
|
+
if (sorted.every((unit, index) => unit === group[index])) {
|
|
380
|
+
continue;
|
|
301
381
|
}
|
|
302
|
-
}
|
|
303
382
|
|
|
304
|
-
|
|
383
|
+
const first = group[0];
|
|
384
|
+
const last = group.at(-1);
|
|
305
385
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
replacement: null,
|
|
310
|
-
};
|
|
311
|
-
}
|
|
386
|
+
if (first === undefined || last === undefined) {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
312
389
|
|
|
313
|
-
|
|
314
|
-
|
|
390
|
+
edits.push({
|
|
391
|
+
end: last.endOffset,
|
|
392
|
+
text: getEditText(sorted),
|
|
393
|
+
start: first.startOffset,
|
|
394
|
+
});
|
|
395
|
+
}
|
|
315
396
|
|
|
397
|
+
if (edits.length === 0) {
|
|
316
398
|
return {
|
|
317
|
-
|
|
318
|
-
|
|
399
|
+
edits: null,
|
|
400
|
+
needsFix: false,
|
|
401
|
+
replacement: null,
|
|
319
402
|
};
|
|
320
403
|
}
|
|
321
404
|
|
|
322
|
-
const groupsText = groups
|
|
323
|
-
.map((group) =>
|
|
324
|
-
sortGroup(group)
|
|
325
|
-
.map((unit) => unit.text)
|
|
326
|
-
.join(",\n"),
|
|
327
|
-
)
|
|
328
|
-
.join(",\n\n");
|
|
329
|
-
const lastUnit = groups.flatMap((group) => sortGroup(group)).at(-1);
|
|
330
|
-
const trailingComma = lastUnit?.property.type === "RestElement" ? "" : ",";
|
|
331
|
-
|
|
332
405
|
return {
|
|
406
|
+
edits,
|
|
333
407
|
needsFix: true,
|
|
334
|
-
replacement:
|
|
408
|
+
replacement: null,
|
|
335
409
|
};
|
|
336
410
|
}
|
|
337
411
|
|
|
@@ -6,7 +6,7 @@ import { getSortPlan } from "./object-sort-utils.js";
|
|
|
6
6
|
* オブジェクトリテラルのプロパティーを、名前の長さが短い順、同じ長さなら名前順に並べます。
|
|
7
7
|
*
|
|
8
8
|
* 空行で区切られたグループは意図的なまとまりとみなし、グループ内だけで並べ替えます。
|
|
9
|
-
*
|
|
9
|
+
* プロパティー直上のコメントはプロパティーと一緒に移動し、空行を挟んだコメントはグループ見出しとして残します。
|
|
10
10
|
*/
|
|
11
11
|
const sortObjectProperties = defineRule({
|
|
12
12
|
meta: {
|
|
@@ -34,6 +34,12 @@ const sortObjectProperties = defineRule({
|
|
|
34
34
|
|
|
35
35
|
context.report({
|
|
36
36
|
fix(fixer) {
|
|
37
|
+
if (plan.edits !== null) {
|
|
38
|
+
return plan.edits.map((edit) =>
|
|
39
|
+
fixer.replaceTextRange([edit.start, edit.end], edit.text),
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
37
43
|
return fixer.replaceText(node, plan.replacement);
|
|
38
44
|
},
|
|
39
45
|
node,
|
|
@@ -49,6 +55,12 @@ const sortObjectProperties = defineRule({
|
|
|
49
55
|
|
|
50
56
|
context.report({
|
|
51
57
|
fix(fixer) {
|
|
58
|
+
if (plan.edits !== null) {
|
|
59
|
+
return plan.edits.map((edit) =>
|
|
60
|
+
fixer.replaceTextRange([edit.start, edit.end], edit.text),
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
52
64
|
return fixer.replaceText(node, plan.replacement);
|
|
53
65
|
},
|
|
54
66
|
node,
|