@codady/utils 0.0.14 → 0.0.16
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/CHANGELOG.md +19 -0
- package/dist/utils.cjs.js +32 -33
- package/dist/utils.cjs.min.js +3 -3
- package/dist/utils.esm.js +32 -33
- package/dist/utils.esm.min.js +3 -3
- package/dist/utils.umd.js +32 -33
- package/dist/utils.umd.min.js +3 -3
- package/dist.zip +0 -0
- package/package.json +1 -1
- package/src/deepClone.js +13 -18
- package/src/deepClone.ts +9 -15
- package/src/deepMerge.js +25 -15
- package/src/deepMerge.ts +70 -31
package/dist/utils.umd.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
/*!
|
|
3
|
-
* @since Last modified: 2025-12-26
|
|
3
|
+
* @since Last modified: 2025-12-26 15:47:37
|
|
4
4
|
* @name Utils for web front-end.
|
|
5
|
-
* @version 0.0.
|
|
5
|
+
* @version 0.0.16
|
|
6
6
|
* @author AXUI development team <3217728223@qq.com>
|
|
7
7
|
* @description This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.
|
|
8
8
|
* @see {@link https://www.axui.cn|Official website}
|
|
@@ -65,16 +65,10 @@
|
|
|
65
65
|
}, options);
|
|
66
66
|
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
67
67
|
if (opts.interceptor && typeof opts.interceptor === 'function') {
|
|
68
|
-
let result = opts.interceptor(data, dataType);
|
|
68
|
+
let result = opts.interceptor({ input: data, type: dataType, parent: opts.parent });
|
|
69
69
|
if ((result ?? false)) {
|
|
70
70
|
// Call onAfterClone if set
|
|
71
|
-
|
|
72
|
-
output: result,
|
|
73
|
-
input: data,
|
|
74
|
-
type: dataType,
|
|
75
|
-
cloned: result !== data,
|
|
76
|
-
parent: opts.parent
|
|
77
|
-
});
|
|
71
|
+
|
|
78
72
|
return result;
|
|
79
73
|
}
|
|
80
74
|
// If interceptor returns null/undefined, continue with normal cloning process
|
|
@@ -88,8 +82,6 @@
|
|
|
88
82
|
let newData, cloned = true;
|
|
89
83
|
if (dataType === 'Object' && opts.cloneObject) {
|
|
90
84
|
const newObj = {}, symbols = Object.getOwnPropertySymbols(data);
|
|
91
|
-
//存储parent对象,在下一次深复制时使用
|
|
92
|
-
opts.parent = data;
|
|
93
85
|
// Clone regular properties
|
|
94
86
|
for (const key in data) {
|
|
95
87
|
//临时保存父对象
|
|
@@ -98,30 +90,27 @@
|
|
|
98
90
|
// Clone Symbol properties
|
|
99
91
|
if (symbols.length > 0) {
|
|
100
92
|
for (const symbol of symbols) {
|
|
101
|
-
newObj[symbol] = deepClone(data[symbol], opts);
|
|
93
|
+
newObj[symbol] = deepClone(data[symbol], { ...opts, parent: data });
|
|
102
94
|
}
|
|
103
95
|
}
|
|
104
96
|
newData = newObj;
|
|
105
97
|
}
|
|
106
98
|
else if (dataType === 'Array' && opts.cloneArray) {
|
|
107
|
-
opts
|
|
108
|
-
newData = data.map(item => deepClone(item, opts));
|
|
99
|
+
newData = data.map(item => deepClone(item, { ...opts, parent: data }));
|
|
109
100
|
}
|
|
110
101
|
else if (dataType === 'Map' && opts.cloneMap) {
|
|
111
102
|
const newMap = new Map();
|
|
112
|
-
opts.parent = data;
|
|
113
103
|
for (const [key, value] of data) {
|
|
114
104
|
// Both Map keys and values need deep cloning
|
|
115
|
-
newMap.set(deepClone(key, opts), deepClone(value, opts));
|
|
105
|
+
newMap.set(deepClone(key, opts), deepClone(value, { ...opts, parent: data }));
|
|
116
106
|
}
|
|
117
107
|
newData = newMap;
|
|
118
108
|
}
|
|
119
109
|
else if (dataType === 'Set' && opts.cloneSet) {
|
|
120
110
|
const newSet = new Set();
|
|
121
|
-
opts.parent = data;
|
|
122
111
|
for (const value of data) {
|
|
123
112
|
// Set values need deep cloning
|
|
124
|
-
newSet.add(deepClone(value, opts));
|
|
113
|
+
newSet.add(deepClone(value, { ...opts, parent: data }));
|
|
125
114
|
}
|
|
126
115
|
newData = newSet;
|
|
127
116
|
}
|
|
@@ -541,8 +530,25 @@
|
|
|
541
530
|
}, opts),
|
|
542
531
|
// Main helper function for recursive merging
|
|
543
532
|
// 递归合并的主辅助函数
|
|
544
|
-
|
|
533
|
+
smartMerger = (target, source, options) => {
|
|
545
534
|
let targetType = getDataType(target), sourceType = getDataType(source), flag = true, type, result;
|
|
535
|
+
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
536
|
+
if (options.interceptor && typeof options.interceptor === 'function') {
|
|
537
|
+
let interceptorResult = options.interceptor({ target, source, parent: options.parent });
|
|
538
|
+
if ((interceptorResult ?? false)) {
|
|
539
|
+
//如果不是返回{target,source},那么直接返回interceptorResult
|
|
540
|
+
if (interceptorResult?.target === null || interceptorResult?.source === null) {
|
|
541
|
+
return interceptorResult;
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
//interceptorResult={target,source}
|
|
545
|
+
target = interceptorResult.target;
|
|
546
|
+
source = interceptorResult.source;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
// If interceptor returns null/undefined, continue with normal cloning process
|
|
550
|
+
}
|
|
551
|
+
options?.onBeforeMerge?.({ target, source, parent: options.parent });
|
|
546
552
|
// Determine the type and perform appropriate merging
|
|
547
553
|
// 确定类型并执行相应的合并
|
|
548
554
|
if (targetType === 'Object' && sourceType === 'Object') {
|
|
@@ -565,6 +571,7 @@
|
|
|
565
571
|
flag = false;
|
|
566
572
|
result = target;
|
|
567
573
|
}
|
|
574
|
+
options?.onAfterMerge?.({ result, target, source, type, parent: opts.parent });
|
|
568
575
|
return { result, flag, type };
|
|
569
576
|
},
|
|
570
577
|
// Special handling for objects with enable property
|
|
@@ -593,7 +600,6 @@
|
|
|
593
600
|
if (targetType !== 'Object' || sourceType !== 'Object') {
|
|
594
601
|
return target;
|
|
595
602
|
}
|
|
596
|
-
opts?.onBeforeMerge?.(target, source);
|
|
597
603
|
const options = Object.assign({ inheritMissing: true, targetClone: false, useEnable: true }, opts);
|
|
598
604
|
let result = {};
|
|
599
605
|
// If cloning is enabled, clone the target first
|
|
@@ -601,7 +607,7 @@
|
|
|
601
607
|
result = options.targetClone ? shallowCopy(target) : target;
|
|
602
608
|
for (let k in source) {
|
|
603
609
|
if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
|
|
604
|
-
let resp =
|
|
610
|
+
let resp = smartMerger(result[k], source[k], { ...opts, parent: result });
|
|
605
611
|
//resp={result,flag,type}
|
|
606
612
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
607
613
|
if (!resp.flag) {
|
|
@@ -641,13 +647,11 @@
|
|
|
641
647
|
result[k] = source[k];
|
|
642
648
|
}
|
|
643
649
|
}
|
|
644
|
-
options?.onAfterMerge?.(result, target, source);
|
|
645
650
|
return result;
|
|
646
651
|
}, deepMergeArrays = (target, source, options = {}) => {
|
|
647
652
|
// Ensure both target and source are arrays
|
|
648
653
|
if (!Array.isArray(target) || !Array.isArray(source))
|
|
649
654
|
return target;
|
|
650
|
-
options?.onBeforeMerge?.(target, source);
|
|
651
655
|
// Merge options, with default values
|
|
652
656
|
const opts = Object.assign({ dataMode: 'clear', inheritMissing: true, targetClone: false }, options),
|
|
653
657
|
// If cloning is enabled, create a deep copy of the target array
|
|
@@ -660,7 +664,7 @@
|
|
|
660
664
|
// 如果不允许添加超过长度
|
|
661
665
|
if (!opts.inheritMissing && i >= result.length)
|
|
662
666
|
break;
|
|
663
|
-
let resp =
|
|
667
|
+
let resp = smartMerger(result[i], source[i], { ...opts, parent: result });
|
|
664
668
|
//resp={result,flag,type}
|
|
665
669
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
666
670
|
if (!resp.flag) {
|
|
@@ -677,13 +681,11 @@
|
|
|
677
681
|
result.length = 0;
|
|
678
682
|
result.push(...source);
|
|
679
683
|
}
|
|
680
|
-
options?.onAfterMerge?.(result, target, source);
|
|
681
684
|
return result;
|
|
682
685
|
}, deepMergeMaps = (target, source, options = {}) => {
|
|
683
686
|
// Ensure both target and source are Maps
|
|
684
687
|
if (!(target instanceof Map) || !(source instanceof Map))
|
|
685
688
|
return target;
|
|
686
|
-
options?.onBeforeMerge?.(target, source);
|
|
687
689
|
// Merge options, with default values
|
|
688
690
|
const opts = Object.assign({ inheritMissing: true, targetClone: false, useEnable: true }, options),
|
|
689
691
|
// If cloning is enabled, create a deep copy of the target Map
|
|
@@ -692,7 +694,7 @@
|
|
|
692
694
|
for (const [key, value] of source.entries())
|
|
693
695
|
// Check if the key already exists in the target Map
|
|
694
696
|
if (result.has(key)) {
|
|
695
|
-
const _target = result.get(key), _source = value, resp =
|
|
697
|
+
const _target = result.get(key), _source = value, resp = smartMerger(_target, _source, opts);
|
|
696
698
|
//resp={result,flag,type}
|
|
697
699
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
698
700
|
if (!resp.flag) {
|
|
@@ -708,13 +710,11 @@
|
|
|
708
710
|
// If the key doesn't exist in the target, add the entry from the source Map
|
|
709
711
|
options.inheritMissing && result.set(key, value);
|
|
710
712
|
}
|
|
711
|
-
options?.onAfterMerge?.(result, target, source);
|
|
712
713
|
return result;
|
|
713
714
|
}, deepMergeSets = (target, source, options = {}) => {
|
|
714
715
|
// Ensure both target and source are Sets
|
|
715
716
|
if (!(target instanceof Set) || !(source instanceof Set))
|
|
716
717
|
return target;
|
|
717
|
-
options?.onBeforeMerge?.(target, source);
|
|
718
718
|
// Merge options, with default values
|
|
719
719
|
const opts = Object.assign({ dataMode: 'clear', inheritMissing: true, targetClone: false, useEnable: true }, options),
|
|
720
720
|
// If cloning is enabled, create a deep copy of the target Set
|
|
@@ -722,7 +722,7 @@
|
|
|
722
722
|
// Handle different merge strategies based on dataMode
|
|
723
723
|
if (opts.dataMode === 'replace') {
|
|
724
724
|
// Replace mode: recursively merge items in the Sets
|
|
725
|
-
const _result = [...result], _source = [...source], resp =
|
|
725
|
+
const _result = [...result], _source = [...source], resp = smartMerger(_result, _source, opts);
|
|
726
726
|
result.clear();
|
|
727
727
|
for (let item of resp.result)
|
|
728
728
|
result.add(item);
|
|
@@ -738,10 +738,9 @@
|
|
|
738
738
|
for (let item of source)
|
|
739
739
|
result.add(item);
|
|
740
740
|
}
|
|
741
|
-
options?.onAfterMerge?.(result, target, source);
|
|
742
741
|
return result;
|
|
743
742
|
};
|
|
744
|
-
return
|
|
743
|
+
return smartMerger(target, source, options).result;
|
|
745
744
|
};
|
|
746
745
|
|
|
747
746
|
const utils = {
|
package/dist/utils.umd.min.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @since Last modified: 2025-12-26
|
|
2
|
+
* @since Last modified: 2025-12-26 15:47:37
|
|
3
3
|
* @name Utils for web front-end.
|
|
4
|
-
* @version 0.0.
|
|
4
|
+
* @version 0.0.16
|
|
5
5
|
* @author AXUI development team <3217728223@qq.com>
|
|
6
6
|
* @description This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.
|
|
7
7
|
* @see {@link https://www.axui.cn|Official website}
|
|
@@ -12,4 +12,4 @@
|
|
|
12
12
|
* @copyright This software supports the MIT License, allowing free learning and commercial use, but please retain the terms 'ax,' 'axui,' 'AX,' and 'AXUI' within the software.
|
|
13
13
|
* @license MIT license
|
|
14
14
|
*/
|
|
15
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).utils=t()}(this,function(){"use strict";const getDataType=e=>{let t,r=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===r&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===r&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":r,t},deepClone=(e,t={})=>{const r=getDataType(e),n=Object.assign({cloneSet:!0,cloneMap:!0,cloneObject:!0,cloneArray:!0,cloneDate:!0,cloneRegex:!0},t);if(n.interceptor&&"function"==typeof n.interceptor){let t=n.interceptor(
|
|
15
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).utils=t()}(this,function(){"use strict";const getDataType=e=>{let t,r=Object.prototype.toString.call(e).slice(8,-1);return t="Function"===r&&/^\s*class\s+/.test(e.toString())?"Class":"Object"===r&&Object.getPrototypeOf(e)!==Object.prototype?"Instance":r,t},deepClone=(e,t={})=>{const r=getDataType(e),n=Object.assign({cloneSet:!0,cloneMap:!0,cloneObject:!0,cloneArray:!0,cloneDate:!0,cloneRegex:!0},t);if(n.interceptor&&"function"==typeof n.interceptor){let t=n.interceptor({input:e,type:r,parent:n.parent});if(t)return t}n.onBeforeClone?.({input:e,type:r,parent:n.parent});let o,a=!0;if("Object"===r&&n.cloneObject){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r],n);if(r.length>0)for(const o of r)t[o]=deepClone(e[o],{...n,parent:e});o=t}else if("Array"===r&&n.cloneArray)o=e.map(t=>deepClone(t,{...n,parent:e}));else if("Map"===r&&n.cloneMap){const t=new Map;for(const[r,o]of e)t.set(deepClone(r,n),deepClone(o,{...n,parent:e}));o=t}else if("Set"===r&&n.cloneSet){const t=new Set;for(const r of e)t.add(deepClone(r,{...n,parent:e}));o=t}else if("Date"===r&&n.cloneDate)o=new Date(e.getTime());else if("RegExp"===r&&n.cloneRegex){const t=e;o=new RegExp(t.source,t.flags)}else o=e,a=!1;return n.onAfterClone?.({output:o,input:e,type:r,cloned:a,parent:n.parent}),o},deepCloneToJSON=e=>{const t=getDataType(e);if("Object"===t){const t={};for(const r in e)t[r]=deepCloneToJSON(e[r]);for(const e in t)void 0===t[e]&&Reflect.deleteProperty(t,e);return t}if("Array"===t){return e.map((e,t)=>deepCloneToJSON(e)).filter(e=>void 0!==e)}return["Number","String","Boolean","Null"].includes(t)?e:void 0},e=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"],t=["add","delete","clear"],r=["set","delete","clear"],copyObjectWithSymbol=e=>{if(!e||"object"!=typeof e)return e;const t=e,r=Object.getOwnPropertySymbols(t).reduce((e,r)=>(e[r]=t[r],e),{});return{...t,...r}},shallowCopy=(e,t={})=>{const r=getDataType(e);return"Set"===r?new Set([...e]):"Map"===r?new Map([...e]):Array.isArray(e)?[...e]:"object"===r?copyObjectWithSymbol(e):"Date"===r?new Date(e.getTime()):"RegExp"===r?new RegExp(e.source,e.flags):"Buffer"===r?Buffer.from(e):"ArrayBuffer"===r||ArrayBuffer.isView(e)?e.slice(0):"WeakSet"===r?new WeakSet([...e]):"WeakMap"===r?new WeakMap([...e]):"Error"===r?new Error(e.message):e};return{getDataType:getDataType,requireTypes:(e,t,r)=>{let n=Array.isArray(t)?t:[t],o=getDataType(e),a=o.toLowerCase(),s=n.map(e=>e.toLowerCase()),i=a.includes("html")?"element":a;if(r)try{if(!s.includes(i))throw new TypeError(`Expected data type(s): [${s.join(", ")}], but got: ${i}`)}catch(e){r(e,o)}else if(!s.includes(i))throw new TypeError(`Expected data type(s): [${s.join(", ")}], but got: ${i}`);return o},deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:({target:t,onBeforeMutate:r=()=>{},onAfterMutate:n=()=>{},allowList:o,props:a={}})=>{if(!Array.isArray(t))throw new TypeError("The 'target' parameter must be an array.");o&&!o?.length||(o=e);const s={};for(let e of o)s[e]=function(...o){const s={},i=t.length;switch(e){case"push":case"unshift":s.addedItems=[...o];break;case"pop":s.poppedItem=t[i-1];break;case"shift":s.shiftedItem=t[0];break;case"splice":const[e,r]=o,n=e<0?Math.max(i+e,0):Math.min(e,i),a=void 0===r?i-n:r;s.deletedItems=t.slice(n,n+a);break;case"sort":case"reverse":s.oldSnapshot=[...t];break;case"fill":case"copyWithin":const c=o[1]||0,l=void 0===o[2]?i:o[2];s.oldItems=t.slice(c,l),s.start=c,s.end=l}r?.(s);const c=Array.prototype[e].apply(t,o),l={value:c,key:e,args:o,context:s,target:t,...a};return n?.(l),c};return s},arrayMutableMethods:e,setMutableMethods:t,mapMutableMethods:r,wrapSetMethods:({target:e,onBeforeMutate:r=()=>{},onAfterMutate:n=()=>{},allowList:o=t,props:a={}})=>{if(!(e instanceof Set))throw new TypeError("The 'target' parameter must be a Set.");const s={},createWrappedMethod=t=>function(...o){const s={};switch(t){case"add":{const[t]=o;s.addedItem=t,s.existed=e.has(t);break}case"delete":{const[t]=o;s.existed=e.has(t),s.deletedItem=s.existed?t:void 0;break}case"clear":s.clearedItems=Array.from(e),s.previousSize=e.size}r(s);const i=e[t].apply(e,o),c={method:t,result:i,args:o,context:s,target:e,...a};return n(c),i};for(const e of o)t.includes(e)&&(s[e]=createWrappedMethod(e));return Object.defineProperty(s,"target",{get:()=>e,enumerable:!1,configurable:!1}),s},wrapMapMethods:({target:e,onBeforeMutate:t=()=>{},onAfterMutate:n=()=>{},allowList:o=r,props:a={}})=>{if(!(e instanceof Map))throw new TypeError("The 'target' parameter must be a Map.");const s={},createWrappedMethod=r=>function(...o){const s={};switch(r){case"set":{const[t,r]=o;s.key=t,s.newValue=r,s.existed=e.has(t),s.oldValue=s.existed?e.get(t):void 0;break}case"delete":{const[t]=o;s.key=t,s.existed=e.has(t),s.value=s.existed?e.get(t):void 0;break}case"clear":s.clearedItems=Array.from(e.entries()),s.previousSize=e.size}t(s);const i=e[r].apply(e,o),c={method:r,result:i,args:o,context:s,target:e,...a};return n(c),i};for(const e of o)r.includes(e)&&(s[e]=createWrappedMethod(e));return Object.defineProperty(s,"target",{get:()=>e,enumerable:!1,configurable:!1}),s},getUniqueId:(e={})=>{const t=e.prefix,r=e.suffix,n=e.base10,o=e.base36;return`${t?t+"-":""}${Date.now()}${o?"-"+Math.random().toString(36).substring(2,11):""}${n?"-"+Math.floor(1e4*Math.random()).toString().padStart(4,"0"):""}${r?"-"+r:""}`},deepMerge:(e,t,r={})=>{const n=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0,useSymbol:!0,deepClone:{},onBeforeMerge:void 0,onAfterMerge:void 0},r),smartMerger=(e,t,n)=>{let o,a,s=getDataType(e),i=getDataType(t),c=!0;if(n.interceptor&&"function"==typeof n.interceptor){let r=n.interceptor({target:e,source:t,parent:n.parent});if(r){if(null===r?.target||null===r?.source)return r;e=r.target,t=r.source}}return n?.onBeforeMerge?.({target:e,source:t,parent:n.parent}),"Object"===s&&"Object"===i?(a=deepMergeObjects(e,t,n),o="Object"):"Array"===s&&"Array"===i?(a=deepMergeArrays(e,t,n),o="Array"):"Set"===s&&"Set"===i?(a=deepMergeSets(e,t,n),o="Set"):"Map"===s&&"Map"===i?(a=deepMergeMaps(e,t,n),o="Map"):(c=!1,a=e),n?.onAfterMerge?.({result:a,target:e,source:t,type:o,parent:r.parent}),{result:a,flag:c,type:o}},mergeEnableObject=(e,t)=>e?.hasOwnProperty("enable")&&"boolean"==typeof t?(e.enable=t,e):t?.hasOwnProperty("enable")&&"boolean"==typeof e?Object.assign({enable:e},t):t,deepMergeObjects=(e,t,r={})=>{let n=getDataType(e),o=getDataType(t);if("Object"!==n||"Object"!==o)return e;const a=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r);let s={};s=a.targetClone?shallowCopy(e):e;for(let e in t)if(t.hasOwnProperty(e)&&s.hasOwnProperty(e)){let n=smartMerger(s[e],t[e],{...r,parent:s});n.flag?n.type?"Object"===n.type&&(s[e]=n.result):s[e]=t[e]:a.useEnable?s[e]=mergeEnableObject(s[e],t[e]):s[e]=t[e]}else t.hasOwnProperty(e)&&!s.hasOwnProperty(e)&&a.inheritMissing&&(s[e]=t[e]);if(a.useSymbol){let e=Object.getOwnPropertySymbols(t);if(e.length)for(let r of e)s[r]=t[r]}return s},deepMergeArrays=(e,t,r={})=>{if(!Array.isArray(e)||!Array.isArray(t))return e;const n=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1},r),o=n.targetClone?[...e]:e;if("replace"===n.dataMode)for(let e=0;e<t.length&&(n.inheritMissing||!(e>=o.length));e++){smartMerger(o[e],t[e],{...n,parent:o}).flag||(o[e]=t[e])}else"concat"===n.dataMode||(o.length=0),o.push(...t);return o},deepMergeMaps=(e,t,r={})=>{if(!(e instanceof Map&&t instanceof Map))return e;const n=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r),o=n.targetClone?new Map([...e]):e;for(const[e,a]of t.entries())if(o.has(e)){const t=o.get(e),r=a,s=smartMerger(t,r,n);s.flag?"Object"===s.type&&o.set(e,s.result):o.set(e,r)}else r.inheritMissing&&o.set(e,a);return o},deepMergeSets=(e,t,r={})=>{if(!(e instanceof Set&&t instanceof Set))return e;const n=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0},r),o=n.targetClone?new Set(...e):e;if("replace"===n.dataMode){const e=[...o],r=[...t],a=smartMerger(e,r,n);o.clear();for(let e of a.result)o.add(e)}else if("concat"===n.dataMode)for(let e of t)o.add(e);else{o.clear();for(let e of t)o.add(e)}return o};return smartMerger(e,t,n).result},shallowCopy:shallowCopy,copyObjectWithSymbol:copyObjectWithSymbol}});
|
package/dist.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codady/utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
4
4
|
"author": "AXUI Development Team",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "This is a set of general-purpose JavaScript utility functions developed by the AXUI team. All functions are pure and do not involve CSS or other third-party libraries. They are suitable for any web front-end environment.",
|
package/src/deepClone.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/26
|
|
2
|
+
* @since Last modified: 2025/12/26 15:13:54
|
|
3
3
|
* Deep clone an array, object, or other cloneable data types.
|
|
4
4
|
*
|
|
5
5
|
* Features:
|
|
@@ -100,16 +100,16 @@ const deepClone = (data, options = {}) => {
|
|
|
100
100
|
}, options);
|
|
101
101
|
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
102
102
|
if (opts.interceptor && typeof opts.interceptor === 'function') {
|
|
103
|
-
let result = opts.interceptor(data, dataType);
|
|
103
|
+
let result = opts.interceptor({ input: data, type: dataType, parent: opts.parent });
|
|
104
104
|
if ((result ?? false)) {
|
|
105
105
|
// Call onAfterClone if set
|
|
106
|
-
opts.onAfterClone?.({
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
/* opts.onAfterClone?.({
|
|
107
|
+
output: result,
|
|
108
|
+
input: data,
|
|
109
|
+
type: dataType,
|
|
110
|
+
cloned: result !== data,
|
|
111
|
+
parent: opts.parent
|
|
112
|
+
}); */
|
|
113
113
|
return result;
|
|
114
114
|
}
|
|
115
115
|
// If interceptor returns null/undefined, continue with normal cloning process
|
|
@@ -123,8 +123,6 @@ const deepClone = (data, options = {}) => {
|
|
|
123
123
|
let newData, cloned = true;
|
|
124
124
|
if (dataType === 'Object' && opts.cloneObject) {
|
|
125
125
|
const newObj = {}, symbols = Object.getOwnPropertySymbols(data);
|
|
126
|
-
//存储parent对象,在下一次深复制时使用
|
|
127
|
-
opts.parent = data;
|
|
128
126
|
// Clone regular properties
|
|
129
127
|
for (const key in data) {
|
|
130
128
|
//临时保存父对象
|
|
@@ -133,30 +131,27 @@ const deepClone = (data, options = {}) => {
|
|
|
133
131
|
// Clone Symbol properties
|
|
134
132
|
if (symbols.length > 0) {
|
|
135
133
|
for (const symbol of symbols) {
|
|
136
|
-
newObj[symbol] = deepClone(data[symbol], opts);
|
|
134
|
+
newObj[symbol] = deepClone(data[symbol], { ...opts, parent: data });
|
|
137
135
|
}
|
|
138
136
|
}
|
|
139
137
|
newData = newObj;
|
|
140
138
|
}
|
|
141
139
|
else if (dataType === 'Array' && opts.cloneArray) {
|
|
142
|
-
opts
|
|
143
|
-
newData = data.map(item => deepClone(item, opts));
|
|
140
|
+
newData = data.map(item => deepClone(item, { ...opts, parent: data }));
|
|
144
141
|
}
|
|
145
142
|
else if (dataType === 'Map' && opts.cloneMap) {
|
|
146
143
|
const newMap = new Map();
|
|
147
|
-
opts.parent = data;
|
|
148
144
|
for (const [key, value] of data) {
|
|
149
145
|
// Both Map keys and values need deep cloning
|
|
150
|
-
newMap.set(deepClone(key, opts), deepClone(value, opts));
|
|
146
|
+
newMap.set(deepClone(key, opts), deepClone(value, { ...opts, parent: data }));
|
|
151
147
|
}
|
|
152
148
|
newData = newMap;
|
|
153
149
|
}
|
|
154
150
|
else if (dataType === 'Set' && opts.cloneSet) {
|
|
155
151
|
const newSet = new Set();
|
|
156
|
-
opts.parent = data;
|
|
157
152
|
for (const value of data) {
|
|
158
153
|
// Set values need deep cloning
|
|
159
|
-
newSet.add(deepClone(value, opts));
|
|
154
|
+
newSet.add(deepClone(value, { ...opts, parent: data }));
|
|
160
155
|
}
|
|
161
156
|
newData = newSet;
|
|
162
157
|
}
|
package/src/deepClone.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/26
|
|
2
|
+
* @since Last modified: 2025/12/26 15:13:54
|
|
3
3
|
* Deep clone an array, object, or other cloneable data types.
|
|
4
4
|
*
|
|
5
5
|
* Features:
|
|
@@ -94,7 +94,7 @@ export interface DeepCloneOptions {
|
|
|
94
94
|
/**
|
|
95
95
|
* Interceptor function. If returns a non-null value, use it as the cloning result.
|
|
96
96
|
*/
|
|
97
|
-
interceptor?: <T>(
|
|
97
|
+
interceptor?: <T>(result: BeforeCloneResult) => T | null | undefined;
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
100
|
* Callback before cloning.
|
|
@@ -147,16 +147,16 @@ const deepClone = <T>(data: T, options: DeepCloneOptions = {}): T => {
|
|
|
147
147
|
|
|
148
148
|
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
149
149
|
if (opts.interceptor && typeof opts.interceptor === 'function') {
|
|
150
|
-
let result = opts.interceptor(data, dataType);
|
|
150
|
+
let result = opts.interceptor({input: data, type: dataType,parent: opts.parent});
|
|
151
151
|
if ((result ?? false)) {
|
|
152
152
|
// Call onAfterClone if set
|
|
153
|
-
|
|
153
|
+
/* opts.onAfterClone?.({
|
|
154
154
|
output: result,
|
|
155
155
|
input: data,
|
|
156
156
|
type: dataType,
|
|
157
157
|
cloned: result !== data,
|
|
158
158
|
parent: opts.parent
|
|
159
|
-
});
|
|
159
|
+
}); */
|
|
160
160
|
return result as T;
|
|
161
161
|
}
|
|
162
162
|
// If interceptor returns null/undefined, continue with normal cloning process
|
|
@@ -176,9 +176,6 @@ const deepClone = <T>(data: T, options: DeepCloneOptions = {}): T => {
|
|
|
176
176
|
const newObj: Record<string | symbol, any> = {},
|
|
177
177
|
symbols = Object.getOwnPropertySymbols(data);
|
|
178
178
|
|
|
179
|
-
//存储parent对象,在下一次深复制时使用
|
|
180
|
-
opts.parent = data as any;
|
|
181
|
-
|
|
182
179
|
// Clone regular properties
|
|
183
180
|
for (const key in data) {
|
|
184
181
|
//临时保存父对象
|
|
@@ -188,27 +185,24 @@ const deepClone = <T>(data: T, options: DeepCloneOptions = {}): T => {
|
|
|
188
185
|
// Clone Symbol properties
|
|
189
186
|
if (symbols.length > 0) {
|
|
190
187
|
for (const symbol of symbols) {
|
|
191
|
-
newObj[symbol] = deepClone((data as any)[symbol], opts);
|
|
188
|
+
newObj[symbol] = deepClone((data as any)[symbol], {...opts,parent:data as any});
|
|
192
189
|
}
|
|
193
190
|
}
|
|
194
191
|
newData = newObj as T;
|
|
195
192
|
} else if (dataType === 'Array' && opts.cloneArray) {
|
|
196
|
-
|
|
197
|
-
newData = (data as any[]).map(item => deepClone(item, opts)) as T;
|
|
193
|
+
newData = (data as any[]).map(item => deepClone(item, {...opts,parent:data as any})) as T;
|
|
198
194
|
} else if (dataType === 'Map' && opts.cloneMap) {
|
|
199
195
|
const newMap = new Map();
|
|
200
|
-
opts.parent = data as any;
|
|
201
196
|
for (const [key, value] of data as Map<any, any>) {
|
|
202
197
|
// Both Map keys and values need deep cloning
|
|
203
|
-
newMap.set(deepClone(key, opts), deepClone(value, opts));
|
|
198
|
+
newMap.set(deepClone(key, opts), deepClone(value, {...opts,parent:data as any}));
|
|
204
199
|
}
|
|
205
200
|
newData = newMap as T;
|
|
206
201
|
} else if (dataType === 'Set' && opts.cloneSet) {
|
|
207
202
|
const newSet = new Set();
|
|
208
|
-
opts.parent = data as any;
|
|
209
203
|
for (const value of data as Set<any>) {
|
|
210
204
|
// Set values need deep cloning
|
|
211
|
-
newSet.add(deepClone(value, opts));
|
|
205
|
+
newSet.add(deepClone(value, {...opts,parent:data as any}));
|
|
212
206
|
}
|
|
213
207
|
newData = newSet as T;
|
|
214
208
|
} else if (dataType === 'Date' && opts.cloneDate) {
|
package/src/deepMerge.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/
|
|
2
|
+
* @since Last modified: 2025/12/26 15:40:50
|
|
3
3
|
* @function deepMerge
|
|
4
4
|
* @description Deeply merges two data structures (Object, Array, Map, or Set) based on their types.
|
|
5
5
|
* This function recursively merges the properties or items of the target and source, depending on their types.
|
|
@@ -69,8 +69,25 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
69
69
|
}, opts),
|
|
70
70
|
// Main helper function for recursive merging
|
|
71
71
|
// 递归合并的主辅助函数
|
|
72
|
-
|
|
72
|
+
smartMerger = (target, source, options) => {
|
|
73
73
|
let targetType = getDataType(target), sourceType = getDataType(source), flag = true, type, result;
|
|
74
|
+
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
75
|
+
if (options.interceptor && typeof options.interceptor === 'function') {
|
|
76
|
+
let interceptorResult = options.interceptor({ target, source, parent: options.parent });
|
|
77
|
+
if ((interceptorResult ?? false)) {
|
|
78
|
+
//如果不是返回{target,source},那么直接返回interceptorResult
|
|
79
|
+
if (interceptorResult?.target === null || interceptorResult?.source === null) {
|
|
80
|
+
return interceptorResult;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
//interceptorResult={target,source}
|
|
84
|
+
target = interceptorResult.target;
|
|
85
|
+
source = interceptorResult.source;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// If interceptor returns null/undefined, continue with normal cloning process
|
|
89
|
+
}
|
|
90
|
+
options?.onBeforeMerge?.({ target, source, parent: options.parent });
|
|
74
91
|
// Determine the type and perform appropriate merging
|
|
75
92
|
// 确定类型并执行相应的合并
|
|
76
93
|
if (targetType === 'Object' && sourceType === 'Object') {
|
|
@@ -93,6 +110,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
93
110
|
flag = false;
|
|
94
111
|
result = target;
|
|
95
112
|
}
|
|
113
|
+
options?.onAfterMerge?.({ result, target, source, type, parent: opts.parent });
|
|
96
114
|
return { result, flag, type };
|
|
97
115
|
},
|
|
98
116
|
// Special handling for objects with enable property
|
|
@@ -121,7 +139,6 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
121
139
|
if (targetType !== 'Object' || sourceType !== 'Object') {
|
|
122
140
|
return target;
|
|
123
141
|
}
|
|
124
|
-
opts?.onBeforeMerge?.(target, source);
|
|
125
142
|
const options = Object.assign({ inheritMissing: true, targetClone: false, useEnable: true }, opts);
|
|
126
143
|
let result = {};
|
|
127
144
|
// If cloning is enabled, clone the target first
|
|
@@ -129,7 +146,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
129
146
|
result = options.targetClone ? shallowCopy(target) : target;
|
|
130
147
|
for (let k in source) {
|
|
131
148
|
if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
|
|
132
|
-
let resp =
|
|
149
|
+
let resp = smartMerger(result[k], source[k], { ...opts, parent: result });
|
|
133
150
|
//resp={result,flag,type}
|
|
134
151
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
135
152
|
if (!resp.flag) {
|
|
@@ -169,13 +186,11 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
169
186
|
result[k] = source[k];
|
|
170
187
|
}
|
|
171
188
|
}
|
|
172
|
-
options?.onAfterMerge?.(result, target, source);
|
|
173
189
|
return result;
|
|
174
190
|
}, deepMergeArrays = (target, source, options = {}) => {
|
|
175
191
|
// Ensure both target and source are arrays
|
|
176
192
|
if (!Array.isArray(target) || !Array.isArray(source))
|
|
177
193
|
return target;
|
|
178
|
-
options?.onBeforeMerge?.(target, source);
|
|
179
194
|
// Merge options, with default values
|
|
180
195
|
const opts = Object.assign({ dataMode: 'clear', inheritMissing: true, targetClone: false }, options),
|
|
181
196
|
// If cloning is enabled, create a deep copy of the target array
|
|
@@ -188,7 +203,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
188
203
|
// 如果不允许添加超过长度
|
|
189
204
|
if (!opts.inheritMissing && i >= result.length)
|
|
190
205
|
break;
|
|
191
|
-
let resp =
|
|
206
|
+
let resp = smartMerger(result[i], source[i], { ...opts, parent: result });
|
|
192
207
|
//resp={result,flag,type}
|
|
193
208
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
194
209
|
if (!resp.flag) {
|
|
@@ -205,13 +220,11 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
205
220
|
result.length = 0;
|
|
206
221
|
result.push(...source);
|
|
207
222
|
}
|
|
208
|
-
options?.onAfterMerge?.(result, target, source);
|
|
209
223
|
return result;
|
|
210
224
|
}, deepMergeMaps = (target, source, options = {}) => {
|
|
211
225
|
// Ensure both target and source are Maps
|
|
212
226
|
if (!(target instanceof Map) || !(source instanceof Map))
|
|
213
227
|
return target;
|
|
214
|
-
options?.onBeforeMerge?.(target, source);
|
|
215
228
|
// Merge options, with default values
|
|
216
229
|
const opts = Object.assign({ inheritMissing: true, targetClone: false, useEnable: true }, options),
|
|
217
230
|
// If cloning is enabled, create a deep copy of the target Map
|
|
@@ -220,7 +233,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
220
233
|
for (const [key, value] of source.entries())
|
|
221
234
|
// Check if the key already exists in the target Map
|
|
222
235
|
if (result.has(key)) {
|
|
223
|
-
const _target = result.get(key), _source = value, resp =
|
|
236
|
+
const _target = result.get(key), _source = value, resp = smartMerger(_target, _source, opts);
|
|
224
237
|
//resp={result,flag,type}
|
|
225
238
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
226
239
|
if (!resp.flag) {
|
|
@@ -236,13 +249,11 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
236
249
|
// If the key doesn't exist in the target, add the entry from the source Map
|
|
237
250
|
options.inheritMissing && result.set(key, value);
|
|
238
251
|
}
|
|
239
|
-
options?.onAfterMerge?.(result, target, source);
|
|
240
252
|
return result;
|
|
241
253
|
}, deepMergeSets = (target, source, options = {}) => {
|
|
242
254
|
// Ensure both target and source are Sets
|
|
243
255
|
if (!(target instanceof Set) || !(source instanceof Set))
|
|
244
256
|
return target;
|
|
245
|
-
options?.onBeforeMerge?.(target, source);
|
|
246
257
|
// Merge options, with default values
|
|
247
258
|
const opts = Object.assign({ dataMode: 'clear', inheritMissing: true, targetClone: false, useEnable: true }, options),
|
|
248
259
|
// If cloning is enabled, create a deep copy of the target Set
|
|
@@ -250,7 +261,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
250
261
|
// Handle different merge strategies based on dataMode
|
|
251
262
|
if (opts.dataMode === 'replace') {
|
|
252
263
|
// Replace mode: recursively merge items in the Sets
|
|
253
|
-
const _result = [...result], _source = [...source], resp =
|
|
264
|
+
const _result = [...result], _source = [...source], resp = smartMerger(_result, _source, opts);
|
|
254
265
|
result.clear();
|
|
255
266
|
for (let item of resp.result)
|
|
256
267
|
result.add(item);
|
|
@@ -266,9 +277,8 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
266
277
|
for (let item of source)
|
|
267
278
|
result.add(item);
|
|
268
279
|
}
|
|
269
|
-
options?.onAfterMerge?.(result, target, source);
|
|
270
280
|
return result;
|
|
271
281
|
};
|
|
272
|
-
return
|
|
282
|
+
return smartMerger(target, source, options).result;
|
|
273
283
|
};
|
|
274
284
|
export default deepMerge;
|