cmpstr 3.0.2 → 3.0.4
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/CmpStr.esm.js +2228 -4930
- package/dist/CmpStr.esm.js.map +1 -1
- package/dist/CmpStr.esm.min.js +2 -2
- package/dist/CmpStr.esm.min.js.map +1 -1
- package/dist/CmpStr.umd.js +2348 -5026
- package/dist/CmpStr.umd.js.map +1 -1
- package/dist/CmpStr.umd.min.js +2 -2
- package/dist/CmpStr.umd.min.js.map +1 -1
- package/dist/cjs/CmpStr.cjs +10 -404
- package/dist/cjs/CmpStr.cjs.map +1 -1
- package/dist/cjs/CmpStrAsync.cjs +10 -220
- package/dist/cjs/CmpStrAsync.cjs.map +1 -1
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/metric/Cosine.cjs +1 -56
- package/dist/cjs/metric/Cosine.cjs.map +1 -1
- package/dist/cjs/metric/DamerauLevenshtein.cjs +2 -64
- package/dist/cjs/metric/DamerauLevenshtein.cjs.map +1 -1
- package/dist/cjs/metric/DiceSorensen.cjs +1 -56
- package/dist/cjs/metric/DiceSorensen.cjs.map +1 -1
- package/dist/cjs/metric/Hamming.cjs +2 -51
- package/dist/cjs/metric/Hamming.cjs.map +1 -1
- package/dist/cjs/metric/Jaccard.cjs +1 -48
- package/dist/cjs/metric/Jaccard.cjs.map +1 -1
- package/dist/cjs/metric/JaroWinkler.cjs +1 -53
- package/dist/cjs/metric/JaroWinkler.cjs.map +1 -1
- package/dist/cjs/metric/LCS.cjs +1 -54
- package/dist/cjs/metric/LCS.cjs.map +1 -1
- package/dist/cjs/metric/Levenshtein.cjs +2 -60
- package/dist/cjs/metric/Levenshtein.cjs.map +1 -1
- package/dist/cjs/metric/Metric.cjs +2 -262
- package/dist/cjs/metric/Metric.cjs.map +1 -1
- package/dist/cjs/metric/NeedlemanWunsch.cjs +4 -56
- package/dist/cjs/metric/NeedlemanWunsch.cjs.map +1 -1
- package/dist/cjs/metric/SmithWaterman.cjs +4 -58
- package/dist/cjs/metric/SmithWaterman.cjs.map +1 -1
- package/dist/cjs/metric/qGram.cjs +1 -55
- package/dist/cjs/metric/qGram.cjs.map +1 -1
- package/dist/cjs/phonetic/Caverphone.cjs +1 -78
- package/dist/cjs/phonetic/Caverphone.cjs.map +1 -1
- package/dist/cjs/phonetic/Cologne.cjs +1 -43
- package/dist/cjs/phonetic/Cologne.cjs.map +1 -1
- package/dist/cjs/phonetic/Metaphone.cjs +1 -76
- package/dist/cjs/phonetic/Metaphone.cjs.map +1 -1
- package/dist/cjs/phonetic/Phonetic.cjs +1 -261
- package/dist/cjs/phonetic/Phonetic.cjs.map +1 -1
- package/dist/cjs/phonetic/Soundex.cjs +1 -47
- package/dist/cjs/phonetic/Soundex.cjs.map +1 -1
- package/dist/cjs/root.cjs +37 -0
- package/dist/cjs/root.cjs.map +1 -0
- package/dist/cjs/utils/DeepMerge.cjs +8 -75
- package/dist/cjs/utils/DeepMerge.cjs.map +1 -1
- package/dist/cjs/utils/DiffChecker.cjs +2 -190
- package/dist/cjs/utils/DiffChecker.cjs.map +1 -1
- package/dist/cjs/utils/Filter.cjs +1 -112
- package/dist/cjs/utils/Filter.cjs.map +1 -1
- package/dist/cjs/utils/HashTable.cjs +1 -99
- package/dist/cjs/utils/HashTable.cjs.map +1 -1
- package/dist/cjs/utils/Normalizer.cjs +3 -94
- package/dist/cjs/utils/Normalizer.cjs.map +1 -1
- package/dist/cjs/utils/Pool.cjs +1 -105
- package/dist/cjs/utils/Pool.cjs.map +1 -1
- package/dist/cjs/utils/Profiler.cjs +1 -133
- package/dist/cjs/utils/Profiler.cjs.map +1 -1
- package/dist/cjs/utils/Registry.cjs +2 -90
- package/dist/cjs/utils/Registry.cjs.map +1 -1
- package/dist/cjs/utils/TextAnalyzer.cjs +1 -180
- package/dist/cjs/utils/TextAnalyzer.cjs.map +1 -1
- package/dist/esm/CmpStr.mjs +10 -404
- package/dist/esm/CmpStr.mjs.map +1 -1
- package/dist/esm/CmpStrAsync.mjs +10 -220
- package/dist/esm/CmpStrAsync.mjs.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/metric/Cosine.mjs +1 -56
- package/dist/esm/metric/Cosine.mjs.map +1 -1
- package/dist/esm/metric/DamerauLevenshtein.mjs +2 -64
- package/dist/esm/metric/DamerauLevenshtein.mjs.map +1 -1
- package/dist/esm/metric/DiceSorensen.mjs +1 -56
- package/dist/esm/metric/DiceSorensen.mjs.map +1 -1
- package/dist/esm/metric/Hamming.mjs +2 -51
- package/dist/esm/metric/Hamming.mjs.map +1 -1
- package/dist/esm/metric/Jaccard.mjs +1 -48
- package/dist/esm/metric/Jaccard.mjs.map +1 -1
- package/dist/esm/metric/JaroWinkler.mjs +1 -53
- package/dist/esm/metric/JaroWinkler.mjs.map +1 -1
- package/dist/esm/metric/LCS.mjs +1 -54
- package/dist/esm/metric/LCS.mjs.map +1 -1
- package/dist/esm/metric/Levenshtein.mjs +2 -60
- package/dist/esm/metric/Levenshtein.mjs.map +1 -1
- package/dist/esm/metric/Metric.mjs +2 -262
- package/dist/esm/metric/Metric.mjs.map +1 -1
- package/dist/esm/metric/NeedlemanWunsch.mjs +4 -56
- package/dist/esm/metric/NeedlemanWunsch.mjs.map +1 -1
- package/dist/esm/metric/SmithWaterman.mjs +4 -58
- package/dist/esm/metric/SmithWaterman.mjs.map +1 -1
- package/dist/esm/metric/qGram.mjs +1 -55
- package/dist/esm/metric/qGram.mjs.map +1 -1
- package/dist/esm/phonetic/Caverphone.mjs +1 -78
- package/dist/esm/phonetic/Caverphone.mjs.map +1 -1
- package/dist/esm/phonetic/Cologne.mjs +1 -43
- package/dist/esm/phonetic/Cologne.mjs.map +1 -1
- package/dist/esm/phonetic/Metaphone.mjs +1 -76
- package/dist/esm/phonetic/Metaphone.mjs.map +1 -1
- package/dist/esm/phonetic/Phonetic.mjs +1 -261
- package/dist/esm/phonetic/Phonetic.mjs.map +1 -1
- package/dist/esm/phonetic/Soundex.mjs +1 -47
- package/dist/esm/phonetic/Soundex.mjs.map +1 -1
- package/dist/esm/root.mjs +29 -0
- package/dist/esm/root.mjs.map +1 -0
- package/dist/esm/utils/DeepMerge.mjs +8 -76
- package/dist/esm/utils/DeepMerge.mjs.map +1 -1
- package/dist/esm/utils/DiffChecker.mjs +2 -190
- package/dist/esm/utils/DiffChecker.mjs.map +1 -1
- package/dist/esm/utils/Filter.mjs +1 -112
- package/dist/esm/utils/Filter.mjs.map +1 -1
- package/dist/esm/utils/HashTable.mjs +1 -99
- package/dist/esm/utils/HashTable.mjs.map +1 -1
- package/dist/esm/utils/Normalizer.mjs +3 -94
- package/dist/esm/utils/Normalizer.mjs.map +1 -1
- package/dist/esm/utils/Pool.mjs +1 -105
- package/dist/esm/utils/Pool.mjs.map +1 -1
- package/dist/esm/utils/Profiler.mjs +1 -133
- package/dist/esm/utils/Profiler.mjs.map +1 -1
- package/dist/esm/utils/Registry.mjs +2 -90
- package/dist/esm/utils/Registry.mjs.map +1 -1
- package/dist/esm/utils/TextAnalyzer.mjs +1 -180
- package/dist/esm/utils/TextAnalyzer.mjs.map +1 -1
- package/dist/types/index.d.ts +3 -2
- package/dist/types/root.d.ts +38 -0
- package/dist/types/utils/Types.d.ts +1 -0
- package/package.json +15 -9
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// CmpStr v3.0.4 build-74e65a5-250915 by Paul Köhler @komed3 / MIT License
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
require('./metric/Cosine.cjs');
|
|
5
|
+
require('./metric/DamerauLevenshtein.cjs');
|
|
6
|
+
require('./metric/DiceSorensen.cjs');
|
|
7
|
+
require('./metric/Hamming.cjs');
|
|
8
|
+
require('./metric/Jaccard.cjs');
|
|
9
|
+
require('./metric/JaroWinkler.cjs');
|
|
10
|
+
require('./metric/LCS.cjs');
|
|
11
|
+
require('./metric/Levenshtein.cjs');
|
|
12
|
+
require('./metric/NeedlemanWunsch.cjs');
|
|
13
|
+
require('./metric/qGram.cjs');
|
|
14
|
+
require('./metric/SmithWaterman.cjs');
|
|
15
|
+
var Metric = require('./metric/Metric.cjs');
|
|
16
|
+
require('./phonetic/Caverphone.cjs');
|
|
17
|
+
require('./phonetic/Cologne.cjs');
|
|
18
|
+
require('./phonetic/Metaphone.cjs');
|
|
19
|
+
require('./phonetic/Soundex.cjs');
|
|
20
|
+
var Phonetic = require('./phonetic/Phonetic.cjs');
|
|
21
|
+
var DeepMerge = require('./utils/DeepMerge.cjs');
|
|
22
|
+
var Filter = require('./utils/Filter.cjs');
|
|
23
|
+
var HashTable = require('./utils/HashTable.cjs');
|
|
24
|
+
var Pool = require('./utils/Pool.cjs');
|
|
25
|
+
var Profiler = require('./utils/Profiler.cjs');
|
|
26
|
+
|
|
27
|
+
exports.Metric = Metric.Metric;
|
|
28
|
+
exports.MetricRegistry = Metric.MetricRegistry;
|
|
29
|
+
exports.Phonetic = Phonetic.Phonetic;
|
|
30
|
+
exports.PhoneticMappingRegistry = Phonetic.PhoneticMappingRegistry;
|
|
31
|
+
exports.PhoneticRegistry = Phonetic.PhoneticRegistry;
|
|
32
|
+
exports.DeepMerge = DeepMerge;
|
|
33
|
+
exports.Filter = Filter.Filter;
|
|
34
|
+
exports.HashTable = HashTable.HashTable;
|
|
35
|
+
exports.Pool = Pool.Pool;
|
|
36
|
+
exports.Profiler = Profiler.Profiler;
|
|
37
|
+
//# sourceMappingURL=root.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1,96 +1,40 @@
|
|
|
1
|
-
// CmpStr v3.0.
|
|
1
|
+
// CmpStr v3.0.4 build-74e65a5-250915 by Paul Köhler @komed3 / MIT License
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Deep Merge Utility
|
|
6
|
-
* src/utils/DeepMerge.ts
|
|
7
|
-
*
|
|
8
|
-
* This module provides utility functions for deep merging objects, getting values by path,
|
|
9
|
-
* and setting values by path in a deeply nested object structure.
|
|
10
|
-
*
|
|
11
|
-
* It supports dot and bracket notation (e.g. `a.b[0].c`) as well as escaped keys.
|
|
12
|
-
*
|
|
13
|
-
* Included functions:
|
|
14
|
-
* - `get`: Retrieve a deeply nested value by path
|
|
15
|
-
* - `set`: Assign a value to a nested path
|
|
16
|
-
* - `merge`: Deeply merge two objects
|
|
17
|
-
* - `has`: Check whether a path exists
|
|
18
|
-
* - `rmv`: Delete a value at a path
|
|
19
|
-
*
|
|
20
|
-
* @module Utils/DeepMerge
|
|
21
|
-
* @author Paul Köhler
|
|
22
|
-
* @license MIT
|
|
23
|
-
*/
|
|
24
|
-
/**
|
|
25
|
-
* Parse a path string into an array of keys.
|
|
26
|
-
*
|
|
27
|
-
* @param {string} p - The path string, e.g. `a.b.c` or `a[0].b`
|
|
28
|
-
* @returns {(string|number)[]} - An array of keys, e.g. `['a', 'b', 'c']` or `['a', 0, 'b']`
|
|
29
|
-
*/
|
|
30
4
|
const parse = (p) =>
|
|
31
5
|
p
|
|
32
6
|
.replace(/\[(\d+)]/g, '.$1')
|
|
33
7
|
.split('.')
|
|
34
8
|
.map((s) => (/^\d+$/.test(s) ? +s : s));
|
|
35
|
-
/**
|
|
36
|
-
* Deeply get a value from an object by a path string.
|
|
37
|
-
*
|
|
38
|
-
* @template T - The type of the object to get the value from
|
|
39
|
-
* @param {T} t - The object to get the value from
|
|
40
|
-
* @param {string} path - The path string, e.g. `a.b.c`
|
|
41
|
-
* @param {any} fallback - The default value to return if the path does not exist
|
|
42
|
-
* @returns {T|R|undefined} - The value at the specified path, otherwise the default value
|
|
43
|
-
*/
|
|
44
9
|
function get(t, path, fallback) {
|
|
45
10
|
return parse(path).reduce((o, k) => o?.[k] ?? fallback, t);
|
|
46
11
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* @param {any} value - The value to set at the specified path
|
|
54
|
-
* @returns {T} - The modified object with the value set at the specified path
|
|
55
|
-
* @throws {Error} - Throws an error if the key is not a valid identifier
|
|
56
|
-
*/
|
|
12
|
+
function has(t, path) {
|
|
13
|
+
return (
|
|
14
|
+
parse(path).reduce((o, k) => (o && k in o ? o[k] : undefined), t) !==
|
|
15
|
+
undefined
|
|
16
|
+
);
|
|
17
|
+
}
|
|
57
18
|
function set(t, path, value) {
|
|
58
|
-
// If the path is empty, return the value
|
|
59
19
|
if (path === '') return value;
|
|
60
|
-
// Split the path into the first key and the rest of the path
|
|
61
20
|
const [k, ...r] = parse(path);
|
|
62
|
-
// Throw an error if the key is not a valid identifier
|
|
63
21
|
if (t !== undefined && (typeof t !== 'object' || t === null))
|
|
64
22
|
throw Error(`Cannot set property <${k}> of <${JSON.stringify(t)}>`);
|
|
65
|
-
// Assign the value to the specified key in the object
|
|
66
23
|
return Object.assign(
|
|
67
24
|
t ?? (typeof k === 'number' ? [] : Object.create(null)),
|
|
68
25
|
{ [k]: set(t?.[k], r.join('.'), value) }
|
|
69
26
|
);
|
|
70
27
|
}
|
|
71
|
-
/**
|
|
72
|
-
* Deeply merge two objects, where the second object overrides the first.
|
|
73
|
-
*
|
|
74
|
-
* @template T - The type of the object to get the value from
|
|
75
|
-
* @param {T} t - The target object to merge into
|
|
76
|
-
* @param {T} o - The source object to merge from
|
|
77
|
-
* @param {boolean} [mergeUndefined=false] - Whether to merge undefined values
|
|
78
|
-
* @returns {T} - The merged object
|
|
79
|
-
*/
|
|
80
28
|
function merge(
|
|
81
29
|
t = Object.create(null),
|
|
82
30
|
o = Object.create(null),
|
|
83
31
|
mergeUndefined = false
|
|
84
32
|
) {
|
|
85
|
-
// Iterate over the keys of the source object and merge them into the target object
|
|
86
33
|
return (
|
|
87
34
|
Object.keys(o).forEach((k) => {
|
|
88
35
|
const val = o[k];
|
|
89
|
-
// If the value is undefined and mergeUndefined is false, skip it
|
|
90
36
|
if (!mergeUndefined && val === undefined) return;
|
|
91
|
-
// Skip dangerous property names to prevent prototype pollution
|
|
92
37
|
if (k === '__proto__' || k === 'constructor') return;
|
|
93
|
-
// If the value is an object and not an array, recursively merge it
|
|
94
38
|
t[k] =
|
|
95
39
|
typeof val === 'object' && !Array.isArray(val)
|
|
96
40
|
? merge(
|
|
@@ -104,26 +48,14 @@ function merge(
|
|
|
104
48
|
t
|
|
105
49
|
);
|
|
106
50
|
}
|
|
107
|
-
/**
|
|
108
|
-
* Delete a value at a specified path in an object.
|
|
109
|
-
*
|
|
110
|
-
* @template T - The type of the object to get the value from
|
|
111
|
-
* @param {T} t - The object to delete the value from
|
|
112
|
-
* @param {string} path - The path string, e.g. `a.b.c`
|
|
113
|
-
* @param {boolean} [preserveEmpty=false] - Whether to preserve empty objects/arrays
|
|
114
|
-
* @returns {T} - The modified object with the value deleted at the specified path
|
|
115
|
-
*/
|
|
116
51
|
function rmv(t, path, preserveEmpty = false) {
|
|
117
52
|
const r = (o, k, i = 0) => {
|
|
118
53
|
const key = k[i];
|
|
119
|
-
// Delete the key if it is not an object or if it is the last key in the path
|
|
120
54
|
if (!o || typeof o !== 'object') return false;
|
|
121
55
|
if (i === k.length - 1) return delete o[key];
|
|
122
56
|
if (!r(o[key], k, i + 1)) return false;
|
|
123
|
-
// If preserveEmpty is false, check if the object or array is empty
|
|
124
57
|
if (!preserveEmpty) {
|
|
125
58
|
const val = o[key];
|
|
126
|
-
// If the value is an empty array or object, delete the key
|
|
127
59
|
if (
|
|
128
60
|
typeof val === 'object' &&
|
|
129
61
|
((Array.isArray(val) && val.every((v) => v == null)) ||
|
|
@@ -138,6 +70,7 @@ function rmv(t, path, preserveEmpty = false) {
|
|
|
138
70
|
}
|
|
139
71
|
|
|
140
72
|
exports.get = get;
|
|
73
|
+
exports.has = has;
|
|
141
74
|
exports.merge = merge;
|
|
142
75
|
exports.rmv = rmv;
|
|
143
76
|
exports.set = set;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeepMerge.cjs","sources":["../../../src/utils/DeepMerge.ts"],"sourcesContent":[null],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"DeepMerge.cjs","sources":["../../../src/utils/DeepMerge.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -1,57 +1,15 @@
|
|
|
1
|
-
// CmpStr v3.0.
|
|
1
|
+
// CmpStr v3.0.4 build-74e65a5-250915 by Paul Köhler @komed3 / MIT License
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* DiffChecker Utility
|
|
6
|
-
* src/utils/DiffChecker.ts
|
|
7
|
-
*
|
|
8
|
-
* The DiffChecker class provides a robust and efficient utility for comparing two
|
|
9
|
-
* texts and extracting their differences (full lines or word mode). It supports
|
|
10
|
-
* context-aware grouping of changes, unified diff output (with CLI color or ASCII
|
|
11
|
-
* markup), and detailed change magnitude metrics. The class is highly configurable,
|
|
12
|
-
* allowing users to choose the diff granularity, case sensitivity, context lines,
|
|
13
|
-
* grouping, and output style. It is suitable for text comparison, code review
|
|
14
|
-
* tools, document versioning, and any application requiring precise and human-
|
|
15
|
-
* readable difference reporting.
|
|
16
|
-
*
|
|
17
|
-
* Features:
|
|
18
|
-
* - Line and word-based diffing
|
|
19
|
-
* - Case-insensitive comparison option
|
|
20
|
-
* - Context lines and grouping of adjacent changes
|
|
21
|
-
* - Unified diff output (ASCII or colored CLI)
|
|
22
|
-
* - Highlighting of changed segments within lines
|
|
23
|
-
* - Change magnitude calculation (relative to group or line)
|
|
24
|
-
* - Expand-all mode for full file context
|
|
25
|
-
*
|
|
26
|
-
* @module Utils/DiffChecker
|
|
27
|
-
* @author Paul Köhler (komed3)
|
|
28
|
-
* @license MIT
|
|
29
|
-
*/
|
|
30
|
-
/**
|
|
31
|
-
* The DiffChecker class provides methods to compare two texts and generate
|
|
32
|
-
* structured diffs, grouped diffs, and unified diff outputs.
|
|
33
|
-
*/
|
|
34
4
|
class DiffChecker {
|
|
35
|
-
// Original input texts and options
|
|
36
5
|
a;
|
|
37
6
|
b;
|
|
38
7
|
options;
|
|
39
|
-
// Computed diff entries and groups
|
|
40
8
|
entries = [];
|
|
41
9
|
grouped = [];
|
|
42
|
-
// Flag to indicate if the diff has already been computed
|
|
43
10
|
diffRun = false;
|
|
44
|
-
/**
|
|
45
|
-
* Constructs a new DiffChecker instance for comparing two texts.
|
|
46
|
-
*
|
|
47
|
-
* @param {string} a - The first (original) text
|
|
48
|
-
* @param {string} b - The second (modified) text
|
|
49
|
-
* @param {DiffOptions} [opt] - Optional diff configuration
|
|
50
|
-
*/
|
|
51
11
|
constructor(a, b, opt = {}) {
|
|
52
|
-
// Set the two texts to compare
|
|
53
12
|
((this.a = a), (this.b = b));
|
|
54
|
-
// Merge default with user-provided options
|
|
55
13
|
this.options = {
|
|
56
14
|
...{
|
|
57
15
|
mode: 'word',
|
|
@@ -65,88 +23,48 @@ class DiffChecker {
|
|
|
65
23
|
},
|
|
66
24
|
...opt
|
|
67
25
|
};
|
|
68
|
-
// Run the diff computation immediately
|
|
69
26
|
this.computeDiff();
|
|
70
27
|
}
|
|
71
|
-
/**
|
|
72
|
-
* Splits both input texts into arrays of lines and returns them
|
|
73
|
-
* with the maximum line count.
|
|
74
|
-
*
|
|
75
|
-
* @returns { linesA: string[], linesB: string[], maxLen: number }
|
|
76
|
-
*/
|
|
77
28
|
text2lines() {
|
|
78
|
-
// Trim and split the input texts into lines
|
|
79
29
|
const linesA = this.a.trim().split(/\r?\n/);
|
|
80
30
|
const linesB = this.b.trim().split(/\r?\n/);
|
|
81
31
|
return { linesA, linesB, maxLen: Math.max(linesA.length, linesB.length) };
|
|
82
32
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Tokenizes a string according to the current diff mode (line or word).
|
|
85
|
-
*
|
|
86
|
-
* @param {string} input - The string to tokenize
|
|
87
|
-
* @returns {string[]} - Array of tokens
|
|
88
|
-
*/
|
|
89
33
|
tokenize(input) {
|
|
90
34
|
const { mode } = this.options;
|
|
91
35
|
switch (mode) {
|
|
92
|
-
// Tokenize by lines
|
|
93
36
|
case 'line':
|
|
94
37
|
return [input];
|
|
95
|
-
// Tokenize by words
|
|
96
38
|
case 'word':
|
|
97
39
|
return input.split(/\s+/);
|
|
98
40
|
}
|
|
99
41
|
}
|
|
100
|
-
/**
|
|
101
|
-
* Concatenates an array of tokens back into a string, respecting the diff mode.
|
|
102
|
-
*
|
|
103
|
-
* @param {string[]} input - Array of tokens
|
|
104
|
-
* @returns {string} - Concatenated string
|
|
105
|
-
*/
|
|
106
42
|
concat(input) {
|
|
107
43
|
const { mode } = this.options;
|
|
108
44
|
return input.join(mode === 'word' ? ' ' : '');
|
|
109
45
|
}
|
|
110
|
-
/**
|
|
111
|
-
* Computes the diff between the two input texts and populates the
|
|
112
|
-
* entries and grouped arrays.
|
|
113
|
-
*/
|
|
114
46
|
computeDiff() {
|
|
115
47
|
if (!this.diffRun) {
|
|
116
|
-
// Get the lines from both texts
|
|
117
48
|
const { linesA, linesB, maxLen } = this.text2lines();
|
|
118
|
-
// Loop through each line and compare them
|
|
119
49
|
for (let i = 0; i < maxLen; i++) {
|
|
120
50
|
const a = linesA[i] || '';
|
|
121
51
|
const b = linesB[i] || '';
|
|
122
|
-
// Perform line diffing
|
|
123
52
|
this.lineDiff(a, b, i);
|
|
124
53
|
}
|
|
125
|
-
// Find groups of adjacent changes
|
|
126
54
|
this.findGroups();
|
|
127
|
-
// Set the diff run flag to true
|
|
128
55
|
this.diffRun = true;
|
|
129
56
|
}
|
|
130
57
|
}
|
|
131
|
-
/**
|
|
132
|
-
* Compares two lines and records their differences at the configured granularity.
|
|
133
|
-
*
|
|
134
|
-
* @param {string} a - Line from the first text
|
|
135
|
-
* @param {string} b - Line from the second text
|
|
136
|
-
* @param {number} line - Line number
|
|
137
|
-
*/
|
|
138
58
|
lineDiff(a, b, line) {
|
|
139
59
|
const { mode, caseInsensitive } = this.options;
|
|
140
60
|
const baseLen = Math.max(a.length, b.length);
|
|
141
61
|
let A = a,
|
|
142
62
|
B = b;
|
|
143
|
-
// If case-insensitive mode is enabled, convert both lines to lowercase
|
|
144
63
|
if (caseInsensitive) ((A = a.toLowerCase()), (B = b.toLowerCase()));
|
|
145
64
|
let diffs = [];
|
|
146
65
|
let delSize = 0,
|
|
147
66
|
insSize = 0;
|
|
148
67
|
if (mode === 'line') {
|
|
149
|
-
// For line mode, compare the entire lines directly
|
|
150
68
|
if (A !== B) {
|
|
151
69
|
diffs.push({
|
|
152
70
|
posA: 0,
|
|
@@ -159,14 +77,11 @@ class DiffChecker {
|
|
|
159
77
|
insSize = b.length;
|
|
160
78
|
}
|
|
161
79
|
} else {
|
|
162
|
-
// For word mode, find precise diffs between tokenized lines
|
|
163
80
|
diffs = this.preciseDiff(a, A, b, B);
|
|
164
|
-
// Calculate total sizes of deletions and insertions
|
|
165
81
|
for (const d of diffs)
|
|
166
82
|
((delSize += d.del.length), (insSize += d.ins.length));
|
|
167
83
|
}
|
|
168
84
|
if (diffs.length) {
|
|
169
|
-
// Add the diff entry for this line
|
|
170
85
|
this.entries.push({
|
|
171
86
|
line,
|
|
172
87
|
diffs,
|
|
@@ -178,24 +93,12 @@ class DiffChecker {
|
|
|
178
93
|
});
|
|
179
94
|
}
|
|
180
95
|
}
|
|
181
|
-
/**
|
|
182
|
-
* Finds all minimal diff blocks between two tokenized strings,
|
|
183
|
-
* returning original text and positions.
|
|
184
|
-
*
|
|
185
|
-
* @param {string} a - Original line (case preserved)
|
|
186
|
-
* @param {string} A - Original line (possibly lowercased)
|
|
187
|
-
* @param {string} b - Modified line (case preserved)
|
|
188
|
-
* @param {string} B - Modified line (possibly lowercased)
|
|
189
|
-
* @returns {DiffEntry[]} - Array of diff entries for this line
|
|
190
|
-
*/
|
|
191
96
|
preciseDiff(a, A, b, B) {
|
|
192
|
-
// Helper function to calculate positions of tokens in the original text
|
|
193
97
|
const posIndex = (t) =>
|
|
194
98
|
t.reduce(
|
|
195
99
|
(p, _, i) => (p.push(i ? p[i - 1] + t[i - 1].length + 1 : 0), p),
|
|
196
100
|
[]
|
|
197
101
|
);
|
|
198
|
-
// Original and tokenized arrays, their lengths and position arrays
|
|
199
102
|
const origA = this.tokenize(a);
|
|
200
103
|
const origB = this.tokenize(b);
|
|
201
104
|
const tokenA = this.tokenize(A);
|
|
@@ -204,15 +107,12 @@ class DiffChecker {
|
|
|
204
107
|
const lenB = tokenB.length;
|
|
205
108
|
const posArrA = posIndex(origA);
|
|
206
109
|
const posArrB = posIndex(origB);
|
|
207
|
-
// Find all matching blocks (LCS)
|
|
208
110
|
const matches = [];
|
|
209
111
|
let ai = 0,
|
|
210
112
|
bi = 0;
|
|
211
113
|
while (ai < lenA && bi < lenB) {
|
|
212
|
-
// If tokens match, find the length of the match
|
|
213
114
|
if (tokenA[ai] === tokenB[bi]) {
|
|
214
115
|
let len = 1;
|
|
215
|
-
// Extend the match as long as tokens continue to match
|
|
216
116
|
while (
|
|
217
117
|
ai + len < lenA &&
|
|
218
118
|
bi + len < lenB &&
|
|
@@ -223,34 +123,25 @@ class DiffChecker {
|
|
|
223
123
|
((ai += len), (bi += len));
|
|
224
124
|
} else {
|
|
225
125
|
let found = false;
|
|
226
|
-
// Look ahead for next sync point (greedy, but avoids long tails)
|
|
227
126
|
for (let offset = 1; offset <= 3 && !found; offset++) {
|
|
228
|
-
// Check if the next token in A matches the current token in B
|
|
229
127
|
if (ai + offset < lenA && tokenA[ai + offset] === tokenB[bi]) {
|
|
230
128
|
matches.push({ ai: ai + offset, bi, len: 1 });
|
|
231
129
|
((ai += offset + 1), (bi += 1), (found = true));
|
|
232
|
-
}
|
|
233
|
-
// Check if the next token in B matches the current token in A
|
|
234
|
-
else if (bi + offset < lenB && tokenA[ai] === tokenB[bi + offset]) {
|
|
130
|
+
} else if (bi + offset < lenB && tokenA[ai] === tokenB[bi + offset]) {
|
|
235
131
|
matches.push({ ai, bi: bi + offset, len: 1 });
|
|
236
132
|
((ai += 1), (bi += offset + 1), (found = true));
|
|
237
133
|
}
|
|
238
134
|
}
|
|
239
|
-
// If no match was found, advance both pointers by one
|
|
240
135
|
if (!found) (ai++, bi++);
|
|
241
136
|
}
|
|
242
137
|
}
|
|
243
|
-
// Walk through tokens and emit diffs between matches
|
|
244
138
|
const diffs = [];
|
|
245
139
|
let i = 0,
|
|
246
140
|
j = 0;
|
|
247
141
|
for (const m of matches) {
|
|
248
|
-
// If there are unmatched tokens before the match, record them
|
|
249
142
|
if (i < m.ai || j < m.bi) {
|
|
250
|
-
// Slice the original arrays to get the unmatched tokens
|
|
251
143
|
const delArr = origA.slice(i, m.ai);
|
|
252
144
|
const insArr = origB.slice(j, m.bi);
|
|
253
|
-
// Push the diff entry for unmatched tokens
|
|
254
145
|
diffs.push({
|
|
255
146
|
posA: posArrA[i] ?? 0,
|
|
256
147
|
posB: posArrB[j] ?? 0,
|
|
@@ -259,15 +150,11 @@ class DiffChecker {
|
|
|
259
150
|
size: insArr.join('').length - delArr.join('').length
|
|
260
151
|
});
|
|
261
152
|
}
|
|
262
|
-
// Advance to after the match
|
|
263
153
|
((i = m.ai + m.len), (j = m.bi + m.len));
|
|
264
154
|
}
|
|
265
|
-
// Tail diffs after the last match
|
|
266
155
|
if (i < lenA || j < lenB) {
|
|
267
|
-
// Slice the original arrays to get the unmatched tokens
|
|
268
156
|
const delArr = origA.slice(i);
|
|
269
157
|
const insArr = origB.slice(j);
|
|
270
|
-
// Push the diff entry for unmatched tokens at the end
|
|
271
158
|
diffs.push({
|
|
272
159
|
posA: posArrA[i] ?? 0,
|
|
273
160
|
posB: posArrB[j] ?? 0,
|
|
@@ -276,25 +163,17 @@ class DiffChecker {
|
|
|
276
163
|
size: insArr.join('').length - delArr.join('').length
|
|
277
164
|
});
|
|
278
165
|
}
|
|
279
|
-
// Remove empty diffs
|
|
280
166
|
return diffs.filter((d) => d.del.length > 0 || d.ins.length > 0);
|
|
281
167
|
}
|
|
282
|
-
/**
|
|
283
|
-
* Groups adjacent changed lines together, including context lines,
|
|
284
|
-
* and calculates group metrics.
|
|
285
|
-
*/
|
|
286
168
|
findGroups() {
|
|
287
169
|
const { contextLines } = this.options;
|
|
288
|
-
// Helper function to add a group to the grouped array
|
|
289
170
|
const addGroup = (group, start, end) => {
|
|
290
|
-
// Calculate total sizes and base length for the group
|
|
291
171
|
const [delSize, insSize, totalSize, baseLen] = [
|
|
292
172
|
'delSize',
|
|
293
173
|
'insSize',
|
|
294
174
|
'totalSize',
|
|
295
175
|
'baseLen'
|
|
296
176
|
].map((k) => group.reduce((sum, e) => sum + e[k], 0));
|
|
297
|
-
// Push the group to the grouped array
|
|
298
177
|
this.grouped.push({
|
|
299
178
|
start,
|
|
300
179
|
end,
|
|
@@ -309,55 +188,32 @@ class DiffChecker {
|
|
|
309
188
|
let group = [];
|
|
310
189
|
let start = 0,
|
|
311
190
|
end = 0;
|
|
312
|
-
// Iterate through each diff entry to find groups
|
|
313
191
|
for (const entry of this.entries) {
|
|
314
192
|
const s = Math.max(0, entry.line - contextLines);
|
|
315
193
|
const e = entry.line + contextLines;
|
|
316
|
-
// If the group is empty or the current entry is adjacent to the last one
|
|
317
194
|
if (!group.length || s <= end + 1) {
|
|
318
|
-
// If this is the first entry, set the start position
|
|
319
195
|
if (!group.length) start = s;
|
|
320
196
|
end = Math.max(end, e);
|
|
321
197
|
group.push(entry);
|
|
322
198
|
} else {
|
|
323
|
-
// If the group is not empty, finalize it and start a new one
|
|
324
199
|
addGroup(group, start, end);
|
|
325
200
|
((group = [entry]), (start = s), (end = e));
|
|
326
201
|
}
|
|
327
202
|
}
|
|
328
|
-
// If there is a remaining group, finalize it
|
|
329
203
|
if (group.length) addGroup(group, start, end);
|
|
330
204
|
}
|
|
331
|
-
/**
|
|
332
|
-
* Calculates the change magnitude string for a group or line.
|
|
333
|
-
*
|
|
334
|
-
* @param {number} del - Number of deleted characters
|
|
335
|
-
* @param {number} ins - Number of inserted characters
|
|
336
|
-
* @param {number} baseLen - Base length for normalization
|
|
337
|
-
* @returns {string} - Magnitude string (e.g. "++-")
|
|
338
|
-
*/
|
|
339
205
|
magnitude(del, ins, baseLen) {
|
|
340
206
|
const { maxMagnitudeSymbols } = this.options;
|
|
341
207
|
const total = del + ins;
|
|
342
|
-
// If there are no changes or base length is zero, return empty string
|
|
343
208
|
if (total === 0 || baseLen === 0) return '';
|
|
344
|
-
// Calculate the length of the magnitude string based on the full length
|
|
345
209
|
const magLen = Math.min(
|
|
346
210
|
maxMagnitudeSymbols,
|
|
347
211
|
Math.max(Math.round((total / baseLen) * maxMagnitudeSymbols), 1)
|
|
348
212
|
);
|
|
349
|
-
// Calculate the number of plus and minus symbols
|
|
350
213
|
const plus = Math.round((ins / total) * magLen);
|
|
351
214
|
const minus = magLen - plus;
|
|
352
|
-
// Return the magnitude string with plus and minus symbols
|
|
353
215
|
return '+'.repeat(plus) + '-'.repeat(minus);
|
|
354
216
|
}
|
|
355
|
-
/**
|
|
356
|
-
* Generates a unified diff output as a string, with optional CLI coloring.
|
|
357
|
-
*
|
|
358
|
-
* @param {boolean} cli - If true, use CLI colors; otherwise, ASCII markup
|
|
359
|
-
* @returns {string} - Unified diff output
|
|
360
|
-
*/
|
|
361
217
|
output(cli) {
|
|
362
218
|
const {
|
|
363
219
|
mode,
|
|
@@ -367,10 +223,8 @@ class DiffChecker {
|
|
|
367
223
|
showChangeMagnitude,
|
|
368
224
|
lineBreak
|
|
369
225
|
} = this.options;
|
|
370
|
-
// Get the lines and maximum length from the input texts
|
|
371
226
|
const { linesA, linesB, maxLen } = this.text2lines();
|
|
372
227
|
const linePad = Math.max(4, maxLen.toString().length);
|
|
373
|
-
// Helper functions for coloring and formatting (ASCII or CLI colored)
|
|
374
228
|
const highlight = (s, ansi) => (cli ? `\x1b[${ansi}m${s}\x1b[0m` : s);
|
|
375
229
|
const cy = (s) => highlight(s, '36');
|
|
376
230
|
const gy = (s) => highlight(s, '90');
|
|
@@ -379,30 +233,21 @@ class DiffChecker {
|
|
|
379
233
|
const ye = (s) => highlight(s, '33');
|
|
380
234
|
const del = (s) => (cli ? `\x1b[37;41m${s}\x1b[31;49m` : `-[${s}]`);
|
|
381
235
|
const ins = (s) => (cli ? `\x1b[37;42m${s}\x1b[32;49m` : `+[${s}]`);
|
|
382
|
-
// Function to output a block of lines with optional header
|
|
383
236
|
const block = (start, end, forced, headerEntry) => {
|
|
384
|
-
// If there is a header entry, output the header
|
|
385
237
|
if (headerEntry) header(headerEntry);
|
|
386
|
-
// Loop through the range and output lines
|
|
387
238
|
for (let i = start; i <= end; i++) line(i, forced ?? i);
|
|
388
239
|
out.push('');
|
|
389
240
|
};
|
|
390
|
-
// Function to output a header for a group or line
|
|
391
241
|
const header = (e) => {
|
|
392
242
|
out.push(
|
|
393
243
|
`${' '.repeat(linePad)} ${cy(`@@ -${e.line + 1},${e.delSize} +${e.line + 1},${e.insSize} @@`)} ${showChangeMagnitude ? ye(e.magnitude) : ''}`
|
|
394
244
|
);
|
|
395
245
|
};
|
|
396
|
-
// Function to output a single line with optional diff highlighting
|
|
397
246
|
const line = (i, forced) => {
|
|
398
|
-
// If the line exists in either text, output it
|
|
399
247
|
if (linesA[i] || linesB[i]) {
|
|
400
|
-
// Find the diff entry for this line, if it exists
|
|
401
248
|
const entry = this.entries.find((e) => e.line === i);
|
|
402
|
-
// Format the line number with padding
|
|
403
249
|
const lineNo = (i + 1).toString().padStart(linePad, ' ');
|
|
404
250
|
if (entry && forced === i) {
|
|
405
|
-
// If there is an entry, output the line with diff highlighting
|
|
406
251
|
out.push(
|
|
407
252
|
`${lineNo} ${rd(`- ${mark(linesA[i], entry.diffs, 'del')}`)}`
|
|
408
253
|
);
|
|
@@ -410,45 +255,33 @@ class DiffChecker {
|
|
|
410
255
|
`${' '.repeat(linePad)} ${gn(`+ ${mark(linesB[i], entry.diffs, 'ins')}`)}`
|
|
411
256
|
);
|
|
412
257
|
} else {
|
|
413
|
-
// If no entry, just output the line without diff (context lines)
|
|
414
258
|
out.push(`${lineNo} ${gy(linesA[i])}`);
|
|
415
259
|
}
|
|
416
260
|
}
|
|
417
261
|
};
|
|
418
|
-
// Function to mark changes in a line based on the diffs
|
|
419
262
|
const mark = (line, diffs, type) => {
|
|
420
|
-
// If there are no diffs or the mode is line, return the line as is
|
|
421
263
|
if (!diffs.length || mode === 'line') return line;
|
|
422
264
|
let res = '',
|
|
423
265
|
idx = 0;
|
|
424
|
-
// Loop through each diff entry and apply the changes
|
|
425
266
|
for (const d of diffs) {
|
|
426
|
-
// Get the position and value based on the type
|
|
427
267
|
const pos = type === 'del' ? d.posA : d.posB;
|
|
428
268
|
const val = type === 'del' ? d.del : d.ins;
|
|
429
|
-
// If the value is empty, skip it
|
|
430
269
|
if (!val) continue;
|
|
431
|
-
// Add the unchanged part of the line before the change
|
|
432
270
|
if (pos > idx) res += line.slice(idx, pos);
|
|
433
|
-
// Add the changed part of the line with appropriate formatting
|
|
434
271
|
res += type === 'del' ? del(val) : ins(val);
|
|
435
272
|
idx = pos + val.length;
|
|
436
273
|
}
|
|
437
|
-
// Return the marked line with any remaining unchanged part
|
|
438
274
|
return res + line.slice(idx);
|
|
439
275
|
};
|
|
440
276
|
let out = [''];
|
|
441
277
|
switch (true) {
|
|
442
|
-
// For expandLines, output the entire file context
|
|
443
278
|
case expandLines:
|
|
444
279
|
block(0, maxLen);
|
|
445
280
|
break;
|
|
446
|
-
// For groupedLines, output each group with its start and end
|
|
447
281
|
case groupedLines:
|
|
448
282
|
for (const group of this.grouped)
|
|
449
283
|
block(group.start, group.end, undefined, group);
|
|
450
284
|
break;
|
|
451
|
-
// For individual lines, output each entry with context lines
|
|
452
285
|
default:
|
|
453
286
|
for (const entry of this.entries)
|
|
454
287
|
block(
|
|
@@ -459,38 +292,17 @@ class DiffChecker {
|
|
|
459
292
|
);
|
|
460
293
|
break;
|
|
461
294
|
}
|
|
462
|
-
// Output the final diff as a string (ASCII or CLI colored)
|
|
463
295
|
return out.join(lineBreak);
|
|
464
296
|
}
|
|
465
|
-
/**
|
|
466
|
-
* Returns the structured diff as an array of DiffLine objects.
|
|
467
|
-
*
|
|
468
|
-
* @returns {DiffLine[]} - Array of line-level diffs
|
|
469
|
-
*/
|
|
470
297
|
getStructuredDiff() {
|
|
471
298
|
return this.entries;
|
|
472
299
|
}
|
|
473
|
-
/**
|
|
474
|
-
* Returns the grouped diff as an array of DiffGroup objects.
|
|
475
|
-
*
|
|
476
|
-
* @returns {DiffGroup[]} - Array of grouped diffs
|
|
477
|
-
*/
|
|
478
300
|
getGroupedDiff() {
|
|
479
301
|
return this.grouped;
|
|
480
302
|
}
|
|
481
|
-
/**
|
|
482
|
-
* Returns the unified diff as a plain ASCII string.
|
|
483
|
-
*
|
|
484
|
-
* @returns {string} - Unified diff (ASCII)
|
|
485
|
-
*/
|
|
486
303
|
getASCIIDiff() {
|
|
487
304
|
return this.output(false);
|
|
488
305
|
}
|
|
489
|
-
/**
|
|
490
|
-
* Returns the unified diff as a CLI-colored string.
|
|
491
|
-
*
|
|
492
|
-
* @returns {string} - Unified diff (CLI colors)
|
|
493
|
-
*/
|
|
494
306
|
getCLIDiff() {
|
|
495
307
|
return this.output(true);
|
|
496
308
|
}
|