877 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			877 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| const Char = {
 | |
|   ANCHOR: '&',
 | |
|   COMMENT: '#',
 | |
|   TAG: '!',
 | |
|   DIRECTIVES_END: '-',
 | |
|   DOCUMENT_END: '.'
 | |
| };
 | |
| const Type = {
 | |
|   ALIAS: 'ALIAS',
 | |
|   BLANK_LINE: 'BLANK_LINE',
 | |
|   BLOCK_FOLDED: 'BLOCK_FOLDED',
 | |
|   BLOCK_LITERAL: 'BLOCK_LITERAL',
 | |
|   COMMENT: 'COMMENT',
 | |
|   DIRECTIVE: 'DIRECTIVE',
 | |
|   DOCUMENT: 'DOCUMENT',
 | |
|   FLOW_MAP: 'FLOW_MAP',
 | |
|   FLOW_SEQ: 'FLOW_SEQ',
 | |
|   MAP: 'MAP',
 | |
|   MAP_KEY: 'MAP_KEY',
 | |
|   MAP_VALUE: 'MAP_VALUE',
 | |
|   PLAIN: 'PLAIN',
 | |
|   QUOTE_DOUBLE: 'QUOTE_DOUBLE',
 | |
|   QUOTE_SINGLE: 'QUOTE_SINGLE',
 | |
|   SEQ: 'SEQ',
 | |
|   SEQ_ITEM: 'SEQ_ITEM'
 | |
| };
 | |
| const defaultTagPrefix = 'tag:yaml.org,2002:';
 | |
| const defaultTags = {
 | |
|   MAP: 'tag:yaml.org,2002:map',
 | |
|   SEQ: 'tag:yaml.org,2002:seq',
 | |
|   STR: 'tag:yaml.org,2002:str'
 | |
| };
 | |
| 
 | |
| function findLineStarts(src) {
 | |
|   const ls = [0];
 | |
|   let offset = src.indexOf('\n');
 | |
| 
 | |
|   while (offset !== -1) {
 | |
|     offset += 1;
 | |
|     ls.push(offset);
 | |
|     offset = src.indexOf('\n', offset);
 | |
|   }
 | |
| 
 | |
|   return ls;
 | |
| }
 | |
| 
 | |
