blockString.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true,
  4. });
  5. exports.dedentBlockStringLines = dedentBlockStringLines;
  6. exports.isPrintableAsBlockString = isPrintableAsBlockString;
  7. exports.printBlockString = printBlockString;
  8. var _characterClasses = require('./characterClasses.js');
  9. /**
  10. * Produces the value of a block string from its parsed raw value, similar to
  11. * CoffeeScript's block string, Python's docstring trim or Ruby's strip_heredoc.
  12. *
  13. * This implements the GraphQL spec's BlockStringValue() static algorithm.
  14. *
  15. * @internal
  16. */
  17. function dedentBlockStringLines(lines) {
  18. var _firstNonEmptyLine2;
  19. let commonIndent = Number.MAX_SAFE_INTEGER;
  20. let firstNonEmptyLine = null;
  21. let lastNonEmptyLine = -1;
  22. for (let i = 0; i < lines.length; ++i) {
  23. var _firstNonEmptyLine;
  24. const line = lines[i];
  25. const indent = leadingWhitespace(line);
  26. if (indent === line.length) {
  27. continue; // skip empty lines
  28. }
  29. firstNonEmptyLine =
  30. (_firstNonEmptyLine = firstNonEmptyLine) !== null &&
  31. _firstNonEmptyLine !== void 0
  32. ? _firstNonEmptyLine
  33. : i;
  34. lastNonEmptyLine = i;
  35. if (i !== 0 && indent < commonIndent) {
  36. commonIndent = indent;
  37. }
  38. }
  39. return lines // Remove common indentation from all lines but first.
  40. .map((line, i) => (i === 0 ? line : line.slice(commonIndent))) // Remove leading and trailing blank lines.
  41. .slice(
  42. (_firstNonEmptyLine2 = firstNonEmptyLine) !== null &&
  43. _firstNonEmptyLine2 !== void 0
  44. ? _firstNonEmptyLine2
  45. : 0,
  46. lastNonEmptyLine + 1,
  47. );
  48. }
  49. function leadingWhitespace(str) {
  50. let i = 0;
  51. while (
  52. i < str.length &&
  53. (0, _characterClasses.isWhiteSpace)(str.charCodeAt(i))
  54. ) {
  55. ++i;
  56. }
  57. return i;
  58. }
  59. /**
  60. * @internal
  61. */
  62. function isPrintableAsBlockString(value) {
  63. if (value === '') {
  64. return true; // empty string is printable
  65. }
  66. let isEmptyLine = true;
  67. let hasIndent = false;
  68. let hasCommonIndent = true;
  69. let seenNonEmptyLine = false;
  70. for (let i = 0; i < value.length; ++i) {
  71. switch (value.codePointAt(i)) {
  72. case 0x0000:
  73. case 0x0001:
  74. case 0x0002:
  75. case 0x0003:
  76. case 0x0004:
  77. case 0x0005:
  78. case 0x0006:
  79. case 0x0007:
  80. case 0x0008:
  81. case 0x000b:
  82. case 0x000c:
  83. case 0x000e:
  84. case 0x000f:
  85. return false;
  86. // Has non-printable characters
  87. case 0x000d:
  88. // \r
  89. return false;
  90. // Has \r or \r\n which will be replaced as \n
  91. case 10:
  92. // \n
  93. if (isEmptyLine && !seenNonEmptyLine) {
  94. return false; // Has leading new line
  95. }
  96. seenNonEmptyLine = true;
  97. isEmptyLine = true;
  98. hasIndent = false;
  99. break;
  100. case 9: // \t
  101. case 32:
  102. // <space>
  103. hasIndent || (hasIndent = isEmptyLine);
  104. break;
  105. default:
  106. hasCommonIndent && (hasCommonIndent = hasIndent);
  107. isEmptyLine = false;
  108. }
  109. }
  110. if (isEmptyLine) {
  111. return false; // Has trailing empty lines
  112. }
  113. if (hasCommonIndent && seenNonEmptyLine) {
  114. return false; // Has internal indent
  115. }
  116. return true;
  117. }
  118. /**
  119. * Print a block string in the indented block form by adding a leading and
  120. * trailing blank line. However, if a block string starts with whitespace and is
  121. * a single-line, adding a leading blank line would strip that whitespace.
  122. *
  123. * @internal
  124. */
  125. function printBlockString(value, options) {
  126. const escapedValue = value.replace(/"""/g, '\\"""'); // Expand a block string's raw value into independent lines.
  127. const lines = escapedValue.split(/\r\n|[\n\r]/g);
  128. const isSingleLine = lines.length === 1; // If common indentation is found we can fix some of those cases by adding leading new line
  129. const forceLeadingNewLine =
  130. lines.length > 1 &&
  131. lines
  132. .slice(1)
  133. .every(
  134. (line) =>
  135. line.length === 0 ||
  136. (0, _characterClasses.isWhiteSpace)(line.charCodeAt(0)),
  137. ); // Trailing triple quotes just looks confusing but doesn't force trailing new line
  138. const hasTrailingTripleQuotes = escapedValue.endsWith('\\"""'); // Trailing quote (single or double) or slash forces trailing new line
  139. const hasTrailingQuote = value.endsWith('"') && !hasTrailingTripleQuotes;
  140. const hasTrailingSlash = value.endsWith('\\');
  141. const forceTrailingNewline = hasTrailingQuote || hasTrailingSlash;
  142. const printAsMultipleLines =
  143. !(options !== null && options !== void 0 && options.minimize) && // add leading and trailing new lines only if it improves readability
  144. (!isSingleLine ||
  145. value.length > 70 ||
  146. forceTrailingNewline ||
  147. forceLeadingNewLine ||
  148. hasTrailingTripleQuotes);
  149. let result = ''; // Format a multi-line block quote to account for leading space.
  150. const skipLeadingNewLine =
  151. isSingleLine && (0, _characterClasses.isWhiteSpace)(value.charCodeAt(0));
  152. if ((printAsMultipleLines && !skipLeadingNewLine) || forceLeadingNewLine) {
  153. result += '\n';
  154. }
  155. result += escapedValue;
  156. if (printAsMultipleLines || forceTrailingNewline) {
  157. result += '\n';
  158. }
  159. return '"""' + result + '"""';
  160. }