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;
|
---|