eslint-plugin-yml 1.17.0 → 1.19.0

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/lib/meta.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const name = "eslint-plugin-yml";
2
- export declare const version = "1.17.0";
1
+ export declare const name: "eslint-plugin-yml";
2
+ export declare const version: "1.19.0";
package/lib/meta.js CHANGED
@@ -2,4 +2,4 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.version = exports.name = void 0;
4
4
  exports.name = "eslint-plugin-yml";
5
- exports.version = "1.17.0";
5
+ exports.version = "1.19.0";
@@ -12,6 +12,7 @@ function parseOptions(context) {
12
12
  const numOfIndent = (0, yaml_1.getNumOfIndent)(context, indentOption);
13
13
  let indentBlockSequences = true;
14
14
  let indicatorValueIndent = numOfIndent;
15
+ let alignMultilineFlowScalars = false;
15
16
  if (objectOptions) {
16
17
  if (objectOptions.indentBlockSequences === false) {
17
18
  indentBlockSequences = false;
@@ -19,11 +20,15 @@ function parseOptions(context) {
19
20
  if (objectOptions.indicatorValueIndent != null) {
20
21
  indicatorValueIndent = objectOptions.indicatorValueIndent;
21
22
  }
23
+ if (objectOptions.alignMultilineFlowScalars != null) {
24
+ alignMultilineFlowScalars = objectOptions.alignMultilineFlowScalars;
25
+ }
22
26
  }
23
27
  return {
24
28
  numOfIndent,
25
29
  indentBlockSequences,
26
30
  indicatorValueIndent,
31
+ alignMultilineFlowScalars,
27
32
  };
28
33
  }
29
34
  exports.default = (0, index_1.createRule)("indent", {
@@ -48,6 +53,9 @@ exports.default = (0, index_1.createRule)("indent", {
48
53
  type: "integer",
49
54
  minimum: 2,
50
55
  },
56
+ alignMultilineFlowScalars: {
57
+ type: "boolean",
58
+ },
51
59
  },
52
60
  additionalProperties: false,
53
61
  },
@@ -66,7 +74,7 @@ exports.default = (0, index_1.createRule)("indent", {
66
74
  if ((0, yaml_1.hasTabIndent)(context)) {
67
75
  return {};
68
76
  }
69
- const { numOfIndent, indentBlockSequences, indicatorValueIndent } = parseOptions(context);
77
+ const { numOfIndent, indentBlockSequences, indicatorValueIndent, alignMultilineFlowScalars, } = parseOptions(context);
70
78
  const indents = new Map();
71
79
  const indicators = new Set();
72
80
  const blockLiteralMarks = new Set();
@@ -451,7 +459,13 @@ exports.default = (0, index_1.createRule)("indent", {
451
459
  processBlockLiteral(indent, scalarNode, lineIndent.lastScalar.expectedIndent);
452
460
  }
453
461
  else {
454
- processScalar(indent, scalarNode, lineIndent.lastScalar.expectedIndent);
462
+ let expectedIndent = lineIndent.lastScalar.expectedIndent;
463
+ if (alignMultilineFlowScalars) {
464
+ if (!isBeginningToken(lineIndent.lastScalar.token)) {
465
+ expectedIndent = lineIndent.lastScalar.node.loc.start.column;
466
+ }
467
+ }
468
+ processMultilineScalar(indent, scalarNode, expectedIndent);
455
469
  }
456
470
  }
457
471
  }
@@ -568,7 +582,7 @@ exports.default = (0, index_1.createRule)("indent", {
568
582
  };
569
583
  }
570
584
  }
571
- function processScalar(lineIndent, scalarNode, expectedIndent) {
585
+ function processMultilineScalar(lineIndent, scalarNode, expectedIndent) {
572
586
  for (let scalarLine = lineIndent.line + 1; scalarLine <= scalarNode.loc.end.line; scalarLine++) {
573
587
  const scalarActualIndent = getActualLineIndent(scalarLine);
574
588
  if (scalarActualIndent == null) {
@@ -7,6 +7,7 @@ const natural_compare_1 = __importDefault(require("natural-compare"));
7
7
  const index_1 = require("../utils/index");
8
8
  const ast_utils_1 = require("../utils/ast-utils");
9
9
  const compat_1 = require("../utils/compat");
10
+ const calc_shortest_edit_script_1 = require("../utils/calc-shortest-edit-script");
10
11
  function isNewLine(char) {
11
12
  return (char === "\n" || char === "\r" || char === "\u2028" || char === "\u2029");
12
13
  }
@@ -306,7 +307,8 @@ exports.default = (0, index_1.createRule)("sort-keys", {
306
307
  ],
307
308
  },
308
309
  messages: {
309
- sortKeys: "Expected mapping keys to be in {{orderText}} order. '{{thisName}}' should be before '{{prevName}}'.",
310
+ shouldBeBefore: "Expected mapping keys to be in {{orderText}} order. '{{thisName}}' should be before '{{targetName}}'.",
311
+ shouldBeAfter: "Expected mapping keys to be in {{orderText}} order. '{{thisName}}' should be after '{{targetName}}'.",
310
312
  },
311
313
  type: "suggestion",
312
314
  },
@@ -317,16 +319,18 @@ exports.default = (0, index_1.createRule)("sort-keys", {
317
319
  return {};
318
320
  }
319
321
  const parsedOptions = parseOptions(context.options, sourceCode);
320
- function isValidOrder(prevData, thisData, option) {
321
- if (option.isValidOrder(prevData, thisData)) {
322
- return true;
323
- }
324
- for (const aliasName of thisData.anchorAlias.aliases) {
322
+ function shouldKeepOrder(prevData, nextData) {
323
+ if ((prevData.anchorAlias.aliases.size === 0 &&
324
+ prevData.anchorAlias.anchors.size === 0) ||
325
+ (nextData.anchorAlias.aliases.size === 0 &&
326
+ nextData.anchorAlias.anchors.size === 0))
327
+ return false;
328
+ for (const aliasName of nextData.anchorAlias.aliases) {
325
329
  if (prevData.anchorAlias.anchors.has(aliasName)) {
326
330
  return true;
327
331
  }
328
332
  }
329
- for (const anchorName of thisData.anchorAlias.anchors) {
333
+ for (const anchorName of nextData.anchorAlias.anchors) {
330
334
  if (prevData.anchorAlias.aliases.has(anchorName)) {
331
335
  return true;
332
336
  }
@@ -339,55 +343,176 @@ exports.default = (0, index_1.createRule)("sort-keys", {
339
343
  }
340
344
  return option.ignore(data);
341
345
  }
342
- function verifyPair(data, option) {
343
- if (ignore(data, option)) {
344
- return;
345
- }
346
- const prevList = [];
347
- let currTarget = data;
348
- let prevTarget;
349
- while ((prevTarget = currTarget.getPrev())) {
350
- if (option.allowLineSeparatedGroups) {
351
- if (hasBlankLine(prevTarget, currTarget)) {
352
- break;
353
- }
346
+ function groupingPairs(pairs, option) {
347
+ const groups = [];
348
+ let group = [];
349
+ let prev = null;
350
+ for (const pair of pairs) {
351
+ if (ignore(pair, option)) {
352
+ prev = pair;
353
+ continue;
354
354
  }
355
- if (!ignore(prevTarget, option)) {
356
- prevList.push(prevTarget);
355
+ if (prev &&
356
+ option.allowLineSeparatedGroups &&
357
+ hasBlankLine(prev, pair)) {
358
+ if (group.length > 0) {
359
+ groups.push(group);
360
+ group = [];
361
+ }
357
362
  }
358
- currTarget = prevTarget;
363
+ group.push(pair);
364
+ prev = pair;
359
365
  }
360
- if (prevList.length === 0) {
361
- return;
366
+ if (group.length > 0) {
367
+ groups.push(group);
362
368
  }
363
- const prev = prevList[0];
364
- if (!isValidOrder(prev, data, option)) {
365
- context.report({
366
- loc: data.reportLoc,
367
- messageId: "sortKeys",
368
- data: {
369
- thisName: data.name,
370
- prevName: prev.name,
371
- orderText: option.orderText,
372
- },
373
- *fix(fixer) {
374
- let moveTarget = prevList[0];
375
- for (const prev of prevList) {
376
- if (isValidOrder(prev, data, option)) {
377
- break;
369
+ return groups;
370
+ }
371
+ function bubbleSort(pairs, option) {
372
+ const l = pairs.length;
373
+ const result = [...pairs];
374
+ let swapped;
375
+ do {
376
+ swapped = false;
377
+ for (let nextIndex = 1; nextIndex < l; nextIndex++) {
378
+ const prevIndex = nextIndex - 1;
379
+ if (option.isValidOrder(result[prevIndex], result[nextIndex]) ||
380
+ shouldKeepOrder(result[prevIndex], result[nextIndex]))
381
+ continue;
382
+ [result[prevIndex], result[nextIndex]] = [
383
+ result[nextIndex],
384
+ result[prevIndex],
385
+ ];
386
+ swapped = true;
387
+ }
388
+ } while (swapped);
389
+ return result;
390
+ }
391
+ function verifyPairs(pairs, option) {
392
+ const sorted = bubbleSort(pairs, option);
393
+ const editScript = (0, calc_shortest_edit_script_1.calcShortestEditScript)(pairs, sorted);
394
+ for (let index = 0; index < editScript.length; index++) {
395
+ const edit = editScript[index];
396
+ if (edit.type !== "delete")
397
+ continue;
398
+ const insertEditIndex = editScript.findIndex((e) => e.type === "insert" && e.b === edit.a);
399
+ if (insertEditIndex === -1) {
400
+ continue;
401
+ }
402
+ if (index < insertEditIndex) {
403
+ const target = findInsertAfterTarget(edit.a, insertEditIndex);
404
+ if (!target) {
405
+ continue;
406
+ }
407
+ context.report({
408
+ loc: edit.a.reportLoc,
409
+ messageId: "shouldBeAfter",
410
+ data: {
411
+ thisName: edit.a.name,
412
+ targetName: target.name,
413
+ orderText: option.orderText,
414
+ },
415
+ *fix(fixer) {
416
+ if (edit.a.mapping.node.style === "flow") {
417
+ yield* fixToMoveDownForFlow(fixer, edit.a, target);
378
418
  }
379
419
  else {
380
- moveTarget = prev;
420
+ yield* fixToMoveDownForBlock(fixer, edit.a, target);
381
421
  }
422
+ },
423
+ });
424
+ }
425
+ else {
426
+ const target = findInsertBeforeTarget(edit.a, insertEditIndex);
427
+ if (!target) {
428
+ continue;
429
+ }
430
+ context.report({
431
+ loc: edit.a.reportLoc,
432
+ messageId: "shouldBeBefore",
433
+ data: {
434
+ thisName: edit.a.name,
435
+ targetName: target.name,
436
+ orderText: option.orderText,
437
+ },
438
+ *fix(fixer) {
439
+ if (edit.a.mapping.node.style === "flow") {
440
+ yield* fixToMoveUpForFlow(fixer, edit.a, target);
441
+ }
442
+ else {
443
+ yield* fixToMoveUpForBlock(fixer, edit.a, target);
444
+ }
445
+ },
446
+ });
447
+ }
448
+ }
449
+ function findInsertAfterTarget(pair, insertEditIndex) {
450
+ let candidate = null;
451
+ for (let index = insertEditIndex - 1; index >= 0; index--) {
452
+ const edit = editScript[index];
453
+ if (edit.type === "delete" && edit.a === pair)
454
+ break;
455
+ if (edit.type !== "common")
456
+ continue;
457
+ candidate = edit.a;
458
+ break;
459
+ }
460
+ const pairIndex = pairs.indexOf(pair);
461
+ if (candidate) {
462
+ for (let index = pairIndex + 1; index < pairs.length; index++) {
463
+ const element = pairs[index];
464
+ if (element === candidate)
465
+ return candidate;
466
+ if (shouldKeepOrder(pair, element)) {
467
+ break;
382
468
  }
383
- if (data.mapping.node.style === "flow") {
384
- yield* fixForFlow(fixer, data, moveTarget);
385
- }
386
- else {
387
- yield* fixForBlock(fixer, data, moveTarget);
469
+ }
470
+ }
471
+ let lastTarget = null;
472
+ for (let index = pairIndex + 1; index < pairs.length; index++) {
473
+ const element = pairs[index];
474
+ if (option.isValidOrder(element, pair) &&
475
+ !shouldKeepOrder(pair, element)) {
476
+ lastTarget = element;
477
+ continue;
478
+ }
479
+ return lastTarget;
480
+ }
481
+ return lastTarget;
482
+ }
483
+ function findInsertBeforeTarget(pair, insertEditIndex) {
484
+ let candidate = null;
485
+ for (let index = insertEditIndex + 1; index < editScript.length; index++) {
486
+ const edit = editScript[index];
487
+ if (edit.type === "delete" && edit.a === pair)
488
+ break;
489
+ if (edit.type !== "common")
490
+ continue;
491
+ candidate = edit.a;
492
+ break;
493
+ }
494
+ const pairIndex = pairs.indexOf(pair);
495
+ if (candidate) {
496
+ for (let index = pairIndex - 1; index >= 0; index--) {
497
+ const element = pairs[index];
498
+ if (element === candidate)
499
+ return candidate;
500
+ if (shouldKeepOrder(element, pair)) {
501
+ break;
388
502
  }
389
- },
390
- });
503
+ }
504
+ }
505
+ let lastTarget = null;
506
+ for (let index = pairIndex - 1; index >= 0; index--) {
507
+ const element = pairs[index];
508
+ if (option.isValidOrder(pair, element) &&
509
+ !shouldKeepOrder(element, pair)) {
510
+ lastTarget = element;
511
+ continue;
512
+ }
513
+ return lastTarget;
514
+ }
515
+ return lastTarget;
391
516
  }
392
517
  }
393
518
  function hasBlankLine(prev, next) {
@@ -444,12 +569,44 @@ exports.default = (0, index_1.createRule)("sort-keys", {
444
569
  if (!option) {
445
570
  return;
446
571
  }
447
- for (const pair of data.pairs) {
448
- verifyPair(pair, option);
572
+ for (const pairs of groupingPairs(data.pairs, option)) {
573
+ verifyPairs(pairs, option);
449
574
  }
450
575
  },
451
576
  };
452
- function* fixForFlow(fixer, data, moveTarget) {
577
+ function* fixToMoveDownForFlow(fixer, data, moveTarget) {
578
+ const beforeToken = sourceCode.getTokenBefore(data.node);
579
+ let insertCode, removeRange, insertTargetToken;
580
+ const afterCommaToken = sourceCode.getTokenAfter(data.node);
581
+ if ((0, ast_utils_1.isComma)(afterCommaToken)) {
582
+ removeRange = [beforeToken.range[1], afterCommaToken.range[1]];
583
+ const moveTargetAfterToken = sourceCode.getTokenAfter(moveTarget.node);
584
+ if ((0, ast_utils_1.isComma)(moveTargetAfterToken)) {
585
+ insertTargetToken = moveTargetAfterToken;
586
+ insertCode = sourceCode.text.slice(...removeRange);
587
+ }
588
+ else {
589
+ insertTargetToken = sourceCode.getLastToken(moveTarget.node);
590
+ insertCode = sourceCode.text.slice(beforeToken.range[1], afterCommaToken.range[0]);
591
+ insertCode = `,${insertCode}`;
592
+ }
593
+ }
594
+ else {
595
+ if ((0, ast_utils_1.isComma)(beforeToken)) {
596
+ removeRange = [beforeToken.range[0], data.node.range[1]];
597
+ insertCode = sourceCode.text.slice(...removeRange);
598
+ insertTargetToken = sourceCode.getLastToken(moveTarget.node);
599
+ }
600
+ else {
601
+ removeRange = [beforeToken.range[1], data.node.range[1]];
602
+ insertCode = `,${sourceCode.text.slice(...removeRange)}`;
603
+ insertTargetToken = sourceCode.getLastToken(moveTarget.node);
604
+ }
605
+ }
606
+ yield fixer.removeRange(removeRange);
607
+ yield fixer.insertTextAfterRange(insertTargetToken.range, insertCode);
608
+ }
609
+ function* fixToMoveUpForFlow(fixer, data, moveTarget) {
453
610
  const beforeCommaToken = sourceCode.getTokenBefore(data.node);
454
611
  let insertCode, removeRange, insertTargetToken;
455
612
  const afterCommaToken = sourceCode.getTokenAfter(data.node);
@@ -473,7 +630,39 @@ exports.default = (0, index_1.createRule)("sort-keys", {
473
630
  yield fixer.insertTextAfterRange(insertTargetToken.range, insertCode);
474
631
  yield fixer.removeRange(removeRange);
475
632
  }
476
- function* fixForBlock(fixer, data, moveTarget) {
633
+ function* fixToMoveDownForBlock(fixer, data, moveTarget) {
634
+ const nodeLocs = getPairRangeForBlock(data.node);
635
+ const moveTargetLocs = getPairRangeForBlock(moveTarget.node);
636
+ if (nodeLocs.loc.start.column === 0) {
637
+ const removeRange = [
638
+ getNewlineStartIndex(nodeLocs.range[0]),
639
+ nodeLocs.range[1],
640
+ ];
641
+ const moveTargetRange = [
642
+ getNewlineStartIndex(moveTargetLocs.range[0]),
643
+ moveTargetLocs.range[1],
644
+ ];
645
+ const insertCode = sourceCode.text.slice(...removeRange);
646
+ yield fixer.removeRange(removeRange);
647
+ yield fixer.insertTextAfterRange(moveTargetRange, insertCode);
648
+ }
649
+ else {
650
+ const nextToken = sourceCode.getTokenAfter(data.node, {
651
+ includeComments: true,
652
+ });
653
+ const removeRange = [data.node.range[0], nextToken.range[0]];
654
+ yield fixer.removeRange(removeRange);
655
+ const indentCode = sourceCode.text
656
+ .slice(sourceCode.getIndexFromLoc({
657
+ line: nodeLocs.loc.start.line,
658
+ column: 0,
659
+ }), data.node.range[0])
660
+ .replace(/\S/g, " ");
661
+ const insertCode = `\n${indentCode}${sourceCode.text.slice(data.node.range[0], nodeLocs.range[1])}`;
662
+ yield fixer.insertTextAfterRange(moveTargetLocs.range, insertCode);
663
+ }
664
+ }
665
+ function* fixToMoveUpForBlock(fixer, data, moveTarget) {
477
666
  const nodeLocs = getPairRangeForBlock(data.node);
478
667
  const moveTargetLocs = getPairRangeForBlock(moveTarget.node);
479
668
  if (moveTargetLocs.loc.start.column === 0) {
@@ -514,7 +703,7 @@ exports.default = (0, index_1.createRule)("sort-keys", {
514
703
  return 0;
515
704
  }
516
705
  function getPairRangeForBlock(node) {
517
- let endOfRange, end;
706
+ let end;
518
707
  const afterToken = sourceCode.getTokenAfter(node, {
519
708
  includeComments: true,
520
709
  filter: (t) => !(0, ast_utils_1.isCommentToken)(t) || node.loc.end.line < t.loc.start.line,
@@ -525,14 +714,17 @@ exports.default = (0, index_1.createRule)("sort-keys", {
525
714
  : node.loc.end.line;
526
715
  const lineText = sourceCode.lines[line - 1];
527
716
  end = {
528
- line,
529
- column: lineText.length,
717
+ loc: { line, column: lineText.length },
718
+ get index() {
719
+ return sourceCode.getIndexFromLoc(this.loc);
720
+ },
530
721
  };
531
- endOfRange = sourceCode.getIndexFromLoc(end);
532
722
  }
533
723
  else {
534
- endOfRange = node.range[1];
535
- end = node.loc.end;
724
+ end = {
725
+ index: node.range[1],
726
+ loc: node.loc.end,
727
+ };
536
728
  }
537
729
  const beforeToken = sourceCode.getTokenBefore(node);
538
730
  if (beforeToken) {
@@ -547,18 +739,15 @@ exports.default = (0, index_1.createRule)("sort-keys", {
547
739
  : node.loc.start.line,
548
740
  column: 0,
549
741
  };
550
- const startOfRange = sourceCode.getIndexFromLoc(start);
551
742
  return {
552
- range: [startOfRange, endOfRange],
553
- loc: { start, end },
743
+ range: [sourceCode.getIndexFromLoc(start), end.index],
744
+ loc: { start, end: end.loc },
554
745
  indentColumn: next.loc.start.column,
555
746
  };
556
747
  }
557
- const start = beforeToken.loc.end;
558
- const startOfRange = beforeToken.range[1];
559
748
  return {
560
- range: [startOfRange, endOfRange],
561
- loc: { start, end },
749
+ range: [beforeToken.range[1], end.index],
750
+ loc: { start: beforeToken.loc.end, end: end.loc },
562
751
  indentColumn: node.range[0] - beforeToken.range[1],
563
752
  };
564
753
  }
@@ -575,8 +764,8 @@ exports.default = (0, index_1.createRule)("sort-keys", {
575
764
  };
576
765
  const startOfRange = sourceCode.getIndexFromLoc(start);
577
766
  return {
578
- range: [startOfRange, endOfRange],
579
- loc: { start, end },
767
+ range: [startOfRange, end.index],
768
+ loc: { start, end: end.loc },
580
769
  indentColumn: next.loc.start.column,
581
770
  };
582
771
  }
@@ -588,8 +777,8 @@ exports.default = (0, index_1.createRule)("sort-keys", {
588
777
  };
589
778
  const startOfRange = sourceCode.getIndexFromLoc(start);
590
779
  return {
591
- range: [startOfRange, endOfRange],
592
- loc: { start, end },
780
+ range: [startOfRange, end.index],
781
+ loc: { start, end: end.loc },
593
782
  indentColumn: node.loc.start.column,
594
783
  };
595
784
  }
@@ -8,6 +8,7 @@ const index_1 = require("../utils/index");
8
8
  const ast_utils_1 = require("../utils/ast-utils");
9
9
  const yaml_eslint_parser_1 = require("yaml-eslint-parser");
10
10
  const compat_1 = require("../utils/compat");
11
+ const calc_shortest_edit_script_1 = require("../utils/calc-shortest-edit-script");
11
12
  class YAMLEntryData {
12
13
  get reportLoc() {
13
14
  if (this.node) {
@@ -298,7 +299,8 @@ exports.default = (0, index_1.createRule)("sort-sequence-values", {
298
299
  minItems: 1,
299
300
  },
300
301
  messages: {
301
- sortValues: "Expected sequence values to be in {{orderText}} order. '{{thisValue}}' should be before '{{prevValue}}'.",
302
+ shouldBeBefore: "Expected sequence values to be in {{orderText}} order. '{{thisValue}}' should be before '{{targetValue}}'.",
303
+ shouldBeAfter: "Expected sequence values to be in {{orderText}} order. '{{thisValue}}' should be after '{{targetValue}}'.",
302
304
  },
303
305
  type: "suggestion",
304
306
  },
@@ -309,62 +311,169 @@ exports.default = (0, index_1.createRule)("sort-sequence-values", {
309
311
  return {};
310
312
  }
311
313
  const parsedOptions = parseOptions(context.options, sourceCode);
312
- function isValidOrder(prevData, thisData, option) {
313
- if (option.isValidOrder(prevData, thisData)) {
314
- return true;
315
- }
316
- for (const aliasName of thisData.anchorAlias.aliases) {
314
+ function shouldKeepOrder(prevData, nextData) {
315
+ if ((prevData.anchorAlias.aliases.size === 0 &&
316
+ prevData.anchorAlias.anchors.size === 0) ||
317
+ (nextData.anchorAlias.aliases.size === 0 &&
318
+ nextData.anchorAlias.anchors.size === 0))
319
+ return false;
320
+ for (const aliasName of nextData.anchorAlias.aliases) {
317
321
  if (prevData.anchorAlias.anchors.has(aliasName)) {
318
322
  return true;
319
323
  }
320
324
  }
321
- for (const anchorName of thisData.anchorAlias.anchors) {
325
+ for (const anchorName of nextData.anchorAlias.anchors) {
322
326
  if (prevData.anchorAlias.aliases.has(anchorName)) {
323
327
  return true;
324
328
  }
325
329
  }
326
330
  return false;
327
331
  }
328
- function verifyArrayElement(data, option) {
329
- if (option.ignore(data)) {
330
- return;
331
- }
332
- const prevList = data.sequence.entries
333
- .slice(0, data.index)
334
- .reverse()
335
- .filter((d) => !option.ignore(d));
336
- if (prevList.length === 0) {
337
- return;
338
- }
339
- const prev = prevList[0];
340
- if (!isValidOrder(prev, data, option)) {
341
- const reportLoc = data.reportLoc;
342
- context.report({
343
- loc: reportLoc,
344
- messageId: "sortValues",
345
- data: {
346
- thisValue: toText(data),
347
- prevValue: toText(prev),
348
- orderText: option.orderText(data),
349
- },
350
- *fix(fixer) {
351
- let moveTarget = prevList[0];
352
- for (const prev of prevList) {
353
- if (isValidOrder(prev, data, option)) {
354
- break;
332
+ function bubbleSort(entries, option) {
333
+ const l = entries.length;
334
+ const result = [...entries];
335
+ let swapped;
336
+ do {
337
+ swapped = false;
338
+ for (let nextIndex = 1; nextIndex < l; nextIndex++) {
339
+ const prevIndex = nextIndex - 1;
340
+ if (option.isValidOrder(result[prevIndex], result[nextIndex]) ||
341
+ shouldKeepOrder(result[prevIndex], result[nextIndex]))
342
+ continue;
343
+ [result[prevIndex], result[nextIndex]] = [
344
+ result[nextIndex],
345
+ result[prevIndex],
346
+ ];
347
+ swapped = true;
348
+ }
349
+ } while (swapped);
350
+ return result;
351
+ }
352
+ function verifyArrayElements(entries, option) {
353
+ const sorted = bubbleSort(entries, option);
354
+ const editScript = (0, calc_shortest_edit_script_1.calcShortestEditScript)(entries, sorted);
355
+ for (let index = 0; index < editScript.length; index++) {
356
+ const edit = editScript[index];
357
+ if (edit.type !== "delete")
358
+ continue;
359
+ const insertEditIndex = editScript.findIndex((e) => e.type === "insert" && e.b === edit.a);
360
+ if (insertEditIndex === -1) {
361
+ continue;
362
+ }
363
+ if (index < insertEditIndex) {
364
+ const target = findInsertAfterTarget(edit.a, insertEditIndex);
365
+ if (!target) {
366
+ continue;
367
+ }
368
+ context.report({
369
+ loc: edit.a.reportLoc,
370
+ messageId: "shouldBeAfter",
371
+ data: {
372
+ thisValue: toText(edit.a),
373
+ targetValue: toText(target),
374
+ orderText: option.orderText(edit.a),
375
+ },
376
+ *fix(fixer) {
377
+ if (edit.a.sequence.node.style === "flow") {
378
+ yield* fixToMoveDownForFlow(fixer, edit.a, target);
355
379
  }
356
380
  else {
357
- moveTarget = prev;
381
+ yield* fixToMoveDownForBlock(fixer, edit.a, target);
358
382
  }
383
+ },
384
+ });
385
+ }
386
+ else {
387
+ const target = findInsertBeforeTarget(edit.a, insertEditIndex);
388
+ if (!target) {
389
+ continue;
390
+ }
391
+ context.report({
392
+ loc: edit.a.reportLoc,
393
+ messageId: "shouldBeBefore",
394
+ data: {
395
+ thisValue: toText(edit.a),
396
+ targetValue: toText(target),
397
+ orderText: option.orderText(edit.a),
398
+ },
399
+ *fix(fixer) {
400
+ if (edit.a.sequence.node.style === "flow") {
401
+ yield* fixToMoveUpForFlow(fixer, edit.a, target);
402
+ }
403
+ else {
404
+ yield* fixToMoveUpForBlock(fixer, edit.a, target);
405
+ }
406
+ },
407
+ });
408
+ }
409
+ }
410
+ function findInsertAfterTarget(entry, insertEditIndex) {
411
+ let candidate = null;
412
+ for (let index = insertEditIndex - 1; index >= 0; index--) {
413
+ const edit = editScript[index];
414
+ if (edit.type === "delete" && edit.a === entry)
415
+ break;
416
+ if (edit.type !== "common")
417
+ continue;
418
+ candidate = edit.a;
419
+ break;
420
+ }
421
+ const entryIndex = entries.indexOf(entry);
422
+ if (candidate) {
423
+ for (let index = entryIndex + 1; index < entries.length; index++) {
424
+ const element = entries[index];
425
+ if (element === candidate)
426
+ return candidate;
427
+ if (shouldKeepOrder(entry, element)) {
428
+ break;
359
429
  }
360
- if (data.sequence.node.style === "flow") {
361
- yield* fixForFlow(fixer, data, moveTarget);
362
- }
363
- else {
364
- yield* fixForBlock(fixer, data, moveTarget);
430
+ }
431
+ }
432
+ let lastTarget = null;
433
+ for (let index = entryIndex + 1; index < entries.length; index++) {
434
+ const element = entries[index];
435
+ if (option.isValidOrder(element, entry) &&
436
+ !shouldKeepOrder(entry, element)) {
437
+ lastTarget = element;
438
+ continue;
439
+ }
440
+ return lastTarget;
441
+ }
442
+ return lastTarget;
443
+ }
444
+ function findInsertBeforeTarget(entry, insertEditIndex) {
445
+ let candidate = null;
446
+ for (let index = insertEditIndex + 1; index < editScript.length; index++) {
447
+ const edit = editScript[index];
448
+ if (edit.type === "delete" && edit.a === entry)
449
+ break;
450
+ if (edit.type !== "common")
451
+ continue;
452
+ candidate = edit.a;
453
+ break;
454
+ }
455
+ const entryIndex = entries.indexOf(entry);
456
+ if (candidate) {
457
+ for (let index = entryIndex - 1; index >= 0; index--) {
458
+ const element = entries[index];
459
+ if (element === candidate)
460
+ return candidate;
461
+ if (shouldKeepOrder(element, entry)) {
462
+ break;
365
463
  }
366
- },
367
- });
464
+ }
465
+ }
466
+ let lastTarget = null;
467
+ for (let index = entryIndex - 1; index >= 0; index--) {
468
+ const element = entries[index];
469
+ if (option.isValidOrder(entry, element) &&
470
+ !shouldKeepOrder(element, entry)) {
471
+ lastTarget = element;
472
+ continue;
473
+ }
474
+ return lastTarget;
475
+ }
476
+ return lastTarget;
368
477
  }
369
478
  }
370
479
  function toText(data) {
@@ -419,12 +528,48 @@ exports.default = (0, index_1.createRule)("sort-sequence-values", {
419
528
  if (!option) {
420
529
  return;
421
530
  }
422
- for (const element of data.entries) {
423
- verifyArrayElement(element, option);
424
- }
531
+ verifyArrayElements(data.entries.filter((d) => !option.ignore(d)), option);
425
532
  },
426
533
  };
427
- function* fixForFlow(fixer, data, moveTarget) {
534
+ function* fixToMoveDownForFlow(fixer, data, moveTarget) {
535
+ const beforeToken = data.aroundTokens.before;
536
+ const afterToken = data.aroundTokens.after;
537
+ let insertCode, removeRange, insertTargetToken;
538
+ if ((0, ast_utils_1.isComma)(afterToken)) {
539
+ removeRange = [beforeToken.range[1], afterToken.range[1]];
540
+ const moveTargetAfterToken = moveTarget.aroundTokens.after;
541
+ if ((0, ast_utils_1.isComma)(moveTargetAfterToken)) {
542
+ insertTargetToken = moveTargetAfterToken;
543
+ insertCode = sourceCode.text.slice(...removeRange);
544
+ }
545
+ else {
546
+ insertTargetToken = moveTarget.node
547
+ ? sourceCode.getLastToken(moveTarget.node)
548
+ : moveTarget.aroundTokens.before;
549
+ insertCode = sourceCode.text.slice(beforeToken.range[1], afterToken.range[0]);
550
+ insertCode = `,${insertCode}`;
551
+ }
552
+ }
553
+ else {
554
+ if ((0, ast_utils_1.isComma)(beforeToken)) {
555
+ removeRange = [beforeToken.range[0], data.range[1]];
556
+ insertCode = sourceCode.text.slice(...removeRange);
557
+ insertTargetToken = moveTarget.node
558
+ ? sourceCode.getLastToken(moveTarget.node)
559
+ : moveTarget.aroundTokens.before;
560
+ }
561
+ else {
562
+ removeRange = [beforeToken.range[1], data.range[1]];
563
+ insertCode = `,${sourceCode.text.slice(...removeRange)}`;
564
+ insertTargetToken = moveTarget.node
565
+ ? sourceCode.getLastToken(moveTarget.node)
566
+ : moveTarget.aroundTokens.before;
567
+ }
568
+ }
569
+ yield fixer.removeRange(removeRange);
570
+ yield fixer.insertTextAfterRange(insertTargetToken.range, insertCode);
571
+ }
572
+ function* fixToMoveUpForFlow(fixer, data, moveTarget) {
428
573
  const beforeToken = data.aroundTokens.before;
429
574
  const afterToken = data.aroundTokens.after;
430
575
  let insertCode, removeRange, insertTargetToken;
@@ -447,7 +592,16 @@ exports.default = (0, index_1.createRule)("sort-sequence-values", {
447
592
  yield fixer.insertTextAfterRange(insertTargetToken.range, insertCode);
448
593
  yield fixer.removeRange(removeRange);
449
594
  }
450
- function* fixForBlock(fixer, data, moveTarget) {
595
+ function* fixToMoveDownForBlock(fixer, data, moveTarget) {
596
+ const moveDataList = data.sequence.entries.slice(data.index, moveTarget.index + 1);
597
+ let replacementCodeRange = getBlockEntryRange(data);
598
+ for (const target of moveDataList.reverse()) {
599
+ const range = getBlockEntryRange(target);
600
+ yield fixer.replaceTextRange(range, sourceCode.text.slice(...replacementCodeRange));
601
+ replacementCodeRange = range;
602
+ }
603
+ }
604
+ function* fixToMoveUpForBlock(fixer, data, moveTarget) {
451
605
  const moveDataList = data.sequence.entries.slice(moveTarget.index, data.index + 1);
452
606
  let replacementCodeRange = getBlockEntryRange(data);
453
607
  for (const target of moveDataList) {
@@ -4,7 +4,10 @@ export declare function isTokenOnSameLine(left: YAMLToken, right: YAMLToken): bo
4
4
  export declare function isQuestion(token: YAMLToken | null): token is YAMLToken;
5
5
  export declare function isHyphen(token: YAMLToken | null): token is YAMLToken;
6
6
  export declare function isColon(token: YAMLToken | null): token is YAMLToken;
7
- export declare function isComma(token: YAMLToken | null): token is YAMLToken;
7
+ export declare function isComma(token: YAMLToken | null): token is YAMLToken & {
8
+ type: "Punctuator";
9
+ value: ",";
10
+ };
8
11
  export declare function isOpeningBracketToken(token: YAMLToken | null): token is YAMLToken;
9
12
  export declare function isClosingBracketToken(token: YAMLToken | null): token is YAMLToken;
10
13
  export declare function isOpeningBraceToken(token: YAMLToken | null): token is YAMLToken;
@@ -0,0 +1,15 @@
1
+ export type DeleteEntry<E> = {
2
+ type: "delete";
3
+ a: E;
4
+ };
5
+ export type InsertEntry<E> = {
6
+ type: "insert";
7
+ b: E;
8
+ };
9
+ export type CommonEntry<E> = {
10
+ type: "common";
11
+ a: E;
12
+ b: E;
13
+ };
14
+ export type DiffEntry<E> = DeleteEntry<E> | InsertEntry<E> | CommonEntry<E>;
15
+ export declare function calcShortestEditScript<E>(a: E[], b: E[]): DiffEntry<E>[];
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.calcShortestEditScript = calcShortestEditScript;
7
+ const diff_sequences_1 = __importDefault(require("diff-sequences"));
8
+ function calcShortestEditScript(a, b) {
9
+ let aIndex = 0;
10
+ let bIndex = 0;
11
+ const result = [];
12
+ (0, diff_sequences_1.default)(a.length, b.length, (aIndex, bIndex) => a[aIndex] === b[bIndex], (nCommon, aCommon, bCommon) => {
13
+ pushDelIns(aIndex, aCommon, bIndex, bCommon);
14
+ aIndex = aCommon + nCommon;
15
+ bIndex = bCommon + nCommon;
16
+ if (nCommon > 0) {
17
+ for (let index = 0; index < nCommon; index++) {
18
+ const elementA = a[aCommon + index];
19
+ const elementB = b[bCommon + index];
20
+ result.push({
21
+ type: "common",
22
+ a: elementA,
23
+ b: elementB,
24
+ });
25
+ }
26
+ }
27
+ });
28
+ pushDelIns(aIndex, a.length, bIndex, b.length);
29
+ return result;
30
+ function pushDelIns(aStart, aEnd, bStart, bEnd) {
31
+ for (const element of a.slice(aStart, aEnd)) {
32
+ result.push({
33
+ type: "delete",
34
+ a: element,
35
+ });
36
+ }
37
+ for (const element of b.slice(bStart, bEnd)) {
38
+ result.push({
39
+ type: "insert",
40
+ b: element,
41
+ });
42
+ }
43
+ }
44
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-yml",
3
- "version": "1.17.0",
3
+ "version": "1.19.0",
4
4
  "description": "This ESLint plugin provides linting rules for YAML.",
5
5
  "main": "lib/index.js",
6
6
  "files": [
@@ -29,8 +29,8 @@
29
29
  "docs:watch": "vitepress dev docs",
30
30
  "docs:build": "npm run build:ts && vitepress build docs",
31
31
  "preversion": "npm test && git add .",
32
- "version": "env-cmd -e version npm run update && git add .",
33
- "version:ci": "env-cmd -e version-ci npm run update && changeset version",
32
+ "version": "env-cmd -e version -- npm run update && git add .",
33
+ "version:ci": "env-cmd -e version-ci -- npm run update && changeset version",
34
34
  "prerelease": "npm run build",
35
35
  "release": "changeset publish",
36
36
  "update-fixtures": "npm run ts -- ./tools/update-fixtures.ts && npm run eslint-fix",
@@ -58,6 +58,7 @@
58
58
  "homepage": "https://ota-meshi.github.io/eslint-plugin-yml/",
59
59
  "dependencies": {
60
60
  "debug": "^4.3.2",
61
+ "diff-sequences": "^27.5.1",
61
62
  "escape-string-regexp": "4.0.0",
62
63
  "eslint-compat-utils": "^0.6.0",
63
64
  "natural-compare": "^1.4.0",
@@ -72,59 +73,59 @@
72
73
  "@eslint-community/eslint-plugin-eslint-comments": "^4.3.0",
73
74
  "@eslint/eslintrc": "^3.1.0",
74
75
  "@eslint/js": "^9.5.0",
75
- "@eslint/json": "^0.10.0",
76
- "@ota-meshi/eslint-plugin": "^0.17.3",
76
+ "@eslint/json": "^0.13.0",
77
+ "@ota-meshi/eslint-plugin": "^0.18.0",
77
78
  "@ota-meshi/site-kit-eslint-editor-vue": "^0.2.0",
78
79
  "@types/debug": "^4.1.5",
79
80
  "@types/eslint": "^9.0.0",
80
- "@types/eslint-scope": "^3.7.0",
81
+ "@types/eslint-scope": "^8.0.0",
81
82
  "@types/eslint-visitor-keys": "^3.0.0",
82
83
  "@types/estree": "^1.0.0",
83
84
  "@types/mocha": "^10.0.0",
84
85
  "@types/natural-compare": "^1.4.0",
85
86
  "@types/node": "^22.0.0",
86
87
  "@types/semver": "^7.3.1",
87
- "@typescript-eslint/eslint-plugin": "~8.24.0",
88
- "@typescript-eslint/parser": "~8.24.0",
89
- "cross-env": "^7.0.2",
90
- "env-cmd": "^10.1.0",
88
+ "@typescript-eslint/eslint-plugin": "~8.45.0",
89
+ "@typescript-eslint/parser": "~8.45.0",
90
+ "cross-env": "^10.0.0",
91
+ "env-cmd": "^11.0.0",
91
92
  "esbuild": "^0.25.0",
92
93
  "esbuild-register": "^3.2.0",
93
94
  "eslint": "^9.16.0",
94
95
  "eslint-config-prettier": "^10.0.0",
95
- "eslint-plugin-eslint-plugin": "^6.0.0",
96
+ "eslint-plugin-eslint-plugin": "^7.0.0",
96
97
  "eslint-plugin-eslint-rule-tester": "^0.6.0",
97
- "eslint-plugin-jsdoc": "^50.0.0",
98
+ "eslint-plugin-jsdoc": "^60.0.0",
98
99
  "eslint-plugin-json-schema-validator": "^5.0.0",
99
100
  "eslint-plugin-jsonc": "^2.0.0",
100
101
  "eslint-plugin-markdown": "^5.0.0",
101
102
  "eslint-plugin-n": "^17.0.0",
102
- "eslint-plugin-node-dependencies": "^0.12.0",
103
+ "eslint-plugin-node-dependencies": "^1.0.0",
103
104
  "eslint-plugin-prettier": "^5.0.0",
104
105
  "eslint-plugin-regexp": "^2.0.0",
105
- "eslint-plugin-vue": "^9.0.0",
106
+ "eslint-plugin-vue": "^10.0.0",
106
107
  "eslint-plugin-yml": "^1.0.0",
107
108
  "events": "^3.3.0",
108
109
  "mocha": "^11.0.0",
109
- "monaco-editor": "^0.52.0",
110
+ "monaco-editor": "^0.53.0",
110
111
  "nyc": "^17.0.0",
111
112
  "pako": "^2.1.0",
112
113
  "prettier": "^3.0.3",
113
114
  "semver": "^7.3.2",
114
115
  "stylelint": "^16.0.0",
115
116
  "stylelint-config-recommended-vue": "^1.0.0",
116
- "stylelint-config-standard": "^37.0.0",
117
+ "stylelint-config-standard": "^39.0.0",
117
118
  "stylelint-config-standard-vue": "^1.0.0",
118
119
  "stylelint-stylus": "^1.0.0",
119
- "typescript": "~5.7.0",
120
+ "typescript": "~5.9.0",
120
121
  "typescript-eslint": "^8.0.0",
121
- "vite-plugin-eslint4b": "^0.5.0",
122
+ "vite-plugin-eslint4b": "^0.6.0",
122
123
  "vitepress": "^1.0.0-rc.17",
123
- "vue-eslint-parser": "^9.0.0",
124
+ "vue-eslint-parser": "^10.0.0",
124
125
  "yaml": "^2.1.1"
125
126
  },
126
127
  "publishConfig": {
127
128
  "access": "public"
128
129
  },
129
- "packageManager": "npm@11.1.0"
130
+ "packageManager": "npm@11.6.1"
130
131
  }