[6a3a178] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | var PlainValue = require('./PlainValue-ec8e588e.js');
|
---|
| 4 | var resolveSeq = require('./resolveSeq-d03cb037.js');
|
---|
| 5 | var Schema = require('./Schema-88e323a7.js');
|
---|
| 6 |
|
---|
| 7 | const defaultOptions = {
|
---|
| 8 | anchorPrefix: 'a',
|
---|
| 9 | customTags: null,
|
---|
| 10 | indent: 2,
|
---|
| 11 | indentSeq: true,
|
---|
| 12 | keepCstNodes: false,
|
---|
| 13 | keepNodeTypes: true,
|
---|
| 14 | keepBlobsInJSON: true,
|
---|
| 15 | mapAsMap: false,
|
---|
| 16 | maxAliasCount: 100,
|
---|
| 17 | prettyErrors: false,
|
---|
| 18 | // TODO Set true in v2
|
---|
| 19 | simpleKeys: false,
|
---|
| 20 | version: '1.2'
|
---|
| 21 | };
|
---|
| 22 | const scalarOptions = {
|
---|
| 23 | get binary() {
|
---|
| 24 | return resolveSeq.binaryOptions;
|
---|
| 25 | },
|
---|
| 26 |
|
---|
| 27 | set binary(opt) {
|
---|
| 28 | Object.assign(resolveSeq.binaryOptions, opt);
|
---|
| 29 | },
|
---|
| 30 |
|
---|
| 31 | get bool() {
|
---|
| 32 | return resolveSeq.boolOptions;
|
---|
| 33 | },
|
---|
| 34 |
|
---|
| 35 | set bool(opt) {
|
---|
| 36 | Object.assign(resolveSeq.boolOptions, opt);
|
---|
| 37 | },
|
---|
| 38 |
|
---|
| 39 | get int() {
|
---|
| 40 | return resolveSeq.intOptions;
|
---|
| 41 | },
|
---|
| 42 |
|
---|
| 43 | set int(opt) {
|
---|
| 44 | Object.assign(resolveSeq.intOptions, opt);
|
---|
| 45 | },
|
---|
| 46 |
|
---|
| 47 | get null() {
|
---|
| 48 | return resolveSeq.nullOptions;
|
---|
| 49 | },
|
---|
| 50 |
|
---|
| 51 | set null(opt) {
|
---|
| 52 | Object.assign(resolveSeq.nullOptions, opt);
|
---|
| 53 | },
|
---|
| 54 |
|
---|
| 55 | get str() {
|
---|
| 56 | return resolveSeq.strOptions;
|
---|
| 57 | },
|
---|
| 58 |
|
---|
| 59 | set str(opt) {
|
---|
| 60 | Object.assign(resolveSeq.strOptions, opt);
|
---|
| 61 | }
|
---|
| 62 |
|
---|
| 63 | };
|
---|
| 64 | const documentOptions = {
|
---|
| 65 | '1.0': {
|
---|
| 66 | schema: 'yaml-1.1',
|
---|
| 67 | merge: true,
|
---|
| 68 | tagPrefixes: [{
|
---|
| 69 | handle: '!',
|
---|
| 70 | prefix: PlainValue.defaultTagPrefix
|
---|
| 71 | }, {
|
---|
| 72 | handle: '!!',
|
---|
| 73 | prefix: 'tag:private.yaml.org,2002:'
|
---|
| 74 | }]
|
---|
| 75 | },
|
---|
| 76 | 1.1: {
|
---|
| 77 | schema: 'yaml-1.1',
|
---|
| 78 | merge: true,
|
---|
| 79 | tagPrefixes: [{
|
---|
| 80 | handle: '!',
|
---|
| 81 | prefix: '!'
|
---|
| 82 | }, {
|
---|
| 83 | handle: '!!',
|
---|
| 84 | prefix: PlainValue.defaultTagPrefix
|
---|
| 85 | }]
|
---|
| 86 | },
|
---|
| 87 | 1.2: {
|
---|
| 88 | schema: 'core',
|
---|
| 89 | merge: false,
|
---|
| 90 | tagPrefixes: [{
|
---|
| 91 | handle: '!',
|
---|
| 92 | prefix: '!'
|
---|
| 93 | }, {
|
---|
| 94 | handle: '!!',
|
---|
| 95 | prefix: PlainValue.defaultTagPrefix
|
---|
| 96 | }]
|
---|
| 97 | }
|
---|
| 98 | };
|
---|
| 99 |
|
---|
| 100 | function stringifyTag(doc, tag) {
|
---|
| 101 | if ((doc.version || doc.options.version) === '1.0') {
|
---|
| 102 | const priv = tag.match(/^tag:private\.yaml\.org,2002:([^:/]+)$/);
|
---|
| 103 | if (priv) return '!' + priv[1];
|
---|
| 104 | const vocab = tag.match(/^tag:([a-zA-Z0-9-]+)\.yaml\.org,2002:(.*)/);
|
---|
| 105 | return vocab ? `!${vocab[1]}/${vocab[2]}` : `!${tag.replace(/^tag:/, '')}`;
|
---|
| 106 | }
|
---|
| 107 |
|
---|
| 108 | let p = doc.tagPrefixes.find(p => tag.indexOf(p.prefix) === 0);
|
---|
| 109 |
|
---|
| 110 | if (!p) {
|
---|
| 111 | const dtp = doc.getDefaults().tagPrefixes;
|
---|
| 112 | p = dtp && dtp.find(p => tag.indexOf(p.prefix) === 0);
|
---|
| 113 | }
|
---|
| 114 |
|
---|
| 115 | if (!p) return tag[0] === '!' ? tag : `!<${tag}>`;
|
---|
| 116 | const suffix = tag.substr(p.prefix.length).replace(/[!,[\]{}]/g, ch => ({
|
---|
| 117 | '!': '%21',
|
---|
| 118 | ',': '%2C',
|
---|
| 119 | '[': '%5B',
|
---|
| 120 | ']': '%5D',
|
---|
| 121 | '{': '%7B',
|
---|
| 122 | '}': '%7D'
|
---|
| 123 | })[ch]);
|
---|
| 124 | return p.handle + suffix;
|
---|
| 125 | }
|
---|
| 126 |
|
---|
| 127 | function getTagObject(tags, item) {
|
---|
| 128 | if (item instanceof resolveSeq.Alias) return resolveSeq.Alias;
|
---|
| 129 |
|
---|
| 130 | if (item.tag) {
|
---|
| 131 | const match = tags.filter(t => t.tag === item.tag);
|
---|
| 132 | if (match.length > 0) return match.find(t => t.format === item.format) || match[0];
|
---|
| 133 | }
|
---|
| 134 |
|
---|
| 135 | let tagObj, obj;
|
---|
| 136 |
|
---|
| 137 | if (item instanceof resolveSeq.Scalar) {
|
---|
| 138 | obj = item.value; // TODO: deprecate/remove class check
|
---|
| 139 |
|
---|
| 140 | const match = tags.filter(t => t.identify && t.identify(obj) || t.class && obj instanceof t.class);
|
---|
| 141 | tagObj = match.find(t => t.format === item.format) || match.find(t => !t.format);
|
---|
| 142 | } else {
|
---|
| 143 | obj = item;
|
---|
| 144 | tagObj = tags.find(t => t.nodeClass && obj instanceof t.nodeClass);
|
---|
| 145 | }
|
---|
| 146 |
|
---|
| 147 | if (!tagObj) {
|
---|
| 148 | const name = obj && obj.constructor ? obj.constructor.name : typeof obj;
|
---|
| 149 | throw new Error(`Tag not resolved for ${name} value`);
|
---|
| 150 | }
|
---|
| 151 |
|
---|
| 152 | return tagObj;
|
---|
| 153 | } // needs to be called before value stringifier to allow for circular anchor refs
|
---|
| 154 |
|
---|
| 155 |
|
---|
| 156 | function stringifyProps(node, tagObj, {
|
---|
| 157 | anchors,
|
---|
| 158 | doc
|
---|
| 159 | }) {
|
---|
| 160 | const props = [];
|
---|
| 161 | const anchor = doc.anchors.getName(node);
|
---|
| 162 |
|
---|
| 163 | if (anchor) {
|
---|
| 164 | anchors[anchor] = node;
|
---|
| 165 | props.push(`&${anchor}`);
|
---|
| 166 | }
|
---|
| 167 |
|
---|
| 168 | if (node.tag) {
|
---|
| 169 | props.push(stringifyTag(doc, node.tag));
|
---|
| 170 | } else if (!tagObj.default) {
|
---|
| 171 | props.push(stringifyTag(doc, tagObj.tag));
|
---|
| 172 | }
|
---|
| 173 |
|
---|
| 174 | return props.join(' ');
|
---|
| 175 | }
|
---|
| 176 |
|
---|
| 177 | function stringify(item, ctx, onComment, onChompKeep) {
|
---|
| 178 | const {
|
---|
| 179 | anchors,
|
---|
| 180 | schema
|
---|
| 181 | } = ctx.doc;
|
---|
| 182 | let tagObj;
|
---|
| 183 |
|
---|
| 184 | if (!(item instanceof resolveSeq.Node)) {
|
---|
| 185 | const createCtx = {
|
---|
| 186 | aliasNodes: [],
|
---|
| 187 | onTagObj: o => tagObj = o,
|
---|
| 188 | prevObjects: new Map()
|
---|
| 189 | };
|
---|
| 190 | item = schema.createNode(item, true, null, createCtx);
|
---|
| 191 |
|
---|
| 192 | for (const alias of createCtx.aliasNodes) {
|
---|
| 193 | alias.source = alias.source.node;
|
---|
| 194 | let name = anchors.getName(alias.source);
|
---|
| 195 |
|
---|
| 196 | if (!name) {
|
---|
| 197 | name = anchors.newName();
|
---|
| 198 | anchors.map[name] = alias.source;
|
---|
| 199 | }
|
---|
| 200 | }
|
---|
| 201 | }
|
---|
| 202 |
|
---|
| 203 | if (item instanceof resolveSeq.Pair) return item.toString(ctx, onComment, onChompKeep);
|
---|
| 204 | if (!tagObj) tagObj = getTagObject(schema.tags, item);
|
---|
| 205 | const props = stringifyProps(item, tagObj, ctx);
|
---|
| 206 | if (props.length > 0) ctx.indentAtStart = (ctx.indentAtStart || 0) + props.length + 1;
|
---|
| 207 | const str = typeof tagObj.stringify === 'function' ? tagObj.stringify(item, ctx, onComment, onChompKeep) : item instanceof resolveSeq.Scalar ? resolveSeq.stringifyString(item, ctx, onComment, onChompKeep) : item.toString(ctx, onComment, onChompKeep);
|
---|
| 208 | if (!props) return str;
|
---|
| 209 | return item instanceof resolveSeq.Scalar || str[0] === '{' || str[0] === '[' ? `${props} ${str}` : `${props}\n${ctx.indent}${str}`;
|
---|
| 210 | }
|
---|
| 211 |
|
---|
| 212 | class Anchors {
|
---|
| 213 | static validAnchorNode(node) {
|
---|
| 214 | return node instanceof resolveSeq.Scalar || node instanceof resolveSeq.YAMLSeq || node instanceof resolveSeq.YAMLMap;
|
---|
| 215 | }
|
---|
| 216 |
|
---|
| 217 | constructor(prefix) {
|
---|
| 218 | PlainValue._defineProperty(this, "map", Object.create(null));
|
---|
| 219 |
|
---|
| 220 | this.prefix = prefix;
|
---|
| 221 | }
|
---|
| 222 |
|
---|
| 223 | createAlias(node, name) {
|
---|
| 224 | this.setAnchor(node, name);
|
---|
| 225 | return new resolveSeq.Alias(node);
|
---|
| 226 | }
|
---|
| 227 |
|
---|
| 228 | createMergePair(...sources) {
|
---|
| 229 | const merge = new resolveSeq.Merge();
|
---|
| 230 | merge.value.items = sources.map(s => {
|
---|
| 231 | if (s instanceof resolveSeq.Alias) {
|
---|
| 232 | if (s.source instanceof resolveSeq.YAMLMap) return s;
|
---|
| 233 | } else if (s instanceof resolveSeq.YAMLMap) {
|
---|
| 234 | return this.createAlias(s);
|
---|
| 235 | }
|
---|
| 236 |
|
---|
| 237 | throw new Error('Merge sources must be Map nodes or their Aliases');
|
---|
| 238 | });
|
---|
| 239 | return merge;
|
---|
| 240 | }
|
---|
| 241 |
|
---|
| 242 | getName(node) {
|
---|
| 243 | const {
|
---|
| 244 | map
|
---|
| 245 | } = this;
|
---|
| 246 | return Object.keys(map).find(a => map[a] === node);
|
---|
| 247 | }
|
---|
| 248 |
|
---|
| 249 | getNames() {
|
---|
| 250 | return Object.keys(this.map);
|
---|
| 251 | }
|
---|
| 252 |
|
---|
| 253 | getNode(name) {
|
---|
| 254 | return this.map[name];
|
---|
| 255 | }
|
---|
| 256 |
|
---|
| 257 | newName(prefix) {
|
---|
| 258 | if (!prefix) prefix = this.prefix;
|
---|
| 259 | const names = Object.keys(this.map);
|
---|
| 260 |
|
---|
| 261 | for (let i = 1; true; ++i) {
|
---|
| 262 | const name = `${prefix}${i}`;
|
---|
| 263 | if (!names.includes(name)) return name;
|
---|
| 264 | }
|
---|
| 265 | } // During parsing, map & aliases contain CST nodes
|
---|
| 266 |
|
---|
| 267 |
|
---|
| 268 | resolveNodes() {
|
---|
| 269 | const {
|
---|
| 270 | map,
|
---|
| 271 | _cstAliases
|
---|
| 272 | } = this;
|
---|
| 273 | Object.keys(map).forEach(a => {
|
---|
| 274 | map[a] = map[a].resolved;
|
---|
| 275 | });
|
---|
| 276 |
|
---|
| 277 | _cstAliases.forEach(a => {
|
---|
| 278 | a.source = a.source.resolved;
|
---|
| 279 | });
|
---|
| 280 |
|
---|
| 281 | delete this._cstAliases;
|
---|
| 282 | }
|
---|
| 283 |
|
---|
| 284 | setAnchor(node, name) {
|
---|
| 285 | if (node != null && !Anchors.validAnchorNode(node)) {
|
---|
| 286 | throw new Error('Anchors may only be set for Scalar, Seq and Map nodes');
|
---|
| 287 | }
|
---|
| 288 |
|
---|
| 289 | if (name && /[\x00-\x19\s,[\]{}]/.test(name)) {
|
---|
| 290 | throw new Error('Anchor names must not contain whitespace or control characters');
|
---|
| 291 | }
|
---|
| 292 |
|
---|
| 293 | const {
|
---|
| 294 | map
|
---|
| 295 | } = this;
|
---|
| 296 | const prev = node && Object.keys(map).find(a => map[a] === node);
|
---|
| 297 |
|
---|
| 298 | if (prev) {
|
---|
| 299 | if (!name) {
|
---|
| 300 | return prev;
|
---|
| 301 | } else if (prev !== name) {
|
---|
| 302 | delete map[prev];
|
---|
| 303 | map[name] = node;
|
---|
| 304 | }
|
---|
| 305 | } else {
|
---|
| 306 | if (!name) {
|
---|
| 307 | if (!node) return null;
|
---|
| 308 | name = this.newName();
|
---|
| 309 | }
|
---|
| 310 |
|
---|
| 311 | map[name] = node;
|
---|
| 312 | }
|
---|
| 313 |
|
---|
| 314 | return name;
|
---|
| 315 | }
|
---|
| 316 |
|
---|
| 317 | }
|
---|
| 318 |
|
---|
| 319 | const visit = (node, tags) => {
|
---|
| 320 | if (node && typeof node === 'object') {
|
---|
| 321 | const {
|
---|
| 322 | tag
|
---|
| 323 | } = node;
|
---|
| 324 |
|
---|
| 325 | if (node instanceof resolveSeq.Collection) {
|
---|
| 326 | if (tag) tags[tag] = true;
|
---|
| 327 | node.items.forEach(n => visit(n, tags));
|
---|
| 328 | } else if (node instanceof resolveSeq.Pair) {
|
---|
| 329 | visit(node.key, tags);
|
---|
| 330 | visit(node.value, tags);
|
---|
| 331 | } else if (node instanceof resolveSeq.Scalar) {
|
---|
| 332 | if (tag) tags[tag] = true;
|
---|
| 333 | }
|
---|
| 334 | }
|
---|
| 335 |
|
---|
| 336 | return tags;
|
---|
| 337 | };
|
---|
| 338 |
|
---|
| 339 | const listTagNames = node => Object.keys(visit(node, {}));
|
---|
| 340 |
|
---|
| 341 | function parseContents(doc, contents) {
|
---|
| 342 | const comments = {
|
---|
| 343 | before: [],
|
---|
| 344 | after: []
|
---|
| 345 | };
|
---|
| 346 | let body = undefined;
|
---|
| 347 | let spaceBefore = false;
|
---|
| 348 |
|
---|
| 349 | for (const node of contents) {
|
---|
| 350 | if (node.valueRange) {
|
---|
| 351 | if (body !== undefined) {
|
---|
| 352 | const msg = 'Document contains trailing content not separated by a ... or --- line';
|
---|
| 353 | doc.errors.push(new PlainValue.YAMLSyntaxError(node, msg));
|
---|
| 354 | break;
|
---|
| 355 | }
|
---|
| 356 |
|
---|
| 357 | const res = resolveSeq.resolveNode(doc, node);
|
---|
| 358 |
|
---|
| 359 | if (spaceBefore) {
|
---|
| 360 | res.spaceBefore = true;
|
---|
| 361 | spaceBefore = false;
|
---|
| 362 | }
|
---|
| 363 |
|
---|
| 364 | body = res;
|
---|
| 365 | } else if (node.comment !== null) {
|
---|
| 366 | const cc = body === undefined ? comments.before : comments.after;
|
---|
| 367 | cc.push(node.comment);
|
---|
| 368 | } else if (node.type === PlainValue.Type.BLANK_LINE) {
|
---|
| 369 | spaceBefore = true;
|
---|
| 370 |
|
---|
| 371 | if (body === undefined && comments.before.length > 0 && !doc.commentBefore) {
|
---|
| 372 | // space-separated comments at start are parsed as document comments
|
---|
| 373 | doc.commentBefore = comments.before.join('\n');
|
---|
| 374 | comments.before = [];
|
---|
| 375 | }
|
---|
| 376 | }
|
---|
| 377 | }
|
---|
| 378 |
|
---|
| 379 | doc.contents = body || null;
|
---|
| 380 |
|
---|
| 381 | if (!body) {
|
---|
| 382 | doc.comment = comments.before.concat(comments.after).join('\n') || null;
|
---|
| 383 | } else {
|
---|
| 384 | const cb = comments.before.join('\n');
|
---|
| 385 |
|
---|
| 386 | if (cb) {
|
---|
| 387 | const cbNode = body instanceof resolveSeq.Collection && body.items[0] ? body.items[0] : body;
|
---|
| 388 | cbNode.commentBefore = cbNode.commentBefore ? `${cb}\n${cbNode.commentBefore}` : cb;
|
---|
| 389 | }
|
---|
| 390 |
|
---|
| 391 | doc.comment = comments.after.join('\n') || null;
|
---|
| 392 | }
|
---|
| 393 | }
|
---|
| 394 |
|
---|
| 395 | function resolveTagDirective({
|
---|
| 396 | tagPrefixes
|
---|
| 397 | }, directive) {
|
---|
| 398 | const [handle, prefix] = directive.parameters;
|
---|
| 399 |
|
---|
| 400 | if (!handle || !prefix) {
|
---|
| 401 | const msg = 'Insufficient parameters given for %TAG directive';
|
---|
| 402 | throw new PlainValue.YAMLSemanticError(directive, msg);
|
---|
| 403 | }
|
---|
| 404 |
|
---|
| 405 | if (tagPrefixes.some(p => p.handle === handle)) {
|
---|
| 406 | const msg = 'The %TAG directive must only be given at most once per handle in the same document.';
|
---|
| 407 | throw new PlainValue.YAMLSemanticError(directive, msg);
|
---|
| 408 | }
|
---|
| 409 |
|
---|
| 410 | return {
|
---|
| 411 | handle,
|
---|
| 412 | prefix
|
---|
| 413 | };
|
---|
| 414 | }
|
---|
| 415 |
|
---|
| 416 | function resolveYamlDirective(doc, directive) {
|
---|
| 417 | let [version] = directive.parameters;
|
---|
| 418 | if (directive.name === 'YAML:1.0') version = '1.0';
|
---|
| 419 |
|
---|
| 420 | if (!version) {
|
---|
| 421 | const msg = 'Insufficient parameters given for %YAML directive';
|
---|
| 422 | throw new PlainValue.YAMLSemanticError(directive, msg);
|
---|
| 423 | }
|
---|
| 424 |
|
---|
| 425 | if (!documentOptions[version]) {
|
---|
| 426 | const v0 = doc.version || doc.options.version;
|
---|
| 427 | const msg = `Document will be parsed as YAML ${v0} rather than YAML ${version}`;
|
---|
| 428 | doc.warnings.push(new PlainValue.YAMLWarning(directive, msg));
|
---|
| 429 | }
|
---|
| 430 |
|
---|
| 431 | return version;
|
---|
| 432 | }
|
---|
| 433 |
|
---|
| 434 | function parseDirectives(doc, directives, prevDoc) {
|
---|
| 435 | const directiveComments = [];
|
---|
| 436 | let hasDirectives = false;
|
---|
| 437 |
|
---|
| 438 | for (const directive of directives) {
|
---|
| 439 | const {
|
---|
| 440 | comment,
|
---|
| 441 | name
|
---|
| 442 | } = directive;
|
---|
| 443 |
|
---|
| 444 | switch (name) {
|
---|
| 445 | case 'TAG':
|
---|
| 446 | try {
|
---|
| 447 | doc.tagPrefixes.push(resolveTagDirective(doc, directive));
|
---|
| 448 | } catch (error) {
|
---|
| 449 | doc.errors.push(error);
|
---|
| 450 | }
|
---|
| 451 |
|
---|
| 452 | hasDirectives = true;
|
---|
| 453 | break;
|
---|
| 454 |
|
---|
| 455 | case 'YAML':
|
---|
| 456 | case 'YAML:1.0':
|
---|
| 457 | if (doc.version) {
|
---|
| 458 | const msg = 'The %YAML directive must only be given at most once per document.';
|
---|
| 459 | doc.errors.push(new PlainValue.YAMLSemanticError(directive, msg));
|
---|
| 460 | }
|
---|
| 461 |
|
---|
| 462 | try {
|
---|
| 463 | doc.version = resolveYamlDirective(doc, directive);
|
---|
| 464 | } catch (error) {
|
---|
| 465 | doc.errors.push(error);
|
---|
| 466 | }
|
---|
| 467 |
|
---|
| 468 | hasDirectives = true;
|
---|
| 469 | break;
|
---|
| 470 |
|
---|
| 471 | default:
|
---|
| 472 | if (name) {
|
---|
| 473 | const msg = `YAML only supports %TAG and %YAML directives, and not %${name}`;
|
---|
| 474 | doc.warnings.push(new PlainValue.YAMLWarning(directive, msg));
|
---|
| 475 | }
|
---|
| 476 |
|
---|
| 477 | }
|
---|
| 478 |
|
---|
| 479 | if (comment) directiveComments.push(comment);
|
---|
| 480 | }
|
---|
| 481 |
|
---|
| 482 | if (prevDoc && !hasDirectives && '1.1' === (doc.version || prevDoc.version || doc.options.version)) {
|
---|
| 483 | const copyTagPrefix = ({
|
---|
| 484 | handle,
|
---|
| 485 | prefix
|
---|
| 486 | }) => ({
|
---|
| 487 | handle,
|
---|
| 488 | prefix
|
---|
| 489 | });
|
---|
| 490 |
|
---|
| 491 | doc.tagPrefixes = prevDoc.tagPrefixes.map(copyTagPrefix);
|
---|
| 492 | doc.version = prevDoc.version;
|
---|
| 493 | }
|
---|
| 494 |
|
---|
| 495 | doc.commentBefore = directiveComments.join('\n') || null;
|
---|
| 496 | }
|
---|
| 497 |
|
---|
| 498 | function assertCollection(contents) {
|
---|
| 499 | if (contents instanceof resolveSeq.Collection) return true;
|
---|
| 500 | throw new Error('Expected a YAML collection as document contents');
|
---|
| 501 | }
|
---|
| 502 |
|
---|
| 503 | class Document {
|
---|
| 504 | constructor(options) {
|
---|
| 505 | this.anchors = new Anchors(options.anchorPrefix);
|
---|
| 506 | this.commentBefore = null;
|
---|
| 507 | this.comment = null;
|
---|
| 508 | this.contents = null;
|
---|
| 509 | this.directivesEndMarker = null;
|
---|
| 510 | this.errors = [];
|
---|
| 511 | this.options = options;
|
---|
| 512 | this.schema = null;
|
---|
| 513 | this.tagPrefixes = [];
|
---|
| 514 | this.version = null;
|
---|
| 515 | this.warnings = [];
|
---|
| 516 | }
|
---|
| 517 |
|
---|
| 518 | add(value) {
|
---|
| 519 | assertCollection(this.contents);
|
---|
| 520 | return this.contents.add(value);
|
---|
| 521 | }
|
---|
| 522 |
|
---|
| 523 | addIn(path, value) {
|
---|
| 524 | assertCollection(this.contents);
|
---|
| 525 | this.contents.addIn(path, value);
|
---|
| 526 | }
|
---|
| 527 |
|
---|
| 528 | delete(key) {
|
---|
| 529 | assertCollection(this.contents);
|
---|
| 530 | return this.contents.delete(key);
|
---|
| 531 | }
|
---|
| 532 |
|
---|
| 533 | deleteIn(path) {
|
---|
| 534 | if (resolveSeq.isEmptyPath(path)) {
|
---|
| 535 | if (this.contents == null) return false;
|
---|
| 536 | this.contents = null;
|
---|
| 537 | return true;
|
---|
| 538 | }
|
---|
| 539 |
|
---|
| 540 | assertCollection(this.contents);
|
---|
| 541 | return this.contents.deleteIn(path);
|
---|
| 542 | }
|
---|
| 543 |
|
---|
| 544 | getDefaults() {
|
---|
| 545 | return Document.defaults[this.version] || Document.defaults[this.options.version] || {};
|
---|
| 546 | }
|
---|
| 547 |
|
---|
| 548 | get(key, keepScalar) {
|
---|
| 549 | return this.contents instanceof resolveSeq.Collection ? this.contents.get(key, keepScalar) : undefined;
|
---|
| 550 | }
|
---|
| 551 |
|
---|
| 552 | getIn(path, keepScalar) {
|
---|
| 553 | if (resolveSeq.isEmptyPath(path)) return !keepScalar && this.contents instanceof resolveSeq.Scalar ? this.contents.value : this.contents;
|
---|
| 554 | return this.contents instanceof resolveSeq.Collection ? this.contents.getIn(path, keepScalar) : undefined;
|
---|
| 555 | }
|
---|
| 556 |
|
---|
| 557 | has(key) {
|
---|
| 558 | return this.contents instanceof resolveSeq.Collection ? this.contents.has(key) : false;
|
---|
| 559 | }
|
---|
| 560 |
|
---|
| 561 | hasIn(path) {
|
---|
| 562 | if (resolveSeq.isEmptyPath(path)) return this.contents !== undefined;
|
---|
| 563 | return this.contents instanceof resolveSeq.Collection ? this.contents.hasIn(path) : false;
|
---|
| 564 | }
|
---|
| 565 |
|
---|
| 566 | set(key, value) {
|
---|
| 567 | assertCollection(this.contents);
|
---|
| 568 | this.contents.set(key, value);
|
---|
| 569 | }
|
---|
| 570 |
|
---|
| 571 | setIn(path, value) {
|
---|
| 572 | if (resolveSeq.isEmptyPath(path)) this.contents = value;else {
|
---|
| 573 | assertCollection(this.contents);
|
---|
| 574 | this.contents.setIn(path, value);
|
---|
| 575 | }
|
---|
| 576 | }
|
---|
| 577 |
|
---|
| 578 | setSchema(id, customTags) {
|
---|
| 579 | if (!id && !customTags && this.schema) return;
|
---|
| 580 | if (typeof id === 'number') id = id.toFixed(1);
|
---|
| 581 |
|
---|
| 582 | if (id === '1.0' || id === '1.1' || id === '1.2') {
|
---|
| 583 | if (this.version) this.version = id;else this.options.version = id;
|
---|
| 584 | delete this.options.schema;
|
---|
| 585 | } else if (id && typeof id === 'string') {
|
---|
| 586 | this.options.schema = id;
|
---|
| 587 | }
|
---|
| 588 |
|
---|
| 589 | if (Array.isArray(customTags)) this.options.customTags = customTags;
|
---|
| 590 | const opt = Object.assign({}, this.getDefaults(), this.options);
|
---|
| 591 | this.schema = new Schema.Schema(opt);
|
---|
| 592 | }
|
---|
| 593 |
|
---|
| 594 | parse(node, prevDoc) {
|
---|
| 595 | if (this.options.keepCstNodes) this.cstNode = node;
|
---|
| 596 | if (this.options.keepNodeTypes) this.type = 'DOCUMENT';
|
---|
| 597 | const {
|
---|
| 598 | directives = [],
|
---|
| 599 | contents = [],
|
---|
| 600 | directivesEndMarker,
|
---|
| 601 | error,
|
---|
| 602 | valueRange
|
---|
| 603 | } = node;
|
---|
| 604 |
|
---|
| 605 | if (error) {
|
---|
| 606 | if (!error.source) error.source = this;
|
---|
| 607 | this.errors.push(error);
|
---|
| 608 | }
|
---|
| 609 |
|
---|
| 610 | parseDirectives(this, directives, prevDoc);
|
---|
| 611 | if (directivesEndMarker) this.directivesEndMarker = true;
|
---|
| 612 | this.range = valueRange ? [valueRange.start, valueRange.end] : null;
|
---|
| 613 | this.setSchema();
|
---|
| 614 | this.anchors._cstAliases = [];
|
---|
| 615 | parseContents(this, contents);
|
---|
| 616 | this.anchors.resolveNodes();
|
---|
| 617 |
|
---|
| 618 | if (this.options.prettyErrors) {
|
---|
| 619 | for (const error of this.errors) if (error instanceof PlainValue.YAMLError) error.makePretty();
|
---|
| 620 |
|
---|
| 621 | for (const warn of this.warnings) if (warn instanceof PlainValue.YAMLError) warn.makePretty();
|
---|
| 622 | }
|
---|
| 623 |
|
---|
| 624 | return this;
|
---|
| 625 | }
|
---|
| 626 |
|
---|
| 627 | listNonDefaultTags() {
|
---|
| 628 | return listTagNames(this.contents).filter(t => t.indexOf(Schema.Schema.defaultPrefix) !== 0);
|
---|
| 629 | }
|
---|
| 630 |
|
---|
| 631 | setTagPrefix(handle, prefix) {
|
---|
| 632 | if (handle[0] !== '!' || handle[handle.length - 1] !== '!') throw new Error('Handle must start and end with !');
|
---|
| 633 |
|
---|
| 634 | if (prefix) {
|
---|
| 635 | const prev = this.tagPrefixes.find(p => p.handle === handle);
|
---|
| 636 | if (prev) prev.prefix = prefix;else this.tagPrefixes.push({
|
---|
| 637 | handle,
|
---|
| 638 | prefix
|
---|
| 639 | });
|
---|
| 640 | } else {
|
---|
| 641 | this.tagPrefixes = this.tagPrefixes.filter(p => p.handle !== handle);
|
---|
| 642 | }
|
---|
| 643 | }
|
---|
| 644 |
|
---|
| 645 | toJSON(arg, onAnchor) {
|
---|
| 646 | const {
|
---|
| 647 | keepBlobsInJSON,
|
---|
| 648 | mapAsMap,
|
---|
| 649 | maxAliasCount
|
---|
| 650 | } = this.options;
|
---|
| 651 | const keep = keepBlobsInJSON && (typeof arg !== 'string' || !(this.contents instanceof resolveSeq.Scalar));
|
---|
| 652 | const ctx = {
|
---|
| 653 | doc: this,
|
---|
| 654 | indentStep: ' ',
|
---|
| 655 | keep,
|
---|
| 656 | mapAsMap: keep && !!mapAsMap,
|
---|
| 657 | maxAliasCount,
|
---|
| 658 | stringify // Requiring directly in Pair would create circular dependencies
|
---|
| 659 |
|
---|
| 660 | };
|
---|
| 661 | const anchorNames = Object.keys(this.anchors.map);
|
---|
| 662 | if (anchorNames.length > 0) ctx.anchors = new Map(anchorNames.map(name => [this.anchors.map[name], {
|
---|
| 663 | alias: [],
|
---|
| 664 | aliasCount: 0,
|
---|
| 665 | count: 1
|
---|
| 666 | }]));
|
---|
| 667 | const res = resolveSeq.toJSON(this.contents, arg, ctx);
|
---|
| 668 | if (typeof onAnchor === 'function' && ctx.anchors) for (const {
|
---|
| 669 | count,
|
---|
| 670 | res
|
---|
| 671 | } of ctx.anchors.values()) onAnchor(res, count);
|
---|
| 672 | return res;
|
---|
| 673 | }
|
---|
| 674 |
|
---|
| 675 | toString() {
|
---|
| 676 | if (this.errors.length > 0) throw new Error('Document with errors cannot be stringified');
|
---|
| 677 | const indentSize = this.options.indent;
|
---|
| 678 |
|
---|
| 679 | if (!Number.isInteger(indentSize) || indentSize <= 0) {
|
---|
| 680 | const s = JSON.stringify(indentSize);
|
---|
| 681 | throw new Error(`"indent" option must be a positive integer, not ${s}`);
|
---|
| 682 | }
|
---|
| 683 |
|
---|
| 684 | this.setSchema();
|
---|
| 685 | const lines = [];
|
---|
| 686 | let hasDirectives = false;
|
---|
| 687 |
|
---|
| 688 | if (this.version) {
|
---|
| 689 | let vd = '%YAML 1.2';
|
---|
| 690 |
|
---|
| 691 | if (this.schema.name === 'yaml-1.1') {
|
---|
| 692 | if (this.version === '1.0') vd = '%YAML:1.0';else if (this.version === '1.1') vd = '%YAML 1.1';
|
---|
| 693 | }
|
---|
| 694 |
|
---|
| 695 | lines.push(vd);
|
---|
| 696 | hasDirectives = true;
|
---|
| 697 | }
|
---|
| 698 |
|
---|
| 699 | const tagNames = this.listNonDefaultTags();
|
---|
| 700 | this.tagPrefixes.forEach(({
|
---|
| 701 | handle,
|
---|
| 702 | prefix
|
---|
| 703 | }) => {
|
---|
| 704 | if (tagNames.some(t => t.indexOf(prefix) === 0)) {
|
---|
| 705 | lines.push(`%TAG ${handle} ${prefix}`);
|
---|
| 706 | hasDirectives = true;
|
---|
| 707 | }
|
---|
| 708 | });
|
---|
| 709 | if (hasDirectives || this.directivesEndMarker) lines.push('---');
|
---|
| 710 |
|
---|
| 711 | if (this.commentBefore) {
|
---|
| 712 | if (hasDirectives || !this.directivesEndMarker) lines.unshift('');
|
---|
| 713 | lines.unshift(this.commentBefore.replace(/^/gm, '#'));
|
---|
| 714 | }
|
---|
| 715 |
|
---|
| 716 | const ctx = {
|
---|
| 717 | anchors: Object.create(null),
|
---|
| 718 | doc: this,
|
---|
| 719 | indent: '',
|
---|
| 720 | indentStep: ' '.repeat(indentSize),
|
---|
| 721 | stringify // Requiring directly in nodes would create circular dependencies
|
---|
| 722 |
|
---|
| 723 | };
|
---|
| 724 | let chompKeep = false;
|
---|
| 725 | let contentComment = null;
|
---|
| 726 |
|
---|
| 727 | if (this.contents) {
|
---|
| 728 | if (this.contents instanceof resolveSeq.Node) {
|
---|
| 729 | if (this.contents.spaceBefore && (hasDirectives || this.directivesEndMarker)) lines.push('');
|
---|
| 730 | if (this.contents.commentBefore) lines.push(this.contents.commentBefore.replace(/^/gm, '#')); // top-level block scalars need to be indented if followed by a comment
|
---|
| 731 |
|
---|
| 732 | ctx.forceBlockIndent = !!this.comment;
|
---|
| 733 | contentComment = this.contents.comment;
|
---|
| 734 | }
|
---|
| 735 |
|
---|
| 736 | const onChompKeep = contentComment ? null : () => chompKeep = true;
|
---|
| 737 | const body = stringify(this.contents, ctx, () => contentComment = null, onChompKeep);
|
---|
| 738 | lines.push(resolveSeq.addComment(body, '', contentComment));
|
---|
| 739 | } else if (this.contents !== undefined) {
|
---|
| 740 | lines.push(stringify(this.contents, ctx));
|
---|
| 741 | }
|
---|
| 742 |
|
---|
| 743 | if (this.comment) {
|
---|
| 744 | if ((!chompKeep || contentComment) && lines[lines.length - 1] !== '') lines.push('');
|
---|
| 745 | lines.push(this.comment.replace(/^/gm, '#'));
|
---|
| 746 | }
|
---|
| 747 |
|
---|
| 748 | return lines.join('\n') + '\n';
|
---|
| 749 | }
|
---|
| 750 |
|
---|
| 751 | }
|
---|
| 752 |
|
---|
| 753 | PlainValue._defineProperty(Document, "defaults", documentOptions);
|
---|
| 754 |
|
---|
| 755 | exports.Document = Document;
|
---|
| 756 | exports.defaultOptions = defaultOptions;
|
---|
| 757 | exports.scalarOptions = scalarOptions;
|
---|