@speclynx/apidom-parser-adapter-yaml-1-2 5.0.2 → 5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@speclynx/apidom-parser-adapter-yaml-1-2",
3
- "version": "5.0.2",
3
+ "version": "5.1.0",
4
4
  "description": "Parser adapter for parsing YAML documents into base namespace.",
5
5
  "keywords": [
6
6
  "apidom",
@@ -58,9 +58,9 @@
58
58
  "license": "Apache-2.0",
59
59
  "dependencies": {
60
60
  "@babel/runtime-corejs3": "^8.0.0",
61
- "@speclynx/apidom-core": "5.0.2",
62
- "@speclynx/apidom-datamodel": "5.0.2",
63
- "@speclynx/apidom-error": "5.0.2",
61
+ "@speclynx/apidom-core": "5.1.0",
62
+ "@speclynx/apidom-datamodel": "5.1.0",
63
+ "@speclynx/apidom-error": "5.1.0",
64
64
  "ramda": "~0.32.0",
65
65
  "ramda-adjunct": "^6.1.0",
66
66
  "unraw": "^3.0.0",
@@ -80,5 +80,5 @@
80
80
  "README.md",
81
81
  "CHANGELOG.md"
82
82
  ],
83
- "gitHead": "01792b567baea3a47cb08d09e478178aa04afda9"
83
+ "gitHead": "2976a9fe41a9eb3a2de387695c41d21603f57c98"
84
84
  }
@@ -103,8 +103,11 @@ const processChildren = (cursor, ctx, transformerMap) => {
103
103
  return results;
104
104
  };
105
105
 
