source: trip-planner-front/node_modules/yaml/dist/parse-cst.js@ 1ad8e64

Last change on this file since 1ad8e64 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 46.0 KB
Line 
1'use strict';
2
3var PlainValue = require('./PlainValue-ec8e588e.js');
4
5class BlankLine extends PlainValue.Node {
6 constructor() {
7 super(PlainValue.Type.BLANK_LINE);
8 }
9 /* istanbul ignore next */
10
11
12 get includesTrailingLines() {
13 // This is never called from anywhere, but if it were,
14 // this is the value it should return.
15 return true;
16 }
17 /**
18 * Parses a blank line from the source
19 *
20 * @param {ParseContext} context
21 * @param {number} start - Index of first \n character
22 * @returns {number} - Index of the character after this
23 */
24
25
26 parse(context, start) {
27 this.context = context;
28 this.range = new PlainValue.Range(start, start + 1);
29 return start + 1;
30 }
31
32}
33
34class CollectionItem extends PlainValue.Node {
35 constructor(type, props) {
36 super(type, props);
37 this.node = null;
38 }
39
40 get includesTrailingLines() {
41 return !!this.node && this.node.includesTrailingLines;
42 }
43 /**
44 * @param {ParseContext} context
45 * @param {number} start - Index of first character
46 * @returns {number} - Index of the character after this
47 */
48
49
50 parse(context, start) {
51 this.context = context;
52 const {
53 parseNode,
54 src
55 } = context;
56 let {
57 atLineStart,
58 lineStart
59 } = context;
60 if (!atLineStart && this.type === PlainValue.Type.SEQ_ITEM) this.error = new PlainValue.YAMLSemanticError(this, 'Sequence items must not have preceding content on the same line');
61 const indent = atLineStart ? start - lineStart : context.indent;
62 let offset = PlainValue.Node.endOfWhiteSpace(src, start + 1);
63 let ch = src[offset];
64 const inlineComment = ch === '#';
65 const comments = [];
66 let blankLine = null;
67
68 while (ch === '\n' || ch === '#') {
69 if (ch === '#') {
70 const end = PlainValue.Node.endOfLine(src, offset + 1);
71 comments.push(new PlainValue.Range(offset, end));
72 offset = end;
73 } else {
74 atLineStart = true;
75 lineStart = offset + 1;
76 const wsEnd = PlainValue.Node.endOfWhiteSpace(src, lineStart);
77
78 if (src[wsEnd] === '\n' && comments.length === 0) {
79 blankLine = new BlankLine();
80 lineStart = blankLine.parse({
81 src
82 }, lineStart);
83 }
84
85 offset = PlainValue.Node.endOfIndent(src, lineStart);
86 }
87
88 ch = src[offset];
89 }
90
91 if (PlainValue.Node.nextNodeIsIndented(ch, offset - (lineStart + indent), this.type !== PlainValue.Type.SEQ_ITEM)) {
92 this.node = parseNode({
93 atLineStart,
94 inCollection: false,
95 indent,
96 lineStart,
97 parent: this
98 }, offset);
99 } else if (ch && lineStart > start + 1) {
100 offset = lineStart - 1;
101 }
102
103 if (this.node) {
104 if (blankLine) {
105 // Only blank lines preceding non-empty nodes are captured. Note that
106 // this means that collection item range start indices do not always
107 // increase monotonically. -- eemeli/yaml#126
108 const items = context.parent.items || context.parent.contents;
109 if (items) items.push(blankLine);
110 }
111
112 if (comments.length) Array.prototype.push.apply(this.props, comments);
113 offset = this.node.range.end;
114 } else {
115 if (inlineComment) {
116 const c = comments[0];
117 this.props.push(c);
118 offset = c.end;
119 } else {
120 offset = PlainValue.Node.endOfLine(src, start + 1);
121 }
122 }
123
124 const end = this.node ? this.node.valueRange.end : offset;
125 this.valueRange = new PlainValue.Range(start, end);
126 return offset;
127 }
128
129 setOrigRanges(cr, offset) {
130 offset = super.setOrigRanges(cr, offset);
131 return this.node ? this.node.setOrigRanges(cr, offset) : offset;
132 }
133
134 toString() {
135 const {
136 context: {
137 src
138 },
139 node,
140 range,
141 value
142 } = this;
143 if (value != null) return value;
144 const str = node ? src.slice(range.start, node.range.start) + String(node) : src.slice(range.start, range.end);
145 return PlainValue.Node.addStringTerminator(src, range.end, str);
146 }
147
148}
149
150class Comment extends PlainValue.Node {
151 constructor() {
152 super(PlainValue.Type.COMMENT);
153 }
154 /**
155 * Parses a comment line from the source
156 *
157 * @param {ParseContext} context
158 * @param {number} start - Index of first character
159 * @returns {number} - Index of the character after this scalar
160 */
161
162
163 parse(context, start) {
164 this.context = context;
165 const offset = this.parseComment(start);
166 this.range = new PlainValue.Range(start, offset);
167 return offset;
168 }
169
170}
171
172function grabCollectionEndComments(node) {
173 let cnode = node;
174
175 while (cnode instanceof CollectionItem) cnode = cnode.node;
176
177 if (!(cnode instanceof Collection)) return null;
178 const len = cnode.items.length;
179 let ci = -1;
180
181 for (let i = len - 1; i >= 0; --i) {
182 const n = cnode.items[i];
183
184 if (n.type === PlainValue.Type.COMMENT) {
185 // Keep sufficiently indented comments with preceding node
186 const {
187 indent,
188 lineStart
189 } = n.context;
190 if (indent > 0 && n.range.start >= lineStart + indent) break;
191 ci = i;
192 } else if (n.type === PlainValue.Type.BLANK_LINE) ci = i;else break;
193 }
194
195 if (ci === -1) return null;
196 const ca = cnode.items.splice(ci, len - ci);
197 const prevEnd = ca[0].range.start;
198
199 while (true) {
200 cnode.range.end = prevEnd;
201 if (cnode.valueRange && cnode.valueRange.end > prevEnd) cnode.valueRange.end = prevEnd;
202 if (cnode === node) break;
203 cnode = cnode.context.parent;
204 }
205
206 return ca;
207}
208class Collection extends PlainValue.Node {
209 static nextContentHasIndent(src, offset, indent) {
210 const lineStart = PlainValue.Node.endOfLine(src, offset) + 1;
211 offset = PlainValue.Node.endOfWhiteSpace(src, lineStart);
212 const ch = src[offset];
213 if (!ch) return false;
214 if (offset >= lineStart + indent) return true;
215 if (ch !== '#' && ch !== '\n') return false;
216 return Collection.nextContentHasIndent(src, offset, indent);
217 }
218
219 constructor(firstItem) {
220 super(firstItem.type === PlainValue.Type.SEQ_ITEM ? PlainValue.Type.SEQ : PlainValue.Type.MAP);
221
222 for (let i = firstItem.props.length - 1; i >= 0; --i) {
223 if (firstItem.props[i].start < firstItem.context.lineStart) {
224 // props on previous line are assumed by the collection
225 this.props = firstItem.props.slice(0, i + 1);
226 firstItem.props = firstItem.props.slice(i + 1);
227 const itemRange = firstItem.props[0] || firstItem.valueRange;
228 firstItem.range.start = itemRange.start;
229 break;
230 }
231 }
232
233 this.items = [firstItem];
234 const ec = grabCollectionEndComments(firstItem);
235 if (ec) Array.prototype.push.apply(this.items, ec);
236 }
237
238 get includesTrailingLines() {
239 return this.items.length > 0;
240 }
241 /**
242 * @param {ParseContext} context
243 * @param {number} start - Index of first character
244 * @returns {number} - Index of the character after this
245 */
246
247
248 parse(context, start) {
249 this.context = context;
250 const {
251 parseNode,
252 src
253 } = context; // It's easier to recalculate lineStart here rather than tracking down the
254 // last context from which to read it -- eemeli/yaml#2
255
256 let lineStart = PlainValue.Node.startOfLine(src, start);
257 const firstItem = this.items[0]; // First-item context needs to be correct for later comment handling
258 // -- eemeli/yaml#17
259
260 firstItem.context.parent = this;
261 this.valueRange = PlainValue.Range.copy(firstItem.valueRange);
262 const indent = firstItem.range.start - firstItem.context.lineStart;
263 let offset = start;
264 offset = PlainValue.Node.normalizeOffset(src, offset);
265 let ch = src[offset];
266 let atLineStart = PlainValue.Node.endOfWhiteSpace(src, lineStart) === offset;
267 let prevIncludesTrailingLines = false;
268
269 while (ch) {
270 while (ch === '\n' || ch === '#') {
271 if (atLineStart && ch === '\n' && !prevIncludesTrailingLines) {
272 const blankLine = new BlankLine();
273 offset = blankLine.parse({
274 src
275 }, offset);
276 this.valueRange.end = offset;
277
278 if (offset >= src.length) {
279 ch = null;
280 break;
281 }
282
283 this.items.push(blankLine);
284 offset -= 1; // blankLine.parse() consumes terminal newline
285 } else if (ch === '#') {
286 if (offset < lineStart + indent && !Collection.nextContentHasIndent(src, offset, indent)) {
287 return offset;
288 }
289
290 const comment = new Comment();
291 offset = comment.parse({
292 indent,
293 lineStart,
294 src
295 }, offset);
296 this.items.push(comment);
297 this.valueRange.end = offset;
298
299 if (offset >= src.length) {
300 ch = null;
301 break;
302 }
303 }
304
305 lineStart = offset + 1;
306 offset = PlainValue.Node.endOfIndent(src, lineStart);
307
308 if (PlainValue.Node.atBlank(src, offset)) {
309 const wsEnd = PlainValue.Node.endOfWhiteSpace(src, offset);
310 const next = src[wsEnd];
311
312 if (!next || next === '\n' || next === '#') {
313 offset = wsEnd;
314 }
315 }
316
317 ch = src[offset];
318 atLineStart = true;
319 }
320
321 if (!ch) {
322 break;
323 }
324
325 if (offset !== lineStart + indent && (atLineStart || ch !== ':')) {
326 if (offset < lineStart + indent) {
327 if (lineStart > start) offset = lineStart;
328 break;
329 } else if (!this.error) {
330 const msg = 'All collection items must start at the same column';
331 this.error = new PlainValue.YAMLSyntaxError(this, msg);
332 }
333 }
334
335 if (firstItem.type === PlainValue.Type.SEQ_ITEM) {
336 if (ch !== '-') {
337 if (lineStart > start) offset = lineStart;
338 break;
339 }
340 } else if (ch === '-' && !this.error) {
341 // map key may start with -, as long as it's followed by a non-whitespace char
342 const next = src[offset + 1];
343
344 if (!next || next === '\n' || next === '\t' || next === ' ') {
345 const msg = 'A collection cannot be both a mapping and a sequence';
346 this.error = new PlainValue.YAMLSyntaxError(this, msg);
347 }
348 }
349
350 const node = parseNode({
351 atLineStart,
352 inCollection: true,
353 indent,
354 lineStart,
355 parent: this
356 }, offset);
357 if (!node) return offset; // at next document start
358
359 this.items.push(node);
360 this.valueRange.end = node.valueRange.end;
361 offset = PlainValue.Node.normalizeOffset(src, node.range.end);
362 ch = src[offset];
363 atLineStart = false;
364 prevIncludesTrailingLines = node.includesTrailingLines; // Need to reset lineStart and atLineStart here if preceding node's range
365 // has advanced to check the current line's indentation level
366 // -- eemeli/yaml#10 & eemeli/yaml#38
367
368 if (ch) {
369 let ls = offset - 1;
370 let prev = src[ls];
371
372 while (prev === ' ' || prev === '\t') prev = src[--ls];
373
374 if (prev === '\n') {
375 lineStart = ls + 1;
376 atLineStart = true;
377 }
378 }
379
380 const ec = grabCollectionEndComments(node);
381 if (ec) Array.prototype.push.apply(this.items, ec);
382 }
383
384 return offset;
385 }
386
387 setOrigRanges(cr, offset) {
388 offset = super.setOrigRanges(cr, offset);
389 this.items.forEach(node => {
390 offset = node.setOrigRanges(cr, offset);
391 });
392 return offset;
393 }
394
395 toString() {
396 const {
397 context: {
398 src
399 },
400 items,
401 range,
402 value
403 } = this;
404 if (value != null) return value;
405 let str = src.slice(range.start, items[0].range.start) + String(items[0]);
406
407 for (let i = 1; i < items.length; ++i) {
408 const item = items[i];
409 const {
410 atLineStart,
411 indent
412 } = item.context;
413 if (atLineStart) for (let i = 0; i < indent; ++i) str += ' ';
414 str += String(item);
415 }
416
417 return PlainValue.Node.addStringTerminator(src, range.end, str);
418 }
419
420}
421
422class Directive extends PlainValue.Node {
423 constructor() {
424 super(PlainValue.Type.DIRECTIVE);
425 this.name = null;
426 }
427
428 get parameters() {
429 const raw = this.rawValue;
430 return raw ? raw.trim().split(/[ \t]+/) : [];
431 }
432
433 parseName(start) {
434 const {
435 src
436 } = this.context;
437 let offset = start;
438 let ch = src[offset];
439
440 while (ch && ch !== '\n' && ch !== '\t' && ch !== ' ') ch = src[offset += 1];
441
442 this.name = src.slice(start, offset);
443 return offset;
444 }
445
446 parseParameters(start) {
447 const {
448 src
449 } = this.context;
450 let offset = start;
451 let ch = src[offset];
452
453 while (ch && ch !== '\n' && ch !== '#') ch = src[offset += 1];
454
455 this.valueRange = new PlainValue.Range(start, offset);
456 return offset;
457 }
458
459 parse(context, start) {
460 this.context = context;
461 let offset = this.parseName(start + 1);
462 offset = this.parseParameters(offset);
463 offset = this.parseComment(offset);
464 this.range = new PlainValue.Range(start, offset);
465 return offset;
466 }
467
468}
469
470class Document extends PlainValue.Node {
471 static startCommentOrEndBlankLine(src, start) {
472 const offset = PlainValue.Node.endOfWhiteSpace(src, start);
473 const ch = src[offset];
474 return ch === '#' || ch === '\n' ? offset : start;
475 }
476
477 constructor() {
478 super(PlainValue.Type.DOCUMENT);
479 this.directives = null;
480 this.contents = null;
481 this.directivesEndMarker = null;
482 this.documentEndMarker = null;
483 }
484
485 parseDirectives(start) {
486 const {
487 src
488 } = this.context;
489 this.directives = [];
490 let atLineStart = true;
491 let hasDirectives = false;
492 let offset = start;
493
494 while (!PlainValue.Node.atDocumentBoundary(src, offset, PlainValue.Char.DIRECTIVES_END)) {
495 offset = Document.startCommentOrEndBlankLine(src, offset);
496
497 switch (src[offset]) {
498 case '\n':
499 if (atLineStart) {
500 const blankLine = new BlankLine();
501 offset = blankLine.parse({
502 src
503 }, offset);
504
505 if (offset < src.length) {
506 this.directives.push(blankLine);
507 }
508 } else {
509 offset += 1;
510 atLineStart = true;
511 }
512
513 break;
514
515 case '#':
516 {
517 const comment = new Comment();
518 offset = comment.parse({
519 src
520 }, offset);
521 this.directives.push(comment);
522 atLineStart = false;
523 }
524 break;
525
526 case '%':
527 {
528 const directive = new Directive();
529 offset = directive.parse({
530 parent: this,
531 src
532 }, offset);
533 this.directives.push(directive);
534 hasDirectives = true;
535 atLineStart = false;
536 }
537 break;
538
539 default:
540 if (hasDirectives) {
541 this.error = new PlainValue.YAMLSemanticError(this, 'Missing directives-end indicator line');
542 } else if (this.directives.length > 0) {
543 this.contents = this.directives;
544 this.directives = [];
545 }
546
547 return offset;
548 }
549 }
550
551 if (src[offset]) {
552 this.directivesEndMarker = new PlainValue.Range(offset, offset + 3);
553 return offset + 3;
554 }
555
556 if (hasDirectives) {
557 this.error = new PlainValue.YAMLSemanticError(this, 'Missing directives-end indicator line');
558 } else if (this.directives.length > 0) {
559 this.contents = this.directives;
560 this.directives = [];
561 }
562
563 return offset;
564 }
565
566 parseContents(start) {
567 const {
568 parseNode,
569 src
570 } = this.context;
571 if (!this.contents) this.contents = [];
572 let lineStart = start;
573
574 while (src[lineStart - 1] === '-') lineStart -= 1;
575
576 let offset = PlainValue.Node.endOfWhiteSpace(src, start);
577 let atLineStart = lineStart === start;
578 this.valueRange = new PlainValue.Range(offset);
579
580 while (!PlainValue.Node.atDocumentBoundary(src, offset, PlainValue.Char.DOCUMENT_END)) {
581 switch (src[offset]) {
582 case '\n':
583 if (atLineStart) {
584 const blankLine = new BlankLine();
585 offset = blankLine.parse({
586 src
587 }, offset);
588
589 if (offset < src.length) {
590 this.contents.push(blankLine);
591 }
592 } else {
593 offset += 1;
594 atLineStart = true;
595 }
596
597 lineStart = offset;
598 break;
599
600 case '#':
601 {
602 const comment = new Comment();
603 offset = comment.parse({
604 src
605 }, offset);
606 this.contents.push(comment);
607 atLineStart = false;
608 }
609 break;
610
611 default:
612 {
613 const iEnd = PlainValue.Node.endOfIndent(src, offset);
614 const context = {
615 atLineStart,
616 indent: -1,
617 inFlow: false,
618 inCollection: false,
619 lineStart,
620 parent: this
621 };
622 const node = parseNode(context, iEnd);
623 if (!node) return this.valueRange.end = iEnd; // at next document start
624
625 this.contents.push(node);
626 offset = node.range.end;
627 atLineStart = false;
628 const ec = grabCollectionEndComments(node);
629 if (ec) Array.prototype.push.apply(this.contents, ec);
630 }
631 }
632
633 offset = Document.startCommentOrEndBlankLine(src, offset);
634 }
635
636 this.valueRange.end = offset;
637
638 if (src[offset]) {
639 this.documentEndMarker = new PlainValue.Range(offset, offset + 3);
640 offset += 3;
641
642 if (src[offset]) {
643 offset = PlainValue.Node.endOfWhiteSpace(src, offset);
644
645 if (src[offset] === '#') {
646 const comment = new Comment();
647 offset = comment.parse({
648 src
649 }, offset);
650 this.contents.push(comment);
651 }
652
653 switch (src[offset]) {
654 case '\n':
655 offset += 1;
656 break;
657
658 case undefined:
659 break;
660
661 default:
662 this.error = new PlainValue.YAMLSyntaxError(this, 'Document end marker line cannot have a non-comment suffix');
663 }
664 }
665 }
666
667 return offset;
668 }
669 /**
670 * @param {ParseContext} context
671 * @param {number} start - Index of first character
672 * @returns {number} - Index of the character after this
673 */
674
675
676 parse(context, start) {
677 context.root = this;
678 this.context = context;
679 const {
680 src
681 } = context;
682 let offset = src.charCodeAt(start) === 0xfeff ? start + 1 : start; // skip BOM
683
684 offset = this.parseDirectives(offset);
685 offset = this.parseContents(offset);
686 return offset;
687 }
688
689 setOrigRanges(cr, offset) {
690 offset = super.setOrigRanges(cr, offset);
691 this.directives.forEach(node => {
692 offset = node.setOrigRanges(cr, offset);
693 });
694 if (this.directivesEndMarker) offset = this.directivesEndMarker.setOrigRange(cr, offset);
695 this.contents.forEach(node => {
696 offset = node.setOrigRanges(cr, offset);
697 });
698 if (this.documentEndMarker) offset = this.documentEndMarker.setOrigRange(cr, offset);
699 return offset;
700 }
701
702 toString() {
703 const {
704 contents,
705 directives,
706 value
707 } = this;
708 if (value != null) return value;
709 let str = directives.join('');
710
711 if (contents.length > 0) {
712 if (directives.length > 0 || contents[0].type === PlainValue.Type.COMMENT) str += '---\n';
713 str += contents.join('');
714 }
715
716 if (str[str.length - 1] !== '\n') str += '\n';
717 return str;
718 }
719
720}
721
722class Alias extends PlainValue.Node {
723 /**
724 * Parses an *alias from the source
725 *
726 * @param {ParseContext} context
727 * @param {number} start - Index of first character
728 * @returns {number} - Index of the character after this scalar
729 */
730 parse(context, start) {
731 this.context = context;
732 const {
733 src
734 } = context;
735 let offset = PlainValue.Node.endOfIdentifier(src, start + 1);
736 this.valueRange = new PlainValue.Range(start + 1, offset);
737 offset = PlainValue.Node.endOfWhiteSpace(src, offset);
738 offset = this.parseComment(offset);
739 return offset;
740 }
741
742}
743
744const Chomp = {
745 CLIP: 'CLIP',
746 KEEP: 'KEEP',
747 STRIP: 'STRIP'
748};
749class BlockValue extends PlainValue.Node {
750 constructor(type, props) {
751 super(type, props);
752 this.blockIndent = null;
753 this.chomping = Chomp.CLIP;
754 this.header = null;
755 }
756
757 get includesTrailingLines() {
758 return this.chomping === Chomp.KEEP;
759 }
760
761 get strValue() {
762 if (!this.valueRange || !this.context) return null;
763 let {
764 start,
765 end
766 } = this.valueRange;
767 const {
768 indent,
769 src
770 } = this.context;
771 if (this.valueRange.isEmpty()) return '';
772 let lastNewLine = null;
773 let ch = src[end - 1];
774
775 while (ch === '\n' || ch === '\t' || ch === ' ') {
776 end -= 1;
777
778 if (end <= start) {
779 if (this.chomping === Chomp.KEEP) break;else return ''; // probably never happens
780 }
781
782 if (ch === '\n') lastNewLine = end;
783 ch = src[end - 1];
784 }
785
786 let keepStart = end + 1;
787
788 if (lastNewLine) {
789 if (this.chomping === Chomp.KEEP) {
790 keepStart = lastNewLine;
791 end = this.valueRange.end;
792 } else {
793 end = lastNewLine;
794 }
795 }
796
797 const bi = indent + this.blockIndent;
798 const folded = this.type === PlainValue.Type.BLOCK_FOLDED;
799 let atStart = true;
800 let str = '';
801 let sep = '';
802 let prevMoreIndented = false;
803
804 for (let i = start; i < end; ++i) {
805 for (let j = 0; j < bi; ++j) {
806 if (src[i] !== ' ') break;
807 i += 1;
808 }
809
810 const ch = src[i];
811
812 if (ch === '\n') {
813 if (sep === '\n') str += '\n';else sep = '\n';
814 } else {
815 const lineEnd = PlainValue.Node.endOfLine(src, i);
816 const line = src.slice(i, lineEnd);
817 i = lineEnd;
818
819 if (folded && (ch === ' ' || ch === '\t') && i < keepStart) {
820 if (sep === ' ') sep = '\n';else if (!prevMoreIndented && !atStart && sep === '\n') sep = '\n\n';
821 str += sep + line; //+ ((lineEnd < end && src[lineEnd]) || '')
822
823 sep = lineEnd < end && src[lineEnd] || '';
824 prevMoreIndented = true;
825 } else {
826 str += sep + line;
827 sep = folded && i < keepStart ? ' ' : '\n';
828 prevMoreIndented = false;
829 }
830
831 if (atStart && line !== '') atStart = false;
832 }
833 }
834
835 return this.chomping === Chomp.STRIP ? str : str + '\n';
836 }
837
838 parseBlockHeader(start) {
839 const {
840 src
841 } = this.context;
842 let offset = start + 1;
843 let bi = '';
844
845 while (true) {
846 const ch = src[offset];
847
848 switch (ch) {
849 case '-':
850 this.chomping = Chomp.STRIP;
851 break;
852
853 case '+':
854 this.chomping = Chomp.KEEP;
855 break;
856
857 case '0':
858 case '1':
859 case '2':
860 case '3':
861 case '4':
862 case '5':
863 case '6':
864 case '7':
865 case '8':
866 case '9':
867 bi += ch;
868 break;
869
870 default:
871 this.blockIndent = Number(bi) || null;
872 this.header = new PlainValue.Range(start, offset);
873 return offset;
874 }
875
876 offset += 1;
877 }
878 }
879
880 parseBlockValue(start) {
881 const {
882 indent,
883 src
884 } = this.context;
885 const explicit = !!this.blockIndent;
886 let offset = start;
887 let valueEnd = start;
888 let minBlockIndent = 1;
889
890 for (let ch = src[offset]; ch === '\n'; ch = src[offset]) {
891 offset += 1;
892 if (PlainValue.Node.atDocumentBoundary(src, offset)) break;
893 const end = PlainValue.Node.endOfBlockIndent(src, indent, offset); // should not include tab?
894
895 if (end === null) break;
896 const ch = src[end];
897 const lineIndent = end - (offset + indent);
898
899 if (!this.blockIndent) {
900 // no explicit block indent, none yet detected
901 if (src[end] !== '\n') {
902 // first line with non-whitespace content
903 if (lineIndent < minBlockIndent) {
904 const msg = 'Block scalars with more-indented leading empty lines must use an explicit indentation indicator';
905 this.error = new PlainValue.YAMLSemanticError(this, msg);
906 }
907
908 this.blockIndent = lineIndent;
909 } else if (lineIndent > minBlockIndent) {
910 // empty line with more whitespace
911 minBlockIndent = lineIndent;
912 }
913 } else if (ch && ch !== '\n' && lineIndent < this.blockIndent) {
914 if (src[end] === '#') break;
915
916 if (!this.error) {
917 const src = explicit ? 'explicit indentation indicator' : 'first line';
918 const msg = `Block scalars must not be less indented than their ${src}`;
919 this.error = new PlainValue.YAMLSemanticError(this, msg);
920 }
921 }
922
923 if (src[end] === '\n') {
924 offset = end;
925 } else {
926 offset = valueEnd = PlainValue.Node.endOfLine(src, end);
927 }
928 }
929
930 if (this.chomping !== Chomp.KEEP) {
931 offset = src[valueEnd] ? valueEnd + 1 : valueEnd;
932 }
933
934 this.valueRange = new PlainValue.Range(start + 1, offset);
935 return offset;
936 }
937 /**
938 * Parses a block value from the source
939 *
940 * Accepted forms are:
941 * ```
942 * BS
943 * block
944 * lines
945 *
946 * BS #comment
947 * block
948 * lines
949 * ```
950 * where the block style BS matches the regexp `[|>][-+1-9]*` and block lines
951 * are empty or have an indent level greater than `indent`.
952 *
953 * @param {ParseContext} context
954 * @param {number} start - Index of first character
955 * @returns {number} - Index of the character after this block
956 */
957
958
959 parse(context, start) {
960 this.context = context;
961 const {
962 src
963 } = context;
964 let offset = this.parseBlockHeader(start);
965 offset = PlainValue.Node.endOfWhiteSpace(src, offset);
966 offset = this.parseComment(offset);
967 offset = this.parseBlockValue(offset);
968 return offset;
969 }
970
971 setOrigRanges(cr, offset) {
972 offset = super.setOrigRanges(cr, offset);
973 return this.header ? this.header.setOrigRange(cr, offset) : offset;
974 }
975
976}
977
978class FlowCollection extends PlainValue.Node {
979 constructor(type, props) {
980 super(type, props);
981 this.items = null;
982 }
983
984 prevNodeIsJsonLike(idx = this.items.length) {
985 const node = this.items[idx - 1];
986 return !!node && (node.jsonLike || node.type === PlainValue.Type.COMMENT && this.prevNodeIsJsonLike(idx - 1));
987 }
988 /**
989 * @param {ParseContext} context
990 * @param {number} start - Index of first character
991 * @returns {number} - Index of the character after this
992 */
993
994
995 parse(context, start) {
996 this.context = context;
997 const {
998 parseNode,
999 src
1000 } = context;
1001 let {
1002 indent,
1003 lineStart
1004 } = context;
1005 let char = src[start]; // { or [
1006
1007 this.items = [{
1008 char,
1009 offset: start
1010 }];
1011 let offset = PlainValue.Node.endOfWhiteSpace(src, start + 1);
1012 char = src[offset];
1013
1014 while (char && char !== ']' && char !== '}') {
1015 switch (char) {
1016 case '\n':
1017 {
1018 lineStart = offset + 1;
1019 const wsEnd = PlainValue.Node.endOfWhiteSpace(src, lineStart);
1020
1021 if (src[wsEnd] === '\n') {
1022 const blankLine = new BlankLine();
1023 lineStart = blankLine.parse({
1024 src
1025 }, lineStart);
1026 this.items.push(blankLine);
1027 }
1028
1029 offset = PlainValue.Node.endOfIndent(src, lineStart);
1030
1031 if (offset <= lineStart + indent) {
1032 char = src[offset];
1033
1034 if (offset < lineStart + indent || char !== ']' && char !== '}') {
1035 const msg = 'Insufficient indentation in flow collection';
1036 this.error = new PlainValue.YAMLSemanticError(this, msg);
1037 }
1038 }
1039 }
1040 break;
1041
1042 case ',':
1043 {
1044 this.items.push({
1045 char,
1046 offset
1047 });
1048 offset += 1;
1049 }
1050 break;
1051
1052 case '#':
1053 {
1054 const comment = new Comment();
1055 offset = comment.parse({
1056 src
1057 }, offset);
1058 this.items.push(comment);
1059 }
1060 break;
1061
1062 case '?':
1063 case ':':
1064 {
1065 const next = src[offset + 1];
1066
1067 if (next === '\n' || next === '\t' || next === ' ' || next === ',' || // in-flow : after JSON-like key does not need to be followed by whitespace
1068 char === ':' && this.prevNodeIsJsonLike()) {
1069 this.items.push({
1070 char,
1071 offset
1072 });
1073 offset += 1;
1074 break;
1075 }
1076 }
1077 // fallthrough
1078
1079 default:
1080 {
1081 const node = parseNode({
1082 atLineStart: false,
1083 inCollection: false,
1084 inFlow: true,
1085 indent: -1,
1086 lineStart,
1087 parent: this
1088 }, offset);
1089
1090 if (!node) {
1091 // at next document start
1092 this.valueRange = new PlainValue.Range(start, offset);
1093 return offset;
1094 }
1095
1096 this.items.push(node);
1097 offset = PlainValue.Node.normalizeOffset(src, node.range.end);
1098 }
1099 }
1100
1101 offset = PlainValue.Node.endOfWhiteSpace(src, offset);
1102 char = src[offset];
1103 }
1104
1105 this.valueRange = new PlainValue.Range(start, offset + 1);
1106
1107 if (char) {
1108 this.items.push({
1109 char,
1110 offset
1111 });
1112 offset = PlainValue.Node.endOfWhiteSpace(src, offset + 1);
1113 offset = this.parseComment(offset);
1114 }
1115
1116 return offset;
1117 }
1118
1119 setOrigRanges(cr, offset) {
1120 offset = super.setOrigRanges(cr, offset);
1121 this.items.forEach(node => {
1122 if (node instanceof PlainValue.Node) {
1123 offset = node.setOrigRanges(cr, offset);
1124 } else if (cr.length === 0) {
1125 node.origOffset = node.offset;
1126 } else {
1127 let i = offset;
1128
1129 while (i < cr.length) {
1130 if (cr[i] > node.offset) break;else ++i;
1131 }
1132
1133 node.origOffset = node.offset + i;
1134 offset = i;
1135 }
1136 });
1137 return offset;
1138 }
1139
1140 toString() {
1141 const {
1142 context: {
1143 src
1144 },
1145 items,
1146 range,
1147 value
1148 } = this;
1149 if (value != null) return value;
1150 const nodes = items.filter(item => item instanceof PlainValue.Node);
1151 let str = '';
1152 let prevEnd = range.start;
1153 nodes.forEach(node => {
1154 const prefix = src.slice(prevEnd, node.range.start);
1155 prevEnd = node.range.end;
1156 str += prefix + String(node);
1157
1158 if (str[str.length - 1] === '\n' && src[prevEnd - 1] !== '\n' && src[prevEnd] === '\n') {
1159 // Comment range does not include the terminal newline, but its
1160 // stringified value does. Without this fix, newlines at comment ends
1161 // get duplicated.
1162 prevEnd += 1;
1163 }
1164 });
1165 str += src.slice(prevEnd, range.end);
1166 return PlainValue.Node.addStringTerminator(src, range.end, str);
1167 }
1168
1169}
1170
1171class QuoteDouble extends PlainValue.Node {
1172 static endOfQuote(src, offset) {
1173 let ch = src[offset];
1174
1175 while (ch && ch !== '"') {
1176 offset += ch === '\\' ? 2 : 1;
1177 ch = src[offset];
1178 }
1179
1180 return offset + 1;
1181 }
1182 /**
1183 * @returns {string | { str: string, errors: YAMLSyntaxError[] }}
1184 */
1185
1186
1187 get strValue() {
1188 if (!this.valueRange || !this.context) return null;
1189 const errors = [];
1190 const {
1191 start,
1192 end
1193 } = this.valueRange;
1194 const {
1195 indent,
1196 src
1197 } = this.context;
1198 if (src[end - 1] !== '"') errors.push(new PlainValue.YAMLSyntaxError(this, 'Missing closing "quote')); // Using String#replace is too painful with escaped newlines preceded by
1199 // escaped backslashes; also, this should be faster.
1200
1201 let str = '';
1202
1203 for (let i = start + 1; i < end - 1; ++i) {
1204 const ch = src[i];
1205
1206 if (ch === '\n') {
1207 if (PlainValue.Node.atDocumentBoundary(src, i + 1)) errors.push(new PlainValue.YAMLSemanticError(this, 'Document boundary indicators are not allowed within string values'));
1208 const {
1209 fold,
1210 offset,
1211 error
1212 } = PlainValue.Node.foldNewline(src, i, indent);
1213 str += fold;
1214 i = offset;
1215 if (error) errors.push(new PlainValue.YAMLSemanticError(this, 'Multi-line double-quoted string needs to be sufficiently indented'));
1216 } else if (ch === '\\') {
1217 i += 1;
1218
1219 switch (src[i]) {
1220 case '0':
1221 str += '\0';
1222 break;
1223 // null character
1224
1225 case 'a':
1226 str += '\x07';
1227 break;
1228 // bell character
1229
1230 case 'b':
1231 str += '\b';
1232 break;
1233 // backspace
1234
1235 case 'e':
1236 str += '\x1b';
1237 break;
1238 // escape character
1239
1240 case 'f':
1241 str += '\f';
1242 break;
1243 // form feed
1244
1245 case 'n':
1246 str += '\n';
1247 break;
1248 // line feed
1249
1250 case 'r':
1251 str += '\r';
1252 break;
1253 // carriage return
1254
1255 case 't':
1256 str += '\t';
1257 break;
1258 // horizontal tab
1259
1260 case 'v':
1261 str += '\v';
1262 break;
1263 // vertical tab
1264
1265 case 'N':
1266 str += '\u0085';
1267 break;
1268 // Unicode next line
1269
1270 case '_':
1271 str += '\u00a0';
1272 break;
1273 // Unicode non-breaking space
1274
1275 case 'L':
1276 str += '\u2028';
1277 break;
1278 // Unicode line separator
1279
1280 case 'P':
1281 str += '\u2029';
1282 break;
1283 // Unicode paragraph separator
1284
1285 case ' ':
1286 str += ' ';
1287 break;
1288
1289 case '"':
1290 str += '"';
1291 break;
1292
1293 case '/':
1294 str += '/';
1295 break;
1296
1297 case '\\':
1298 str += '\\';
1299 break;
1300
1301 case '\t':
1302 str += '\t';
1303 break;
1304
1305 case 'x':
1306 str += this.parseCharCode(i + 1, 2, errors);
1307 i += 2;
1308 break;
1309
1310 case 'u':
1311 str += this.parseCharCode(i + 1, 4, errors);
1312 i += 4;
1313 break;
1314
1315 case 'U':
1316 str += this.parseCharCode(i + 1, 8, errors);
1317 i += 8;
1318 break;
1319
1320 case '\n':
1321 // skip escaped newlines, but still trim the following line
1322 while (src[i + 1] === ' ' || src[i + 1] === '\t') i += 1;
1323
1324 break;
1325
1326 default:
1327 errors.push(new PlainValue.YAMLSyntaxError(this, `Invalid escape sequence ${src.substr(i - 1, 2)}`));
1328 str += '\\' + src[i];
1329 }
1330 } else if (ch === ' ' || ch === '\t') {
1331 // trim trailing whitespace
1332 const wsStart = i;
1333 let next = src[i + 1];
1334
1335 while (next === ' ' || next === '\t') {
1336 i += 1;
1337 next = src[i + 1];
1338 }
1339
1340 if (next !== '\n') str += i > wsStart ? src.slice(wsStart, i + 1) : ch;
1341 } else {
1342 str += ch;
1343 }
1344 }
1345
1346 return errors.length > 0 ? {
1347 errors,
1348 str
1349 } : str;
1350 }
1351
1352 parseCharCode(offset, length, errors) {
1353 const {
1354 src
1355 } = this.context;
1356 const cc = src.substr(offset, length);
1357 const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc);
1358 const code = ok ? parseInt(cc, 16) : NaN;
1359
1360 if (isNaN(code)) {
1361 errors.push(new PlainValue.YAMLSyntaxError(this, `Invalid escape sequence ${src.substr(offset - 2, length + 2)}`));
1362 return src.substr(offset - 2, length + 2);
1363 }
1364
1365 return String.fromCodePoint(code);
1366 }
1367 /**
1368 * Parses a "double quoted" value from the source
1369 *
1370 * @param {ParseContext} context
1371 * @param {number} start - Index of first character
1372 * @returns {number} - Index of the character after this scalar
1373 */
1374
1375
1376 parse(context, start) {
1377 this.context = context;
1378 const {
1379 src
1380 } = context;
1381 let offset = QuoteDouble.endOfQuote(src, start + 1);
1382 this.valueRange = new PlainValue.Range(start, offset);
1383 offset = PlainValue.Node.endOfWhiteSpace(src, offset);
1384 offset = this.parseComment(offset);
1385 return offset;
1386 }
1387
1388}
1389
1390class QuoteSingle extends PlainValue.Node {
1391 static endOfQuote(src, offset) {
1392 let ch = src[offset];
1393
1394 while (ch) {
1395 if (ch === "'") {
1396 if (src[offset + 1] !== "'") break;
1397 ch = src[offset += 2];
1398 } else {
1399 ch = src[offset += 1];
1400 }
1401 }
1402
1403 return offset + 1;
1404 }
1405 /**
1406 * @returns {string | { str: string, errors: YAMLSyntaxError[] }}
1407 */
1408
1409
1410 get strValue() {
1411 if (!this.valueRange || !this.context) return null;
1412 const errors = [];
1413 const {
1414 start,
1415 end
1416 } = this.valueRange;
1417 const {
1418 indent,
1419 src
1420 } = this.context;
1421 if (src[end - 1] !== "'") errors.push(new PlainValue.YAMLSyntaxError(this, "Missing closing 'quote"));
1422 let str = '';
1423
1424 for (let i = start + 1; i < end - 1; ++i) {
1425 const ch = src[i];
1426
1427 if (ch === '\n') {
1428 if (PlainValue.Node.atDocumentBoundary(src, i + 1)) errors.push(new PlainValue.YAMLSemanticError(this, 'Document boundary indicators are not allowed within string values'));
1429 const {
1430 fold,
1431 offset,
1432 error
1433 } = PlainValue.Node.foldNewline(src, i, indent);
1434 str += fold;
1435 i = offset;
1436 if (error) errors.push(new PlainValue.YAMLSemanticError(this, 'Multi-line single-quoted string needs to be sufficiently indented'));
1437 } else if (ch === "'") {
1438 str += ch;
1439 i += 1;
1440 if (src[i] !== "'") errors.push(new PlainValue.YAMLSyntaxError(this, 'Unescaped single quote? This should not happen.'));
1441 } else if (ch === ' ' || ch === '\t') {
1442 // trim trailing whitespace
1443 const wsStart = i;
1444 let next = src[i + 1];
1445
1446 while (next === ' ' || next === '\t') {
1447 i += 1;
1448 next = src[i + 1];
1449 }
1450
1451 if (next !== '\n') str += i > wsStart ? src.slice(wsStart, i + 1) : ch;
1452 } else {
1453 str += ch;
1454 }
1455 }
1456
1457 return errors.length > 0 ? {
1458 errors,
1459 str
1460 } : str;
1461 }
1462 /**
1463 * Parses a 'single quoted' value from the source
1464 *
1465 * @param {ParseContext} context
1466 * @param {number} start - Index of first character
1467 * @returns {number} - Index of the character after this scalar
1468 */
1469
1470
1471 parse(context, start) {
1472 this.context = context;
1473 const {
1474 src
1475 } = context;
1476 let offset = QuoteSingle.endOfQuote(src, start + 1);
1477 this.valueRange = new PlainValue.Range(start, offset);
1478 offset = PlainValue.Node.endOfWhiteSpace(src, offset);
1479 offset = this.parseComment(offset);
1480 return offset;
1481 }
1482
1483}
1484
1485function createNewNode(type, props) {
1486 switch (type) {
1487 case PlainValue.Type.ALIAS:
1488 return new Alias(type, props);
1489
1490 case PlainValue.Type.BLOCK_FOLDED:
1491 case PlainValue.Type.BLOCK_LITERAL:
1492 return new BlockValue(type, props);
1493
1494 case PlainValue.Type.FLOW_MAP:
1495 case PlainValue.Type.FLOW_SEQ:
1496 return new FlowCollection(type, props);
1497
1498 case PlainValue.Type.MAP_KEY:
1499 case PlainValue.Type.MAP_VALUE:
1500 case PlainValue.Type.SEQ_ITEM:
1501 return new CollectionItem(type, props);
1502
1503 case PlainValue.Type.COMMENT:
1504 case PlainValue.Type.PLAIN:
1505 return new PlainValue.PlainValue(type, props);
1506
1507 case PlainValue.Type.QUOTE_DOUBLE:
1508 return new QuoteDouble(type, props);
1509
1510 case PlainValue.Type.QUOTE_SINGLE:
1511 return new QuoteSingle(type, props);
1512
1513 /* istanbul ignore next */
1514
1515 default:
1516 return null;
1517 // should never happen
1518 }
1519}
1520/**
1521 * @param {boolean} atLineStart - Node starts at beginning of line
1522 * @param {boolean} inFlow - true if currently in a flow context
1523 * @param {boolean} inCollection - true if currently in a collection context
1524 * @param {number} indent - Current level of indentation
1525 * @param {number} lineStart - Start of the current line
1526 * @param {Node} parent - The parent of the node
1527 * @param {string} src - Source of the YAML document
1528 */
1529
1530
1531class ParseContext {
1532 static parseType(src, offset, inFlow) {
1533 switch (src[offset]) {
1534 case '*':
1535 return PlainValue.Type.ALIAS;
1536
1537 case '>':
1538 return PlainValue.Type.BLOCK_FOLDED;
1539
1540 case '|':
1541 return PlainValue.Type.BLOCK_LITERAL;
1542
1543 case '{':
1544 return PlainValue.Type.FLOW_MAP;
1545
1546 case '[':
1547 return PlainValue.Type.FLOW_SEQ;
1548
1549 case '?':
1550 return !inFlow && PlainValue.Node.atBlank(src, offset + 1, true) ? PlainValue.Type.MAP_KEY : PlainValue.Type.PLAIN;
1551
1552 case ':':
1553 return !inFlow && PlainValue.Node.atBlank(src, offset + 1, true) ? PlainValue.Type.MAP_VALUE : PlainValue.Type.PLAIN;
1554
1555 case '-':
1556 return !inFlow && PlainValue.Node.atBlank(src, offset + 1, true) ? PlainValue.Type.SEQ_ITEM : PlainValue.Type.PLAIN;
1557
1558 case '"':
1559 return PlainValue.Type.QUOTE_DOUBLE;
1560
1561 case "'":
1562 return PlainValue.Type.QUOTE_SINGLE;
1563
1564 default:
1565 return PlainValue.Type.PLAIN;
1566 }
1567 }
1568
1569 constructor(orig = {}, {
1570 atLineStart,
1571 inCollection,
1572 inFlow,
1573 indent,
1574 lineStart,
1575 parent
1576 } = {}) {
1577 PlainValue._defineProperty(this, "parseNode", (overlay, start) => {
1578 if (PlainValue.Node.atDocumentBoundary(this.src, start)) return null;
1579 const context = new ParseContext(this, overlay);
1580 const {
1581 props,
1582 type,
1583 valueStart
1584 } = context.parseProps(start);
1585 const node = createNewNode(type, props);
1586 let offset = node.parse(context, valueStart);
1587 node.range = new PlainValue.Range(start, offset);
1588 /* istanbul ignore if */
1589
1590 if (offset <= start) {
1591 // This should never happen, but if it does, let's make sure to at least
1592 // step one character forward to avoid a busy loop.
1593 node.error = new Error(`Node#parse consumed no characters`);
1594 node.error.parseEnd = offset;
1595 node.error.source = node;
1596 node.range.end = start + 1;
1597 }
1598
1599 if (context.nodeStartsCollection(node)) {
1600 if (!node.error && !context.atLineStart && context.parent.type === PlainValue.Type.DOCUMENT) {
1601 node.error = new PlainValue.YAMLSyntaxError(node, 'Block collection must not have preceding content here (e.g. directives-end indicator)');
1602 }
1603
1604 const collection = new Collection(node);
1605 offset = collection.parse(new ParseContext(context), offset);
1606 collection.range = new PlainValue.Range(start, offset);
1607 return collection;
1608 }
1609
1610 return node;
1611 });
1612
1613 this.atLineStart = atLineStart != null ? atLineStart : orig.atLineStart || false;
1614 this.inCollection = inCollection != null ? inCollection : orig.inCollection || false;
1615 this.inFlow = inFlow != null ? inFlow : orig.inFlow || false;
1616 this.indent = indent != null ? indent : orig.indent;
1617 this.lineStart = lineStart != null ? lineStart : orig.lineStart;
1618 this.parent = parent != null ? parent : orig.parent || {};
1619 this.root = orig.root;
1620 this.src = orig.src;
1621 }
1622
1623 nodeStartsCollection(node) {
1624 const {
1625 inCollection,
1626 inFlow,
1627 src
1628 } = this;
1629 if (inCollection || inFlow) return false;
1630 if (node instanceof CollectionItem) return true; // check for implicit key
1631
1632 let offset = node.range.end;
1633 if (src[offset] === '\n' || src[offset - 1] === '\n') return false;
1634 offset = PlainValue.Node.endOfWhiteSpace(src, offset);
1635 return src[offset] === ':';
1636 } // Anchor and tag are before type, which determines the node implementation
1637 // class; hence this intermediate step.
1638
1639
1640 parseProps(offset) {
1641 const {
1642 inFlow,
1643 parent,
1644 src
1645 } = this;
1646 const props = [];
1647 let lineHasProps = false;
1648 offset = this.atLineStart ? PlainValue.Node.endOfIndent(src, offset) : PlainValue.Node.endOfWhiteSpace(src, offset);
1649 let ch = src[offset];
1650
1651 while (ch === PlainValue.Char.ANCHOR || ch === PlainValue.Char.COMMENT || ch === PlainValue.Char.TAG || ch === '\n') {
1652 if (ch === '\n') {
1653 let inEnd = offset;
1654 let lineStart;
1655
1656 do {
1657 lineStart = inEnd + 1;
1658 inEnd = PlainValue.Node.endOfIndent(src, lineStart);
1659 } while (src[inEnd] === '\n');
1660
1661 const indentDiff = inEnd - (lineStart + this.indent);
1662 const noIndicatorAsIndent = parent.type === PlainValue.Type.SEQ_ITEM && parent.context.atLineStart;
1663 if (src[inEnd] !== '#' && !PlainValue.Node.nextNodeIsIndented(src[inEnd], indentDiff, !noIndicatorAsIndent)) break;
1664 this.atLineStart = true;
1665 this.lineStart = lineStart;
1666 lineHasProps = false;
1667 offset = inEnd;
1668 } else if (ch === PlainValue.Char.COMMENT) {
1669 const end = PlainValue.Node.endOfLine(src, offset + 1);
1670 props.push(new PlainValue.Range(offset, end));
1671 offset = end;
1672 } else {
1673 let end = PlainValue.Node.endOfIdentifier(src, offset + 1);
1674
1675 if (ch === PlainValue.Char.TAG && src[end] === ',' && /^[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+,\d\d\d\d(-\d\d){0,2}\/\S/.test(src.slice(offset + 1, end + 13))) {
1676 // Let's presume we're dealing with a YAML 1.0 domain tag here, rather
1677 // than an empty but 'foo.bar' private-tagged node in a flow collection
1678 // followed without whitespace by a plain string starting with a year
1679 // or date divided by something.
1680 end = PlainValue.Node.endOfIdentifier(src, end + 5);
1681 }
1682
1683 props.push(new PlainValue.Range(offset, end));
1684 lineHasProps = true;
1685 offset = PlainValue.Node.endOfWhiteSpace(src, end);
1686 }
1687
1688 ch = src[offset];
1689 } // '- &a : b' has an anchor on an empty node
1690
1691
1692 if (lineHasProps && ch === ':' && PlainValue.Node.atBlank(src, offset + 1, true)) offset -= 1;
1693 const type = ParseContext.parseType(src, offset, inFlow);
1694 return {
1695 props,
1696 type,
1697 valueStart: offset
1698 };
1699 }
1700 /**
1701 * Parses a node from the source
1702 * @param {ParseContext} overlay
1703 * @param {number} start - Index of first non-whitespace character for the node
1704 * @returns {?Node} - null if at a document boundary
1705 */
1706
1707
1708}
1709
1710// Published as 'yaml/parse-cst'
1711function parse(src) {
1712 const cr = [];
1713
1714 if (src.indexOf('\r') !== -1) {
1715 src = src.replace(/\r\n?/g, (match, offset) => {
1716 if (match.length > 1) cr.push(offset);
1717 return '\n';
1718 });
1719 }
1720
1721 const documents = [];
1722 let offset = 0;
1723
1724 do {
1725 const doc = new Document();
1726 const context = new ParseContext({
1727 src
1728 });
1729 offset = doc.parse(context, offset);
1730 documents.push(doc);
1731 } while (offset < src.length);
1732
1733 documents.setOrigRanges = () => {
1734 if (cr.length === 0) return false;
1735
1736 for (let i = 1; i < cr.length; ++i) cr[i] -= i;
1737
1738 let crOffset = 0;
1739
1740 for (let i = 0; i < documents.length; ++i) {
1741 crOffset = documents[i].setOrigRanges(cr, crOffset);
1742 }
1743
1744 cr.splice(0, cr.length);
1745 return true;
1746 };
1747
1748 documents.toString = () => documents.join('...\n');
1749
1750 return documents;
1751}
1752
1753exports.parse = parse;
Note: See TracBrowser for help on using the repository browser.