compose-collection.js 3.4 KB

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