addPairToJSMap.js 3.8 KB

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