create.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. "use strict";
  2. var __assign = (this && this.__assign) || function () {
  3. __assign = Object.assign || function(t) {
  4. for (var s, i = 1, n = arguments.length; i < n; i++) {
  5. s = arguments[i];
  6. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
  7. t[p] = s[p];
  8. }
  9. return t;
  10. };
  11. return __assign.apply(this, arguments);
  12. };
  13. Object.defineProperty(exports, "__esModule", { value: true });
  14. exports.structuredPatch = structuredPatch;
  15. exports.formatPatch = formatPatch;
  16. exports.createTwoFilesPatch = createTwoFilesPatch;
  17. exports.createPatch = createPatch;
  18. var line_js_1 = require("../diff/line.js");
  19. function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
  20. var optionsObj;
  21. if (!options) {
  22. optionsObj = {};
  23. }
  24. else if (typeof options === 'function') {
  25. optionsObj = { callback: options };
  26. }
  27. else {
  28. optionsObj = options;
  29. }
  30. if (typeof optionsObj.context === 'undefined') {
  31. optionsObj.context = 4;
  32. }
  33. // We copy this into its own variable to placate TypeScript, which thinks
  34. // optionsObj.context might be undefined in the callbacks below.
  35. var context = optionsObj.context;
  36. // @ts-expect-error (runtime check for something that is correctly a static type error)
  37. if (optionsObj.newlineIsToken) {
  38. throw new Error('newlineIsToken may not be used with patch-generation functions, only with diffing functions');
  39. }
  40. if (!optionsObj.callback) {
  41. return diffLinesResultToPatch((0, line_js_1.diffLines)(oldStr, newStr, optionsObj));
  42. }
  43. else {
  44. var callback_1 = optionsObj.callback;
  45. (0, line_js_1.diffLines)(oldStr, newStr, __assign(__assign({}, optionsObj), { callback: function (diff) {
  46. var patch = diffLinesResultToPatch(diff);
  47. // TypeScript is unhappy without the cast because it does not understand that `patch` may
  48. // be undefined here only if `callback` is StructuredPatchCallbackAbortable:
  49. callback_1(patch);
  50. } }));
  51. }
  52. function diffLinesResultToPatch(diff) {
  53. // STEP 1: Build up the patch with no "\ No newline at end of file" lines and with the arrays
  54. // of lines containing trailing newline characters. We'll tidy up later...
  55. if (!diff) {
  56. return;
  57. }
  58. diff.push({ value: '', lines: [] }); // Append an empty value to make cleanup easier
  59. function contextLines(lines) {
  60. return lines.map(function (entry) { return ' ' + entry; });
  61. }
  62. var hunks = [];
  63. var oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
  64. for (var i = 0; i < diff.length; i++) {
  65. var current = diff[i], lines = current.lines || splitLines(current.value);
  66. current.lines = lines;
  67. if (current.added || current.removed) {
  68. // If we have previous context, start with that
  69. if (!oldRangeStart) {
  70. var prev = diff[i - 1];
  71. oldRangeStart = oldLine;
  72. newRangeStart = newLine;
  73. if (prev) {
  74. curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
  75. oldRangeStart -= curRange.length;
  76. newRangeStart -= curRange.length;
  77. }
  78. }
  79. // Output our changes
  80. for (var _i = 0, lines_1 = lines; _i < lines_1.length; _i++) {
  81. var line = lines_1[_i];
  82. curRange.push((current.added ? '+' : '-') + line);
  83. }
  84. // Track the updated file position
  85. if (current.added) {
  86. newLine += lines.length;
  87. }
  88. else {
  89. oldLine += lines.length;
  90. }
  91. }
  92. else {
  93. // Identical context lines. Track line changes
  94. if (oldRangeStart) {
  95. // Close out any changes that have been output (or join overlapping)
  96. if (lines.length <= context * 2 && i < diff.length - 2) {
  97. // Overlapping
  98. for (var _a = 0, _b = contextLines(lines); _a < _b.length; _a++) {
  99. var line = _b[_a];
  100. curRange.push(line);
  101. }
  102. }
  103. else {
  104. // end the range and output
  105. var contextSize = Math.min(lines.length, context);
  106. for (var _c = 0, _d = contextLines(lines.slice(0, contextSize)); _c < _d.length; _c++) {
  107. var line = _d[_c];
  108. curRange.push(line);
  109. }
  110. var hunk = {
  111. oldStart: oldRangeStart,
  112. oldLines: (oldLine - oldRangeStart + contextSize),
  113. newStart: newRangeStart,
  114. newLines: (newLine - newRangeStart + contextSize),
  115. lines: curRange
  116. };
  117. hunks.push(hunk);
  118. oldRangeStart = 0;
  119. newRangeStart = 0;
  120. curRange = [];
  121. }
  122. }
  123. oldLine += lines.length;
  124. newLine += lines.length;
  125. }
  126. }
  127. // Step 2: eliminate the trailing `\n` from each line of each hunk, and, where needed, add
  128. // "\ No newline at end of file".
  129. for (var _e = 0, hunks_1 = hunks; _e < hunks_1.length; _e++) {
  130. var hunk = hunks_1[_e];
  131. for (var i = 0; i < hunk.lines.length; i++) {
  132. if (hunk.lines[i].endsWith('\n')) {
  133. hunk.lines[i] = hunk.lines[i].slice(0, -1);
  134. }
  135. else {
  136. hunk.lines.splice(i + 1, 0, '\\ No newline at end of file');
  137. i++; // Skip the line we just added, then continue iterating
  138. }
  139. }
  140. }
  141. return {
  142. oldFileName: oldFileName, newFileName: newFileName,
  143. oldHeader: oldHeader, newHeader: newHeader,
  144. hunks: hunks
  145. };
  146. }
  147. }
  148. /**
  149. * creates a unified diff patch.
  150. * @param patch either a single structured patch object (as returned by `structuredPatch`) or an array of them (as returned by `parsePatch`)
  151. */
  152. function formatPatch(patch) {
  153. if (Array.isArray(patch)) {
  154. return patch.map(formatPatch).join('\n');
  155. }
  156. var ret = [];
  157. if (patch.oldFileName == patch.newFileName) {
  158. ret.push('Index: ' + patch.oldFileName);
  159. }
  160. ret.push('===================================================================');
  161. ret.push('--- ' + patch.oldFileName + (typeof patch.oldHeader === 'undefined' ? '' : '\t' + patch.oldHeader));
  162. ret.push('+++ ' + patch.newFileName + (typeof patch.newHeader === 'undefined' ? '' : '\t' + patch.newHeader));
  163. for (var i = 0; i < patch.hunks.length; i++) {
  164. var hunk = patch.hunks[i];
  165. // Unified Diff Format quirk: If the chunk size is 0,
  166. // the first number is one lower than one would expect.
  167. // https://www.artima.com/weblogs/viewpost.jsp?thread=164293
  168. if (hunk.oldLines === 0) {
  169. hunk.oldStart -= 1;
  170. }
  171. if (hunk.newLines === 0) {
  172. hunk.newStart -= 1;
  173. }
  174. ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines
  175. + ' +' + hunk.newStart + ',' + hunk.newLines
  176. + ' @@');
  177. for (var _i = 0, _a = hunk.lines; _i < _a.length; _i++) {
  178. var line = _a[_i];
  179. ret.push(line);
  180. }
  181. }
  182. return ret.join('\n') + '\n';
  183. }
  184. function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
  185. if (typeof options === 'function') {
  186. options = { callback: options };
  187. }
  188. if (!(options === null || options === void 0 ? void 0 : options.callback)) {
  189. var patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
  190. if (!patchObj) {
  191. return;
  192. }
  193. return formatPatch(patchObj);
  194. }
  195. else {
  196. var callback_2 = options.callback;
  197. structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, __assign(__assign({}, options), { callback: function (patchObj) {
  198. if (!patchObj) {
  199. callback_2(undefined);
  200. }
  201. else {
  202. callback_2(formatPatch(patchObj));
  203. }
  204. } }));
  205. }
  206. }
  207. function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) {
  208. return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options);
  209. }
  210. /**
  211. * Split `text` into an array of lines, including the trailing newline character (where present)
  212. */
  213. function splitLines(text) {
  214. var hasTrailingNl = text.endsWith('\n');
  215. var result = text.split('\n').map(function (line) { return line + '\n'; });
  216. if (hasTrailingNl) {
  217. result.pop();
  218. }
  219. else {
  220. result.push(result.pop().slice(0, -1));
  221. }
  222. return result;
  223. }