@effected/yaml 0.8.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.
- package/README.md +5 -3
- package/YamlDocument.js +11 -9
- package/YamlFormat.js +4 -14
- package/YamlNode.js +15 -10
- package/YamlVisitor.js +23 -22
- package/index.d.ts +22 -15
- package/internal/composer/block.js +70 -34
- package/internal/composer/comments.js +35 -5
- package/internal/composer/document.js +100 -10
- package/internal/composer/flow.js +21 -1
- package/internal/stringifier.js +127 -51
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -126,7 +126,9 @@ console.log(YamlFormat.formatToString(source, undefined, YamlFormattingOptions.m
|
|
|
126
126
|
// port: 3000
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
Comments are readable as data too. `YamlScalar`, `YamlMap`, `YamlSeq` and `
|
|
129
|
+
Comments are readable as data too. Every node class — `YamlScalar`, `YamlMap`, `YamlSeq` and `YamlAlias` — carries `commentBefore` (the own-line block above the node), `comment` (strictly the trailing comment on the node's own line) and `spaceBefore` (a blank line came first). `YamlPair` carries none of them: an entry is its `key` and its `value`, and the comments live on those two nodes. `YamlDocument` uses the same two names as the nodes do, `commentBefore` for a header block sitting ahead of a `---` marker and `comment` for the trailing block, and `YamlVisitor` tags every `Comment` event with `placement: "leading" | "trailing"`.
|
|
130
|
+
|
|
131
|
+
Which node a comment lands on follows one rule. An own-line comment above an entry leads that entry's key node, setting `commentBefore` and — when a blank line came first — `spaceBefore`. A trailing comment belongs to the last node on its line: in `a: 1 # t` the value ends the line, so `# t` is the value's `comment`; in `push: # only main` the value renders below, so the comment is the key's. Two slots per entry rather than one is what lets `push: # only main` and an own-line comment above the value both round-trip byte-intact, instead of one spelling being rewritten into the other. A header block with no `---` marker to separate it from the item stream leads the first entry's key by the same rule; one after the marker leads the root node.
|
|
130
132
|
|
|
131
133
|
## Lint and autofix
|
|
132
134
|
|
|
@@ -182,8 +184,8 @@ Multi-document streams — a Kubernetes manifest, or the two-document `pnpm-lock
|
|
|
182
184
|
- `Yaml.stripComments` — quote-aware comment removal that keeps line numbers stable, or every byte offset stable when given a replacement character.
|
|
183
185
|
- `Yaml.equals` / `Yaml.equalsValue` — semantic equality that ignores comments, whitespace, formatting and mapping key order while keeping sequence order significant.
|
|
184
186
|
- `Yaml.schema` / `Yaml.fromString` / `Yaml.YamlFromString` / `Yaml.allFromString` — string→domain schema factories that decode a single document or a whole stream into a validated Effect `Schema` value.
|
|
185
|
-
- `YamlNode` — an offset-preserving AST (`YamlScalar`, `YamlMap`, `YamlSeq`, `YamlPair`, `YamlAlias`) for locating and reading nodes by position or path
|
|
186
|
-
- `YamlDocument` — the full parsed AST plus the recovered `errors` and `warnings` arrays, so a partially valid document is still inspectable, and the
|
|
187
|
+
- `YamlNode` — an offset-preserving AST (`YamlScalar`, `YamlMap`, `YamlSeq`, `YamlPair`, `YamlAlias`) for locating and reading nodes by position or path; every node class except `YamlPair` carries its own `commentBefore`, `comment` and `spaceBefore`, and a pair is just its `key` and `value`.
|
|
188
|
+
- `YamlDocument` — the full parsed AST plus the recovered `errors` and `warnings` arrays, so a partially valid document is still inspectable, and the header and trailing comment blocks as `commentBefore` / `comment`, the same two names the node classes use.
|
|
187
189
|
- `YamlEdit` / `YamlFormat` — compute byte-minimal edit arrays for formatting and path-based modification, so a change preserves every comment and byte you did not touch; `preserveComments: false` opts out. `format` handles multi-document streams whole, re-emitting every document and its framing; `modify` is single-document by contract (a path names no particular document of a stream) and fails typed with `MultiDocumentStream` rather than guessing.
|
|
188
190
|
- `YamlVisitor` — walk a parsed document as a `Stream` of a tagged-enum event union, with `Stream.take` early termination on large inputs and a `placement` on every comment event.
|
|
189
191
|
- `YamlToken` / `YamlTokens` — the positioned token stream the lint rules read: `tokenize` is a synchronous `Result` that always succeeds, surfacing lexical problems as `error`-kind tokens, and `stream` derives an Effect `Stream` from the same walk.
|
package/YamlDocument.js
CHANGED
|
@@ -22,12 +22,14 @@ var YamlDirective = class extends Schema.Class("YamlDirective")({
|
|
|
22
22
|
/**
|
|
23
23
|
* A parsed YAML document: the root {@link (YamlNode:type)} (or `null` when
|
|
24
24
|
* empty), recovered `errors` and `warnings` as {@link YamlDiagnostic} data,
|
|
25
|
-
* the {@link YamlDirective} list, the optional document-level comments
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
25
|
+
* the {@link YamlDirective} list, the optional document-level comments and the
|
|
26
|
+
* `---`/`...` framing flags (absent flags read as `false`).
|
|
27
|
+
*
|
|
28
|
+
* `commentBefore` is a header block sitting AHEAD of a `---` marker; `comment`
|
|
29
|
+
* is the trailing block after the content or the `...` marker. A header with
|
|
30
|
+
* no marker, or one after the marker, belongs to the content rather than the
|
|
31
|
+
* document — it leads the root node, or the first entry when there is no
|
|
32
|
+
* marker to separate it from the item stream.
|
|
31
33
|
*
|
|
32
34
|
* Construct via `YamlDocument.parse` / `parseAll`; `YamlDocument.make` is for
|
|
33
35
|
* synthetic documents.
|
|
@@ -39,8 +41,8 @@ var YamlDocument = class YamlDocument extends Schema.Class("YamlDocument")({
|
|
|
39
41
|
errors: Schema.Array(YamlDiagnostic),
|
|
40
42
|
warnings: Schema.Array(YamlDiagnostic),
|
|
41
43
|
directives: Schema.Array(YamlDirective),
|
|
44
|
+
commentBefore: Schema.optionalKey(Schema.String),
|
|
42
45
|
comment: Schema.optionalKey(Schema.String),
|
|
43
|
-
commentAfter: Schema.optionalKey(Schema.String),
|
|
44
46
|
hasDocumentStart: Schema.optionalKey(Schema.Boolean),
|
|
45
47
|
hasDocumentEnd: Schema.optionalKey(Schema.Boolean),
|
|
46
48
|
hasDocumentStartTab: Schema.optionalKey(Schema.Boolean)
|
|
@@ -186,8 +188,8 @@ function fromRawDocument(raw, text) {
|
|
|
186
188
|
name: d.name,
|
|
187
189
|
parameters: d.parameters
|
|
188
190
|
})),
|
|
191
|
+
...raw.commentBefore !== void 0 ? { commentBefore: raw.commentBefore } : {},
|
|
189
192
|
...raw.comment !== void 0 ? { comment: raw.comment } : {},
|
|
190
|
-
...raw.commentAfter !== void 0 ? { commentAfter: raw.commentAfter } : {},
|
|
191
193
|
hasDocumentStart: raw.hasDocumentStart,
|
|
192
194
|
hasDocumentEnd: raw.hasDocumentEnd,
|
|
193
195
|
hasDocumentStartTab: raw.hasDocumentStartTab
|
|
@@ -200,8 +202,8 @@ function toRawDocument(doc) {
|
|
|
200
202
|
errors: [],
|
|
201
203
|
warnings: [],
|
|
202
204
|
directives: doc.directives,
|
|
205
|
+
...doc.commentBefore !== void 0 ? { commentBefore: doc.commentBefore } : {},
|
|
203
206
|
...doc.comment !== void 0 ? { comment: doc.comment } : {},
|
|
204
|
-
...doc.commentAfter !== void 0 ? { commentAfter: doc.commentAfter } : {},
|
|
205
207
|
hasDocumentStart: doc.hasDocumentStart ?? false,
|
|
206
208
|
hasDocumentEnd: doc.hasDocumentEnd ?? false,
|
|
207
209
|
hasDocumentStartTab: doc.hasDocumentStartTab ?? false
|
package/YamlFormat.js
CHANGED
|
@@ -112,8 +112,8 @@ function toOutputDocument(doc, preserveComments) {
|
|
|
112
112
|
warnings: doc.warnings,
|
|
113
113
|
directives: doc.directives,
|
|
114
114
|
...preserveComments ? definedFields({
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
commentBefore: doc.commentBefore,
|
|
116
|
+
comment: doc.comment
|
|
117
117
|
}) : {},
|
|
118
118
|
hasDocumentStart: doc.hasDocumentStart,
|
|
119
119
|
hasDocumentEnd: doc.hasDocumentEnd,
|
|
@@ -208,12 +208,7 @@ function modifyNode(node, path, depth, value) {
|
|
|
208
208
|
const oldPair = newItems[pairIndex];
|
|
209
209
|
newItems[pairIndex] = YamlPair.make({
|
|
210
210
|
key: oldPair.key,
|
|
211
|
-
value: newValueNode
|
|
212
|
-
...definedFields({
|
|
213
|
-
commentBefore: oldPair.commentBefore,
|
|
214
|
-
comment: oldPair.comment,
|
|
215
|
-
spaceBefore: oldPair.spaceBefore
|
|
216
|
-
})
|
|
211
|
+
value: newValueNode
|
|
217
212
|
});
|
|
218
213
|
return rebuildMap(node, newItems);
|
|
219
214
|
}
|
|
@@ -236,12 +231,7 @@ function modifyNode(node, path, depth, value) {
|
|
|
236
231
|
const newItems = [...node.items];
|
|
237
232
|
newItems[pairIndex] = YamlPair.make({
|
|
238
233
|
key: pair.key,
|
|
239
|
-
value: newValue
|
|
240
|
-
...definedFields({
|
|
241
|
-
commentBefore: pair.commentBefore,
|
|
242
|
-
comment: pair.comment,
|
|
243
|
-
spaceBefore: pair.spaceBefore
|
|
244
|
-
})
|
|
234
|
+
value: newValue
|
|
245
235
|
});
|
|
246
236
|
return rebuildMap(node, newItems);
|
|
247
237
|
}
|
package/YamlNode.js
CHANGED
|
@@ -120,12 +120,19 @@ var YamlScalar = class extends Schema.TaggedClass()("YamlScalar", {
|
|
|
120
120
|
* A YAML alias AST node, referencing a previously defined anchor by name
|
|
121
121
|
* (without the leading `*`).
|
|
122
122
|
*
|
|
123
|
+
* Carries the same comment triple as every other node class — see
|
|
124
|
+
* {@link YamlScalar} for the field semantics. An alias is a node like any
|
|
125
|
+
* other and a comment can legally sit above or after one.
|
|
126
|
+
*
|
|
123
127
|
* @public
|
|
124
128
|
*/
|
|
125
129
|
var YamlAlias = class extends Schema.TaggedClass()("YamlAlias", {
|
|
126
130
|
name: Schema.String,
|
|
127
131
|
offset: Schema.Number,
|
|
128
|
-
length: Schema.Number
|
|
132
|
+
length: Schema.Number,
|
|
133
|
+
commentBefore: Schema.optionalKey(Schema.String),
|
|
134
|
+
comment: Schema.optionalKey(Schema.String),
|
|
135
|
+
spaceBefore: Schema.optionalKey(Schema.Boolean)
|
|
129
136
|
}) {
|
|
130
137
|
/** See `YamlScalar.find`. Pure. */
|
|
131
138
|
find(path) {
|
|
@@ -168,20 +175,18 @@ const YamlNode = Schema.suspend(() => Schema.Union([
|
|
|
168
175
|
* A YAML key-value pair AST node, representing one entry within a mapping.
|
|
169
176
|
* `value` is `null` when absent (e.g. `key:` with no value).
|
|
170
177
|
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
178
|
+
* A pair carries **no comment fields**. Comments belong to the pair's `key`
|
|
179
|
+
* and `value` nodes, which have one comment slot each: an own-line comment
|
|
180
|
+
* above the entry leads the `key`, and a trailing comment on the entry's line
|
|
181
|
+
* follows the `value`. Two slots rather than one is what lets `a: # kc` keep
|
|
182
|
+
* its comment where the author wrote it instead of relocating it onto the
|
|
183
|
+
* value's line.
|
|
176
184
|
*
|
|
177
185
|
* @public
|
|
178
186
|
*/
|
|
179
187
|
var YamlPair = class extends Schema.TaggedClass()("YamlPair", {
|
|
180
188
|
key: Schema.suspend(() => YamlNode),
|
|
181
|
-
value: Schema.NullOr(Schema.suspend(() => YamlNode))
|
|
182
|
-
commentBefore: Schema.optionalKey(Schema.String),
|
|
183
|
-
comment: Schema.optionalKey(Schema.String),
|
|
184
|
-
spaceBefore: Schema.optionalKey(Schema.Boolean)
|
|
189
|
+
value: Schema.NullOr(Schema.suspend(() => YamlNode))
|
|
185
190
|
}) {};
|
|
186
191
|
/**
|
|
187
192
|
* A YAML mapping AST node, representing a collection of {@link YamlPair}
|
package/YamlVisitor.js
CHANGED
|
@@ -76,17 +76,17 @@ function* walkDocument(doc, text) {
|
|
|
76
76
|
depth,
|
|
77
77
|
diagnostic: YamlDiagnostic.fromRaw(raw, text)
|
|
78
78
|
});
|
|
79
|
-
if (doc.
|
|
79
|
+
if (doc.commentBefore !== void 0) yield YamlVisitorEvent.Comment({
|
|
80
80
|
path,
|
|
81
81
|
depth,
|
|
82
|
-
text: doc.
|
|
82
|
+
text: doc.commentBefore,
|
|
83
83
|
placement: "leading"
|
|
84
84
|
});
|
|
85
85
|
if (doc.contents !== null) yield* walkNode(doc.contents, path, depth);
|
|
86
|
-
if (doc.
|
|
86
|
+
if (doc.comment !== void 0) yield YamlVisitorEvent.Comment({
|
|
87
87
|
path,
|
|
88
88
|
depth,
|
|
89
|
-
text: doc.
|
|
89
|
+
text: doc.comment,
|
|
90
90
|
placement: "trailing"
|
|
91
91
|
});
|
|
92
92
|
yield YamlVisitorEvent.DocumentEnd({
|
|
@@ -116,12 +116,25 @@ function* walkNode(node, path, depth) {
|
|
|
116
116
|
...node.tag !== void 0 ? { tag: node.tag } : {},
|
|
117
117
|
...node.anchor !== void 0 ? { anchor: node.anchor } : {}
|
|
118
118
|
});
|
|
119
|
-
} else if (node instanceof YamlAlias)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
} else if (node instanceof YamlAlias) {
|
|
120
|
+
if (node.commentBefore !== void 0) yield YamlVisitorEvent.Comment({
|
|
121
|
+
path,
|
|
122
|
+
depth,
|
|
123
|
+
text: node.commentBefore,
|
|
124
|
+
placement: "leading"
|
|
125
|
+
});
|
|
126
|
+
if (node.comment !== void 0) yield YamlVisitorEvent.Comment({
|
|
127
|
+
path,
|
|
128
|
+
depth,
|
|
129
|
+
text: node.comment,
|
|
130
|
+
placement: "trailing"
|
|
131
|
+
});
|
|
132
|
+
yield YamlVisitorEvent.Alias({
|
|
133
|
+
path,
|
|
134
|
+
depth,
|
|
135
|
+
name: node.name
|
|
136
|
+
});
|
|
137
|
+
} else if (node instanceof YamlMap) {
|
|
125
138
|
if (node.commentBefore !== void 0) yield YamlVisitorEvent.Comment({
|
|
126
139
|
path,
|
|
127
140
|
depth,
|
|
@@ -181,18 +194,6 @@ function* walkPair(pair, parentPath, depth) {
|
|
|
181
194
|
const resolvedValue = pair.value instanceof YamlScalar ? pair.value.value : null;
|
|
182
195
|
const keySegment = typeof resolvedKey === "string" ? resolvedKey : typeof resolvedKey === "number" ? resolvedKey : String(resolvedKey);
|
|
183
196
|
const pairPath = [...parentPath, keySegment];
|
|
184
|
-
if (pair.commentBefore !== void 0) yield YamlVisitorEvent.Comment({
|
|
185
|
-
path: pairPath,
|
|
186
|
-
depth,
|
|
187
|
-
text: pair.commentBefore,
|
|
188
|
-
placement: "leading"
|
|
189
|
-
});
|
|
190
|
-
if (pair.comment !== void 0) yield YamlVisitorEvent.Comment({
|
|
191
|
-
path: pairPath,
|
|
192
|
-
depth,
|
|
193
|
-
text: pair.comment,
|
|
194
|
-
placement: "trailing"
|
|
195
|
-
});
|
|
196
197
|
yield YamlVisitorEvent.Pair({
|
|
197
198
|
path: pairPath,
|
|
198
199
|
depth,
|
package/index.d.ts
CHANGED
|
@@ -727,11 +727,18 @@ declare const YamlAlias_base: Schema.Class<YamlAlias, Schema.TaggedStruct<"YamlA
|
|
|
727
727
|
readonly name: Schema.String;
|
|
728
728
|
readonly offset: Schema.Number;
|
|
729
729
|
readonly length: Schema.Number;
|
|
730
|
+
readonly commentBefore: Schema.optionalKey<Schema.String>;
|
|
731
|
+
readonly comment: Schema.optionalKey<Schema.String>;
|
|
732
|
+
readonly spaceBefore: Schema.optionalKey<Schema.Boolean>;
|
|
730
733
|
}>, {}>;
|
|
731
734
|
/**
|
|
732
735
|
* A YAML alias AST node, referencing a previously defined anchor by name
|
|
733
736
|
* (without the leading `*`).
|
|
734
737
|
*
|
|
738
|
+
* Carries the same comment triple as every other node class — see
|
|
739
|
+
* {@link YamlScalar} for the field semantics. An alias is a node like any
|
|
740
|
+
* other and a comment can legally sit above or after one.
|
|
741
|
+
*
|
|
735
742
|
* @public
|
|
736
743
|
*/
|
|
737
744
|
declare class YamlAlias extends YamlAlias_base {
|
|
@@ -797,19 +804,17 @@ type YamlNode = YamlScalar | YamlMap | YamlSeq | YamlAlias;
|
|
|
797
804
|
declare const YamlPair_base: Schema.Class<YamlPair, Schema.TaggedStruct<"YamlPair", {
|
|
798
805
|
readonly key: Schema.suspend<Schema.Codec<YamlAlias | YamlMap | YamlScalar | YamlSeq, YamlAliasEncoded | YamlMapEncoded | YamlScalarEncoded | YamlSeqEncoded, never, never>>;
|
|
799
806
|
readonly value: Schema.NullOr<Schema.suspend<Schema.Codec<YamlAlias | YamlMap | YamlScalar | YamlSeq, YamlAliasEncoded | YamlMapEncoded | YamlScalarEncoded | YamlSeqEncoded, never, never>>>;
|
|
800
|
-
readonly commentBefore: Schema.optionalKey<Schema.String>;
|
|
801
|
-
readonly comment: Schema.optionalKey<Schema.String>;
|
|
802
|
-
readonly spaceBefore: Schema.optionalKey<Schema.Boolean>;
|
|
803
807
|
}>, {}>;
|
|
804
808
|
/**
|
|
805
809
|
* A YAML key-value pair AST node, representing one entry within a mapping.
|
|
806
810
|
* `value` is `null` when absent (e.g. `key:` with no value).
|
|
807
811
|
*
|
|
808
|
-
*
|
|
809
|
-
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
*
|
|
812
|
+
* A pair carries **no comment fields**. Comments belong to the pair's `key`
|
|
813
|
+
* and `value` nodes, which have one comment slot each: an own-line comment
|
|
814
|
+
* above the entry leads the `key`, and a trailing comment on the entry's line
|
|
815
|
+
* follows the `value`. Two slots rather than one is what lets `a: # kc` keep
|
|
816
|
+
* its comment where the author wrote it instead of relocating it onto the
|
|
817
|
+
* value's line.
|
|
813
818
|
*
|
|
814
819
|
* @public
|
|
815
820
|
*/
|
|
@@ -905,8 +910,8 @@ declare const YamlDocument_base: Schema.Class<YamlDocument, Schema.Struct<{
|
|
|
905
910
|
readonly errors: Schema.$Array<typeof YamlDiagnostic>;
|
|
906
911
|
readonly warnings: Schema.$Array<typeof YamlDiagnostic>;
|
|
907
912
|
readonly directives: Schema.$Array<typeof YamlDirective>;
|
|
913
|
+
readonly commentBefore: Schema.optionalKey<Schema.String>;
|
|
908
914
|
readonly comment: Schema.optionalKey<Schema.String>;
|
|
909
|
-
readonly commentAfter: Schema.optionalKey<Schema.String>;
|
|
910
915
|
readonly hasDocumentStart: Schema.optionalKey<Schema.Boolean>;
|
|
911
916
|
readonly hasDocumentEnd: Schema.optionalKey<Schema.Boolean>;
|
|
912
917
|
readonly hasDocumentStartTab: Schema.optionalKey<Schema.Boolean>;
|
|
@@ -914,12 +919,14 @@ declare const YamlDocument_base: Schema.Class<YamlDocument, Schema.Struct<{
|
|
|
914
919
|
/**
|
|
915
920
|
* A parsed YAML document: the root {@link (YamlNode:type)} (or `null` when
|
|
916
921
|
* empty), recovered `errors` and `warnings` as {@link YamlDiagnostic} data,
|
|
917
|
-
* the {@link YamlDirective} list, the optional document-level comments
|
|
918
|
-
*
|
|
919
|
-
*
|
|
920
|
-
*
|
|
921
|
-
*
|
|
922
|
-
*
|
|
922
|
+
* the {@link YamlDirective} list, the optional document-level comments and the
|
|
923
|
+
* `---`/`...` framing flags (absent flags read as `false`).
|
|
924
|
+
*
|
|
925
|
+
* `commentBefore` is a header block sitting AHEAD of a `---` marker; `comment`
|
|
926
|
+
* is the trailing block after the content or the `...` marker. A header with
|
|
927
|
+
* no marker, or one after the marker, belongs to the content rather than the
|
|
928
|
+
* document — it leads the root node, or the first entry when there is no
|
|
929
|
+
* marker to separate it from the item stream.
|
|
923
930
|
*
|
|
924
931
|
* Construct via `YamlDocument.parse` / `parseAll`; `YamlDocument.make` is for
|
|
925
932
|
* synthetic documents.
|
|
@@ -1,6 +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, isOwnLineAt, joinComments, rawCommentText, sameLineSpan, withCommentFields } from "./comments.js";
|
|
3
|
+
import { blankAboveIsKeepChompContent, columnAt, hasBlankLineAbove, isAfterIndicatorOnly, isOwnLineAt, joinComments, rawCommentText, sameLineSpan, withCommentFields } from "./comments.js";
|
|
4
4
|
import { enterNesting, exitNesting, hasMeta, hasNonWhitespaceBeforeOnLine, lineCol, lineIndentColumn, sameLine } from "./state.js";
|
|
5
5
|
import { blockMapStartsWithValueSep, classifyPlainNumeric, collectMultilineKey, collectMultilinePlainScalar, findFirstContent, findLastContent, findNextContentInList, findNextSignificantChild, findValueSepOffset, getScalarStyle, hasValueSepAfterInList, hasValueSepBetween, hasValueSepThroughPlainScalars, makeScalar, resolveScalar, shouldPreserveRaw } from "./scalars.js";
|
|
6
6
|
|
|
@@ -507,6 +507,29 @@ function buildPairs(items, pairs, text, escaped) {
|
|
|
507
507
|
let lastNode;
|
|
508
508
|
let contentColumn = -1;
|
|
509
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
|
+
};
|
|
510
533
|
const noteContentColumn = (offset) => {
|
|
511
534
|
if (contentColumn < 0 && offset >= 0) contentColumn = columnAt(text, offset);
|
|
512
535
|
};
|
|
@@ -544,15 +567,9 @@ function buildPairs(items, pairs, text, escaped) {
|
|
|
544
567
|
if (item.kind === "comment") {
|
|
545
568
|
const cOff = item.offset ?? -1;
|
|
546
569
|
const cText = item.comment ?? "";
|
|
547
|
-
if (!(cOff < 0 ? true : isOwnLineAt(text, cOff)) && pairs.length > 0) {
|
|
570
|
+
if (!(cOff < 0 ? true : isOwnLineAt(text, cOff) || isAfterIndicatorOnly(text, cOff)) && pairs.length > 0) {
|
|
548
571
|
const last = pairs[pairs.length - 1];
|
|
549
|
-
if (last) pairs[pairs.length - 1] =
|
|
550
|
-
key: last.key,
|
|
551
|
-
value: last.value,
|
|
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
|
-
});
|
|
572
|
+
if (last) pairs[pairs.length - 1] = withTrailingComment(last, cText);
|
|
556
573
|
} else {
|
|
557
574
|
const blankAbove = cOff >= 0 && hasBlankLineAbove(text, cOff) && !blankAboveIsKeepChompContent(text, cOff, lastNode);
|
|
558
575
|
if (blankAbove && pending.length === 0 && lastEnd >= 0) pendingSpace = true;
|
|
@@ -578,20 +595,18 @@ function buildPairs(items, pairs, text, escaped) {
|
|
|
578
595
|
length: 0
|
|
579
596
|
});
|
|
580
597
|
if (valueNode) {
|
|
581
|
-
pairs.push(new YamlPair({
|
|
598
|
+
pairs.push(pushPair(new YamlPair({
|
|
582
599
|
key: nullKey,
|
|
583
|
-
value: valueNode.node ?? null
|
|
584
|
-
|
|
585
|
-
}));
|
|
600
|
+
value: valueNode.node ?? null
|
|
601
|
+
}), pendingFields, void 0));
|
|
586
602
|
i = valueNode.nextIdx;
|
|
587
603
|
lastEnd = valueNode.node ? nodeEnd(valueNode.node) : valueSepOffset + 1;
|
|
588
604
|
lastNode = valueNode.node ?? void 0;
|
|
589
605
|
} else {
|
|
590
|
-
pairs.push(new YamlPair({
|
|
606
|
+
pairs.push(pushPair(new YamlPair({
|
|
591
607
|
key: nullKey,
|
|
592
|
-
value: null
|
|
593
|
-
|
|
594
|
-
}));
|
|
608
|
+
value: null
|
|
609
|
+
}), pendingFields, void 0));
|
|
595
610
|
lastEnd = valueSepOffset + 1;
|
|
596
611
|
lastNode = void 0;
|
|
597
612
|
}
|
|
@@ -629,32 +644,26 @@ function buildPairs(items, pairs, text, escaped) {
|
|
|
629
644
|
for (const c of valueResult.sameLineComments) pairTrailing = joinComments(pairTrailing, c);
|
|
630
645
|
let valNode = valueResult.node;
|
|
631
646
|
if (valNode !== null && valueResult.leadingComments.length > 0) valNode = withCommentFields(valNode, { commentBefore: valueResult.leadingComments.join("\n") });
|
|
632
|
-
pairs.push(new YamlPair({
|
|
647
|
+
pairs.push(pushPair(new YamlPair({
|
|
633
648
|
key: keyOrNull(),
|
|
634
|
-
value: valNode
|
|
635
|
-
|
|
636
|
-
...pairTrailing !== void 0 ? { comment: pairTrailing } : {}
|
|
637
|
-
}));
|
|
649
|
+
value: valNode
|
|
650
|
+
}), pendingFields, pairTrailing, keyIsSimple(keyNode) && (valNode === null || !sameLineSpan(text, sepOffset, valNode.offset))));
|
|
638
651
|
i = valueResult.nextIdx;
|
|
639
652
|
lastEnd = valNode ? nodeEnd(valNode) : sepOffset + 1;
|
|
640
653
|
lastNode = valNode ?? void 0;
|
|
641
654
|
} else {
|
|
642
|
-
pairs.push(new YamlPair({
|
|
655
|
+
pairs.push(pushPair(new YamlPair({
|
|
643
656
|
key: keyOrNull(),
|
|
644
|
-
value: null
|
|
645
|
-
|
|
646
|
-
...pairTrailing !== void 0 ? { comment: pairTrailing } : {}
|
|
647
|
-
}));
|
|
657
|
+
value: null
|
|
658
|
+
}), pendingFields, pairTrailing));
|
|
648
659
|
lastEnd = sepOffset + 1;
|
|
649
660
|
lastNode = void 0;
|
|
650
661
|
}
|
|
651
662
|
} else {
|
|
652
|
-
pairs.push(new YamlPair({
|
|
663
|
+
pairs.push(pushPair(new YamlPair({
|
|
653
664
|
key: keyOrNull(),
|
|
654
|
-
value: null
|
|
655
|
-
|
|
656
|
-
...pairTrailing !== void 0 ? { comment: pairTrailing } : {}
|
|
657
|
-
}));
|
|
665
|
+
value: null
|
|
666
|
+
}), pendingFields, pairTrailing));
|
|
658
667
|
lastEnd = keyNode ? nodeEnd(keyNode) : lastEnd;
|
|
659
668
|
if (keyNode) lastNode = keyNode;
|
|
660
669
|
}
|
|
@@ -679,6 +688,17 @@ function buildPairs(items, pairs, text, escaped) {
|
|
|
679
688
|
}
|
|
680
689
|
return withLeadingBlank(pending);
|
|
681
690
|
}
|
|
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
|
+
}
|
|
682
702
|
function consumeValueNode(items, startIdx, text, sepOffset) {
|
|
683
703
|
let peek = startIdx;
|
|
684
704
|
while (peek < items.length && items[peek]?.kind === "comment") peek++;
|
|
@@ -687,13 +707,17 @@ function consumeValueNode(items, startIdx, text, sepOffset) {
|
|
|
687
707
|
let i = startIdx;
|
|
688
708
|
const sameLineComments = [];
|
|
689
709
|
const leadingComments = [];
|
|
710
|
+
let firstOwnLineIdx = -1;
|
|
690
711
|
while (i < items.length) {
|
|
691
712
|
const item = items[i];
|
|
692
713
|
if (!item) break;
|
|
693
714
|
if (item.kind === "comment") {
|
|
694
715
|
const cText = item.comment ?? "";
|
|
695
716
|
if (item.offset !== void 0 && sameLineSpan(text, sepOffset, item.offset)) sameLineComments.push(cText);
|
|
696
|
-
else
|
|
717
|
+
else {
|
|
718
|
+
if (firstOwnLineIdx < 0) firstOwnLineIdx = i;
|
|
719
|
+
leadingComments.push(cText);
|
|
720
|
+
}
|
|
697
721
|
i++;
|
|
698
722
|
continue;
|
|
699
723
|
}
|
|
@@ -705,6 +729,12 @@ function consumeValueNode(items, startIdx, text, sepOffset) {
|
|
|
705
729
|
};
|
|
706
730
|
break;
|
|
707
731
|
}
|
|
732
|
+
if (firstOwnLineIdx >= 0) return {
|
|
733
|
+
node: null,
|
|
734
|
+
nextIdx: firstOwnLineIdx,
|
|
735
|
+
sameLineComments,
|
|
736
|
+
leadingComments: []
|
|
737
|
+
};
|
|
708
738
|
return i > startIdx ? {
|
|
709
739
|
node: null,
|
|
710
740
|
nextIdx: i,
|
|
@@ -723,10 +753,12 @@ function consumeValueNode(items, startIdx, text, sepOffset) {
|
|
|
723
753
|
*/
|
|
724
754
|
function consumeValueNodeForNullKey(items, startIdx, text, valueSepOffset) {
|
|
725
755
|
let i = startIdx;
|
|
756
|
+
let firstOwnLineIdx = -1;
|
|
726
757
|
while (i < items.length) {
|
|
727
758
|
const item = items[i];
|
|
728
759
|
if (!item) break;
|
|
729
760
|
if (item.kind === "comment") {
|
|
761
|
+
if (firstOwnLineIdx < 0 && item.offset !== void 0 && !sameLineSpan(text, valueSepOffset, item.offset)) firstOwnLineIdx = i;
|
|
730
762
|
i++;
|
|
731
763
|
continue;
|
|
732
764
|
}
|
|
@@ -742,6 +774,10 @@ function consumeValueNodeForNullKey(items, startIdx, text, valueSepOffset) {
|
|
|
742
774
|
}
|
|
743
775
|
break;
|
|
744
776
|
}
|
|
777
|
+
if (firstOwnLineIdx >= 0) return {
|
|
778
|
+
node: null,
|
|
779
|
+
nextIdx: firstOwnLineIdx
|
|
780
|
+
};
|
|
745
781
|
return i > startIdx ? {
|
|
746
782
|
node: null,
|
|
747
783
|
nextIdx: i
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { YamlMap, YamlScalar, YamlSeq } from "../../YamlNode.js";
|
|
1
|
+
import { YamlAlias, YamlMap, YamlScalar, YamlSeq } from "../../YamlNode.js";
|
|
2
2
|
|
|
3
3
|
//#region src/internal/composer/comments.ts
|
|
4
4
|
/** True when there is no line break between `start` and `end` in `text`. */
|
|
@@ -24,6 +24,31 @@ function isOwnLineAt(text, offset) {
|
|
|
24
24
|
return true;
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
27
|
+
* True when the only thing before `offset` on its line is a block indicator —
|
|
28
|
+
* `?`, `:` or `-` — plus whitespace. Such a comment sits on an indicator line
|
|
29
|
+
* with its node BELOW, so it leads that node rather than trailing whatever
|
|
30
|
+
* came before (`? # c` / ` - seq1`). Without this, the `? ` prefix makes the
|
|
31
|
+
* comment look like a trailing comment on the previous entry.
|
|
32
|
+
*/
|
|
33
|
+
function isAfterIndicatorOnly(text, offset) {
|
|
34
|
+
let i = offset - 1;
|
|
35
|
+
let sawIndicator = false;
|
|
36
|
+
while (i >= 0) {
|
|
37
|
+
const ch = text[i];
|
|
38
|
+
if (ch === " " || ch === " ") {
|
|
39
|
+
i--;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (!sawIndicator && (ch === "?" || ch === "-" || ch === ":")) {
|
|
43
|
+
sawIndicator = true;
|
|
44
|
+
i--;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
return sawIndicator && (ch === "\n" || ch === "\r");
|
|
48
|
+
}
|
|
49
|
+
return sawIndicator;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
27
52
|
* True when the line immediately above the line containing `offset` is blank
|
|
28
53
|
* (empty or horizontal whitespace only). Purely local — see
|
|
29
54
|
* {@link isOwnLineAt} for why span-based gap checks are not used.
|
|
@@ -133,8 +158,8 @@ function columnAt(text, offset) {
|
|
|
133
158
|
/**
|
|
134
159
|
* Rebuild `node` with the given comment fields merged in (existing fields are
|
|
135
160
|
* kept unless overridden; `commentBefore`/`comment` join with a newline when
|
|
136
|
-
* both sides are present).
|
|
137
|
-
*
|
|
161
|
+
* both sides are present). Every node class carries the triple, aliases
|
|
162
|
+
* included.
|
|
138
163
|
*/
|
|
139
164
|
function withCommentFields(node, fields) {
|
|
140
165
|
if (node instanceof YamlScalar) return new YamlScalar({
|
|
@@ -170,7 +195,12 @@ function withCommentFields(node, fields) {
|
|
|
170
195
|
offset: node.offset,
|
|
171
196
|
length: node.length
|
|
172
197
|
});
|
|
173
|
-
return
|
|
198
|
+
return new YamlAlias({
|
|
199
|
+
name: node.name,
|
|
200
|
+
...mergedCommentFields(node, fields),
|
|
201
|
+
offset: node.offset,
|
|
202
|
+
length: node.length
|
|
203
|
+
});
|
|
174
204
|
}
|
|
175
205
|
function mergedCommentFields(existing, incoming) {
|
|
176
206
|
const commentBefore = incoming.commentBefore !== void 0 ? existing.commentBefore !== void 0 ? `${existing.commentBefore}\n${incoming.commentBefore}` : incoming.commentBefore : existing.commentBefore;
|
|
@@ -184,4 +214,4 @@ function mergedCommentFields(existing, incoming) {
|
|
|
184
214
|
}
|
|
185
215
|
|
|
186
216
|
//#endregion
|
|
187
|
-
export { blankAboveIsKeepChompContent, blankLineAboveStart, columnAt, hasBlankLineAbove, hasBlankLineBelow, isOwnLineAt, joinComments, rawCommentText, sameLineSpan, withCommentFields };
|
|
217
|
+
export { blankAboveIsKeepChompContent, blankLineAboveStart, columnAt, hasBlankLineAbove, hasBlankLineBelow, isAfterIndicatorOnly, isOwnLineAt, joinComments, rawCommentText, sameLineSpan, withCommentFields };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { YamlAlias, YamlMap, YamlPair, YamlScalar, YamlSeq } from "../../YamlNode.js";
|
|
2
2
|
import { parseCSTAll } from "../cst-parser.js";
|
|
3
3
|
import { checkAnchorOnAlias, getAnchorName, makeAlias, registerAnchor } from "./anchors.js";
|
|
4
|
-
import { hasBlankLineAbove, hasBlankLineBelow, rawCommentText } from "./comments.js";
|
|
4
|
+
import { hasBlankLineAbove, hasBlankLineBelow, rawCommentText, withCommentFields } from "./comments.js";
|
|
5
5
|
import { clearMeta, commentProps, createState, hasMeta, sameLine } from "./state.js";
|
|
6
6
|
import { parseDirective, validateTagHandlesInDocument } from "./tags.js";
|
|
7
7
|
import { collectMultilinePlainScalar, findNextContentChild, getScalarStyle, hasBlockMapAfterInList, hasValueSepAfter, indexOfChild, makeScalar, resolveScalar } from "./scalars.js";
|
|
@@ -134,7 +134,10 @@ function composeDocument(cst, state, hasSubsequentDocuments = false, nextDocCst)
|
|
|
134
134
|
let documentComment;
|
|
135
135
|
let documentCommentAfter;
|
|
136
136
|
let lastLeadingCommentOffset = -1;
|
|
137
|
+
let lastAfterMarkerCommentOffset = -1;
|
|
137
138
|
let sawDocEndMarker = false;
|
|
139
|
+
let sawDocStartMarker = false;
|
|
140
|
+
let afterMarkerComment;
|
|
138
141
|
const hasDocStart = children.some((c) => c.type === "whitespace" && c.source === "---");
|
|
139
142
|
let i = 0;
|
|
140
143
|
const meta = {};
|
|
@@ -171,6 +174,7 @@ function composeDocument(cst, state, hasSubsequentDocuments = false, nextDocCst)
|
|
|
171
174
|
if (child.type === "newline" && hasMeta(meta)) sawNewlineSinceMeta = true;
|
|
172
175
|
if (child.type === "whitespace" || child.type === "newline") {
|
|
173
176
|
if (child.type === "whitespace" && child.source === "...") sawDocEndMarker = true;
|
|
177
|
+
if (child.type === "whitespace" && child.source === "---") sawDocStartMarker = true;
|
|
174
178
|
if (child.type === "whitespace" && (child.source === "]" || child.source === "}")) state.errors.push({
|
|
175
179
|
code: "MalformedFlowCollection",
|
|
176
180
|
message: `Unexpected flow indicator '${child.source}' at document level`,
|
|
@@ -182,9 +186,15 @@ function composeDocument(cst, state, hasSubsequentDocuments = false, nextDocCst)
|
|
|
182
186
|
}
|
|
183
187
|
if (child.type === "comment" && contents === null && !sawDocEndMarker) {
|
|
184
188
|
const text = rawCommentText(child.source);
|
|
185
|
-
if (
|
|
186
|
-
|
|
187
|
-
|
|
189
|
+
if (sawDocStartMarker) {
|
|
190
|
+
if (afterMarkerComment !== void 0 && hasBlankLineAbove(state.text, child.offset)) afterMarkerComment = `${afterMarkerComment}\n`;
|
|
191
|
+
afterMarkerComment = afterMarkerComment === void 0 ? text : `${afterMarkerComment}\n${text}`;
|
|
192
|
+
lastAfterMarkerCommentOffset = child.offset;
|
|
193
|
+
} else {
|
|
194
|
+
if (documentComment !== void 0 && hasBlankLineAbove(state.text, child.offset)) documentComment = `${documentComment}\n`;
|
|
195
|
+
documentComment = documentComment === void 0 ? text : `${documentComment}\n${text}`;
|
|
196
|
+
lastLeadingCommentOffset = child.offset;
|
|
197
|
+
}
|
|
188
198
|
i++;
|
|
189
199
|
continue;
|
|
190
200
|
}
|
|
@@ -411,17 +421,32 @@ function composeDocument(cst, state, hasSubsequentDocuments = false, nextDocCst)
|
|
|
411
421
|
}
|
|
412
422
|
}
|
|
413
423
|
if (documentComment !== void 0 && contents !== null && hasBlankLineBelow(state.text, lastLeadingCommentOffset)) documentComment = `${documentComment}\n`;
|
|
424
|
+
if (afterMarkerComment !== void 0 && contents !== null && hasBlankLineBelow(state.text, lastAfterMarkerCommentOffset)) afterMarkerComment = `${afterMarkerComment}\n`;
|
|
414
425
|
state.escapedComments.length = 0;
|
|
426
|
+
if (documentCommentAfter === void 0 && contents !== null && contents.comment !== void 0) {
|
|
427
|
+
if ((contents instanceof YamlMap || contents instanceof YamlSeq) && contents.style !== "flow") {
|
|
428
|
+
documentCommentAfter = contents.comment;
|
|
429
|
+
contents = stripOwnComment(contents);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
let headerForDocument;
|
|
433
|
+
let decorated = contents;
|
|
434
|
+
if (documentComment !== void 0) {
|
|
435
|
+
if (hasDocStart || contents === null) headerForDocument = documentComment;
|
|
436
|
+
else decorated = attachHeaderToFirstEntry(contents, documentComment);
|
|
437
|
+
}
|
|
438
|
+
if (afterMarkerComment !== void 0 && decorated !== null) decorated = withCommentFields(decorated, { commentBefore: afterMarkerComment });
|
|
439
|
+
else if (afterMarkerComment !== void 0) headerForDocument = headerForDocument === void 0 ? afterMarkerComment : `${headerForDocument}\n${afterMarkerComment}`;
|
|
415
440
|
return {
|
|
416
|
-
contents,
|
|
441
|
+
contents: decorated,
|
|
417
442
|
errors: [...state.errors],
|
|
418
443
|
warnings: [...state.warnings],
|
|
419
444
|
directives,
|
|
420
445
|
hasDocumentStart: hasDocStart,
|
|
421
446
|
hasDocumentEnd: hasDocEnd,
|
|
422
447
|
hasDocumentStartTab: hasDocStartTab,
|
|
423
|
-
...
|
|
424
|
-
...documentCommentAfter !== void 0 ? {
|
|
448
|
+
...headerForDocument !== void 0 ? { commentBefore: headerForDocument } : {},
|
|
449
|
+
...documentCommentAfter !== void 0 ? { comment: documentCommentAfter } : {}
|
|
425
450
|
};
|
|
426
451
|
}
|
|
427
452
|
/**
|
|
@@ -598,8 +623,7 @@ function decorateSourceMultiline(node, text) {
|
|
|
598
623
|
if (node instanceof YamlMap) {
|
|
599
624
|
const newItems = node.items.map((pair) => new YamlPair({
|
|
600
625
|
key: decorateSourceMultiline(pair.key, text) ?? pair.key,
|
|
601
|
-
value: pair.value === null ? null : decorateSourceMultiline(pair.value, text)
|
|
602
|
-
...commentProps(pair)
|
|
626
|
+
value: pair.value === null ? null : decorateSourceMultiline(pair.value, text)
|
|
603
627
|
}));
|
|
604
628
|
const multiline = isSourceMultiline(text, node.offset, node.length);
|
|
605
629
|
return new YamlMap({
|
|
@@ -637,8 +661,8 @@ function decorateDocumentSourceMultiline(doc, text) {
|
|
|
637
661
|
errors: doc.errors,
|
|
638
662
|
warnings: doc.warnings,
|
|
639
663
|
directives: doc.directives,
|
|
664
|
+
...doc.commentBefore !== void 0 ? { commentBefore: doc.commentBefore } : {},
|
|
640
665
|
...doc.comment !== void 0 ? { comment: doc.comment } : {},
|
|
641
|
-
...doc.commentAfter !== void 0 ? { commentAfter: doc.commentAfter } : {},
|
|
642
666
|
hasDocumentStart: doc.hasDocumentStart,
|
|
643
667
|
hasDocumentEnd: doc.hasDocumentEnd,
|
|
644
668
|
hasDocumentStartTab: doc.hasDocumentStartTab
|
|
@@ -709,6 +733,72 @@ function composeAllDocuments(text, options) {
|
|
|
709
733
|
streamErrors: crossDocState.errors
|
|
710
734
|
};
|
|
711
735
|
}
|
|
736
|
+
/**
|
|
737
|
+
* Attach a marker-less document header to the FIRST entry of the content — the
|
|
738
|
+
* first pair's key for a mapping, the first item for a sequence, the node
|
|
739
|
+
* itself for a scalar. Without a `---` boundary the header is just the first
|
|
740
|
+
* own-line comment in the document's item stream, so it forward-attributes
|
|
741
|
+
* like any other.
|
|
742
|
+
*/
|
|
743
|
+
function attachHeaderToFirstEntry(contents, header) {
|
|
744
|
+
if (contents instanceof YamlMap && contents.items.length > 0) {
|
|
745
|
+
const first = contents.items[0];
|
|
746
|
+
const items = [...contents.items];
|
|
747
|
+
items[0] = new YamlPair({
|
|
748
|
+
key: withCommentFields(first.key, { commentBefore: header }),
|
|
749
|
+
value: first.value
|
|
750
|
+
});
|
|
751
|
+
return new YamlMap({
|
|
752
|
+
items,
|
|
753
|
+
style: contents.style,
|
|
754
|
+
...contents.tag !== void 0 ? { tag: contents.tag } : {},
|
|
755
|
+
...contents.anchor !== void 0 ? { anchor: contents.anchor } : {},
|
|
756
|
+
...contents.commentBefore !== void 0 ? { commentBefore: contents.commentBefore } : {},
|
|
757
|
+
...contents.comment !== void 0 ? { comment: contents.comment } : {},
|
|
758
|
+
...contents.spaceBefore !== void 0 ? { spaceBefore: contents.spaceBefore } : {},
|
|
759
|
+
...contents.sourceMultiline !== void 0 ? { sourceMultiline: contents.sourceMultiline } : {},
|
|
760
|
+
offset: contents.offset,
|
|
761
|
+
length: contents.length
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
if (contents instanceof YamlSeq && contents.items.length > 0) {
|
|
765
|
+
const items = [...contents.items];
|
|
766
|
+
items[0] = withCommentFields(items[0], { commentBefore: header });
|
|
767
|
+
return new YamlSeq({
|
|
768
|
+
items,
|
|
769
|
+
style: contents.style,
|
|
770
|
+
...contents.tag !== void 0 ? { tag: contents.tag } : {},
|
|
771
|
+
...contents.anchor !== void 0 ? { anchor: contents.anchor } : {},
|
|
772
|
+
...contents.commentBefore !== void 0 ? { commentBefore: contents.commentBefore } : {},
|
|
773
|
+
...contents.comment !== void 0 ? { comment: contents.comment } : {},
|
|
774
|
+
...contents.spaceBefore !== void 0 ? { spaceBefore: contents.spaceBefore } : {},
|
|
775
|
+
...contents.sourceMultiline !== void 0 ? { sourceMultiline: contents.sourceMultiline } : {},
|
|
776
|
+
offset: contents.offset,
|
|
777
|
+
length: contents.length
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
return withCommentFields(contents, { commentBefore: header });
|
|
781
|
+
}
|
|
782
|
+
/** Rebuild a collection without its own trailing `comment`. */
|
|
783
|
+
function stripOwnComment(node) {
|
|
784
|
+
const shared = {
|
|
785
|
+
style: node.style,
|
|
786
|
+
...node.tag !== void 0 ? { tag: node.tag } : {},
|
|
787
|
+
...node.anchor !== void 0 ? { anchor: node.anchor } : {},
|
|
788
|
+
...node.commentBefore !== void 0 ? { commentBefore: node.commentBefore } : {},
|
|
789
|
+
...node.spaceBefore !== void 0 ? { spaceBefore: node.spaceBefore } : {},
|
|
790
|
+
...node.sourceMultiline !== void 0 ? { sourceMultiline: node.sourceMultiline } : {},
|
|
791
|
+
offset: node.offset,
|
|
792
|
+
length: node.length
|
|
793
|
+
};
|
|
794
|
+
return node instanceof YamlMap ? new YamlMap({
|
|
795
|
+
items: node.items,
|
|
796
|
+
...shared
|
|
797
|
+
}) : new YamlSeq({
|
|
798
|
+
items: node.items,
|
|
799
|
+
...shared
|
|
800
|
+
});
|
|
801
|
+
}
|
|
712
802
|
|
|
713
803
|
//#endregion
|
|
714
804
|
export { EMPTY_DOCUMENT, composeAllDocuments, composeDocument, composeFirstDocument, composeFirstDocumentCounted, validateCrossDocumentDirectives };
|
|
@@ -77,7 +77,7 @@ function validateFlowContentIndent(cst, state, parentBlockColumn) {
|
|
|
77
77
|
i++;
|
|
78
78
|
continue;
|
|
79
79
|
}
|
|
80
|
-
if (i - lineStart <= parentBlockColumn) {
|
|
80
|
+
if (i - lineStart <= parentBlockColumn && !isClosersOnly(text, i, end)) {
|
|
81
81
|
state.errors.push({
|
|
82
82
|
code: "InvalidIndentation",
|
|
83
83
|
message: "Flow content continuation line must be indented past the parent block",
|
|
@@ -91,6 +91,26 @@ function validateFlowContentIndent(cst, state, parentBlockColumn) {
|
|
|
91
91
|
i++;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* True when everything from `start` to the end of that line (bounded by `end`)
|
|
96
|
+
* is flow closing punctuation — `}`, `]`, separating commas and whitespace.
|
|
97
|
+
* Such a line closes the collection rather than carrying content, so the
|
|
98
|
+
* continuation-indent floor does not apply to it.
|
|
99
|
+
*/
|
|
100
|
+
function isClosersOnly(text, start, end) {
|
|
101
|
+
let sawCloser = false;
|
|
102
|
+
for (let i = start; i < end && i < text.length; i++) {
|
|
103
|
+
const ch = text[i];
|
|
104
|
+
if (ch === "\n") break;
|
|
105
|
+
if (ch === "}" || ch === "]") {
|
|
106
|
+
sawCloser = true;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (ch === "," || ch === " " || ch === " " || ch === "\r") continue;
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
return sawCloser;
|
|
113
|
+
}
|
|
94
114
|
function composeFlowMap(cst, state, meta, parentBlockColumn) {
|
|
95
115
|
if (!enterNesting(state, cst)) return new YamlMap({
|
|
96
116
|
items: [],
|
package/internal/stringifier.js
CHANGED
|
@@ -487,10 +487,7 @@ function normalizeNodeTags(node, tagMap) {
|
|
|
487
487
|
if (node instanceof YamlMap) return new YamlMap({
|
|
488
488
|
items: node.items.map((pair) => new YamlPair({
|
|
489
489
|
key: normalizeNodeTags(pair.key, tagMap),
|
|
490
|
-
value: pair.value ? normalizeNodeTags(pair.value, tagMap) : null
|
|
491
|
-
...pair.commentBefore !== void 0 ? { commentBefore: pair.commentBefore } : {},
|
|
492
|
-
...pair.comment !== void 0 ? { comment: pair.comment } : {},
|
|
493
|
-
...pair.spaceBefore !== void 0 ? { spaceBefore: pair.spaceBefore } : {}
|
|
490
|
+
value: pair.value ? normalizeNodeTags(pair.value, tagMap) : null
|
|
494
491
|
})),
|
|
495
492
|
style: node.style,
|
|
496
493
|
...node.tag ? { tag: normalizeTag(node.tag, tagMap) } : {},
|
|
@@ -643,6 +640,63 @@ function unescapeCommentLine(line) {
|
|
|
643
640
|
function commentBlockLines(comment) {
|
|
644
641
|
return comment.split("\n").map((l) => l === "" ? "" : `#${unescapeCommentLine(l)}`);
|
|
645
642
|
}
|
|
643
|
+
/**
|
|
644
|
+
* Splice a trailing comment onto the FIRST line of an already-rendered node —
|
|
645
|
+
* for a block scalar, the header line, which is the one line of the scalar
|
|
646
|
+
* that legally carries a `#` comment.
|
|
647
|
+
*/
|
|
648
|
+
function withHeaderComment(rendered, comment) {
|
|
649
|
+
const nl = rendered.indexOf("\n");
|
|
650
|
+
return nl === -1 ? `${rendered}${renderTrailingComment(comment)}` : `${rendered.slice(0, nl)}${renderTrailingComment(comment)}${rendered.slice(nl)}`;
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* The leading comment block of a mapping entry. Under the node-level comment
|
|
654
|
+
* model it lives on the entry's KEY node, not on the pair.
|
|
655
|
+
*/
|
|
656
|
+
function pairLeading(pair) {
|
|
657
|
+
return pair.key.commentBefore;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* The trailing comment of a mapping entry: it belongs to whichever node owns
|
|
661
|
+
* the entry's last line — the value when there is one, otherwise the key. For
|
|
662
|
+
* a block-scalar value that line is the HEADER line, which is why a block
|
|
663
|
+
* scalar's header comment needs no separate concept.
|
|
664
|
+
*/
|
|
665
|
+
function pairTrailing(pair) {
|
|
666
|
+
return (pair.value ?? pair.key).comment;
|
|
667
|
+
}
|
|
668
|
+
/** True when a blank line preceded the entry (recorded on its KEY node). */
|
|
669
|
+
function pairSpaceBefore(pair) {
|
|
670
|
+
return pair.key.spaceBefore;
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* The trailing comment to place on an entry's own line, given how its value
|
|
674
|
+
* actually rendered.
|
|
675
|
+
*
|
|
676
|
+
* A collection's `comment` field carries its TERMINAL comment run — the
|
|
677
|
+
* own-line comments after its last entry — and the collection emits those
|
|
678
|
+
* itself, after that last entry. Only when the collection renders on a single
|
|
679
|
+
* line (inline flow, `a: {b: 1} # t`) is its `comment` the entry's trailing
|
|
680
|
+
* comment. Reading it unconditionally emits the same comment twice: once on
|
|
681
|
+
* the entry's line and again as the terminal run.
|
|
682
|
+
*/
|
|
683
|
+
function entryTrailing(pair, ctx) {
|
|
684
|
+
if (pair.key.comment !== void 0) return pair.key.comment;
|
|
685
|
+
const owner = pair.value ?? pair.key;
|
|
686
|
+
if (rendersAcrossLines(owner, ctx)) return void 0;
|
|
687
|
+
return owner.comment;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* True when `node` renders across more than one line, so its own `comment` is
|
|
691
|
+
* a terminal run rather than something that can sit on the entry's line.
|
|
692
|
+
*/
|
|
693
|
+
function rendersAcrossLines(node, ctx) {
|
|
694
|
+
if (!(node instanceof YamlMap || node instanceof YamlSeq)) return false;
|
|
695
|
+
if (node.items.length === 0) return false;
|
|
696
|
+
if ((ctx.forceDefaultStyles ? ctx.defaultCollectionStyle : node.style ?? ctx.defaultCollectionStyle) === "block" || node.sourceMultiline === true) return true;
|
|
697
|
+
if (ctx.forceDefaultStyles) return false;
|
|
698
|
+
return node instanceof YamlMap ? node.items.some((p) => pairLeading(p) !== void 0 || (p.value ?? p.key).comment !== void 0 || pairSpaceBefore(p) === true) : node.items.some((item) => item.commentBefore !== void 0 || item.comment !== void 0 || item.spaceBefore === true);
|
|
699
|
+
}
|
|
646
700
|
/** Render a trailing comment for appending after content on the same line. */
|
|
647
701
|
function renderTrailingComment(comment) {
|
|
648
702
|
return ` #${comment.split("\n").map(unescapeCommentLine).join(" ")}`;
|
|
@@ -664,6 +718,14 @@ function pushExplicitKeyValueLines(lines, valNode, ctx, depth, pad) {
|
|
|
664
718
|
return lines.length - 1;
|
|
665
719
|
}
|
|
666
720
|
const valLines = stringifyNodeLines(valNode, ctx, depth + 1);
|
|
721
|
+
const valueLeading = !ctx.forceDefaultStyles ? valNode.commentBefore : void 0;
|
|
722
|
+
if (valueLeading !== void 0) {
|
|
723
|
+
const colonIdx = lines.length;
|
|
724
|
+
lines.push(":");
|
|
725
|
+
for (const cl of commentBlockLines(valueLeading)) lines.push(cl === "" ? "" : `${pad}${cl}`);
|
|
726
|
+
for (const vl of valLines) lines.push(vl === "" ? "" : `${pad}${vl}`);
|
|
727
|
+
return colonIdx;
|
|
728
|
+
}
|
|
667
729
|
if (valLines.length === 1) {
|
|
668
730
|
lines.push(`: ${valLines[0]}`);
|
|
669
731
|
return lines.length - 1;
|
|
@@ -674,15 +736,9 @@ function pushExplicitKeyValueLines(lines, valNode, ctx, depth, pad) {
|
|
|
674
736
|
const isInlineQuoted = valNode instanceof YamlScalar && (firstStripped.startsWith("'") || firstStripped.startsWith("\""));
|
|
675
737
|
if (isBlockScalarHeader || isInlineQuoted) {
|
|
676
738
|
const headerIdx = lines.length;
|
|
677
|
-
if (isBlockScalarHeader) {
|
|
678
|
-
const scalarComment = !ctx.forceDefaultStyles && valNode instanceof YamlScalar ? valNode.comment : void 0;
|
|
679
|
-
lines.push(scalarComment !== void 0 ? `: ${first}${renderTrailingComment(scalarComment)}` : `: ${first}`);
|
|
680
|
-
for (let v = 1; v < valLines.length; v++) lines.push(valLines[v]);
|
|
681
|
-
return scalarComment !== void 0 ? -1 : headerIdx;
|
|
682
|
-
}
|
|
683
739
|
lines.push(`: ${first}`);
|
|
684
740
|
for (let v = 1; v < valLines.length; v++) lines.push(valLines[v]);
|
|
685
|
-
return lines.length - 1;
|
|
741
|
+
return isBlockScalarHeader ? headerIdx : lines.length - 1;
|
|
686
742
|
}
|
|
687
743
|
if (first.startsWith("-")) {
|
|
688
744
|
lines.push(`: ${first}`);
|
|
@@ -718,17 +774,19 @@ function stringifyMapNodeLines(node, ctx, depth) {
|
|
|
718
774
|
}
|
|
719
775
|
if (style === "flow") {
|
|
720
776
|
const flowPrefix = buildMetadataPrefix(node.tag, node.anchor);
|
|
721
|
-
if (!ctx.forceDefaultStyles && (
|
|
777
|
+
if (!ctx.forceDefaultStyles && (items.some((p) => pairLeading(p) !== void 0 || pairTrailing(p) !== void 0 || pairSpaceBefore(p) === true) || node.comment !== void 0 && node.sourceMultiline === true)) {
|
|
722
778
|
const flowPad = " ".repeat(ctx.indent);
|
|
723
779
|
const flowLines = [flowPrefix ? `${flowPrefix} {` : "{"];
|
|
724
780
|
for (let fi = 0; fi < items.length; fi++) {
|
|
725
781
|
const pair = items[fi];
|
|
726
|
-
if (pair
|
|
727
|
-
|
|
782
|
+
if (pairSpaceBefore(pair) === true) flowLines.push("");
|
|
783
|
+
const flowLeading = pairLeading(pair);
|
|
784
|
+
if (flowLeading !== void 0) for (const cl of commentBlockLines(flowLeading)) flowLines.push(cl === "" ? "" : `${flowPad}${cl}`);
|
|
728
785
|
const keyStr = pair.key ? stringifyMappingKeyLines(pair.key, ctx, depth + 1).join(" ") : "null";
|
|
729
786
|
const valStr = pair.value ? stringifyNodeLines(pair.value, ctx, depth + 1).join(" ") : "null";
|
|
730
787
|
const comma = fi < items.length - 1 ? "," : "";
|
|
731
|
-
const
|
|
788
|
+
const flowTrailing = pairTrailing(pair);
|
|
789
|
+
const trailing = flowTrailing !== void 0 ? renderTrailingComment(flowTrailing) : "";
|
|
732
790
|
flowLines.push(`${flowPad}${keyStr}: ${valStr}${comma}${trailing}`);
|
|
733
791
|
}
|
|
734
792
|
if (node.comment !== void 0) for (const cl of commentBlockLines(node.comment)) flowLines.push(cl === "" ? "" : `${flowPad}${cl}`);
|
|
@@ -750,10 +808,13 @@ function stringifyMapNodeLines(node, ctx, depth) {
|
|
|
750
808
|
lines[idx] = `${lines[idx]}${renderTrailingComment(comment)}`;
|
|
751
809
|
};
|
|
752
810
|
/** A value node's own leading comment block, or undefined. */
|
|
753
|
-
const valueLeading = (valNode) => emitComments
|
|
811
|
+
const valueLeading = (valNode) => emitComments ? valNode.commentBefore : void 0;
|
|
754
812
|
for (const pair of items) {
|
|
755
|
-
if (emitComments && pair
|
|
756
|
-
|
|
813
|
+
if (emitComments && pairSpaceBefore(pair) === true) lines.push("");
|
|
814
|
+
const leading = emitComments ? pairLeading(pair) : void 0;
|
|
815
|
+
const keyOwnComment = emitComments ? pair.key.comment : void 0;
|
|
816
|
+
const valueOwnComment = emitComments && pair.value !== null ? pair.value.comment : void 0;
|
|
817
|
+
if (leading !== void 0) for (const cl of commentBlockLines(leading)) lines.push(cl);
|
|
757
818
|
const keyIsScalarWithNewline = pair.key instanceof YamlScalar && (typeof pair.key.value === "string" && pair.key.value.includes("\n") || pair.key.style === "block-literal" || pair.key.style === "block-folded");
|
|
758
819
|
if ((pair.key instanceof YamlMap || pair.key instanceof YamlSeq) && pair.key.items.length > 0 || keyIsScalarWithNewline) {
|
|
759
820
|
const keyLines = stringifyNodeLines(pair.key, ctx, depth + 1);
|
|
@@ -765,7 +826,7 @@ function stringifyMapNodeLines(node, ctx, depth) {
|
|
|
765
826
|
const contPad = firstIsMetaOnly || keyIsBlockScalar ? "" : EXPLICIT_COMPACT_PAD;
|
|
766
827
|
for (let k = 1; k < keyLines.length; k++) lines.push(`${contPad}${keyLines[k]}`);
|
|
767
828
|
const inlineIdx = pushExplicitKeyValueLines(lines, pair.value, ctx, depth, pad);
|
|
768
|
-
appendTrailing(inlineIdx >= 0 ? inlineIdx : keyLines.length === 1 ? explicitKeyLineIdx : -1, pair
|
|
829
|
+
appendTrailing(inlineIdx >= 0 ? inlineIdx : keyLines.length === 1 ? explicitKeyLineIdx : -1, entryTrailing(pair, ctx));
|
|
769
830
|
continue;
|
|
770
831
|
}
|
|
771
832
|
let resolvedKeyStr;
|
|
@@ -776,7 +837,7 @@ function stringifyMapNodeLines(node, ctx, depth) {
|
|
|
776
837
|
lines.push(`? ${keyStr}`);
|
|
777
838
|
const spillKeyLineIdx = lines.length - 1;
|
|
778
839
|
const inlineIdx = pushExplicitKeyValueLines(lines, pair.value, ctx, depth, pad);
|
|
779
|
-
appendTrailing(inlineIdx >= 0 ? inlineIdx : spillKeyLineIdx, pair
|
|
840
|
+
appendTrailing(inlineIdx >= 0 ? inlineIdx : spillKeyLineIdx, entryTrailing(pair, ctx));
|
|
780
841
|
continue;
|
|
781
842
|
}
|
|
782
843
|
const keyIsAnchoredOrTaggedEmpty = pair.key instanceof YamlScalar && pair.key.length === 0 && (pair.key.value === null || pair.key.value === void 0 || pair.key.value === "") && (pair.key.anchor !== void 0 || pair.key.tag !== void 0);
|
|
@@ -788,7 +849,7 @@ function stringifyMapNodeLines(node, ctx, depth) {
|
|
|
788
849
|
const isRootFlowMap = ctx.parentPosition === void 0 && node.style === "flow" && node.sourceMultiline === true;
|
|
789
850
|
if (ctx.forceDefaultStyles && isRootFlowMap && isPlainKey && keyIsNonEmpty) lines.push(`${keyStr}${sep} null`);
|
|
790
851
|
else lines.push(`${keyStr}${sep}`);
|
|
791
|
-
appendTrailing(lines.length - 1, pair
|
|
852
|
+
appendTrailing(lines.length - 1, entryTrailing(pair, ctx));
|
|
792
853
|
continue;
|
|
793
854
|
}
|
|
794
855
|
const valLines = stringifyNodeLines(valNode, {
|
|
@@ -800,7 +861,7 @@ function stringifyMapNodeLines(node, ctx, depth) {
|
|
|
800
861
|
const seqMeta = buildMetadataPrefix(valNode.tag, valNode.anchor);
|
|
801
862
|
const startIdx = seqMeta ? 1 : 0;
|
|
802
863
|
lines.push(seqMeta ? `${keyStr}${sep} ${seqMeta}` : `${keyStr}${sep}`);
|
|
803
|
-
appendTrailing(lines.length - 1, pair
|
|
864
|
+
appendTrailing(lines.length - 1, entryTrailing(pair, ctx));
|
|
804
865
|
if (valLeading !== void 0) for (const cl of commentBlockLines(valLeading)) lines.push(ctx.indentSequences ? `${pad}${cl}` : cl);
|
|
805
866
|
for (let i = startIdx; i < valLines.length; i++) {
|
|
806
867
|
const vl = valLines[i];
|
|
@@ -810,47 +871,57 @@ function stringifyMapNodeLines(node, ctx, depth) {
|
|
|
810
871
|
const mapMeta = buildMetadataPrefix(valNode.tag, valNode.anchor);
|
|
811
872
|
const startIdx = mapMeta ? 1 : 0;
|
|
812
873
|
lines.push(mapMeta ? `${keyStr}${sep} ${mapMeta}` : `${keyStr}${sep}`);
|
|
813
|
-
appendTrailing(lines.length - 1, pair
|
|
874
|
+
appendTrailing(lines.length - 1, entryTrailing(pair, ctx));
|
|
814
875
|
if (valLeading !== void 0) for (const cl of commentBlockLines(valLeading)) lines.push(`${pad}${cl}`);
|
|
815
876
|
for (let i = startIdx; i < valLines.length; i++) lines.push(valLines[i] === "" ? "" : `${pad}${valLines[i]}`);
|
|
816
|
-
} else if (valLines.length === 1 && valLeading !== void 0) {
|
|
877
|
+
} else if (valLines.length === 1 && (valLeading !== void 0 || keyOwnComment !== void 0)) {
|
|
817
878
|
lines.push(`${keyStr}${sep}`);
|
|
818
|
-
appendTrailing(lines.length - 1,
|
|
819
|
-
for (const cl of commentBlockLines(valLeading)) lines.push(`${pad}${cl}`);
|
|
879
|
+
appendTrailing(lines.length - 1, keyOwnComment);
|
|
880
|
+
if (valLeading !== void 0) for (const cl of commentBlockLines(valLeading)) lines.push(`${pad}${cl}`);
|
|
820
881
|
lines.push(valLines[0] === "" ? "" : `${pad}${valLines[0]}`);
|
|
882
|
+
appendTrailing(lines.length - 1, valueOwnComment);
|
|
821
883
|
} else if (valLines.length === 1) {
|
|
822
884
|
lines.push(valLines[0] === "" ? `${keyStr}${sep}` : `${keyStr}${sep} ${valLines[0]}`);
|
|
823
|
-
appendTrailing(lines.length - 1, pair
|
|
885
|
+
appendTrailing(lines.length - 1, entryTrailing(pair, ctx));
|
|
824
886
|
} else {
|
|
825
887
|
const first = valLines[0];
|
|
826
888
|
const firstStripped = stripScalarMetadataPrefix(first);
|
|
827
889
|
const isBlockScalarHeader = firstStripped.startsWith("|") || firstStripped.startsWith(">");
|
|
828
890
|
const isInlineQuoted = valNode instanceof YamlScalar && (firstStripped.startsWith("'") || firstStripped.startsWith("\""));
|
|
829
891
|
if (isBlockScalarHeader || isInlineQuoted) {
|
|
830
|
-
|
|
831
|
-
if (scalarComment !== void 0 && pair.comment !== void 0) {
|
|
892
|
+
if (valLeading !== void 0) {
|
|
832
893
|
lines.push(`${keyStr}${sep}`);
|
|
833
|
-
|
|
834
|
-
const
|
|
894
|
+
for (const cl of commentBlockLines(valLeading)) lines.push(cl === "" ? "" : `${pad}${cl}`);
|
|
895
|
+
const spilledIdx = lines.length;
|
|
835
896
|
lines.push(`${pad}${first}`);
|
|
836
897
|
for (let i = 1; i < valLines.length; i++) lines.push(valLines[i]);
|
|
837
|
-
appendTrailing(
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
898
|
+
appendTrailing(isBlockScalarHeader ? spilledIdx : lines.length - 1, entryTrailing(pair, ctx));
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
const keyComment = !ctx.forceDefaultStyles ? pair.key.comment : void 0;
|
|
902
|
+
const scalarComment = isBlockScalarHeader && valNode instanceof YamlScalar ? valNode.comment : void 0;
|
|
903
|
+
if (keyComment !== void 0 && scalarComment !== void 0) {
|
|
904
|
+
lines.push(`${keyStr}${sep}`);
|
|
905
|
+
appendTrailing(lines.length - 1, keyComment);
|
|
906
|
+
const spilledIdx = lines.length;
|
|
907
|
+
lines.push(`${pad}${first}`);
|
|
841
908
|
for (let i = 1; i < valLines.length; i++) lines.push(valLines[i]);
|
|
842
|
-
|
|
843
|
-
|
|
909
|
+
appendTrailing(spilledIdx, scalarComment);
|
|
910
|
+
continue;
|
|
844
911
|
}
|
|
912
|
+
const headerIdx = lines.length;
|
|
913
|
+
lines.push(`${keyStr}${sep} ${first}`);
|
|
914
|
+
for (let i = 1; i < valLines.length; i++) lines.push(valLines[i]);
|
|
915
|
+
appendTrailing(isBlockScalarHeader ? headerIdx : lines.length - 1, entryTrailing(pair, ctx));
|
|
845
916
|
} else {
|
|
846
917
|
const mapMeta = valNode instanceof YamlMap && (ctx.forceDefaultStyles ? ctx.defaultCollectionStyle : valNode.style ?? ctx.defaultCollectionStyle) === "block" ? buildMetadataPrefix(valNode.tag, valNode.anchor) : void 0;
|
|
847
918
|
if (mapMeta) {
|
|
848
919
|
lines.push(`${keyStr}${sep} ${mapMeta}`);
|
|
849
|
-
appendTrailing(lines.length - 1, pair
|
|
920
|
+
appendTrailing(lines.length - 1, entryTrailing(pair, ctx));
|
|
850
921
|
for (let i = 1; i < valLines.length; i++) lines.push(valLines[i] === "" ? "" : `${pad}${valLines[i]}`);
|
|
851
922
|
} else {
|
|
852
923
|
lines.push(`${keyStr}${sep}`);
|
|
853
|
-
appendTrailing(lines.length - 1, pair
|
|
924
|
+
appendTrailing(lines.length - 1, entryTrailing(pair, ctx));
|
|
854
925
|
if (valLeading !== void 0) for (const cl of commentBlockLines(valLeading)) lines.push(`${pad}${cl}`);
|
|
855
926
|
for (const vl of valLines) lines.push(vl === "" ? "" : `${pad}${vl}`);
|
|
856
927
|
}
|
|
@@ -906,13 +977,13 @@ function stringifySeqNodeLines(node, ctx, depth) {
|
|
|
906
977
|
}
|
|
907
978
|
const emitComments = !ctx.forceDefaultStyles;
|
|
908
979
|
const itemFields = (item) => {
|
|
909
|
-
if (!emitComments
|
|
980
|
+
if (!emitComments) return void 0;
|
|
910
981
|
if (item.commentBefore === void 0 && item.comment === void 0 && item.spaceBefore !== true) return void 0;
|
|
911
982
|
return item;
|
|
912
983
|
};
|
|
913
984
|
if (style === "flow") {
|
|
914
985
|
const flowPrefix = buildMetadataPrefix(node.tag, node.anchor);
|
|
915
|
-
if (emitComments && (
|
|
986
|
+
if (emitComments && (items.some((it) => itemFields(it) !== void 0) || node.comment !== void 0 && node.sourceMultiline === true)) {
|
|
916
987
|
const flowPad = " ".repeat(ctx.indent);
|
|
917
988
|
const flowLines = [flowPrefix ? `${flowPrefix} [` : "["];
|
|
918
989
|
for (let fi = 0; fi < items.length; fi++) {
|
|
@@ -998,7 +1069,7 @@ function stringifyDocument(doc, options) {
|
|
|
998
1069
|
const ctx = createContext(options);
|
|
999
1070
|
const finalNewline = options?.finalNewline ?? true;
|
|
1000
1071
|
let contents = doc.contents;
|
|
1001
|
-
const docCommentRaw = ctx.forceDefaultStyles ? void 0 : doc.
|
|
1072
|
+
const docCommentRaw = ctx.forceDefaultStyles ? void 0 : doc.commentBefore;
|
|
1002
1073
|
const docComment = docCommentRaw !== void 0 ? commentBlockLines(docCommentRaw).join("\n") : void 0;
|
|
1003
1074
|
if (ctx.forceDefaultStyles && contents) {
|
|
1004
1075
|
contents = stripNodeComments(contents);
|
|
@@ -1010,28 +1081,31 @@ function stringifyDocument(doc, options) {
|
|
|
1010
1081
|
if (doc.hasDocumentStart) return `---\n${doc.hasDocumentEnd ? "...\n" : ""}`;
|
|
1011
1082
|
return "";
|
|
1012
1083
|
}
|
|
1013
|
-
if (docComment !== void 0 || doc.
|
|
1084
|
+
if (docComment !== void 0 || doc.comment !== void 0 || doc.hasDocumentStart === true || doc.hasDocumentEnd === true) {
|
|
1014
1085
|
const parts = [];
|
|
1015
1086
|
if (docComment !== void 0) parts.push(`${docComment}\n`);
|
|
1016
1087
|
if (doc.hasDocumentStart) parts.push("---\n");
|
|
1017
1088
|
if (doc.hasDocumentEnd) parts.push("...\n");
|
|
1018
|
-
if (doc.
|
|
1089
|
+
if (doc.comment !== void 0) parts.push(`${commentBlockLines(doc.comment).join("\n")}\n`);
|
|
1019
1090
|
const out = parts.join("");
|
|
1020
1091
|
return finalNewline ? out : out.replace(/\n$/, "");
|
|
1021
1092
|
}
|
|
1022
1093
|
return finalNewline ? "null\n" : "null";
|
|
1023
1094
|
}
|
|
1024
1095
|
const result = stringifyNodeLines(contents, ctx, 0).join("\n");
|
|
1096
|
+
const rootHeaderComment = !ctx.forceDefaultStyles && contents instanceof YamlScalar && (contents.style === "block-literal" || contents.style === "block-folded") ? contents.comment : void 0;
|
|
1097
|
+
const rendered = rootHeaderComment === void 0 ? result : withHeaderComment(result, rootHeaderComment);
|
|
1025
1098
|
const needsTerminatorForKeepChomp = ctx.forceDefaultStyles && endsWithKeepChomp(result);
|
|
1026
1099
|
const needsTerminatorForAnchoredPlainScalar = ctx.forceDefaultStyles && doc.hasDocumentStart && contents instanceof YamlScalar && contents.style === "plain" && contents.anchor !== void 0 && !contents.tag;
|
|
1027
1100
|
const looksLikeDirectiveContinuation = contents instanceof YamlScalar && typeof contents.value === "string" && / %[A-Z]/.test(contents.value);
|
|
1028
1101
|
const needsTerminatorForMultilinePlainScalar = ctx.forceDefaultStyles && doc.hasDocumentStart && contents instanceof YamlScalar && contents.style === "plain" && contents.sourceMultiline === true && looksLikeDirectiveContinuation;
|
|
1029
1102
|
const needsTerminatorForDocStartTab = ctx.forceDefaultStyles && doc.hasDocumentStartTab === true;
|
|
1030
1103
|
const docEndMarker = doc.hasDocumentEnd || needsTerminatorForKeepChomp || needsTerminatorForAnchoredPlainScalar || needsTerminatorForMultilinePlainScalar || needsTerminatorForDocStartTab ? "...\n" : "";
|
|
1031
|
-
const docCommentAfter = ctx.forceDefaultStyles || doc.
|
|
1104
|
+
const docCommentAfter = ctx.forceDefaultStyles || doc.comment === void 0 ? void 0 : commentBlockLines(doc.comment).join("\n");
|
|
1032
1105
|
const docEndFull = docCommentAfter !== void 0 ? `${docEndMarker}${docCommentAfter}\n` : docEndMarker;
|
|
1033
|
-
const body = finalNewline || docEndFull !== "" ? `${
|
|
1106
|
+
const body = finalNewline || docEndFull !== "" ? `${rendered}\n` : rendered;
|
|
1034
1107
|
const docEnd = finalNewline || docEndFull === "" ? docEndFull : docEndFull.replace(/\n$/, "");
|
|
1108
|
+
const rootLeading = !ctx.forceDefaultStyles && contents !== null && contents.commentBefore !== void 0 ? `${commentBlockLines(contents.commentBefore).join("\n")}\n` : "";
|
|
1035
1109
|
if (doc.hasDocumentStart) {
|
|
1036
1110
|
const rootTag = contents && "tag" in contents ? contents.tag : void 0;
|
|
1037
1111
|
const rootAnchor = contents && "anchor" in contents ? contents.anchor : void 0;
|
|
@@ -1048,13 +1122,15 @@ function stringifyDocument(doc, options) {
|
|
|
1048
1122
|
if (bodyClean.startsWith(metaLinePrefix)) bodyClean = bodyClean.slice(metaLinePrefix.length);
|
|
1049
1123
|
else if (bodyClean.startsWith(metaInlinePrefix)) bodyClean = bodyClean.slice(metaInlinePrefix.length);
|
|
1050
1124
|
const docStart = `--- ${metaStr}`;
|
|
1051
|
-
const
|
|
1052
|
-
return docComment ? `${docComment}\n${docStart}${
|
|
1125
|
+
const metaBody = rootLeading === "" ? `${isCollection ? "\n" : " "}${bodyClean}` : `\n${rootLeading}${bodyClean}`;
|
|
1126
|
+
return docComment ? `${docComment}\n${docStart}${metaBody}${docEnd}` : `${docStart}${metaBody}${docEnd}`;
|
|
1053
1127
|
}
|
|
1054
|
-
if (isScalar) return docComment ? `${docComment}\n--- ${body}${docEnd}` : `--- ${body}${docEnd}`;
|
|
1055
|
-
|
|
1128
|
+
if (isScalar && rootLeading === "") return docComment ? `${docComment}\n--- ${body}${docEnd}` : `--- ${body}${docEnd}`;
|
|
1129
|
+
const afterMarker = `---\n${rootLeading}${body}${docEnd}`;
|
|
1130
|
+
return docComment ? `${docComment}\n${afterMarker}` : afterMarker;
|
|
1056
1131
|
}
|
|
1057
|
-
|
|
1132
|
+
const plain = `${rootLeading}${body}${docEnd}`;
|
|
1133
|
+
return docComment ? `${docComment}\n${plain}` : plain;
|
|
1058
1134
|
}
|
|
1059
1135
|
|
|
1060
1136
|
//#endregion
|