| function getSrcInfo(cst) {
 | |
|   let lineStarts, src;
 | |
| 
 | |
|   if (typeof cst === 'string') {
 | |
|     lineStarts = findLineStarts(cst);
 | |
|     src = cst;
 | |
|   } else {
 | |
|     if (Array.isArray(cst)) cst = cst[0];
 | |
| 
 | |
|     if (cst && cst.context) {
 | |
|       if (!cst.lineStarts) cst.lineStarts = findLineStarts(cst.context.src);
 | |
|       lineStarts = cst.lineStarts;
 | |
|       src = cst.context.src;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return {
 | |
|     lineStarts,
 | |
|     src
 | |
|   };
 | |
| }
 | |
| /**
 | |
|  * @typedef {Object} LinePos - One-indexed position in the source
 | |
|  * @property {number} line
 | |
|  * @property {number} col
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * Determine the line/col position matching a character offset.
 | |
|  *
 | |
|  * Accepts a source string or a CST document as the second parameter. With
 | |
|  * the latter, starting indices for lines are cached in the document as
 | |
|  * `lineStarts: number[]`.
 | |
|  *
 | |
|  * Returns a one-indexed `{ line, col }` location if found, or
 | |
|  * `undefined` otherwise.
 | |
|  *
 | |
|  * @param {number} offset
 | |
|  * @param {string|Document|Document[]} cst
 | |
|  * @returns {?LinePos}
 | |
|  */
 | |
| 
 | |
| 
 | |
| function getLinePos(offset, cst) {
 | |
|   if (typeof offset !== 'number' || offset < 0) return null;
 | |
|   const {
 | |
|     lineStarts,
 | |
|     src
 | |
|   } = getSrcInfo(cst);
 | |
|   if (!lineStarts || !src || offset > src.length) return null;
 | |
| 
 | |
|   for (let i = 0; i < lineStarts.length; ++i) {
 | |
|     const start = lineStarts[i];
 | |
| 
 | |
|     if (offset < start) {
 | |
|       return {
 | |
|         line: i,
 | |
|         col: offset - lineStarts[i - 1] + 1
 | |
|       };
 | |
|     }
 | |
| 
 | |
|     if (offset === start) return {
 | |
|       line: i + 1,
 | |
|       col: 1
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   const line = lineStarts.length;
 | |
|   return {
 | |
|     line,
 | |
|     col: offset - lineStarts[line - 1] + 1
 | |
|   };
 | |
| }
 | |
| /**
 | |
|  * Get a specified line from the source.
 | |
|  *
 | |
|  * Accepts a source string or a CST document as the second parameter. With
 | |
|  * the latter, starting indices for lines are cached in the document as
 | |
|  * `lineStarts: number[]`.
 | |
|  *
 | |
|  * Returns the line as a string if found, or `null` otherwise.
 | |
|  *
 | |
|  * @param {number} line One-indexed line number
 | |
|  * @param {string|Document|Document[]} cst
 | |
|  * @returns {?string}
 | |
|  */
 | |
| 
 | |
| function getLine(line, cst) {
 | |
|   const {
 | |
|     lineStarts,
 | |
|     src
 | |
|   } = getSrcInfo(cst);
 | |
|   if (!lineStarts || !(line >= 1) || line > lineStarts.length) return null;
 | |
|   const start = lineStarts[line - 1];
 | |
|   let end = lineStarts[line]; // undefined for last line; that's ok for slice()
 | |
| 
 | |
|   while (end && end > start && src[end - 1] === '\n') --end;
 | |
| 
 | |
|   return src.slice(start, end);
 | |
| }
 | |
| /**
 | |
|  * Pretty-print the starting line from the source indicated by the range `pos`
 | |
|  *
 | |
|  * Trims output to `maxWidth` chars while keeping the starting column visible,
 | |
|  * using `…` at either end to indicate dropped characters.
 | |
|  *
 | |
|  * Returns a two-line string (or `null`) with `\n` as separator; the second line
 | |
|  * will hold appropriately indented `^` marks indicating the column range.
 | |
|  *
 | |
|  * @param {Object} pos
 | |
|  * @param {LinePos} pos.start
 | |
|  * @param {LinePos} [pos.end]
 | |
|  * @param {string|Document|Document[]*} cst
 | |
|  * @param {number} [maxWidth=80]
 | |
|  * @returns {?string}
 | |
|  */
 | |
| 
 | |
| function getPrettyContext({
 | |
|   start,
 | |
|   end
 | |
| }, cst, maxWidth = 80) {
 | |
|   let src = getLine(start.line, cst);
 | |
|   if (!src) return null;
 | |
|   let {
 | |
|     col
 | |
|   } = start;
 | |
| 
 | |
|   if (src.length > maxWidth) {
 | |
|     if (col <= maxWidth - 10) {
 | |
|       src = src.substr(0, maxWidth - 1) + '…';
 | |
|     } else {
 | |
|       const halfWidth = Math.round(maxWidth / 2);
 | |
|       if (src.length > col + halfWidth) src = src.substr(0, col + halfWidth - 1) + '…';
 | |
|       col -= src.length - maxWidth;
 | |
|       src = '…' + src.substr(1 - maxWidth);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   let errLen = 1;
 | |
|   let errEnd = '';
 | |
| 
 | |
|   if (end) {
 | |
|     if (end.line === start.line && col + (end.col - start.col) <= maxWidth + 1) {
 | |
|       errLen = end.col - start.col;
 | |
|     } else {
 | |
|       errLen = Math.min(src.length + 1, maxWidth) - col;
 | |
|       errEnd = '…';
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const offset = col > 1 ? ' '.repeat(col - 1) : '';
 | |
|   const err = '^'.repeat(errLen);
 | |
|   return `${src}\n${offset}${err}${errEnd}`;
 | |
| }
 | |
| 
 | |
| class Range {
 | |
|   static copy(orig) {
 | |
|     return new Range(orig.start, orig.end);
 | |
|   }
 | |
| 
 | |
|   constructor(start, end) {
 | |
|     this.start = start;
 | |
|     this.end = end || start;
 | |
|   }
 | |
| 
 | |
|   isEmpty() {
 | |
|     return typeof this.start !== 'number' || !this.end || this.end <= this.start;
 | |
|   }
 | |
|   /**
 | |
|    * Set `origStart` and `origEnd` to point to the original source range for
 | |
|    * this node, which may differ due to dropped CR characters.
 | |
|    *
 | |
|    * @param {number[]} cr - Positions of dropped CR characters
 | |
|    * @param {number} offset - Starting index of `cr` from the last call
 | |
|    * @returns {number} - The next offset, matching the one found for `origStart`
 | |
|    */
 | |
| 
 | |
| 
 | |
|   setOrigRange(cr, offset) {
 | |
|     const {
 | |
|       start,
 | |
|       end
 | |
|     } = this;
 | |
| 
 | |
|     if (cr.length === 0 || end <= cr[0]) {
 | |
|       this.origStart = start;
 | |
|       this.origEnd = end;
 | |
|       return offset;
 | |
|     }
 | |
| 
 | |
|     let i = offset;
 | |
| 
 | |
|     while (i < cr.length) {
 | |
|       if (cr[i] > start) break;else ++i;
 | |
|     }
 | |
| 
 | |
|     this.origStart = start + i;
 | |
|     const nextOffset = i;
 | |
| 
 | |
|     while (i < cr.length) {
 | |
|       // if end was at \n, it should now be at \r
 | |
|       if (cr[i] >= end) break;else ++i;
 | |
|     }
 | |
| 
 | |
|     this.origEnd = end + i;
 | |
|     return nextOffset;
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| /** Root class of all nodes */
 | |
| 
 | |
| class Node {
 | |
|   static addStringTerminator(src, offset, str) {
 | |
|     if (str[str.length - 1] === '\n') return str;
 | |
|     const next = Node.endOfWhiteSpace(src, offset);
 | |
|     return next >= src.length || src[next] === '\n' ? str + '\n' : str;
 | |
|   } // ^(---|...)
 | |
| 
 | |
| 
 | |
|   static atDocumentBoundary(src, offset, sep) {
 | |
|     const ch0 = src[offset];
 | |
|     if (!ch0) return true;
 | |
|     const prev = src[offset - 1];
 | |
|     if (prev && prev !== '\n') return false;
 | |
| 
 | |
|     if (sep) {
 | |
|       if (ch0 !== sep) return false;
 | |
|     } else {
 | |
|       if (ch0 !== Char.DIRECTIVES_END && ch0 !== Char.DOCUMENT_END) return false;
 | |
|     }
 | |
| 
 | |
|     const ch1 = src[offset + 1];
 | |
|     const ch2 = src[offset + 2];
 | |
|     if (ch1 !== ch0 || ch2 !== ch0) return false;
 | |
|     const ch3 = src[offset + 3];
 | |
|     return !ch3 || ch3 === '\n' || ch3 === '\t' || ch3 === ' ';
 | |
|   }
 | |
| 
 | |
|   static endOfIdentifier(src, offset) {
 | |
|     let ch = src[offset];
 | |
|     const isVerbatim = ch === '<';
 | |
|     const notOk = isVerbatim ? ['\n', '\t', ' ', '>'] : ['\n', '\t', ' ', '[', ']', '{', '}', ','];
 | |
| 
 | |
|     while (ch && notOk.indexOf(ch) === -1) ch = src[offset += 1];
 | |
| 
 | |
|     if (isVerbatim && ch === '>') offset += 1;
 | |
|     return offset;
 | |
|   }
 | |
| 
 | |
|   static endOfIndent(src, offset) {
 | |
|     let ch = src[offset];
 | |
| 
 | |
|     while (ch === ' ') ch = src[offset += 1];
 | |
| 
 | |
|     return offset;
 | |
|   }
 | |
| 
 | |
|   static endOfLine(src, offset) {
 | |
|     let ch = src[offset];
 | |
| 
 | |
|     while (ch && ch !== '\n') ch = src[offset += 1];
 | |
| 
 | |
|     return offset;
 | |
|   }
 | |
| 
 | |
|   static endOfWhiteSpace(src, offset) {
 | |
|     let ch = src[offset];
 | |
| 
 | |
|     while (ch === '\t' || ch === ' ') ch = src[offset += 1];
 | |
| 
 | |
|     return offset;
 | |
|   }
 | |
| 
 | |
|   static startOfLine(src, offset) {
 | |
|     let ch = src[offset - 1];
 | |
|     if (ch === '\n') return offset;
 | |
| 
 | |
|     while (ch && ch !== '\n') ch = src[offset -= 1];
 | |
| 
 | |
|     return offset + 1;
 | |
|   }
 | |
|   /**
 | |
|    * End of indentation, or null if the line's indent level is not more
 | |
|    * than `indent`
 | |
|    *
 | |
|    * @param {string} src
 | |
|    * @param {number} indent
 | |
|    * @param {number} lineStart
 | |
|    * @returns {?number}
 | |
|    */
 | |
| 
 | |
| 
 | |
|   static endOfBlockIndent(src, indent, lineStart) {
 | |
|     const inEnd = Node.endOfIndent(src, lineStart);
 | |
| 
 | |
|     if (inEnd > lineStart + indent) {
 | |
|       return inEnd;
 | |
|     } else {
 | |
|       const wsEnd = Node.endOfWhiteSpace(src, inEnd);
 | |
|       const ch = src[wsEnd];
 | |
|       if (!ch || ch === '\n') return wsEnd;
 | |
|     }
 | |
| 
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   static atBlank(src, offset, endAsBlank) {
 | |
|     const ch = src[offset];
 | |
|     return ch === '\n' || ch === '\t' || ch === ' ' || endAsBlank && !ch;
 | |
|   }
 | |
| 
 | |
|   static nextNodeIsIndented(ch, indentDiff, indicatorAsIndent) {
 | |
|     if (!ch || indentDiff < 0) return false;
 | |
|     if (indentDiff > 0) return true;
 | |
|     return indicatorAsIndent && ch === '-';
 | |
|   } // should be at line or string end, or at next non-whitespace char
 | |
| 
 | |
| 
 | |
|   static normalizeOffset(src, offset) {
 | |
|     const ch = src[offset];
 | |
|     return !ch ? offset : ch !== '\n' && src[offset - 1] === '\n' ? offset - 1 : Node.endOfWhiteSpace(src, offset);
 | |
|   } // fold single newline into space, multiple newlines to N - 1 newlines
 | |
|   // presumes src[offset] === '\n'
 | |
| 
 | |
| 
 | |
|   static foldNewline(src, offset, indent) {
 | |
|     let inCount = 0;
 | |
|     let error = false;
 | |
|     let fold = '';
 | |
|     let ch = src[offset + 1];
 | |
| 
 | |
|     while (ch === ' ' || ch === '\t' || ch === '\n') {
 | |
|       switch (ch) {
 | |
|         case '\n':
 | |
|           inCount = 0;
 | |
|           offset += 1;
 | |
|           fold += '\n';
 | |
|           break;
 | |
| 
 | |
|         case '\t':
 | |
|           if (inCount <= indent) error = true;
 | |
|           offset = Node.endOfWhiteSpace(src, offset + 2) - 1;
 | |
|           break;
 | |
| 
 | |
|         case ' ':
 | |
|           inCount += 1;
 | |
|           offset += 1;
 | |
|           break;
 | |
|       }
 | |
| 
 | |
|       ch = src[offset + 1];
 | |
|     }
 | |
| 
 | |
|     if (!fold) fold = ' ';
 | |
|     if (ch && inCount <= indent) error = true;
 | |
|     return {
 | |
|       fold,
 | |
|       offset,
 | |
|       error
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   constructor(type, props, context) {
 | |
|     Object.defineProperty(this, 'context', {
 | |
|       value: context || null,
 | |
|       writable: true
 | |
|     });
 | |
|     this.error = null;
 | |
|     this.range = null;
 | |
|     this.valueRange = null;
 | |
|     this.props = props || [];
 | |
|     this.type = type;
 | |
|     this.value = null;
 | |
|   }
 | |
| 
 | |
|   getPropValue(idx, key, skipKey) {
 | |
|     if (!this.context) return null;
 | |
|     const {
 | |
|       src
 | |
|     } = this.context;
 | |
|     const prop = this.props[idx];
 | |
|     return prop && src[prop.start] === key ? src.slice(prop.start + (skipKey ? 1 : 0), prop.end) : null;
 | |
|   }
 | |
| 
 | |
|   get anchor() {
 | |
|     for (let i = 0; i < this.props.length; ++i) {
 | |
|       const anchor = this.getPropValue(i, Char.ANCHOR, true);
 | |
|       if (anchor != null) return anchor;
 | |
|     }
 | |
| 
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   get comment() {
 | |
|     const comments = [];
 | |
| 
 | |
|     for (let i = 0; i < this.props.length; ++i) {
 | |
|       const comment = this.getPropValue(i, Char.COMMENT, true);
 | |
|       if (comment != null) comments.push(comment);
 | |
|     }
 | |
| 
 | |
|     return comments.length > 0 ? comments.join('\n') : null;
 | |
|   }
 | |
| 
 | |
|   commentHasRequiredWhitespace(start) {
 | |
|     const {
 | |
|       src
 | |
|     } = this.context;
 | |
|     if (this.header && start === this.header.end) return false;
 | |
|     if (!this.valueRange) return false;
 | |
|     const {
 | |
|       end
 | |
|     } = this.valueRange;
 | |
|     return start !== end || Node.atBlank(src, end - 1);
 | |
|   }
 | |
| 
 | |
|   get hasComment() {
 | |
|     if (this.context) {
 | |
|       const {
 | |
|         src
 | |
|       } = this.context;
 | |
| 
 | |
|       for (let i = 0; i < this.props.length; ++i) {
 | |
|         if (src[this.props[i].start] === Char.COMMENT) return true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   get hasProps() {
 | |
|     if (this.context) {
 | |
|       const {
 | |
|         src
 | |
|       } = this.context;
 | |
| 
 | |
|       for (let i = 0; i < this.props.length; ++i) {
 | |
|         if (src[this.props[i].start] !== Char.COMMENT) return true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   get includesTrailingLines() {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   get jsonLike() {
 | |
|     const jsonLikeTypes = [Type.FLOW_MAP, Type.FLOW_SEQ, Type.QUOTE_DOUBLE, Type.QUOTE_SINGLE];
 | |
|     return jsonLikeTypes.indexOf(this.type) !== -1;
 | |
|   }
 | |
| 
 | |
|   get rangeAsLinePos() {
 | |
|     if (!this.range || !this.context) return undefined;
 | |
|     const start = getLinePos(this.range.start, this.context.root);
 | |
|     if (!start) return undefined;
 | |
|     const end = getLinePos(this.range.end, this.context.root);
 | |
|     return {
 | |
|       start,
 | |
|       end
 | |
|     };
 | |
|   }
 | |
| 
 | |
|   get rawValue() {
 | |
|     if (!this.valueRange || !this.context) return null;
 | |
|     const {
 | |
|       start,
 | |
|       end
 | |
|     } = this.valueRange;
 | |
|     return this.context.src.slice(start, end);
 | |
|   }
 | |
| 
 | |
|   get tag() {
 | |
|     for (let i = 0; i < this.props.length; ++i) {
 | |
|       const tag = this.getPropValue(i, Char.TAG, false);
 | |
| 
 | |
|       if (tag != null) {
 | |
|         if (tag[1] === '<') {
 | |
|           return {
 | |
|             verbatim: tag.slice(2, -1)
 | |
|           };
 | |
|         } else {
 | |
|           // eslint-disable-next-line no-unused-vars
 | |
|           const [_, handle, suffix] = tag.match(/^(.*!)([^!]*)$/);
 | |
|           return {
 | |
|             handle,
 | |
|             suffix
 | |
|           };
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   get valueRangeContainsNewline() {
 | |
|     if (!this.valueRange || !this.context) return false;
 | |
|     const {
 | |
|       start,
 | |
|       end
 | |
|     } = this.valueRange;
 | |
|     const {
 | |
|       src
 | |
|     } = this.context;
 | |
| 
 | |
|     for (let i = start; i < end; ++i) {
 | |
|       if (src[i] === '\n') return true;
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   parseComment(start) {
 | |
|     const {
 | |
|       src
 | |
|     } = this.context;
 | |
| 
 | |
|     if (src[start] === Char.COMMENT) {
 | |
|       const end = Node.endOfLine(src, start + 1);
 | |
|       const commentRange = new Range(start, end);
 | |
|       this.props.push(commentRange);
 | |
|       return end;
 | |
|     }
 | |
| 
 | |
|     return start;
 | |
|   }
 | |
|   /**
 | |
|    * Populates the `origStart` and `origEnd` values of all ranges for this
 | |
|    * node. Extended by child classes to handle descendant nodes.
 | |
|    *
 | |
|    * @param {number[]} cr - Positions of dropped CR characters
 | |
|    * @param {number} offset - Starting index of `cr` from the last call
 | |
|    * @returns {number} - The next offset, matching the one found for `origStart`
 | |
|    */
 | |
| 
 | |
| 
 | |
|   setOrigRanges(cr, offset) {
 | |
|     if (this.range) offset = this.range.setOrigRange(cr, offset);
 | |
|     if (this.valueRange) this.valueRange.setOrigRange(cr, offset);
 | |
|     this.props.forEach(prop => prop.setOrigRange(cr, offset));
 | |
|     return offset;
 | |
|   }
 | |
| 
 | |
|   toString() {
 | |
|     const {
 | |
|       context: {
 | |
|         src
 | |
|       },
 | |
|       range,
 | |
|       value
 | |
|     } = this;
 | |
|     if (value != null) return value;
 | |
|     const str = src.slice(range.start, range.end);
 | |
|     return Node.addStringTerminator(src, range.end, str);
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| class YAMLError extends Error {
 | |
|   constructor(name, source, message) {
 | |
|     if (!message || !(source instanceof Node)) throw new Error(`Invalid arguments for new ${name}`);
 | |
|     super();
 | |
|     this.name = name;
 | |
|     this.message = message;
 | |
|     this.source = source;
 | |
|   }
 | |
| 
 | |
|   makePretty() {
 | |
|     if (!this.source) return;
 | |
|     this.nodeType = this.source.type;
 | |
|     const cst = this.source.context && this.source.context.root;
 | |
| 
 | |
|     if (typeof this.offset === 'number') {
 | |
|       this.range = new Range(this.offset, this.offset + 1);
 | |
|       const start = cst && getLinePos(this.offset, cst);
 | |
| 
 | |
|       if (start) {
 | |
|         const end = {
 | |
|           line: start.line,
 | |
|           col: start.col + 1
 | |
|         };
 | |
|         this.linePos = {
 | |
|           start,
 | |
|           end
 | |
|         };
 | |
|       }
 | |
| 
 | |
|       delete this.offset;
 | |
|     } else {
 | |
|       this.range = this.source.range;
 | |
|       this.linePos = this.source.rangeAsLinePos;
 | |
|     }
 | |
| 
 | |
|     if (this.linePos) {
 | |
|       const {
 | |
|         line,
 | |
|         col
 | |
|       } = this.linePos.start;
 | |
|       this.message += ` at line ${line}, column ${col}`;
 | |
|       const ctx = cst && getPrettyContext(this.linePos, cst);
 | |
|       if (ctx) this.message += `:\n\n${ctx}\n`;
 | |
|     }
 | |
| 
 | |
|     delete this.source;
 | |
|   }
 | |
| 
 | |
| }
 | |
| class YAMLReferenceError extends YAMLError {
 | |
|   constructor(source, message) {
 | |
|     super('YAMLReferenceError', source, message);
 | |
|   }
 | |
| 
 | |
| }
 | |
| class YAMLSemanticError extends YAMLError {
 | |
|   constructor(source, message) {
 | |
|     super('YAMLSemanticError', source, message);
 | |
|   }
 | |
| 
 | |
| }
 | |
| class YAMLSyntaxError extends YAMLError {
 | |
|   constructor(source, message) {
 | |
|     super('YAMLSyntaxError', source, message);
 | |
|   }
 | |
| 
 | |
| }
 | |
| class YAMLWarning extends YAMLError {
 | |
|   constructor(source, message) {
 | |
|     super('YAMLWarning', source, message);
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| function _defineProperty(obj, key, value) {
 | |
|   if (key in obj) {
 | |
|     Object.defineProperty(obj, key, {
 | |
|       value: value,
 | |
|       enumerable: true,
 | |
|       configurable: true,
 | |
|       writable: true
 | |
|     });
 | |
|   } else {
 | |
|     obj[key] = value;
 | |
|   }
 | |
| 
 | |
|   return obj;
 | |
| }
 | |
| 
 | |
| class PlainValue extends Node {
 | |
|   static endOfLine(src, start, inFlow) {
 | |
|     let ch = src[start];
 | |
|     let offset = start;
 | |
| 
 | |
|     while (ch && ch !== '\n') {
 | |
|       if (inFlow && (ch === '[' || ch === ']' || ch === '{' || ch === '}' || ch === ',')) break;
 | |
|       const next = src[offset + 1];
 | |
|       if (ch === ':' && (!next || next === '\n' || next === '\t' || next === ' ' || inFlow && next === ',')) break;
 | |
|       if ((ch === ' ' || ch === '\t') && next === '#') break;
 | |
|       offset += 1;
 | |
|       ch = next;
 | |
|     }
 | |
| 
 | |
|     return offset;
 | |
|   }
 | |
| 
 | |
|   get strValue() {
 | |
|     if (!this.valueRange || !this.context) return null;
 | |
|     let {
 | |
|       start,
 | |
|       end
 | |
|     } = this.valueRange;
 | |
|     const {
 | |
|       src
 | |
|     } = this.context;
 | |
|     let ch = src[end - 1];
 | |
| 
 | |
|     while (start < end && (ch === '\n' || ch === '\t' || ch === ' ')) ch = src[--end - 1];
 | |
| 
 | |
|     let str = '';
 | |
| 
 | |
|     for (let i = start; i < end; ++i) {
 | |
|       const ch = src[i];
 | |
| 
 | |
|       if (ch === '\n') {
 | |
|         const {
 | |
|           fold,
 | |
|           offset
 | |
|         } = Node.foldNewline(src, i, -1);
 | |
|         str += fold;
 | |
|         i = offset;
 | |
|       } else if (ch === ' ' || ch === '\t') {
 | |
|         // trim trailing whitespace
 | |
|         const wsStart = i;
 | |
|         let next = src[i + 1];
 | |
| 
 | |
|         while (i < end && (next === ' ' || next === '\t')) {
 | |
|           i += 1;
 | |
|           next = src[i + 1];
 | |
|         }
 | |
| 
 | |
|         if (next !== '\n') str += i > wsStart ? src.slice(wsStart, i + 1) : ch;
 | |
|       } else {
 | |
|         str += ch;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     const ch0 = src[start];
 | |
| 
 | |
|     switch (ch0) {
 | |
|       case '\t':
 | |
|         {
 | |
|           const msg = 'Plain value cannot start with a tab character';
 | |
|           const errors = [new YAMLSemanticError(this, msg)];
 | |
|           return {
 | |
|             errors,
 | |
|             str
 | |
|           };
 | |
|         }
 | |
| 
 | |
|       case '@':
 | |
|       case '`':
 | |
|         {
 | |
|           const msg = `Plain value cannot start with reserved character ${ch0}`;
 | |
|           const errors = [new YAMLSemanticError(this, msg)];
 | |
|           return {
 | |
|             errors,
 | |
|             str
 | |
|           };
 | |
|         }
 | |
| 
 | |
|       default:
 | |
|         return str;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   parseBlockValue(start) {
 | |
|     const {
 | |
|       indent,
 | |
|       inFlow,
 | |
|       src
 | |
|     } = this.context;
 | |
|     let offset = start;
 | |
|     let valueEnd = start;
 | |
| 
 | |
|     for (let ch = src[offset]; ch === '\n'; ch = src[offset]) {
 | |
|       if (Node.atDocumentBoundary(src, offset + 1)) break;
 | |
|       const end = Node.endOfBlockIndent(src, indent, offset + 1);
 | |
|       if (end === null || src[end] === '#') break;
 | |
| 
 | |
|       if (src[end] === '\n') {
 | |
|         offset = end;
 | |
|       } else {
 | |
|         valueEnd = PlainValue.endOfLine(src, end, inFlow);
 | |
|         offset = valueEnd;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (this.valueRange.isEmpty()) this.valueRange.start = start;
 | |
|     this.valueRange.end = valueEnd;
 | |
|     return valueEnd;
 | |
|   }
 | |
|   /**
 | |
|    * Parses a plain value from the source
 | |
|    *
 | |
|    * Accepted forms are:
 | |
|    * ```
 | |
|    * #comment
 | |
|    *
 | |
|    * first line
 | |
|    *
 | |
|    * first line #comment
 | |
|    *
 | |
|    * first line
 | |
|    * block
 | |
|    * lines
 | |
|    *
 | |
|    * #comment
 | |
|    * block
 | |
|    * lines
 | |
|    * ```
 | |
|    * where block lines are empty or have an indent level greater than `indent`.
 | |
|    *
 | |
|    * @param {ParseContext} context
 | |
|    * @param {number} start - Index of first character
 | |
|    * @returns {number} - Index of the character after this scalar, may be `\n`
 | |
|    */
 | |
| 
 | |
| 
 | |
|   parse(context, start) {
 | |
|     this.context = context;
 | |
|     const {
 | |
|       inFlow,
 | |
|       src
 | |
|     } = context;
 | |
|     let offset = start;
 | |
|     const ch = src[offset];
 | |
| 
 | |
|     if (ch && ch !== '#' && ch !== '\n') {
 | |
|       offset = PlainValue.endOfLine(src, start, inFlow);
 | |
|     }
 | |
| 
 | |
|     this.valueRange = new Range(start, offset);
 | |
|     offset = Node.endOfWhiteSpace(src, offset);
 | |
|     offset = this.parseComment(offset);
 | |
| 
 | |
|     if (!this.hasComment || this.valueRange.isEmpty()) {
 | |
|       offset = this.parseBlockValue(offset);
 | |
|     }
 | |
| 
 | |
|     return offset;
 | |
|   }
 | |
| 
 | |
| }
 | |
| 
 | |
| exports.Char = Char;
 | |
| exports.Node = Node;
 | |
| exports.PlainValue = PlainValue;
 | |
| exports.Range = Range;
 | |
| exports.Type = Type;
 | |
| exports.YAMLError = YAMLError;
 | |
| exports.YAMLReferenceError = YAMLReferenceError;
 | |
| exports.YAMLSemanticError = YAMLSemanticError;
 | |
| exports.YAMLSyntaxError = YAMLSyntaxError;
 | |
| exports.YAMLWarning = YAMLWarning;
 | |
| exports._defineProperty = _defineProperty;
 | |
| exports.defaultTagPrefix = defaultTagPrefix;
 | |
| exports.defaultTags = defaultTags;
 |