106
- // strip leading '# ' (or bare '#') from comment text, keeping only the content
107
- const stripCommentHash = text => text.split('\n').map(line => line.replace(/^#\s?/, '')).join('\n');
106
+ // strip leading '#' from each line of comment text; the text after '#' is kept
107
+ // verbatim (including leading whitespace) so serializers can round-trip it exactly;
108
+ // a bare '#' becomes a single space — the yaml library's encoding for an empty
109
+ // comment, which its stringifier turns back into '#' (plain '' would be dropped)
110
+ const stripCommentHash = text => text.split('\n').map(line => line === '#' ? ' ' : line.replace(/^#/, '')).join('\n');
108
111
 
109
112
  // find the first YamlNode in a TransformResult (which may be an array from block_node)
110
113
  const findYamlNode = result => {
@@ -147,7 +150,10 @@ const processKeyValuePairChildren = (cursor, ctx, transformerMap) => {
147
150
  if (seenValue) {
148
151
  commentsAfterValue.push(commentText);
149
152
  } else if (seenKey) {
150
- commentsBetweenKeyValue.push(commentText);
153
+ commentsBetweenKeyValue.push({
154
+ text: commentText,
155
+ startLine: info.startPosition.row
156
+ });
151
157
  }
152
158
  continue;
153
159
  }
@@ -176,6 +182,35 @@ const processKeyValuePairChildren = (cursor, ctx, transformerMap) => {
176
182
  commentsAfterValue
177
183
  };
178
184
  };
185
+
186
+ // attach comments collected inside a pair: comments on the key's own line belong
187
+ // after the key (`key: # comment`), the rest before the value node; comments
188
+ // found after the value become the value's trailing comment
189
+ const attachPairComments = ({
190
+ key,
191
+ value,
192
+ commentsBetweenKeyValue,
193
+ commentsAfterValue
194
+ }) => {
195
+ if (commentsBetweenKeyValue.length > 0) {
196
+ const keyNode = findYamlNode(key);
197
+ const valueNode = findYamlNode(value);
198
+ const onKeyLine = commentsBetweenKeyValue.filter(comment => keyNode !== null && comment.startLine === keyNode.endLine);
199
+ const onOwnLine = commentsBetweenKeyValue.filter(comment => !onKeyLine.includes(comment));
200
+ if (onKeyLine.length > 0 && keyNode) {
201
+ keyNode.comment = onKeyLine.map(comment => comment.text).join('\n');
202
+ }
203
+ if (onOwnLine.length > 0 && valueNode) {
204
+ valueNode.commentBefore = onOwnLine.map(comment => comment.text).join('\n');
205
+ }
206
+ }
207
+ if (commentsAfterValue.length > 0) {
208
+ const valueNode = findYamlNode(value);
209
+ if (valueNode) {
210
+ valueNode.comment = commentsAfterValue.join('\n');
211
+ }
212
+ }
213
+ };
179
214
  const transform = (cursor, ctx, transformerMap, siblings = {}) => {
180
215
  const info = getCursorInfo(cursor);
181
216
 
@@ -347,13 +382,12 @@ const createTransformers = transformerMap => ({
347
382
  },
348
383
  block_mapping_pair(cursor, ctx) {
349
384
  const info = getCursorInfo(cursor);
385
+ const pairResult = processKeyValuePairChildren(cursor, ctx, transformerMap);
350
386
  const {
351
387
  key,
352
388
  value,
353
- errors,
354
- commentsBetweenKeyValue,
355
- commentsAfterValue
356
- } = processKeyValuePairChildren(cursor, ctx, transformerMap);
389
+ errors
390
+ } = pairResult;
357
391
  const children = [];
358
392
 
359
393
  // Handle empty key
@@ -399,21 +433,7 @@ const createTransformers = transformerMap => ({
399
433
  } else {
400
434
  children.push(value);
401
435
  }
402
-
403
- // attach comments found between key and value to the value node
404
- if (commentsBetweenKeyValue.length > 0) {
405
- const valueNode = findYamlNode(value);
406
- if (valueNode) {
407
- valueNode.commentBefore = commentsBetweenKeyValue.join('\n');
408
- }
409
- }
410
- // attach comments found after value to the value node
411
- if (commentsAfterValue.length > 0) {
412
- const valueNode = findYamlNode(value);
413
- if (valueNode) {
414
- valueNode.comment = commentsAfterValue.join('\n');
415
- }
416
- }
436
+ attachPairComments(pairResult);
417
437
  children.push(...errors);
418
438
  return new _YamlKeyValuePair.default({
419
439
  children: children.flat().filter(c => c !== null),
@@ -441,13 +461,12 @@ const createTransformers = transformerMap => ({
441
461
  },
442
462
  flow_pair(cursor, ctx) {
443
463
  const info = getCursorInfo(cursor);
464
+ const pairResult = processKeyValuePairChildren(cursor, ctx, transformerMap);
444
465
  const {
445
466
  key,
446
467
  value,
447
- errors,
448
- commentsBetweenKeyValue,
449
- commentsAfterValue
450
- } = processKeyValuePairChildren(cursor, ctx, transformerMap);
468
+ errors
469
+ } = pairResult;
451
470
  const children = [];
452
471
 
453
472
  // Handle empty key
@@ -493,21 +512,7 @@ const createTransformers = transformerMap => ({
493
512
  } else {
494
513
  children.push(value);
495
514
  }
496
-
497
- // attach comments found between key and value to the value node
498
- if (commentsBetweenKeyValue.length > 0) {
499
- const valueNode = findYamlNode(value);
500
- if (valueNode) {
501
- valueNode.commentBefore = commentsBetweenKeyValue.join('\n');
502
- }
503
- }
504
- // attach comments found after value to the value node
505
- if (commentsAfterValue.length > 0) {
506
- const valueNode = findYamlNode(value);
507
- if (valueNode) {
508
- valueNode.comment = commentsAfterValue.join('\n');
509
- }
510
- }
515
+ attachPairComments(pairResult);
511
516
  children.push(...errors);
512
517
  return new _YamlKeyValuePair.default({
513
518
  children: children.flat().filter(c => c !== null),
@@ -95,8 +95,11 @@ const processChildren = (cursor, ctx, transformerMap) => {
95
95
  return results;
96
96
  };
97
97
 
98
- // strip leading '# ' (or bare '#') from comment text, keeping only the content
99
- const stripCommentHash = text => text.split('\n').map(line => line.replace(/^#\s?/, '')).join('\n');
98
+ // strip leading '#' from each line of comment text; the text after '#' is kept
99
+ // verbatim (including leading whitespace) so serializers can round-trip it exactly;
100
+ // a bare '#' becomes a single space — the yaml library's encoding for an empty
101
+ // comment, which its stringifier turns back into '#' (plain '' would be dropped)
102
+ const stripCommentHash = text => text.split('\n').map(line => line === '#' ? ' ' : line.replace(/^#/, '')).join('\n');
100
103
 
101
104
  // find the first YamlNode in a TransformResult (which may be an array from block_node)
102
105
  const findYamlNode = result => {
@@ -139,7 +142,10 @@ const processKeyValuePairChildren = (cursor, ctx, transformerMap) => {
139
142
  if (seenValue) {
140
143
  commentsAfterValue.push(commentText);
141
144
  } else if (seenKey) {
142
- commentsBetweenKeyValue.push(commentText);
145
+ commentsBetweenKeyValue.push({
146
+ text: commentText,
147
+ startLine: info.startPosition.row
148
+ });
143
149
  }
144
150
  continue;
145
151
  }
@@ -168,6 +174,35 @@ const processKeyValuePairChildren = (cursor, ctx, transformerMap) => {
168
174
  commentsAfterValue
169
175
  };
170
176
  };
177
+
178
+ // attach comments collected inside a pair: comments on the key's own line belong
179
+ // after the key (`key: # comment`), the rest before the value node; comments
180
+ // found after the value become the value's trailing comment
181
+ const attachPairComments = ({
182
+ key,
183
+ value,
184
+ commentsBetweenKeyValue,
185
+ commentsAfterValue
186
+ }) => {
187
+ if (commentsBetweenKeyValue.length > 0) {
188
+ const keyNode = findYamlNode(key);
189
+ const valueNode = findYamlNode(value);
190
+ const onKeyLine = commentsBetweenKeyValue.filter(comment => keyNode !== null && comment.startLine === keyNode.endLine);
191
+ const onOwnLine = commentsBetweenKeyValue.filter(comment => !onKeyLine.includes(comment));
192
+ if (onKeyLine.length > 0 && keyNode) {
193
+ keyNode.comment = onKeyLine.map(comment => comment.text).join('\n');
194
+ }
195
+ if (onOwnLine.length > 0 && valueNode) {
196
+ valueNode.commentBefore = onOwnLine.map(comment => comment.text).join('\n');
197
+ }
198
+ }
199
+ if (commentsAfterValue.length > 0) {
200
+ const valueNode = findYamlNode(value);
201
+ if (valueNode) {
202
+ valueNode.comment = commentsAfterValue.join('\n');
203
+ }
204
+ }
205
+ };
171
206
  const transform = (cursor, ctx, transformerMap, siblings = {}) => {
172
207
  const info = getCursorInfo(cursor);
173
208
 
@@ -339,13 +374,12 @@ const createTransformers = transformerMap => ({
339
374
  },
340
375
  block_mapping_pair(cursor, ctx) {
341
376
  const info = getCursorInfo(cursor);
377
+ const pairResult = processKeyValuePairChildren(cursor, ctx, transformerMap);
342
378
  const {
343
379
  key,
344
380
  value,
345
- errors,
346
- commentsBetweenKeyValue,
347
- commentsAfterValue
348
- } = processKeyValuePairChildren(cursor, ctx, transformerMap);
381
+ errors
382
+ } = pairResult;
349
383
  const children = [];
350
384
 
351
385
  // Handle empty key
@@ -391,21 +425,7 @@ const createTransformers = transformerMap => ({
391
425
  } else {
392
426
  children.push(value);
393
427
  }
394
-
395
- // attach comments found between key and value to the value node
396
- if (commentsBetweenKeyValue.length > 0) {
397
- const valueNode = findYamlNode(value);
398
- if (valueNode) {
399
- valueNode.commentBefore = commentsBetweenKeyValue.join('\n');
400
- }
401
- }
402
- // attach comments found after value to the value node
403
- if (commentsAfterValue.length > 0) {
404
- const valueNode = findYamlNode(value);
405
- if (valueNode) {
406
- valueNode.comment = commentsAfterValue.join('\n');
407
- }
408
- }
428
+ attachPairComments(pairResult);
409
429
  children.push(...errors);
410
430
  return new YamlKeyValuePair({
411
431
  children: children.flat().filter(c => c !== null),
@@ -433,13 +453,12 @@ const createTransformers = transformerMap => ({
433
453
  },
434
454
  flow_pair(cursor, ctx) {
435
455
  const info = getCursorInfo(cursor);
456
+ const pairResult = processKeyValuePairChildren(cursor, ctx, transformerMap);
436
457
  const {
437
458
  key,
438
459
  value,
439
- errors,
440
- commentsBetweenKeyValue,
441
- commentsAfterValue
442
- } = processKeyValuePairChildren(cursor, ctx, transformerMap);
460
+ errors
461
+ } = pairResult;
443
462
  const children = [];
444
463
 
445
464
  // Handle empty key
@@ -485,21 +504,7 @@ const createTransformers = transformerMap => ({
485
504
  } else {
486
505
  children.push(value);
487
506
  }
488
-
489
- // attach comments found between key and value to the value node
490
- if (commentsBetweenKeyValue.length > 0) {
491
- const valueNode = findYamlNode(value);
492
- if (valueNode) {
493
- valueNode.commentBefore = commentsBetweenKeyValue.join('\n');
494
- }
495
- }
496
- // attach comments found after value to the value node
497
- if (commentsAfterValue.length > 0) {
498
- const valueNode = findYamlNode(value);
499
- if (valueNode) {
500
- valueNode.comment = commentsAfterValue.join('\n');
501
- }
502
- }
507
+ attachPairComments(pairResult);
503
508
  children.push(...errors);
504
509
  return new YamlKeyValuePair({
505
510
  children: children.flat().filter(c => c !== null),
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.transformYamlAstToApiDOM = exports.default = void 0;
7
7
  var _apidomDatamodel = require("@speclynx/apidom-datamodel");
8
+ var _predicates = require("./ast/nodes/predicates.cjs");
8
9
  var _YamlStyle = require("./ast/nodes/YamlStyle.cjs");
9
10
  // Transform context passed through transformation
10
11
 
@@ -39,8 +40,11 @@ const detectIndent = node => {
39
40
  return 2;
40
41
  };
41
42
 
42
- // strip leading '#' from each line of comment text; yaml library adds '#' during stringification
43
- const stripCommentHash = text => text.split('\n').map(line => line.replace(/^#/, '')).join('\n');
43
+ // strip leading '#' from each line of comment text; yaml library adds '#' during
44
+ // stringification; a bare '#' becomes a single space — the yaml library's encoding
45
+ // for an empty comment, which its stringifier turns back into '#' (plain '' would
46
+ // be dropped)
47
+ const stripCommentHash = text => text.split('\n').map(line => line === '#' ? ' ' : line.replace(/^#/, '')).join('\n');
44
48
 
45
49
  // collect comment texts from raw AST children, indexed by position relative to non-comment siblings
46
50
 
@@ -110,6 +114,7 @@ const setYamlStyleProp = (element, key, value) => {
110
114
  yaml[key] = value;
111
115
  element.style.yaml = yaml;
112
116
  };
117
+ const getYamlStyleProp = (element, key) => element.style?.yaml?.[key];
113
118
 
114
119
  // apply collected comments to transformed ApiDOM elements
115
120
  const applyComments = (elements, comments) => {
@@ -240,6 +245,17 @@ const transformChildren = (children, ctx) => {
240
245
  return results;
241
246
  };
242
247
 
248
+ // stream comments that precede the first document, joined; null when there are none
249
+ // (stream children are in source order, so everything before the document node qualifies)
250
+ const collectPreDocumentComments = node => {
251
+ const texts = [];
252
+ for (const child of node.children) {
253
+ if ((0, _predicates.isDocument)(child)) break;
254
+ if ((0, _predicates.isComment)(child)) texts.push(stripCommentHash(child.content));
255
+ }
256
+ return texts.length > 0 ? texts.join('\n') : null;
257
+ };
258
+
243
259
  // Stream: Wraps transformed children in ParseResultElement
244
260
  const transformStream = (node, ctx) => {
245
261
  // detect indent from the stream structure (only needed for style preservation)
@@ -261,6 +277,16 @@ const transformStream = (node, ctx) => {
261
277
  if (elements.length > 0) {
262
278
  const resultElement = elements[0];
263
279
  resultElement.classes.push('result');
280
+
281
+ // stream comments before the first document belong above the result element;
282
+ // capture them as commentBefore so style round-trips can re-emit them
283
+ if (ctx.style) {
284
+ const preDocumentComments = collectPreDocumentComments(node);
285
+ if (preDocumentComments !== null) {
286
+ const existing = getYamlStyleProp(resultElement, 'commentBefore');
287
+ setYamlStyleProp(resultElement, 'commentBefore', typeof existing === 'string' ? `${preDocumentComments}\n${existing}` : preDocumentComments);
288
+ }
289
+ }
264
290
  }
265
291
 
266
292
  // Add collected annotations
@@ -1,4 +1,5 @@
1
1
  import { ParseResultElement, AnnotationElement, CommentElement, MemberElement, ObjectElement, ArrayElement, isPrimitiveElement, Namespace, SourceMapElement } from '@speclynx/apidom-datamodel';
2
+ import { isComment, isDocument } from "./ast/nodes/predicates.mjs";
2
3
  import { YamlStyle, YamlStyleGroup } from "./ast/nodes/YamlStyle.mjs"; // Transform context passed through transformation
3
4
  // Node with type property and flat position properties
4
5
  // Helper to add source map to element
@@ -30,8 +31,11 @@ const detectIndent = node => {
30
31
  return 2;
31
32
  };
32
33
 
33
- // strip leading '#' from each line of comment text; yaml library adds '#' during stringification
34
- const stripCommentHash = text => text.split('\n').map(line => line.replace(/^#/, '')).join('\n');
34
+ // strip leading '#' from each line of comment text; yaml library adds '#' during
35
+ // stringification; a bare '#' becomes a single space — the yaml library's encoding
36
+ // for an empty comment, which its stringifier turns back into '#' (plain '' would
37
+ // be dropped)
38
+ const stripCommentHash = text => text.split('\n').map(line => line === '#' ? ' ' : line.replace(/^#/, '')).join('\n');
35
39
 
36
40
  // collect comment texts from raw AST children, indexed by position relative to non-comment siblings
37
41
 
@@ -101,6 +105,7 @@ const setYamlStyleProp = (element, key, value) => {
101
105
  yaml[key] = value;
102
106
  element.style.yaml = yaml;
103
107
  };
108
+ const getYamlStyleProp = (element, key) => element.style?.yaml?.[key];
104
109
 
105
110
  // apply collected comments to transformed ApiDOM elements
106
111
  const applyComments = (elements, comments) => {
@@ -231,6 +236,17 @@ const transformChildren = (children, ctx) => {
231
236
  return results;
232
237
  };
233
238
 
239
+ // stream comments that precede the first document, joined; null when there are none
240
+ // (stream children are in source order, so everything before the document node qualifies)
241
+ const collectPreDocumentComments = node => {
242
+ const texts = [];
243
+ for (const child of node.children) {
244
+ if (isDocument(child)) break;
245
+ if (isComment(child)) texts.push(stripCommentHash(child.content));
246
+ }
247
+ return texts.length > 0 ? texts.join('\n') : null;
248
+ };
249
+
234
250
  // Stream: Wraps transformed children in ParseResultElement
235
251
  const transformStream = (node, ctx) => {
236
252
  // detect indent from the stream structure (only needed for style preservation)
@@ -252,6 +268,16 @@ const transformStream = (node, ctx) => {
252
268
  if (elements.length > 0) {
253
269
  const resultElement = elements[0];
254
270
  resultElement.classes.push('result');
271
+
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
274
+ if (ctx.style) {
275
+ const preDocumentComments = collectPreDocumentComments(node);
276
+ if (preDocumentComments !== null) {
277
+ const existing = getYamlStyleProp(resultElement, 'commentBefore');
278
+ setYamlStyleProp(resultElement, 'commentBefore', typeof existing === 'string' ? `${preDocumentComments}\n${existing}` : preDocumentComments);
279
+ }
280
+ }
255
281
  }
256
282
 
257
283
  // Add collected annotations