@potmot/diff 0.0.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/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/arrayDiff.d.ts +2 -0
- package/dist/deepEquals.d.ts +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +150 -0
- package/dist/index.umd.cjs +1 -0
- package/dist/objectDiff.d.ts +2 -0
- package/dist/type/DeepReadonly.d.ts +7 -0
- package/dist/type/DiffItem.d.ts +61 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 pot-mot
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# @potmot/diff
|
|
2
|
+
|
|
3
|
+
简单的差异对比工具库,支持对象、数组的差异分析。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
- ✨ 深度相等比较 (`deepEquals`)
|
|
8
|
+
- 📊 对象差异分析 (`objectDiff`)
|
|
9
|
+
- 📋 数组差异分析 (`arrayDiff`)
|
|
10
|
+
- 🔁 循环引用检测
|
|
11
|
+
- 📤 TypeScript 类型支持
|
|
12
|
+
|
|
13
|
+
## 安装
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @potmot/diff
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
yarn add @potmot/diff
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add @potmot/diff
|
|
25
|
+
```
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { ArrayDiff } from './type/DiffItem';
|
|
2
|
+
export declare const arrayDiff: <T extends Record<string, unknown>>(prevList: ReadonlyArray<T> | undefined | null, nextList: ReadonlyArray<T> | undefined | null, matchFnList: ((a: T, b: T) => boolean)[], deepMatchFnList?: ((a: any, b: any) => boolean)[], visitedOld?: WeakSet<object>, visitedNew?: WeakSet<object>) => ArrayDiff<T>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const deepEquals: (a: any, b: any) => boolean;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
const j = (t, e) => {
|
|
2
|
+
if (t === e)
|
|
3
|
+
return !0;
|
|
4
|
+
if (t === null || e === null || t === void 0 || e === void 0)
|
|
5
|
+
return t === e;
|
|
6
|
+
if (typeof t != typeof e)
|
|
7
|
+
return !1;
|
|
8
|
+
if (typeof t != "object")
|
|
9
|
+
return t === e;
|
|
10
|
+
if (Array.isArray(t) && Array.isArray(e)) {
|
|
11
|
+
if (t.length !== e.length)
|
|
12
|
+
return !1;
|
|
13
|
+
for (let o = 0; o < t.length; o++)
|
|
14
|
+
if (!j(t[o], e[o]))
|
|
15
|
+
return !1;
|
|
16
|
+
return !0;
|
|
17
|
+
}
|
|
18
|
+
if (Array.isArray(t) || Array.isArray(e))
|
|
19
|
+
return !1;
|
|
20
|
+
const i = Object.keys(t), s = Object.keys(e);
|
|
21
|
+
if (i.length !== s.length)
|
|
22
|
+
return !1;
|
|
23
|
+
for (const o of i)
|
|
24
|
+
if (!s.includes(o) || !j(t[o], e[o]))
|
|
25
|
+
return !1;
|
|
26
|
+
return !0;
|
|
27
|
+
}, v = (t, e, i = [j], s = /* @__PURE__ */ new WeakSet(), o = /* @__PURE__ */ new WeakSet()) => {
|
|
28
|
+
if ((t == null || Object.keys(t).length === 0) && (e == null || Object.keys(e).length === 0))
|
|
29
|
+
return { type: "object" };
|
|
30
|
+
if (t == null || typeof t != "object") {
|
|
31
|
+
const r = {};
|
|
32
|
+
return e != null && typeof e == "object" && Object.keys(e).forEach((f) => {
|
|
33
|
+
const n = f;
|
|
34
|
+
r[n] = {
|
|
35
|
+
propertyName: n,
|
|
36
|
+
value: e[n]
|
|
37
|
+
};
|
|
38
|
+
}), { type: "object", added: r };
|
|
39
|
+
}
|
|
40
|
+
if (e == null || typeof e != "object") {
|
|
41
|
+
const r = {};
|
|
42
|
+
return Object.keys(t).forEach((f) => {
|
|
43
|
+
const n = f;
|
|
44
|
+
r[n] = {
|
|
45
|
+
propertyName: n,
|
|
46
|
+
value: t[n]
|
|
47
|
+
};
|
|
48
|
+
}), { type: "object", deleted: r };
|
|
49
|
+
}
|
|
50
|
+
if (typeof t == "object") {
|
|
51
|
+
if (s.has(t))
|
|
52
|
+
return { type: "circular reference" };
|
|
53
|
+
s.add(t);
|
|
54
|
+
}
|
|
55
|
+
if (typeof e == "object") {
|
|
56
|
+
if (o.has(e))
|
|
57
|
+
return { type: "circular reference" };
|
|
58
|
+
o.add(e);
|
|
59
|
+
}
|
|
60
|
+
const p = { type: "object" }, c = {};
|
|
61
|
+
for (const r in t)
|
|
62
|
+
r in e || (c[r] = {
|
|
63
|
+
propertyName: r,
|
|
64
|
+
value: t[r]
|
|
65
|
+
});
|
|
66
|
+
Object.keys(c).length > 0 && (p.deleted = c);
|
|
67
|
+
const a = {};
|
|
68
|
+
for (const r in e)
|
|
69
|
+
r in t || (a[r] = {
|
|
70
|
+
propertyName: r,
|
|
71
|
+
value: e[r]
|
|
72
|
+
});
|
|
73
|
+
Object.keys(a).length > 0 && (p.added = a);
|
|
74
|
+
const h = {};
|
|
75
|
+
for (const r in t)
|
|
76
|
+
if (r in e) {
|
|
77
|
+
const f = r, n = r;
|
|
78
|
+
if (!j(t[f], e[n])) {
|
|
79
|
+
const y = t[f], d = e[n];
|
|
80
|
+
let u;
|
|
81
|
+
Array.isArray(y) && Array.isArray(d) ? u = x(y, d, i, i) : typeof y == "object" && typeof d == "object" && y !== null && d !== null && y !== void 0 && d !== void 0 && (u = v(y, d, i, s, o)), h[r] = {
|
|
82
|
+
propertyName: r,
|
|
83
|
+
prevValue: t[f],
|
|
84
|
+
nextValue: e[n],
|
|
85
|
+
diff: u
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return Object.keys(h).length > 0 && (p.updated = h), p;
|
|
90
|
+
}, x = (t, e, i, s = [j], o = /* @__PURE__ */ new WeakSet(), p = /* @__PURE__ */ new WeakSet()) => {
|
|
91
|
+
const c = {
|
|
92
|
+
type: "array",
|
|
93
|
+
added: [],
|
|
94
|
+
updated: [],
|
|
95
|
+
deleted: [],
|
|
96
|
+
moved: [],
|
|
97
|
+
equals: []
|
|
98
|
+
};
|
|
99
|
+
if (!t && !e)
|
|
100
|
+
return c;
|
|
101
|
+
if (!t)
|
|
102
|
+
return e?.forEach((r, f) => {
|
|
103
|
+
c.added.push({ data: r, nextIndex: f });
|
|
104
|
+
}), c;
|
|
105
|
+
if (!e)
|
|
106
|
+
return t?.forEach((r, f) => {
|
|
107
|
+
c.deleted.push({ data: r, prevIndex: f });
|
|
108
|
+
}), c;
|
|
109
|
+
const a = new Set(t.map((r, f) => ({ item: r, index: f }))), h = new Set(e.map((r, f) => ({ item: r, index: f })));
|
|
110
|
+
for (const r of i)
|
|
111
|
+
for (const f of a) {
|
|
112
|
+
const { item: n, index: y } = f;
|
|
113
|
+
let d;
|
|
114
|
+
for (const u of h)
|
|
115
|
+
r(n, u.item) && (d = u);
|
|
116
|
+
if (d !== void 0) {
|
|
117
|
+
const { item: u, index: k } = d;
|
|
118
|
+
if (j(n, u))
|
|
119
|
+
y === k ? c.equals.push({
|
|
120
|
+
data: u,
|
|
121
|
+
index: k
|
|
122
|
+
}) : c.moved.push({
|
|
123
|
+
data: u,
|
|
124
|
+
prevIndex: y,
|
|
125
|
+
nextIndex: k
|
|
126
|
+
});
|
|
127
|
+
else {
|
|
128
|
+
const A = Array.isArray(n) && Array.isArray(u) ? x(n, u, s, s, o, p) : typeof n == "object" && typeof u == "object" ? v(n, u, s, o, p) : void 0;
|
|
129
|
+
c.updated.push({
|
|
130
|
+
prevData: n,
|
|
131
|
+
prevIndex: y,
|
|
132
|
+
nextData: u,
|
|
133
|
+
nextIndex: k,
|
|
134
|
+
diff: A
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
a.delete(f), h.delete(d);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return a.forEach(({ item: r, index: f }) => {
|
|
141
|
+
c.deleted.push({ data: r, prevIndex: f });
|
|
142
|
+
}), h.forEach(({ item: r, index: f }) => {
|
|
143
|
+
c.added.push({ data: r, nextIndex: f });
|
|
144
|
+
}), c;
|
|
145
|
+
};
|
|
146
|
+
export {
|
|
147
|
+
x as arrayDiff,
|
|
148
|
+
j as deepEquals,
|
|
149
|
+
v as objectDiff
|
|
150
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(p,u){typeof exports=="object"&&typeof module<"u"?u(exports):typeof define=="function"&&define.amd?define(["exports"],u):(p=typeof globalThis<"u"?globalThis:p||self,u(p["@potmot/diff"]={}))})(this,(function(p){"use strict";const u=(t,e)=>{if(t===e)return!0;if(t===null||e===null||t===void 0||e===void 0)return t===e;if(typeof t!=typeof e)return!1;if(typeof t!="object")return t===e;if(Array.isArray(t)&&Array.isArray(e)){if(t.length!==e.length)return!1;for(let o=0;o<t.length;o++)if(!u(t[o],e[o]))return!1;return!0}if(Array.isArray(t)||Array.isArray(e))return!1;const a=Object.keys(t),s=Object.keys(e);if(a.length!==s.length)return!1;for(const o of a)if(!s.includes(o)||!u(t[o],e[o]))return!1;return!0},v=(t,e,a=[u],s=new WeakSet,o=new WeakSet)=>{if((t==null||Object.keys(t).length===0)&&(e==null||Object.keys(e).length===0))return{type:"object"};if(t==null||typeof t!="object"){const r={};return e!=null&&typeof e=="object"&&Object.keys(e).forEach(f=>{const n=f;r[n]={propertyName:n,value:e[n]}}),{type:"object",added:r}}if(e==null||typeof e!="object"){const r={};return Object.keys(t).forEach(f=>{const n=f;r[n]={propertyName:n,value:t[n]}}),{type:"object",deleted:r}}if(typeof t=="object"){if(s.has(t))return{type:"circular reference"};s.add(t)}if(typeof e=="object"){if(o.has(e))return{type:"circular reference"};o.add(e)}const h={type:"object"},i={};for(const r in t)r in e||(i[r]={propertyName:r,value:t[r]});Object.keys(i).length>0&&(h.deleted=i);const j={};for(const r in e)r in t||(j[r]={propertyName:r,value:e[r]});Object.keys(j).length>0&&(h.added=j);const k={};for(const r in t)if(r in e){const f=r,n=r;if(!u(t[f],e[n])){const y=t[f],c=e[n];let d;Array.isArray(y)&&Array.isArray(c)?d=l(y,c,a,a):typeof y=="object"&&typeof c=="object"&&y!==null&&c!==null&&y!==void 0&&c!==void 0&&(d=v(y,c,a,s,o)),k[r]={propertyName:r,prevValue:t[f],nextValue:e[n],diff:d}}}return Object.keys(k).length>0&&(h.updated=k),h},l=(t,e,a,s=[u],o=new WeakSet,h=new WeakSet)=>{const i={type:"array",added:[],updated:[],deleted:[],moved:[],equals:[]};if(!t&&!e)return i;if(!t)return e?.forEach((r,f)=>{i.added.push({data:r,nextIndex:f})}),i;if(!e)return t?.forEach((r,f)=>{i.deleted.push({data:r,prevIndex:f})}),i;const j=new Set(t.map((r,f)=>({item:r,index:f}))),k=new Set(e.map((r,f)=>({item:r,index:f})));for(const r of a)for(const f of j){const{item:n,index:y}=f;let c;for(const d of k)r(n,d.item)&&(c=d);if(c!==void 0){const{item:d,index:m}=c;if(u(n,d))y===m?i.equals.push({data:d,index:m}):i.moved.push({data:d,prevIndex:y,nextIndex:m});else{const b=Array.isArray(n)&&Array.isArray(d)?l(n,d,s,s,o,h):typeof n=="object"&&typeof d=="object"?v(n,d,s,o,h):void 0;i.updated.push({prevData:n,prevIndex:y,nextData:d,nextIndex:m,diff:b})}j.delete(f),k.delete(c)}}return j.forEach(({item:r,index:f})=>{i.deleted.push({data:r,prevIndex:f})}),k.forEach(({item:r,index:f})=>{i.added.push({data:r,nextIndex:f})}),i};p.arrayDiff=l,p.deepEquals=u,p.objectDiff=v,Object.defineProperty(p,Symbol.toStringTag,{value:"Module"})}));
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { CircularReferenceDiff, ObjectDiff } from './type/DiffItem';
|
|
2
|
+
export declare const objectDiff: <T extends Record<string, unknown>, U extends Record<string, unknown> = T>(oldVal: T | undefined | null, newVal: U | undefined | null, deepMatchFnList?: ((a: any, b: any) => boolean)[], visitedOld?: WeakSet<object>, visitedNew?: WeakSet<object>) => ObjectDiff<T, U> | CircularReferenceDiff;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 深度只读类型 - 将对象及其所有嵌套属性都设置为只读
|
|
3
|
+
* 处理了多种特殊情况,包括数组、函数、Map、Set等
|
|
4
|
+
*/
|
|
5
|
+
export type DeepReadonly<T> = T extends (...args: any[]) => any ? T : T extends ReadonlyMap<infer K, infer V> | Map<infer K, infer V> ? ReadonlyMap<DeepReadonly<K>, DeepReadonly<V>> : T extends ReadonlySet<infer U> | Set<infer U> ? ReadonlySet<DeepReadonly<U>> : T extends ReadonlyArray<infer U> | Array<infer U> ? ReadonlyArray<DeepReadonly<U>> : T extends object ? {
|
|
6
|
+
readonly [K in keyof T]: DeepReadonly<T[K]>;
|
|
7
|
+
} : T;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export type CircularReferenceDiff = {
|
|
2
|
+
type: "circular reference";
|
|
3
|
+
};
|
|
4
|
+
export type ObjectDiff<T extends Record<string, unknown>, U extends Record<string, unknown> = T> = {
|
|
5
|
+
type: "object";
|
|
6
|
+
updated?: {
|
|
7
|
+
[K in keyof T & keyof U]?: PropertyUpdatedDiffItem<T, U, K>;
|
|
8
|
+
};
|
|
9
|
+
added?: {
|
|
10
|
+
[K in Exclude<keyof U, keyof T>]?: PropertyAddedDiffItem<U, K>;
|
|
11
|
+
};
|
|
12
|
+
deleted?: {
|
|
13
|
+
[K in Exclude<keyof T, keyof U>]?: PropertyDeletedDiffItem<T, K>;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export type PropertyAddedDiffItem<T extends Record<string, unknown>, K extends keyof T = keyof T> = {
|
|
17
|
+
propertyName: K;
|
|
18
|
+
value: T[K];
|
|
19
|
+
};
|
|
20
|
+
export type PropertyDeletedDiffItem<T extends Record<string, unknown>, K extends keyof T = keyof T> = {
|
|
21
|
+
propertyName: K;
|
|
22
|
+
value: T[K];
|
|
23
|
+
};
|
|
24
|
+
export type PropertyUpdatedDiffItem<T extends Record<string, unknown>, U extends Record<string, unknown> = T, K extends (keyof T & keyof U) = (keyof T & keyof U)> = {
|
|
25
|
+
propertyName: K;
|
|
26
|
+
prevValue: T[K];
|
|
27
|
+
nextValue: U[K];
|
|
28
|
+
diff?: (T[K] & U[K]) extends Array<infer Item> | ReadonlyArray<infer Item> ? ArrayDiff<Item> : (T[K] & U[K]) extends Record<string, unknown> ? ObjectDiff<T[K] & U[K], T[K] & U[K]> | CircularReferenceDiff : never;
|
|
29
|
+
};
|
|
30
|
+
export type ArrayAddedDiffItem<T> = {
|
|
31
|
+
data: T;
|
|
32
|
+
nextIndex: number;
|
|
33
|
+
};
|
|
34
|
+
export type ArrayUpdatedDiffItem<T> = {
|
|
35
|
+
prevData: T;
|
|
36
|
+
prevIndex: number;
|
|
37
|
+
nextData: T;
|
|
38
|
+
nextIndex: number;
|
|
39
|
+
diff: T extends Array<infer Item> | ReadonlyArray<infer Item> ? ArrayDiff<Item> : T extends Record<string, unknown> ? ObjectDiff<T> | CircularReferenceDiff : never;
|
|
40
|
+
};
|
|
41
|
+
export type ArrayDeletedDiffItem<T> = {
|
|
42
|
+
data: T;
|
|
43
|
+
prevIndex: number;
|
|
44
|
+
};
|
|
45
|
+
export type ArrayMovedDiffItem<T> = {
|
|
46
|
+
data: T;
|
|
47
|
+
prevIndex: number;
|
|
48
|
+
nextIndex: number;
|
|
49
|
+
};
|
|
50
|
+
export type ArrayEqualsDiffItem<T> = {
|
|
51
|
+
data: T;
|
|
52
|
+
index: number;
|
|
53
|
+
};
|
|
54
|
+
export type ArrayDiff<T> = {
|
|
55
|
+
type: "array";
|
|
56
|
+
added: ArrayAddedDiffItem<T>[];
|
|
57
|
+
updated: ArrayUpdatedDiffItem<T>[];
|
|
58
|
+
deleted: ArrayDeletedDiffItem<T>[];
|
|
59
|
+
moved: ArrayMovedDiffItem<T>[];
|
|
60
|
+
equals: ArrayEqualsDiffItem<T>[];
|
|
61
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@potmot/diff",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"description": "Lightweight deep diff tool for JavaScript objects and arrays.",
|
|
6
|
+
"keywords": ["diff"],
|
|
7
|
+
"homepage": "https://github.com/pot-mot/diff/blob/main/README.md",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/pot-mot/diff.git",
|
|
11
|
+
"directory": "dist"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/pot-mot/diff/issues"
|
|
15
|
+
},
|
|
16
|
+
"type": "module",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"author": "potmot",
|
|
19
|
+
"main": "./dist/index.umd.cjs",
|
|
20
|
+
"module": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": "./dist/index.js",
|
|
25
|
+
"require": "./dist/index.umd.cjs"
|
|
26
|
+
},
|
|
27
|
+
"./*": "./*"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"package.json",
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE",
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc & vite build",
|
|
40
|
+
"test": "vitest run"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@tsconfig/node22": "^22.0.5",
|
|
44
|
+
"@types/node": "^25.2.3",
|
|
45
|
+
"typescript": "~5.9.3",
|
|
46
|
+
"vite": "^7.3.1",
|
|
47
|
+
"vite-plugin-dts": "^4.5.4",
|
|
48
|
+
"vitest": "^4.0.18"
|
|
49
|
+
}
|
|
50
|
+
}
|