compose-scalar.js 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { isScalar, SCALAR } from '../nodes/identity.js';
  2. import { Scalar } from '../nodes/Scalar.js';
  3. import { resolveBlockScalar } from './resolve-block-scalar.js';
  4. import { resolveFlowScalar } from './resolve-flow-scalar.js';
  5. function composeScalar(ctx, token, tagToken, onError) {
  6. const { value, type, comment, range } = token.type === 'block-scalar'
  7. ? resolveBlockScalar(ctx, token, onError)
  8. : resolveFlowScalar(token, ctx.options.strict, onError);
  9. const tagName = tagToken
  10. ? ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg))
  11. : null;
  12. let tag;
  13. if (ctx.options.stringKeys && ctx.atKey) {
  14. tag = ctx.schema[SCALAR];
  15. }
  16. else if (tagName)
  17. tag = findScalarTagByName(ctx.schema, value, tagName, tagToken, onError);
  18. else if (token.type === 'scalar')
  19. tag = findScalarTagByTest(ctx, value, token, onError);
  20. else
  21. tag = ctx.schema[SCALAR];
  22. let scalar;
  23. try {
  24. const res = tag.resolve(value, msg => onError(tagToken ?? token, 'TAG_RESOLVE_FAILED', msg), ctx.options);
  25. scalar = isScalar(res) ? res : new Scalar(res);
  26. }
  27. catch (error) {
  28. const msg = error instanceof Error ? error.message : String(error);
  29. onError(tagToken ?? token, 'TAG_RESOLVE_FAILED', msg);
  30. scalar = new Scalar(value);
  31. }
  32. scalar.range = range;
  33. scalar.source = value;
  34. if (type)
  35. scalar.type = type;
  36. if (tagName)
  37. scalar.tag = tagName;
  38. if (tag.format)
  39. scalar.format = tag.format;
  40. if (comment)
  41. scalar.comment = comment;
  42. return scalar;
  43. }
  44. function findScalarTagByName(schema, value, tagName, tagToken, onError) {
  45. if (tagName === '!')
  46. return schema[SCALAR]; // non-specific tag
  47. const matchWithTest = [];
  48. for (const tag of schema.tags) {
  49. if (!tag.collection && tag.tag === tagName) {
  50. if (tag.default && tag.test)
  51. matchWithTest.push(tag);
  52. else
  53. return tag;
  54. }
  55. }
  56. for (const tag of matchWithTest)
  57. if (tag.test?.test(value))
  58. return tag;
  59. const kt = schema.knownTags[tagName];
  60. if (kt && !kt.collection) {
  61. // Ensure that the known tag is available for stringifying,
  62. // but does not get used by default.
  63. schema.tags.push(Object.assign({}, kt, { default: false, test: undefined }));
  64. return kt;
  65. }
  66. onError(tagToken, 'TAG_RESOLVE_FAILED', `Unresolved tag: ${tagName}`, tagName !== 'tag:yaml.org,2002:str');
  67. return schema[SCALAR];
  68. }
  69. function findScalarTagByTest({ atKey, directives, schema }, value, token, onError) {
  70. const tag = schema.tags.find(tag => (tag.default === true || (atKey && tag.default === 'key')) &&
  71. tag.test?.test(value)) || schema[SCALAR];
  72. if (schema.compat) {
  73. const compat = schema.compat.find(tag => tag.default && tag.test?.test(value)) ??
  74. schema[SCALAR];
  75. if (tag.tag !== compat.tag) {
  76. const ts = directives.tagString(tag.tag);
  77. const cs = directives.tagString(compat.tag);
  78. const msg = `Value may be parsed as either ${ts} or ${cs}`;
  79. onError(token, 'TAG_RESOLVE_FAILED', msg, true);
  80. }
  81. }
  82. return tag;
  83. }
  84. export { composeScalar };