stringifyPair.js 5.3 KB

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