• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ota-meshi / eslint-plugin-jsonc / 15085399755

17 May 2025 12:54PM UTC coverage: 68.782% (-3.7%) from 72.499%
15085399755

push

github

web-flow
fix(deps): update dependency synckit to `^0.6.2 || ^0.7.3 || ^0.11.5` (#404)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: JounQin <admin@1stg.me>

1164 of 1985 branches covered (58.64%)

Branch coverage included in aggregate %.

1 of 1 new or added line in 1 file covered. (100.0%)

564 existing lines in 40 files now uncovered.

2011 of 2631 relevant lines covered (76.43%)

102.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.7
/lib/utils/fix-sort-elements.ts
1
import {
2
  isClosingBraceToken,
1✔
3
  isClosingBracketToken,
4
  isClosingParenToken,
5
  isCommaToken,
1✔
6
  isCommentToken,
7
  isNotCommaToken,
8
  isOpeningParenToken,
172✔
9
} from "@eslint-community/eslint-utils";
10
import type { Rule, AST as ESLintAST, SourceCode } from "eslint";
11
import type { AST } from "jsonc-eslint-parser";
1✔
12
import type * as ESTree from "estree";
13

172✔
14
export type AroundTarget =
172✔
15
  | {
172!
UNCOV
16
      before: ESLintAST.Token;
×
17
      after: ESLintAST.Token;
18
      node?: AST.JSONNode | undefined;
172✔
19
    }
20
  | {
21
      before: ESLintAST.Token;
22
      after: ESLintAST.Token;
172✔
23
      node?: undefined;
24
    };
25
type NodeTarget = { node: AST.JSONNode; before?: undefined; after?: undefined };
172✔
26
type Target = NodeTarget | AroundTarget;
27
/**
8✔
28
 * Fixed target element for sorting.
29
 */
30
export function* fixForSorting(
31
  fixer: Rule.RuleFixer,
8✔
32
  sourceCode: SourceCode,
33
  target: Target,
34
  to: Target,
35
): IterableIterator<Rule.Fix> {
36
  const targetInfo = calcTargetInfo(sourceCode, target);
172✔
37

172✔
38
  const toPrevInfo = getPrevElementInfo(sourceCode, to);
442✔
39

40
  if (
41
    toPrevInfo.comma &&
42
    toPrevInfo.last.range![1] <= toPrevInfo.comma.range[0]
43
  ) {
44
    yield fixer.removeRange(toPrevInfo.comma.range);
172!
45
  }
172✔
46

172✔
47
  let insertRange = [
48
    toPrevInfo.last.range![1],
172✔
49
    toPrevInfo.last.range![1],
172✔
50
  ] as ESLintAST.Range;
4✔
51
  const toBeforeNextToken = sourceCode.getTokenAfter(toPrevInfo.last, {
4✔
52
    includeComments: true,
53
  })!;
54
  if (toBeforeNextToken.loc!.start.line - toPrevInfo.last.loc!.end.line > 1) {
55
    // If there are blank lines, the element is inserted after the blank lines.
56
    const offset = sourceCode.getIndexFromLoc({
57
      line: toBeforeNextToken.loc!.start.line - 1,
58
      column: 0,
59
    });
168✔
60
    insertRange = [offset, offset];
168✔
61
  }
62
  yield fixer.insertTextAfterRange(insertRange, targetInfo.insertCode);
63

64
  for (const removeRange of targetInfo.removeRanges) {
65
    yield fixer.removeRange(removeRange);
172✔
66
  }
172✔
67
}
94✔
68

94!
69
/**
94✔
70
 * Calculate the fix information of the target.
71
 */
72
function calcTargetInfo(
172✔
73
  sourceCode: SourceCode,
172✔
74
  target: Target,
75
): {
76
  insertCode: string;
77
  removeRanges: ESLintAST.Range[];
172✔
78
} {
79
  const nodeEndIndex = target.node
80
    ? getLastTokenOfNode(sourceCode, target.node).range[1]
81
    : target.after.range[0];
82

83
  const endInfo = getElementEndInfo(sourceCode, target);
84
  const prevInfo = getPrevElementInfo(sourceCode, target);
85

344✔
86
  let insertCode: string;
344✔
87

344✔
88
  const removeRanges: ESLintAST.Range[] = [];
2✔
89
  if (prevInfo.comma && prevInfo.last.range![1] <= prevInfo.comma.range[0]) {
90
    insertCode = `${sourceCode.text.slice(
344✔
91
      prevInfo.last.range![1],
92
      prevInfo.comma.range[0],
93
    )}${sourceCode.text.slice(prevInfo.comma.range[1], nodeEndIndex)}`;
94
    removeRanges.push(
95
      [prevInfo.last.range![1], prevInfo.comma.range[0]],
819✔
96
      [prevInfo.comma.range[1], nodeEndIndex],
819✔
97
    );
819✔
98
  } else {
3✔
99
    insertCode = sourceCode.text.slice(prevInfo.last.range![1], nodeEndIndex);
100
    removeRanges.push([prevInfo.last.range![1], nodeEndIndex]);
819✔
101
  }
102

103
  const hasTrailingComma =
104
    endInfo.comma && endInfo.comma.range[1] <= endInfo.last.range![1];
105
  if (!hasTrailingComma) {
424!
106
    insertCode += ",";
424✔
107
    if (prevInfo.comma) {
108
      removeRanges.push(prevInfo.comma.range);
94✔
109
    }
110
  }
111
  insertCode += sourceCode.text.slice(nodeEndIndex, endInfo.last.range![1]);
112
  removeRanges.push([nodeEndIndex, endInfo.last.range![1]]);
113

114
  return {
330✔
115
    insertCode,
330✔
116
    removeRanges,
330✔
117
  };
118
}
119

1✔
120
/**
121
 * Get the first token of the node.
122
 */
123
function getFirstTokenOfNode(
124
  sourceCode: SourceCode,
125
  node: AST.JSONNode | ESLintAST.Token,
329✔
126
): ESLintAST.Token {
127
  let token = sourceCode.getFirstToken(node as never)!;
128
  let target: ESLintAST.Token | null = token;
10✔
129
  while (
130
    (target = sourceCode.getTokenBefore(token)) &&
131
    isOpeningParenToken(target)
132
  ) {
133
    token = target;
134
  }
319✔
135
  return token;
319✔
136
}
137

198✔
138
/**
139
 * Get the last token of the node.
140
 */
141
function getLastTokenOfNode(
142
  sourceCode: SourceCode,
143
  node: AST.JSONNode | ESLintAST.Token,
144
): ESLintAST.Token {
121✔
145
  let token = sourceCode.getLastToken(node as never)!;
146
  let target: ESLintAST.Token | null = token;
147
  while (
2✔
148
    (target = sourceCode.getTokenAfter(token)) &&
149
    isClosingParenToken(target)
150
  ) {
151
    token = target;
152
  }
153
  return token;
119✔
154
}
155

156
/**
157
 * Get the end of the target element and the next element and token information.
158
 */
159
function getElementEndInfo(
160
  sourceCode: SourceCode,
161
  target: Target | { node: ESLintAST.Token },
162
): {
223!
UNCOV
163
  // Trailing comma
×
164
  comma: ESLintAST.Token | null;
165
  // Next element token
166
  nextElement: ESLintAST.Token | null;
167
  // The last token of the target element
223✔
168
  last: ESLintAST.Token | ESTree.Comment;
223✔
169
} {
170
  const afterToken = target.node
223✔
171
    ? sourceCode.getTokenAfter(getLastTokenOfNode(sourceCode, target.node))!
172
    : target.after;
173
  if (isNotCommaToken(afterToken)) {
151✔
174
    // If there is no comma, the element is the last element.
175
    return {
223✔
176
      comma: null,
177
      nextElement: null,
178
      last: getLastTokenWithTrailingComments(sourceCode, target),
179
    };
180
  }
344!
181
  const comma = afterToken;
344✔
182
  const nextElement = sourceCode.getTokenAfter(afterToken)!;
183
  if (isCommaToken(nextElement)) {
91✔
184
    // If the next element is empty,
185
    // the position of the comma is the end of the element's range.
186
    return {
187
      comma,
188
      nextElement: null,
189
      last: comma,
253✔
190
    };
253✔
191
  }
253✔
192
  if (isClosingBraceToken(nextElement) || isClosingBracketToken(nextElement)) {
193
    // If the next token is a closing brace or bracket,
194
    // the position of the comma is the end of the element's range.
1✔
195
    return {
196
      comma,
197
      nextElement: null,
198
      last: getLastTokenWithTrailingComments(sourceCode, target),
199
    };
200
  }
252✔
201

202
  const node = target.node;
203

252✔
204
  if (node && node.loc.end.line === nextElement.loc.start.line) {
205
    // There is no line break between the target element and the next element.
206
    return {
207
      comma,
208
      nextElement,
209
      last: comma,
210
    };
211
  }
212
  // There are line breaks between the target element and the next element.
213
  if (
214
    node &&
215
    node.loc.end.line < comma.loc.start.line &&
216
    comma.loc.end.line < nextElement.loc.start.line
217
  ) {
218
    // If there is a line break between the target element and a comma and the next element,
219
    // the position of the comma is the end of the element's range.
220
    return {
221
      comma,
222
      nextElement,
223
      last: comma,
224
    };
225
  }
226

227
  return {
228
    comma,
229
    nextElement,
230
    last: getLastTokenWithTrailingComments(sourceCode, target),
231
  };
232
}
233

234
/**
235
 * Get the last token of the target element with trailing comments.
236
 */
237
function getLastTokenWithTrailingComments(
238
  sourceCode: SourceCode,
239
  target: Target | { node: ESLintAST.Token },
240
) {
241
  if (!target.node) {
242
    return sourceCode.getTokenBefore(target.after, {
243
      includeComments: true,
244
    })!;
245
  }
246
  const node = target.node;
247
  let last: ESLintAST.Token | ESTree.Comment = getLastTokenOfNode(
248
    sourceCode,
249
    node,
250
  );
251
  let after: ESLintAST.Token | ESTree.Comment | null;
252
  while (
253
    (after = sourceCode.getTokenAfter(last, {
254
      includeComments: true,
255
    })) &&
256
    (isCommentToken(after) || isCommaToken(after)) &&
257
    node.loc.end.line === after.loc!.end.line
258
  ) {
259
    last = after;
260
  }
261
  return last;
262
}
263

264
/**
265
 * Get the previous element and token information.
266
 */
267
function getPrevElementInfo(
268
  sourceCode: SourceCode,
269
  target: Target,
270
): {
271
  // Leading comma
272
  comma: ESLintAST.Token | null;
273
  // Previous element token
274
  prevElement: ESLintAST.Token | null;
275
  // The last token of the target element
276
  last: ESLintAST.Token | ESTree.Comment;
277
} {
278
  const beforeToken = target.node
279
    ? sourceCode.getTokenBefore(getFirstTokenOfNode(sourceCode, target.node))!
280
    : target.before;
281
  if (isNotCommaToken(beforeToken)) {
282
    // If there is no comma, the element is the first element.
283
    return {
284
      comma: null,
285
      prevElement: null,
286
      last: beforeToken,
287
    };
288
  }
289
  const comma = beforeToken;
290
  const prevElement = sourceCode.getTokenBefore(beforeToken)!;
291

292
  if (isCommaToken(prevElement)) {
293
    // If the previous element is empty,
294
    // the position of the comma is the end of the previous element's range.
295
    return {
296
      comma,
297
      prevElement: null,
298
      last: comma,
299
    };
300
  }
301

302
  const endInfo = getElementEndInfo(sourceCode, { node: prevElement });
303

304
  return {
305
    comma: endInfo.comma,
306
    prevElement,
307
    last: endInfo.last,
308
  };
309
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc