UNPKG

5.22 kBJavaScriptView Raw
1'use strict';
2
3var createNode = require('../doc/createNode.js');
4var identity = require('./identity.js');
5var Node = require('./Node.js');
6
7function collectionFromPath(schema, path, value) {
8 let v = value;
9 for (let i = path.length - 1; i >= 0; --i) {
10 const k = path[i];
11 if (typeof k === 'number' && Number.isInteger(k) && k >= 0) {
12 const a = [];
13 a[k] = v;
14 v = a;
15 }
16 else {
17 v = new Map([[k, v]]);
18 }
19 }
20 return createNode.createNode(v, undefined, {
21 aliasDuplicateObjects: false,
22 keepUndefined: false,
23 onAnchor: () => {
24 throw new Error('This should not happen, please report a bug.');
25 },
26 schema,
27 sourceObjects: new Map()
28 });
29}
30// Type guard is intentionally a little wrong so as to be more useful,
31// as it does not cover untypable empty non-string iterables (e.g. []).
32const isEmptyPath = (path) => path == null ||
33 (typeof path === 'object' && !!path[Symbol.iterator]().next().done);
34class Collection extends Node.NodeBase {
35 constructor(type, schema) {
36 super(type);
37 Object.defineProperty(this, 'schema', {
38 value: schema,
39 configurable: true,
40 enumerable: false,
41 writable: true
42 });
43 }
44 /**
45 * Create a copy of this collection.
46 *
47 * @param schema - If defined, overwrites the original's schema
48 */
49 clone(schema) {
50 const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this));
51 if (schema)
52 copy.schema = schema;
53 copy.items = copy.items.map(it => identity.isNode(it) || identity.isPair(it) ? it.clone(schema) : it);
54 if (this.range)
55 copy.range = this.range.slice();
56 return copy;
57 }
58 /**
59 * Adds a value to the collection. For `!!map` and `!!omap` the value must
60 * be a Pair instance or a `{ key, value }` object, which may not have a key
61 * that already exists in the map.
62 */
63 addIn(path, value) {
64 if (isEmptyPath(path))
65 this.add(value);
66 else {
67 const [key, ...rest] = path;
68 const node = this.get(key, true);
69 if (identity.isCollection(node))
70 node.addIn(rest, value);
71 else if (node === undefined && this.schema)
72 this.set(key, collectionFromPath(this.schema, rest, value));
73 else
74 throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
75 }
76 }
77 /**
78 * Removes a value from the collection.
79 * @returns `true` if the item was found and removed.
80 */
81 deleteIn(path) {
82 const [key, ...rest] = path;
83 if (rest.length === 0)
84 return this.delete(key);
85 const node = this.get(key, true);
86 if (identity.isCollection(node))
87 return node.deleteIn(rest);
88 else
89 throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
90 }
91 /**
92 * Returns item at `key`, or `undefined` if not found. By default unwraps
93 * scalar values from their surrounding node; to disable set `keepScalar` to
94 * `true` (collections are always returned intact).
95 */
96 getIn(path, keepScalar) {
97 const [key, ...rest] = path;
98 const node = this.get(key, true);
99 if (rest.length === 0)
100 return !keepScalar && identity.isScalar(node) ? node.value : node;
101 else
102 return identity.isCollection(node) ? node.getIn(rest, keepScalar) : undefined;
103 }
104 hasAllNullValues(allowScalar) {
105 return this.items.every(node => {
106 if (!identity.isPair(node))
107 return false;
108 const n = node.value;
109 return (n == null ||
110 (allowScalar &&
111 identity.isScalar(n) &&
112 n.value == null &&
113 !n.commentBefore &&
114 !n.comment &&
115 !n.tag));
116 });
117 }
118 /**
119 * Checks if the collection includes a value with the key `key`.
120 */
121 hasIn(path) {
122 const [key, ...rest] = path;
123 if (rest.length === 0)
124 return this.has(key);
125 const node = this.get(key, true);
126 return identity.isCollection(node) ? node.hasIn(rest) : false;
127 }
128 /**
129 * Sets a value in this collection. For `!!set`, `value` needs to be a
130 * boolean to add/remove the item from the set.
131 */
132 setIn(path, value) {
133 const [key, ...rest] = path;
134 if (rest.length === 0) {
135 this.set(key, value);
136 }
137 else {
138 const node = this.get(key, true);
139 if (identity.isCollection(node))
140 node.setIn(rest, value);
141 else if (node === undefined && this.schema)
142 this.set(key, collectionFromPath(this.schema, rest, value));
143 else
144 throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
145 }
146 }
147}
148
149exports.Collection = Collection;
150exports.collectionFromPath = collectionFromPath;
151exports.isEmptyPath = isEmptyPath;