addPairToJSMap.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. 'use strict';
  2. var log = require('../log.js');
  3. var stringify = require('../stringify/stringify.js');
  4. var identity = require('./identity.js');
  5. var Scalar = require('./Scalar.js');
  6. var toJS = require('./toJS.js');
  7. const MERGE_KEY = '<<';
  8. function addPairToJSMap(ctx, map, { key, value }) {
  9. if (ctx?.doc.schema.merge && isMergeKey(key)) {
  10. value = identity.isAlias(value) ? value.resolve(ctx.doc) : value;
  11. if (identity.isSeq(value))
  12. for (const it of value.items)
  13. mergeToJSMap(ctx, map, it);
  14. else if (Array.isArray(value))
  15. for (const it of value)
  16. mergeToJSMap(ctx, map, it);
  17. else
  18. mergeToJSMap(ctx, map, value);
  19. }
  20. else {
  21. const jsKey = toJS.toJS(key, '', ctx);
  22. if (map instanceof Map) {
  23. map.set(jsKey, toJS.toJS(value, jsKey, ctx));
  24. }
  25. else if (map instanceof Set) {
  26. map.add(jsKey);
  27. }
  28. else {
  29. const stringKey = stringifyKey(key, jsKey, ctx);
  30. const jsValue = toJS.toJS(value, stringKey, ctx);
  31. if (stringKey in map)
  32. Object.defineProperty(map, stringKey, {
  33. value: jsValue,
  34. writable: true,
  35. enumerable: true,
  36. configurable: true
  37. });
  38. else
  39. map[stringKey] = jsValue;
  40. }
  41. }
  42. return map;
  43. }
  44. const isMergeKey = (key) => key === MERGE_KEY ||
  45. (identity.isScalar(key) &&
  46. key.value === MERGE_KEY &&
  47. (!key.type || key.type === Scalar.Scalar.PLAIN));
  48. // If the value associated with a merge key is a single mapping node, each of
  49. // its key/value pairs is inserted into the current mapping, unless the key
  50. // already exists in it. If the value associated with the merge key is a
  51. // sequence, then this sequence is expected to contain mapping nodes and each
  52. // of these nodes is merged in turn according to its order in the sequence.
  53. // Keys in mapping nodes earlier in the sequence override keys specified in
  54. // later mapping nodes. -- http://yaml.org/type/merge.html
  55. function mergeToJSMap(ctx, map, value) {
  56. const source = ctx && identity.isAlias(value) ? value.resolve(ctx.doc) : value;
  57. if (!identity.isMap(source))
  58. throw new Error('Merge sources must be maps or map aliases');
  59. const srcMap = source.toJSON(null, ctx, Map);
  60. for (const [key, value] of srcMap) {
  61. if (map instanceof Map) {
  62. if (!map.has(key))
  63. map.set(key, value);
  64. }
  65. else if (map instanceof Set) {
  66. map.add(key);
  67. }
  68. else if (!Object.prototype.hasOwnProperty.call(map, key)) {
  69. Object.defineProperty(map, key, {
  70. value,
  71. writable: true,
  72. enumerable: true,
  73. configurable: true
  74. });
  75. }
  76. }
  77. return map;
  78. }
  79. function stringifyKey(key, jsKey, ctx) {
  80. if (jsKey === null)
  81. return '';
  82. if (typeof jsKey !== 'object')
  83. return String(jsKey);
  84. if (identity.isNode(key) && ctx && ctx.doc) {
  85. const strCtx = stringify.createStringifyContext(ctx.doc, {});
  86. strCtx.anchors = new Set();
  87. for (const node of ctx.anchors.keys())
  88. strCtx.anchors.add(node.anchor);
  89. strCtx.inFlow = true;
  90. strCtx.inStringifyKey = true;
  91. const strKey = key.toString(strCtx);
  92. if (!ctx.mapKeyWarned) {
  93. let jsonStr = JSON.stringify(strKey);
  94. if (jsonStr.length > 40)
  95. jsonStr = jsonStr.substring(0, 36) + '..."';
  96. 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.`);
  97. ctx.mapKeyWarned = true;
  98. }
  99. return strKey;
  100. }
  101. return JSON.stringify(jsKey);
  102. }
  103. exports.addPairToJSMap = addPairToJSMap;