compose-collection.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. 'use strict';
  2. var identity = require('../nodes/identity.js');
  3. var Scalar = require('../nodes/Scalar.js');
  4. var YAMLMap = require('../nodes/YAMLMap.js');
  5. var YAMLSeq = require('../nodes/YAMLSeq.js');
  6. var resolveBlockMap = require('./resolve-block-map.js');
  7. var resolveBlockSeq = require('./resolve-block-seq.js');
  8. var resolveFlowCollection = require('./resolve-flow-collection.js');
  9. function resolveCollection(CN, ctx, token, onError, tagName, tag) {
  10. const coll = token.type === 'block-map'
  11. ? resolveBlockMap.resolveBlockMap(CN, ctx, token, onError, tag)
  12. : token.type === 'block-seq'
  13. ? resolveBlockSeq.resolveBlockSeq(CN, ctx, token, onError, tag)
  14. : resolveFlowCollection.resolveFlowCollection(CN, ctx, token, onError, tag);
  15. const Coll = coll.constructor;
  16. // If we got a tagName matching the class, or the tag name is '!',
  17. // then use the tagName from the node class used to create it.
  18. if (tagName === '!' || tagName === Coll.tagName) {
  19. coll.tag = Coll.tagName;
  20. return coll;
  21. }
  22. if (tagName)
  23. coll.tag = tagName;
  24. return coll;
  25. }
  26. function composeCollection(CN, ctx, token, props, onError) {
  27. const tagToken = props.tag;
  28. const tagName = !tagToken
  29. ? null
  30. : ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg));
  31. if (token.type === 'block-seq') {
  32. const { anchor, newlineAfterProp: nl } = props;
  33. const lastProp = anchor && tagToken
  34. ? anchor.offset > tagToken.offset
  35. ? anchor
  36. : tagToken
  37. : (anchor ?? tagToken);
  38. if (lastProp && (!nl || nl.offset < lastProp.offset)) {
  39. const message = 'Missing newline after block sequence props';
  40. onError(lastProp, 'MISSING_CHAR', message);
  41. }
  42. }
  43. const expType = token.type === 'block-map'
  44. ? 'map'
  45. : token.type === 'block-seq'
  46. ? 'seq'
  47. : token.start.source === '{'
  48. ? 'map'
  49. : 'seq';
  50. // shortcut: check if it's a generic YAMLMap or YAMLSeq
  51. // before jumping into the custom tag logic.
  52. if (!tagToken ||
  53. !tagName ||
  54. tagName === '!' ||
  55. (tagName === YAMLMap.YAMLMap.tagName && expType === 'map') ||
  56. (tagName === YAMLSeq.YAMLSeq.tagName && expType === 'seq')) {
  57. return resolveCollection(CN, ctx, token, onError, tagName);
  58. }
  59. let tag = ctx.schema.tags.find(t => t.tag === tagName && t.collection === expType);
  60. if (!tag) {
  61. const kt = ctx.schema.knownTags[tagName];
  62. if (kt && kt.collection === expType) {
  63. ctx.schema.tags.push(Object.assign({}, kt, { default: false }));
  64. tag = kt;
  65. }
  66. else {
  67. if (kt?.collection) {
  68. onError(tagToken, 'BAD_COLLECTION_TYPE', `${kt.tag} used for ${expType} collection, but expects ${kt.collection}`, true);
  69. }
  70. else {
  71. onError(tagToken, 'TAG_RESOLVE_FAILED', `Unresolved tag: ${tagName}`, true);
  72. }
  73. return resolveCollection(CN, ctx, token, onError, tagName);
  74. }
  75. }
  76. const coll = resolveCollection(CN, ctx, token, onError, tagName, tag);
  77. const res = tag.resolve?.(coll, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg), ctx.options) ?? coll;
  78. const node = identity.isNode(res)
  79. ? res
  80. : new Scalar.Scalar(res);
  81. node.range = coll.range;
  82. node.tag = tagName;
  83. if (tag?.format)
  84. node.format = tag.format;
  85. return node;
  86. }
  87. exports.composeCollection = composeCollection;