@effected/yaml 0.7.0 → 0.9.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 +80 -7
  2. package/Yaml.js +80 -32
  3. package/YamlDocument.js +23 -2
  4. package/YamlFormat.js +162 -21
  5. package/YamlLint.js +239 -0
  6. package/YamlLintRule.js +36 -0
  7. package/YamlNode.js +43 -4
  8. package/YamlToken.js +139 -0
  9. package/YamlVisitor.js +52 -16
  10. package/index.d.ts +446 -12
  11. package/index.js +4 -1
  12. package/internal/composer/anchors.js +2 -27
  13. package/internal/composer/block.js +291 -67
  14. package/internal/composer/comments.js +217 -0
  15. package/internal/composer/document.js +141 -14
  16. package/internal/composer/flow.js +103 -10
  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 +342 -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
- import { YamlMap, YamlPair, YamlScalar, YamlSeq } from "../../YamlNode.js";
1
+ import { YamlAlias, YamlMap, YamlPair, YamlScalar, YamlSeq } from "../../YamlNode.js";
2
2
  import { checkAnchorOnAlias, getAnchorName, makeAlias, registerAnchor } from "./anchors.js";
3
+ import { blankAboveIsKeepChompContent, columnAt, hasBlankLineAbove, isAfterIndicatorOnly, 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,74 @@ 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
+ /**
511
+ * Rebuild `pair` with `text` appended to the trailing comment of whichever
512
+ * node owns the pair's last line: the value when present, otherwise the key.
513
+ */
514
+ const withTrailingComment = (pair, text, onKey = false) => pair.value !== null && !onKey ? new YamlPair({
515
+ key: pair.key,
516
+ value: withCommentFields(pair.value, { comment: text })
517
+ }) : new YamlPair({
518
+ key: withCommentFields(pair.key, { comment: text }),
519
+ value: pair.value
520
+ });
521
+ /**
522
+ * Finish a pair: the pending leading fields go to its KEY node, the entry's
523
+ * trailing comment to whichever node owns the last line. The one place the
524
+ * node-level split is applied, so no construction site has to know it.
525
+ */
526
+ const pushPair = (pair, leading, trailing, trailingOnKey = false) => {
527
+ const withLeading = Object.keys(leading).length === 0 ? pair : new YamlPair({
528
+ key: withCommentFields(pair.key, leading),
529
+ value: pair.value
530
+ });
531
+ return trailing === void 0 ? withLeading : withTrailingComment(withLeading, trailing, trailingOnKey);
532
+ };
533
+ const noteContentColumn = (offset) => {
534
+ if (contentColumn < 0 && offset >= 0) contentColumn = columnAt(text, offset);
535
+ };
536
+ /** Join pending parts: `\n` between lines, an extra `\n` per embedded blank. */
537
+ const joinPending = (parts) => {
538
+ if (parts.length === 0) return void 0;
539
+ let out = parts[0].text;
540
+ for (let k = 1; k < parts.length; k++) {
541
+ const part = parts[k];
542
+ out += part.blankAbove ? `\n\n${part.text}` : `\n${part.text}`;
543
+ }
544
+ return out;
545
+ };
546
+ /** The pending comment fields for the pair being constructed, then reset. */
547
+ const takePendingFields = (anchorOffset) => {
548
+ let commentBefore = joinPending(pending);
549
+ if (anchorOffset >= 0 && hasBlankLineAbove(text, anchorOffset)) {
550
+ if (commentBefore !== void 0) commentBefore = `${commentBefore}\n`;
551
+ else if (!pendingSpace && lastEnd >= 0 && !blankAboveIsKeepChompContent(text, anchorOffset, lastNode)) pendingSpace = true;
552
+ }
553
+ const fields = {
554
+ ...commentBefore !== void 0 ? { commentBefore } : {},
555
+ ...pendingSpace ? { spaceBefore: true } : {}
556
+ };
557
+ pending = [];
558
+ pendingSpace = false;
559
+ return fields;
560
+ };
485
561
  while (i < items.length) {
486
562
  const item = items[i];
487
563
  if (!item) {
@@ -489,12 +565,18 @@ function buildPairs(items, pairs, text) {
489
565
  continue;
490
566
  }
491
567
  if (item.kind === "comment") {
492
- if (pairs.length > 0) {
568
+ const cOff = item.offset ?? -1;
569
+ const cText = item.comment ?? "";
570
+ if (!(cOff < 0 ? true : isOwnLineAt(text, cOff) || isAfterIndicatorOnly(text, cOff)) && pairs.length > 0) {
493
571
  const last = pairs[pairs.length - 1];
494
- if (last) pairs[pairs.length - 1] = new YamlPair({
495
- key: last.key,
496
- value: last.value,
497
- ...item.comment !== void 0 ? { comment: item.comment } : {}
572
+ if (last) pairs[pairs.length - 1] = withTrailingComment(last, cText);
573
+ } else {
574
+ const blankAbove = cOff >= 0 && hasBlankLineAbove(text, cOff) && !blankAboveIsKeepChompContent(text, cOff, lastNode);
575
+ if (blankAbove && pending.length === 0 && lastEnd >= 0) pendingSpace = true;
576
+ pending.push({
577
+ text: cText,
578
+ offset: cOff,
579
+ blankAbove: blankAbove && pending.length > 0
498
580
  });
499
581
  }
500
582
  i++;
@@ -503,30 +585,30 @@ function buildPairs(items, pairs, text) {
503
585
  if (item.kind === "value-sep") {
504
586
  const valueSepOffset = item.offset ?? 0;
505
587
  i++;
588
+ const pendingFields = takePendingFields(valueSepOffset);
589
+ noteContentColumn(valueSepOffset);
506
590
  const valueNode = consumeValueNodeForNullKey(items, i, text, valueSepOffset);
591
+ const nullKey = new YamlScalar({
592
+ value: null,
593
+ style: "plain",
594
+ offset: valueSepOffset,
595
+ length: 0
596
+ });
507
597
  if (valueNode) {
508
- const nullKey = new YamlScalar({
509
- value: null,
510
- style: "plain",
511
- offset: valueSepOffset,
512
- length: 0
513
- });
514
- pairs.push(new YamlPair({
598
+ pairs.push(pushPair(new YamlPair({
515
599
  key: nullKey,
516
600
  value: valueNode.node ?? null
517
- }));
601
+ }), pendingFields, void 0));
518
602
  i = valueNode.nextIdx;
603
+ lastEnd = valueNode.node ? nodeEnd(valueNode.node) : valueSepOffset + 1;
604
+ lastNode = valueNode.node ?? void 0;
519
605
  } else {
520
- const nullKey = new YamlScalar({
521
- value: null,
522
- style: "plain",
523
- offset: valueSepOffset,
524
- length: 0
525
- });
526
- pairs.push(new YamlPair({
606
+ pairs.push(pushPair(new YamlPair({
527
607
  key: nullKey,
528
608
  value: null
529
- }));
609
+ }), pendingFields, void 0));
610
+ lastEnd = valueSepOffset + 1;
611
+ lastNode = void 0;
530
612
  }
531
613
  continue;
532
614
  }
@@ -540,62 +622,124 @@ function buildPairs(items, pairs, text) {
540
622
  i++;
541
623
  }
542
624
  }
543
- while (i < items.length && items[i]?.kind === "comment") i++;
625
+ if (keyNode) noteContentColumn(keyNode.offset);
626
+ const pendingFields = takePendingFields(keyNode ? keyNode.offset : -1);
627
+ let pairTrailing;
628
+ while (i < items.length && items[i]?.kind === "comment") {
629
+ const c = items[i]?.comment;
630
+ if (c !== void 0) pairTrailing = joinComments(pairTrailing, c);
631
+ i++;
632
+ }
633
+ const keyOrNull = () => keyNode ?? new YamlScalar({
634
+ value: null,
635
+ style: "plain",
636
+ offset: 0,
637
+ length: 0
638
+ });
544
639
  if (i < items.length && items[i]?.kind === "value-sep") {
640
+ const sepOffset = items[i]?.offset ?? (keyNode ? nodeEnd(keyNode) : 0);
545
641
  i++;
546
- const valueResult = consumeValueNode(items, i);
642
+ const valueResult = consumeValueNode(items, i, text, sepOffset);
547
643
  if (valueResult) {
548
- 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
556
- }));
644
+ for (const c of valueResult.sameLineComments) pairTrailing = joinComments(pairTrailing, c);
645
+ let valNode = valueResult.node;
646
+ if (valNode !== null && valueResult.leadingComments.length > 0) valNode = withCommentFields(valNode, { commentBefore: valueResult.leadingComments.join("\n") });
647
+ pairs.push(pushPair(new YamlPair({
648
+ key: keyOrNull(),
649
+ value: valNode
650
+ }), pendingFields, pairTrailing, keyIsSimple(keyNode) && (valNode === null || !sameLineSpan(text, sepOffset, valNode.offset))));
557
651
  i = valueResult.nextIdx;
558
- } else pairs.push(new YamlPair({
559
- key: keyNode ?? new YamlScalar({
560
- value: null,
561
- style: "plain",
562
- offset: 0,
563
- length: 0
564
- }),
652
+ lastEnd = valNode ? nodeEnd(valNode) : sepOffset + 1;
653
+ lastNode = valNode ?? void 0;
654
+ } else {
655
+ pairs.push(pushPair(new YamlPair({
656
+ key: keyOrNull(),
657
+ value: null
658
+ }), pendingFields, pairTrailing));
659
+ lastEnd = sepOffset + 1;
660
+ lastNode = void 0;
661
+ }
662
+ } else {
663
+ pairs.push(pushPair(new YamlPair({
664
+ key: keyOrNull(),
565
665
  value: null
566
- }));
567
- } else pairs.push(new YamlPair({
568
- key: keyNode ?? new YamlScalar({
569
- value: null,
570
- style: "plain",
571
- offset: 0,
572
- length: 0
573
- }),
574
- value: null
575
- }));
666
+ }), pendingFields, pairTrailing));
667
+ lastEnd = keyNode ? nodeEnd(keyNode) : lastEnd;
668
+ if (keyNode) lastNode = keyNode;
669
+ }
576
670
  continue;
577
671
  }
578
672
  i++;
579
673
  }
