compose-scalar.js 3.4 KB

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