printDiffs.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.getStringDiff = exports.printMultilineStringDiffs = exports.createPatchMark = exports.printAnnotation = exports.hasCommonDiff = exports.computeStringDiffs = exports.printCommonLine = exports.printInsertLine = exports.printDeleteLine = exports.MULTILINE_REGEXP = exports.getReceivedString = exports.getExpectedString = exports.getHighlightedString = exports.RECEIVED_COLOR = exports.INVERTED_COLOR = exports.EXPECTED_COLOR = exports.DIM_COLOR = void 0;
  6. var _chalk = _interopRequireDefault(require('chalk'));
  7. var _cleanupSemantic = require('./cleanupSemantic');
  8. var _diffStrings = _interopRequireDefault(require('./diffStrings'));
  9. var _getAlignedDiffs = _interopRequireDefault(require('./getAlignedDiffs'));
  10. var _joinAlignedDiffs = require('./joinAlignedDiffs');
  11. function _interopRequireDefault(obj) {
  12. return obj && obj.__esModule ? obj : {default: obj};
  13. }
  14. /**
  15. * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
  16. *
  17. * This source code is licensed under the MIT license found in the
  18. * LICENSE file in the root directory of this source tree.
  19. */
  20. const DIM_COLOR = _chalk.default.dim;
  21. exports.DIM_COLOR = DIM_COLOR;
  22. const EXPECTED_COLOR = _chalk.default.green;
  23. exports.EXPECTED_COLOR = EXPECTED_COLOR;
  24. const INVERTED_COLOR = _chalk.default.inverse;
  25. exports.INVERTED_COLOR = INVERTED_COLOR;
  26. const RECEIVED_COLOR = _chalk.default.red;
  27. exports.RECEIVED_COLOR = RECEIVED_COLOR;
  28. const PATCH_COLOR = _chalk.default.yellow; // Given change op and array of diffs, return concatenated string:
  29. // * include common strings
  30. // * include change strings which have argument op (inverse highlight)
  31. // * exclude change strings which have opposite op
  32. const getHighlightedString = (op, diffs) =>
  33. diffs.reduce(
  34. (reduced, diff) =>
  35. reduced +
  36. (diff[0] === _cleanupSemantic.DIFF_EQUAL
  37. ? diff[1]
  38. : diff[0] === op
  39. ? INVERTED_COLOR(diff[1])
  40. : ''),
  41. ''
  42. );
  43. exports.getHighlightedString = getHighlightedString;
  44. const getExpectedString = diffs =>
  45. getHighlightedString(_cleanupSemantic.DIFF_DELETE, diffs);
  46. exports.getExpectedString = getExpectedString;
  47. const getReceivedString = diffs =>
  48. getHighlightedString(_cleanupSemantic.DIFF_INSERT, diffs);
  49. exports.getReceivedString = getReceivedString;
  50. const MULTILINE_REGEXP = /\n/;
  51. exports.MULTILINE_REGEXP = MULTILINE_REGEXP;
  52. const NEWLINE_SYMBOL = '\u{21B5}'; // downwards arrow with corner leftwards
  53. const SPACE_SYMBOL = '\u{00B7}'; // middle dot
  54. // Instead of inverse highlight which now implies a change,
  55. // replace common spaces with middle dot at the end of the line.
  56. const replaceSpacesAtEnd = line =>
  57. line.replace(/\s+$/, spaces => SPACE_SYMBOL.repeat(spaces.length));
  58. const printDeleteLine = line =>
  59. EXPECTED_COLOR(line.length !== 0 ? '- ' + replaceSpacesAtEnd(line) : '-');
  60. exports.printDeleteLine = printDeleteLine;
  61. const printInsertLine = line =>
  62. RECEIVED_COLOR(line.length !== 0 ? '+ ' + replaceSpacesAtEnd(line) : '+'); // Prevent visually ambiguous empty line as the first or the last.
  63. exports.printInsertLine = printInsertLine;
  64. const printCommonLine = (line, isFirstOrLast = false) =>
  65. line.length !== 0
  66. ? DIM_COLOR(' ' + replaceSpacesAtEnd(line))
  67. : isFirstOrLast
  68. ? DIM_COLOR(' ' + NEWLINE_SYMBOL)
  69. : '';
  70. exports.printCommonLine = printCommonLine;
  71. const computeStringDiffs = (expected, received) => {
  72. const isMultiline =
  73. MULTILINE_REGEXP.test(expected) || MULTILINE_REGEXP.test(received); // getAlignedDiffs assumes that a newline was appended to the strings.
  74. if (isMultiline) {
  75. expected += '\n';
  76. received += '\n';
  77. }
  78. const diffs = (0, _diffStrings.default)(expected, received);
  79. (0, _cleanupSemantic.cleanupSemantic)(diffs); // impure function
  80. return {
  81. diffs,
  82. isMultiline
  83. };
  84. };
  85. exports.computeStringDiffs = computeStringDiffs;
  86. const hasCommonDiff = (diffs, isMultiline) => {
  87. if (isMultiline) {
  88. // Important: Ignore common newline that was appended to multiline strings!
  89. const iLast = diffs.length - 1;
  90. return diffs.some(
  91. (diff, i) =>
  92. diff[0] === _cleanupSemantic.DIFF_EQUAL &&
  93. (i !== iLast || diff[1] !== '\n')
  94. );
  95. }
  96. return diffs.some(diff => diff[0] === _cleanupSemantic.DIFF_EQUAL);
  97. };
  98. exports.hasCommonDiff = hasCommonDiff;
  99. const printAnnotation = options =>
  100. EXPECTED_COLOR('- ' + ((options && options.aAnnotation) || 'Expected')) +
  101. '\n' +
  102. RECEIVED_COLOR('+ ' + ((options && options.bAnnotation) || 'Received')) +
  103. '\n\n'; // In GNU diff format, indexes are one-based instead of zero-based.
  104. exports.printAnnotation = printAnnotation;
  105. const createPatchMark = (aStart, aEnd, bStart, bEnd) =>
  106. PATCH_COLOR(
  107. `@@ -${aStart + 1},${aEnd - aStart} +${bStart + 1},${bEnd - bStart} @@`
  108. ); // Return formatted diff lines without labels.
  109. exports.createPatchMark = createPatchMark;
  110. const printMultilineStringDiffs = (diffs, expand) => {
  111. const lines = (0, _getAlignedDiffs.default)(diffs);
  112. return expand
  113. ? (0, _joinAlignedDiffs.joinAlignedDiffsExpand)(lines)
  114. : (0, _joinAlignedDiffs.joinAlignedDiffsNoExpand)(lines);
  115. };
  116. exports.printMultilineStringDiffs = printMultilineStringDiffs;
  117. const MAX_DIFF_STRING_LENGTH = 20000;
  118. // Print specific substring diff for strings only:
  119. // * if strings are not equal
  120. // * if neither string is empty
  121. // * if neither string is too long
  122. // * if there is a common string after semantic cleanup
  123. const getStringDiff = (expected, received, options) => {
  124. if (
  125. expected === received ||
  126. expected.length === 0 ||
  127. received.length === 0 ||
  128. expected.length > MAX_DIFF_STRING_LENGTH ||
  129. received.length > MAX_DIFF_STRING_LENGTH
  130. ) {
  131. return null;
  132. }
  133. const _computeStringDiffs = computeStringDiffs(expected, received),
  134. diffs = _computeStringDiffs.diffs,
  135. isMultiline = _computeStringDiffs.isMultiline;
  136. if (!hasCommonDiff(diffs, isMultiline)) {
  137. return null;
  138. }
  139. return isMultiline
  140. ? {
  141. annotatedDiff:
  142. printAnnotation(options) +
  143. printMultilineStringDiffs(
  144. diffs,
  145. options === undefined || options.expand !== false
  146. ),
  147. isMultiline
  148. }
  149. : {
  150. a: getExpectedString(diffs),
  151. b: getReceivedString(diffs),
  152. isMultiline
  153. };
  154. };
  155. exports.getStringDiff = getStringDiff;