674
+ if (pending.length === 0) return void 0;
675
+ const withLeadingBlank = (parts) => {
676
+ const joined = joinPending(parts);
677
+ const first = parts[0];
678
+ return joined !== void 0 && first !== void 0 && lastEnd >= 0 && first.offset >= 0 && hasBlankLineAbove(text, first.offset) && !blankAboveIsKeepChompContent(text, first.offset, lastNode) ? `\n${joined}` : joined;
679
+ };
680
+ if (escaped !== void 0 && contentColumn > 0) {
681
+ const kept = [];
682
+ for (const part of pending) if (part.offset >= 0 && columnAt(text, part.offset) < contentColumn) escaped.push({
683
+ text: part.text,
684
+ offset: part.offset
685
+ });
686
+ else kept.push(part);
687
+ return withLeadingBlank(kept);
688
+ }
689
+ return withLeadingBlank(pending);
580
690
  }
581
- function consumeValueNode(items, startIdx) {
691
+ /**
692
+ * True when a key renders in implicit `key: value` form, so the `:` sits on
693
+ * the key's own line. A multi-line or block-scalar key forces the explicit
694
+ * `? key` / `: value` spelling, where the `:` gets a line of its own.
695
+ */
696
+ function keyIsSimple(keyNode) {
697
+ if (keyNode instanceof YamlAlias) return true;
698
+ if (!(keyNode instanceof YamlScalar)) return false;
699
+ if (typeof keyNode.value === "string" && keyNode.value.includes("\n")) return false;
700
+ return keyNode.style !== "block-literal" && keyNode.style !== "block-folded";
701
+ }
702
+ function consumeValueNode(items, startIdx, text, sepOffset) {
703
+ let peek = startIdx;
704
+ while (peek < items.length && items[peek]?.kind === "comment") peek++;
705
+ const candidate = peek < items.length ? items[peek] : void 0;
706
+ 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
707
  let i = startIdx;
708
+ const sameLineComments = [];
709
+ const leadingComments = [];
710
+ let firstOwnLineIdx = -1;
583
711
  while (i < items.length) {
584
712
  const item = items[i];
585
713
  if (!item) break;
586
714
  if (item.kind === "comment") {
715
+ const cText = item.comment ?? "";
716
+ if (item.offset !== void 0 && sameLineSpan(text, sepOffset, item.offset)) sameLineComments.push(cText);
717
+ else {
718
+ if (firstOwnLineIdx < 0) firstOwnLineIdx = i;
719
+ leadingComments.push(cText);
720
+ }
587
721
  i++;
588
722
  continue;
589
723
  }
590
724
  if (item.kind === "node") return {
591
725
  node: item.node ?? null,
592
- nextIdx: i + 1
726
+ nextIdx: i + 1,
727
+ sameLineComments,
728
+ leadingComments
593
729
  };
594
730
  break;
595
731
  }
732
+ if (firstOwnLineIdx >= 0) return {
733
+ node: null,
734
+ nextIdx: firstOwnLineIdx,
735
+ sameLineComments,
736
+ leadingComments: []
737
+ };
596
738
  return i > startIdx ? {
597
739
  node: null,
598
- nextIdx: i
740
+ nextIdx: i,
741
+ sameLineComments,
742
+ leadingComments
599
743
  } : null;
600
744
  }
601
745
  /**
@@ -609,10 +753,12 @@ function consumeValueNode(items, startIdx) {
609
753
  */
610
754
  function consumeValueNodeForNullKey(items, startIdx, text, valueSepOffset) {
611
755
  let i = startIdx;
756
+ let firstOwnLineIdx = -1;
612
757
  while (i < items.length) {
613
758
  const item = items[i];
614
759
  if (!item) break;
615
760
  if (item.kind === "comment") {
761
+ if (firstOwnLineIdx < 0 && item.offset !== void 0 && !sameLineSpan(text, valueSepOffset, item.offset)) firstOwnLineIdx = i;
616
762
  i++;
617
763
  continue;
618
764
  }
@@ -628,6 +774,10 @@ function consumeValueNodeForNullKey(items, startIdx, text, valueSepOffset) {
628
774
  }
629
775
  break;
630
776
  }
777
+ if (firstOwnLineIdx >= 0) return {
778
+ node: null,
779
+ nextIdx: firstOwnLineIdx
780
+ };
631
781
  return i > startIdx ? {
632
782
  node: null,
633
783
  nextIdx: i
@@ -950,11 +1100,59 @@ function composeBlockSeq(cst, state, meta) {
950
1100
  }
951
1101
  function composeBlockSeqInner(cst, state, meta) {
952
1102
  const children = cst.children ?? [];
953
- const items = [];
1103
+ const rawItems = [];
954
1104
  let pendingMeta = {};
955
1105
  let sawEntry = false;
956
1106
  const seqIndent = lineIndentColumn(state.text, cst.offset);
957
1107
  let sawNewlineSincePending = false;
1108
+ let pending = [];
1109
+ let pendingSpace = false;
1110
+ let lastItemEnd = -1;
1111
+ let lastItemNode;
1112
+ const joinPending = (parts) => {
1113
+ if (parts.length === 0) return void 0;
1114
+ let out = parts[0].text;
1115
+ for (let k = 1; k < parts.length; k++) {
1116
+ const part = parts[k];
1117
+ out += part.blankAbove ? `\n\n${part.text}` : `\n${part.text}`;
1118
+ }
1119
+ return out;
1120
+ };
1121
+ const acceptOwnLineComment = (cText, cOff) => {
1122
+ const blankAbove = cOff >= 0 && hasBlankLineAbove(state.text, cOff) && !blankAboveIsKeepChompContent(state.text, cOff, lastItemNode);
1123
+ if (blankAbove && pending.length === 0 && lastItemEnd >= 0) pendingSpace = true;
1124
+ pending.push({
1125
+ text: cText,
1126
+ offset: cOff,
1127
+ blankAbove: blankAbove && pending.length > 0
1128
+ });
1129
+ };
1130
+ const items = {
1131
+ get length() {
1132
+ return rawItems.length;
1133
+ },
1134
+ push(node) {
1135
+ let commentBefore = joinPending(pending);
1136
+ if (node.length > 0 && hasBlankLineAbove(state.text, node.offset)) {
1137
+ if (commentBefore !== void 0) commentBefore = `${commentBefore}\n`;
1138
+ else if (!pendingSpace && lastItemEnd >= 0 && !blankAboveIsKeepChompContent(state.text, node.offset, lastItemNode)) pendingSpace = true;
1139
+ }
1140
+ let decorated = node;
1141
+ if (commentBefore !== void 0 || pendingSpace) decorated = withCommentFields(node, {
1142
+ ...commentBefore !== void 0 ? { commentBefore } : {},
1143
+ ...pendingSpace ? { spaceBefore: true } : {}
1144
+ });
1145
+ pending = [];
1146
+ pendingSpace = false;
1147
+ rawItems.push(decorated);
1148
+ lastItemEnd = node.offset + node.length;
1149
+ lastItemNode = node;
1150
+ if (state.escapedComments.length > 0) {
1151
+ for (const ec of state.escapedComments) acceptOwnLineComment(ec.text, ec.offset);
1152
+ state.escapedComments.length = 0;
1153
+ }
1154
+ }
1155
+ };
958
1156
  for (let ci = 0; ci < children.length; ci++) {
959
1157
  const child = children[ci];
960
1158
  if (!child) continue;
@@ -962,7 +1160,14 @@ function composeBlockSeqInner(cst, state, meta) {
962
1160
  if (hasMeta(pendingMeta)) sawNewlineSincePending = true;
963
1161
  continue;
964
1162
  }
965
- if (child.type === "comment") continue;
1163
+ if (child.type === "comment") {
1164
+ const cText = rawCommentText(child.source);
1165
+ if (rawItems.length > 0 && !isOwnLineAt(state.text, child.offset)) {
1166
+ const prev = rawItems[rawItems.length - 1];
1167
+ if (prev) rawItems[rawItems.length - 1] = withCommentFields(prev, { comment: cText });
1168
+ } else acceptOwnLineComment(cText, child.offset);
1169
+ continue;
1170
+ }
966
1171
  if (child.type === "whitespace") {
967
1172
  if (child.source.trim() === "-") {
968
1173
  if (sawEntry) {
@@ -1121,14 +1326,32 @@ function composeBlockSeqInner(cst, state, meta) {
1121
1326
  if (pendingMeta.anchor) registerAnchor(scalar, pendingMeta.anchor, state, 0);
1122
1327
  items.push(scalar);
1123
1328
  }
1329
+ const withLeadingBlank = (parts) => {
1330
+ const joined = joinPending(parts);
1331
+ const first = parts[0];
1332
+ 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;
1333
+ };
1334
+ let seqTrailing;
1335
+ if (pending.length > 0) {
1336
+ if (seqIndent > 0) {
1337
+ const kept = [];
1338
+ for (const part of pending) if (part.offset >= 0 && columnAt(state.text, part.offset) < seqIndent) state.escapedComments.push({
1339
+ text: part.text,
1340
+ offset: part.offset
1341
+ });
1342
+ else kept.push(part);
1343
+ seqTrailing = withLeadingBlank(kept);
1344
+ } else seqTrailing = withLeadingBlank(pending);
1345
+ }
1346
+ const seqComment = meta?.comment !== void 0 ? seqTrailing !== void 0 ? `${meta.comment}\n${seqTrailing}` : meta.comment : seqTrailing;
1124
1347
  const seq = new YamlSeq({
1125
- items,
1348
+ items: rawItems,
1126
1349
  style: "block",
1127
1350
  offset: cst.offset,
1128
1351
  length: cst.length,
1129
1352
  ...meta?.tag !== void 0 ? { tag: meta.tag } : {},
1130
1353
  ...meta?.anchor !== void 0 ? { anchor: meta.anchor } : {},
1131
- ...meta?.comment !== void 0 ? { comment: meta.comment } : {}
1354
+ ...seqComment !== void 0 ? { comment: seqComment } : {}
1132
1355
  });
1133
1356
  if (meta?.anchor) registerAnchor(seq, meta.anchor, state, cst.offset);
1134
1357
  return seq;
@@ -1144,11 +1367,12 @@ function composeFlatBlockMap(children, startIdx, parentCst, state, externalFirst
1144
1367
  node: externalFirstKey
1145
1368
  });
1146
1369
  const pairs = [];
1147
- buildPairs(items, pairs, state.text);
1370
+ const trailingComment = buildPairs(items, pairs, state.text, state.escapedComments);
1148
1371
  if (state.options.uniqueKeys) checkDuplicateKeys(pairs, state);
1149
1372
  checkMultilineImplicitKeys(pairs, state);
1150
1373
  const offset = "offset" in externalFirstKey ? externalFirstKey.offset : parentCst.offset;
1151
1374
  const end = parentCst.offset + parentCst.length;
1375
+ const mapComment = meta?.comment !== void 0 ? trailingComment !== void 0 ? `${meta.comment}\n${trailingComment}` : meta.comment : trailingComment;
1152
1376
  const map = new YamlMap({
1153
1377
  items: pairs,
1154
1378
  style: "block",
@@ -1156,11 +1380,11 @@ function composeFlatBlockMap(children, startIdx, parentCst, state, externalFirst
1156
1380
  length: end - offset,
1157
1381
  ...meta?.tag !== void 0 ? { tag: meta.tag } : {},
1158
1382
  ...meta?.anchor !== void 0 ? { anchor: meta.anchor } : {},
1159
- ...meta?.comment !== void 0 ? { comment: meta.comment } : {}
1383
+ ...mapComment !== void 0 ? { comment: mapComment } : {}
1160
1384
  });
1161
1385
  if (meta?.anchor) registerAnchor(map, meta.anchor, state, offset);
1162
1386
  return map;
1163
1387
  }
1164
1388
 
1165
1389
  //#endregion
1166
- export { buildPairs, checkDuplicateKeys, checkMultilineImplicitKeys, checkTrailingContentOnSameLine, composeBlockMap, composeBlockSeq, composeFlatBlockMap, flattenBlockMapChildren };
1390
+ export { buildPairs, checkDuplicateKeys, checkMultilineImplicitKeys, checkTrailingContentOnSameLine, composeBlockMap, composeBlockSeq, composeFlatBlockMap, flattenBlockMapChildren, keyIdentity };