@effected/yaml 0.7.0 → 0.8.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.
Files changed (39) hide show
  1. package/README.md +78 -7
  2. package/Yaml.js +80 -32
  3. package/YamlDocument.js +22 -3
  4. package/YamlFormat.js +170 -19
  5. package/YamlLint.js +239 -0
  6. package/YamlLintRule.js +36 -0
  7. package/YamlNode.js +36 -2
  8. package/YamlToken.js +139 -0
  9. package/YamlVisitor.js +40 -5
  10. package/index.d.ts +439 -12
  11. package/index.js +4 -1
  12. package/internal/composer/anchors.js +2 -27
  13. package/internal/composer/block.js +246 -58
  14. package/internal/composer/comments.js +187 -0
  15. package/internal/composer/document.js +49 -12
  16. package/internal/composer/flow.js +82 -9
  17. package/internal/composer/scalars.js +48 -11
  18. package/internal/composer/state.js +16 -2
  19. package/internal/cst-parser.js +2 -1
  20. package/internal/diagnostics.js +12 -3
  21. package/internal/fold.js +22 -10
  22. package/internal/rules/catalog.js +39 -0
  23. package/internal/rules/colon-spacing.js +77 -0
  24. package/internal/rules/comments-spacing.js +68 -0
  25. package/internal/rules/document-end.js +65 -0
  26. package/internal/rules/document-start.js +64 -0
  27. package/internal/rules/empty-lines.js +70 -0
  28. package/internal/rules/eof-newline.js +33 -0
  29. package/internal/rules/hyphen-spacing.js +52 -0
  30. package/internal/rules/indentation.js +103 -0
  31. package/internal/rules/key-duplicates.js +49 -0
  32. package/internal/rules/line-length.js +36 -0
  33. package/internal/rules/parse-validity.js +33 -0
  34. package/internal/rules/quoted-strings.js +81 -0
  35. package/internal/rules/trailing-spaces.js +39 -0
  36. package/internal/rules/truthy.js +84 -0
  37. package/internal/rules/util.js +92 -0
  38. package/internal/stringifier.js +266 -54
  39. package/package.json +3 -2
