@createiq/htmldiff 1.1.0 → 1.2.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/.claude/settings.local.json +15 -0
- package/README.md +40 -0
- package/dist/HtmlDiff.cjs +1255 -493
- package/dist/HtmlDiff.cjs.map +1 -1
- package/dist/HtmlDiff.d.cts +141 -7
- package/dist/HtmlDiff.d.mts +140 -7
- package/dist/HtmlDiff.mjs +1255 -493
- package/dist/HtmlDiff.mjs.map +1 -1
- package/package.json +1 -1
- package/src/Alignment.ts +349 -0
- package/src/HtmlDiff.ts +323 -33
- package/src/HtmlScanner.ts +200 -0
- package/src/TableDiff.ts +67 -522
- package/src/ThreeWayDiff.ts +223 -0
- package/src/ThreeWayTable.ts +701 -0
- package/src/Utils.ts +34 -2
- package/test/HtmlDiff.analyze.spec.ts +152 -0
- package/test/HtmlDiff.tables.spec.ts +43 -19
- package/test/HtmlDiff.threeWay.spec.ts +175 -0
- package/test/HtmlDiff.threeWay.tables.spec.ts +407 -0
- package/test/TableDiff.bench.ts +39 -0
- package/test/Utils.spec.ts +48 -0
package/dist/HtmlDiff.d.cts
CHANGED
|
@@ -1,4 +1,68 @@
|
|
|
1
|
+
//#region src/Action.d.ts
|
|
2
|
+
declare enum Action {
|
|
3
|
+
Equal = 0,
|
|
4
|
+
Delete = 1,
|
|
5
|
+
Insert = 2,
|
|
6
|
+
None = 3,
|
|
7
|
+
Replace = 4
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/Operation.d.ts
|
|
11
|
+
declare class Operation {
|
|
12
|
+
action: Action;
|
|
13
|
+
startInOld: number;
|
|
14
|
+
endInOld: number;
|
|
15
|
+
startInNew: number;
|
|
16
|
+
endInNew: number;
|
|
17
|
+
constructor(action: Action, startInOld: number, endInOld: number, startInNew: number, endInNew: number);
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
1
20
|
//#region src/HtmlDiff.d.ts
|
|
21
|
+
/**
|
|
22
|
+
* Options for the `HtmlDiff.analyze` static helper.
|
|
23
|
+
*
|
|
24
|
+
* `useProjections` controls structural-tag normalisation:
|
|
25
|
+
* - `undefined` → use the same heuristic as `build()` (per-call decision)
|
|
26
|
+
* - `true` → force projection on (skipped if either side has no content)
|
|
27
|
+
* - `false` → force projection off (diff runs on raw word arrays)
|
|
28
|
+
* Composers of multiple analyses (e.g. three-way diff) MUST pass the
|
|
29
|
+
* same explicit boolean to all calls so shared inputs tokenise
|
|
30
|
+
* identically across analyses.
|
|
31
|
+
*
|
|
32
|
+
* The remaining options mirror the per-instance fields on `HtmlDiff`
|
|
33
|
+
* itself — they exist on the options bag because `analyze` constructs
|
|
34
|
+
* the inner instance internally.
|
|
35
|
+
*/
|
|
36
|
+
interface AnalyzeOptions {
|
|
37
|
+
useProjections?: boolean;
|
|
38
|
+
blockExpressions?: readonly RegExp[];
|
|
39
|
+
repeatingWordsAccuracy?: number;
|
|
40
|
+
orphanMatchThreshold?: number;
|
|
41
|
+
ignoreWhitespaceDifferences?: boolean;
|
|
42
|
+
}
|
|
43
|
+
interface AnalyzeResult {
|
|
44
|
+
/** Word array the `operations` index into (projected or raw). */
|
|
45
|
+
readonly oldDiffWords: readonly string[];
|
|
46
|
+
readonly newDiffWords: readonly string[];
|
|
47
|
+
readonly operations: readonly Operation[];
|
|
48
|
+
/** Original WordSplitter output, before any projection. */
|
|
49
|
+
readonly oldOriginalWords: readonly string[];
|
|
50
|
+
readonly newOriginalWords: readonly string[];
|
|
51
|
+
/** Diff-index → original-word-index map; null when projections inactive. */
|
|
52
|
+
readonly oldContentToOriginal: readonly number[] | null;
|
|
53
|
+
readonly newContentToOriginal: readonly number[] | null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Options for `HtmlDiff.executeThreeWay`. Same shape as `AnalyzeOptions`
|
|
57
|
+
* — the values flow unchanged into both internal `analyze` calls so V2's
|
|
58
|
+
* tokenisation stays symmetric. Aliased so future divergence in either
|
|
59
|
+
* direction lives in one place.
|
|
60
|
+
*
|
|
61
|
+
* `useProjections`: when undefined, `executeThreeWay` computes the
|
|
62
|
+
* decision as the conjunction of both pair-wise
|
|
63
|
+
* `evaluateProjectionApplicability` results.
|
|
64
|
+
*/
|
|
65
|
+
type ThreeWayOptions = AnalyzeOptions;
|
|
2
66
|
declare class HtmlDiff {
|
|
3
67
|
/**
|
|
4
68
|
* This value defines balance between speed and memory utilization. The higher it is the faster it works and more memory consumes.
|
|
@@ -21,10 +85,20 @@ declare class HtmlDiff {
|
|
|
21
85
|
* pathological input.
|
|
22
86
|
*/
|
|
23
87
|
private static MaxTablePreprocessDepth;
|
|
88
|
+
/**
|
|
89
|
+
* Mirror cap for the three-way path. The 2-way `MaxTablePreprocessDepth`
|
|
90
|
+
* guards the recursion inside `executeWithContext`; the 3-way path has
|
|
91
|
+
* its own recursion (`executeThreeWay` → `preprocessTablesThreeWay` →
|
|
92
|
+
* `cellDiff` → `executeThreeWay`) which needs its own guard. Once the
|
|
93
|
+
* cap is reached, `executeThreeWay` skips table preprocessing and
|
|
94
|
+
* falls back to the word-level merge — same bail-out semantics as the
|
|
95
|
+
* 2-way path.
|
|
96
|
+
*/
|
|
97
|
+
private static MaxThreeWayDepth;
|
|
24
98
|
private content;
|
|
25
99
|
private newText;
|
|
26
100
|
private oldText;
|
|
27
|
-
private
|
|
101
|
+
private tablePreprocessDepth;
|
|
28
102
|
private specialTagDiffStack;
|
|
29
103
|
private newWords;
|
|
30
104
|
private oldWords;
|
|
@@ -87,12 +161,71 @@ declare class HtmlDiff {
|
|
|
87
161
|
* Initializes a new instance of the class.
|
|
88
162
|
* @param oldText The old text.
|
|
89
163
|
* @param newText The new text.
|
|
90
|
-
* @param tablePreprocessDepth Internal: nested-call depth for table
|
|
91
|
-
* preprocessing. Callers should leave at default (0); the recursive
|
|
92
|
-
* `diffCell` callback in TableDiff bumps it.
|
|
93
164
|
*/
|
|
94
|
-
constructor(oldText: string, newText: string
|
|
95
|
-
static execute(oldText: string, newText: string
|
|
165
|
+
constructor(oldText: string, newText: string);
|
|
166
|
+
static execute(oldText: string, newText: string): string;
|
|
167
|
+
/**
|
|
168
|
+
* Analyse a two-way diff and return its raw building blocks: the word
|
|
169
|
+
* arrays the diff ran against, the operations produced, the original
|
|
170
|
+
* (pre-projection) word arrays, and the mappings from diff-index back
|
|
171
|
+
* to original-word index when structural projection is active.
|
|
172
|
+
* Consumed by `executeThreeWay` so it can compose two diffs by walking
|
|
173
|
+
* their Operation streams.
|
|
174
|
+
*
|
|
175
|
+
* The caller is expected to coordinate `useProjections` symmetrically
|
|
176
|
+
* across composed analyses — if V1↔V2 projects but V2↔V3 doesn't,
|
|
177
|
+
* V2's "new" array in the first analysis won't equal V2's "old" array
|
|
178
|
+
* in the second. `evaluateProjectionApplicability` exposes the same
|
|
179
|
+
* heuristic `build()` uses internally, so the orchestrator can compute
|
|
180
|
+
* a single decision and pass it into every `analyze` call.
|
|
181
|
+
*
|
|
182
|
+
* Table preprocessing is skipped here. Placeholders mutate the input
|
|
183
|
+
* in ways that don't compose across two independent analyses; the
|
|
184
|
+
* 3-way orchestrator handles tables explicitly before calling analyze.
|
|
185
|
+
*/
|
|
186
|
+
static analyze(oldText: string, newText: string, options?: AnalyzeOptions): AnalyzeResult;
|
|
187
|
+
/**
|
|
188
|
+
* Whether content-projection (structural-tag normalisation) would
|
|
189
|
+
* apply to this pair of inputs under `build()`'s default heuristic.
|
|
190
|
+
* Exposed so composers of multiple analyses can compute a symmetric
|
|
191
|
+
* decision before calling `analyze` — see `analyze`'s docstring for
|
|
192
|
+
* why symmetry matters.
|
|
193
|
+
*/
|
|
194
|
+
static evaluateProjectionApplicability(oldText: string, newText: string): boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Three-way HTML diff. Given V1 (the version Me last sent), V2 (the
|
|
197
|
+
* version CP sent back), and V3 (Me's current draft), produces a
|
|
198
|
+
* single attributed HTML output where CP's and Me's changes are
|
|
199
|
+
* distinguished by `data-author` ('cp' or 'me') and matching
|
|
200
|
+
* `class='diffins cp'` / `class='diffdel me'` etc. The "Me rejected
|
|
201
|
+
* CP's proposal" case (Me deleted text CP had inserted) gets a
|
|
202
|
+
* dedicated marker: `data-rejects='cp'` plus `class='... rejects-cp'`.
|
|
203
|
+
*
|
|
204
|
+
* Coordinates the symmetric-projection decision (D1) across both
|
|
205
|
+
* internal `analyze` calls so V2 tokenises identically on each side
|
|
206
|
+
* of the spine. When `useProjections` is left undefined, the decision
|
|
207
|
+
* is the conjunction of both pair-wise heuristics — project iff both
|
|
208
|
+
* pairs would project on their own. Pass an explicit boolean to
|
|
209
|
+
* override.
|
|
210
|
+
*/
|
|
211
|
+
static executeThreeWay(v1: string, v2: string, v3: string, options?: ThreeWayOptions): string;
|
|
212
|
+
private static executeThreeWayWithDepth;
|
|
213
|
+
/**
|
|
214
|
+
* Drives a fresh `HtmlDiff` instance through `insertTag` for ins/del
|
|
215
|
+
* segments and pushes equal segments straight to its `content`
|
|
216
|
+
* buffer. Reusing the instance keeps the formatting-tag stack
|
|
217
|
+
* (`specialTagDiffStack`) coherent across segments — a `<strong>`
|
|
218
|
+
* opened in one segment and closed in another stays balanced.
|
|
219
|
+
*/
|
|
220
|
+
private static emitSegments;
|
|
221
|
+
/**
|
|
222
|
+
* Internal entry point used by the table-cell recursion. Constructs an
|
|
223
|
+
* inner `HtmlDiff`, applies the caller's settings, and bumps the
|
|
224
|
+
* recursion depth — keeping the public constructor signature clean
|
|
225
|
+
* while still threading the configuration that's required for cell-
|
|
226
|
+
* level output to match the top-level call's behaviour.
|
|
227
|
+
*/
|
|
228
|
+
private static executeWithContext;
|
|
96
229
|
/**
|
|
97
230
|
* Builds the HTML diff output
|
|
98
231
|
* @return HTML diff markup
|
|
@@ -185,5 +318,6 @@ declare class HtmlDiff {
|
|
|
185
318
|
private findMatchingBlocks;
|
|
186
319
|
private findMatch;
|
|
187
320
|
}
|
|
188
|
-
|
|
321
|
+
//#endregion
|
|
322
|
+
export { AnalyzeOptions, AnalyzeResult, ThreeWayOptions, HtmlDiff as default };
|
|
189
323
|
//# sourceMappingURL=HtmlDiff.d.cts.map
|
package/dist/HtmlDiff.d.mts
CHANGED
|
@@ -1,4 +1,68 @@
|
|
|
1
|
+
//#region src/Action.d.ts
|
|
2
|
+
declare enum Action {
|
|
3
|
+
Equal = 0,
|
|
4
|
+
Delete = 1,
|
|
5
|
+
Insert = 2,
|
|
6
|
+
None = 3,
|
|
7
|
+
Replace = 4
|
|
8
|
+
}
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region src/Operation.d.ts
|
|
11
|
+
declare class Operation {
|
|
12
|
+
action: Action;
|
|
13
|
+
startInOld: number;
|
|
14
|
+
endInOld: number;
|
|
15
|
+
startInNew: number;
|
|
16
|
+
endInNew: number;
|
|
17
|
+
constructor(action: Action, startInOld: number, endInOld: number, startInNew: number, endInNew: number);
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
1
20
|
//#region src/HtmlDiff.d.ts
|
|
21
|
+
/**
|
|
22
|
+
* Options for the `HtmlDiff.analyze` static helper.
|
|
23
|
+
*
|
|
24
|
+
* `useProjections` controls structural-tag normalisation:
|
|
25
|
+
* - `undefined` → use the same heuristic as `build()` (per-call decision)
|
|
26
|
+
* - `true` → force projection on (skipped if either side has no content)
|
|
27
|
+
* - `false` → force projection off (diff runs on raw word arrays)
|
|
28
|
+
* Composers of multiple analyses (e.g. three-way diff) MUST pass the
|
|
29
|
+
* same explicit boolean to all calls so shared inputs tokenise
|
|
30
|
+
* identically across analyses.
|
|
31
|
+
*
|
|
32
|
+
* The remaining options mirror the per-instance fields on `HtmlDiff`
|
|
33
|
+
* itself — they exist on the options bag because `analyze` constructs
|
|
34
|
+
* the inner instance internally.
|
|
35
|
+
*/
|
|
36
|
+
interface AnalyzeOptions {
|
|
37
|
+
useProjections?: boolean;
|
|
38
|
+
blockExpressions?: readonly RegExp[];
|
|
39
|
+
repeatingWordsAccuracy?: number;
|
|
40
|
+
orphanMatchThreshold?: number;
|
|
41
|
+
ignoreWhitespaceDifferences?: boolean;
|
|
42
|
+
}
|
|
43
|
+
interface AnalyzeResult {
|
|
44
|
+
/** Word array the `operations` index into (projected or raw). */
|
|
45
|
+
readonly oldDiffWords: readonly string[];
|
|
46
|
+
readonly newDiffWords: readonly string[];
|
|
47
|
+
readonly operations: readonly Operation[];
|
|
48
|
+
/** Original WordSplitter output, before any projection. */
|
|
49
|
+
readonly oldOriginalWords: readonly string[];
|
|
50
|
+
readonly newOriginalWords: readonly string[];
|
|
51
|
+
/** Diff-index → original-word-index map; null when projections inactive. */
|
|
52
|
+
readonly oldContentToOriginal: readonly number[] | null;
|
|
53
|
+
readonly newContentToOriginal: readonly number[] | null;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Options for `HtmlDiff.executeThreeWay`. Same shape as `AnalyzeOptions`
|
|
57
|
+
* — the values flow unchanged into both internal `analyze` calls so V2's
|
|
58
|
+
* tokenisation stays symmetric. Aliased so future divergence in either
|
|
59
|
+
* direction lives in one place.
|
|
60
|
+
*
|
|
61
|
+
* `useProjections`: when undefined, `executeThreeWay` computes the
|
|
62
|
+
* decision as the conjunction of both pair-wise
|
|
63
|
+
* `evaluateProjectionApplicability` results.
|
|
64
|
+
*/
|
|
65
|
+
type ThreeWayOptions = AnalyzeOptions;
|
|
2
66
|
declare class HtmlDiff {
|
|
3
67
|
/**
|
|
4
68
|
* This value defines balance between speed and memory utilization. The higher it is the faster it works and more memory consumes.
|
|
@@ -21,10 +85,20 @@ declare class HtmlDiff {
|
|
|
21
85
|
* pathological input.
|
|
22
86
|
*/
|
|
23
87
|
private static MaxTablePreprocessDepth;
|
|
88
|
+
/**
|
|
89
|
+
* Mirror cap for the three-way path. The 2-way `MaxTablePreprocessDepth`
|
|
90
|
+
* guards the recursion inside `executeWithContext`; the 3-way path has
|
|
91
|
+
* its own recursion (`executeThreeWay` → `preprocessTablesThreeWay` →
|
|
92
|
+
* `cellDiff` → `executeThreeWay`) which needs its own guard. Once the
|
|
93
|
+
* cap is reached, `executeThreeWay` skips table preprocessing and
|
|
94
|
+
* falls back to the word-level merge — same bail-out semantics as the
|
|
95
|
+
* 2-way path.
|
|
96
|
+
*/
|
|
97
|
+
private static MaxThreeWayDepth;
|
|
24
98
|
private content;
|
|
25
99
|
private newText;
|
|
26
100
|
private oldText;
|
|
27
|
-
private
|
|
101
|
+
private tablePreprocessDepth;
|
|
28
102
|
private specialTagDiffStack;
|
|
29
103
|
private newWords;
|
|
30
104
|
private oldWords;
|
|
@@ -87,12 +161,71 @@ declare class HtmlDiff {
|
|
|
87
161
|
* Initializes a new instance of the class.
|
|
88
162
|
* @param oldText The old text.
|
|
89
163
|
* @param newText The new text.
|
|
90
|
-
* @param tablePreprocessDepth Internal: nested-call depth for table
|
|
91
|
-
* preprocessing. Callers should leave at default (0); the recursive
|
|
92
|
-
* `diffCell` callback in TableDiff bumps it.
|
|
93
164
|
*/
|
|
94
|
-
constructor(oldText: string, newText: string
|
|
95
|
-
static execute(oldText: string, newText: string
|
|
165
|
+
constructor(oldText: string, newText: string);
|
|
166
|
+
static execute(oldText: string, newText: string): string;
|
|
167
|
+
/**
|
|
168
|
+
* Analyse a two-way diff and return its raw building blocks: the word
|
|
169
|
+
* arrays the diff ran against, the operations produced, the original
|
|
170
|
+
* (pre-projection) word arrays, and the mappings from diff-index back
|
|
171
|
+
* to original-word index when structural projection is active.
|
|
172
|
+
* Consumed by `executeThreeWay` so it can compose two diffs by walking
|
|
173
|
+
* their Operation streams.
|
|
174
|
+
*
|
|
175
|
+
* The caller is expected to coordinate `useProjections` symmetrically
|
|
176
|
+
* across composed analyses — if V1↔V2 projects but V2↔V3 doesn't,
|
|
177
|
+
* V2's "new" array in the first analysis won't equal V2's "old" array
|
|
178
|
+
* in the second. `evaluateProjectionApplicability` exposes the same
|
|
179
|
+
* heuristic `build()` uses internally, so the orchestrator can compute
|
|
180
|
+
* a single decision and pass it into every `analyze` call.
|
|
181
|
+
*
|
|
182
|
+
* Table preprocessing is skipped here. Placeholders mutate the input
|
|
183
|
+
* in ways that don't compose across two independent analyses; the
|
|
184
|
+
* 3-way orchestrator handles tables explicitly before calling analyze.
|
|
185
|
+
*/
|
|
186
|
+
static analyze(oldText: string, newText: string, options?: AnalyzeOptions): AnalyzeResult;
|
|
187
|
+
/**
|
|
188
|
+
* Whether content-projection (structural-tag normalisation) would
|
|
189
|
+
* apply to this pair of inputs under `build()`'s default heuristic.
|
|
190
|
+
* Exposed so composers of multiple analyses can compute a symmetric
|
|
191
|
+
* decision before calling `analyze` — see `analyze`'s docstring for
|
|
192
|
+
* why symmetry matters.
|
|
193
|
+
*/
|
|
194
|
+
static evaluateProjectionApplicability(oldText: string, newText: string): boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Three-way HTML diff. Given V1 (the version Me last sent), V2 (the
|
|
197
|
+
* version CP sent back), and V3 (Me's current draft), produces a
|
|
198
|
+
* single attributed HTML output where CP's and Me's changes are
|
|
199
|
+
* distinguished by `data-author` ('cp' or 'me') and matching
|
|
200
|
+
* `class='diffins cp'` / `class='diffdel me'` etc. The "Me rejected
|
|
201
|
+
* CP's proposal" case (Me deleted text CP had inserted) gets a
|
|
202
|
+
* dedicated marker: `data-rejects='cp'` plus `class='... rejects-cp'`.
|
|
203
|
+
*
|
|
204
|
+
* Coordinates the symmetric-projection decision (D1) across both
|
|
205
|
+
* internal `analyze` calls so V2 tokenises identically on each side
|
|
206
|
+
* of the spine. When `useProjections` is left undefined, the decision
|
|
207
|
+
* is the conjunction of both pair-wise heuristics — project iff both
|
|
208
|
+
* pairs would project on their own. Pass an explicit boolean to
|
|
209
|
+
* override.
|
|
210
|
+
*/
|
|
211
|
+
static executeThreeWay(v1: string, v2: string, v3: string, options?: ThreeWayOptions): string;
|
|
212
|
+
private static executeThreeWayWithDepth;
|
|
213
|
+
/**
|
|
214
|
+
* Drives a fresh `HtmlDiff` instance through `insertTag` for ins/del
|
|
215
|
+
* segments and pushes equal segments straight to its `content`
|
|
216
|
+
* buffer. Reusing the instance keeps the formatting-tag stack
|
|
217
|
+
* (`specialTagDiffStack`) coherent across segments — a `<strong>`
|
|
218
|
+
* opened in one segment and closed in another stays balanced.
|
|
219
|
+
*/
|
|
220
|
+
private static emitSegments;
|
|
221
|
+
/**
|
|
222
|
+
* Internal entry point used by the table-cell recursion. Constructs an
|
|
223
|
+
* inner `HtmlDiff`, applies the caller's settings, and bumps the
|
|
224
|
+
* recursion depth — keeping the public constructor signature clean
|
|
225
|
+
* while still threading the configuration that's required for cell-
|
|
226
|
+
* level output to match the top-level call's behaviour.
|
|
227
|
+
*/
|
|
228
|
+
private static executeWithContext;
|
|
96
229
|
/**
|
|
97
230
|
* Builds the HTML diff output
|
|
98
231
|
* @return HTML diff markup
|
|
@@ -186,5 +319,5 @@ declare class HtmlDiff {
|
|
|
186
319
|
private findMatch;
|
|
187
320
|
}
|
|
188
321
|
//#endregion
|
|
189
|
-
export { HtmlDiff as default };
|
|
322
|
+
export { AnalyzeOptions, AnalyzeResult, ThreeWayOptions, HtmlDiff as default };
|
|
190
323
|
//# sourceMappingURL=HtmlDiff.d.mts.map
|