index.js 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560
  1. /*!
  2. * /**
  3. * * Copyright (c) Meta Platforms, Inc. and affiliates.
  4. * *
  5. * * This source code is licensed under the MIT license found in the
  6. * * LICENSE file in the root directory of this source tree.
  7. * * /
  8. */
  9. /******/ (() => { // webpackBootstrap
  10. /******/ "use strict";
  11. /******/ var __webpack_modules__ = ({
  12. /***/ "./src/cleanupSemantic.ts":
  13. /***/ ((__unused_webpack_module, exports) => {
  14. Object.defineProperty(exports, "__esModule", ({
  15. value: true
  16. }));
  17. exports.cleanupSemantic = exports.Diff = exports.DIFF_INSERT = exports.DIFF_EQUAL = exports.DIFF_DELETE = void 0;
  18. /**
  19. * Diff Match and Patch
  20. * Copyright 2018 The diff-match-patch Authors.
  21. * https://github.com/google/diff-match-patch
  22. *
  23. * Licensed under the Apache License, Version 2.0 (the "License");
  24. * you may not use this file except in compliance with the License.
  25. * You may obtain a copy of the License at
  26. *
  27. * http://www.apache.org/licenses/LICENSE-2.0
  28. *
  29. * Unless required by applicable law or agreed to in writing, software
  30. * distributed under the License is distributed on an "AS IS" BASIS,
  31. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  32. * See the License for the specific language governing permissions and
  33. * limitations under the License.
  34. */
  35. /**
  36. * @fileoverview Computes the difference between two texts to create a patch.
  37. * Applies the patch onto another text, allowing for errors.
  38. * @author fraser@google.com (Neil Fraser)
  39. */
  40. /**
  41. * CHANGES by pedrottimark to diff_match_patch_uncompressed.ts file:
  42. *
  43. * 1. Delete anything not needed to use diff_cleanupSemantic method
  44. * 2. Convert from prototype properties to var declarations
  45. * 3. Convert Diff to class from constructor and prototype
  46. * 4. Add type annotations for arguments and return values
  47. * 5. Add exports
  48. */
  49. /**
  50. * The data structure representing a diff is an array of tuples:
  51. * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
  52. * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
  53. */
  54. var DIFF_DELETE = exports.DIFF_DELETE = -1;
  55. var DIFF_INSERT = exports.DIFF_INSERT = 1;
  56. var DIFF_EQUAL = exports.DIFF_EQUAL = 0;
  57. /**
  58. * Class representing one diff tuple.
  59. * Attempts to look like a two-element array (which is what this used to be).
  60. * @param {number} op Operation, one of: DIFF_DELETE, DIFF_INSERT, DIFF_EQUAL.
  61. * @param {string} text Text to be deleted, inserted, or retained.
  62. * @constructor
  63. */
  64. class Diff {
  65. 0;
  66. 1;
  67. constructor(op, text) {
  68. this[0] = op;
  69. this[1] = text;
  70. }
  71. }
  72. /**
  73. * Determine the common prefix of two strings.
  74. * @param {string} text1 First string.
  75. * @param {string} text2 Second string.
  76. * @return {number} The number of characters common to the start of each
  77. * string.
  78. */
  79. exports.Diff = Diff;
  80. var diff_commonPrefix = function (text1, text2) {
  81. // Quick check for common null cases.
  82. if (!text1 || !text2 || text1.charAt(0) != text2.charAt(0)) {
  83. return 0;
  84. }
  85. // Binary search.
  86. // Performance analysis: https://neil.fraser.name/news/2007/10/09/
  87. var pointermin = 0;
  88. var pointermax = Math.min(text1.length, text2.length);
  89. var pointermid = pointermax;
  90. var pointerstart = 0;
  91. while (pointermin < pointermid) {
  92. if (text1.substring(pointerstart, pointermid) == text2.substring(pointerstart, pointermid)) {
  93. pointermin = pointermid;
  94. pointerstart = pointermin;
  95. } else {
  96. pointermax = pointermid;
  97. }
  98. pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
  99. }
  100. return pointermid;
  101. };
  102. /**
  103. * Determine the common suffix of two strings.
  104. * @param {string} text1 First string.
  105. * @param {string} text2 Second string.
  106. * @return {number} The number of characters common to the end of each string.
  107. */
  108. var diff_commonSuffix = function (text1, text2) {
  109. // Quick check for common null cases.
  110. if (!text1 || !text2 || text1.charAt(text1.length - 1) != text2.charAt(text2.length - 1)) {
  111. return 0;
  112. }
  113. // Binary search.
  114. // Performance analysis: https://neil.fraser.name/news/2007/10/09/
  115. var pointermin = 0;
  116. var pointermax = Math.min(text1.length, text2.length);
  117. var pointermid = pointermax;
  118. var pointerend = 0;
  119. while (pointermin < pointermid) {
  120. if (text1.substring(text1.length - pointermid, text1.length - pointerend) == text2.substring(text2.length - pointermid, text2.length - pointerend)) {
  121. pointermin = pointermid;
  122. pointerend = pointermin;
  123. } else {
  124. pointermax = pointermid;
  125. }
  126. pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
  127. }
  128. return pointermid;
  129. };
  130. /**
  131. * Determine if the suffix of one string is the prefix of another.
  132. * @param {string} text1 First string.
  133. * @param {string} text2 Second string.
  134. * @return {number} The number of characters common to the end of the first
  135. * string and the start of the second string.
  136. * @private
  137. */
  138. var diff_commonOverlap_ = function (text1, text2) {
  139. // Cache the text lengths to prevent multiple calls.
  140. var text1_length = text1.length;
  141. var text2_length = text2.length;
  142. // Eliminate the null case.
  143. if (text1_length == 0 || text2_length == 0) {
  144. return 0;
  145. }
  146. // Truncate the longer string.
  147. if (text1_length > text2_length) {
  148. text1 = text1.substring(text1_length - text2_length);
  149. } else if (text1_length < text2_length) {
  150. text2 = text2.substring(0, text1_length);
  151. }
  152. var text_length = Math.min(text1_length, text2_length);
  153. // Quick check for the worst case.
  154. if (text1 == text2) {
  155. return text_length;
  156. }
  157. // Start by looking for a single character match
  158. // and increase length until no match is found.
  159. // Performance analysis: https://neil.fraser.name/news/2010/11/04/
  160. var best = 0;
  161. var length = 1;
  162. while (true) {
  163. var pattern = text1.substring(text_length - length);
  164. var found = text2.indexOf(pattern);
  165. if (found == -1) {
  166. return best;
  167. }
  168. length += found;
  169. if (found == 0 || text1.substring(text_length - length) == text2.substring(0, length)) {
  170. best = length;
  171. length++;
  172. }
  173. }
  174. };
  175. /**
  176. * Reduce the number of edits by eliminating semantically trivial equalities.
  177. * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
  178. */
  179. var diff_cleanupSemantic = function (diffs) {
  180. var changes = false;
  181. var equalities = []; // Stack of indices where equalities are found.
  182. var equalitiesLength = 0; // Keeping our own length var is faster in JS.
  183. /** @type {?string} */
  184. var lastEquality = null;
  185. // Always equal to diffs[equalities[equalitiesLength - 1]][1]
  186. var pointer = 0; // Index of current position.
  187. // Number of characters that changed prior to the equality.
  188. var length_insertions1 = 0;
  189. var length_deletions1 = 0;
  190. // Number of characters that changed after the equality.
  191. var length_insertions2 = 0;
  192. var length_deletions2 = 0;
  193. while (pointer < diffs.length) {
  194. if (diffs[pointer][0] == DIFF_EQUAL) {
  195. // Equality found.
  196. equalities[equalitiesLength++] = pointer;
  197. length_insertions1 = length_insertions2;
  198. length_deletions1 = length_deletions2;
  199. length_insertions2 = 0;
  200. length_deletions2 = 0;
  201. lastEquality = diffs[pointer][1];
  202. } else {
  203. // An insertion or deletion.
  204. if (diffs[pointer][0] == DIFF_INSERT) {
  205. length_insertions2 += diffs[pointer][1].length;
  206. } else {
  207. length_deletions2 += diffs[pointer][1].length;
  208. }
  209. // Eliminate an equality that is smaller or equal to the edits on both
  210. // sides of it.
  211. if (lastEquality && lastEquality.length <= Math.max(length_insertions1, length_deletions1) && lastEquality.length <= Math.max(length_insertions2, length_deletions2)) {
  212. // Duplicate record.
  213. diffs.splice(equalities[equalitiesLength - 1], 0, new Diff(DIFF_DELETE, lastEquality));
  214. // Change second copy to insert.
  215. diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
  216. // Throw away the equality we just deleted.
  217. equalitiesLength--;
  218. // Throw away the previous equality (it needs to be reevaluated).
  219. equalitiesLength--;
  220. pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
  221. length_insertions1 = 0; // Reset the counters.
  222. length_deletions1 = 0;
  223. length_insertions2 = 0;
  224. length_deletions2 = 0;
  225. lastEquality = null;
  226. changes = true;
  227. }
  228. }
  229. pointer++;
  230. }
  231. // Normalize the diff.
  232. if (changes) {
  233. diff_cleanupMerge(diffs);
  234. }
  235. diff_cleanupSemanticLossless(diffs);
  236. // Find any overlaps between deletions and insertions.
  237. // e.g: <del>abcxxx</del><ins>xxxdef</ins>
  238. // -> <del>abc</del>xxx<ins>def</ins>
  239. // e.g: <del>xxxabc</del><ins>defxxx</ins>
  240. // -> <ins>def</ins>xxx<del>abc</del>
  241. // Only extract an overlap if it is as big as the edit ahead or behind it.
  242. pointer = 1;
  243. while (pointer < diffs.length) {
  244. if (diffs[pointer - 1][0] == DIFF_DELETE && diffs[pointer][0] == DIFF_INSERT) {
  245. var deletion = diffs[pointer - 1][1];
  246. var insertion = diffs[pointer][1];
  247. var overlap_length1 = diff_commonOverlap_(deletion, insertion);
  248. var overlap_length2 = diff_commonOverlap_(insertion, deletion);
  249. if (overlap_length1 >= overlap_length2) {
  250. if (overlap_length1 >= deletion.length / 2 || overlap_length1 >= insertion.length / 2) {
  251. // Overlap found. Insert an equality and trim the surrounding edits.
  252. diffs.splice(pointer, 0, new Diff(DIFF_EQUAL, insertion.substring(0, overlap_length1)));
  253. diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlap_length1);
  254. diffs[pointer + 1][1] = insertion.substring(overlap_length1);
  255. pointer++;
  256. }
  257. } else {
  258. if (overlap_length2 >= deletion.length / 2 || overlap_length2 >= insertion.length / 2) {
  259. // Reverse overlap found.
  260. // Insert an equality and swap and trim the surrounding edits.
  261. diffs.splice(pointer, 0, new Diff(DIFF_EQUAL, deletion.substring(0, overlap_length2)));
  262. diffs[pointer - 1][0] = DIFF_INSERT;
  263. diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlap_length2);
  264. diffs[pointer + 1][0] = DIFF_DELETE;
  265. diffs[pointer + 1][1] = deletion.substring(overlap_length2);
  266. pointer++;
  267. }
  268. }
  269. pointer++;
  270. }
  271. pointer++;
  272. }
  273. };
  274. /**
  275. * Look for single edits surrounded on both sides by equalities
  276. * which can be shifted sideways to align the edit to a word boundary.
  277. * e.g: The c<ins>at c</ins>ame. -> The <ins>cat </ins>came.
  278. * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
  279. */
  280. exports.cleanupSemantic = diff_cleanupSemantic;
  281. var diff_cleanupSemanticLossless = function (diffs) {
  282. /**
  283. * Given two strings, compute a score representing whether the internal
  284. * boundary falls on logical boundaries.
  285. * Scores range from 6 (best) to 0 (worst).
  286. * Closure, but does not reference any external variables.
  287. * @param {string} one First string.
  288. * @param {string} two Second string.
  289. * @return {number} The score.
  290. * @private
  291. */
  292. function diff_cleanupSemanticScore_(one, two) {
  293. if (!one || !two) {
  294. // Edges are the best.
  295. return 6;
  296. }
  297. // Each port of this function behaves slightly differently due to
  298. // subtle differences in each language's definition of things like
  299. // 'whitespace'. Since this function's purpose is largely cosmetic,
  300. // the choice has been made to use each language's native features
  301. // rather than force total conformity.
  302. var char1 = one.charAt(one.length - 1);
  303. var char2 = two.charAt(0);
  304. var nonAlphaNumeric1 = char1.match(nonAlphaNumericRegex_);
  305. var nonAlphaNumeric2 = char2.match(nonAlphaNumericRegex_);
  306. var whitespace1 = nonAlphaNumeric1 && char1.match(whitespaceRegex_);
  307. var whitespace2 = nonAlphaNumeric2 && char2.match(whitespaceRegex_);
  308. var lineBreak1 = whitespace1 && char1.match(linebreakRegex_);
  309. var lineBreak2 = whitespace2 && char2.match(linebreakRegex_);
  310. var blankLine1 = lineBreak1 && one.match(blanklineEndRegex_);
  311. var blankLine2 = lineBreak2 && two.match(blanklineStartRegex_);
  312. if (blankLine1 || blankLine2) {
  313. // Five points for blank lines.
  314. return 5;
  315. } else if (lineBreak1 || lineBreak2) {
  316. // Four points for line breaks.
  317. return 4;
  318. } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) {
  319. // Three points for end of sentences.
  320. return 3;
  321. } else if (whitespace1 || whitespace2) {
  322. // Two points for whitespace.
  323. return 2;
  324. } else if (nonAlphaNumeric1 || nonAlphaNumeric2) {
  325. // One point for non-alphanumeric.
  326. return 1;
  327. }
  328. return 0;
  329. }
  330. var pointer = 1;
  331. // Intentionally ignore the first and last element (don't need checking).
  332. while (pointer < diffs.length - 1) {
  333. if (diffs[pointer - 1][0] == DIFF_EQUAL && diffs[pointer + 1][0] == DIFF_EQUAL) {
  334. // This is a single edit surrounded by equalities.
  335. var equality1 = diffs[pointer - 1][1];
  336. var edit = diffs[pointer][1];
  337. var equality2 = diffs[pointer + 1][1];
  338. // First, shift the edit as far left as possible.
  339. var commonOffset = diff_commonSuffix(equality1, edit);
  340. if (commonOffset) {
  341. var commonString = edit.substring(edit.length - commonOffset);
  342. equality1 = equality1.substring(0, equality1.length - commonOffset);
  343. edit = commonString + edit.substring(0, edit.length - commonOffset);
  344. equality2 = commonString + equality2;
  345. }
  346. // Second, step character by character right, looking for the best fit.
  347. var bestEquality1 = equality1;
  348. var bestEdit = edit;
  349. var bestEquality2 = equality2;
  350. var bestScore = diff_cleanupSemanticScore_(equality1, edit) + diff_cleanupSemanticScore_(edit, equality2);
  351. while (edit.charAt(0) === equality2.charAt(0)) {
  352. equality1 += edit.charAt(0);
  353. edit = edit.substring(1) + equality2.charAt(0);
  354. equality2 = equality2.substring(1);
  355. var score = diff_cleanupSemanticScore_(equality1, edit) + diff_cleanupSemanticScore_(edit, equality2);
  356. // The >= encourages trailing rather than leading whitespace on edits.
  357. if (score >= bestScore) {
  358. bestScore = score;
  359. bestEquality1 = equality1;
  360. bestEdit = edit;
  361. bestEquality2 = equality2;
  362. }
  363. }
  364. if (diffs[pointer - 1][1] != bestEquality1) {
  365. // We have an improvement, save it back to the diff.
  366. if (bestEquality1) {
  367. diffs[pointer - 1][1] = bestEquality1;
  368. } else {
  369. diffs.splice(pointer - 1, 1);
  370. pointer--;
  371. }
  372. diffs[pointer][1] = bestEdit;
  373. if (bestEquality2) {
  374. diffs[pointer + 1][1] = bestEquality2;
  375. } else {
  376. diffs.splice(pointer + 1, 1);
  377. pointer--;
  378. }
  379. }
  380. }
  381. pointer++;
  382. }
  383. };
  384. // Define some regex patterns for matching boundaries.
  385. var nonAlphaNumericRegex_ = /[^a-zA-Z0-9]/;
  386. var whitespaceRegex_ = /\s/;
  387. var linebreakRegex_ = /[\r\n]/;
  388. var blanklineEndRegex_ = /\n\r?\n$/;
  389. var blanklineStartRegex_ = /^\r?\n\r?\n/;
  390. /**
  391. * Reorder and merge like edit sections. Merge equalities.
  392. * Any edit section can move as long as it doesn't cross an equality.
  393. * @param {!Array.<!diff_match_patch.Diff>} diffs Array of diff tuples.
  394. */
  395. var diff_cleanupMerge = function (diffs) {
  396. // Add a dummy entry at the end.
  397. diffs.push(new Diff(DIFF_EQUAL, ''));
  398. var pointer = 0;
  399. var count_delete = 0;
  400. var count_insert = 0;
  401. var text_delete = '';
  402. var text_insert = '';
  403. var commonlength;
  404. while (pointer < diffs.length) {
  405. switch (diffs[pointer][0]) {
  406. case DIFF_INSERT:
  407. count_insert++;
  408. text_insert += diffs[pointer][1];
  409. pointer++;
  410. break;
  411. case DIFF_DELETE:
  412. count_delete++;
  413. text_delete += diffs[pointer][1];
  414. pointer++;
  415. break;
  416. case DIFF_EQUAL:
  417. // Upon reaching an equality, check for prior redundancies.
  418. if (count_delete + count_insert > 1) {
  419. if (count_delete !== 0 && count_insert !== 0) {
  420. // Factor out any common prefixies.
  421. commonlength = diff_commonPrefix(text_insert, text_delete);
  422. if (commonlength !== 0) {
  423. if (pointer - count_delete - count_insert > 0 && diffs[pointer - count_delete - count_insert - 1][0] == DIFF_EQUAL) {
  424. diffs[pointer - count_delete - count_insert - 1][1] += text_insert.substring(0, commonlength);
  425. } else {
  426. diffs.splice(0, 0, new Diff(DIFF_EQUAL, text_insert.substring(0, commonlength)));
  427. pointer++;
  428. }
  429. text_insert = text_insert.substring(commonlength);
  430. text_delete = text_delete.substring(commonlength);
  431. }
  432. // Factor out any common suffixies.
  433. commonlength = diff_commonSuffix(text_insert, text_delete);
  434. if (commonlength !== 0) {
  435. diffs[pointer][1] = text_insert.substring(text_insert.length - commonlength) + diffs[pointer][1];
  436. text_insert = text_insert.substring(0, text_insert.length - commonlength);
  437. text_delete = text_delete.substring(0, text_delete.length - commonlength);
  438. }
  439. }
  440. // Delete the offending records and add the merged ones.
  441. pointer -= count_delete + count_insert;
  442. diffs.splice(pointer, count_delete + count_insert);
  443. if (text_delete.length) {
  444. diffs.splice(pointer, 0, new Diff(DIFF_DELETE, text_delete));
  445. pointer++;
  446. }
  447. if (text_insert.length) {
  448. diffs.splice(pointer, 0, new Diff(DIFF_INSERT, text_insert));
  449. pointer++;
  450. }
  451. pointer++;
  452. } else if (pointer !== 0 && diffs[pointer - 1][0] == DIFF_EQUAL) {
  453. // Merge this equality with the previous one.
  454. diffs[pointer - 1][1] += diffs[pointer][1];
  455. diffs.splice(pointer, 1);
  456. } else {
  457. pointer++;
  458. }
  459. count_insert = 0;
  460. count_delete = 0;
  461. text_delete = '';
  462. text_insert = '';
  463. break;
  464. }
  465. }
  466. if (diffs[diffs.length - 1][1] === '') {
  467. diffs.pop(); // Remove the dummy entry at the end.
  468. }
  469. // Second pass: look for single edits surrounded on both sides by equalities
  470. // which can be shifted sideways to eliminate an equality.
  471. // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
  472. var changes = false;
  473. pointer = 1;
  474. // Intentionally ignore the first and last element (don't need checking).
  475. while (pointer < diffs.length - 1) {
  476. if (diffs[pointer - 1][0] == DIFF_EQUAL && diffs[pointer + 1][0] == DIFF_EQUAL) {
  477. // This is a single edit surrounded by equalities.
  478. if (diffs[pointer][1].substring(diffs[pointer][1].length - diffs[pointer - 1][1].length) == diffs[pointer - 1][1]) {
  479. // Shift the edit over the previous equality.
  480. diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
  481. diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
  482. diffs.splice(pointer - 1, 1);
  483. changes = true;
  484. } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == diffs[pointer + 1][1]) {
  485. // Shift the edit over the next equality.
  486. diffs[pointer - 1][1] += diffs[pointer + 1][1];
  487. diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
  488. diffs.splice(pointer + 1, 1);
  489. changes = true;
  490. }
  491. }
  492. pointer++;
  493. }
  494. // If shifts were made, the diff needs reordering and another shift sweep.
  495. if (changes) {
  496. diff_cleanupMerge(diffs);
  497. }
  498. };
  499. /***/ }),
  500. /***/ "./src/constants.ts":
  501. /***/ ((__unused_webpack_module, exports) => {
  502. Object.defineProperty(exports, "__esModule", ({
  503. value: true
  504. }));
  505. exports.SIMILAR_MESSAGE = exports.NO_DIFF_MESSAGE = void 0;
  506. /**
  507. * Copyright (c) Meta Platforms, Inc. and affiliates.
  508. *
  509. * This source code is licensed under the MIT license found in the
  510. * LICENSE file in the root directory of this source tree.
  511. */
  512. const NO_DIFF_MESSAGE = exports.NO_DIFF_MESSAGE = 'Compared values have no visual difference.';
  513. const SIMILAR_MESSAGE = exports.SIMILAR_MESSAGE = 'Compared values serialize to the same structure.\n' + 'Printing internal object structure without calling `toJSON` instead.';
  514. /***/ }),
  515. /***/ "./src/diffLines.ts":
  516. /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
  517. Object.defineProperty(exports, "__esModule", ({
  518. value: true
  519. }));
  520. exports.printDiffLines = exports.diffLinesUnified2 = exports.diffLinesUnified = exports.diffLinesRaw = void 0;
  521. var _diffSequences = _interopRequireDefault(require("@jest/diff-sequences"));
  522. var _cleanupSemantic = __webpack_require__("./src/cleanupSemantic.ts");
  523. var _escapeControlCharacters = __webpack_require__("./src/escapeControlCharacters.ts");
  524. var _joinAlignedDiffs = __webpack_require__("./src/joinAlignedDiffs.ts");
  525. var _normalizeDiffOptions = __webpack_require__("./src/normalizeDiffOptions.ts");
  526. function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  527. /**
  528. * Copyright (c) Meta Platforms, Inc. and affiliates.
  529. *
  530. * This source code is licensed under the MIT license found in the
  531. * LICENSE file in the root directory of this source tree.
  532. */
  533. const isEmptyString = lines => lines.length === 1 && lines[0].length === 0;
  534. const countChanges = diffs => {
  535. let a = 0;
  536. let b = 0;
  537. for (const diff of diffs) {
  538. switch (diff[0]) {
  539. case _cleanupSemantic.DIFF_DELETE:
  540. a += 1;
  541. break;
  542. case _cleanupSemantic.DIFF_INSERT:
  543. b += 1;
  544. break;
  545. }
  546. }
  547. return {
  548. a,
  549. b
  550. };
  551. };
  552. const printAnnotation = ({
  553. aAnnotation,
  554. aColor,
  555. aIndicator,
  556. bAnnotation,
  557. bColor,
  558. bIndicator,
  559. includeChangeCounts,
  560. omitAnnotationLines
  561. }, changeCounts) => {
  562. if (omitAnnotationLines) {
  563. return '';
  564. }
  565. let aRest = '';
  566. let bRest = '';
  567. if (includeChangeCounts) {
  568. const aCount = String(changeCounts.a);
  569. const bCount = String(changeCounts.b);
  570. // Padding right aligns the ends of the annotations.
  571. const baAnnotationLengthDiff = bAnnotation.length - aAnnotation.length;
  572. const aAnnotationPadding = ' '.repeat(Math.max(0, baAnnotationLengthDiff));
  573. const bAnnotationPadding = ' '.repeat(Math.max(0, -baAnnotationLengthDiff));
  574. // Padding left aligns the ends of the counts.
  575. const baCountLengthDiff = bCount.length - aCount.length;
  576. const aCountPadding = ' '.repeat(Math.max(0, baCountLengthDiff));
  577. const bCountPadding = ' '.repeat(Math.max(0, -baCountLengthDiff));
  578. aRest = `${aAnnotationPadding} ${aIndicator} ${aCountPadding}${aCount}`;
  579. bRest = `${bAnnotationPadding} ${bIndicator} ${bCountPadding}${bCount}`;
  580. }
  581. const a = `${aIndicator} ${aAnnotation}${aRest}`;
  582. const b = `${bIndicator} ${bAnnotation}${bRest}`;
  583. return `${aColor(a)}\n${bColor(b)}\n\n`;
  584. };
  585. const printDiffLines = (diffs, options) => printAnnotation(options, countChanges(diffs)) + (options.expand ? (0, _joinAlignedDiffs.joinAlignedDiffsExpand)(diffs, options) : (0, _joinAlignedDiffs.joinAlignedDiffsNoExpand)(diffs, options));
  586. // Compare two arrays of strings line-by-line. Format as comparison lines.
  587. exports.printDiffLines = printDiffLines;
  588. const diffLinesUnified = (aLines, bLines, options) => printDiffLines(diffLinesRaw(isEmptyString(aLines) ? [] : aLines.map(_escapeControlCharacters.escapeControlCharacters), isEmptyString(bLines) ? [] : bLines.map(_escapeControlCharacters.escapeControlCharacters)), (0, _normalizeDiffOptions.normalizeDiffOptions)(options));
  589. // Given two pairs of arrays of strings:
  590. // Compare the pair of comparison arrays line-by-line.
  591. // Format the corresponding lines in the pair of displayable arrays.
  592. exports.diffLinesUnified = diffLinesUnified;
  593. const diffLinesUnified2 = (aLinesDisplay, bLinesDisplay, aLinesCompare, bLinesCompare, options) => {
  594. if (isEmptyString(aLinesDisplay) && isEmptyString(aLinesCompare)) {
  595. aLinesDisplay = [];
  596. aLinesCompare = [];
  597. }
  598. if (isEmptyString(bLinesDisplay) && isEmptyString(bLinesCompare)) {
  599. bLinesDisplay = [];
  600. bLinesCompare = [];
  601. }
  602. if (aLinesDisplay.length !== aLinesCompare.length || bLinesDisplay.length !== bLinesCompare.length) {
  603. // Fall back to diff of display lines.
  604. return diffLinesUnified(aLinesDisplay, bLinesDisplay, options);
  605. }
  606. const diffs = diffLinesRaw(aLinesCompare, bLinesCompare);
  607. // Replace comparison lines with displayable lines.
  608. let aIndex = 0;
  609. let bIndex = 0;
  610. for (const diff of diffs) {
  611. switch (diff[0]) {
  612. case _cleanupSemantic.DIFF_DELETE:
  613. diff[1] = aLinesDisplay[aIndex];
  614. aIndex += 1;
  615. break;
  616. case _cleanupSemantic.DIFF_INSERT:
  617. diff[1] = bLinesDisplay[bIndex];
  618. bIndex += 1;
  619. break;
  620. default:
  621. diff[1] = bLinesDisplay[bIndex];
  622. aIndex += 1;
  623. bIndex += 1;
  624. }
  625. }
  626. return printDiffLines(diffs, (0, _normalizeDiffOptions.normalizeDiffOptions)(options));
  627. };
  628. // Compare two arrays of strings line-by-line.
  629. exports.diffLinesUnified2 = diffLinesUnified2;
  630. const diffLinesRaw = (aLines, bLines) => {
  631. const aLength = aLines.length;
  632. const bLength = bLines.length;
  633. const isCommon = (aIndex, bIndex) => aLines[aIndex] === bLines[bIndex];
  634. const diffs = [];
  635. let aIndex = 0;
  636. let bIndex = 0;
  637. const foundSubsequence = (nCommon, aCommon, bCommon) => {
  638. for (; aIndex !== aCommon; aIndex += 1) {
  639. diffs.push(new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_DELETE, aLines[aIndex]));
  640. }
  641. for (; bIndex !== bCommon; bIndex += 1) {
  642. diffs.push(new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_INSERT, bLines[bIndex]));
  643. }
  644. for (; nCommon !== 0; nCommon -= 1, aIndex += 1, bIndex += 1) {
  645. diffs.push(new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_EQUAL, bLines[bIndex]));
  646. }
  647. };
  648. (0, _diffSequences.default)(aLength, bLength, isCommon, foundSubsequence);
  649. // After the last common subsequence, push remaining change items.
  650. for (; aIndex !== aLength; aIndex += 1) {
  651. diffs.push(new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_DELETE, aLines[aIndex]));
  652. }
  653. for (; bIndex !== bLength; bIndex += 1) {
  654. diffs.push(new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_INSERT, bLines[bIndex]));
  655. }
  656. return diffs;
  657. };
  658. exports.diffLinesRaw = diffLinesRaw;
  659. /***/ }),
  660. /***/ "./src/diffStrings.ts":
  661. /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
  662. Object.defineProperty(exports, "__esModule", ({
  663. value: true
  664. }));
  665. exports["default"] = void 0;
  666. var _diffSequences = _interopRequireDefault(require("@jest/diff-sequences"));
  667. var _cleanupSemantic = __webpack_require__("./src/cleanupSemantic.ts");
  668. function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  669. /**
  670. * Copyright (c) Meta Platforms, Inc. and affiliates.
  671. *
  672. * This source code is licensed under the MIT license found in the
  673. * LICENSE file in the root directory of this source tree.
  674. */
  675. const diffStrings = (a, b) => {
  676. const isCommon = (aIndex, bIndex) => a[aIndex] === b[bIndex];
  677. let aIndex = 0;
  678. let bIndex = 0;
  679. const diffs = [];
  680. const foundSubsequence = (nCommon, aCommon, bCommon) => {
  681. if (aIndex !== aCommon) {
  682. diffs.push(new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_DELETE, a.slice(aIndex, aCommon)));
  683. }
  684. if (bIndex !== bCommon) {
  685. diffs.push(new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_INSERT, b.slice(bIndex, bCommon)));
  686. }
  687. aIndex = aCommon + nCommon; // number of characters compared in a
  688. bIndex = bCommon + nCommon; // number of characters compared in b
  689. diffs.push(new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_EQUAL, b.slice(bCommon, bIndex)));
  690. };
  691. (0, _diffSequences.default)(a.length, b.length, isCommon, foundSubsequence);
  692. // After the last common subsequence, push remaining change items.
  693. if (aIndex !== a.length) {
  694. diffs.push(new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_DELETE, a.slice(aIndex)));
  695. }
  696. if (bIndex !== b.length) {
  697. diffs.push(new _cleanupSemantic.Diff(_cleanupSemantic.DIFF_INSERT, b.slice(bIndex)));
  698. }
  699. return diffs;
  700. };
  701. var _default = exports["default"] = diffStrings;
  702. /***/ }),
  703. /***/ "./src/escapeControlCharacters.ts":
  704. /***/ ((__unused_webpack_module, exports) => {
  705. Object.defineProperty(exports, "__esModule", ({
  706. value: true
  707. }));
  708. exports.escapeControlCharacters = void 0;
  709. /**
  710. * Copyright (c) Meta Platforms, Inc. and affiliates.
  711. *
  712. * This source code is licensed under the MIT license found in the
  713. * LICENSE file in the root directory of this source tree.
  714. */
  715. // Escape control characters to make them visible in diffs
  716. const escapeControlCharacters = str => str.replaceAll(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g, match => {
  717. switch (match) {
  718. case '\b':
  719. return '\\b';
  720. case '\f':
  721. return '\\f';
  722. case '\v':
  723. return '\\v';
  724. default:
  725. {
  726. const code = match.codePointAt(0);
  727. return `\\x${code.toString(16).padStart(2, '0')}`;
  728. }
  729. }
  730. });
  731. exports.escapeControlCharacters = escapeControlCharacters;
  732. /***/ }),
  733. /***/ "./src/getAlignedDiffs.ts":
  734. /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
  735. Object.defineProperty(exports, "__esModule", ({
  736. value: true
  737. }));
  738. exports["default"] = void 0;
  739. var _cleanupSemantic = __webpack_require__("./src/cleanupSemantic.ts");
  740. /**
  741. * Copyright (c) Meta Platforms, Inc. and affiliates.
  742. *
  743. * This source code is licensed under the MIT license found in the
  744. * LICENSE file in the root directory of this source tree.
  745. */
  746. // Given change op and array of diffs, return concatenated string:
  747. // * include common strings
  748. // * include change strings which have argument op with changeColor
  749. // * exclude change strings which have opposite op
  750. const concatenateRelevantDiffs = (op, diffs, changeColor) => diffs.reduce((reduced, diff) => reduced + (diff[0] === _cleanupSemantic.DIFF_EQUAL ? diff[1] : diff[0] === op && diff[1].length > 0 // empty if change is newline
  751. ? changeColor(diff[1]) : ''), '');
  752. // Encapsulate change lines until either a common newline or the end.
  753. class ChangeBuffer {
  754. op;
  755. line; // incomplete line
  756. lines; // complete lines
  757. changeColor;
  758. constructor(op, changeColor) {
  759. this.op = op;
  760. this.line = [];
  761. this.lines = [];
  762. this.changeColor = changeColor;
  763. }
  764. pushSubstring(substring) {
  765. this.pushDiff(new _cleanupSemantic.Diff(this.op, substring));
  766. }
  767. pushLine() {
  768. // Assume call only if line has at least one diff,
  769. // therefore an empty line must have a diff which has an empty string.
  770. // If line has multiple diffs, then assume it has a common diff,
  771. // therefore change diffs have change color;
  772. // otherwise then it has line color only.
  773. this.lines.push(this.line.length === 1 ? this.line[0][0] === this.op ? this.line[0] // can use instance
  774. : new _cleanupSemantic.Diff(this.op, this.line[0][1]) : new _cleanupSemantic.Diff(this.op, concatenateRelevantDiffs(this.op, this.line, this.changeColor)) // was common diff
  775. );
  776. this.line.length = 0;
  777. }
  778. isLineEmpty() {
  779. return this.line.length === 0;
  780. }
  781. // Minor input to buffer.
  782. pushDiff(diff) {
  783. this.line.push(diff);
  784. }
  785. // Main input to buffer.
  786. align(diff) {
  787. const string = diff[1];
  788. if (string.includes('\n')) {
  789. const substrings = string.split('\n');
  790. const iLast = substrings.length - 1;
  791. for (const [i, substring] of substrings.entries()) {
  792. if (i < iLast) {
  793. // The first substring completes the current change line.
  794. // A middle substring is a change line.
  795. this.pushSubstring(substring);
  796. this.pushLine();
  797. } else if (substring.length > 0) {
  798. // The last substring starts a change line, if it is not empty.
  799. // Important: This non-empty condition also automatically omits
  800. // the newline appended to the end of expected and received strings.
  801. this.pushSubstring(substring);
  802. }
  803. }
  804. } else {
  805. // Append non-multiline string to current change line.
  806. this.pushDiff(diff);
  807. }
  808. }
  809. // Output from buffer.
  810. moveLinesTo(lines) {
  811. if (!this.isLineEmpty()) {
  812. this.pushLine();
  813. }
  814. lines.push(...this.lines);
  815. this.lines.length = 0;
  816. }
  817. }
  818. // Encapsulate common and change lines.
  819. class CommonBuffer {
  820. deleteBuffer;
  821. insertBuffer;
  822. lines;
  823. constructor(deleteBuffer, insertBuffer) {
  824. this.deleteBuffer = deleteBuffer;
  825. this.insertBuffer = insertBuffer;
  826. this.lines = [];
  827. }
  828. pushDiffCommonLine(diff) {
  829. this.lines.push(diff);
  830. }
  831. pushDiffChangeLines(diff) {
  832. const isDiffEmpty = diff[1].length === 0;
  833. // An empty diff string is redundant, unless a change line is empty.
  834. if (!isDiffEmpty || this.deleteBuffer.isLineEmpty()) {
  835. this.deleteBuffer.pushDiff(diff);
  836. }
  837. if (!isDiffEmpty || this.insertBuffer.isLineEmpty()) {
  838. this.insertBuffer.pushDiff(diff);
  839. }
  840. }
  841. flushChangeLines() {
  842. this.deleteBuffer.moveLinesTo(this.lines);
  843. this.insertBuffer.moveLinesTo(this.lines);
  844. }
  845. // Input to buffer.
  846. align(diff) {
  847. const op = diff[0];
  848. const string = diff[1];
  849. if (string.includes('\n')) {
  850. const substrings = string.split('\n');
  851. const iLast = substrings.length - 1;
  852. for (const [i, substring] of substrings.entries()) {
  853. if (i === 0) {
  854. const subdiff = new _cleanupSemantic.Diff(op, substring);
  855. if (this.deleteBuffer.isLineEmpty() && this.insertBuffer.isLineEmpty()) {
  856. // If both current change lines are empty,
  857. // then the first substring is a common line.
  858. this.flushChangeLines();
  859. this.pushDiffCommonLine(subdiff);
  860. } else {
  861. // If either current change line is non-empty,
  862. // then the first substring completes the change lines.
  863. this.pushDiffChangeLines(subdiff);
  864. this.flushChangeLines();
  865. }
  866. } else if (i < iLast) {
  867. // A middle substring is a common line.
  868. this.pushDiffCommonLine(new _cleanupSemantic.Diff(op, substring));
  869. } else if (substring.length > 0) {
  870. // The last substring starts a change line, if it is not empty.
  871. // Important: This non-empty condition also automatically omits
  872. // the newline appended to the end of expected and received strings.
  873. this.pushDiffChangeLines(new _cleanupSemantic.Diff(op, substring));
  874. }
  875. }
  876. } else {
  877. // Append non-multiline string to current change lines.
  878. // Important: It cannot be at the end following empty change lines,
  879. // because newline appended to the end of expected and received strings.
  880. this.pushDiffChangeLines(diff);
  881. }
  882. }
  883. // Output from buffer.
  884. getLines() {
  885. this.flushChangeLines();
  886. return this.lines;
  887. }
  888. }
  889. // Given diffs from expected and received strings,
  890. // return new array of diffs split or joined into lines.
  891. //
  892. // To correctly align a change line at the end, the algorithm:
  893. // * assumes that a newline was appended to the strings
  894. // * omits the last newline from the output array
  895. //
  896. // Assume the function is not called:
  897. // * if either expected or received is empty string
  898. // * if neither expected nor received is multiline string
  899. const getAlignedDiffs = (diffs, changeColor) => {
  900. const deleteBuffer = new ChangeBuffer(_cleanupSemantic.DIFF_DELETE, changeColor);
  901. const insertBuffer = new ChangeBuffer(_cleanupSemantic.DIFF_INSERT, changeColor);
  902. const commonBuffer = new CommonBuffer(deleteBuffer, insertBuffer);
  903. for (const diff of diffs) {
  904. switch (diff[0]) {
  905. case _cleanupSemantic.DIFF_DELETE:
  906. deleteBuffer.align(diff);
  907. break;
  908. case _cleanupSemantic.DIFF_INSERT:
  909. insertBuffer.align(diff);
  910. break;
  911. default:
  912. commonBuffer.align(diff);
  913. }
  914. }
  915. return commonBuffer.getLines();
  916. };
  917. var _default = exports["default"] = getAlignedDiffs;
  918. /***/ }),
  919. /***/ "./src/joinAlignedDiffs.ts":
  920. /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
  921. Object.defineProperty(exports, "__esModule", ({
  922. value: true
  923. }));
  924. exports.joinAlignedDiffsNoExpand = exports.joinAlignedDiffsExpand = void 0;
  925. var _cleanupSemantic = __webpack_require__("./src/cleanupSemantic.ts");
  926. /**
  927. * Copyright (c) Meta Platforms, Inc. and affiliates.
  928. *
  929. * This source code is licensed under the MIT license found in the
  930. * LICENSE file in the root directory of this source tree.
  931. */
  932. const formatTrailingSpaces = (line, trailingSpaceFormatter) => line.replace(/\s+$/, match => trailingSpaceFormatter(match));
  933. const printDiffLine = (line, isFirstOrLast, color, indicator, trailingSpaceFormatter, emptyFirstOrLastLinePlaceholder) => line.length === 0 ? indicator === ' ' ? isFirstOrLast && emptyFirstOrLastLinePlaceholder.length > 0 ? color(`${indicator} ${emptyFirstOrLastLinePlaceholder}`) : '' : color(indicator) : color(`${indicator} ${formatTrailingSpaces(line, trailingSpaceFormatter)}`);
  934. const printDeleteLine = (line, isFirstOrLast, {
  935. aColor,
  936. aIndicator,
  937. changeLineTrailingSpaceColor,
  938. emptyFirstOrLastLinePlaceholder
  939. }) => printDiffLine(line, isFirstOrLast, aColor, aIndicator, changeLineTrailingSpaceColor, emptyFirstOrLastLinePlaceholder);
  940. const printInsertLine = (line, isFirstOrLast, {
  941. bColor,
  942. bIndicator,
  943. changeLineTrailingSpaceColor,
  944. emptyFirstOrLastLinePlaceholder
  945. }) => printDiffLine(line, isFirstOrLast, bColor, bIndicator, changeLineTrailingSpaceColor, emptyFirstOrLastLinePlaceholder);
  946. const printCommonLine = (line, isFirstOrLast, {
  947. commonColor,
  948. commonIndicator,
  949. commonLineTrailingSpaceColor,
  950. emptyFirstOrLastLinePlaceholder
  951. }) => printDiffLine(line, isFirstOrLast, commonColor, commonIndicator, commonLineTrailingSpaceColor, emptyFirstOrLastLinePlaceholder);
  952. // In GNU diff format, indexes are one-based instead of zero-based.
  953. const createPatchMark = (aStart, aEnd, bStart, bEnd, {
  954. patchColor
  955. }) => patchColor(`@@ -${aStart + 1},${aEnd - aStart} +${bStart + 1},${bEnd - bStart} @@`);
  956. // jest --no-expand
  957. //
  958. // Given array of aligned strings with inverse highlight formatting,
  959. // return joined lines with diff formatting (and patch marks, if needed).
  960. const joinAlignedDiffsNoExpand = (diffs, options) => {
  961. const iLength = diffs.length;
  962. const nContextLines = options.contextLines;
  963. const nContextLines2 = nContextLines + nContextLines;
  964. // First pass: count output lines and see if it has patches.
  965. let jLength = iLength;
  966. let hasExcessAtStartOrEnd = false;
  967. let nExcessesBetweenChanges = 0;
  968. let i = 0;
  969. while (i !== iLength) {
  970. const iStart = i;
  971. while (i !== iLength && diffs[i][0] === _cleanupSemantic.DIFF_EQUAL) {
  972. i += 1;
  973. }
  974. if (iStart !== i) {
  975. if (iStart === 0) {
  976. // at start
  977. if (i > nContextLines) {
  978. jLength -= i - nContextLines; // subtract excess common lines
  979. hasExcessAtStartOrEnd = true;
  980. }
  981. } else if (i === iLength) {
  982. // at end
  983. const n = i - iStart;
  984. if (n > nContextLines) {
  985. jLength -= n - nContextLines; // subtract excess common lines
  986. hasExcessAtStartOrEnd = true;
  987. }
  988. } else {
  989. // between changes
  990. const n = i - iStart;
  991. if (n > nContextLines2) {
  992. jLength -= n - nContextLines2; // subtract excess common lines
  993. nExcessesBetweenChanges += 1;
  994. }
  995. }
  996. }
  997. while (i !== iLength && diffs[i][0] !== _cleanupSemantic.DIFF_EQUAL) {
  998. i += 1;
  999. }
  1000. }
  1001. const hasPatch = nExcessesBetweenChanges !== 0 || hasExcessAtStartOrEnd;
  1002. if (nExcessesBetweenChanges !== 0) {
  1003. jLength += nExcessesBetweenChanges + 1; // add patch lines
  1004. } else if (hasExcessAtStartOrEnd) {
  1005. jLength += 1; // add patch line
  1006. }
  1007. const jLast = jLength - 1;
  1008. const lines = [];
  1009. let jPatchMark = 0; // index of placeholder line for current patch mark
  1010. if (hasPatch) {
  1011. lines.push(''); // placeholder line for first patch mark
  1012. }
  1013. // Indexes of expected or received lines in current patch:
  1014. let aStart = 0;
  1015. let bStart = 0;
  1016. let aEnd = 0;
  1017. let bEnd = 0;
  1018. const pushCommonLine = line => {
  1019. const j = lines.length;
  1020. lines.push(printCommonLine(line, j === 0 || j === jLast, options));
  1021. aEnd += 1;
  1022. bEnd += 1;
  1023. };
  1024. const pushDeleteLine = line => {
  1025. const j = lines.length;
  1026. lines.push(printDeleteLine(line, j === 0 || j === jLast, options));
  1027. aEnd += 1;
  1028. };
  1029. const pushInsertLine = line => {
  1030. const j = lines.length;
  1031. lines.push(printInsertLine(line, j === 0 || j === jLast, options));
  1032. bEnd += 1;
  1033. };
  1034. // Second pass: push lines with diff formatting (and patch marks, if needed).
  1035. i = 0;
  1036. while (i !== iLength) {
  1037. let iStart = i;
  1038. while (i !== iLength && diffs[i][0] === _cleanupSemantic.DIFF_EQUAL) {
  1039. i += 1;
  1040. }
  1041. if (iStart !== i) {
  1042. if (iStart === 0) {
  1043. // at beginning
  1044. if (i > nContextLines) {
  1045. iStart = i - nContextLines;
  1046. aStart = iStart;
  1047. bStart = iStart;
  1048. aEnd = aStart;
  1049. bEnd = bStart;
  1050. }
  1051. for (let iCommon = iStart; iCommon !== i; iCommon += 1) {
  1052. pushCommonLine(diffs[iCommon][1]);
  1053. }
  1054. } else if (i === iLength) {
  1055. // at end
  1056. const iEnd = i - iStart > nContextLines ? iStart + nContextLines : i;
  1057. for (let iCommon = iStart; iCommon !== iEnd; iCommon += 1) {
  1058. pushCommonLine(diffs[iCommon][1]);
  1059. }
  1060. } else {
  1061. // between changes
  1062. const nCommon = i - iStart;
  1063. if (nCommon > nContextLines2) {
  1064. const iEnd = iStart + nContextLines;
  1065. for (let iCommon = iStart; iCommon !== iEnd; iCommon += 1) {
  1066. pushCommonLine(diffs[iCommon][1]);
  1067. }
  1068. lines[jPatchMark] = createPatchMark(aStart, aEnd, bStart, bEnd, options);
  1069. jPatchMark = lines.length;
  1070. lines.push(''); // placeholder line for next patch mark
  1071. const nOmit = nCommon - nContextLines2;
  1072. aStart = aEnd + nOmit;
  1073. bStart = bEnd + nOmit;
  1074. aEnd = aStart;
  1075. bEnd = bStart;
  1076. for (let iCommon = i - nContextLines; iCommon !== i; iCommon += 1) {
  1077. pushCommonLine(diffs[iCommon][1]);
  1078. }
  1079. } else {
  1080. for (let iCommon = iStart; iCommon !== i; iCommon += 1) {
  1081. pushCommonLine(diffs[iCommon][1]);
  1082. }
  1083. }
  1084. }
  1085. }
  1086. while (i !== iLength && diffs[i][0] === _cleanupSemantic.DIFF_DELETE) {
  1087. pushDeleteLine(diffs[i][1]);
  1088. i += 1;
  1089. }
  1090. while (i !== iLength && diffs[i][0] === _cleanupSemantic.DIFF_INSERT) {
  1091. pushInsertLine(diffs[i][1]);
  1092. i += 1;
  1093. }
  1094. }
  1095. if (hasPatch) {
  1096. lines[jPatchMark] = createPatchMark(aStart, aEnd, bStart, bEnd, options);
  1097. }
  1098. return lines.join('\n');
  1099. };
  1100. // jest --expand
  1101. //
  1102. // Given array of aligned strings with inverse highlight formatting,
  1103. // return joined lines with diff formatting.
  1104. exports.joinAlignedDiffsNoExpand = joinAlignedDiffsNoExpand;
  1105. const joinAlignedDiffsExpand = (diffs, options) => diffs.map((diff, i, diffs) => {
  1106. const line = diff[1];
  1107. const isFirstOrLast = i === 0 || i === diffs.length - 1;
  1108. switch (diff[0]) {
  1109. case _cleanupSemantic.DIFF_DELETE:
  1110. return printDeleteLine(line, isFirstOrLast, options);
  1111. case _cleanupSemantic.DIFF_INSERT:
  1112. return printInsertLine(line, isFirstOrLast, options);
  1113. default:
  1114. return printCommonLine(line, isFirstOrLast, options);
  1115. }
  1116. }).join('\n');
  1117. exports.joinAlignedDiffsExpand = joinAlignedDiffsExpand;
  1118. /***/ }),
  1119. /***/ "./src/normalizeDiffOptions.ts":
  1120. /***/ ((__unused_webpack_module, exports) => {
  1121. Object.defineProperty(exports, "__esModule", ({
  1122. value: true
  1123. }));
  1124. exports.normalizeDiffOptions = exports.noColor = void 0;
  1125. var _chalk = _interopRequireDefault(require("chalk"));
  1126. function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  1127. /**
  1128. * Copyright (c) Meta Platforms, Inc. and affiliates.
  1129. *
  1130. * This source code is licensed under the MIT license found in the
  1131. * LICENSE file in the root directory of this source tree.
  1132. */
  1133. const noColor = string => string;
  1134. exports.noColor = noColor;
  1135. const DIFF_CONTEXT_DEFAULT = 5;
  1136. const OPTIONS_DEFAULT = {
  1137. aAnnotation: 'Expected',
  1138. aColor: _chalk.default.green,
  1139. aIndicator: '-',
  1140. bAnnotation: 'Received',
  1141. bColor: _chalk.default.red,
  1142. bIndicator: '+',
  1143. changeColor: _chalk.default.inverse,
  1144. changeLineTrailingSpaceColor: noColor,
  1145. commonColor: _chalk.default.dim,
  1146. commonIndicator: ' ',
  1147. commonLineTrailingSpaceColor: noColor,
  1148. compareKeys: undefined,
  1149. contextLines: DIFF_CONTEXT_DEFAULT,
  1150. emptyFirstOrLastLinePlaceholder: '',
  1151. expand: true,
  1152. includeChangeCounts: false,
  1153. omitAnnotationLines: false,
  1154. patchColor: _chalk.default.yellow
  1155. };
  1156. const getCompareKeys = compareKeys => compareKeys && typeof compareKeys === 'function' ? compareKeys : OPTIONS_DEFAULT.compareKeys;
  1157. const getContextLines = contextLines => typeof contextLines === 'number' && Number.isSafeInteger(contextLines) && contextLines >= 0 ? contextLines : DIFF_CONTEXT_DEFAULT;
  1158. // Pure function returns options with all properties.
  1159. const normalizeDiffOptions = (options = {}) => ({
  1160. ...OPTIONS_DEFAULT,
  1161. ...options,
  1162. compareKeys: getCompareKeys(options.compareKeys),
  1163. contextLines: getContextLines(options.contextLines)
  1164. });
  1165. exports.normalizeDiffOptions = normalizeDiffOptions;
  1166. /***/ }),
  1167. /***/ "./src/printDiffs.ts":
  1168. /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
  1169. Object.defineProperty(exports, "__esModule", ({
  1170. value: true
  1171. }));
  1172. exports.diffStringsUnified = exports.diffStringsRaw = void 0;
  1173. var _cleanupSemantic = __webpack_require__("./src/cleanupSemantic.ts");
  1174. var _diffLines = __webpack_require__("./src/diffLines.ts");
  1175. var _diffStrings = _interopRequireDefault(__webpack_require__("./src/diffStrings.ts"));
  1176. var _getAlignedDiffs = _interopRequireDefault(__webpack_require__("./src/getAlignedDiffs.ts"));
  1177. var _normalizeDiffOptions = __webpack_require__("./src/normalizeDiffOptions.ts");
  1178. function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  1179. /**
  1180. * Copyright (c) Meta Platforms, Inc. and affiliates.
  1181. *
  1182. * This source code is licensed under the MIT license found in the
  1183. * LICENSE file in the root directory of this source tree.
  1184. */
  1185. const hasCommonDiff = (diffs, isMultiline) => {
  1186. if (isMultiline) {
  1187. // Important: Ignore common newline that was appended to multiline strings!
  1188. const iLast = diffs.length - 1;
  1189. return diffs.some((diff, i) => diff[0] === _cleanupSemantic.DIFF_EQUAL && (i !== iLast || diff[1] !== '\n'));
  1190. }
  1191. return diffs.some(diff => diff[0] === _cleanupSemantic.DIFF_EQUAL);
  1192. };
  1193. // Compare two strings character-by-character.
  1194. // Format as comparison lines in which changed substrings have inverse colors.
  1195. const diffStringsUnified = (a, b, options) => {
  1196. if (a !== b && a.length > 0 && b.length > 0) {
  1197. const isMultiline = a.includes('\n') || b.includes('\n');
  1198. // getAlignedDiffs assumes that a newline was appended to the strings.
  1199. const diffs = diffStringsRaw(isMultiline ? `${a}\n` : a, isMultiline ? `${b}\n` : b, true // cleanupSemantic
  1200. );
  1201. if (hasCommonDiff(diffs, isMultiline)) {
  1202. const optionsNormalized = (0, _normalizeDiffOptions.normalizeDiffOptions)(options);
  1203. const lines = (0, _getAlignedDiffs.default)(diffs, optionsNormalized.changeColor);
  1204. return (0, _diffLines.printDiffLines)(lines, optionsNormalized);
  1205. }
  1206. }
  1207. // Fall back to line-by-line diff.
  1208. return (0, _diffLines.diffLinesUnified)(a.split('\n'), b.split('\n'), options);
  1209. };
  1210. // Compare two strings character-by-character.
  1211. // Optionally clean up small common substrings, also known as chaff.
  1212. exports.diffStringsUnified = diffStringsUnified;
  1213. const diffStringsRaw = (a, b, cleanup) => {
  1214. const diffs = (0, _diffStrings.default)(a, b);
  1215. if (cleanup) {
  1216. (0, _cleanupSemantic.cleanupSemantic)(diffs); // impure function
  1217. }
  1218. return diffs;
  1219. };
  1220. exports.diffStringsRaw = diffStringsRaw;
  1221. /***/ })
  1222. /******/ });
  1223. /************************************************************************/
  1224. /******/ // The module cache
  1225. /******/ var __webpack_module_cache__ = {};
  1226. /******/
  1227. /******/ // The require function
  1228. /******/ function __webpack_require__(moduleId) {
  1229. /******/ // Check if module is in cache
  1230. /******/ var cachedModule = __webpack_module_cache__[moduleId];
  1231. /******/ if (cachedModule !== undefined) {
  1232. /******/ return cachedModule.exports;
  1233. /******/ }
  1234. /******/ // Create a new module (and put it into the cache)
  1235. /******/ var module = __webpack_module_cache__[moduleId] = {
  1236. /******/ // no module.id needed
  1237. /******/ // no module.loaded needed
  1238. /******/ exports: {}
  1239. /******/ };
  1240. /******/
  1241. /******/ // Execute the module function
  1242. /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
  1243. /******/
  1244. /******/ // Return the exports of the module
  1245. /******/ return module.exports;
  1246. /******/ }
  1247. /******/
  1248. /************************************************************************/
  1249. var __webpack_exports__ = {};
  1250. // This entry needs to be wrapped in an IIFE because it uses a non-standard name for the exports (exports).
  1251. (() => {
  1252. var exports = __webpack_exports__;
  1253. Object.defineProperty(exports, "__esModule", ({
  1254. value: true
  1255. }));
  1256. Object.defineProperty(exports, "DIFF_DELETE", ({
  1257. enumerable: true,
  1258. get: function () {
  1259. return _cleanupSemantic.DIFF_DELETE;
  1260. }
  1261. }));
  1262. Object.defineProperty(exports, "DIFF_EQUAL", ({
  1263. enumerable: true,
  1264. get: function () {
  1265. return _cleanupSemantic.DIFF_EQUAL;
  1266. }
  1267. }));
  1268. Object.defineProperty(exports, "DIFF_INSERT", ({
  1269. enumerable: true,
  1270. get: function () {
  1271. return _cleanupSemantic.DIFF_INSERT;
  1272. }
  1273. }));
  1274. Object.defineProperty(exports, "Diff", ({
  1275. enumerable: true,
  1276. get: function () {
  1277. return _cleanupSemantic.Diff;
  1278. }
  1279. }));
  1280. exports.diff = diff;
  1281. Object.defineProperty(exports, "diffLinesRaw", ({
  1282. enumerable: true,
  1283. get: function () {
  1284. return _diffLines.diffLinesRaw;
  1285. }
  1286. }));
  1287. Object.defineProperty(exports, "diffLinesUnified", ({
  1288. enumerable: true,
  1289. get: function () {
  1290. return _diffLines.diffLinesUnified;
  1291. }
  1292. }));
  1293. Object.defineProperty(exports, "diffLinesUnified2", ({
  1294. enumerable: true,
  1295. get: function () {
  1296. return _diffLines.diffLinesUnified2;
  1297. }
  1298. }));
  1299. Object.defineProperty(exports, "diffStringsRaw", ({
  1300. enumerable: true,
  1301. get: function () {
  1302. return _printDiffs.diffStringsRaw;
  1303. }
  1304. }));
  1305. Object.defineProperty(exports, "diffStringsUnified", ({
  1306. enumerable: true,
  1307. get: function () {
  1308. return _printDiffs.diffStringsUnified;
  1309. }
  1310. }));
  1311. var _chalk = _interopRequireDefault(require("chalk"));
  1312. var _getType = require("@jest/get-type");
  1313. var _prettyFormat = require("pretty-format");
  1314. var _cleanupSemantic = __webpack_require__("./src/cleanupSemantic.ts");
  1315. var _constants = __webpack_require__("./src/constants.ts");
  1316. var _diffLines = __webpack_require__("./src/diffLines.ts");
  1317. var _escapeControlCharacters = __webpack_require__("./src/escapeControlCharacters.ts");
  1318. var _normalizeDiffOptions = __webpack_require__("./src/normalizeDiffOptions.ts");
  1319. var _printDiffs = __webpack_require__("./src/printDiffs.ts");
  1320. function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
  1321. var src_Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
  1322. /**
  1323. * Copyright (c) Meta Platforms, Inc. and affiliates.
  1324. *
  1325. * This source code is licensed under the MIT license found in the
  1326. * LICENSE file in the root directory of this source tree.
  1327. */
  1328. const getCommonMessage = (message, options) => {
  1329. const {
  1330. commonColor
  1331. } = (0, _normalizeDiffOptions.normalizeDiffOptions)(options);
  1332. return commonColor(message);
  1333. };
  1334. const {
  1335. AsymmetricMatcher,
  1336. DOMCollection,
  1337. DOMElement,
  1338. Immutable,
  1339. ReactElement,
  1340. ReactTestComponent
  1341. } = _prettyFormat.plugins;
  1342. const PLUGINS = [ReactTestComponent, ReactElement, DOMElement, DOMCollection, Immutable, AsymmetricMatcher];
  1343. const FORMAT_OPTIONS = {
  1344. plugins: PLUGINS
  1345. };
  1346. const FALLBACK_FORMAT_OPTIONS = {
  1347. callToJSON: false,
  1348. maxDepth: 10,
  1349. plugins: PLUGINS
  1350. };
  1351. // Generate a string that will highlight the difference between two values
  1352. // with green and red. (similar to how github does code diffing)
  1353. // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
  1354. function diff(a, b, options) {
  1355. if (Object.is(a, b)) {
  1356. return getCommonMessage(_constants.NO_DIFF_MESSAGE, options);
  1357. }
  1358. const aType = (0, _getType.getType)(a);
  1359. let expectedType = aType;
  1360. let omitDifference = false;
  1361. if (aType === 'object' && typeof a.asymmetricMatch === 'function') {
  1362. if (a.$$typeof !== src_Symbol.for('jest.asymmetricMatcher')) {
  1363. // Do not know expected type of user-defined asymmetric matcher.
  1364. return null;
  1365. }
  1366. if (typeof a.getExpectedType !== 'function') {
  1367. // For example, expect.anything() matches either null or undefined
  1368. return null;
  1369. }
  1370. expectedType = a.getExpectedType();
  1371. // Primitive types boolean and number omit difference below.
  1372. // For example, omit difference for expect.stringMatching(regexp)
  1373. omitDifference = expectedType === 'string';
  1374. }
  1375. if (expectedType !== (0, _getType.getType)(b)) {
  1376. return ' Comparing two different types of values.' + ` Expected ${_chalk.default.green(expectedType)} but ` + `received ${_chalk.default.red((0, _getType.getType)(b))}.`;
  1377. }
  1378. if (omitDifference) {
  1379. return null;
  1380. }
  1381. switch (aType) {
  1382. case 'string':
  1383. return (0, _diffLines.diffLinesUnified)((0, _escapeControlCharacters.escapeControlCharacters)(a).split('\n'), (0, _escapeControlCharacters.escapeControlCharacters)(b).split('\n'), options);
  1384. case 'boolean':
  1385. case 'number':
  1386. return comparePrimitive(a, b, options);
  1387. case 'map':
  1388. return compareObjects(sortMap(a), sortMap(b), options);
  1389. case 'set':
  1390. return compareObjects(sortSet(a), sortSet(b), options);
  1391. default:
  1392. return compareObjects(a, b, options);
  1393. }
  1394. }
  1395. function comparePrimitive(a, b, options) {
  1396. const aFormat = (0, _prettyFormat.format)(a, FORMAT_OPTIONS);
  1397. const bFormat = (0, _prettyFormat.format)(b, FORMAT_OPTIONS);
  1398. return aFormat === bFormat ? getCommonMessage(_constants.NO_DIFF_MESSAGE, options) : (0, _diffLines.diffLinesUnified)(aFormat.split('\n'), bFormat.split('\n'), options);
  1399. }
  1400. function sortMap(map) {
  1401. return new Map([...map].sort());
  1402. }
  1403. function sortSet(set) {
  1404. return new Set([...set].sort());
  1405. }
  1406. function compareObjects(a, b, options) {
  1407. let difference;
  1408. let hasThrown = false;
  1409. try {
  1410. const formatOptions = getFormatOptions(FORMAT_OPTIONS, options);
  1411. difference = getObjectsDifference(a, b, formatOptions, options);
  1412. } catch {
  1413. hasThrown = true;
  1414. }
  1415. const noDiffMessage = getCommonMessage(_constants.NO_DIFF_MESSAGE, options);
  1416. // If the comparison yields no results, compare again but this time
  1417. // without calling `toJSON`. It's also possible that toJSON might throw.
  1418. if (difference === undefined || difference === noDiffMessage) {
  1419. const formatOptions = getFormatOptions(FALLBACK_FORMAT_OPTIONS, options);
  1420. difference = getObjectsDifference(a, b, formatOptions, options);
  1421. if (difference !== noDiffMessage && !hasThrown) {
  1422. difference = `${getCommonMessage(_constants.SIMILAR_MESSAGE, options)}\n\n${difference}`;
  1423. }
  1424. }
  1425. return difference;
  1426. }
  1427. function getFormatOptions(formatOptions, options) {
  1428. const {
  1429. compareKeys
  1430. } = (0, _normalizeDiffOptions.normalizeDiffOptions)(options);
  1431. return {
  1432. ...formatOptions,
  1433. compareKeys
  1434. };
  1435. }
  1436. function getObjectsDifference(a, b, formatOptions, options) {
  1437. const formatOptionsZeroIndent = {
  1438. ...formatOptions,
  1439. indent: 0
  1440. };
  1441. const aCompare = (0, _prettyFormat.format)(a, formatOptionsZeroIndent);
  1442. const bCompare = (0, _prettyFormat.format)(b, formatOptionsZeroIndent);
  1443. if (aCompare === bCompare) {
  1444. return getCommonMessage(_constants.NO_DIFF_MESSAGE, options);
  1445. } else {
  1446. const aDisplay = (0, _prettyFormat.format)(a, formatOptions);
  1447. const bDisplay = (0, _prettyFormat.format)(b, formatOptions);
  1448. return (0, _diffLines.diffLinesUnified2)(aDisplay.split('\n'), bDisplay.split('\n'), aCompare.split('\n'), bCompare.split('\n'), options);
  1449. }
  1450. }
  1451. })();
  1452. module.exports = __webpack_exports__;
  1453. /******/ })()
  1454. ;