merge.js 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. import { isScalar, isAlias, isSeq, isMap } from '../../nodes/identity.js';
  2. import { Scalar } from '../../nodes/Scalar.js';
  3. // If the value associated with a merge key is a single mapping node, each of
  4. // its key/value pairs is inserted into the current mapping, unless the key
  5. // already exists in it. If the value associated with the merge key is a
  6. // sequence, then this sequence is expected to contain mapping nodes and each
  7. // of these nodes is merged in turn according to its order in the sequence.
  8. // Keys in mapping nodes earlier in the sequence override keys specified in
  9. // later mapping nodes. -- http://yaml.org/type/merge.html
  10. const MERGE_KEY = '<<';
  11. const merge = {
  12. identify: value => value === MERGE_KEY ||
  13. (typeof value === 'symbol' && value.description === MERGE_KEY),
  14. default: 'key',
  15. tag: 'tag:yaml.org,2002:merge',
  16. test: /^<<$/,
  17. resolve: () => Object.assign(new Scalar(Symbol(MERGE_KEY)), {
  18. addToJSMap: addMergeToJSMap
  19. }),
  20. stringify: () => MERGE_KEY
  21. };
  22. const isMergeKey = (ctx, key) => (merge.identify(key) ||
  23. (isScalar(key) &&
  24. (!key.type || key.type === Scalar.PLAIN) &&
  25. merge.identify(key.value))) &&
  26. ctx?.doc.schema.tags.some(tag => tag.tag === merge.tag && tag.default);
  27. function addMergeToJSMap(ctx, map, value) {
  28. value = ctx && isAlias(value) ? value.resolve(ctx.doc) : value;
  29. if (isSeq(value))
  30. for (const it of value.items)
  31. mergeValue(ctx, map, it);
  32. else if (Array.isArray(value))
  33. for (const it of value)
  34. mergeValue(ctx, map, it);
  35. else
  36. mergeValue(ctx, map, value);
  37. }
  38. function mergeValue(ctx, map, value) {
  39. const source = ctx && isAlias(value) ? value.resolve(ctx.doc) : value;
  40. if (!isMap(source))
  41. throw new Error('Merge sources must be maps or map aliases');
  42. const srcMap = source.toJSON(null, ctx, Map);
  43. for (const [key, value] of srcMap) {
  44. if (map instanceof Map) {
  45. if (!map.has(key))
  46. map.set(key, value);
  47. }
  48. else if (map instanceof Set) {
  49. map.add(key);
  50. }
  51. else if (!Object.prototype.hasOwnProperty.call(map, key)) {
  52. Object.defineProperty(map, key, {
  53. value,
  54. writable: true,
  55. enumerable: true,
  56. configurable: true
  57. });
  58. }
  59. }
  60. return map;
  61. }
  62. export { addMergeToJSMap, isMergeKey, merge };