package/index.js CHANGED
@@ -4,6 +4,9 @@ import { Yaml, YamlParseError, YamlParseOptions, YamlStringifyError, YamlStringi
4
4
  import { YamlDirective, YamlDocument } from "./YamlDocument.js";
5
5
  import { YamlEdit, YamlRange } from "./YamlEdit.js";
6
6
  import { YamlFormat, YamlFormattingOptions, YamlModificationError } from "./YamlFormat.js";
7
+ import { YamlLintDiagnostic, YamlLintSeverity } from "./YamlLintRule.js";
8
+ import { YamlToken, YamlTokenKind, YamlTokens } from "./YamlToken.js";
9
+ import { YamlLint, YamlLintConfig, YamlLintRuleSetting } from "./YamlLint.js";
7
10
  import { YamlVisitor, YamlVisitorEvent } from "./YamlVisitor.js";
8
11
 
9
- export { CollectionStyle, QuoteStyle, ScalarChomp, ScalarStyle, Yaml, YamlAlias, YamlComposerErrorCode, YamlDiagnostic, YamlDirective, YamlDocument, YamlEdit, YamlErrorCode, YamlFormat, YamlFormattingOptions, YamlLexErrorCode, YamlMap, YamlModificationError, YamlModifyErrorCode, YamlNode, YamlPair, YamlParseError, YamlParseErrorCode, YamlParseOptions, YamlRange, YamlScalar, YamlSeq, YamlStringifyError, YamlStringifyErrorCode, YamlStringifyOptions, YamlVisitor, YamlVisitorEvent };
12
+ export { CollectionStyle, QuoteStyle, ScalarChomp, ScalarStyle, Yaml, YamlAlias, YamlComposerErrorCode, YamlDiagnostic, YamlDirective, YamlDocument, YamlEdit, YamlErrorCode, YamlFormat, YamlFormattingOptions, YamlLexErrorCode, YamlLint, YamlLintConfig, YamlLintDiagnostic, YamlLintRuleSetting, YamlLintSeverity, YamlMap, YamlModificationError, YamlModifyErrorCode, YamlNode, YamlPair, YamlParseError, YamlParseErrorCode, YamlParseOptions, YamlRange, YamlScalar, YamlSeq, YamlStringifyError, YamlStringifyErrorCode, YamlStringifyOptions, YamlToken, YamlTokenKind, YamlTokens, YamlVisitor, YamlVisitorEvent };
@@ -1,4 +1,4 @@
1
- import { YamlAlias, YamlMap, YamlScalar, YamlSeq } from "../../YamlNode.js";
1
+ import { YamlAlias } from "../../YamlNode.js";
2
2
 
3
3
  //#region src/internal/composer/anchors.ts
4
4
  /**
@@ -67,31 +67,6 @@ function scanName(text, start) {
67
67
  }
68
68
  return text.slice(start, end);
69
69
  }
70
- /**
71
- * Build an anchor map by walking the AST, collecting nodes that have anchors.
72
- * Used to resolve aliases when extracting plain JavaScript values from
73
- * parsed YAML documents.
74
- */
75
- function buildAnchorMap(node) {
76
- const anchors = /* @__PURE__ */ new Map();
77
- collectAnchors(node, anchors);
78
- return anchors;
79
- }
80
- function collectAnchors(node, anchors) {
81
- if (node === null) return;
82
- if (node instanceof YamlScalar) {
83
- if (node.anchor !== void 0) anchors.set(node.anchor, node);
84
- } else if (node instanceof YamlMap) {
85
- if (node.anchor !== void 0) anchors.set(node.anchor, node);
86
- for (const pair of node.items) {
87
- collectAnchors(pair.key, anchors);
88
- collectAnchors(pair.value, anchors);
89
- }
90
- } else if (node instanceof YamlSeq) {
91
- if (node.anchor !== void 0) anchors.set(node.anchor, node);
92
- for (const item of node.items) collectAnchors(item, anchors);
93
- }
94
- }
95
70
 
96
71
  //#endregion
97
- export { buildAnchorMap, checkAnchorOnAlias, getAliasName, getAnchorName, makeAlias, registerAnchor, scanName };
72
+ export { checkAnchorOnAlias, getAliasName, getAnchorName, makeAlias, registerAnchor, scanName };
@@ -1,5 +1,6 @@
1
1
  import { YamlMap, YamlPair, YamlScalar, YamlSeq } from "../../YamlNode.js";
2
2
  import { checkAnchorOnAlias, getAnchorName, makeAlias, registerAnchor } from "./anchors.js";
3
+ import { blankAboveIsKeepChompContent, columnAt, hasBlankLineAbove, isOwnLineAt, joinComments, rawCommentText, sameLineSpan, withCommentFields } from "./comments.js";
3
4
  import { enterNesting, exitNesting, hasMeta, hasNonWhitespaceBeforeOnLine, lineCol, lineIndentColumn, sameLine } from "./state.js";
4
5
  import { blockMapStartsWithValueSep, classifyPlainNumeric, collectMultilineKey, collectMultilinePlainScalar, findFirstContent, findLastContent, findNextContentInList, findNextSignificantChild, findValueSepOffset, getScalarStyle, hasValueSepAfterInList, hasValueSepBetween, hasValueSepThroughPlainScalars, makeScalar, resolveScalar, shouldPreserveRaw } from "./scalars.js";
5
6
 
@@ -35,11 +36,12 @@ function composeBlockMapInner(blockMapCst, state, externalFirstKey, meta) {
35
36
  kind: "key",
36
37
  node: externalFirstKey
37
38
  });
38
- buildPairs(items, pairs, state.text);
39
+ const trailingComment = buildPairs(items, pairs, state.text, state.escapedComments);
39
40
  if (state.options.uniqueKeys) checkDuplicateKeys(pairs, state);
