'use strict'; var log = require('../log.js'); var stringify = require('../stringify/stringify.js'); var identity = require('./identity.js'); var Scalar = require('./Scalar.js'); var toJS = require('./toJS.js'); const MERGE_KEY = '<<'; function addPairToJSMap(ctx, map, { key, value }) { if (ctx?.doc.schema.merge && isMergeKey(key)) { value = identity.isAlias(value) ? value.resolve(ctx.doc) : value; if (identity.isSeq(value)) for (const it of value.items) mergeToJSMap(ctx, map, it); else if (Array.isArray(value)) for (const it of value) mergeToJSMap(ctx, map, it); else mergeToJSMap(ctx, map, value); } else { const jsKey = toJS.toJS(key, '', ctx); if (map instanceof Map) { map.set(jsKey, toJS.toJS(value, jsKey, ctx)); } else if (map instanceof Set) { map.add(jsKey); } else { const stringKey = stringifyKey(key, jsKey, ctx); const jsValue = toJS.toJS(value, stringKey, ctx); if (stringKey in map) Object.defineProperty(map, stringKey, { value: jsValue, writable: true, enumerable: true, configurable: true }); else map[stringKey] = jsValue; } } return map; } const isMergeKey = (key) => key === MERGE_KEY || (identity.isScalar(key) && key.value === MERGE_KEY && (!key.type || key.type === Scalar.Scalar.PLAIN)); // If the value associated with a merge key is a single mapping node, each of // its key/value pairs is inserted into the current mapping, unless the key // already exists in it. If the value associated with the merge key is a // sequence, then this sequence is expected to contain mapping nodes and each // of these nodes is merged in turn according to its order in the sequence. // Keys in mapping nodes earlier in the sequence override keys specified in // later mapping nodes. -- http://yaml.org/type/merge.html function mergeToJSMap(ctx, map, value) { const source = ctx && identity.isAlias(value) ? value.resolve(ctx.doc) : value; if (!identity.isMap(source)) throw new Error('Merge sources must be maps or map aliases'); const srcMap = source.toJSON(null, ctx, Map); for (const [key, value] of srcMap) { if (map instanceof Map) { if (!map.has(key)) map.set(key, value); } else if (map instanceof Set) { map.add(key); } else if (!Object.prototype.hasOwnProperty.call(map, key)) { Object.defineProperty(map, key, { value, writable: true, enumerable: true, configurable: true }); } } return map; } function stringifyKey(key, jsKey, ctx) { if (jsKey === null) return ''; if (typeof jsKey !== 'object') return String(jsKey); if (identity.isNode(key) && ctx?.doc) { const strCtx = stringify.createStringifyContext(ctx.doc, {}); strCtx.anchors = new Set(); for (const node of ctx.anchors.keys()) strCtx.anchors.add(node.anchor); strCtx.inFlow = true; strCtx.inStringifyKey = true; const strKey = key.toString(strCtx); if (!ctx.mapKeyWarned) { let jsonStr = JSON.stringify(strKey); if (jsonStr.length > 40) jsonStr = jsonStr.substring(0, 36) + '..."'; log.warn(ctx.doc.options.logLevel, `Keys with collection values will be stringified due to JS Object restrictions: ${jsonStr}. Set mapAsMap: true to use object keys.`); ctx.mapKeyWarned = true; } return strKey; } return JSON.stringify(jsKey); } exports.addPairToJSMap = addPairToJSMap;