From e48cc561e04f4ec571e3d4e986ed19e2ab6e0429 Mon Sep 17 00:00:00 2001 From: ragool Date: Tue, 13 Oct 2026 16:27:08 +0200 Subject: [PATCH 1/2] fix the replacement range logic --- src/tables/html.spec.ts | 30 ++++++++++++++++++++++++++++++ src/tables/html.ts | 16 ++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/tables/html.spec.ts b/src/tables/html.spec.ts index e901e9e..59c31eb 100644 --- a/src/tables/html.spec.ts +++ b/src/tables/html.spec.ts @@ -98,5 +98,35 @@ describe("html", () => { ]), ).to.equal("0YY3456X9"); }); + + it("applies adjacent replacements", () => { + expect( + replaceRanges("aabbcc", [ + { start: 2, end: 4, html: "B" }, + { start: 0, end: 2, html: "A" }, + ]), + ).to.equal("ABcc"); + }); + + it("inserts at an empty range", () => { + expect(replaceRanges("abcd", [{ start: 2, end: 2, html: "X" }])).to.equal("abXcd"); + }); + + it("returns the html when there are no replacements", () => { + expect(replaceRanges("abcd", [])).to.equal("abcd"); + }); + + it("replaces every table and keeps the html between them", () => { + const html = "

a

1

b

2

c

3
"; + const replacements = findElements(html, ["table"]).map((table, index) => ({ + start: table.start, + end: table.end, + html: `
`, + })); + + expect(replaceRanges(html, replacements)).to.equal( + '

a

b

c

', + ); + }); }); }); diff --git a/src/tables/html.ts b/src/tables/html.ts index ed15ba2..49240db 100644 --- a/src/tables/html.ts +++ b/src/tables/html.ts @@ -183,14 +183,22 @@ export function decodeEntities(text: string): string { } /** - * Applies replacements to the html, from the back so the positions stay valid. + * Applies replacements to the html in one pass: the kept parts and the replacements are joined + * once, so a document with many tables is not copied whole for every table. * @param html The html. * @param replacements Non overlapping replacements. * @returns The html with the replacements applied. */ export function replaceRanges(html: string, replacements: Replacement[]): string { - return replacements + const parts: string[] = []; + let position = 0; + replacements .slice() - .sort((first, second) => second.start - first.start) - .reduce((result, replacement) => result.slice(0, replacement.start) + replacement.html + result.slice(replacement.end), html); + .sort((first, second) => first.start - second.start) + .forEach((replacement) => { + parts.push(html.slice(position, replacement.start), replacement.html); + position = replacement.end; + }); + parts.push(html.slice(position)); + return parts.join(""); } From 68c37259d45461d9e69add942def0606006052f8 Mon Sep 17 00:00:00 2001 From: ragool Date: Tue, 13 Oct 2026 16:29:01 +0200 Subject: [PATCH 2/2] bump version --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 625d907..2e600b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@matrixreq/htmldiff", - "version": "2.0.0", + "version": "2.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@matrixreq/htmldiff", - "version": "2.0.0", + "version": "2.0.1", "license": "MIT", "bin": { "htmldiff": "htmldiff-cli.js" diff --git a/package.json b/package.json index f63d149..8ed093a 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@matrixreq/htmldiff", "description": "Diff and markup HTML with and tags", "companyname": "Matrix Requirements", - "version": "2.0.0", + "version": "2.0.1", "keywords": [ "diff", "html",