@speclynx/apidom-parser-adapter-yaml-1-2 6.0.6 → 6.1.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.
@@ -125,7 +125,13 @@ const applyComments = (elements, comments) => {
125
125
  const detectFlowPadding = node => {
126
126
  const firstChild = (node.children || [])[0];
127
127
  if (firstChild && typeof node.startCharacter === 'number' && typeof firstChild.startCharacter === 'number') {
128
- return firstChild.startCharacter - node.startCharacter > 1;
128
+ // a sequence item starts with its tag or anchor, which aren't part of its node's position
129
+ const {
130
+ tag,
131
+ anchor
132
+ } = firstChild;
133
+ const firstStart = Math.min(firstChild.startCharacter, tag?.startCharacter ?? Infinity, anchor?.startCharacter ?? Infinity);
134
+ return firstStart - node.startCharacter > 1;
129
135
  }
130
136
  return true;
131
137
  };
@@ -139,12 +145,25 @@ const buildYamlStyle = (node, ctx, extras) => {
139
145
  if (ctx.flowCollectionPadding !== null) {
140
146
  yamlStyle.flowCollectionPadding = ctx.flowCollectionPadding;
141
147
  }
148
+ if (ctx.indentSeq !== null) {
149
+ yamlStyle.indentSeq = ctx.indentSeq;
150
+ }
142
151
  if (node.comment) {
143
152
  yamlStyle.comment = node.comment;
144
153
  }
145
154
  if (node.commentBefore) {
146
155
  yamlStyle.commentBefore = node.commentBefore;
147
156
  }
157
+ // an untagged node has the non-specific tag '?' when it's a plain scalar and '!' otherwise
158
+ // (YAML 1.2 §6.9.1); an explicit '!' on any other node means the same as no tag
159
+ const tag = node.tag?.explicitName;
160
+ if (tag !== undefined && tag !== (node.style === YamlStyle.Plain ? '?' : '!')) {
161
+ yamlStyle.tag = tag;
162
+ }
163
+ // the anchor's name, without the & indicator
164
+ if (node.anchor) {
165
+ yamlStyle.anchor = node.anchor.name.slice(1);
166
+ }
148
167
  if (extras) {
149
168
  for (const [key, value] of Object.entries(extras)) {
150
169
  if (value !== undefined) yamlStyle[key] = value;
@@ -238,13 +257,35 @@ const transformChildren = (children, ctx) => {
238
257
 
239
258
  // stream comments that precede the first document, joined; null when there are none
240
259
  // (stream children are in source order, so everything before the document node qualifies)
241
- const collectPreDocumentComments = node => {
242
- const texts = [];
260
+ // stream comments around the first document: before it, on its last line and on the lines after it,
261
+ // up to the next document
262
+ const collectStreamComments = node => {
263
+ const comments = {
264
+ before: [],
265
+ inline: [],
266
+ after: []
267
+ };
268
+ let root;
269
+ const collect = comment => {
270
+ const text = stripCommentHash(comment.content);
271
+ if (!root) comments.before.push(text);else if (comment.startLine === root.endLine) comments.inline.push(text);else comments.after.push(text);
272
+ };
243
273
  for (const child of node.children) {
244
- if (isDocument(child)) break;
245
- if (isComment(child)) texts.push(stripCommentHash(child.content));
274
+ if (isDocument(child)) {
275
+ if (root) break;
276
+ // comments after an explicit document start (---) or before an explicit document end (...)
277
+ // are the document's own children, around its root node
278
+ const children = child.children || [];
279
+ const rootIndex = children.findIndex(c => !isComment(c));
280
+ if (rootIndex === -1) break;
281
+ children.slice(0, rootIndex).filter(isComment).forEach(collect);
282
+ root = children[rootIndex];
283
+ children.slice(rootIndex + 1).filter(isComment).forEach(collect);
284
+ } else if (isComment(child)) {
285
+ collect(child);
286
+ }
246
287
  }
247
- return texts.length > 0 ? texts.join('\n') : null;
288
+ return comments;
248
289
  };
249
290
 
250
291
  // Stream: Wraps transformed children in ParseResultElement
@@ -269,13 +310,32 @@ const transformStream = (node, ctx) => {
269
310
  const resultElement = elements[0];
270
311
  resultElement.classes.push('result');
271
312
 
272
- // stream comments before the first document belong above the result element;
273
- // capture them as commentBefore so style round-trips can re-emit them
313
+ // stream comments before the first document belong above the result element, ones on its last
314
+ // line to it and ones on later lines to the document; capture them so style round-trips can
315
+ // re-emit them
274
316
  if (ctx.style) {
275
- const preDocumentComments = collectPreDocumentComments(node);
276
- if (preDocumentComments !== null) {
317
+ const {
318
+ before,
319
+ inline,
320
+ after
321
+ } = collectStreamComments(node);
322
+ if (before.length > 0) {
277
323
  const existing = getYamlStyleProp(resultElement, 'commentBefore');
278
- setYamlStyleProp(resultElement, 'commentBefore', typeof existing === 'string' ? `${preDocumentComments}\n${existing}` : preDocumentComments);
324
+ const text = before.join('\n');
325
+ setYamlStyleProp(resultElement, 'commentBefore', typeof existing === 'string' ? `${text}\n${existing}` : text);
326
+ }
327
+ if (inline.length > 0 && typeof getYamlStyleProp(resultElement, 'comment') !== 'string') {
328
+ setYamlStyleProp(resultElement, 'comment', inline.join('\n'));
329
+ }
330
+ // comments promoted out of the root's last nested collection have no next sibling to precede
331
+ // and end the root, so they come before the stream comments after it
332
+ after.unshift(...ctx.promotedComments);
333
+ if (after.length > 0) {
334
+ setYamlStyleProp(resultElement, 'documentComment', after.join('\n'));
335
+ }
336
+ // line breaks other than LF, which the YAML library writes
337
+ if (node.lineBreak === '\r\n') {
338
+ setYamlStyleProp(resultElement, 'lineBreak', node.lineBreak);
279
339
  }
280
340
  }
281
341
  }
@@ -322,7 +382,9 @@ const transformCollection = (element, node, ctx) => {
322
382
  element._content = childElements; // eslint-disable-line @typescript-eslint/no-explicit-any
323
383
  applyComments(childElements, comments);
324
384
  element.style = buildYamlStyle(node, ctx, {
325
- comment: comments.trailing
385
+ comment: comments.trailing,
386
+ // a single pair written directly in a flow sequence (e.g. [a: 1])
387
+ singlePair: node.style === YamlStyle.SinglePair || undefined
326
388
  });
327
389
  if (comments.outOfScope.length > 0) {
328
390
  ctx.promotedComments.push(...comments.outOfScope);
@@ -343,12 +405,24 @@ const transformMapping = (node, ctx) => {
343
405
  // KeyValuePair: Transforms to MemberElement
344
406
  const transformKeyValuePair = (node, ctx) => {
345
407
  const element = new MemberElement();
408
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
409
+ const {
410
+ key,
411
+ value
412
+ } = node;
413
+ if (ctx.style && ctx.indentSeq === null && value?.type === 'sequence') {
414
+ const {
415
+ styleGroup,
416
+ startCharacter
417
+ } = value;
418
+ if (styleGroup === YamlStyleGroup.Block) {
419
+ ctx.indentSeq = startCharacter > node.startCharacter;
420
+ }
421
+ }
346
422
 
347
423
  // Transform key and value (these are dynamically defined properties)
348
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
349
- const keyResult = transform(node.key, ctx);
350
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
351
- const valueResult = transform(node.value, ctx);
424
+ const keyResult = transform(key, ctx);
425
+ const valueResult = transform(value, ctx);
352
426
 
353
427
  // @ts-ignore
354
428
  element.content.key = keyResult;
@@ -378,8 +452,11 @@ const transformSequence = (node, ctx) => {
378
452
  const transformScalar = (node, ctx) => {
379
453
  const element = ctx.namespace.toElement(node.content);
380
454
 
381
- // Translate style information about empty nodes
382
- if (node.content === '' && node.style === YamlStyle.Plain) {
455
+ // Translate style information about empty nodes; an empty node (YAML 1.2 §7.2) is a null unless
456
+ // a tag other than the null tag makes it something else (e.g. !!str makes it an empty string)
457
+ const tag = node.tag?.explicitName;
458
+ const isEmptyNode = node.content === '' && node.style === YamlStyle.Plain && [undefined, '?', '!!null', '!<tag:yaml.org,2002:null>'].includes(tag);
459
+ if (isEmptyNode) {
383
460
  element.classes.push('yaml-e-node');
384
461
  element.classes.push('yaml-e-scalar');
385
462
  }
@@ -390,6 +467,9 @@ const transformScalar = (node, ctx) => {
390
467
  if (node.rawContent !== undefined) {
391
468
  extras.rawContent = node.rawContent;
392
469
  }
470
+ if (node.alias !== undefined) {
471
+ extras.alias = node.alias;
472
+ }
393
473
  element.style = buildYamlStyle(node, ctx, extras);
394
474
  }
395
475
  maybeAddSourceMap(node, element, ctx);
@@ -467,6 +547,7 @@ export const transformYamlAstToApiDOM = (yamlAst, {
467
547
  processedDocumentCount: 0,
468
548
  indent: 2,
469
549
  flowCollectionPadding: null,
550
+ indentSeq: null,
470
551
  promotedComments: []
471
552
  };
472
553
  const rootNode = yamlAst.rootNode;
@@ -15,11 +15,27 @@ class ReferenceManager {
15
15
  // Anchor registration - currently a no-op as aliases resolve to scalars
16
16
  }
17
17
  resolveAlias(alias) {
18
- return new _YamlScalar.default({
18
+ const {
19
+ startLine,
20
+ startCharacter,
21
+ startOffset,
22
+ endLine,
23
+ endCharacter,
24
+ endOffset
25
+ } = alias;
26
+ const scalar = new _YamlScalar.default({
19
27
  content: alias.content,
28
+ startLine,
29
+ startCharacter,
30
+ startOffset,
31
+ endLine,
32
+ endCharacter,
33
+ endOffset,
20
34
  style: _YamlStyle.YamlStyle.Plain,
21
35
  styleGroup: _YamlStyle.YamlStyleGroup.Flow
22
36
  });
37
+ scalar.alias = alias.content.slice(1);
38
+ return scalar;
23
39
  }
24
40
  }
25
41
  var _default = exports.default = ReferenceManager;
@@ -8,11 +8,27 @@ class ReferenceManager {
8
8
  // Anchor registration - currently a no-op as aliases resolve to scalars
9
9
  }
10
10
  resolveAlias(alias) {
11
- return new YamlScalar({
11
+ const {
12
+ startLine,
13
+ startCharacter,
14
+ startOffset,
15
+ endLine,
16
+ endCharacter,
17
+ endOffset
18
+ } = alias;
19
+ const scalar = new YamlScalar({
12
20
  content: alias.content,
21
+ startLine,
22
+ startCharacter,
23
+ startOffset,
24
+ endLine,
25
+ endCharacter,
26
+ endOffset,
13
27
  style: YamlStyle.Plain,
14
28
  styleGroup: YamlStyleGroup.Flow
15
29
  });
30
+ scalar.alias = alias.content.slice(1);
31
+ return scalar;
16
32
  }
17
33
  }
18
34
  export default ReferenceManager;
@@ -17,6 +17,9 @@ class YamlScalar extends _YamlNode.default {
17
17
  static type = 'scalar';
18
18
  content;
19
19
  rawContent;
20
+
21
+ // the name of the alias (without the * indicator) the scalar was resolved from
22
+ alias;
20
23
  constructor({
21
24
  content,
22
25
  ...rest
@@ -9,6 +9,9 @@ class YamlScalar extends YamlNode {
9
9
  static type = 'scalar';
10
10
  content;
11
11
  rawContent;
12
+
13
+ // the name of the alias (without the * indicator) the scalar was resolved from
14
+ alias;
12
15
  constructor({
13
16
  content,
14
17
  ...rest
@@ -12,6 +12,9 @@ var _predicates = require("./predicates.cjs");
12
12
  */
13
13
  class YamlStream extends _Node.default {
14
14
  static type = 'stream';
15
+
16
+ // the line break the stream's first line ends with
17
+ lineBreak;
15
18
  }
16
19
  Object.defineProperty(YamlStream.prototype, 'content', {
17
20
  get() {
@@ -5,6 +5,9 @@ import { isDocument, isComment } from "./predicates.mjs";
5
5
  */
6
6
  class YamlStream extends Node {
7
7
  static type = 'stream';
8
+
9
+ // the line break the stream's first line ends with
10
+ lineBreak;
8
11
  }
9
12
  Object.defineProperty(YamlStream.prototype, 'content', {
10
13
  get() {
@@ -6,15 +6,16 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.formatFlowSingleQuoted = exports.formatFlowPlain = exports.formatFlowDoubleQuoted = exports.formatBlockLiteral = exports.formatBlockFolded = void 0;
7
7
  var _ramda = require("ramda");
8
8
  var _ramdaAdjunct = require("ramda-adjunct");
9
- var _unraw = require("unraw");
10
9
  /**
11
10
  * Helpers.
12
11
  */
13
12
 
14
- const blockStyleRegExp = /^(?<style>[|>])(?<chomping>[+-]?)(?<indentation>[0-9]*)\s/;
13
+ // indentation and chomping indicators may appear in either order (YAML 1.2 §8.1.1)
14
+ const blockStyleRegExp = /^(?<style>[|>])(?<indicators>[+-]?[0-9]?|[0-9][+-])(?:\s|$)/;
15
15
  const getIndentationIndicator = content => {
16
16
  const matches = content.match(blockStyleRegExp);
17
- const indicator = (0, _ramda.pathOr)('', ['groups', 'indentation'], matches);
17
+ const indicators = (0, _ramda.pathOr)('', ['groups', 'indicators'], matches);
18
+ const indicator = indicators.replace(/[+-]/, '');
18
19
  return (0, _ramdaAdjunct.isEmptyString)(indicator) ? undefined : parseInt(indicator, 10);
19
20
  };
20
21
  const getIndentation = content => {
@@ -25,30 +26,72 @@ const getIndentation = content => {
25
26
  return (0, _ramdaAdjunct.repeatStr)(' ', explicitIndentationIndicator);
26
27
  }
27
28
 
28
- // we assume indentation indicator from first line
29
- const firstLine = (0, _ramda.pathOr)('', [1], content.split('\n'));
29
+ // we assume indentation indicator from first non-empty line (YAML 1.2 §8.1.1.1);
30
+ // with no non-empty line, the first line is used so that its spaces are still stripped
31
+ const lines = (0, _ramda.tail)(content.split('\n'));
32
+ const firstLine = lines.find(line => !/^ *\r?$/.test(line)) ?? (0, _ramda.pathOr)('', [0], lines);
30
33
  const implicitIndentationIndicator = (0, _ramda.pathOr)(0, ['groups', 'indentation', 'length'], firstLine.match(/^(?<indentation>[ ]*)/));
31
34
  return (0, _ramdaAdjunct.repeatStr)(' ', implicitIndentationIndicator);
32
35
  };
36
+
37
+ // splits a block scalar body into deindented content lines and trailing lines (YAML 1.2 §8.1.1.2):
38
+ // trailing lines are the whitespace-only lines after the last content line, minus any up to and
39
+ // including the last one indented deeper than the block, which are content; without any content
40
+ // line, all lines are trailing
41
+ const splitBlockScalar = (content, chomping) => {
42
+ const indentation = getIndentation(content).length;
43
+ const normalized = normalizeLineBreaks(content);
44
+ const body = normalized.includes('\n') ? normalized.slice(normalized.indexOf('\n') + 1) : '';
45
+ const lines = body === '' ? [] : body.split('\n');
46
+ const deindent = line => line.slice(Math.min(indentation, line.search(/[^ ]|$/)));
47
+
48
+ // without keep chomping, trailing whitespace-only lines not indented deeper than the first
49
+ // content line don't belong to the block scalar
50
+ const firstContent = lines.find(line => /[^ ]/.test(line));
51
+ if (chomping !== '+' && firstContent !== undefined) {
52
+ const contentIndentation = firstContent.search(/[^ ]/);
53
+ while (/^ *$/.test(lines.at(-1)) && lines.at(-1).length <= contentIndentation) lines.pop();
54
+ }
55
+ let chompStart = lines.length;
56
+ while (chompStart > 0 && /^ *$/.test(lines[chompStart - 1])) chompStart -= 1;
57
+ if (chompStart === 0) return {
58
+ lines: [],
59
+ trailing: lines.map(deindent)
60
+ };
61
+ for (let i = lines.length - 1; i >= chompStart; i -= 1) {
62
+ if (lines[i].length > indentation) {
63
+ chompStart = i + 1;
64
+ break;
65
+ }
66
+ }
67
+ return {
68
+ lines: lines.slice(0, chompStart).map(deindent),
69
+ trailing: lines.slice(chompStart).map(deindent)
70
+ };
71
+ };
33
72
  const getChompingIndicator = content => {
34
73
  const matches = content.match(blockStyleRegExp);
35
- const indicator = (0, _ramda.pathOr)('', ['groups', 'chomping'], matches);
74
+ const indicators = (0, _ramda.pathOr)('', ['groups', 'indicators'], matches);
75
+ const indicator = indicators.replace(/[0-9]/, '');
36
76
  return (0, _ramdaAdjunct.isEmptyString)(indicator) ? undefined : indicator;
37
77
  };
38
- const chomp = (indicator, content) => {
39
- // clip (single newline at end)
40
- if ((0, _ramdaAdjunct.isUndefined)(indicator)) {
41
- return `${(0, _ramdaAdjunct.trimEnd)(content)}\n`;
78
+ const chomp = (indicator, value, {
79
+ lines,
80
+ trailing
81
+ }) => {
82
+ // a block without content is empty, unless keep chomping preserves its line breaks
83
+ if (lines.length === 0) {
84
+ return indicator === '+' && trailing.length > 0 ? '\n'.repeat(Math.max(1, trailing.length - 1)) : '';
42
85
  }
43
86
  // strip (no newline at end)
44
- if (indicator === '-') {
45
- return (0, _ramdaAdjunct.trimEnd)(content);
46
- }
47
- // keep (all newlines from end)
87
+ if (indicator === '-') return value;
88
+ // keep (trailing lines and a newline at end)
48
89
  if (indicator === '+') {
49
- return content;
90
+ const kept = value + trailing.map(line => `\n${line}`).join('');
91
+ return kept.endsWith('\n') ? kept : `${kept}\n`;
50
92
  }
51
- return content;
93
+ // clip (single newline at end)
94
+ return `${value}\n`;
52
95
  };
53
96
 
54
97
  /**
@@ -58,56 +101,108 @@ const chomp = (indicator, content) => {
58
101
  // @ts-ignore
59
102
  const normalizeLineBreaks = val => val.replace(/\r\n/g, '\n');
60
103
 
61
- // prevent escaped line breaks from being converted to a space
62
- const preventLineBreakCollapseToSpace = val => val.replace(/\\\n\s*/g, '');
63
-
64
- // collapse line breaks into spaces
65
- const collapseLineBreakToSpace = val => {
66
- /**
67
- * Safari doesn't support negative lookbehind, thus we use mimicking technique:
68
- *
69
- * - https://blog.stevenlevithan.com/archives/mimic-lookbehind-javascript
70
- *
71
- * Ideally we want to use following replace, but that's not currently possible:
72
- *
73
- * .replace(/[^\n]\n([^\n]+)/g, (match: string, p1: string) => ` ${p1.trimLeft()}`)
74
- */
75
- return val.replace(/(\n)?\n([^\n]+)/g, (match, p1, p2) => p1 ? match : ` ${p2.trimStart()}`).replace(/[\n]{2}/g, '\n');
104
+ // folds the line breaks of a flow scalar (YAML 1.2 §6.5, §7.3):
105
+ // white space around a line break is dropped, a single line break becomes a space and every
106
+ // further one a line break; with escapes (double-quoted style), escape sequences are left for
107
+ // unescaping and an escaped line break is dropped together with the next line's indentation
108
+ const foldFlowLines = (0, _ramda.curry)((escapes, text) => text.replace(
109
+ // a white space run not followed by a line break is consumed whole, which keeps the search
110
+ // linear instead of rescanning the run from every position in it
111
+ escapes ? /\\(?:\n[ \t]*|[^])|[ \t]*\n(?:[ \t]*\n)*[ \t]*|[ \t]+/g : /[ \t]*\n(?:[ \t]*\n)*[ \t]*|[ \t]+/g, match => {
112
+ if (match.startsWith('\\')) return match[1] === '\n' ? '' : match;
113
+ if (!match.includes('\n')) return match;
114
+ const lineBreaks = match.split('\n').length - 1;
115
+ return lineBreaks === 1 ? ' ' : '\n'.repeat(lineBreaks - 1);
116
+ }));
117
+
118
+ // folds the line break between two lines into a space; empty lines between them stay as line
119
+ // breaks, and a line break next to a more-indented line isn't folded (YAML 1.2 §6.5)
120
+ const foldLines = text => {
121
+ const parts = text.split(/\n(\n*)/); // lines alternating with the empty lines between them
122
+ let folded = parts[0];
123
+ for (let i = 1; i < parts.length; i += 2) {
124
+ const moreIndented = /^[ \t]/.test(parts[i - 1]) || /^[ \t]/.test(parts[i + 1]);
125
+ folded += (moreIndented ? `\n${parts[i]}` : parts[i] || ' ') + parts[i + 1];
126
+ }
127
+ return folded;
128
+ };
129
+
130
+ // escaped characters of YAML 1.2 §5.7, productions [42] to [58]
131
+ const escapedCharacters = {
132
+ '0': '\u0000',
133
+ // null
134
+ a: '\u0007',
135
+ // bell
136
+ b: '\u0008',
137
+ // backspace
138
+ t: '\u0009',
139
+ // horizontal tab
140
+ '\t': '\u0009',
141
+ // horizontal tab
142
+ n: '\u000a',
143
+ // line feed
144
+ v: '\u000b',
145
+ // vertical tab
146
+ f: '\u000c',
147
+ // form feed
148
+ r: '\u000d',
149
+ // carriage return
150
+ e: '\u001b',
151
+ // escape
152
+ ' ': '\u0020',
153
+ // space
154
+ '"': '\u0022',
155
+ // double quote
156
+ '/': '\u002f',
157
+ // slash
158
+ '\\': '\u005c',
159
+ // backslash
160
+ N: '\u0085',
161
+ // next line
162
+ _: '\u00a0',
163
+ // non-breaking space
164
+ L: '\u2028',
165
+ // line separator
166
+ P: '\u2029' // paragraph separator
76
167
  };
168
+
169
+ // unescapes the escape sequences of a double-quoted scalar (YAML 1.2 §5.7); an escape sequence
170
+ // outside of the spec is kept as-is
171
+ const unescapeDoubleQuoted = text => text.replace(/\\(?:x([0-9a-fA-F]{2})|u([0-9a-fA-F]{4})|U([0-9a-fA-F]{8})|([^]))/g, (match, x, u, U, char) => {
172
+ const hex = x ?? u ?? U;
173
+ if (hex === undefined) return escapedCharacters[char] ?? match;
174
+ const codePoint = parseInt(hex, 16);
175
+ return codePoint <= 0x10ffff ? String.fromCodePoint(codePoint) : match;
176
+ });
77
177
  const removeQuotes = (0, _ramda.curry)((quoteType, val) => val.replace(new RegExp(`^${quoteType}`), '').replace(new RegExp(`${quoteType}$`), ''));
78
178
 
79
179
  /**
80
180
  * Formats Flow Scalar Plain style.
81
181
  * https://yaml.org/spec/1.2/spec.html#id2788859
82
182
  */
83
- const formatFlowPlain = exports.formatFlowPlain = (0, _ramda.pipe)(normalizeLineBreaks, _ramda.trim, collapseLineBreakToSpace, (0, _ramda.split)('\n'), (0, _ramda.map)(_ramdaAdjunct.trimStart), (0, _ramda.join)('\n'));
183
+ const formatFlowPlain = exports.formatFlowPlain = (0, _ramda.pipe)(normalizeLineBreaks, _ramda.trim, foldFlowLines(false));
84
184
 
85
185
  /**
86
186
  * Formats Flow Scalar Single-Quoted style.
87
187
  * https://yaml.org/spec/1.2/spec.html#id2788097
88
188
  */
89
189
 
90
- const formatFlowSingleQuoted = exports.formatFlowSingleQuoted = (0, _ramda.pipe)(normalizeLineBreaks, _ramda.trim, collapseLineBreakToSpace, (0, _ramda.split)('\n'), (0, _ramda.map)(_ramdaAdjunct.trimStart), (0, _ramda.join)('\n'), removeQuotes("'"));
190
+ const formatFlowSingleQuoted = exports.formatFlowSingleQuoted = (0, _ramda.pipe)(normalizeLineBreaks, _ramda.trim, foldFlowLines(false), removeQuotes("'"), (0, _ramda.replace)(/''/g, "'"));
91
191
 
92
192
  /**
93
193
  * Formats Flow Scalar Double-Quoted style.
94
194
  * https://yaml.org/spec/1.2/spec.html#id2787109
95
195
  */
96
- const formatFlowDoubleQuoted = exports.formatFlowDoubleQuoted = (0, _ramda.pipe)(normalizeLineBreaks, _ramda.trim, preventLineBreakCollapseToSpace, collapseLineBreakToSpace, _unraw.unraw, (0, _ramda.split)('\n'), (0, _ramda.map)(_ramdaAdjunct.trimStart), (0, _ramda.join)('\n'), removeQuotes('"'));
196
+ const formatFlowDoubleQuoted = exports.formatFlowDoubleQuoted = (0, _ramda.pipe)(normalizeLineBreaks, _ramda.trim, foldFlowLines(true), unescapeDoubleQuoted, removeQuotes('"'));
97
197
 
98
198
  /**
99
199
  * Formats Block Scalar Literal style.
100
200
  * https://yaml.org/spec/1.2/spec.html#id2795688
101
201
  */
102
202
  const formatBlockLiteral = content => {
103
- const indentation = getIndentation(content);
104
- const chompingIndicator = getChompingIndicator(content);
105
- const normalized = normalizeLineBreaks(content);
106
- const lines = (0, _ramda.tail)(normalized.split('\n')); // first line only contains indicators
107
- const transducer = (0, _ramda.compose)((0, _ramda.map)((0, _ramdaAdjunct.trimCharsStart)(indentation)), (0, _ramda.map)((0, _ramdaAdjunct.concatRight)('\n')));
108
- // @ts-ignore
109
- const deindented = (0, _ramda.transduce)(transducer, _ramda.concat, '', lines);
110
- return chomp(chompingIndicator, deindented);
203
+ const chomping = getChompingIndicator(content);
204
+ const split = splitBlockScalar(content, chomping);
205
+ return chomp(chomping, split.lines.join('\n'), split);
111
206
  };
112
207
 
113
208
  /**
@@ -116,14 +211,12 @@ const formatBlockLiteral = content => {
116
211
  */
117
212
  exports.formatBlockLiteral = formatBlockLiteral;
118
213
  const formatBlockFolded = content => {
119
- const indentation = getIndentation(content);
120
- const chompingIndicator = getChompingIndicator(content);
121
- const normalized = normalizeLineBreaks(content);
122
- const lines = (0, _ramda.tail)(normalized.split('\n')); // first line only contains indicators
123
- const transducer = (0, _ramda.compose)((0, _ramda.map)((0, _ramdaAdjunct.trimCharsStart)(indentation)), (0, _ramda.map)((0, _ramdaAdjunct.concatRight)('\n')));
124
- // @ts-ignore
125
- const deindented = (0, _ramda.transduce)(transducer, _ramda.concat, '', lines);
126
- const collapsed = collapseLineBreakToSpace(deindented);
127
- return chomp(chompingIndicator, collapsed);
214
+ const chomping = getChompingIndicator(content);
215
+ const split = splitBlockScalar(content, chomping);
216
+ // leading line breaks are preserved as-is; folding applies only between content lines
217
+ const text = split.lines.join('\n');
218
+ const leadingLineBreaks = text.match(/^\n*/)[0];
219
+ const folded = leadingLineBreaks + foldLines(text.slice(leadingLineBreaks.length));
220
+ return chomp(chomping, folded, split);
128
221
  };
129
222
  exports.formatBlockFolded = formatBlockFolded;