"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _constants = require("../constants"); var _errors = require("../errors"); var _Alias = _interopRequireDefault(require("./Alias")); var _BlockValue = _interopRequireDefault(require("./BlockValue")); var _Collection = _interopRequireDefault(require("./Collection")); var _CollectionItem = _interopRequireDefault(require("./CollectionItem")); var _FlowCollection = _interopRequireDefault(require("./FlowCollection")); var _Node = _interopRequireDefault(require("./Node")); var _PlainValue = _interopRequireDefault(require("./PlainValue")); var _QuoteDouble = _interopRequireDefault(require("./QuoteDouble")); var _QuoteSingle = _interopRequireDefault(require("./QuoteSingle")); var _Range = _interopRequireDefault(require("./Range")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 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; } function createNewNode(type, props) { switch (type) { case _constants.Type.ALIAS: return new _Alias.default(type, props); case _constants.Type.BLOCK_FOLDED: case _constants.Type.BLOCK_LITERAL: return new _BlockValue.default(type, props); case _constants.Type.FLOW_MAP: case _constants.Type.FLOW_SEQ: return new _FlowCollection.default(type, props); case _constants.Type.MAP_KEY: case _constants.Type.MAP_VALUE: case _constants.Type.SEQ_ITEM: return new _CollectionItem.default(type, props); case _constants.Type.COMMENT: case _constants.Type.PLAIN: return new _PlainValue.default(type, props); case _constants.Type.QUOTE_DOUBLE: return new _QuoteDouble.default(type, props); case _constants.Type.QUOTE_SINGLE: return new _QuoteSingle.default(type, props); /* istanbul ignore next */ default: return null; // should never happen } } /** * @param {boolean} atLineStart - Node starts at beginning of line * @param {boolean} inFlow - true if currently in a flow context * @param {boolean} inCollection - true if currently in a collection context * @param {number} indent - Current level of indentation * @param {number} lineStart - Start of the current line * @param {Node} parent - The parent of the node * @param {string} src - Source of the YAML document */ class ParseContext { static parseType(src, offset, inFlow) { switch (src[offset]) { case '*': return _constants.Type.ALIAS; case '>': return _constants.Type.BLOCK_FOLDED; case '|': return _constants.Type.BLOCK_LITERAL; case '{': return _constants.Type.FLOW_MAP; case '[': return _constants.Type.FLOW_SEQ; case '?': return !inFlow && _Node.default.atBlank(src, offset + 1, true) ? _constants.Type.MAP_KEY : _constants.Type.PLAIN; case ':': return !inFlow && _Node.default.atBlank(src, offset + 1, true) ? _constants.Type.MAP_VALUE : _constants.Type.PLAIN; case '-': return !inFlow && _Node.default.atBlank(src, offset + 1, true) ? _constants.Type.SEQ_ITEM : _constants.Type.PLAIN; case '"': return _constants.Type.QUOTE_DOUBLE; case "'": return _constants.Type.QUOTE_SINGLE; default: return _constants.Type.PLAIN; } } constructor(orig = {}, { atLineStart, inCollection, inFlow, indent, lineStart, parent } = {}) { _defineProperty(this, "parseNode", (overlay, start) => { if (_Node.default.atDocumentBoundary(this.src, start)) return null; const context = new ParseContext(this, overlay); const { props, type, valueStart } = context.parseProps(start); const node = createNewNode(type, props); let offset = node.parse(context, valueStart); node.range = new _Range.default(start, offset); /* istanbul ignore if */ if (offset <= start) { // This should never happen, but if it does, let's make sure to at least // step one character forward to avoid a busy loop. node.error = new Error(`Node#parse consumed no characters`); node.error.parseEnd = offset; node.error.source = node; node.range.end = start + 1; } if (context.nodeStartsCollection(node)) { if (!node.error && !context.atLineStart && context.parent.type === _constants.Type.DOCUMENT) { node.error = new _errors.YAMLSyntaxError(node, 'Block collection must not have preceding content here (e.g. directives-end indicator)'); } const collection = new _Collection.default(node); offset = collection.parse(new ParseContext(context), offset); collection.range = new _Range.default(start, offset); return collection; } return node; }); this.atLineStart = atLineStart != null ? atLineStart : orig.atLineStart || false; this.inCollection = inCollection != null ? inCollection : orig.inCollection || false; this.inFlow = inFlow != null ? inFlow : orig.inFlow || false; this.indent = indent != null ? indent : orig.indent; this.lineStart = lineStart != null ? lineStart : orig.lineStart; this.parent = parent != null ? parent : orig.parent || {}; this.root = orig.root; this.src = orig.src; } nodeStartsCollection(node) { const { inCollection, inFlow, src } = this; if (inCollection || inFlow) return false; if (node instanceof _CollectionItem.default) return true; // check for implicit key let offset = node.range.end; if (src[offset] === '\n' || src[offset - 1] === '\n') return false; offset = _Node.default.endOfWhiteSpace(src, offset); return src[offset] === ':'; } // Anchor and tag are before type, which determines the node implementation // class; hence this intermediate step. parseProps(offset) { const { inFlow, parent, src } = this; const props = []; let lineHasProps = false; offset = _Node.default.endOfWhiteSpace(src, offset); let ch = src[offset]; while (ch === _constants.Char.ANCHOR || ch === _constants.Char.COMMENT || ch === _constants.Char.TAG || ch === '\n') { if (ch === '\n') { const lineStart = offset + 1; const inEnd = _Node.default.endOfIndent(src, lineStart); const indentDiff = inEnd - (lineStart + this.indent); const noIndicatorAsIndent = parent.type === _constants.Type.SEQ_ITEM && parent.context.atLineStart; if (!_Node.default.nextNodeIsIndented(src[inEnd], indentDiff, !noIndicatorAsIndent)) break; this.atLineStart = true; this.lineStart = lineStart; lineHasProps = false; offset = inEnd; } else if (ch === _constants.Char.COMMENT) { const end = _Node.default.endOfLine(src, offset + 1); props.push(new _Range.default(offset, end)); offset = end; } else { let end = _Node.default.endOfIdentifier(src, offset + 1); if (ch === _constants.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))) { // Let's presume we're dealing with a YAML 1.0 domain tag here, rather // than an empty but 'foo.bar' private-tagged node in a flow collection // followed without whitespace by a plain string starting with a year // or date divided by something. end = _Node.default.endOfIdentifier(src, end + 5); } props.push(new _Range.default(offset, end)); lineHasProps = true; offset = _Node.default.endOfWhiteSpace(src, end); } ch = src[offset]; } // '- &a : b' has an anchor on an empty node if (lineHasProps && ch === ':' && _Node.default.atBlank(src, offset + 1, true)) offset -= 1; const type = ParseContext.parseType(src, offset, inFlow); return { props, type, valueStart: offset }; } /** * Parses a node from the source * @param {ParseContext} overlay * @param {number} start - Index of first non-whitespace character for the node * @returns {?Node} - null if at a document boundary */ } exports.default = ParseContext;