Comparing version 1.10.0 to 2.0.0-0
@@ -1,6 +0,5 @@ | ||
import { d as defaultTagPrefix, _ as _createForOfIteratorHelper, a as _typeof, b as _createClass, c as _classCallCheck, e as _defineProperty, Y as YAMLSyntaxError, T as Type, f as YAMLWarning, g as YAMLSemanticError, h as _slicedToArray, i as YAMLError, j as _inherits, k as _createSuper } from './PlainValue-ff5147c6.js'; | ||
import { parse as parse$1 } from './parse-cst.js'; | ||
import { b as binaryOptions, a as boolOptions, i as intOptions, n as nullOptions, s as strOptions, N as Node, P as Pair, S as Scalar, c as stringifyString, A as Alias, Y as YAMLSeq, d as YAMLMap, M as Merge, C as Collection, r as resolveNode, e as isEmptyPath, t as toJSON, f as addComment } from './resolveSeq-04825f30.js'; | ||
import { S as Schema } from './Schema-2bf2c74e.js'; | ||
import { w as warn } from './warnings-0e4b70d3.js'; | ||
import { d as defaultTagPrefix, _ as _typeof, a as _createClass, b as _classCallCheck, c as _defineProperty, e as _createForOfIteratorHelper, Y as YAMLSyntaxError, T as Type, f as YAMLWarning, g as YAMLSemanticError, h as _slicedToArray, i as YAMLError, j as _inherits, k as _createSuper } from './PlainValue-6b68dc0e.js'; | ||
import { D as Document$2, p as parse$1 } from './parse-6e806192.js'; | ||
import { b as binaryOptions, a as boolOptions, i as intOptions, n as nullOptions, s as strOptions, N as Node, P as Pair, S as Scalar, c as stringifyString, A as Alias, Y as YAMLSeq, d as YAMLMap, M as Merge, C as Collection, r as resolveNode, e as createNode, f as isEmptyPath, g as collectionFromPath, t as toJSON, h as addComment } from './resolveSeq-3477c430.js'; | ||
import { S as Schema } from './Schema-d14032c2.js'; | ||
@@ -17,4 +16,3 @@ var defaultOptions = { | ||
maxAliasCount: 100, | ||
prettyErrors: false, | ||
// TODO Set true in v2 | ||
prettyErrors: true, | ||
simpleKeys: false, | ||
@@ -91,2 +89,3 @@ version: '1.2' | ||
merge: false, | ||
resolveKnownTags: true, | ||
tagPrefixes: [{ | ||
@@ -150,6 +149,6 @@ handle: '!', | ||
if (item instanceof Scalar) { | ||
obj = item.value; // TODO: deprecate/remove class check | ||
obj = item.value; | ||
var _match = tags.filter(function (t) { | ||
return t.identify && t.identify(obj) || t.class && obj instanceof t.class; | ||
return t.identify && t.identify(obj); | ||
}); | ||
@@ -199,36 +198,12 @@ | ||
function stringify(item, ctx, onComment, onChompKeep) { | ||
var _ctx$doc = ctx.doc, | ||
anchors = _ctx$doc.anchors, | ||
schema = _ctx$doc.schema; | ||
var schema = ctx.doc.schema; | ||
var tagObj; | ||
if (!(item instanceof Node)) { | ||
var createCtx = { | ||
aliasNodes: [], | ||
item = ctx.doc.createNode(item, { | ||
onTagObj: function onTagObj(o) { | ||
return tagObj = o; | ||
}, | ||
prevObjects: new Map() | ||
}; | ||
item = schema.createNode(item, true, null, createCtx); | ||
var _iterator = _createForOfIteratorHelper(createCtx.aliasNodes), | ||
_step; | ||
try { | ||
for (_iterator.s(); !(_step = _iterator.n()).done;) { | ||
var alias = _step.value; | ||
alias.source = alias.source.node; | ||
var name = anchors.getName(alias.source); | ||
if (!name) { | ||
name = anchors.newName(); | ||
anchors.map[name] = alias.source; | ||
} | ||
} | ||
} catch (err) { | ||
_iterator.e(err); | ||
} finally { | ||
_iterator.f(); | ||
} | ||
wrapScalars: true | ||
}); | ||
} | ||
@@ -589,3 +564,3 @@ | ||
var Document = /*#__PURE__*/function () { | ||
function Document(options) { | ||
function Document(value, options) { | ||
_classCallCheck(this, Document); | ||
@@ -596,3 +571,2 @@ | ||
this.comment = null; | ||
this.contents = null; | ||
this.directivesEndMarker = null; | ||
@@ -605,2 +579,11 @@ this.errors = []; | ||
this.warnings = []; | ||
if (value === undefined) { | ||
// note that this.schema is left as null here | ||
this.contents = null; | ||
} else if (value instanceof Document$2) { | ||
this.parse(value); | ||
} else { | ||
this.contents = this.createNode(value); | ||
} | ||
} | ||
@@ -621,2 +604,50 @@ | ||
}, { | ||
key: "createNode", | ||
value: function createNode$1(value) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
onTagObj = _ref.onTagObj, | ||
tag = _ref.tag, | ||
wrapScalars = _ref.wrapScalars; | ||
this.setSchema(); | ||
var aliasNodes = []; | ||
var ctx = { | ||
onAlias: function onAlias(source) { | ||
var alias = new Alias(source); | ||
aliasNodes.push(alias); | ||
return alias; | ||
}, | ||
onTagObj: onTagObj, | ||
prevObjects: new Map(), | ||
schema: this.schema, | ||
wrapScalars: wrapScalars !== false | ||
}; | ||
var node = createNode(value, tag, ctx); | ||
for (var _i = 0, _aliasNodes = aliasNodes; _i < _aliasNodes.length; _i++) { | ||
var alias = _aliasNodes[_i]; | ||
// With circular references, the source node is only resolved after all of | ||
// its child nodes are. This is why anchors are set only after all of the | ||
// nodes have been created. | ||
alias.source = alias.source.node; | ||
var name = this.anchors.getName(alias.source); | ||
if (!name) { | ||
name = this.anchors.newName(); | ||
this.anchors.map[name] = alias.source; | ||
} | ||
} | ||
return node; | ||
} | ||
}, { | ||
key: "createPair", | ||
value: function createPair(key, value) { | ||
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; | ||
var k = this.createNode(key, options); | ||
var v = this.createNode(value, options); | ||
return new Pair(k, v); | ||
} | ||
}, { | ||
key: "delete", | ||
@@ -669,4 +700,9 @@ value: function _delete(key) { | ||
value: function set(key, value) { | ||
assertCollection(this.contents); | ||
this.contents.set(key, value); | ||
if (this.contents == null) { | ||
this.setSchema(); | ||
this.contents = collectionFromPath(this.schema, [key], value); | ||
} else { | ||
assertCollection(this.contents); | ||
this.contents.set(key, value); | ||
} | ||
} | ||
@@ -676,3 +712,6 @@ }, { | ||
value: function setIn(path, value) { | ||
if (isEmptyPath(path)) this.contents = value;else { | ||
if (isEmptyPath(path)) this.contents = value;else if (this.contents == null) { | ||
this.setSchema(); | ||
this.contents = collectionFromPath(this.schema, path, value); | ||
} else { | ||
assertCollection(this.contents); | ||
@@ -761,3 +800,3 @@ this.contents.setIn(path, value); | ||
return listTagNames(this.contents).filter(function (t) { | ||
return t.indexOf(Schema.defaultPrefix) !== 0; | ||
return t.indexOf(defaultTagPrefix) !== 0; | ||
}); | ||
@@ -861,5 +900,5 @@ } | ||
var tagNames = this.listNonDefaultTags(); | ||
this.tagPrefixes.forEach(function (_ref) { | ||
var handle = _ref.handle, | ||
prefix = _ref.prefix; | ||
this.tagPrefixes.forEach(function (_ref2) { | ||
var handle = _ref2.handle, | ||
prefix = _ref2.prefix; | ||
@@ -907,3 +946,3 @@ if (tagNames.some(function (t) { | ||
lines.push(addComment(body, '', contentComment)); | ||
} else if (this.contents !== undefined) { | ||
} else { | ||
lines.push(stringify(this.contents, ctx)); | ||
@@ -926,14 +965,18 @@ } | ||
function createNode(value) { | ||
var wrapScalars = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; | ||
var tag = arguments.length > 2 ? arguments[2] : undefined; | ||
/* global console, process, YAML_SILENCE_WARNINGS */ | ||
function warn(warning, type) { | ||
if (typeof YAML_SILENCE_WARNINGS !== 'undefined' && YAML_SILENCE_WARNINGS) return; | ||
if (tag === undefined && typeof wrapScalars === 'string') { | ||
tag = wrapScalars; | ||
wrapScalars = true; | ||
} | ||
if (typeof process !== 'undefined') { | ||
if (process.env.YAML_SILENCE_WARNINGS) return; // This will throw in Jest if `warning` is an Error instance due to | ||
// https://github.com/facebook/jest/issues/2549 | ||
var options = Object.assign({}, Document.defaults[defaultOptions.version], defaultOptions); | ||
var schema = new Schema(options); | ||
return schema.createNode(value, wrapScalars, tag); | ||
if (process.emitWarning) { | ||
process.emitWarning(warning, type); | ||
return; | ||
} | ||
} // eslint-disable-next-line no-console | ||
console.warn(type ? "".concat(type, ": ").concat(warning) : warning); | ||
} | ||
@@ -946,6 +989,6 @@ | ||
function Document(options) { | ||
function Document(contents, options) { | ||
_classCallCheck(this, Document); | ||
return _super.call(this, Object.assign({}, defaultOptions, options)); | ||
return _super.call(this, contents, Object.assign({}, defaultOptions, options)); | ||
} | ||
@@ -966,3 +1009,3 @@ | ||
var cstDoc = _step.value; | ||
var doc = new Document$1(options); | ||
var doc = new Document$1(undefined, options); | ||
doc.parse(cstDoc, prev); | ||
@@ -983,3 +1026,3 @@ stream.push(doc); | ||
var cst = parse$1(src); | ||
var doc = new Document$1(options).parse(cst[0]); | ||
var doc = new Document$1(cst[0], options); | ||
@@ -1004,9 +1047,7 @@ if (cst.length > 1) { | ||
function stringify$1(value, options) { | ||
var doc = new Document$1(options); | ||
doc.contents = value; | ||
return String(doc); | ||
if (value === undefined) return '\n'; | ||
return new Document$1(value, options).toString(); | ||
} | ||
var YAML = { | ||
createNode: createNode, | ||
defaultOptions: defaultOptions, | ||
@@ -1013,0 +1054,0 @@ Document: Document$1, |
@@ -1,1904 +0,2 @@ | ||
import { j as _inherits, k as _createSuper, c as _classCallCheck, T as Type, b as _createClass, R as Range, N as Node, g as YAMLSemanticError, l as _get, m as _getPrototypeOf, Y as YAMLSyntaxError, C as Char, e as _defineProperty, P as PlainValue } from './PlainValue-ff5147c6.js'; | ||
var BlankLine = /*#__PURE__*/function (_Node) { | ||
_inherits(BlankLine, _Node); | ||
var _super = _createSuper(BlankLine); | ||
function BlankLine() { | ||
_classCallCheck(this, BlankLine); | ||
return _super.call(this, Type.BLANK_LINE); | ||
} | ||
/* istanbul ignore next */ | ||
_createClass(BlankLine, [{ | ||
key: "parse", | ||
/** | ||
* Parses a blank line from the source | ||
* | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first \n character | ||
* @returns {number} - Index of the character after this | ||
*/ | ||
value: function parse(context, start) { | ||
this.context = context; | ||
this.range = new Range(start, start + 1); | ||
return start + 1; | ||
} | ||
}, { | ||
key: "includesTrailingLines", | ||
get: function get() { | ||
// This is never called from anywhere, but if it were, | ||
// this is the value it should return. | ||
return true; | ||
} | ||
}]); | ||
return BlankLine; | ||
}(Node); | ||
var CollectionItem = /*#__PURE__*/function (_Node) { | ||
_inherits(CollectionItem, _Node); | ||
var _super = _createSuper(CollectionItem); | ||
function CollectionItem(type, props) { | ||
var _this; | ||
_classCallCheck(this, CollectionItem); | ||
_this = _super.call(this, type, props); | ||
_this.node = null; | ||
return _this; | ||
} | ||
_createClass(CollectionItem, [{ | ||
key: "parse", | ||
/** | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this | ||
*/ | ||
value: function parse(context, start) { | ||
this.context = context; | ||
var parseNode = context.parseNode, | ||
src = context.src; | ||
var atLineStart = context.atLineStart, | ||
lineStart = context.lineStart; | ||
if (!atLineStart && this.type === Type.SEQ_ITEM) this.error = new YAMLSemanticError(this, 'Sequence items must not have preceding content on the same line'); | ||
var indent = atLineStart ? start - lineStart : context.indent; | ||
var offset = Node.endOfWhiteSpace(src, start + 1); | ||
var ch = src[offset]; | ||
var inlineComment = ch === '#'; | ||
var comments = []; | ||
var blankLine = null; | ||
while (ch === '\n' || ch === '#') { | ||
if (ch === '#') { | ||
var _end = Node.endOfLine(src, offset + 1); | ||
comments.push(new Range(offset, _end)); | ||
offset = _end; | ||
} else { | ||
atLineStart = true; | ||
lineStart = offset + 1; | ||
var wsEnd = Node.endOfWhiteSpace(src, lineStart); | ||
if (src[wsEnd] === '\n' && comments.length === 0) { | ||
blankLine = new BlankLine(); | ||
lineStart = blankLine.parse({ | ||
src: src | ||
}, lineStart); | ||
} | ||
offset = Node.endOfIndent(src, lineStart); | ||
} | ||
ch = src[offset]; | ||
} | ||
if (Node.nextNodeIsIndented(ch, offset - (lineStart + indent), this.type !== Type.SEQ_ITEM)) { | ||
this.node = parseNode({ | ||
atLineStart: atLineStart, | ||
inCollection: false, | ||
indent: indent, | ||
lineStart: lineStart, | ||
parent: this | ||
}, offset); | ||
} else if (ch && lineStart > start + 1) { | ||
offset = lineStart - 1; | ||
} | ||
if (this.node) { | ||
if (blankLine) { | ||
// Only blank lines preceding non-empty nodes are captured. Note that | ||
// this means that collection item range start indices do not always | ||
// increase monotonically. -- eemeli/yaml#126 | ||
var items = context.parent.items || context.parent.contents; | ||
if (items) items.push(blankLine); | ||
} | ||
if (comments.length) Array.prototype.push.apply(this.props, comments); | ||
offset = this.node.range.end; | ||
} else { | ||
if (inlineComment) { | ||
var c = comments[0]; | ||
this.props.push(c); | ||
offset = c.end; | ||
} else { | ||
offset = Node.endOfLine(src, start + 1); | ||
} | ||
} | ||
var end = this.node ? this.node.valueRange.end : offset; | ||
this.valueRange = new Range(start, end); | ||
return offset; | ||
} | ||
}, { | ||
key: "setOrigRanges", | ||
value: function setOrigRanges(cr, offset) { | ||
offset = _get(_getPrototypeOf(CollectionItem.prototype), "setOrigRanges", this).call(this, cr, offset); | ||
return this.node ? this.node.setOrigRanges(cr, offset) : offset; | ||
} | ||
}, { | ||
key: "toString", | ||
value: function toString() { | ||
var src = this.context.src, | ||
node = this.node, | ||
range = this.range, | ||
value = this.value; | ||
if (value != null) return value; | ||
var str = node ? src.slice(range.start, node.range.start) + String(node) : src.slice(range.start, range.end); | ||
return Node.addStringTerminator(src, range.end, str); | ||
} | ||
}, { | ||
key: "includesTrailingLines", | ||
get: function get() { | ||
return !!this.node && this.node.includesTrailingLines; | ||
} | ||
}]); | ||
return CollectionItem; | ||
}(Node); | ||
var Comment = /*#__PURE__*/function (_Node) { | ||
_inherits(Comment, _Node); | ||
var _super = _createSuper(Comment); | ||
function Comment() { | ||
_classCallCheck(this, Comment); | ||
return _super.call(this, Type.COMMENT); | ||
} | ||
/** | ||
* Parses a comment line from the source | ||
* | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this scalar | ||
*/ | ||
_createClass(Comment, [{ | ||
key: "parse", | ||
value: function parse(context, start) { | ||
this.context = context; | ||
var offset = this.parseComment(start); | ||
this.range = new Range(start, offset); | ||
return offset; | ||
} | ||
}]); | ||
return Comment; | ||
}(Node); | ||
function grabCollectionEndComments(node) { | ||
var cnode = node; | ||
while (cnode instanceof CollectionItem) { | ||
cnode = cnode.node; | ||
} | ||
if (!(cnode instanceof Collection)) return null; | ||
var len = cnode.items.length; | ||
var ci = -1; | ||
for (var i = len - 1; i >= 0; --i) { | ||
var n = cnode.items[i]; | ||
if (n.type === Type.COMMENT) { | ||
// Keep sufficiently indented comments with preceding node | ||
var _n$context = n.context, | ||
indent = _n$context.indent, | ||
lineStart = _n$context.lineStart; | ||
if (indent > 0 && n.range.start >= lineStart + indent) break; | ||
ci = i; | ||
} else if (n.type === Type.BLANK_LINE) ci = i;else break; | ||
} | ||
if (ci === -1) return null; | ||
var ca = cnode.items.splice(ci, len - ci); | ||
var prevEnd = ca[0].range.start; | ||
while (true) { | ||
cnode.range.end = prevEnd; | ||
if (cnode.valueRange && cnode.valueRange.end > prevEnd) cnode.valueRange.end = prevEnd; | ||
if (cnode === node) break; | ||
cnode = cnode.context.parent; | ||
} | ||
return ca; | ||
} | ||
var Collection = /*#__PURE__*/function (_Node) { | ||
_inherits(Collection, _Node); | ||
var _super = _createSuper(Collection); | ||
_createClass(Collection, null, [{ | ||
key: "nextContentHasIndent", | ||
value: function nextContentHasIndent(src, offset, indent) { | ||
var lineStart = Node.endOfLine(src, offset) + 1; | ||
offset = Node.endOfWhiteSpace(src, lineStart); | ||
var ch = src[offset]; | ||
if (!ch) return false; | ||
if (offset >= lineStart + indent) return true; | ||
if (ch !== '#' && ch !== '\n') return false; | ||
return Collection.nextContentHasIndent(src, offset, indent); | ||
} | ||
}]); | ||
function Collection(firstItem) { | ||
var _this; | ||
_classCallCheck(this, Collection); | ||
_this = _super.call(this, firstItem.type === Type.SEQ_ITEM ? Type.SEQ : Type.MAP); | ||
for (var i = firstItem.props.length - 1; i >= 0; --i) { | ||
if (firstItem.props[i].start < firstItem.context.lineStart) { | ||
// props on previous line are assumed by the collection | ||
_this.props = firstItem.props.slice(0, i + 1); | ||
firstItem.props = firstItem.props.slice(i + 1); | ||
var itemRange = firstItem.props[0] || firstItem.valueRange; | ||
firstItem.range.start = itemRange.start; | ||
break; | ||
} | ||
} | ||
_this.items = [firstItem]; | ||
var ec = grabCollectionEndComments(firstItem); | ||
if (ec) Array.prototype.push.apply(_this.items, ec); | ||
return _this; | ||
} | ||
_createClass(Collection, [{ | ||
key: "parse", | ||
/** | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this | ||
*/ | ||
value: function parse(context, start) { | ||
this.context = context; | ||
var parseNode = context.parseNode, | ||
src = context.src; // It's easier to recalculate lineStart here rather than tracking down the | ||
// last context from which to read it -- eemeli/yaml#2 | ||
var lineStart = Node.startOfLine(src, start); | ||
var firstItem = this.items[0]; // First-item context needs to be correct for later comment handling | ||
// -- eemeli/yaml#17 | ||
firstItem.context.parent = this; | ||
this.valueRange = Range.copy(firstItem.valueRange); | ||
var indent = firstItem.range.start - firstItem.context.lineStart; | ||
var offset = start; | ||
offset = Node.normalizeOffset(src, offset); | ||
var ch = src[offset]; | ||
var atLineStart = Node.endOfWhiteSpace(src, lineStart) === offset; | ||
var prevIncludesTrailingLines = false; | ||
while (ch) { | ||
while (ch === '\n' || ch === '#') { | ||
if (atLineStart && ch === '\n' && !prevIncludesTrailingLines) { | ||
var blankLine = new BlankLine(); | ||
offset = blankLine.parse({ | ||
src: src | ||
}, offset); | ||
this.valueRange.end = offset; | ||
if (offset >= src.length) { | ||
ch = null; | ||
break; | ||
} | ||
this.items.push(blankLine); | ||
offset -= 1; // blankLine.parse() consumes terminal newline | ||
} else if (ch === '#') { | ||
if (offset < lineStart + indent && !Collection.nextContentHasIndent(src, offset, indent)) { | ||
return offset; | ||
} | ||
var comment = new Comment(); | ||
offset = comment.parse({ | ||
indent: indent, | ||
lineStart: lineStart, | ||
src: src | ||
}, offset); | ||
this.items.push(comment); | ||
this.valueRange.end = offset; | ||
if (offset >= src.length) { | ||
ch = null; | ||
break; | ||
} | ||
} | ||
lineStart = offset + 1; | ||
offset = Node.endOfIndent(src, lineStart); | ||
if (Node.atBlank(src, offset)) { | ||
var wsEnd = Node.endOfWhiteSpace(src, offset); | ||
var next = src[wsEnd]; | ||
if (!next || next === '\n' || next === '#') { | ||
offset = wsEnd; | ||
} | ||
} | ||
ch = src[offset]; | ||
atLineStart = true; | ||
} | ||
if (!ch) { | ||
break; | ||
} | ||
if (offset !== lineStart + indent && (atLineStart || ch !== ':')) { | ||
if (offset < lineStart + indent) { | ||
if (lineStart > start) offset = lineStart; | ||
break; | ||
} else if (!this.error) { | ||
var msg = 'All collection items must start at the same column'; | ||
this.error = new YAMLSyntaxError(this, msg); | ||
} | ||
} | ||
if (firstItem.type === Type.SEQ_ITEM) { | ||
if (ch !== '-') { | ||
if (lineStart > start) offset = lineStart; | ||
break; | ||
} | ||
} else if (ch === '-' && !this.error) { | ||
// map key may start with -, as long as it's followed by a non-whitespace char | ||
var _next = src[offset + 1]; | ||
if (!_next || _next === '\n' || _next === '\t' || _next === ' ') { | ||
var _msg = 'A collection cannot be both a mapping and a sequence'; | ||
this.error = new YAMLSyntaxError(this, _msg); | ||
} | ||
} | ||
var node = parseNode({ | ||
atLineStart: atLineStart, | ||
inCollection: true, | ||
indent: indent, | ||
lineStart: lineStart, | ||
parent: this | ||
}, offset); | ||
if (!node) return offset; // at next document start | ||
this.items.push(node); | ||
this.valueRange.end = node.valueRange.end; | ||
offset = Node.normalizeOffset(src, node.range.end); | ||
ch = src[offset]; | ||
atLineStart = false; | ||
prevIncludesTrailingLines = node.includesTrailingLines; // Need to reset lineStart and atLineStart here if preceding node's range | ||
// has advanced to check the current line's indentation level | ||
// -- eemeli/yaml#10 & eemeli/yaml#38 | ||
if (ch) { | ||
var ls = offset - 1; | ||
var prev = src[ls]; | ||
while (prev === ' ' || prev === '\t') { | ||
prev = src[--ls]; | ||
} | ||
if (prev === '\n') { | ||
lineStart = ls + 1; | ||
atLineStart = true; | ||
} | ||
} | ||
var ec = grabCollectionEndComments(node); | ||
if (ec) Array.prototype.push.apply(this.items, ec); | ||
} | ||
return offset; | ||
} | ||
}, { | ||
key: "setOrigRanges", | ||
value: function setOrigRanges(cr, offset) { | ||
offset = _get(_getPrototypeOf(Collection.prototype), "setOrigRanges", this).call(this, cr, offset); | ||
this.items.forEach(function (node) { | ||
offset = node.setOrigRanges(cr, offset); | ||
}); | ||
return offset; | ||
} | ||
}, { | ||
key: "toString", | ||
value: function toString() { | ||
var src = this.context.src, | ||
items = this.items, | ||
range = this.range, | ||
value = this.value; | ||
if (value != null) return value; | ||
var str = src.slice(range.start, items[0].range.start) + String(items[0]); | ||
for (var i = 1; i < items.length; ++i) { | ||
var item = items[i]; | ||
var _item$context = item.context, | ||
atLineStart = _item$context.atLineStart, | ||
indent = _item$context.indent; | ||
if (atLineStart) for (var _i = 0; _i < indent; ++_i) { | ||
str += ' '; | ||
} | ||
str += String(item); | ||
} | ||
return Node.addStringTerminator(src, range.end, str); | ||
} | ||
}, { | ||
key: "includesTrailingLines", | ||
get: function get() { | ||
return this.items.length > 0; | ||
} | ||
}]); | ||
return Collection; | ||
}(Node); | ||
var Directive = /*#__PURE__*/function (_Node) { | ||
_inherits(Directive, _Node); | ||
var _super = _createSuper(Directive); | ||
function Directive() { | ||
var _this; | ||
_classCallCheck(this, Directive); | ||
_this = _super.call(this, Type.DIRECTIVE); | ||
_this.name = null; | ||
return _this; | ||
} | ||
_createClass(Directive, [{ | ||
key: "parseName", | ||
value: function parseName(start) { | ||
var src = this.context.src; | ||
var offset = start; | ||
var ch = src[offset]; | ||
while (ch && ch !== '\n' && ch !== '\t' && ch !== ' ') { | ||
ch = src[offset += 1]; | ||
} | ||
this.name = src.slice(start, offset); | ||
return offset; | ||
} | ||
}, { | ||
key: "parseParameters", | ||
value: function parseParameters(start) { | ||
var src = this.context.src; | ||
var offset = start; | ||
var ch = src[offset]; | ||
while (ch && ch !== '\n' && ch !== '#') { | ||
ch = src[offset += 1]; | ||
} | ||
this.valueRange = new Range(start, offset); | ||
return offset; | ||
} | ||
}, { | ||
key: "parse", | ||
value: function parse(context, start) { | ||
this.context = context; | ||
var offset = this.parseName(start + 1); | ||
offset = this.parseParameters(offset); | ||
offset = this.parseComment(offset); | ||
this.range = new Range(start, offset); | ||
return offset; | ||
} | ||
}, { | ||
key: "parameters", | ||
get: function get() { | ||
var raw = this.rawValue; | ||
return raw ? raw.trim().split(/[ \t]+/) : []; | ||
} | ||
}]); | ||
return Directive; | ||
}(Node); | ||
var Document = /*#__PURE__*/function (_Node) { | ||
_inherits(Document, _Node); | ||
var _super = _createSuper(Document); | ||
_createClass(Document, null, [{ | ||
key: "startCommentOrEndBlankLine", | ||
value: function startCommentOrEndBlankLine(src, start) { | ||
var offset = Node.endOfWhiteSpace(src, start); | ||
var ch = src[offset]; | ||
return ch === '#' || ch === '\n' ? offset : start; | ||
} | ||
}]); | ||
function Document() { | ||
var _this; | ||
_classCallCheck(this, Document); | ||
_this = _super.call(this, Type.DOCUMENT); | ||
_this.directives = null; | ||
_this.contents = null; | ||
_this.directivesEndMarker = null; | ||
_this.documentEndMarker = null; | ||
return _this; | ||
} | ||
_createClass(Document, [{ | ||
key: "parseDirectives", | ||
value: function parseDirectives(start) { | ||
var src = this.context.src; | ||
this.directives = []; | ||
var atLineStart = true; | ||
var hasDirectives = false; | ||
var offset = start; | ||
while (!Node.atDocumentBoundary(src, offset, Char.DIRECTIVES_END)) { | ||
offset = Document.startCommentOrEndBlankLine(src, offset); | ||
switch (src[offset]) { | ||
case '\n': | ||
if (atLineStart) { | ||
var blankLine = new BlankLine(); | ||
offset = blankLine.parse({ | ||
src: src | ||
}, offset); | ||
if (offset < src.length) { | ||
this.directives.push(blankLine); | ||
} | ||
} else { | ||
offset += 1; | ||
atLineStart = true; | ||
} | ||
break; | ||
case '#': | ||
{ | ||
var comment = new Comment(); | ||
offset = comment.parse({ | ||
src: src | ||
}, offset); | ||
this.directives.push(comment); | ||
atLineStart = false; | ||
} | ||
break; | ||
case '%': | ||
{ | ||
var directive = new Directive(); | ||
offset = directive.parse({ | ||
parent: this, | ||
src: src | ||
}, offset); | ||
this.directives.push(directive); | ||
hasDirectives = true; | ||
atLineStart = false; | ||
} | ||
break; | ||
default: | ||
if (hasDirectives) { | ||
this.error = new YAMLSemanticError(this, 'Missing directives-end indicator line'); | ||
} else if (this.directives.length > 0) { | ||
this.contents = this.directives; | ||
this.directives = []; | ||
} | ||
return offset; | ||
} | ||
} | ||
if (src[offset]) { | ||
this.directivesEndMarker = new Range(offset, offset + 3); | ||
return offset + 3; | ||
} | ||
if (hasDirectives) { | ||
this.error = new YAMLSemanticError(this, 'Missing directives-end indicator line'); | ||
} else if (this.directives.length > 0) { | ||
this.contents = this.directives; | ||
this.directives = []; | ||
} | ||
return offset; | ||
} | ||
}, { | ||
key: "parseContents", | ||
value: function parseContents(start) { | ||
var _this$context = this.context, | ||
parseNode = _this$context.parseNode, | ||
src = _this$context.src; | ||
if (!this.contents) this.contents = []; | ||
var lineStart = start; | ||
while (src[lineStart - 1] === '-') { | ||
lineStart -= 1; | ||
} | ||
var offset = Node.endOfWhiteSpace(src, start); | ||
var atLineStart = lineStart === start; | ||
this.valueRange = new Range(offset); | ||
while (!Node.atDocumentBoundary(src, offset, Char.DOCUMENT_END)) { | ||
switch (src[offset]) { | ||
case '\n': | ||
if (atLineStart) { | ||
var blankLine = new BlankLine(); | ||
offset = blankLine.parse({ | ||
src: src | ||
}, offset); | ||
if (offset < src.length) { | ||
this.contents.push(blankLine); | ||
} | ||
} else { | ||
offset += 1; | ||
atLineStart = true; | ||
} | ||
lineStart = offset; | ||
break; | ||
case '#': | ||
{ | ||
var comment = new Comment(); | ||
offset = comment.parse({ | ||
src: src | ||
}, offset); | ||
this.contents.push(comment); | ||
atLineStart = false; | ||
} | ||
break; | ||
default: | ||
{ | ||
var iEnd = Node.endOfIndent(src, offset); | ||
var context = { | ||
atLineStart: atLineStart, | ||
indent: -1, | ||
inFlow: false, | ||
inCollection: false, | ||
lineStart: lineStart, | ||
parent: this | ||
}; | ||
var node = parseNode(context, iEnd); | ||
if (!node) return this.valueRange.end = iEnd; // at next document start | ||
this.contents.push(node); | ||
offset = node.range.end; | ||
atLineStart = false; | ||
var ec = grabCollectionEndComments(node); | ||
if (ec) Array.prototype.push.apply(this.contents, ec); | ||
} | ||
} | ||
offset = Document.startCommentOrEndBlankLine(src, offset); | ||
} | ||
this.valueRange.end = offset; | ||
if (src[offset]) { | ||
this.documentEndMarker = new Range(offset, offset + 3); | ||
offset += 3; | ||
if (src[offset]) { | ||
offset = Node.endOfWhiteSpace(src, offset); | ||
if (src[offset] === '#') { | ||
var _comment = new Comment(); | ||
offset = _comment.parse({ | ||
src: src | ||
}, offset); | ||
this.contents.push(_comment); | ||
} | ||
switch (src[offset]) { | ||
case '\n': | ||
offset += 1; | ||
break; | ||
case undefined: | ||
break; | ||
default: | ||
this.error = new YAMLSyntaxError(this, 'Document end marker line cannot have a non-comment suffix'); | ||
} | ||
} | ||
} | ||
return offset; | ||
} | ||
/** | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this | ||
*/ | ||
}, { | ||
key: "parse", | ||
value: function parse(context, start) { | ||
context.root = this; | ||
this.context = context; | ||
var src = context.src; | ||
var offset = src.charCodeAt(start) === 0xfeff ? start + 1 : start; // skip BOM | ||
offset = this.parseDirectives(offset); | ||
offset = this.parseContents(offset); | ||
return offset; | ||
} | ||
}, { | ||
key: "setOrigRanges", | ||
value: function setOrigRanges(cr, offset) { | ||
offset = _get(_getPrototypeOf(Document.prototype), "setOrigRanges", this).call(this, cr, offset); | ||
this.directives.forEach(function (node) { | ||
offset = node.setOrigRanges(cr, offset); | ||
}); | ||
if (this.directivesEndMarker) offset = this.directivesEndMarker.setOrigRange(cr, offset); | ||
this.contents.forEach(function (node) { | ||
offset = node.setOrigRanges(cr, offset); | ||
}); | ||
if (this.documentEndMarker) offset = this.documentEndMarker.setOrigRange(cr, offset); | ||
return offset; | ||
} | ||
}, { | ||
key: "toString", | ||
value: function toString() { | ||
var contents = this.contents, | ||
directives = this.directives, | ||
value = this.value; | ||
if (value != null) return value; | ||
var str = directives.join(''); | ||
if (contents.length > 0) { | ||
if (directives.length > 0 || contents[0].type === Type.COMMENT) str += '---\n'; | ||
str += contents.join(''); | ||
} | ||
if (str[str.length - 1] !== '\n') str += '\n'; | ||
return str; | ||
} | ||
}]); | ||
return Document; | ||
}(Node); | ||
var Alias = /*#__PURE__*/function (_Node) { | ||
_inherits(Alias, _Node); | ||
var _super = _createSuper(Alias); | ||
function Alias() { | ||
_classCallCheck(this, Alias); | ||
return _super.apply(this, arguments); | ||
} | ||
_createClass(Alias, [{ | ||
key: "parse", | ||
/** | ||
* Parses an *alias from the source | ||
* | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this scalar | ||
*/ | ||
value: function parse(context, start) { | ||
this.context = context; | ||
var src = context.src; | ||
var offset = Node.endOfIdentifier(src, start + 1); | ||
this.valueRange = new Range(start + 1, offset); | ||
offset = Node.endOfWhiteSpace(src, offset); | ||
offset = this.parseComment(offset); | ||
return offset; | ||
} | ||
}]); | ||
return Alias; | ||
}(Node); | ||
var Chomp = { | ||
CLIP: 'CLIP', | ||
KEEP: 'KEEP', | ||
STRIP: 'STRIP' | ||
}; | ||
var BlockValue = /*#__PURE__*/function (_Node) { | ||
_inherits(BlockValue, _Node); | ||
var _super = _createSuper(BlockValue); | ||
function BlockValue(type, props) { | ||
var _this; | ||
_classCallCheck(this, BlockValue); | ||
_this = _super.call(this, type, props); | ||
_this.blockIndent = null; | ||
_this.chomping = Chomp.CLIP; | ||
_this.header = null; | ||
return _this; | ||
} | ||
_createClass(BlockValue, [{ | ||
key: "parseBlockHeader", | ||
value: function parseBlockHeader(start) { | ||
var src = this.context.src; | ||
var offset = start + 1; | ||
var bi = ''; | ||
while (true) { | ||
var ch = src[offset]; | ||
switch (ch) { | ||
case '-': | ||
this.chomping = Chomp.STRIP; | ||
break; | ||
case '+': | ||
this.chomping = Chomp.KEEP; | ||
break; | ||
case '0': | ||
case '1': | ||
case '2': | ||
case '3': | ||
case '4': | ||
case '5': | ||
case '6': | ||
case '7': | ||
case '8': | ||
case '9': | ||
bi += ch; | ||
break; | ||
default: | ||
this.blockIndent = Number(bi) || null; | ||
this.header = new Range(start, offset); | ||
return offset; | ||
} | ||
offset += 1; | ||
} | ||
} | ||
}, { | ||
key: "parseBlockValue", | ||
value: function parseBlockValue(start) { | ||
var _this$context = this.context, | ||
indent = _this$context.indent, | ||
src = _this$context.src; | ||
var explicit = !!this.blockIndent; | ||
var offset = start; | ||
var valueEnd = start; | ||
var minBlockIndent = 1; | ||
for (var ch = src[offset]; ch === '\n'; ch = src[offset]) { | ||
offset += 1; | ||
if (Node.atDocumentBoundary(src, offset)) break; | ||
var end = Node.endOfBlockIndent(src, indent, offset); // should not include tab? | ||
if (end === null) break; | ||
var _ch = src[end]; | ||
var lineIndent = end - (offset + indent); | ||
if (!this.blockIndent) { | ||
// no explicit block indent, none yet detected | ||
if (src[end] !== '\n') { | ||
// first line with non-whitespace content | ||
if (lineIndent < minBlockIndent) { | ||
var msg = 'Block scalars with more-indented leading empty lines must use an explicit indentation indicator'; | ||
this.error = new YAMLSemanticError(this, msg); | ||
} | ||
this.blockIndent = lineIndent; | ||
} else if (lineIndent > minBlockIndent) { | ||
// empty line with more whitespace | ||
minBlockIndent = lineIndent; | ||
} | ||
} else if (_ch && _ch !== '\n' && lineIndent < this.blockIndent) { | ||
if (src[end] === '#') break; | ||
if (!this.error) { | ||
var _src = explicit ? 'explicit indentation indicator' : 'first line'; | ||
var _msg = "Block scalars must not be less indented than their ".concat(_src); | ||
this.error = new YAMLSemanticError(this, _msg); | ||
} | ||
} | ||
if (src[end] === '\n') { | ||
offset = end; | ||
} else { | ||
offset = valueEnd = Node.endOfLine(src, end); | ||
} | ||
} | ||
if (this.chomping !== Chomp.KEEP) { | ||
offset = src[valueEnd] ? valueEnd + 1 : valueEnd; | ||
} | ||
this.valueRange = new Range(start + 1, offset); | ||
return offset; | ||
} | ||
/** | ||
* Parses a block value from the source | ||
* | ||
* Accepted forms are: | ||
* ``` | ||
* BS | ||
* block | ||
* lines | ||
* | ||
* BS #comment | ||
* block | ||
* lines | ||
* ``` | ||
* where the block style BS matches the regexp `[|>][-+1-9]*` and 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 block | ||
*/ | ||
}, { | ||
key: "parse", | ||
value: function parse(context, start) { | ||
this.context = context; | ||
var src = context.src; | ||
var offset = this.parseBlockHeader(start); | ||
offset = Node.endOfWhiteSpace(src, offset); | ||
offset = this.parseComment(offset); | ||
offset = this.parseBlockValue(offset); | ||
return offset; | ||
} | ||
}, { | ||
key: "setOrigRanges", | ||
value: function setOrigRanges(cr, offset) { | ||
offset = _get(_getPrototypeOf(BlockValue.prototype), "setOrigRanges", this).call(this, cr, offset); | ||
return this.header ? this.header.setOrigRange(cr, offset) : offset; | ||
} | ||
}, { | ||
key: "includesTrailingLines", | ||
get: function get() { | ||
return this.chomping === Chomp.KEEP; | ||
} | ||
}, { | ||
key: "strValue", | ||
get: function get() { | ||
if (!this.valueRange || !this.context) return null; | ||
var _this$valueRange = this.valueRange, | ||
start = _this$valueRange.start, | ||
end = _this$valueRange.end; | ||
var _this$context2 = this.context, | ||
indent = _this$context2.indent, | ||
src = _this$context2.src; | ||
if (this.valueRange.isEmpty()) return ''; | ||
var lastNewLine = null; | ||
var ch = src[end - 1]; | ||
while (ch === '\n' || ch === '\t' || ch === ' ') { | ||
end -= 1; | ||
if (end <= start) { | ||
if (this.chomping === Chomp.KEEP) break;else return ''; // probably never happens | ||
} | ||
if (ch === '\n') lastNewLine = end; | ||
ch = src[end - 1]; | ||
} | ||
var keepStart = end + 1; | ||
if (lastNewLine) { | ||
if (this.chomping === Chomp.KEEP) { | ||
keepStart = lastNewLine; | ||
end = this.valueRange.end; | ||
} else { | ||
end = lastNewLine; | ||
} | ||
} | ||
var bi = indent + this.blockIndent; | ||
var folded = this.type === Type.BLOCK_FOLDED; | ||
var atStart = true; | ||
var str = ''; | ||
var sep = ''; | ||
var prevMoreIndented = false; | ||
for (var i = start; i < end; ++i) { | ||
for (var j = 0; j < bi; ++j) { | ||
if (src[i] !== ' ') break; | ||
i += 1; | ||
} | ||
var _ch2 = src[i]; | ||
if (_ch2 === '\n') { | ||
if (sep === '\n') str += '\n';else sep = '\n'; | ||
} else { | ||
var lineEnd = Node.endOfLine(src, i); | ||
var line = src.slice(i, lineEnd); | ||
i = lineEnd; | ||
if (folded && (_ch2 === ' ' || _ch2 === '\t') && i < keepStart) { | ||
if (sep === ' ') sep = '\n';else if (!prevMoreIndented && !atStart && sep === '\n') sep = '\n\n'; | ||
str += sep + line; //+ ((lineEnd < end && src[lineEnd]) || '') | ||
sep = lineEnd < end && src[lineEnd] || ''; | ||
prevMoreIndented = true; | ||
} else { | ||
str += sep + line; | ||
sep = folded && i < keepStart ? ' ' : '\n'; | ||
prevMoreIndented = false; | ||
} | ||
if (atStart && line !== '') atStart = false; | ||
} | ||
} | ||
return this.chomping === Chomp.STRIP ? str : str + '\n'; | ||
} | ||
}]); | ||
return BlockValue; | ||
}(Node); | ||
var FlowCollection = /*#__PURE__*/function (_Node) { | ||
_inherits(FlowCollection, _Node); | ||
var _super = _createSuper(FlowCollection); | ||
function FlowCollection(type, props) { | ||
var _this; | ||
_classCallCheck(this, FlowCollection); | ||
_this = _super.call(this, type, props); | ||
_this.items = null; | ||
return _this; | ||
} | ||
_createClass(FlowCollection, [{ | ||
key: "prevNodeIsJsonLike", | ||
value: function prevNodeIsJsonLike() { | ||
var idx = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.items.length; | ||
var node = this.items[idx - 1]; | ||
return !!node && (node.jsonLike || node.type === Type.COMMENT && this.prevNodeIsJsonLike(idx - 1)); | ||
} | ||
/** | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this | ||
*/ | ||
}, { | ||
key: "parse", | ||
value: function parse(context, start) { | ||
this.context = context; | ||
var parseNode = context.parseNode, | ||
src = context.src; | ||
var indent = context.indent, | ||
lineStart = context.lineStart; | ||
var char = src[start]; // { or [ | ||
this.items = [{ | ||
char: char, | ||
offset: start | ||
}]; | ||
var offset = Node.endOfWhiteSpace(src, start + 1); | ||
char = src[offset]; | ||
while (char && char !== ']' && char !== '}') { | ||
switch (char) { | ||
case '\n': | ||
{ | ||
lineStart = offset + 1; | ||
var wsEnd = Node.endOfWhiteSpace(src, lineStart); | ||
if (src[wsEnd] === '\n') { | ||
var blankLine = new BlankLine(); | ||
lineStart = blankLine.parse({ | ||
src: src | ||
}, lineStart); | ||
this.items.push(blankLine); | ||
} | ||
offset = Node.endOfIndent(src, lineStart); | ||
if (offset <= lineStart + indent) { | ||
char = src[offset]; | ||
if (offset < lineStart + indent || char !== ']' && char !== '}') { | ||
var msg = 'Insufficient indentation in flow collection'; | ||
this.error = new YAMLSemanticError(this, msg); | ||
} | ||
} | ||
} | ||
break; | ||
case ',': | ||
{ | ||
this.items.push({ | ||
char: char, | ||
offset: offset | ||
}); | ||
offset += 1; | ||
} | ||
break; | ||
case '#': | ||
{ | ||
var comment = new Comment(); | ||
offset = comment.parse({ | ||
src: src | ||
}, offset); | ||
this.items.push(comment); | ||
} | ||
break; | ||
case '?': | ||
case ':': | ||
{ | ||
var next = src[offset + 1]; | ||
if (next === '\n' || next === '\t' || next === ' ' || next === ',' || // in-flow : after JSON-like key does not need to be followed by whitespace | ||
char === ':' && this.prevNodeIsJsonLike()) { | ||
this.items.push({ | ||
char: char, | ||
offset: offset | ||
}); | ||
offset += 1; | ||
break; | ||
} | ||
} | ||
// fallthrough | ||
default: | ||
{ | ||
var node = parseNode({ | ||
atLineStart: false, | ||
inCollection: false, | ||
inFlow: true, | ||
indent: -1, | ||
lineStart: lineStart, | ||
parent: this | ||
}, offset); | ||
if (!node) { | ||
// at next document start | ||
this.valueRange = new Range(start, offset); | ||
return offset; | ||
} | ||
this.items.push(node); | ||
offset = Node.normalizeOffset(src, node.range.end); | ||
} | ||
} | ||
offset = Node.endOfWhiteSpace(src, offset); | ||
char = src[offset]; | ||
} | ||
this.valueRange = new Range(start, offset + 1); | ||
if (char) { | ||
this.items.push({ | ||
char: char, | ||
offset: offset | ||
}); | ||
offset = Node.endOfWhiteSpace(src, offset + 1); | ||
offset = this.parseComment(offset); | ||
} | ||
return offset; | ||
} | ||
}, { | ||
key: "setOrigRanges", | ||
value: function setOrigRanges(cr, offset) { | ||
offset = _get(_getPrototypeOf(FlowCollection.prototype), "setOrigRanges", this).call(this, cr, offset); | ||
this.items.forEach(function (node) { | ||
if (node instanceof Node) { | ||
offset = node.setOrigRanges(cr, offset); | ||
} else if (cr.length === 0) { | ||
node.origOffset = node.offset; | ||
} else { | ||
var i = offset; | ||
while (i < cr.length) { | ||
if (cr[i] > node.offset) break;else ++i; | ||
} | ||
node.origOffset = node.offset + i; | ||
offset = i; | ||
} | ||
}); | ||
return offset; | ||
} | ||
}, { | ||
key: "toString", | ||
value: function toString() { | ||
var src = this.context.src, | ||
items = this.items, | ||
range = this.range, | ||
value = this.value; | ||
if (value != null) return value; | ||
var nodes = items.filter(function (item) { | ||
return item instanceof Node; | ||
}); | ||
var str = ''; | ||
var prevEnd = range.start; | ||
nodes.forEach(function (node) { | ||
var prefix = src.slice(prevEnd, node.range.start); | ||
prevEnd = node.range.end; | ||
str += prefix + String(node); | ||
if (str[str.length - 1] === '\n' && src[prevEnd - 1] !== '\n' && src[prevEnd] === '\n') { | ||
// Comment range does not include the terminal newline, but its | ||
// stringified value does. Without this fix, newlines at comment ends | ||
// get duplicated. | ||
prevEnd += 1; | ||
} | ||
}); | ||
str += src.slice(prevEnd, range.end); | ||
return Node.addStringTerminator(src, range.end, str); | ||
} | ||
}]); | ||
return FlowCollection; | ||
}(Node); | ||
var QuoteDouble = /*#__PURE__*/function (_Node) { | ||
_inherits(QuoteDouble, _Node); | ||
var _super = _createSuper(QuoteDouble); | ||
function QuoteDouble() { | ||
_classCallCheck(this, QuoteDouble); | ||
return _super.apply(this, arguments); | ||
} | ||
_createClass(QuoteDouble, [{ | ||
key: "parseCharCode", | ||
value: function parseCharCode(offset, length, errors) { | ||
var src = this.context.src; | ||
var cc = src.substr(offset, length); | ||
var ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc); | ||
var code = ok ? parseInt(cc, 16) : NaN; | ||
if (isNaN(code)) { | ||
errors.push(new YAMLSyntaxError(this, "Invalid escape sequence ".concat(src.substr(offset - 2, length + 2)))); | ||
return src.substr(offset - 2, length + 2); | ||
} | ||
return String.fromCodePoint(code); | ||
} | ||
/** | ||
* Parses a "double quoted" value from the source | ||
* | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this scalar | ||
*/ | ||
}, { | ||
key: "parse", | ||
value: function parse(context, start) { | ||
this.context = context; | ||
var src = context.src; | ||
var offset = QuoteDouble.endOfQuote(src, start + 1); | ||
this.valueRange = new Range(start, offset); | ||
offset = Node.endOfWhiteSpace(src, offset); | ||
offset = this.parseComment(offset); | ||
return offset; | ||
} | ||
}, { | ||
key: "strValue", | ||
/** | ||
* @returns {string | { str: string, errors: YAMLSyntaxError[] }} | ||
*/ | ||
get: function get() { | ||
if (!this.valueRange || !this.context) return null; | ||
var errors = []; | ||
var _this$valueRange = this.valueRange, | ||
start = _this$valueRange.start, | ||
end = _this$valueRange.end; | ||
var _this$context = this.context, | ||
indent = _this$context.indent, | ||
src = _this$context.src; | ||
if (src[end - 1] !== '"') errors.push(new YAMLSyntaxError(this, 'Missing closing "quote')); // Using String#replace is too painful with escaped newlines preceded by | ||
// escaped backslashes; also, this should be faster. | ||
var str = ''; | ||
for (var i = start + 1; i < end - 1; ++i) { | ||
var ch = src[i]; | ||
if (ch === '\n') { | ||
if (Node.atDocumentBoundary(src, i + 1)) errors.push(new YAMLSemanticError(this, 'Document boundary indicators are not allowed within string values')); | ||
var _Node$foldNewline = Node.foldNewline(src, i, indent), | ||
fold = _Node$foldNewline.fold, | ||
offset = _Node$foldNewline.offset, | ||
error = _Node$foldNewline.error; | ||
str += fold; | ||
i = offset; | ||
if (error) errors.push(new YAMLSemanticError(this, 'Multi-line double-quoted string needs to be sufficiently indented')); | ||
} else if (ch === '\\') { | ||
i += 1; | ||
switch (src[i]) { | ||
case '0': | ||
str += '\0'; | ||
break; | ||
// null character | ||
case 'a': | ||
str += '\x07'; | ||
break; | ||
// bell character | ||
case 'b': | ||
str += '\b'; | ||
break; | ||
// backspace | ||
case 'e': | ||
str += '\x1b'; | ||
break; | ||
// escape character | ||
case 'f': | ||
str += '\f'; | ||
break; | ||
// form feed | ||
case 'n': | ||
str += '\n'; | ||
break; | ||
// line feed | ||
case 'r': | ||
str += '\r'; | ||
break; | ||
// carriage return | ||
case 't': | ||
str += '\t'; | ||
break; | ||
// horizontal tab | ||
case 'v': | ||
str += '\v'; | ||
break; | ||
// vertical tab | ||
case 'N': | ||
str += "\x85"; | ||
break; | ||
// Unicode next line | ||
case '_': | ||
str += "\xA0"; | ||
break; | ||
// Unicode non-breaking space | ||
case 'L': | ||
str += "\u2028"; | ||
break; | ||
// Unicode line separator | ||
case 'P': | ||
str += "\u2029"; | ||
break; | ||
// Unicode paragraph separator | ||
case ' ': | ||
str += ' '; | ||
break; | ||
case '"': | ||
str += '"'; | ||
break; | ||
case '/': | ||
str += '/'; | ||
break; | ||
case '\\': | ||
str += '\\'; | ||
break; | ||
case '\t': | ||
str += '\t'; | ||
break; | ||
case 'x': | ||
str += this.parseCharCode(i + 1, 2, errors); | ||
i += 2; | ||
break; | ||
case 'u': | ||
str += this.parseCharCode(i + 1, 4, errors); | ||
i += 4; | ||
break; | ||
case 'U': | ||
str += this.parseCharCode(i + 1, 8, errors); | ||
i += 8; | ||
break; | ||
case '\n': | ||
// skip escaped newlines, but still trim the following line | ||
while (src[i + 1] === ' ' || src[i + 1] === '\t') { | ||
i += 1; | ||
} | ||
break; | ||
default: | ||
errors.push(new YAMLSyntaxError(this, "Invalid escape sequence ".concat(src.substr(i - 1, 2)))); | ||
str += '\\' + src[i]; | ||
} | ||
} else if (ch === ' ' || ch === '\t') { | ||
// trim trailing whitespace | ||
var wsStart = i; | ||
var next = src[i + 1]; | ||
while (next === ' ' || next === '\t') { | ||
i += 1; | ||
next = src[i + 1]; | ||
} | ||
if (next !== '\n') str += i > wsStart ? src.slice(wsStart, i + 1) : ch; | ||
} else { | ||
str += ch; | ||
} | ||
} | ||
return errors.length > 0 ? { | ||
errors: errors, | ||
str: str | ||
} : str; | ||
} | ||
}], [{ | ||
key: "endOfQuote", | ||
value: function endOfQuote(src, offset) { | ||
var ch = src[offset]; | ||
while (ch && ch !== '"') { | ||
offset += ch === '\\' ? 2 : 1; | ||
ch = src[offset]; | ||
} | ||
return offset + 1; | ||
} | ||
}]); | ||
return QuoteDouble; | ||
}(Node); | ||
var QuoteSingle = /*#__PURE__*/function (_Node) { | ||
_inherits(QuoteSingle, _Node); | ||
var _super = _createSuper(QuoteSingle); | ||
function QuoteSingle() { | ||
_classCallCheck(this, QuoteSingle); | ||
return _super.apply(this, arguments); | ||
} | ||
_createClass(QuoteSingle, [{ | ||
key: "parse", | ||
/** | ||
* Parses a 'single quoted' value from the source | ||
* | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this scalar | ||
*/ | ||
value: function parse(context, start) { | ||
this.context = context; | ||
var src = context.src; | ||
var offset = QuoteSingle.endOfQuote(src, start + 1); | ||
this.valueRange = new Range(start, offset); | ||
offset = Node.endOfWhiteSpace(src, offset); | ||
offset = this.parseComment(offset); | ||
return offset; | ||
} | ||
}, { | ||
key: "strValue", | ||
/** | ||
* @returns {string | { str: string, errors: YAMLSyntaxError[] }} | ||
*/ | ||
get: function get() { | ||
if (!this.valueRange || !this.context) return null; | ||
var errors = []; | ||
var _this$valueRange = this.valueRange, | ||
start = _this$valueRange.start, | ||
end = _this$valueRange.end; | ||
var _this$context = this.context, | ||
indent = _this$context.indent, | ||
src = _this$context.src; | ||
if (src[end - 1] !== "'") errors.push(new YAMLSyntaxError(this, "Missing closing 'quote")); | ||
var str = ''; | ||
for (var i = start + 1; i < end - 1; ++i) { | ||
var ch = src[i]; | ||
if (ch === '\n') { | ||
if (Node.atDocumentBoundary(src, i + 1)) errors.push(new YAMLSemanticError(this, 'Document boundary indicators are not allowed within string values')); | ||
var _Node$foldNewline = Node.foldNewline(src, i, indent), | ||
fold = _Node$foldNewline.fold, | ||
offset = _Node$foldNewline.offset, | ||
error = _Node$foldNewline.error; | ||
str += fold; | ||
i = offset; | ||
if (error) errors.push(new YAMLSemanticError(this, 'Multi-line single-quoted string needs to be sufficiently indented')); | ||
} else if (ch === "'") { | ||
str += ch; | ||
i += 1; | ||
if (src[i] !== "'") errors.push(new YAMLSyntaxError(this, 'Unescaped single quote? This should not happen.')); | ||
} else if (ch === ' ' || ch === '\t') { | ||
// trim trailing whitespace | ||
var wsStart = i; | ||
var next = src[i + 1]; | ||
while (next === ' ' || next === '\t') { | ||
i += 1; | ||
next = src[i + 1]; | ||
} | ||
if (next !== '\n') str += i > wsStart ? src.slice(wsStart, i + 1) : ch; | ||
} else { | ||
str += ch; | ||
} | ||
} | ||
return errors.length > 0 ? { | ||
errors: errors, | ||
str: str | ||
} : str; | ||
} | ||
}], [{ | ||
key: "endOfQuote", | ||
value: function endOfQuote(src, offset) { | ||
var ch = src[offset]; | ||
while (ch) { | ||
if (ch === "'") { | ||
if (src[offset + 1] !== "'") break; | ||
ch = src[offset += 2]; | ||
} else { | ||
ch = src[offset += 1]; | ||
} | ||
} | ||
return offset + 1; | ||
} | ||
}]); | ||
return QuoteSingle; | ||
}(Node); | ||
function createNewNode(type, props) { | ||
switch (type) { | ||
case Type.ALIAS: | ||
return new Alias(type, props); | ||
case Type.BLOCK_FOLDED: | ||
case Type.BLOCK_LITERAL: | ||
return new BlockValue(type, props); | ||
case Type.FLOW_MAP: | ||
case Type.FLOW_SEQ: | ||
return new FlowCollection(type, props); | ||
case Type.MAP_KEY: | ||
case Type.MAP_VALUE: | ||
case Type.SEQ_ITEM: | ||
return new CollectionItem(type, props); | ||
case Type.COMMENT: | ||
case Type.PLAIN: | ||
return new PlainValue(type, props); | ||
case Type.QUOTE_DOUBLE: | ||
return new QuoteDouble(type, props); | ||
case Type.QUOTE_SINGLE: | ||
return new QuoteSingle(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 | ||
*/ | ||
var ParseContext = /*#__PURE__*/function () { | ||
_createClass(ParseContext, null, [{ | ||
key: "parseType", | ||
value: function parseType(src, offset, inFlow) { | ||
switch (src[offset]) { | ||
case '*': | ||
return Type.ALIAS; | ||
case '>': | ||
return Type.BLOCK_FOLDED; | ||
case '|': | ||
return Type.BLOCK_LITERAL; | ||
case '{': | ||
return Type.FLOW_MAP; | ||
case '[': | ||
return Type.FLOW_SEQ; | ||
case '?': | ||
return !inFlow && Node.atBlank(src, offset + 1, true) ? Type.MAP_KEY : Type.PLAIN; | ||
case ':': | ||
return !inFlow && Node.atBlank(src, offset + 1, true) ? Type.MAP_VALUE : Type.PLAIN; | ||
case '-': | ||
return !inFlow && Node.atBlank(src, offset + 1, true) ? Type.SEQ_ITEM : Type.PLAIN; | ||
case '"': | ||
return Type.QUOTE_DOUBLE; | ||
case "'": | ||
return Type.QUOTE_SINGLE; | ||
default: | ||
return Type.PLAIN; | ||
} | ||
} | ||
}]); | ||
function ParseContext() { | ||
var _this = this; | ||
var orig = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
atLineStart = _ref.atLineStart, | ||
inCollection = _ref.inCollection, | ||
inFlow = _ref.inFlow, | ||
indent = _ref.indent, | ||
lineStart = _ref.lineStart, | ||
parent = _ref.parent; | ||
_classCallCheck(this, ParseContext); | ||
_defineProperty(this, "parseNode", function (overlay, start) { | ||
if (Node.atDocumentBoundary(_this.src, start)) return null; | ||
var context = new ParseContext(_this, overlay); | ||
var _context$parseProps = context.parseProps(start), | ||
props = _context$parseProps.props, | ||
type = _context$parseProps.type, | ||
valueStart = _context$parseProps.valueStart; | ||
var node = createNewNode(type, props); | ||
var offset = node.parse(context, valueStart); | ||
node.range = new Range(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 === Type.DOCUMENT) { | ||
node.error = new YAMLSyntaxError(node, 'Block collection must not have preceding content here (e.g. directives-end indicator)'); | ||
} | ||
var collection = new Collection(node); | ||
offset = collection.parse(new ParseContext(context), offset); | ||
collection.range = new Range(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; | ||
} | ||
_createClass(ParseContext, [{ | ||
key: "nodeStartsCollection", | ||
value: function nodeStartsCollection(node) { | ||
var inCollection = this.inCollection, | ||
inFlow = this.inFlow, | ||
src = this.src; | ||
if (inCollection || inFlow) return false; | ||
if (node instanceof CollectionItem) return true; // check for implicit key | ||
var offset = node.range.end; | ||
if (src[offset] === '\n' || src[offset - 1] === '\n') return false; | ||
offset = Node.endOfWhiteSpace(src, offset); | ||
return src[offset] === ':'; | ||
} // Anchor and tag are before type, which determines the node implementation | ||
// class; hence this intermediate step. | ||
}, { | ||
key: "parseProps", | ||
value: function parseProps(offset) { | ||
var inFlow = this.inFlow, | ||
parent = this.parent, | ||
src = this.src; | ||
var props = []; | ||
var lineHasProps = false; | ||
offset = this.atLineStart ? Node.endOfIndent(src, offset) : Node.endOfWhiteSpace(src, offset); | ||
var ch = src[offset]; | ||
while (ch === Char.ANCHOR || ch === Char.COMMENT || ch === Char.TAG || ch === '\n') { | ||
if (ch === '\n') { | ||
var lineStart = offset + 1; | ||
var inEnd = Node.endOfIndent(src, lineStart); | ||
var indentDiff = inEnd - (lineStart + this.indent); | ||
var noIndicatorAsIndent = parent.type === Type.SEQ_ITEM && parent.context.atLineStart; | ||
if (!Node.nextNodeIsIndented(src[inEnd], indentDiff, !noIndicatorAsIndent)) break; | ||
this.atLineStart = true; | ||
this.lineStart = lineStart; | ||
lineHasProps = false; | ||
offset = inEnd; | ||
} else if (ch === Char.COMMENT) { | ||
var end = Node.endOfLine(src, offset + 1); | ||
props.push(new Range(offset, end)); | ||
offset = end; | ||
} else { | ||
var _end = Node.endOfIdentifier(src, offset + 1); | ||
if (ch === 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.endOfIdentifier(src, _end + 5); | ||
} | ||
props.push(new Range(offset, _end)); | ||
lineHasProps = true; | ||
offset = Node.endOfWhiteSpace(src, _end); | ||
} | ||
ch = src[offset]; | ||
} // '- &a : b' has an anchor on an empty node | ||
if (lineHasProps && ch === ':' && Node.atBlank(src, offset + 1, true)) offset -= 1; | ||
var type = ParseContext.parseType(src, offset, inFlow); | ||
return { | ||
props: props, | ||
type: 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 | ||
*/ | ||
}]); | ||
return ParseContext; | ||
}(); | ||
// Published as 'yaml/parse-cst' | ||
function parse(src) { | ||
var cr = []; | ||
if (src.indexOf('\r') !== -1) { | ||
src = src.replace(/\r\n?/g, function (match, offset) { | ||
if (match.length > 1) cr.push(offset); | ||
return '\n'; | ||
}); | ||
} | ||
var documents = []; | ||
var offset = 0; | ||
do { | ||
var doc = new Document(); | ||
var context = new ParseContext({ | ||
src: src | ||
}); | ||
offset = doc.parse(context, offset); | ||
documents.push(doc); | ||
} while (offset < src.length); | ||
documents.setOrigRanges = function () { | ||
if (cr.length === 0) return false; | ||
for (var i = 1; i < cr.length; ++i) { | ||
cr[i] -= i; | ||
} | ||
var crOffset = 0; | ||
for (var _i = 0; _i < documents.length; ++_i) { | ||
crOffset = documents[_i].setOrigRanges(cr, crOffset); | ||
} | ||
cr.splice(0, cr.length); | ||
return true; | ||
}; | ||
documents.toString = function () { | ||
return documents.join('...\n'); | ||
}; | ||
return documents; | ||
} | ||
export { parse }; | ||
import './PlainValue-6b68dc0e.js'; | ||
export { p as parse } from './parse-6e806192.js'; |
@@ -1,4 +0,3 @@ | ||
import './PlainValue-ff5147c6.js'; | ||
export { A as Alias, C as Collection, M as Merge, N as Node, P as Pair, S as Scalar, d as YAMLMap, Y as YAMLSeq, b as binaryOptions, a as boolOptions, i as intOptions, n as nullOptions, s as strOptions } from './resolveSeq-04825f30.js'; | ||
export { S as Schema } from './Schema-2bf2c74e.js'; | ||
import './warnings-0e4b70d3.js'; | ||
import './PlainValue-6b68dc0e.js'; | ||
export { A as Alias, C as Collection, M as Merge, N as Node, P as Pair, S as Scalar, d as YAMLMap, Y as YAMLSeq, b as binaryOptions, a as boolOptions, i as intOptions, n as nullOptions, s as strOptions } from './resolveSeq-3477c430.js'; | ||
export { S as Schema } from './Schema-d14032c2.js'; |
@@ -1,2 +0,2 @@ | ||
export { T as Type, i as YAMLError, o as YAMLReferenceError, g as YAMLSemanticError, Y as YAMLSyntaxError, f as YAMLWarning } from './PlainValue-ff5147c6.js'; | ||
export { l as findPair, g as parseMap, h as parseSeq, k as stringifyNumber, c as stringifyString, t as toJSON } from './resolveSeq-04825f30.js'; | ||
export { T as Type, i as YAMLError, p as YAMLReferenceError, g as YAMLSemanticError, Y as YAMLSyntaxError, f as YAMLWarning } from './PlainValue-6b68dc0e.js'; | ||
export { p as findPair, j as parseMap, l as parseSeq, o as stringifyNumber, c as stringifyString, t as toJSON } from './resolveSeq-3477c430.js'; |
'use strict'; | ||
var PlainValue = require('./PlainValue-ec8e588e.js'); | ||
var parseCst = require('./parse-cst.js'); | ||
require('./resolveSeq-4a68b39b.js'); | ||
var Document$1 = require('./Document-2cf6b08c.js'); | ||
var Schema = require('./Schema-42e9705c.js'); | ||
var warnings = require('./warnings-39684f17.js'); | ||
var parseCst = require('./parse-ab8de154.js'); | ||
require('./resolveSeq-6b2dc4ac.js'); | ||
var Document$1 = require('./Document-59bd9290.js'); | ||
require('./Schema-fe1471c4.js'); | ||
function createNode(value, wrapScalars = true, tag) { | ||
if (tag === undefined && typeof wrapScalars === 'string') { | ||
tag = wrapScalars; | ||
wrapScalars = true; | ||
} | ||
/* global console, process, YAML_SILENCE_WARNINGS */ | ||
function warn(warning, type) { | ||
if (typeof YAML_SILENCE_WARNINGS !== 'undefined' && YAML_SILENCE_WARNINGS) return; | ||
const options = Object.assign({}, Document$1.Document.defaults[Document$1.defaultOptions.version], Document$1.defaultOptions); | ||
const schema = new Schema.Schema(options); | ||
return schema.createNode(value, wrapScalars, tag); | ||
if (typeof process !== 'undefined') { | ||
if (process.env.YAML_SILENCE_WARNINGS) return; // This will throw in Jest if `warning` is an Error instance due to | ||
// https://github.com/facebook/jest/issues/2549 | ||
if (process.emitWarning) { | ||
process.emitWarning(warning, type); | ||
return; | ||
} | ||
} // eslint-disable-next-line no-console | ||
console.warn(type ? `${type}: ${warning}` : warning); | ||
} | ||
class Document extends Document$1.Document { | ||
constructor(options) { | ||
super(Object.assign({}, Document$1.defaultOptions, options)); | ||
constructor(contents, options) { | ||
super(contents, Object.assign({}, Document$1.defaultOptions, options)); | ||
} | ||
@@ -33,3 +39,3 @@ | ||
for (const cstDoc of parseCst.parse(src)) { | ||
const doc = new Document(options); | ||
const doc = new Document(undefined, options); | ||
doc.parse(cstDoc, prev); | ||
@@ -45,3 +51,3 @@ stream.push(doc); | ||
const cst = parseCst.parse(src); | ||
const doc = new Document(options).parse(cst[0]); | ||
const doc = new Document(cst[0], options); | ||
@@ -58,3 +64,3 @@ if (cst.length > 1) { | ||
const doc = parseDocument(src, options); | ||
doc.warnings.forEach(warning => warnings.warn(warning)); | ||
doc.warnings.forEach(warning => warn(warning)); | ||
if (doc.errors.length > 0) throw doc.errors[0]; | ||
@@ -65,9 +71,7 @@ return doc.toJSON(); | ||
function stringify(value, options) { | ||
const doc = new Document(options); | ||
doc.contents = value; | ||
return String(doc); | ||
if (value === undefined) return '\n'; | ||
return new Document(value, options).toString(); | ||
} | ||
const YAML = { | ||
createNode, | ||
defaultOptions: Document$1.defaultOptions, | ||
@@ -74,0 +78,0 @@ Document, |
'use strict'; | ||
var PlainValue = require('./PlainValue-ec8e588e.js'); | ||
require('./PlainValue-ec8e588e.js'); | ||
var parseCst = require('./parse-ab8de154.js'); | ||
class BlankLine extends PlainValue.Node { | ||
constructor() { | ||
super(PlainValue.Type.BLANK_LINE); | ||
} | ||
/* istanbul ignore next */ | ||
get includesTrailingLines() { | ||
// This is never called from anywhere, but if it were, | ||
// this is the value it should return. | ||
return true; | ||
} | ||
/** | ||
* Parses a blank line from the source | ||
* | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first \n character | ||
* @returns {number} - Index of the character after this | ||
*/ | ||
parse(context, start) { | ||
this.context = context; | ||
this.range = new PlainValue.Range(start, start + 1); | ||
return start + 1; | ||
} | ||
} | ||
class CollectionItem extends PlainValue.Node { | ||
constructor(type, props) { | ||
super(type, props); | ||
this.node = null; | ||
} | ||
get includesTrailingLines() { | ||
return !!this.node && this.node.includesTrailingLines; | ||
} | ||
/** | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this | ||
*/ | ||
parse(context, start) { | ||
this.context = context; | ||
const { | ||
parseNode, | ||
src | ||
} = context; | ||
let { | ||
atLineStart, | ||
lineStart | ||
} = context; | ||
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'); | ||
const indent = atLineStart ? start - lineStart : context.indent; | ||
let offset = PlainValue.Node.endOfWhiteSpace(src, start + 1); | ||
let ch = src[offset]; | ||
const inlineComment = ch === '#'; | ||
const comments = []; | ||
let blankLine = null; | ||
while (ch === '\n' || ch === '#') { | ||
if (ch === '#') { | ||
const end = PlainValue.Node.endOfLine(src, offset + 1); | ||
comments.push(new PlainValue.Range(offset, end)); | ||
offset = end; | ||
} else { | ||
atLineStart = true; | ||
lineStart = offset + 1; | ||
const wsEnd = PlainValue.Node.endOfWhiteSpace(src, lineStart); | ||
if (src[wsEnd] === '\n' && comments.length === 0) { | ||
blankLine = new BlankLine(); | ||
lineStart = blankLine.parse({ | ||
src | ||
}, lineStart); | ||
} | ||
offset = PlainValue.Node.endOfIndent(src, lineStart); | ||
} | ||
ch = src[offset]; | ||
} | ||
if (PlainValue.Node.nextNodeIsIndented(ch, offset - (lineStart + indent), this.type !== PlainValue.Type.SEQ_ITEM)) { | ||
this.node = parseNode({ | ||
atLineStart, | ||
inCollection: false, | ||
indent, | ||
lineStart, | ||
parent: this | ||
}, offset); | ||
} else if (ch && lineStart > start + 1) { | ||
offset = lineStart - 1; | ||
} | ||
if (this.node) { | ||
if (blankLine) { | ||
// Only blank lines preceding non-empty nodes are captured. Note that | ||
// this means that collection item range start indices do not always | ||
// increase monotonically. -- eemeli/yaml#126 | ||
const items = context.parent.items || context.parent.contents; | ||
if (items) items.push(blankLine); | ||
} | ||
if (comments.length) Array.prototype.push.apply(this.props, comments); | ||
offset = this.node.range.end; | ||
} else { | ||
if (inlineComment) { | ||
const c = comments[0]; | ||
this.props.push(c); | ||
offset = c.end; | ||
} else { | ||
offset = PlainValue.Node.endOfLine(src, start + 1); | ||
} | ||
} | ||
const end = this.node ? this.node.valueRange.end : offset; | ||
this.valueRange = new PlainValue.Range(start, end); | ||
return offset; | ||
} | ||
setOrigRanges(cr, offset) { | ||
offset = super.setOrigRanges(cr, offset); | ||
return this.node ? this.node.setOrigRanges(cr, offset) : offset; | ||
} | ||
toString() { | ||
const { | ||
context: { | ||
src | ||
}, | ||
node, | ||
range, | ||
value | ||
} = this; | ||
if (value != null) return value; | ||
const str = node ? src.slice(range.start, node.range.start) + String(node) : src.slice(range.start, range.end); | ||
return PlainValue.Node.addStringTerminator(src, range.end, str); | ||
} | ||
} | ||
class Comment extends PlainValue.Node { | ||
constructor() { | ||
super(PlainValue.Type.COMMENT); | ||
} | ||
/** | ||
* Parses a comment line from the source | ||
* | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this scalar | ||
*/ | ||
parse(context, start) { | ||
this.context = context; | ||
const offset = this.parseComment(start); | ||
this.range = new PlainValue.Range(start, offset); | ||
return offset; | ||
} | ||
} | ||
function grabCollectionEndComments(node) { | ||
let cnode = node; | ||
while (cnode instanceof CollectionItem) cnode = cnode.node; | ||
if (!(cnode instanceof Collection)) return null; | ||
const len = cnode.items.length; | ||
let ci = -1; | ||
for (let i = len - 1; i >= 0; --i) { | ||
const n = cnode.items[i]; | ||
if (n.type === PlainValue.Type.COMMENT) { | ||
// Keep sufficiently indented comments with preceding node | ||
const { | ||
indent, | ||
lineStart | ||
} = n.context; | ||
if (indent > 0 && n.range.start >= lineStart + indent) break; | ||
ci = i; | ||
} else if (n.type === PlainValue.Type.BLANK_LINE) ci = i;else break; | ||
} | ||
if (ci === -1) return null; | ||
const ca = cnode.items.splice(ci, len - ci); | ||
const prevEnd = ca[0].range.start; | ||
while (true) { | ||
cnode.range.end = prevEnd; | ||
if (cnode.valueRange && cnode.valueRange.end > prevEnd) cnode.valueRange.end = prevEnd; | ||
if (cnode === node) break; | ||
cnode = cnode.context.parent; | ||
} | ||
return ca; | ||
} | ||
class Collection extends PlainValue.Node { | ||
static nextContentHasIndent(src, offset, indent) { | ||
const lineStart = PlainValue.Node.endOfLine(src, offset) + 1; | ||
offset = PlainValue.Node.endOfWhiteSpace(src, lineStart); | ||
const ch = src[offset]; | ||
if (!ch) return false; | ||
if (offset >= lineStart + indent) return true; | ||
if (ch !== '#' && ch !== '\n') return false; | ||
return Collection.nextContentHasIndent(src, offset, indent); | ||
} | ||
constructor(firstItem) { | ||
super(firstItem.type === PlainValue.Type.SEQ_ITEM ? PlainValue.Type.SEQ : PlainValue.Type.MAP); | ||
for (let i = firstItem.props.length - 1; i >= 0; --i) { | ||
if (firstItem.props[i].start < firstItem.context.lineStart) { | ||
// props on previous line are assumed by the collection | ||
this.props = firstItem.props.slice(0, i + 1); | ||
firstItem.props = firstItem.props.slice(i + 1); | ||
const itemRange = firstItem.props[0] || firstItem.valueRange; | ||
firstItem.range.start = itemRange.start; | ||
break; | ||
} | ||
} | ||
this.items = [firstItem]; | ||
const ec = grabCollectionEndComments(firstItem); | ||
if (ec) Array.prototype.push.apply(this.items, ec); | ||
} | ||
get includesTrailingLines() { | ||
return this.items.length > 0; | ||
} | ||
/** | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this | ||
*/ | ||
parse(context, start) { | ||
this.context = context; | ||
const { | ||
parseNode, | ||
src | ||
} = context; // It's easier to recalculate lineStart here rather than tracking down the | ||
// last context from which to read it -- eemeli/yaml#2 | ||
let lineStart = PlainValue.Node.startOfLine(src, start); | ||
const firstItem = this.items[0]; // First-item context needs to be correct for later comment handling | ||
// -- eemeli/yaml#17 | ||
firstItem.context.parent = this; | ||
this.valueRange = PlainValue.Range.copy(firstItem.valueRange); | ||
const indent = firstItem.range.start - firstItem.context.lineStart; | ||
let offset = start; | ||
offset = PlainValue.Node.normalizeOffset(src, offset); | ||
let ch = src[offset]; | ||
let atLineStart = PlainValue.Node.endOfWhiteSpace(src, lineStart) === offset; | ||
let prevIncludesTrailingLines = false; | ||
while (ch) { | ||
while (ch === '\n' || ch === '#') { | ||
if (atLineStart && ch === '\n' && !prevIncludesTrailingLines) { | ||
const blankLine = new BlankLine(); | ||
offset = blankLine.parse({ | ||
src | ||
}, offset); | ||
this.valueRange.end = offset; | ||
if (offset >= src.length) { | ||
ch = null; | ||
break; | ||
} | ||
this.items.push(blankLine); | ||
offset -= 1; // blankLine.parse() consumes terminal newline | ||
} else if (ch === '#') { | ||
if (offset < lineStart + indent && !Collection.nextContentHasIndent(src, offset, indent)) { | ||
return offset; | ||
} | ||
const comment = new Comment(); | ||
offset = comment.parse({ | ||
indent, | ||
lineStart, | ||
src | ||
}, offset); | ||
this.items.push(comment); | ||
this.valueRange.end = offset; | ||
if (offset >= src.length) { | ||
ch = null; | ||
break; | ||
} | ||
} | ||
lineStart = offset + 1; | ||
offset = PlainValue.Node.endOfIndent(src, lineStart); | ||
if (PlainValue.Node.atBlank(src, offset)) { | ||
const wsEnd = PlainValue.Node.endOfWhiteSpace(src, offset); | ||
const next = src[wsEnd]; | ||
if (!next || next === '\n' || next === '#') { | ||
offset = wsEnd; | ||
} | ||
} | ||
ch = src[offset]; | ||
atLineStart = true; | ||
} | ||
if (!ch) { | ||
break; | ||
} | ||
if (offset !== lineStart + indent && (atLineStart || ch !== ':')) { | ||
if (offset < lineStart + indent) { | ||
if (lineStart > start) offset = lineStart; | ||
break; | ||
} else if (!this.error) { | ||
const msg = 'All collection items must start at the same column'; | ||
this.error = new PlainValue.YAMLSyntaxError(this, msg); | ||
} | ||
} | ||
if (firstItem.type === PlainValue.Type.SEQ_ITEM) { | ||
if (ch !== '-') { | ||
if (lineStart > start) offset = lineStart; | ||
break; | ||
} | ||
} else if (ch === '-' && !this.error) { | ||
// map key may start with -, as long as it's followed by a non-whitespace char | ||
const next = src[offset + 1]; | ||
if (!next || next === '\n' || next === '\t' || next === ' ') { | ||
const msg = 'A collection cannot be both a mapping and a sequence'; | ||
this.error = new PlainValue.YAMLSyntaxError(this, msg); | ||
} | ||
} | ||
const node = parseNode({ | ||
atLineStart, | ||
inCollection: true, | ||
indent, | ||
lineStart, | ||
parent: this | ||
}, offset); | ||
if (!node) return offset; // at next document start | ||
this.items.push(node); | ||
this.valueRange.end = node.valueRange.end; | ||
offset = PlainValue.Node.normalizeOffset(src, node.range.end); | ||
ch = src[offset]; | ||
atLineStart = false; | ||
prevIncludesTrailingLines = node.includesTrailingLines; // Need to reset lineStart and atLineStart here if preceding node's range | ||
// has advanced to check the current line's indentation level | ||
// -- eemeli/yaml#10 & eemeli/yaml#38 | ||
if (ch) { | ||
let ls = offset - 1; | ||
let prev = src[ls]; | ||
while (prev === ' ' || prev === '\t') prev = src[--ls]; | ||
if (prev === '\n') { | ||
lineStart = ls + 1; | ||
atLineStart = true; | ||
} | ||
} | ||
const ec = grabCollectionEndComments(node); | ||
if (ec) Array.prototype.push.apply(this.items, ec); | ||
} | ||
return offset; | ||
} | ||
setOrigRanges(cr, offset) { | ||
offset = super.setOrigRanges(cr, offset); | ||
this.items.forEach(node => { | ||
offset = node.setOrigRanges(cr, offset); | ||
}); | ||
return offset; | ||
} | ||
toString() { | ||
const { | ||
context: { | ||
src | ||
}, | ||
items, | ||
range, | ||
value | ||
} = this; | ||
if (value != null) return value; | ||
let str = src.slice(range.start, items[0].range.start) + String(items[0]); | ||
for (let i = 1; i < items.length; ++i) { | ||
const item = items[i]; | ||
const { | ||
atLineStart, | ||
indent | ||
} = item.context; | ||
if (atLineStart) for (let i = 0; i < indent; ++i) str += ' '; | ||
str += String(item); | ||
} | ||
return PlainValue.Node.addStringTerminator(src, range.end, str); | ||
} | ||
} | ||
class Directive extends PlainValue.Node { | ||
constructor() { | ||
super(PlainValue.Type.DIRECTIVE); | ||
this.name = null; | ||
} | ||
get parameters() { | ||
const raw = this.rawValue; | ||
return raw ? raw.trim().split(/[ \t]+/) : []; | ||
} | ||
parseName(start) { | ||
const { | ||
src | ||
} = this.context; | ||
let offset = start; | ||
let ch = src[offset]; | ||
while (ch && ch !== '\n' && ch !== '\t' && ch !== ' ') ch = src[offset += 1]; | ||
this.name = src.slice(start, offset); | ||
return offset; | ||
} | ||
parseParameters(start) { | ||
const { | ||
src | ||
} = this.context; | ||
let offset = start; | ||
let ch = src[offset]; | ||
while (ch && ch !== '\n' && ch !== '#') ch = src[offset += 1]; | ||
this.valueRange = new PlainValue.Range(start, offset); | ||
return offset; | ||
} | ||
parse(context, start) { | ||
this.context = context; | ||
let offset = this.parseName(start + 1); | ||
offset = this.parseParameters(offset); | ||
offset = this.parseComment(offset); | ||
this.range = new PlainValue.Range(start, offset); | ||
return offset; | ||
} | ||
} | ||
class Document extends PlainValue.Node { | ||
static startCommentOrEndBlankLine(src, start) { | ||
const offset = PlainValue.Node.endOfWhiteSpace(src, start); | ||
const ch = src[offset]; | ||
return ch === '#' || ch === '\n' ? offset : start; | ||
} | ||
constructor() { | ||
super(PlainValue.Type.DOCUMENT); | ||
this.directives = null; | ||
this.contents = null; | ||
this.directivesEndMarker = null; | ||
this.documentEndMarker = null; | ||
} | ||
parseDirectives(start) { | ||
const { | ||
src | ||
} = this.context; | ||
this.directives = []; | ||
let atLineStart = true; | ||
let hasDirectives = false; | ||
let offset = start; | ||
while (!PlainValue.Node.atDocumentBoundary(src, offset, PlainValue.Char.DIRECTIVES_END)) { | ||
offset = Document.startCommentOrEndBlankLine(src, offset); | ||
switch (src[offset]) { | ||
case '\n': | ||
if (atLineStart) { | ||
const blankLine = new BlankLine(); | ||
offset = blankLine.parse({ | ||
src | ||
}, offset); | ||
if (offset < src.length) { | ||
this.directives.push(blankLine); | ||
} | ||
} else { | ||
offset += 1; | ||
atLineStart = true; | ||
} | ||
break; | ||
case '#': | ||
{ | ||
const comment = new Comment(); | ||
offset = comment.parse({ | ||
src | ||
}, offset); | ||
this.directives.push(comment); | ||
atLineStart = false; | ||
} | ||
break; | ||
case '%': | ||
{ | ||
const directive = new Directive(); | ||
offset = directive.parse({ | ||
parent: this, | ||
src | ||
}, offset); | ||
this.directives.push(directive); | ||
hasDirectives = true; | ||
atLineStart = false; | ||
} | ||
break; | ||
default: | ||
if (hasDirectives) { | ||
this.error = new PlainValue.YAMLSemanticError(this, 'Missing directives-end indicator line'); | ||
} else if (this.directives.length > 0) { | ||
this.contents = this.directives; | ||
this.directives = []; | ||
} | ||
return offset; | ||
} | ||
} | ||
if (src[offset]) { | ||
this.directivesEndMarker = new PlainValue.Range(offset, offset + 3); | ||
return offset + 3; | ||
} | ||
if (hasDirectives) { | ||
this.error = new PlainValue.YAMLSemanticError(this, 'Missing directives-end indicator line'); | ||
} else if (this.directives.length > 0) { | ||
this.contents = this.directives; | ||
this.directives = []; | ||
} | ||
return offset; | ||
} | ||
parseContents(start) { | ||
const { | ||
parseNode, | ||
src | ||
} = this.context; | ||
if (!this.contents) this.contents = []; | ||
let lineStart = start; | ||
while (src[lineStart - 1] === '-') lineStart -= 1; | ||
let offset = PlainValue.Node.endOfWhiteSpace(src, start); | ||
let atLineStart = lineStart === start; | ||
this.valueRange = new PlainValue.Range(offset); | ||
while (!PlainValue.Node.atDocumentBoundary(src, offset, PlainValue.Char.DOCUMENT_END)) { | ||
switch (src[offset]) { | ||
case '\n': | ||
if (atLineStart) { | ||
const blankLine = new BlankLine(); | ||
offset = blankLine.parse({ | ||
src | ||
}, offset); | ||
if (offset < src.length) { | ||
this.contents.push(blankLine); | ||
} | ||
} else { | ||
offset += 1; | ||
atLineStart = true; | ||
} | ||
lineStart = offset; | ||
break; | ||
case '#': | ||
{ | ||
const comment = new Comment(); | ||
offset = comment.parse({ | ||
src | ||
}, offset); | ||
this.contents.push(comment); | ||
atLineStart = false; | ||
} | ||
break; | ||
default: | ||
{ | ||
const iEnd = PlainValue.Node.endOfIndent(src, offset); | ||
const context = { | ||
atLineStart, | ||
indent: -1, | ||
inFlow: false, | ||
inCollection: false, | ||
lineStart, | ||
parent: this | ||
}; | ||
const node = parseNode(context, iEnd); | ||
if (!node) return this.valueRange.end = iEnd; // at next document start | ||
this.contents.push(node); | ||
offset = node.range.end; | ||
atLineStart = false; | ||
const ec = grabCollectionEndComments(node); | ||
if (ec) Array.prototype.push.apply(this.contents, ec); | ||
} | ||
} | ||
offset = Document.startCommentOrEndBlankLine(src, offset); | ||
} | ||
this.valueRange.end = offset; | ||
if (src[offset]) { | ||
this.documentEndMarker = new PlainValue.Range(offset, offset + 3); | ||
offset += 3; | ||
if (src[offset]) { | ||
offset = PlainValue.Node.endOfWhiteSpace(src, offset); | ||
if (src[offset] === '#') { | ||
const comment = new Comment(); | ||
offset = comment.parse({ | ||
src | ||
}, offset); | ||
this.contents.push(comment); | ||
} | ||
switch (src[offset]) { | ||
case '\n': | ||
offset += 1; | ||
break; | ||
case undefined: | ||
break; | ||
default: | ||
this.error = new PlainValue.YAMLSyntaxError(this, 'Document end marker line cannot have a non-comment suffix'); | ||
} | ||
} | ||
} | ||
return offset; | ||
} | ||
/** | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this | ||
*/ | ||
parse(context, start) { | ||
context.root = this; | ||
this.context = context; | ||
const { | ||
src | ||
} = context; | ||
let offset = src.charCodeAt(start) === 0xfeff ? start + 1 : start; // skip BOM | ||
offset = this.parseDirectives(offset); | ||
offset = this.parseContents(offset); | ||
return offset; | ||
} | ||
setOrigRanges(cr, offset) { | ||
offset = super.setOrigRanges(cr, offset); | ||
this.directives.forEach(node => { | ||
offset = node.setOrigRanges(cr, offset); | ||
}); | ||
if (this.directivesEndMarker) offset = this.directivesEndMarker.setOrigRange(cr, offset); | ||
this.contents.forEach(node => { | ||
offset = node.setOrigRanges(cr, offset); | ||
}); | ||
if (this.documentEndMarker) offset = this.documentEndMarker.setOrigRange(cr, offset); | ||
return offset; | ||
} | ||
toString() { | ||
const { | ||
contents, | ||
directives, | ||
value | ||
} = this; | ||
if (value != null) return value; | ||
let str = directives.join(''); | ||
if (contents.length > 0) { | ||
if (directives.length > 0 || contents[0].type === PlainValue.Type.COMMENT) str += '---\n'; | ||
str += contents.join(''); | ||
} | ||
if (str[str.length - 1] !== '\n') str += '\n'; | ||
return str; | ||
} | ||
} | ||
class Alias extends PlainValue.Node { | ||
/** | ||
* Parses an *alias from the source | ||
* | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this scalar | ||
*/ | ||
parse(context, start) { | ||
this.context = context; | ||
const { | ||
src | ||
} = context; | ||
let offset = PlainValue.Node.endOfIdentifier(src, start + 1); | ||
this.valueRange = new PlainValue.Range(start + 1, offset); | ||
offset = PlainValue.Node.endOfWhiteSpace(src, offset); | ||
offset = this.parseComment(offset); | ||
return offset; | ||
} | ||
} | ||
const Chomp = { | ||
CLIP: 'CLIP', | ||
KEEP: 'KEEP', | ||
STRIP: 'STRIP' | ||
}; | ||
class BlockValue extends PlainValue.Node { | ||
constructor(type, props) { | ||
super(type, props); | ||
this.blockIndent = null; | ||
this.chomping = Chomp.CLIP; | ||
this.header = null; | ||
} | ||
get includesTrailingLines() { | ||
return this.chomping === Chomp.KEEP; | ||
} | ||
get strValue() { | ||
if (!this.valueRange || !this.context) return null; | ||
let { | ||
start, | ||
end | ||
} = this.valueRange; | ||
const { | ||
indent, | ||
src | ||
} = this.context; | ||
if (this.valueRange.isEmpty()) return ''; | ||
let lastNewLine = null; | ||
let ch = src[end - 1]; | ||
while (ch === '\n' || ch === '\t' || ch === ' ') { | ||
end -= 1; | ||
if (end <= start) { | ||
if (this.chomping === Chomp.KEEP) break;else return ''; // probably never happens | ||
} | ||
if (ch === '\n') lastNewLine = end; | ||
ch = src[end - 1]; | ||
} | ||
let keepStart = end + 1; | ||
if (lastNewLine) { | ||
if (this.chomping === Chomp.KEEP) { | ||
keepStart = lastNewLine; | ||
end = this.valueRange.end; | ||
} else { | ||
end = lastNewLine; | ||
} | ||
} | ||
const bi = indent + this.blockIndent; | ||
const folded = this.type === PlainValue.Type.BLOCK_FOLDED; | ||
let atStart = true; | ||
let str = ''; | ||
let sep = ''; | ||
let prevMoreIndented = false; | ||
for (let i = start; i < end; ++i) { | ||
for (let j = 0; j < bi; ++j) { | ||
if (src[i] !== ' ') break; | ||
i += 1; | ||
} | ||
const ch = src[i]; | ||
if (ch === '\n') { | ||
if (sep === '\n') str += '\n';else sep = '\n'; | ||
} else { | ||
const lineEnd = PlainValue.Node.endOfLine(src, i); | ||
const line = src.slice(i, lineEnd); | ||
i = lineEnd; | ||
if (folded && (ch === ' ' || ch === '\t') && i < keepStart) { | ||
if (sep === ' ') sep = '\n';else if (!prevMoreIndented && !atStart && sep === '\n') sep = '\n\n'; | ||
str += sep + line; //+ ((lineEnd < end && src[lineEnd]) || '') | ||
sep = lineEnd < end && src[lineEnd] || ''; | ||
prevMoreIndented = true; | ||
} else { | ||
str += sep + line; | ||
sep = folded && i < keepStart ? ' ' : '\n'; | ||
prevMoreIndented = false; | ||
} | ||
if (atStart && line !== '') atStart = false; | ||
} | ||
} | ||
return this.chomping === Chomp.STRIP ? str : str + '\n'; | ||
} | ||
parseBlockHeader(start) { | ||
const { | ||
src | ||
} = this.context; | ||
let offset = start + 1; | ||
let bi = ''; | ||
while (true) { | ||
const ch = src[offset]; | ||
switch (ch) { | ||
case '-': | ||
this.chomping = Chomp.STRIP; | ||
break; | ||
case '+': | ||
this.chomping = Chomp.KEEP; | ||
break; | ||
case '0': | ||
case '1': | ||
case '2': | ||
case '3': | ||
case '4': | ||
case '5': | ||
case '6': | ||
case '7': | ||
case '8': | ||
case '9': | ||
bi += ch; | ||
break; | ||
default: | ||
this.blockIndent = Number(bi) || null; | ||
this.header = new PlainValue.Range(start, offset); | ||
return offset; | ||
} | ||
offset += 1; | ||
} | ||
} | ||
parseBlockValue(start) { | ||
const { | ||
indent, | ||
src | ||
} = this.context; | ||
const explicit = !!this.blockIndent; | ||
let offset = start; | ||
let valueEnd = start; | ||
let minBlockIndent = 1; | ||
for (let ch = src[offset]; ch === '\n'; ch = src[offset]) { | ||
offset += 1; | ||
if (PlainValue.Node.atDocumentBoundary(src, offset)) break; | ||
const end = PlainValue.Node.endOfBlockIndent(src, indent, offset); // should not include tab? | ||
if (end === null) break; | ||
const ch = src[end]; | ||
const lineIndent = end - (offset + indent); | ||
if (!this.blockIndent) { | ||
// no explicit block indent, none yet detected | ||
if (src[end] !== '\n') { | ||
// first line with non-whitespace content | ||
if (lineIndent < minBlockIndent) { | ||
const msg = 'Block scalars with more-indented leading empty lines must use an explicit indentation indicator'; | ||
this.error = new PlainValue.YAMLSemanticError(this, msg); | ||
} | ||
this.blockIndent = lineIndent; | ||
} else if (lineIndent > minBlockIndent) { | ||
// empty line with more whitespace | ||
minBlockIndent = lineIndent; | ||
} | ||
} else if (ch && ch !== '\n' && lineIndent < this.blockIndent) { | ||
if (src[end] === '#') break; | ||
if (!this.error) { | ||
const src = explicit ? 'explicit indentation indicator' : 'first line'; | ||
const msg = `Block scalars must not be less indented than their ${src}`; | ||
this.error = new PlainValue.YAMLSemanticError(this, msg); | ||
} | ||
} | ||
if (src[end] === '\n') { | ||
offset = end; | ||
} else { | ||
offset = valueEnd = PlainValue.Node.endOfLine(src, end); | ||
} | ||
} | ||
if (this.chomping !== Chomp.KEEP) { | ||
offset = src[valueEnd] ? valueEnd + 1 : valueEnd; | ||
} | ||
this.valueRange = new PlainValue.Range(start + 1, offset); | ||
return offset; | ||
} | ||
/** | ||
* Parses a block value from the source | ||
* | ||
* Accepted forms are: | ||
* ``` | ||
* BS | ||
* block | ||
* lines | ||
* | ||
* BS #comment | ||
* block | ||
* lines | ||
* ``` | ||
* where the block style BS matches the regexp `[|>][-+1-9]*` and 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 block | ||
*/ | ||
parse(context, start) { | ||
this.context = context; | ||
const { | ||
src | ||
} = context; | ||
let offset = this.parseBlockHeader(start); | ||
offset = PlainValue.Node.endOfWhiteSpace(src, offset); | ||
offset = this.parseComment(offset); | ||
offset = this.parseBlockValue(offset); | ||
return offset; | ||
} | ||
setOrigRanges(cr, offset) { | ||
offset = super.setOrigRanges(cr, offset); | ||
return this.header ? this.header.setOrigRange(cr, offset) : offset; | ||
} | ||
} | ||
class FlowCollection extends PlainValue.Node { | ||
constructor(type, props) { | ||
super(type, props); | ||
this.items = null; | ||
} | ||
prevNodeIsJsonLike(idx = this.items.length) { | ||
const node = this.items[idx - 1]; | ||
return !!node && (node.jsonLike || node.type === PlainValue.Type.COMMENT && this.prevNodeIsJsonLike(idx - 1)); | ||
} | ||
/** | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this | ||
*/ | ||
parse(context, start) { | ||
this.context = context; | ||
const { | ||
parseNode, | ||
src | ||
} = context; | ||
let { | ||
indent, | ||
lineStart | ||
} = context; | ||
let char = src[start]; // { or [ | ||
this.items = [{ | ||
char, | ||
offset: start | ||
}]; | ||
let offset = PlainValue.Node.endOfWhiteSpace(src, start + 1); | ||
char = src[offset]; | ||
while (char && char !== ']' && char !== '}') { | ||
switch (char) { | ||
case '\n': | ||
{ | ||
lineStart = offset + 1; | ||
const wsEnd = PlainValue.Node.endOfWhiteSpace(src, lineStart); | ||
if (src[wsEnd] === '\n') { | ||
const blankLine = new BlankLine(); | ||
lineStart = blankLine.parse({ | ||
src | ||
}, lineStart); | ||
this.items.push(blankLine); | ||
} | ||
offset = PlainValue.Node.endOfIndent(src, lineStart); | ||
if (offset <= lineStart + indent) { | ||
char = src[offset]; | ||
if (offset < lineStart + indent || char !== ']' && char !== '}') { | ||
const msg = 'Insufficient indentation in flow collection'; | ||
this.error = new PlainValue.YAMLSemanticError(this, msg); | ||
} | ||
} | ||
} | ||
break; | ||
case ',': | ||
{ | ||
this.items.push({ | ||
char, | ||
offset | ||
}); | ||
offset += 1; | ||
} | ||
break; | ||
case '#': | ||
{ | ||
const comment = new Comment(); | ||
offset = comment.parse({ | ||
src | ||
}, offset); | ||
this.items.push(comment); | ||
} | ||
break; | ||
case '?': | ||
case ':': | ||
{ | ||
const next = src[offset + 1]; | ||
if (next === '\n' || next === '\t' || next === ' ' || next === ',' || // in-flow : after JSON-like key does not need to be followed by whitespace | ||
char === ':' && this.prevNodeIsJsonLike()) { | ||
this.items.push({ | ||
char, | ||
offset | ||
}); | ||
offset += 1; | ||
break; | ||
} | ||
} | ||
// fallthrough | ||
default: | ||
{ | ||
const node = parseNode({ | ||
atLineStart: false, | ||
inCollection: false, | ||
inFlow: true, | ||
indent: -1, | ||
lineStart, | ||
parent: this | ||
}, offset); | ||
if (!node) { | ||
// at next document start | ||
this.valueRange = new PlainValue.Range(start, offset); | ||
return offset; | ||
} | ||
this.items.push(node); | ||
offset = PlainValue.Node.normalizeOffset(src, node.range.end); | ||
} | ||
} | ||
offset = PlainValue.Node.endOfWhiteSpace(src, offset); | ||
char = src[offset]; | ||
} | ||
this.valueRange = new PlainValue.Range(start, offset + 1); | ||
if (char) { | ||
this.items.push({ | ||
char, | ||
offset | ||
}); | ||
offset = PlainValue.Node.endOfWhiteSpace(src, offset + 1); | ||
offset = this.parseComment(offset); | ||
} | ||
return offset; | ||
} | ||
setOrigRanges(cr, offset) { | ||
offset = super.setOrigRanges(cr, offset); | ||
this.items.forEach(node => { | ||
if (node instanceof PlainValue.Node) { | ||
offset = node.setOrigRanges(cr, offset); | ||
} else if (cr.length === 0) { | ||
node.origOffset = node.offset; | ||
} else { | ||
let i = offset; | ||
while (i < cr.length) { | ||
if (cr[i] > node.offset) break;else ++i; | ||
} | ||
node.origOffset = node.offset + i; | ||
offset = i; | ||
} | ||
}); | ||
return offset; | ||
} | ||
toString() { | ||
const { | ||
context: { | ||
src | ||
}, | ||
items, | ||
range, | ||
value | ||
} = this; | ||
if (value != null) return value; | ||
const nodes = items.filter(item => item instanceof PlainValue.Node); | ||
let str = ''; | ||
let prevEnd = range.start; | ||
nodes.forEach(node => { | ||
const prefix = src.slice(prevEnd, node.range.start); | ||
prevEnd = node.range.end; | ||
str += prefix + String(node); | ||
if (str[str.length - 1] === '\n' && src[prevEnd - 1] !== '\n' && src[prevEnd] === '\n') { | ||
// Comment range does not include the terminal newline, but its | ||
// stringified value does. Without this fix, newlines at comment ends | ||
// get duplicated. | ||
prevEnd += 1; | ||
} | ||
}); | ||
str += src.slice(prevEnd, range.end); | ||
return PlainValue.Node.addStringTerminator(src, range.end, str); | ||
} | ||
} | ||
class QuoteDouble extends PlainValue.Node { | ||
static endOfQuote(src, offset) { | ||
let ch = src[offset]; | ||
while (ch && ch !== '"') { | ||
offset += ch === '\\' ? 2 : 1; | ||
ch = src[offset]; | ||
} | ||
return offset + 1; | ||
} | ||
/** | ||
* @returns {string | { str: string, errors: YAMLSyntaxError[] }} | ||
*/ | ||
get strValue() { | ||
if (!this.valueRange || !this.context) return null; | ||
const errors = []; | ||
const { | ||
start, | ||
end | ||
} = this.valueRange; | ||
const { | ||
indent, | ||
src | ||
} = this.context; | ||
if (src[end - 1] !== '"') errors.push(new PlainValue.YAMLSyntaxError(this, 'Missing closing "quote')); // Using String#replace is too painful with escaped newlines preceded by | ||
// escaped backslashes; also, this should be faster. | ||
let str = ''; | ||
for (let i = start + 1; i < end - 1; ++i) { | ||
const ch = src[i]; | ||
if (ch === '\n') { | ||
if (PlainValue.Node.atDocumentBoundary(src, i + 1)) errors.push(new PlainValue.YAMLSemanticError(this, 'Document boundary indicators are not allowed within string values')); | ||
const { | ||
fold, | ||
offset, | ||
error | ||
} = PlainValue.Node.foldNewline(src, i, indent); | ||
str += fold; | ||
i = offset; | ||
if (error) errors.push(new PlainValue.YAMLSemanticError(this, 'Multi-line double-quoted string needs to be sufficiently indented')); | ||
} else if (ch === '\\') { | ||
i += 1; | ||
switch (src[i]) { | ||
case '0': | ||
str += '\0'; | ||
break; | ||
// null character | ||
case 'a': | ||
str += '\x07'; | ||
break; | ||
// bell character | ||
case 'b': | ||
str += '\b'; | ||
break; | ||
// backspace | ||
case 'e': | ||
str += '\x1b'; | ||
break; | ||
// escape character | ||
case 'f': | ||
str += '\f'; | ||
break; | ||
// form feed | ||
case 'n': | ||
str += '\n'; | ||
break; | ||
// line feed | ||
case 'r': | ||
str += '\r'; | ||
break; | ||
// carriage return | ||
case 't': | ||
str += '\t'; | ||
break; | ||
// horizontal tab | ||
case 'v': | ||
str += '\v'; | ||
break; | ||
// vertical tab | ||
case 'N': | ||
str += '\u0085'; | ||
break; | ||
// Unicode next line | ||
case '_': | ||
str += '\u00a0'; | ||
break; | ||
// Unicode non-breaking space | ||
case 'L': | ||
str += '\u2028'; | ||
break; | ||
// Unicode line separator | ||
case 'P': | ||
str += '\u2029'; | ||
break; | ||
// Unicode paragraph separator | ||
case ' ': | ||
str += ' '; | ||
break; | ||
case '"': | ||
str += '"'; | ||
break; | ||
case '/': | ||
str += '/'; | ||
break; | ||
case '\\': | ||
str += '\\'; | ||
break; | ||
case '\t': | ||
str += '\t'; | ||
break; | ||
case 'x': | ||
str += this.parseCharCode(i + 1, 2, errors); | ||
i += 2; | ||
break; | ||
case 'u': | ||
str += this.parseCharCode(i + 1, 4, errors); | ||
i += 4; | ||
break; | ||
case 'U': | ||
str += this.parseCharCode(i + 1, 8, errors); | ||
i += 8; | ||
break; | ||
case '\n': | ||
// skip escaped newlines, but still trim the following line | ||
while (src[i + 1] === ' ' || src[i + 1] === '\t') i += 1; | ||
break; | ||
default: | ||
errors.push(new PlainValue.YAMLSyntaxError(this, `Invalid escape sequence ${src.substr(i - 1, 2)}`)); | ||
str += '\\' + src[i]; | ||
} | ||
} else if (ch === ' ' || ch === '\t') { | ||
// trim trailing whitespace | ||
const wsStart = i; | ||
let next = src[i + 1]; | ||
while (next === ' ' || next === '\t') { | ||
i += 1; | ||
next = src[i + 1]; | ||
} | ||
if (next !== '\n') str += i > wsStart ? src.slice(wsStart, i + 1) : ch; | ||
} else { | ||
str += ch; | ||
} | ||
} | ||
return errors.length > 0 ? { | ||
errors, | ||
str | ||
} : str; | ||
} | ||
parseCharCode(offset, length, errors) { | ||
const { | ||
src | ||
} = this.context; | ||
const cc = src.substr(offset, length); | ||
const ok = cc.length === length && /^[0-9a-fA-F]+$/.test(cc); | ||
const code = ok ? parseInt(cc, 16) : NaN; | ||
if (isNaN(code)) { | ||
errors.push(new PlainValue.YAMLSyntaxError(this, `Invalid escape sequence ${src.substr(offset - 2, length + 2)}`)); | ||
return src.substr(offset - 2, length + 2); | ||
} | ||
return String.fromCodePoint(code); | ||
} | ||
/** | ||
* Parses a "double quoted" value from the source | ||
* | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this scalar | ||
*/ | ||
parse(context, start) { | ||
this.context = context; | ||
const { | ||
src | ||
} = context; | ||
let offset = QuoteDouble.endOfQuote(src, start + 1); | ||
this.valueRange = new PlainValue.Range(start, offset); | ||
offset = PlainValue.Node.endOfWhiteSpace(src, offset); | ||
offset = this.parseComment(offset); | ||
return offset; | ||
} | ||
} | ||
class QuoteSingle extends PlainValue.Node { | ||
static endOfQuote(src, offset) { | ||
let ch = src[offset]; | ||
while (ch) { | ||
if (ch === "'") { | ||
if (src[offset + 1] !== "'") break; | ||
ch = src[offset += 2]; | ||
} else { | ||
ch = src[offset += 1]; | ||
} | ||
} | ||
return offset + 1; | ||
} | ||
/** | ||
* @returns {string | { str: string, errors: YAMLSyntaxError[] }} | ||
*/ | ||
get strValue() { | ||
if (!this.valueRange || !this.context) return null; | ||
const errors = []; | ||
const { | ||
start, | ||
end | ||
} = this.valueRange; | ||
const { | ||
indent, | ||
src | ||
} = this.context; | ||
if (src[end - 1] !== "'") errors.push(new PlainValue.YAMLSyntaxError(this, "Missing closing 'quote")); | ||
let str = ''; | ||
for (let i = start + 1; i < end - 1; ++i) { | ||
const ch = src[i]; | ||
if (ch === '\n') { | ||
if (PlainValue.Node.atDocumentBoundary(src, i + 1)) errors.push(new PlainValue.YAMLSemanticError(this, 'Document boundary indicators are not allowed within string values')); | ||
const { | ||
fold, | ||
offset, | ||
error | ||
} = PlainValue.Node.foldNewline(src, i, indent); | ||
str += fold; | ||
i = offset; | ||
if (error) errors.push(new PlainValue.YAMLSemanticError(this, 'Multi-line single-quoted string needs to be sufficiently indented')); | ||
} else if (ch === "'") { | ||
str += ch; | ||
i += 1; | ||
if (src[i] !== "'") errors.push(new PlainValue.YAMLSyntaxError(this, 'Unescaped single quote? This should not happen.')); | ||
} else if (ch === ' ' || ch === '\t') { | ||
// trim trailing whitespace | ||
const wsStart = i; | ||
let next = src[i + 1]; | ||
while (next === ' ' || next === '\t') { | ||
i += 1; | ||
next = src[i + 1]; | ||
} | ||
if (next !== '\n') str += i > wsStart ? src.slice(wsStart, i + 1) : ch; | ||
} else { | ||
str += ch; | ||
} | ||
} | ||
return errors.length > 0 ? { | ||
errors, | ||
str | ||
} : str; | ||
} | ||
/** | ||
* Parses a 'single quoted' value from the source | ||
* | ||
* @param {ParseContext} context | ||
* @param {number} start - Index of first character | ||
* @returns {number} - Index of the character after this scalar | ||
*/ | ||
parse(context, start) { | ||
this.context = context; | ||
const { | ||
src | ||
} = context; | ||
let offset = QuoteSingle.endOfQuote(src, start + 1); | ||
this.valueRange = new PlainValue.Range(start, offset); | ||
offset = PlainValue.Node.endOfWhiteSpace(src, offset); | ||
offset = this.parseComment(offset); | ||
return offset; | ||
} | ||
} | ||
function createNewNode(type, props) { | ||
switch (type) { | ||
case PlainValue.Type.ALIAS: | ||
return new Alias(type, props); | ||
case PlainValue.Type.BLOCK_FOLDED: | ||
case PlainValue.Type.BLOCK_LITERAL: | ||
return new BlockValue(type, props); | ||
case PlainValue.Type.FLOW_MAP: | ||
case PlainValue.Type.FLOW_SEQ: | ||
return new FlowCollection(type, props); | ||
case PlainValue.Type.MAP_KEY: | ||
case PlainValue.Type.MAP_VALUE: | ||
case PlainValue.Type.SEQ_ITEM: | ||
return new CollectionItem(type, props); | ||
case PlainValue.Type.COMMENT: | ||
case PlainValue.Type.PLAIN: | ||
return new PlainValue.PlainValue(type, props); | ||
case PlainValue.Type.QUOTE_DOUBLE: | ||
return new QuoteDouble(type, props); | ||
case PlainValue.Type.QUOTE_SINGLE: | ||
return new QuoteSingle(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 PlainValue.Type.ALIAS; | ||
case '>': | ||
return PlainValue.Type.BLOCK_FOLDED; | ||
case '|': | ||
return PlainValue.Type.BLOCK_LITERAL; | ||
case '{': | ||
return PlainValue.Type.FLOW_MAP; | ||
case '[': | ||
return PlainValue.Type.FLOW_SEQ; | ||
case '?': | ||
return !inFlow && PlainValue.Node.atBlank(src, offset + 1, true) ? PlainValue.Type.MAP_KEY : PlainValue.Type.PLAIN; | ||
case ':': | ||
return !inFlow && PlainValue.Node.atBlank(src, offset + 1, true) ? PlainValue.Type.MAP_VALUE : PlainValue.Type.PLAIN; | ||
case '-': | ||
return !inFlow && PlainValue.Node.atBlank(src, offset + 1, true) ? PlainValue.Type.SEQ_ITEM : PlainValue.Type.PLAIN; | ||
case '"': | ||
return PlainValue.Type.QUOTE_DOUBLE; | ||
case "'": | ||
return PlainValue.Type.QUOTE_SINGLE; | ||
default: | ||
return PlainValue.Type.PLAIN; | ||
} | ||
} | ||
constructor(orig = {}, { | ||
atLineStart, | ||
inCollection, | ||
inFlow, | ||
indent, | ||
lineStart, | ||
parent | ||
} = {}) { | ||
PlainValue._defineProperty(this, "parseNode", (overlay, start) => { | ||
if (PlainValue.Node.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 PlainValue.Range(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 === PlainValue.Type.DOCUMENT) { | ||
node.error = new PlainValue.YAMLSyntaxError(node, 'Block collection must not have preceding content here (e.g. directives-end indicator)'); | ||
} | ||
const collection = new Collection(node); | ||
offset = collection.parse(new ParseContext(context), offset); | ||
collection.range = new PlainValue.Range(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) return true; // check for implicit key | ||
let offset = node.range.end; | ||
if (src[offset] === '\n' || src[offset - 1] === '\n') return false; | ||
offset = PlainValue.Node.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 = this.atLineStart ? PlainValue.Node.endOfIndent(src, offset) : PlainValue.Node.endOfWhiteSpace(src, offset); | ||
let ch = src[offset]; | ||
while (ch === PlainValue.Char.ANCHOR || ch === PlainValue.Char.COMMENT || ch === PlainValue.Char.TAG || ch === '\n') { | ||
if (ch === '\n') { | ||
const lineStart = offset + 1; | ||
const inEnd = PlainValue.Node.endOfIndent(src, lineStart); | ||
const indentDiff = inEnd - (lineStart + this.indent); | ||
const noIndicatorAsIndent = parent.type === PlainValue.Type.SEQ_ITEM && parent.context.atLineStart; | ||
if (!PlainValue.Node.nextNodeIsIndented(src[inEnd], indentDiff, !noIndicatorAsIndent)) break; | ||
this.atLineStart = true; | ||
this.lineStart = lineStart; | ||
lineHasProps = false; | ||
offset = inEnd; | ||
} else if (ch === PlainValue.Char.COMMENT) { | ||
const end = PlainValue.Node.endOfLine(src, offset + 1); | ||
props.push(new PlainValue.Range(offset, end)); | ||
offset = end; | ||
} else { | ||
let end = PlainValue.Node.endOfIdentifier(src, offset + 1); | ||
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))) { | ||
// 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 = PlainValue.Node.endOfIdentifier(src, end + 5); | ||
} | ||
props.push(new PlainValue.Range(offset, end)); | ||
lineHasProps = true; | ||
offset = PlainValue.Node.endOfWhiteSpace(src, end); | ||
} | ||
ch = src[offset]; | ||
} // '- &a : b' has an anchor on an empty node | ||
if (lineHasProps && ch === ':' && PlainValue.Node.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 | ||
*/ | ||
} | ||
// Published as 'yaml/parse-cst' | ||
function parse(src) { | ||
const cr = []; | ||
if (src.indexOf('\r') !== -1) { | ||
src = src.replace(/\r\n?/g, (match, offset) => { | ||
if (match.length > 1) cr.push(offset); | ||
return '\n'; | ||
}); | ||
} | ||
const documents = []; | ||
let offset = 0; | ||
do { | ||
const doc = new Document(); | ||
const context = new ParseContext({ | ||
src | ||
}); | ||
offset = doc.parse(context, offset); | ||
documents.push(doc); | ||
} while (offset < src.length); | ||
documents.setOrigRanges = () => { | ||
if (cr.length === 0) return false; | ||
for (let i = 1; i < cr.length; ++i) cr[i] -= i; | ||
let crOffset = 0; | ||
for (let i = 0; i < documents.length; ++i) { | ||
crOffset = documents[i].setOrigRanges(cr, crOffset); | ||
} | ||
cr.splice(0, cr.length); | ||
return true; | ||
}; | ||
documents.toString = () => documents.join('...\n'); | ||
return documents; | ||
} | ||
exports.parse = parse; | ||
exports.parse = parseCst.parse; |
'use strict'; | ||
require('./PlainValue-ec8e588e.js'); | ||
var parseCst = require('./parse-cst.js'); | ||
require('./resolveSeq-4a68b39b.js'); | ||
var Document$1 = require('./Document-2cf6b08c.js'); | ||
require('./Schema-42e9705c.js'); | ||
require('./warnings-39684f17.js'); | ||
var parseCst = require('./parse-ab8de154.js'); | ||
require('./resolveSeq-6b2dc4ac.js'); | ||
var Document = require('./Document-59bd9290.js'); | ||
require('./Schema-fe1471c4.js'); | ||
@@ -16,3 +15,3 @@ function testEvents(src, options) { | ||
}, options); | ||
const docs = parseCst.parse(src).map(cstDoc => new Document$1.Document(opt).parse(cstDoc)); | ||
const docs = parseCst.parse(src).map(cstDoc => new Document.Document(null, opt).parse(cstDoc)); | ||
const errDoc = docs.find(doc => doc.errors.length > 0); | ||
@@ -19,0 +18,0 @@ const error = errDoc ? errDoc.errors[0].message : null; |
'use strict'; | ||
require('./PlainValue-ec8e588e.js'); | ||
var resolveSeq = require('./resolveSeq-4a68b39b.js'); | ||
var Schema = require('./Schema-42e9705c.js'); | ||
require('./warnings-39684f17.js'); | ||
var resolveSeq = require('./resolveSeq-6b2dc4ac.js'); | ||
var Schema = require('./Schema-fe1471c4.js'); | ||
@@ -8,0 +7,0 @@ |
'use strict'; | ||
var PlainValue = require('./PlainValue-ec8e588e.js'); | ||
var resolveSeq = require('./resolveSeq-4a68b39b.js'); | ||
var resolveSeq = require('./resolveSeq-6b2dc4ac.js'); | ||
@@ -6,0 +6,0 @@ |
@@ -8,2 +8,3 @@ import { CST } from './parse-cst' | ||
Node, | ||
Pair, | ||
Scalar, | ||
@@ -82,3 +83,3 @@ Schema, | ||
* | ||
* Default: `false` | ||
* Default: `true` | ||
*/ | ||
@@ -162,3 +163,3 @@ prettyErrors?: boolean | ||
/** | ||
* The default type of string literal used to stringify values | ||
* The default type of string literal used to stringify values in general | ||
* | ||
@@ -168,2 +169,14 @@ * Default: `'PLAIN'` | ||
defaultType: Scalar.Type | ||
/** | ||
* The default type of string literal used to stringify implicit key values | ||
* | ||
* Default: `'PLAIN'` | ||
*/ | ||
defaultKeyType: Scalar.Type | ||
/** | ||
* Use 'single quote' rather than "double quote" by default | ||
* | ||
* Default: `false` | ||
*/ | ||
defaultQuoteSingle: boolean | ||
doubleQuoted: { | ||
@@ -202,3 +215,7 @@ /** | ||
cstNode?: CST.Document | ||
constructor(options?: Options) | ||
/** | ||
* @param value - The initial value for the document, which will be wrapped | ||
* in a Node container. | ||
*/ | ||
constructor(value?: any, options?: Options) | ||
tag: never | ||
@@ -233,3 +250,25 @@ directivesEndMarker?: boolean | ||
warnings: YAMLWarning[] | ||
/** | ||
* Convert any value into a `Node` using the current schema, recursively | ||
* turning objects into collections. | ||
* | ||
* @param options Use `tag` to specify the collection type, e.g. `"!!omap"`. | ||
* Note that this requires the corresponding tag to be available in this | ||
* document's schema. If `wrapScalars` is not `false`, also wraps plain | ||
* values in `Scalar` objects. | ||
*/ | ||
createNode( | ||
value: any, | ||
options?: { tag?: string; wrapScalars?: boolean } | ||
): Node | ||
/** | ||
* Convert a key and a value into a `Pair` using the current schema, | ||
* recursively wrapping all values as `Scalar` or `Collection` nodes. | ||
* | ||
* @param options If `wrapScalars` is not `false`, wraps plain values in | ||
* `Scalar` objects. | ||
*/ | ||
createPair(key: any, value: any, options?: { wrapScalars?: boolean }): Pair | ||
/** | ||
* List the tags used in the document that are not in the default | ||
@@ -236,0 +275,0 @@ * `tag:yaml.org,2002:` namespace. |
{ | ||
"name": "yaml", | ||
"version": "1.10.0", | ||
"version": "2.0.0-0", | ||
"license": "ISC", | ||
@@ -27,15 +27,5 @@ "author": "Eemeli Aro <eemeli@gmail.com>", | ||
"./index.js": "./browser/index.js", | ||
"./map.js": "./browser/map.js", | ||
"./pair.js": "./browser/pair.js", | ||
"./parse-cst.js": "./browser/parse-cst.js", | ||
"./scalar.js": "./browser/scalar.js", | ||
"./schema.js": "./browser/schema.js", | ||
"./seq.js": "./browser/seq.js", | ||
"./types.js": "./browser/types.js", | ||
"./types.mjs": "./browser/types.js", | ||
"./types/binary.js": "./browser/types/binary.js", | ||
"./types/omap.js": "./browser/types/omap.js", | ||
"./types/pairs.js": "./browser/types/pairs.js", | ||
"./types/set.js": "./browser/types/set.js", | ||
"./types/timestamp.js": "./browser/types/timestamp.js", | ||
"./util.js": "./browser/util.js", | ||
@@ -46,2 +36,3 @@ "./util.mjs": "./browser/util.js" | ||
".": "./index.js", | ||
"./package.json": "./package.json", | ||
"./parse-cst": "./parse-cst.js", | ||
@@ -59,4 +50,3 @@ "./types": [ | ||
"./util.js" | ||
], | ||
"./": "./" | ||
] | ||
}, | ||
@@ -72,2 +62,3 @@ "scripts": { | ||
"test": "jest", | ||
"test:browsers": "cd playground && npm test", | ||
"test:dist": "npm run build:node && jest", | ||
@@ -89,19 +80,18 @@ "test:types": "tsc --lib ES2017 --noEmit tests/typings.ts", | ||
"devDependencies": { | ||
"@babel/core": "^7.9.6", | ||
"@babel/plugin-proposal-class-properties": "^7.8.3", | ||
"@babel/preset-env": "^7.9.6", | ||
"@rollup/plugin-babel": "^5.0.0", | ||
"@babel/core": "^7.11.4", | ||
"@babel/plugin-proposal-class-properties": "^7.10.4", | ||
"@babel/preset-env": "^7.11.0", | ||
"@rollup/plugin-babel": "^5.2.0", | ||
"babel-eslint": "^10.1.0", | ||
"babel-jest": "^26.0.1", | ||
"babel-jest": "^26.3.0", | ||
"babel-plugin-trace": "^1.1.0", | ||
"common-tags": "^1.8.0", | ||
"cross-env": "^7.0.2", | ||
"eslint": "^7.0.0", | ||
"eslint": "^7.7.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-plugin-prettier": "^3.1.3", | ||
"fast-check": "^1.24.2", | ||
"jest": "^26.0.1", | ||
"fast-check": "^2.2.0", | ||
"jest": "^26.4.2", | ||
"prettier": "^2.0.5", | ||
"rollup": "^2.10.2", | ||
"typescript": "^3.9.2" | ||
"rollup": "^2.26.5", | ||
"typescript": "^4.0.2" | ||
}, | ||
@@ -108,0 +98,0 @@ "engines": { |
@@ -14,6 +14,6 @@ # YAML <a href="https://www.npmjs.com/package/yaml"><img align="right" src="https://badge.fury.io/js/yaml.svg" title="npm package" /></a><a href="https://travis-ci.org/eemeli/yaml"><img align="right" src="https://travis-ci.org/eemeli/yaml.svg?branch=master" title="Build status" /></a> | ||
```sh | ||
npm install yaml | ||
npm install yaml@next | ||
``` | ||
Note: `yaml` 0.x and 1.x are rather different implementations. For the earlier `yaml`, see [tj/js-yaml](https://github.com/tj/js-yaml). | ||
**Note:** These docs are for `yaml@2`. For v1, see the [v1.10.0 tag](https://github.com/eemeli/yaml/tree/v1.10.0) for the source and [eemeli.org/yaml/v1](https://eemeli.org/yaml/v1/) for the documentation. | ||
@@ -37,7 +37,7 @@ ## API Overview | ||
- [`YAML.createNode(value, wrapScalars, tag): Node`](https://eemeli.org/yaml/#creating-nodes) | ||
- [`YAML.defaultOptions`](https://eemeli.org/yaml/#options) | ||
- [`YAML.Document`](https://eemeli.org/yaml/#yaml-documents) | ||
- [`constructor(options)`](https://eemeli.org/yaml/#creating-documents) | ||
- [`constructor(value, options)`](https://eemeli.org/yaml/#creating-documents) | ||
- [`defaults`](https://eemeli.org/yaml/#options) | ||
- [`#createNode(value, options): Node`](https://eemeli.org/yaml/#creating-nodes) | ||
- [`#anchors`](https://eemeli.org/yaml/#working-with-anchors) | ||
@@ -44,0 +44,0 @@ - [`#contents`](https://eemeli.org/yaml/#content-nodes) |
@@ -12,36 +12,4 @@ import { Document, scalarOptions } from './index' | ||
export class Schema { | ||
/** Default: `'tag:yaml.org,2002:'` */ | ||
static defaultPrefix: string | ||
static defaultTags: { | ||
/** Default: `'tag:yaml.org,2002:map'` */ | ||
MAP: string | ||
/** Default: `'tag:yaml.org,2002:seq'` */ | ||
SEQ: string | ||
/** Default: `'tag:yaml.org,2002:str'` */ | ||
STR: string | ||
} | ||
constructor(options: Schema.Options) | ||
/** | ||
* Convert any value into a `Node` using this schema, recursively turning | ||
* objects into collections. | ||
* | ||
* @param wrapScalars If `true`, also wraps plain values in `Scalar` objects; | ||
* if undefined or `false` and `value` is not an object, it will be returned | ||
* directly. | ||
* @param tag Use to specify the collection type, e.g. `"!!omap"`. Note that | ||
* this requires the corresponding tag to be available in this schema. | ||
*/ | ||
createNode( | ||
value: any, | ||
wrapScalars?: boolean, | ||
tag?: string, | ||
ctx?: Schema.CreateNodeContext | ||
): Node | ||
/** | ||
* Convert a key and a value into a `Pair` using this schema, recursively | ||
* wrapping all values as `Scalar` or `Collection` nodes. | ||
* | ||
* @param ctx To not wrap scalars, use a context `{ wrapScalars: false }` | ||
*/ | ||
createPair(key: any, value: any, ctx?: Schema.CreateNodeContext): Pair | ||
knownTags: { [key: string]: Schema.CustomTag } | ||
merge: boolean | ||
@@ -69,2 +37,11 @@ name: Schema.Name | ||
/** | ||
* When using the `'core'` schema, support parsing values with these | ||
* explicit YAML 1.1 tags: | ||
* | ||
* `!!binary`, `!!omap`, `!!pairs`, `!!set`, `!!timestamp`. | ||
* | ||
* Default `true` | ||
*/ | ||
resolveKnownTags?: boolean | ||
/** | ||
* The base schema to use. | ||
@@ -174,7 +151,2 @@ * | ||
/** | ||
* A JavaScript class that should be matched to this tag, e.g. `Date` for `!!timestamp`. | ||
* @deprecated Use `Tag.identify` instead | ||
*/ | ||
class?: new () => any | ||
/** | ||
* Turns a CST node into an AST node. If returning a non-`Node` value, the | ||
@@ -181,0 +153,0 @@ * output will be wrapped as a `Scalar`. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
16
440813
35
13221
1