merge.js 2.5 KB

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