123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- import { createNode } from '../doc/createNode.js';
- import { isNode, isPair, isCollection, isScalar } from './identity.js';
- import { NodeBase } from './Node.js';
- function collectionFromPath(schema, path, value) {
- let v = value;
- for (let i = path.length - 1; i >= 0; --i) {
- const k = path[i];
- if (typeof k === 'number' && Number.isInteger(k) && k >= 0) {
- const a = [];
- a[k] = v;
- v = a;
- }
- else {
- v = new Map([[k, v]]);
- }
- }
- return createNode(v, undefined, {
- aliasDuplicateObjects: false,
- keepUndefined: false,
- onAnchor: () => {
- throw new Error('This should not happen, please report a bug.');
- },
- schema,
- sourceObjects: new Map()
- });
- }
- // Type guard is intentionally a little wrong so as to be more useful,
- // as it does not cover untypable empty non-string iterables (e.g. []).
- const isEmptyPath = (path) => path == null ||
- (typeof path === 'object' && !!path[Symbol.iterator]().next().done);
- class Collection extends NodeBase {
- constructor(type, schema) {
- super(type);
- Object.defineProperty(this, 'schema', {
- value: schema,
- configurable: true,
- enumerable: false,
- writable: true
- });
- }
- /**
- * Create a copy of this collection.
- *
- * @param schema - If defined, overwrites the original's schema
- */
- clone(schema) {
- const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this));
- if (schema)
- copy.schema = schema;
- copy.items = copy.items.map(it => isNode(it) || isPair(it) ? it.clone(schema) : it);
- if (this.range)
- copy.range = this.range.slice();
- return copy;
- }
- /**
- * Adds a value to the collection. For `!!map` and `!!omap` the value must
- * be a Pair instance or a `{ key, value }` object, which may not have a key
- * that already exists in the map.
- */
- addIn(path, value) {
- if (isEmptyPath(path))
- this.add(value);
- else {
- const [key, ...rest] = path;
- const node = this.get(key, true);
- if (isCollection(node))
- node.addIn(rest, value);
- else if (node === undefined && this.schema)
- this.set(key, collectionFromPath(this.schema, rest, value));
- else
- throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
- }
- }
- /**
- * Removes a value from the collection.
- * @returns `true` if the item was found and removed.
- */
- deleteIn(path) {
- const [key, ...rest] = path;
- if (rest.length === 0)
- return this.delete(key);
- const node = this.get(key, true);
- if (isCollection(node))
- return node.deleteIn(rest);
- else
- throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
- }
- /**
- * Returns item at `key`, or `undefined` if not found. By default unwraps
- * scalar values from their surrounding node; to disable set `keepScalar` to
- * `true` (collections are always returned intact).
- */
- getIn(path, keepScalar) {
- const [key, ...rest] = path;
- const node = this.get(key, true);
- if (rest.length === 0)
- return !keepScalar && isScalar(node) ? node.value : node;
- else
- return isCollection(node) ? node.getIn(rest, keepScalar) : undefined;
- }
- hasAllNullValues(allowScalar) {
- return this.items.every(node => {
- if (!isPair(node))
- return false;
- const n = node.value;
- return (n == null ||
- (allowScalar &&
- isScalar(n) &&
- n.value == null &&
- !n.commentBefore &&
- !n.comment &&
- !n.tag));
- });
- }
- /**
- * Checks if the collection includes a value with the key `key`.
- */
- hasIn(path) {
- const [key, ...rest] = path;
- if (rest.length === 0)
- return this.has(key);
- const node = this.get(key, true);
- return isCollection(node) ? node.hasIn(rest) : false;
- }
- /**
- * Sets a value in this collection. For `!!set`, `value` needs to be a
- * boolean to add/remove the item from the set.
- */
- setIn(path, value) {
- const [key, ...rest] = path;
- if (rest.length === 0) {
- this.set(key, value);
- }
- else {
- const node = this.get(key, true);
- if (isCollection(node))
- node.setIn(rest, value);
- else if (node === undefined && this.schema)
- this.set(key, collectionFromPath(this.schema, rest, value));
- else
- throw new Error(`Expected YAML collection at ${key}. Remaining path: ${rest}`);
- }
- }
- }
- export { Collection, collectionFromPath, isEmptyPath };
|