stringify.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. 'use strict';
  2. var anchors = require('../doc/anchors.js');
  3. var identity = require('../nodes/identity.js');
  4. var stringifyComment = require('./stringifyComment.js');
  5. var stringifyString = require('./stringifyString.js');
  6. function createStringifyContext(doc, options) {
  7. const opt = Object.assign({
  8. blockQuote: true,
  9. commentString: stringifyComment.stringifyComment,
  10. defaultKeyType: null,
  11. defaultStringType: 'PLAIN',
  12. directives: null,
  13. doubleQuotedAsJSON: false,
  14. doubleQuotedMinMultiLineLength: 40,
  15. falseStr: 'false',
  16. flowCollectionPadding: true,
  17. indentSeq: true,
  18. lineWidth: 80,
  19. minContentWidth: 20,
  20. nullStr: 'null',
  21. simpleKeys: false,
  22. singleQuote: null,
  23. trueStr: 'true',
  24. verifyAliasOrder: true
  25. }, doc.schema.toStringOptions, options);
  26. let inFlow;
  27. switch (opt.collectionStyle) {
  28. case 'block':
  29. inFlow = false;
  30. break;
  31. case 'flow':
  32. inFlow = true;
  33. break;
  34. default:
  35. inFlow = null;
  36. }
  37. return {
  38. anchors: new Set(),
  39. doc,
  40. flowCollectionPadding: opt.flowCollectionPadding ? ' ' : '',
  41. indent: '',
  42. indentStep: typeof opt.indent === 'number' ? ' '.repeat(opt.indent) : ' ',
  43. inFlow,
  44. options: opt
  45. };
  46. }
  47. function getTagObject(tags, item) {
  48. if (item.tag) {
  49. const match = tags.filter(t => t.tag === item.tag);
  50. if (match.length > 0)
  51. return match.find(t => t.format === item.format) ?? match[0];
  52. }
  53. let tagObj = undefined;
  54. let obj;
  55. if (identity.isScalar(item)) {
  56. obj = item.value;
  57. let match = tags.filter(t => t.identify?.(obj));
  58. if (match.length > 1) {
  59. const testMatch = match.filter(t => t.test);
  60. if (testMatch.length > 0)
  61. match = testMatch;
  62. }
  63. tagObj =
  64. match.find(t => t.format === item.format) ?? match.find(t => !t.format);
  65. }
  66. else {
  67. obj = item;
  68. tagObj = tags.find(t => t.nodeClass && obj instanceof t.nodeClass);
  69. }
  70. if (!tagObj) {
  71. const name = obj?.constructor?.name ?? typeof obj;
  72. throw new Error(`Tag not resolved for ${name} value`);
  73. }
  74. return tagObj;
  75. }
  76. // needs to be called before value stringifier to allow for circular anchor refs
  77. function stringifyProps(node, tagObj, { anchors: anchors$1, doc }) {
  78. if (!doc.directives)
  79. return '';
  80. const props = [];
  81. const anchor = (identity.isScalar(node) || identity.isCollection(node)) && node.anchor;
  82. if (anchor && anchors.anchorIsValid(anchor)) {
  83. anchors$1.add(anchor);
  84. props.push(`&${anchor}`);
  85. }
  86. const tag = node.tag ? node.tag : tagObj.default ? null : tagObj.tag;
  87. if (tag)
  88. props.push(doc.directives.tagString(tag));
  89. return props.join(' ');
  90. }
  91. function stringify(item, ctx, onComment, onChompKeep) {
  92. if (identity.isPair(item))
  93. return item.toString(ctx, onComment, onChompKeep);
  94. if (identity.isAlias(item)) {
  95. if (ctx.doc.directives)
  96. return item.toString(ctx);
  97. if (ctx.resolvedAliases?.has(item)) {
  98. throw new TypeError(`Cannot stringify circular structure without alias nodes`);
  99. }
  100. else {
  101. if (ctx.resolvedAliases)
  102. ctx.resolvedAliases.add(item);
  103. else
  104. ctx.resolvedAliases = new Set([item]);
  105. item = item.resolve(ctx.doc);
  106. }
  107. }
  108. let tagObj = undefined;
  109. const node = identity.isNode(item)
  110. ? item
  111. : ctx.doc.createNode(item, { onTagObj: o => (tagObj = o) });
  112. if (!tagObj)
  113. tagObj = getTagObject(ctx.doc.schema.tags, node);
  114. const props = stringifyProps(node, tagObj, ctx);
  115. if (props.length > 0)
  116. ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1;
  117. const str = typeof tagObj.stringify === 'function'
  118. ? tagObj.stringify(node, ctx, onComment, onChompKeep)
  119. : identity.isScalar(node)
  120. ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep)
  121. : node.toString(ctx, onComment, onChompKeep);
  122. if (!props)
  123. return str;
  124. return identity.isScalar(node) || str[0] === '{' || str[0] === '['
  125. ? `${props} ${str}`
  126. : `${props}\n${ctx.indent}${str}`;
  127. }
  128. exports.createStringifyContext = createStringifyContext;
  129. exports.stringify = stringify;