@createiq/htmldiff 1.0.5-beta.3 → 1.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/HtmlDiff.cjs CHANGED
@@ -411,7 +411,7 @@ function diffPositionalTable(oldHtml, newHtml, oldTable, newTable, diffCell) {
411
411
  * sides.
412
412
  */
413
413
  function diffStructurallyAlignedTable(oldHtml, newHtml, oldTable, newTable, diffCell) {
414
- const alignment = pairSimilarUnmatchedRows(lcsAlign(oldTable.rows.map((row) => rowKey(oldHtml, row)), newTable.rows.map((row) => rowKey(newHtml, row))), oldTable, newTable, oldHtml, newHtml);
414
+ const alignment = orderAlignmentForEmission(pairSimilarUnmatchedRows(lcsAlign(oldTable.rows.map((row) => rowKey(oldHtml, row)), newTable.rows.map((row) => rowKey(newHtml, row))), oldTable, newTable, oldHtml, newHtml));
415
415
  if (newTable.rows.length === 0) return rebuildStructurallyAlignedTable(oldHtml, newHtml, oldTable, newTable, alignment, diffCell);
416
416
  const out = [];
417
417
  out.push(newHtml.slice(newTable.tableStart, newTable.rows[0].rowStart));
@@ -426,6 +426,72 @@ function diffStructurallyAlignedTable(oldHtml, newHtml, oldTable, newTable, diff
426
426
  out.push(newHtml.slice(cursor, newTable.tableEnd));
427
427
  return out.join("");
428
428
  }
429
+ /**
430
+ * Reorders the alignment so emission produces rows in the visually-
431
+ * correct order. Each entry is assigned a fractional "position" in
432
+ * new's flow:
433
+ *
434
+ * • Preserved/paired (oldIdx, newIdx): position = newIdx.
435
+ * • Pure insert (null, newIdx): position = newIdx.
436
+ * • Pure delete (oldIdx, null): position = newIdx-of-preserved-just-
437
+ * before-this-oldIdx + 0.5. Dels at the same gap sort by oldIdx so
438
+ * they appear in old's row order. The +0.5 places dels BEFORE any
439
+ * insert at the same gap (insert at newIdx N1+1 has position N1+1
440
+ * which is > N1+0.5), giving the natural "delete first, insert
441
+ * second" reading order at a replaced position.
442
+ *
443
+ * This handles the full range:
444
+ * • Run of unpaired dels at the start (no preserved predecessor):
445
+ * position -0.5, sorted by oldIdx.
446
+ * • Dels in the middle: positioned right after their preceding
447
+ * preserved row.
448
+ * • Dels at the end (no preserved successor): positioned after the
449
+ * last preserved row.
450
+ *
451
+ * Without this reordering, a run of unpaired deletes at low alignment
452
+ * indices got emitted at cursor = first-new-row position — putting
453
+ * all deletes before any preserved row in the output, regardless of
454
+ * where they came from in old.
455
+ */
456
+ function orderAlignmentForEmission(alignment) {
457
+ const preserved = [];
458
+ for (const a of alignment) if (a.oldIdx !== null && a.newIdx !== null) preserved.push({
459
+ oldIdx: a.oldIdx,
460
+ newIdx: a.newIdx
461
+ });
462
+ preserved.sort((a, b) => a.oldIdx - b.oldIdx);
463
+ function newIdxOfPreservedBefore(oldIdx) {
464
+ let result = -1;
465
+ for (const p of preserved) {
466
+ if (p.oldIdx >= oldIdx) break;
467
+ result = p.newIdx;
468
+ }
469
+ return result;
470
+ }
471
+ const decorated = alignment.map((a, i) => {
472
+ let primary;
473
+ let secondary;
474
+ if (a.newIdx !== null) {
475
+ primary = a.newIdx;
476
+ secondary = a.oldIdx === null ? 1 : 0;
477
+ } else {
478
+ primary = newIdxOfPreservedBefore(a.oldIdx) + .5;
479
+ secondary = a.oldIdx;
480
+ }
481
+ return {
482
+ entry: a,
483
+ primary,
484
+ secondary,
485
+ originalIdx: i
486
+ };
487
+ });
488
+ decorated.sort((a, b) => {
489
+ if (a.primary !== b.primary) return a.primary - b.primary;
490
+ if (a.secondary !== b.secondary) return a.secondary - b.secondary;
491
+ return a.originalIdx - b.originalIdx;
492
+ });
493
+ return decorated.map((d) => d.entry);
494
+ }
429
495
  function rebuildStructurallyAlignedTable(oldHtml, newHtml, oldTable, newTable, alignment, diffCell) {
430
496
  const out = [];
431
497
  out.push(headerSlice(newHtml, newTable, oldHtml, oldTable));