stringifyPair.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import { isCollection, isNode, isScalar, isSeq } from '../nodes/identity.js';
  2. import { Scalar } from '../nodes/Scalar.js';
  3. import { stringify } from './stringify.js';
  4. import { lineComment, indentComment } from './stringifyComment.js';
  5. function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
  6. const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
  7. let keyComment = (isNode(key) && key.comment) || null;
  8. if (simpleKeys) {
  9. if (keyComment) {
  10. throw new Error('With simple keys, key nodes cannot have comments');
  11. }
  12. if (isCollection(key) || (!isNode(key) && typeof key === 'object')) {
  13. const msg = 'With simple keys, collection cannot be used as a key value';
  14. throw new Error(msg);
  15. }
  16. }
  17. let explicitKey = !simpleKeys &&
  18. (!key ||
  19. (keyComment && value == null && !ctx.inFlow) ||
  20. isCollection(key) ||
  21. (isScalar(key)
  22. ? key.type === Scalar.BLOCK_FOLDED || key.type === Scalar.BLOCK_LITERAL
  23. : typeof key === 'object'));
  24. ctx = Object.assign({}, ctx, {
  25. allNullValues: false,
  26. implicitKey: !explicitKey && (simpleKeys || !allNullValues),
  27. indent: indent + indentStep
  28. });
  29. let keyCommentDone = false;
  30. let chompKeep = false;
  31. let str = stringify(key, ctx, () => (keyCommentDone = true), () => (chompKeep = true));
  32. if (!explicitKey && !ctx.inFlow && str.length > 1024) {
  33. if (simpleKeys)
  34. throw new Error('With simple keys, single line scalar must not span more than 1024 characters');
  35. explicitKey = true;
  36. }
  37. if (ctx.inFlow) {
  38. if (allNullValues || value == null) {
  39. if (keyCommentDone && onComment)
  40. onComment();
  41. return str === '' ? '?' : explicitKey ? `? ${str}` : str;
  42. }
  43. }
  44. else if ((allNullValues && !simpleKeys) || (value == null && explicitKey)) {
  45. str = `? ${str}`;
  46. if (keyComment && !keyCommentDone) {
  47. str += lineComment(str, ctx.indent, commentString(keyComment));
  48. }
  49. else if (chompKeep && onChompKeep)
  50. onChompKeep();
  51. return str;
  52. }
  53. if (keyCommentDone)
  54. keyComment = null;
  55. if (explicitKey) {
  56. if (keyComment)
  57. str += lineComment(str, ctx.indent, commentString(keyComment));
  58. str = `? ${str}\n${indent}:`;
  59. }
  60. else {
  61. str = `${str}:`;
  62. if (keyComment)
  63. str += lineComment(str, ctx.indent, commentString(keyComment));
  64. }
  65. let vsb, vcb, valueComment;
  66. if (isNode(value)) {
  67. vsb = !!value.spaceBefore;
  68. vcb = value.commentBefore;
  69. valueComment = value.comment;
  70. }
  71. else {
  72. vsb = false;
  73. vcb = null;
  74. valueComment = null;
  75. if (value && typeof value === 'object')
  76. value = doc.createNode(value);
  77. }
  78. ctx.implicitKey = false;
  79. if (!explicitKey && !keyComment && isScalar(value))
  80. ctx.indentAtStart = str.length + 1;
  81. chompKeep = false;
  82. if (!indentSeq &&
  83. indentStep.length >= 2 &&
  84. !ctx.inFlow &&
  85. !explicitKey &&
  86. isSeq(value) &&
  87. !value.flow &&
  88. !value.tag &&
  89. !value.anchor) {
  90. // If indentSeq === false, consider '- ' as part of indentation where possible
  91. ctx.indent = ctx.indent.substring(2);
  92. }
  93. let valueCommentDone = false;
  94. const valueStr = stringify(value, ctx, () => (valueCommentDone = true), () => (chompKeep = true));
  95. let ws = ' ';
  96. if (keyComment || vsb || vcb) {
  97. ws = vsb ? '\n' : '';
  98. if (vcb) {
  99. const cs = commentString(vcb);
  100. ws += `\n${indentComment(cs, ctx.indent)}`;
  101. }
  102. if (valueStr === '' && !ctx.inFlow) {
  103. if (ws === '\n')
  104. ws = '\n\n';
  105. }
  106. else {
  107. ws += `\n${ctx.indent}`;
  108. }
  109. }
  110. else if (!explicitKey && isCollection(value)) {
  111. const vs0 = valueStr[0];
  112. const nl0 = valueStr.indexOf('\n');
  113. const hasNewline = nl0 !== -1;
  114. const flow = ctx.inFlow ?? value.flow ?? value.items.length === 0;
  115. if (hasNewline || !flow) {
  116. let hasPropsLine = false;
  117. if (hasNewline && (vs0 === '&' || vs0 === '!')) {
  118. let sp0 = valueStr.indexOf(' ');
  119. if (vs0 === '&' &&
  120. sp0 !== -1 &&
  121. sp0 < nl0 &&
  122. valueStr[sp0 + 1] === '!') {
  123. sp0 = valueStr.indexOf(' ', sp0 + 1);
  124. }
  125. if (sp0 === -1 || nl0 < sp0)
  126. hasPropsLine = true;
  127. }
  128. if (!hasPropsLine)
  129. ws = `\n${ctx.indent}`;
  130. }
  131. }
  132. else if (valueStr === '' || valueStr[0] === '\n') {
  133. ws = '';
  134. }
  135. str += ws + valueStr;
  136. if (ctx.inFlow) {
  137. if (valueCommentDone && onComment)
  138. onComment();
  139. }
  140. else if (valueComment && !valueCommentDone) {
  141. str += lineComment(str, ctx.indent, commentString(valueComment));
  142. }
  143. else if (chompKeep && onChompKeep) {
  144. onChompKeep();
  145. }
  146. return str;
  147. }
  148. export { stringifyPair };