40
41
  checkMultilineImplicitKeys(pairs, state);
41
42
  const offset = externalFirstKey ? "offset" in externalFirstKey ? externalFirstKey.offset : blockMapCst.offset : blockMapCst.offset;
42
43
  const length = blockMapCst.offset + blockMapCst.length - offset;
44
+ const mapComment = meta?.comment !== void 0 ? trailingComment !== void 0 ? `${meta.comment}\n${trailingComment}` : meta.comment : trailingComment;
43
45
  const map = new YamlMap({
44
46
  items: pairs,
45
47
  style: "block",
@@ -47,7 +49,7 @@ function composeBlockMapInner(blockMapCst, state, externalFirstKey, meta) {
47
49
  length,
48
50
  ...meta?.tag !== void 0 ? { tag: meta.tag } : {},
49
51
  ...meta?.anchor !== void 0 ? { anchor: meta.anchor } : {},
50
- ...meta?.comment !== void 0 ? { comment: meta.comment } : {}
52
+ ...mapComment !== void 0 ? { comment: mapComment } : {}
51
53
  });
52
54
  if (meta?.anchor) registerAnchor(map, meta.anchor, state, offset);
53
55
  return map;
@@ -104,6 +106,14 @@ function flattenBlockMapChildren(children, state, externalKeyColumn, externalKey
104
106
  kind: "node",
105
107
  node
106
108
  });
109
+ if (state.escapedComments.length > 0) {
110
+ for (const ec of state.escapedComments) items.push({
111
+ kind: "comment",
112
+ comment: ec.text,
113
+ offset: ec.offset
114
+ });
115
+ state.escapedComments.length = 0;
116
+ }
107
117
  afterValueSep = false;
108
118
  }
109
119
  for (let i = 0; i < children.length; i++) {
@@ -132,7 +142,7 @@ function flattenBlockMapChildren(children, state, externalKeyColumn, externalKey
132
142
  const sliceChildren = children.slice(i + 1, lookahead.endIdx);
133
143
  const innerItems = flattenBlockMapChildren(sliceChildren, state);
134
144
  const innerPairs = [];
135
- buildPairs(innerItems, innerPairs, state.text);
145
+ const innerTrailing = buildPairs(innerItems, innerPairs, state.text);
136
146
  const firstC = findFirstContent(sliceChildren);
137
147
  const lastC = findLastContent(sliceChildren);
138
148
  const innerOffset = firstC ? firstC.offset : child.offset;
@@ -141,7 +151,8 @@ function flattenBlockMapChildren(children, state, externalKeyColumn, externalKey
141
151
  items: innerPairs,
142
152
  style: "block",
143
153
  offset: innerOffset,
144
- length: innerEnd - innerOffset
154
+ length: innerEnd - innerOffset,
155
+ ...innerTrailing !== void 0 ? { comment: innerTrailing } : {}
145
156
  }), innerOffset);
146
157
  i = lookahead.endIdx - 1;
147
158
  continue;
@@ -187,10 +198,10 @@ function flattenBlockMapChildren(children, state, externalKeyColumn, externalKey
187
198
  continue;
188
199
  }
189
200
  if (child.type === "comment") {
190
- const text = child.source.startsWith("#") ? child.source.slice(1).trim() : child.source;
191
201
  items.push({
192
202
  kind: "comment",
193
- comment: text
203
+ comment: rawCommentText(child.source),
204
+ offset: child.offset
194
205
  });
195
206
  continue;
196
207
  }
