@createiq/htmldiff 1.0.5-beta.4 → 1.0.5
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 +5 -1039
- package/dist/HtmlDiff.cjs.map +1 -1
- package/dist/HtmlDiff.d.cts +2 -16
- package/dist/HtmlDiff.d.mts +2 -16
- package/dist/HtmlDiff.mjs +5 -1039
- package/dist/HtmlDiff.mjs.map +1 -1
- package/package.json +3 -3
- package/src/HtmlDiff.ts +5 -51
- package/test/HtmlDiff.spec.ts +1 -1
- package/.claude/settings.local.json +0 -15
- package/src/TableDiff.ts +0 -1504
- package/test/HtmlDiff.tables.matrix.spec.ts +0 -367
- package/test/HtmlDiff.tables.spec.ts +0 -1538
- package/test/TableDiff.bench.ts +0 -244
package/test/TableDiff.bench.ts
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
2
|
-
import { bench, describe, expect } from 'vitest'
|
|
3
|
-
import HtmlDiff from '../src/HtmlDiff'
|
|
4
|
-
|
|
5
|
-
// Helpers for generating synthetic table fixtures inline so each scenario
|
|
6
|
-
// is self-describing and doesn't require external HTML files.
|
|
7
|
-
|
|
8
|
-
function buildTable(rows: number, cols: number, cellContent: (r: number, c: number) => string): string {
|
|
9
|
-
const out: string[] = ['<table>']
|
|
10
|
-
for (let r = 0; r < rows; r++) {
|
|
11
|
-
out.push('<tr>')
|
|
12
|
-
for (let c = 0; c < cols; c++) {
|
|
13
|
-
out.push(`<td>${cellContent(r, c)}</td>`)
|
|
14
|
-
}
|
|
15
|
-
out.push('</tr>')
|
|
16
|
-
}
|
|
17
|
-
out.push('</table>')
|
|
18
|
-
return out.join('')
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function defaultCell(r: number, c: number): string {
|
|
22
|
-
return `Row ${r} Col ${c} content with a few words to diff`
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function nTables(n: number, rows: number, cols: number, prefix: string): string {
|
|
26
|
-
const out: string[] = []
|
|
27
|
-
for (let t = 0; t < n; t++) {
|
|
28
|
-
out.push(`<h2>Table ${t}</h2>`)
|
|
29
|
-
out.push(buildTable(rows, cols, (r, c) => `${prefix} t${t} r${r} c${c}`))
|
|
30
|
-
}
|
|
31
|
-
return out.join('')
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const benchOptions = { iterations: 20, warmupIterations: 5 } as const
|
|
35
|
-
|
|
36
|
-
describe('TableDiff — small table baseline', () => {
|
|
37
|
-
const oldHtml = buildTable(10, 3, defaultCell)
|
|
38
|
-
const newHtml = oldHtml
|
|
39
|
-
|
|
40
|
-
bench(
|
|
41
|
-
'small 10x3 — no changes',
|
|
42
|
-
() => {
|
|
43
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
44
|
-
},
|
|
45
|
-
benchOptions
|
|
46
|
-
)
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
describe('TableDiff — small table single edit', () => {
|
|
50
|
-
const oldHtml = buildTable(10, 3, defaultCell)
|
|
51
|
-
const newHtml = buildTable(10, 3, (r, c) => (r === 4 && c === 1 ? 'Edited content here' : defaultCell(r, c)))
|
|
52
|
-
|
|
53
|
-
bench(
|
|
54
|
-
'small 10x3 — single cell edit',
|
|
55
|
-
() => {
|
|
56
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
57
|
-
},
|
|
58
|
-
benchOptions
|
|
59
|
-
)
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
describe('TableDiff — small table content shift across cells', () => {
|
|
63
|
-
// Shift content one cell to the right within each row: tests cell-by-cell
|
|
64
|
-
// positional preprocessing (the most-common row-shift mistake).
|
|
65
|
-
const cells: string[][] = []
|
|
66
|
-
for (let r = 0; r < 10; r++) {
|
|
67
|
-
const row: string[] = []
|
|
68
|
-
for (let c = 0; c < 3; c++) row.push(defaultCell(r, c))
|
|
69
|
-
cells.push(row)
|
|
70
|
-
}
|
|
71
|
-
const oldHtml = buildTable(10, 3, (r, c) => cells[r][c])
|
|
72
|
-
const newHtml = buildTable(10, 3, (r, c) => (c === 0 ? '' : cells[r][c - 1]))
|
|
73
|
-
|
|
74
|
-
bench(
|
|
75
|
-
'small 10x3 — content shifted right',
|
|
76
|
-
() => {
|
|
77
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
78
|
-
},
|
|
79
|
-
benchOptions
|
|
80
|
-
)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
describe('TableDiff — row added', () => {
|
|
84
|
-
const oldHtml = buildTable(10, 3, defaultCell)
|
|
85
|
-
const newHtml = buildTable(11, 3, (r, c) =>
|
|
86
|
-
r < 5 ? defaultCell(r, c) : r === 5 ? `Inserted row col ${c}` : defaultCell(r - 1, c)
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
bench(
|
|
90
|
-
'small 10→11 rows × 3 cols — row added',
|
|
91
|
-
() => {
|
|
92
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
93
|
-
},
|
|
94
|
-
benchOptions
|
|
95
|
-
)
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
describe('TableDiff — column added', () => {
|
|
99
|
-
const oldHtml = buildTable(10, 3, defaultCell)
|
|
100
|
-
const newHtml = buildTable(10, 4, (r, c) =>
|
|
101
|
-
c < 2 ? defaultCell(r, c) : c === 2 ? `Inserted col cell r${r}` : defaultCell(r, c - 1)
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
bench(
|
|
105
|
-
'small 10×3→10×4 — column added',
|
|
106
|
-
() => {
|
|
107
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
108
|
-
},
|
|
109
|
-
benchOptions
|
|
110
|
-
)
|
|
111
|
-
})
|
|
112
|
-
|
|
113
|
-
describe('TableDiff — horizontal merge (colspan)', () => {
|
|
114
|
-
// Old: <tr><td>a</td><td>b</td><td>c</td></tr>
|
|
115
|
-
// New: <tr><td colspan=2>a b</td><td>c</td></tr>
|
|
116
|
-
const oldHtml = '<table><tr><td>Apple</td><td>Banana</td><td>Cherry</td></tr></table>'
|
|
117
|
-
const newHtml = '<table><tr><td colspan="2">Apple Banana</td><td>Cherry</td></tr></table>'
|
|
118
|
-
|
|
119
|
-
bench(
|
|
120
|
-
'horizontal merge — single row colspan',
|
|
121
|
-
() => {
|
|
122
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
123
|
-
},
|
|
124
|
-
benchOptions
|
|
125
|
-
)
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
describe('TableDiff — vertical merge (rowspan)', () => {
|
|
129
|
-
// Old: 3 rows of single cells; New: row 0 has rowspan=3, rows 1-2 empty.
|
|
130
|
-
const oldHtml = '<table><tr><td>One</td></tr><tr><td>Two</td></tr><tr><td>Three</td></tr></table>'
|
|
131
|
-
const newHtml = '<table><tr><td rowspan="3">One Two Three</td></tr><tr></tr><tr></tr></table>'
|
|
132
|
-
|
|
133
|
-
bench(
|
|
134
|
-
'vertical merge — single col rowspan',
|
|
135
|
-
() => {
|
|
136
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
137
|
-
},
|
|
138
|
-
benchOptions
|
|
139
|
-
)
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
describe('TableDiff — medium table single edit', () => {
|
|
143
|
-
const oldHtml = buildTable(50, 10, defaultCell)
|
|
144
|
-
const newHtml = buildTable(50, 10, (r, c) => (r === 25 && c === 5 ? 'Edited mid-table' : defaultCell(r, c)))
|
|
145
|
-
|
|
146
|
-
bench(
|
|
147
|
-
'medium 50x10 — single edit',
|
|
148
|
-
() => {
|
|
149
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
150
|
-
},
|
|
151
|
-
benchOptions
|
|
152
|
-
)
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
describe('TableDiff — large table stress test', () => {
|
|
156
|
-
const oldHtml = buildTable(500, 5, defaultCell)
|
|
157
|
-
const newHtml = buildTable(500, 5, (r, c) => (r === 250 && c === 2 ? 'Edited deep row' : defaultCell(r, c)))
|
|
158
|
-
|
|
159
|
-
bench(
|
|
160
|
-
'large 500x5 — single edit',
|
|
161
|
-
() => {
|
|
162
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
163
|
-
},
|
|
164
|
-
{ iterations: 5, warmupIterations: 2 }
|
|
165
|
-
)
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
describe('TableDiff — wide table stress test', () => {
|
|
169
|
-
const oldHtml = buildTable(5, 50, defaultCell)
|
|
170
|
-
const newHtml = buildTable(5, 50, (r, c) => (r === 2 && c === 25 ? 'Edited wide table' : defaultCell(r, c)))
|
|
171
|
-
|
|
172
|
-
bench(
|
|
173
|
-
'wide 5x50 — single edit',
|
|
174
|
-
() => {
|
|
175
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
176
|
-
},
|
|
177
|
-
benchOptions
|
|
178
|
-
)
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
describe('TableDiff — many tables in one input', () => {
|
|
182
|
-
const oldHtml = nTables(5, 10, 3, 'old')
|
|
183
|
-
// Edit one cell in each of the 5 tables to exercise preprocessTables iteration.
|
|
184
|
-
const newHtml = nTables(5, 10, 3, 'new')
|
|
185
|
-
|
|
186
|
-
bench(
|
|
187
|
-
'5 tables × 10x3 — all-cells edited',
|
|
188
|
-
() => {
|
|
189
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
190
|
-
},
|
|
191
|
-
benchOptions
|
|
192
|
-
)
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
describe('TableDiff — real-world fixture (input1 vs input2)', () => {
|
|
196
|
-
const input1 = readFileSync('test/input1.html', 'utf8')
|
|
197
|
-
const input2 = readFileSync('test/input2.html', 'utf8')
|
|
198
|
-
|
|
199
|
-
bench(
|
|
200
|
-
'input1.html vs input2.html',
|
|
201
|
-
() => {
|
|
202
|
-
expect(HtmlDiff.execute(input1, input2)).toBeTruthy()
|
|
203
|
-
},
|
|
204
|
-
{ iterations: 3, warmupIterations: 1 }
|
|
205
|
-
)
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
describe('TableDiff — pathological 100x100 sparse edits', () => {
|
|
209
|
-
// 100x100 = 10,000 cells. Sparse edits (5 cells) — checks per-cell
|
|
210
|
-
// recursion overhead and lcsAlign DP cost when dimensions match.
|
|
211
|
-
const oldHtml = buildTable(100, 100, defaultCell)
|
|
212
|
-
const newHtml = buildTable(100, 100, (r, c) => {
|
|
213
|
-
const edited =
|
|
214
|
-
(r === 0 && c === 0) ||
|
|
215
|
-
(r === 50 && c === 50) ||
|
|
216
|
-
(r === 99 && c === 99) ||
|
|
217
|
-
(r === 25 && c === 75) ||
|
|
218
|
-
(r === 75 && c === 25)
|
|
219
|
-
return edited ? `Edited r${r} c${c}` : defaultCell(r, c)
|
|
220
|
-
})
|
|
221
|
-
|
|
222
|
-
bench(
|
|
223
|
-
'100x100 — sparse edits (same dimensions, positional path)',
|
|
224
|
-
() => {
|
|
225
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
226
|
-
},
|
|
227
|
-
{ iterations: 3, warmupIterations: 1 }
|
|
228
|
-
)
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
describe('TableDiff — pathological 100x100 row-LCS', () => {
|
|
232
|
-
// 100x100 with a row deletion — forces lcsAlign over 100 row keys, each
|
|
233
|
-
// ~3-4KB long after content. Tests row-key whitespace-normalization cost.
|
|
234
|
-
const oldHtml = buildTable(100, 100, defaultCell)
|
|
235
|
-
const newHtml = buildTable(99, 100, (r, c) => defaultCell(r < 50 ? r : r + 1, c))
|
|
236
|
-
|
|
237
|
-
bench(
|
|
238
|
-
'100x100 → 99x100 — row deleted (row-LCS path)',
|
|
239
|
-
() => {
|
|
240
|
-
expect(HtmlDiff.execute(oldHtml, newHtml)).toBeTruthy()
|
|
241
|
-
},
|
|
242
|
-
{ iterations: 3, warmupIterations: 1 }
|
|
243
|
-
)
|
|
244
|
-
})
|