@@ -479,9 +490,51 @@ function flattenBlockMapChildren(children, state, externalKeyColumn, externalKey
479
490
  * Pattern: node, value-sep, node produces a key:value pair.
480
491
  * Pattern: node, value-sep (no node) produces a key:null pair.
481
492
  * Pattern: value-sep, node produces a null:value pair.
493
+ *
494
+ * Comment attribution (#127): a comment on the same line as the end of the
495
+ * previous pair attaches to it as the trailing `comment`; an own-line
496
+ * comment attaches FORWARD to the next pair as `commentBefore` (consecutive
497
+ * lines join with `\n`); a blank line before a pair (or before its
498
+ * `commentBefore` block) sets `spaceBefore`. Own-line comments left over
499
+ * after the last pair are returned so the caller can attach them as the
500
+ * containing collection's trailing `comment`.
482
501
  */
483
- function buildPairs(items, pairs, text) {
502
+ function buildPairs(items, pairs, text, escaped) {
484
503
  let i = 0;
504
+ let pending = [];
505
+ let pendingSpace = false;
506
+ let lastEnd = -1;
507
+ let lastNode;
508
+ let contentColumn = -1;
509
+ const nodeEnd = (n) => n.offset + n.length;
510
+ const noteContentColumn = (offset) => {
511
+ if (contentColumn < 0 && offset >= 0) contentColumn = columnAt(text, offset);
512
+ };
513
+ /** Join pending parts: `\n` between lines, an extra `\n` per embedded blank. */
514
+ const joinPending = (parts) => {
515
+ if (parts.length === 0) return void 0;
516
+ let out = parts[0].text;
517
+ for (let k = 1; k < parts.length; k++) {
518
+ const part = parts[k];
519
+ out += part.blankAbove ? `\n\n${part.text}` : `\n${part.text}`;
520
+ }
521
+ return out;
522
+ };
523
+ /** The pending comment fields for the pair being constructed, then reset. */
524
+ const takePendingFields = (anchorOffset) => {
525
+ let commentBefore = joinPending(pending);
526
+ if (anchorOffset >= 0 && hasBlankLineAbove(text, anchorOffset)) {
527
+ if (commentBefore !== void 0) commentBefore = `${commentBefore}\n`;
528
+ else if (!pendingSpace && lastEnd >= 0 && !blankAboveIsKeepChompContent(text, anchorOffset, lastNode)) pendingSpace = true;
529
+ }
530
+ const fields = {
531
+ ...commentBefore !== void 0 ? { commentBefore } : {},
532
+ ...pendingSpace ? { spaceBefore: true } : {}
533
+ };
534
+ pending = [];
535
+ pendingSpace = false;
536
+ return fields;
537
+ };
485
538
  while (i < items.length) {
486
539
  const item = items[i];
487
540
  if (!item) {
@@ -489,12 +542,24 @@ function buildPairs(items, pairs, text) {
489
542
  continue;
490
543
  }
491
544
  if (item.kind === "comment") {
492
- if (pairs.length > 0) {
545
+ const cOff = item.offset ?? -1;
546
+ const cText = item.comment ?? "";
547
+ if (!(cOff < 0 ? true : isOwnLineAt(text, cOff)) && pairs.length > 0) {
493
548
  const last = pairs[pairs.length - 1];
494
549
  if (last) pairs[pairs.length - 1] = new YamlPair({
495
550
  key: last.key,
496
551
  value: last.value,
497
- ...item.comment !== void 0 ? { comment: item.comment } : {}
552
+ ...last.commentBefore !== void 0 ? { commentBefore: last.commentBefore } : {},
553
+ comment: last.comment !== void 0 ? `${last.comment}\n${cText}` : cText,
554
+ ...last.spaceBefore !== void 0 ? { spaceBefore: last.spaceBefore } : {}
555
+ });
556
+ } else {
557
+ const blankAbove = cOff >= 0 && hasBlankLineAbove(text, cOff) && !blankAboveIsKeepChompContent(text, cOff, lastNode);
558
+ if (blankAbove && pending.length === 0 && lastEnd >= 0) pendingSpace = true;
559
+ pending.push({
560
+ text: cText,
561
+ offset: cOff,
562
+ blankAbove: blankAbove && pending.length > 0
498
563
  });
499
564
  }
500
565
  i++;
@@ -503,30 +568,32 @@ function buildPairs(items, pairs, text) {
503
568
  if (item.kind === "value-sep") {
504
569
  const valueSepOffset = item.offset ?? 0;
505
570
  i++;
571
+ const pendingFields = takePendingFields(valueSepOffset);
572
+ noteContentColumn(valueSepOffset);
506
573
  const valueNode = consumeValueNodeForNullKey(items, i, text, valueSepOffset);
574
+ const nullKey = new YamlScalar({
575
+ value: null,
576
+ style: "plain",
577
+ offset: valueSepOffset,
578
+ length: 0
579
+ });
507
580
  if (valueNode) {
508
- const nullKey = new YamlScalar({
509
- value: null,
510
- style: "plain",
511
- offset: valueSepOffset,
512
- length: 0
513
- });
514
581
  pairs.push(new YamlPair({
515
582
  key: nullKey,
516
- value: valueNode.node ?? null
583
+ value: valueNode.node ?? null,
584
+ ...pendingFields
517
585
  }));
518
586
  i = valueNode.nextIdx;
587
+ lastEnd = valueNode.node ? nodeEnd(valueNode.node) : valueSepOffset + 1;
588
+ lastNode = valueNode.node ?? void 0;
519
589
  } else {
520
- const nullKey = new YamlScalar({
521
- value: null,
522
- style: "plain",
523
- offset: valueSepOffset,
524
- length: 0
525
- });
526
590
  pairs.push(new YamlPair({
527
591
  key: nullKey,
528
- value: null
592
+ value: null,
593
+ ...pendingFields
529
594
  }));
595
+ lastEnd = valueSepOffset + 1;
596
+ lastNode = void 0;
530
597
  }
531
598
  continue;
532
599
  }
@@ -540,62 +607,109 @@ function buildPairs(items, pairs, text) {
540
607
  i++;
541
608
  }
542
609
  }
543
- while (i < items.length && items[i]?.kind === "comment") i++;
610
+ if (keyNode) noteContentColumn(keyNode.offset);
611
+ const pendingFields = takePendingFields(keyNode ? keyNode.offset : -1);
612
+ let pairTrailing;
613
+ while (i < items.length && items[i]?.kind === "comment") {
614
+ const c = items[i]?.comment;
615
+ if (c !== void 0) pairTrailing = joinComments(pairTrailing, c);
616
+ i++;
617
+ }
618
+ const keyOrNull = () => keyNode ?? new YamlScalar({
619
+ value: null,
620
+ style: "plain",
621
+ offset: 0,
622
+ length: 0
623
+ });
544
624
  if (i < items.length && items[i]?.kind === "value-sep") {
625
+ const sepOffset = items[i]?.offset ?? (keyNode ? nodeEnd(keyNode) : 0);
545
626
  i++;
546
- const valueResult = consumeValueNode(items, i);
627
+ const valueResult = consumeValueNode(items, i, text, sepOffset);
547
628
  if (valueResult) {
629
+ for (const c of valueResult.sameLineComments) pairTrailing = joinComments(pairTrailing, c);
630
+ let valNode = valueResult.node;
631
+ if (valNode !== null && valueResult.leadingComments.length > 0) valNode = withCommentFields(valNode, { commentBefore: valueResult.leadingComments.join("\n") });
548
632
  pairs.push(new YamlPair({
549
- key: keyNode ?? new YamlScalar({
550
- value: null,
551
- style: "plain",
552
- offset: 0,
553
- length: 0
554
- }),
555
- value: valueResult.node ?? null
633
+ key: keyOrNull(),
634
+ value: valNode,
635
+ ...pendingFields,
636
+ ...pairTrailing !== void 0 ? { comment: pairTrailing } : {}
556
637
  }));
557
638
  i = valueResult.nextIdx;
558
- } else pairs.push(new YamlPair({
559
- key: keyNode ?? new YamlScalar({
639
+ lastEnd = valNode ? nodeEnd(valNode) : sepOffset + 1;
640
+ lastNode = valNode ?? void 0;
641
+ } else {
642
+ pairs.push(new YamlPair({
643
+ key: keyOrNull(),
560
644
  value: null,
561
- style: "plain",
562
- offset: 0,
563
- length: 0
564
- }),
565
- value: null
566
- }));
567
- } else pairs.push(new YamlPair({
568
- key: keyNode ?? new YamlScalar({
645
+ ...pendingFields,
646
+ ...pairTrailing !== void 0 ? { comment: pairTrailing } : {}
647
+ }));
648
+ lastEnd = sepOffset + 1;
649
+ lastNode = void 0;
650
+ }
651
+ } else {
652
+ pairs.push(new YamlPair({
653
+ key: keyOrNull(),
569
654
  value: null,
570
- style: "plain",
571
- offset: 0,
572
- length: 0
573
- }),
574
- value: null
575
- }));
655
+ ...pendingFields,
656
+ ...pairTrailing !== void 0 ? { comment: pairTrailing } : {}
657
+ }));
658
+ lastEnd = keyNode ? nodeEnd(keyNode) : lastEnd;
659
+ if (keyNode) lastNode = keyNode;
660
+ }
576
661
  continue;
577
662
  }
578
663
  i++;
579
664
  }
665
+ if (pending.length === 0) return void 0;
666
+ const withLeadingBlank = (parts) => {
667
+ const joined = joinPending(parts);
668
+ const first = parts[0];
669
+ return joined !== void 0 && first !== void 0 && lastEnd >= 0 && first.offset >= 0 && hasBlankLineAbove(text, first.offset) && !blankAboveIsKeepChompContent(text, first.offset, lastNode) ? `\n${joined}` : joined;
670
+ };
671
+ if (escaped !== void 0 && contentColumn > 0) {
672
+ const kept = [];
673
+ for (const part of pending) if (part.offset >= 0 && columnAt(text, part.offset) < contentColumn) escaped.push({
674
+ text: part.text,
675
+ offset: part.offset
676
+ });
677
+ else kept.push(part);
678
+ return withLeadingBlank(kept);
679
+ }
680
+ return withLeadingBlank(pending);
580
681
  }
581
- function consumeValueNode(items, startIdx) {
682
+ function consumeValueNode(items, startIdx, text, sepOffset) {
683
+ let peek = startIdx;
684
+ while (peek < items.length && items[peek]?.kind === "comment") peek++;
685
+ const candidate = peek < items.length ? items[peek] : void 0;
686
+ if (candidate?.kind === "node" && peek + 1 < items.length && items[peek + 1]?.kind === "value-sep" && candidate.node !== void 0 && text.slice(sepOffset, candidate.node.offset).includes("\n")) return null;
582
687
  let i = startIdx;
688
+ const sameLineComments = [];
689
+ const leadingComments = [];
583
690
  while (i < items.length) {
584
691
  const item = items[i];
585
692
  if (!item) break;
586
693
  if (item.kind === "comment") {
694
+ const cText = item.comment ?? "";
695
+ if (item.offset !== void 0 && sameLineSpan(text, sepOffset, item.offset)) sameLineComments.push(cText);
696
+ else leadingComments.push(cText);
587
697
  i++;
588
698
  continue;
589
699
  }
590
700
  if (item.kind === "node") return {
591
701
  node: item.node ?? null,
592
- nextIdx: i + 1
702
+ nextIdx: i + 1,
703
+ sameLineComments,
704
+ leadingComments
593
705
  };
594
706
  break;
595
707
  }
596
708
  return i > startIdx ? {
597
709
  node: null,
598
- nextIdx: i
710
+ nextIdx: i,
711
+ sameLineComments,
712
+ leadingComments
599
713
  } : null;
600
714
  }
601
715
  /**
@@ -950,11 +1064,59 @@ function composeBlockSeq(cst, state, meta) {
950
1064
  }
951
1065
  function composeBlockSeqInner(cst, state, meta) {
952
1066
  const children = cst.children ?? [];
953
- const items = [];
1067
+ const rawItems = [];
954
1068
  let pendingMeta = {};
955
1069
  let sawEntry = false;
956
1070
  const seqIndent = lineIndentColumn(state.text, cst.offset);
957
1071
  let sawNewlineSincePending = false;
1072
+ let pending = [];
1073
+ let pendingSpace = false;
1074
+ let lastItemEnd = -1;
1075
+ let lastItemNode;
1076
+ const joinPending = (parts) => {
1077
+ if (parts.length === 0) return void 0;
1078
+ let out = parts[0].text;
1079
+ for (let k = 1; k < parts.length; k++) {
1080
+ const part = parts[k];
1081
+ out += part.blankAbove ? `\n\n${part.text}` : `\n${part.text}`;
1082
+ }
1083
+ return out;
1084
+ };
1085
+ const acceptOwnLineComment = (cText, cOff) => {
1086
+ const blankAbove = cOff >= 0 && hasBlankLineAbove(state.text, cOff) && !blankAboveIsKeepChompContent(state.text, cOff, lastItemNode);
1087
+ if (blankAbove && pending.length === 0 && lastItemEnd >= 0) pendingSpace = true;
1088
+ pending.push({
1089
+ text: cText,
1090
+ offset: cOff,
1091
+ blankAbove: blankAbove && pending.length > 0
1092
+ });
1093
+ };
1094
+ const items = {
1095
+ get length() {
1096
+ return rawItems.length;
1097
+ },
1098
+ push(node) {
1099
+ let commentBefore = joinPending(pending);
1100
+ if (node.length > 0 && hasBlankLineAbove(state.text, node.offset)) {
1101
+ if (commentBefore !== void 0) commentBefore = `${commentBefore}\n`;
1102
+ else if (!pendingSpace && lastItemEnd >= 0 && !blankAboveIsKeepChompContent(state.text, node.offset, lastItemNode)) pendingSpace = true;
1103
+ }
1104
+ let decorated = node;
1105
+ if (commentBefore !== void 0 || pendingSpace) decorated = withCommentFields(node, {
1106
+ ...commentBefore !== void 0 ? { commentBefore } : {},
1107
+ ...pendingSpace ? { spaceBefore: true } : {}
1108
+ });
1109
+ pending = [];
1110
+ pendingSpace = false;
1111
+ rawItems.push(decorated);
1112
+ lastItemEnd = node.offset + node.length;
1113
+ lastItemNode = node;
1114
+ if (state.escapedComments.length > 0) {
1115
+ for (const ec of state.escapedComments) acceptOwnLineComment(ec.text, ec.offset);
1116
+ state.escapedComments.length = 0;
1117
+ }
1118
+ }
1119
+ };
958
1120
  for (let ci = 0; ci < children.length; ci++) {
959
1121
  const child = children[ci];
960
1122
  if (!child) continue;
@@ -962,7 +1124,14 @@ function composeBlockSeqInner(cst, state, meta) {
962
1124
  if (hasMeta(pendingMeta)) sawNewlineSincePending = true;
963
1125
  continue;
964
1126
  }
965
- if (child.type === "comment") continue;
1127
+ if (child.type === "comment") {
1128
+ const cText = rawCommentText(child.source);
1129
+ if (rawItems.length > 0 && !isOwnLineAt(state.text, child.offset)) {
1130
+ const prev = rawItems[rawItems.length - 1];
1131
+ if (prev) rawItems[rawItems.length - 1] = withCommentFields(prev, { comment: cText });
1132
+ } else acceptOwnLineComment(cText, child.offset);
1133
+ continue;
1134
+ }
966
1135
  if (child.type === "whitespace") {
967
1136
  if (child.source.trim() === "-") {
968
1137
  if (sawEntry) {
@@ -1121,14 +1290,32 @@ function composeBlockSeqInner(cst, state, meta) {
1121
1290
  if (pendingMeta.anchor) registerAnchor(scalar, pendingMeta.anchor, state, 0);
1122
1291
  items.push(scalar);
1123
1292
  }
1293
+ const withLeadingBlank = (parts) => {
1294
+ const joined = joinPending(parts);
1295
+ const first = parts[0];
1296
+ return joined !== void 0 && first !== void 0 && lastItemEnd >= 0 && first.offset >= 0 && hasBlankLineAbove(state.text, first.offset) && !blankAboveIsKeepChompContent(state.text, first.offset, lastItemNode) ? `\n${joined}` : joined;
1297
+ };
1298
+ let seqTrailing;
1299
+ if (pending.length > 0) {
1300
+ if (seqIndent > 0) {
1301
+ const kept = [];
1302
+ for (const part of pending) if (part.offset >= 0 && columnAt(state.text, part.offset) < seqIndent) state.escapedComments.push({
1303
+ text: part.text,
1304
+ offset: part.offset
1305
+ });
1306
+ else kept.push(part);
1307
+ seqTrailing = withLeadingBlank(kept);
1308
+ } else seqTrailing = withLeadingBlank(pending);
1309
+ }
1310
+ const seqComment = meta?.comment !== void 0 ? seqTrailing !== void 0 ? `${meta.comment}\n${seqTrailing}` : meta.comment : seqTrailing;
1124
1311
  const seq = new YamlSeq({
1125
- items,
1312
+ items: rawItems,
1126
1313
  style: "block",
1127
1314
  offset: cst.offset,
1128
1315
  length: cst.length,
1129
1316
  ...meta?.tag !== void 0 ? { tag: meta.tag } : {},
1130
1317
  ...meta?.anchor !== void 0 ? { anchor: meta.anchor } : {},
1131
- ...meta?.comment !== void 0 ? { comment: meta.comment } : {}
1318
+ ...seqComment !== void 0 ? { comment: seqComment } : {}
1132
1319
  });
1133
1320
  if (meta?.anchor) registerAnchor(seq, meta.anchor, state, cst.offset);
1134
1321
  return seq;
@@ -1144,11 +1331,12 @@ function composeFlatBlockMap(children, startIdx, parentCst, state, externalFirst
1144
1331
  node: externalFirstKey
1145
1332
  });
1146
1333
  const pairs = [];
1147
- buildPairs(items, pairs, state.text);
1334
+ const trailingComment = buildPairs(items, pairs, state.text, state.escapedComments);
1148
1335
  if (state.options.uniqueKeys) checkDuplicateKeys(pairs, state);
1149
1336
  checkMultilineImplicitKeys(pairs, state);
1150
1337
  const offset = "offset" in externalFirstKey ? externalFirstKey.offset : parentCst.offset;
1151
1338
  const end = parentCst.offset + parentCst.length;
1339
+ const mapComment = meta?.comment !== void 0 ? trailingComment !== void 0 ? `${meta.comment}\n${trailingComment}` : meta.comment : trailingComment;
1152
1340
  const map = new YamlMap({
1153
1341
  items: pairs,
1154
1342
  style: "block",
@@ -1156,11 +1344,11 @@ function composeFlatBlockMap(children, startIdx, parentCst, state, externalFirst
1156
1344
  length: end - offset,
1157
1345
  ...meta?.tag !== void 0 ? { tag: meta.tag } : {},
1158
1346
  ...meta?.anchor !== void 0 ? { anchor: meta.anchor } : {},
1159
- ...meta?.comment !== void 0 ? { comment: meta.comment } : {}
1347
+ ...mapComment !== void 0 ? { comment: mapComment } : {}
1160
1348
  });
1161
1349
  if (meta?.anchor) registerAnchor(map, meta.anchor, state, offset);
1162
1350
  return map;
1163
1351
  }
1164
1352
 
1165
1353
  //#endregion
1166
- export { buildPairs, checkDuplicateKeys, checkMultilineImplicitKeys, checkTrailingContentOnSameLine, composeBlockMap, composeBlockSeq, composeFlatBlockMap, flattenBlockMapChildren };
1354
+ export { buildPairs, checkDuplicateKeys, checkMultilineImplicitKeys, checkTrailingContentOnSameLine, composeBlockMap, composeBlockSeq, composeFlatBlockMap, flattenBlockMapChildren, keyIdentity };