@codady/utils 0.0.15 → 0.0.17
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 +37 -33
- package/dist/utils.cjs.min.js +3 -3
- package/dist/utils.esm.js +37 -33
- package/dist/utils.esm.min.js +3 -3
- package/dist/utils.umd.js +37 -33
- package/dist/utils.umd.min.js +3 -3
- package/dist.zip +0 -0
- package/package.json +1 -1
- package/src/deepClone.js +9 -9
- package/src/deepClone.ts +5 -5
- package/src/deepMerge.js +34 -24
- package/src/deepMerge.ts +84 -39
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 16:3:51
|
|
4
4
|
* @name Utils for web front-end.
|
|
5
|
-
* @version 0.0.
|
|
5
|
+
* @version 0.0.17
|
|
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
|
|
@@ -536,31 +530,49 @@
|
|
|
536
530
|
}, opts),
|
|
537
531
|
// Main helper function for recursive merging
|
|
538
532
|
// 递归合并的主辅助函数
|
|
539
|
-
|
|
540
|
-
let targetType = getDataType(target), sourceType = getDataType(source), flag = true,
|
|
533
|
+
smartMerger = (target, source, options) => {
|
|
534
|
+
let targetType = getDataType(target), sourceType = getDataType(source), flag = true, mergeType, 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, targetType, sourceType, 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, targetType, sourceType, parent: options.parent });
|
|
541
552
|
// Determine the type and perform appropriate merging
|
|
542
553
|
// 确定类型并执行相应的合并
|
|
543
554
|
if (targetType === 'Object' && sourceType === 'Object') {
|
|
544
555
|
result = deepMergeObjects(target, source, options);
|
|
545
|
-
|
|
556
|
+
mergeType = 'Object';
|
|
546
557
|
}
|
|
547
558
|
else if (targetType === 'Array' && sourceType === 'Array') {
|
|
548
559
|
result = deepMergeArrays(target, source, options);
|
|
549
|
-
|
|
560
|
+
mergeType = 'Array';
|
|
550
561
|
}
|
|
551
562
|
else if (targetType === 'Set' && sourceType === 'Set') {
|
|
552
563
|
result = deepMergeSets(target, source, options);
|
|
553
|
-
|
|
564
|
+
mergeType = 'Set';
|
|
554
565
|
}
|
|
555
566
|
else if (targetType === 'Map' && sourceType === 'Map') {
|
|
556
567
|
result = deepMergeMaps(target, source, options);
|
|
557
|
-
|
|
568
|
+
mergeType = 'Map';
|
|
558
569
|
}
|
|
559
570
|
else {
|
|
560
571
|
flag = false;
|
|
561
572
|
result = target;
|
|
562
573
|
}
|
|
563
|
-
|
|
574
|
+
options?.onAfterMerge?.({ result, target, source, targetType, sourceType, mergeType, parent: opts.parent });
|
|
575
|
+
return { result, flag, mergeType };
|
|
564
576
|
},
|
|
565
577
|
// Special handling for objects with enable property
|
|
566
578
|
// 对具有enable属性的对象进行特殊处理
|
|
@@ -588,7 +600,6 @@
|
|
|
588
600
|
if (targetType !== 'Object' || sourceType !== 'Object') {
|
|
589
601
|
return target;
|
|
590
602
|
}
|
|
591
|
-
opts?.onBeforeMerge?.(target, source);
|
|
592
603
|
const options = Object.assign({ inheritMissing: true, targetClone: false, useEnable: true }, opts);
|
|
593
604
|
let result = {};
|
|
594
605
|
// If cloning is enabled, clone the target first
|
|
@@ -596,7 +607,7 @@
|
|
|
596
607
|
result = options.targetClone ? shallowCopy(target) : target;
|
|
597
608
|
for (let k in source) {
|
|
598
609
|
if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
|
|
599
|
-
let resp =
|
|
610
|
+
let resp = smartMerger(result[k], source[k], { ...opts, parent: result });
|
|
600
611
|
//resp={result,flag,type}
|
|
601
612
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
602
613
|
if (!resp.flag) {
|
|
@@ -611,8 +622,8 @@
|
|
|
611
622
|
}
|
|
612
623
|
else {
|
|
613
624
|
//类型相同
|
|
614
|
-
if (resp.
|
|
615
|
-
if (resp.
|
|
625
|
+
if (resp.mergeType) {
|
|
626
|
+
if (resp.mergeType === 'Object') {
|
|
616
627
|
//如果遇上对象则深度复制
|
|
617
628
|
result[k] = resp.result;
|
|
618
629
|
}
|
|
@@ -636,13 +647,11 @@
|
|
|
636
647
|
result[k] = source[k];
|
|
637
648
|
}
|
|
638
649
|
}
|
|
639
|
-
options?.onAfterMerge?.(result, target, source);
|
|
640
650
|
return result;
|
|
641
651
|
}, deepMergeArrays = (target, source, options = {}) => {
|
|
642
652
|
// Ensure both target and source are arrays
|
|
643
653
|
if (!Array.isArray(target) || !Array.isArray(source))
|
|
644
654
|
return target;
|
|
645
|
-
options?.onBeforeMerge?.(target, source);
|
|
646
655
|
// Merge options, with default values
|
|
647
656
|
const opts = Object.assign({ dataMode: 'clear', inheritMissing: true, targetClone: false }, options),
|
|
648
657
|
// If cloning is enabled, create a deep copy of the target array
|
|
@@ -655,7 +664,7 @@
|
|
|
655
664
|
// 如果不允许添加超过长度
|
|
656
665
|
if (!opts.inheritMissing && i >= result.length)
|
|
657
666
|
break;
|
|
658
|
-
let resp =
|
|
667
|
+
let resp = smartMerger(result[i], source[i], { ...opts, parent: result });
|
|
659
668
|
//resp={result,flag,type}
|
|
660
669
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
661
670
|
if (!resp.flag) {
|
|
@@ -672,13 +681,11 @@
|
|
|
672
681
|
result.length = 0;
|
|
673
682
|
result.push(...source);
|
|
674
683
|
}
|
|
675
|
-
options?.onAfterMerge?.(result, target, source);
|
|
676
684
|
return result;
|
|
677
685
|
}, deepMergeMaps = (target, source, options = {}) => {
|
|
678
686
|
// Ensure both target and source are Maps
|
|
679
687
|
if (!(target instanceof Map) || !(source instanceof Map))
|
|
680
688
|
return target;
|
|
681
|
-
options?.onBeforeMerge?.(target, source);
|
|
682
689
|
// Merge options, with default values
|
|
683
690
|
const opts = Object.assign({ inheritMissing: true, targetClone: false, useEnable: true }, options),
|
|
684
691
|
// If cloning is enabled, create a deep copy of the target Map
|
|
@@ -687,7 +694,7 @@
|
|
|
687
694
|
for (const [key, value] of source.entries())
|
|
688
695
|
// Check if the key already exists in the target Map
|
|
689
696
|
if (result.has(key)) {
|
|
690
|
-
const _target = result.get(key), _source = value, resp =
|
|
697
|
+
const _target = result.get(key), _source = value, resp = smartMerger(_target, _source, opts);
|
|
691
698
|
//resp={result,flag,type}
|
|
692
699
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
693
700
|
if (!resp.flag) {
|
|
@@ -696,20 +703,18 @@
|
|
|
696
703
|
}
|
|
697
704
|
else {
|
|
698
705
|
// If both target and source are objects, merge them recursively
|
|
699
|
-
resp.
|
|
706
|
+
resp.mergeType === 'Object' && result.set(key, resp.result);
|
|
700
707
|
}
|
|
701
708
|
}
|
|
702
709
|
else {
|
|
703
710
|
// If the key doesn't exist in the target, add the entry from the source Map
|
|
704
711
|
options.inheritMissing && result.set(key, value);
|
|
705
712
|
}
|
|
706
|
-
options?.onAfterMerge?.(result, target, source);
|
|
707
713
|
return result;
|
|
708
714
|
}, deepMergeSets = (target, source, options = {}) => {
|
|
709
715
|
// Ensure both target and source are Sets
|
|
710
716
|
if (!(target instanceof Set) || !(source instanceof Set))
|
|
711
717
|
return target;
|
|
712
|
-
options?.onBeforeMerge?.(target, source);
|
|
713
718
|
// Merge options, with default values
|
|
714
719
|
const opts = Object.assign({ dataMode: 'clear', inheritMissing: true, targetClone: false, useEnable: true }, options),
|
|
715
720
|
// If cloning is enabled, create a deep copy of the target Set
|
|
@@ -717,7 +722,7 @@
|
|
|
717
722
|
// Handle different merge strategies based on dataMode
|
|
718
723
|
if (opts.dataMode === 'replace') {
|
|
719
724
|
// Replace mode: recursively merge items in the Sets
|
|
720
|
-
const _result = [...result], _source = [...source], resp =
|
|
725
|
+
const _result = [...result], _source = [...source], resp = smartMerger(_result, _source, opts);
|
|
721
726
|
result.clear();
|
|
722
727
|
for (let item of resp.result)
|
|
723
728
|
result.add(item);
|
|
@@ -733,10 +738,9 @@
|
|
|
733
738
|
for (let item of source)
|
|
734
739
|
result.add(item);
|
|
735
740
|
}
|
|
736
|
-
options?.onAfterMerge?.(result, target, source);
|
|
737
741
|
return result;
|
|
738
742
|
};
|
|
739
|
-
return
|
|
743
|
+
return smartMerger(target, source, options).result;
|
|
740
744
|
};
|
|
741
745
|
|
|
742
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 16:3:51
|
|
3
3
|
* @name Utils for web front-end.
|
|
4
|
-
* @version 0.0.
|
|
4
|
+
* @version 0.0.17
|
|
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,targetType:s,sourceType:i,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,targetType:s,sourceType:i,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,targetType:s,sourceType:i,mergeType:o,parent:r.parent}),{result:a,flag:c,mergeType: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.mergeType?"Object"===n.mergeType&&(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.mergeType&&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.17",
|
|
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
|
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
|
package/src/deepMerge.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @since Last modified: 2025/12/
|
|
2
|
+
* @since Last modified: 2025/12/26 16:03:37
|
|
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,31 +69,49 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
69
69
|
}, opts),
|
|
70
70
|
// Main helper function for recursive merging
|
|
71
71
|
// 递归合并的主辅助函数
|
|
72
|
-
|
|
73
|
-
let targetType = getDataType(target), sourceType = getDataType(source), flag = true,
|
|
72
|
+
smartMerger = (target, source, options) => {
|
|
73
|
+
let targetType = getDataType(target), sourceType = getDataType(source), flag = true, mergeType, 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, targetType, sourceType, 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, targetType, sourceType, parent: options.parent });
|
|
74
91
|
// Determine the type and perform appropriate merging
|
|
75
92
|
// 确定类型并执行相应的合并
|
|
76
93
|
if (targetType === 'Object' && sourceType === 'Object') {
|
|
77
94
|
result = deepMergeObjects(target, source, options);
|
|
78
|
-
|
|
95
|
+
mergeType = 'Object';
|
|
79
96
|
}
|
|
80
97
|
else if (targetType === 'Array' && sourceType === 'Array') {
|
|
81
98
|
result = deepMergeArrays(target, source, options);
|
|
82
|
-
|
|
99
|
+
mergeType = 'Array';
|
|
83
100
|
}
|
|
84
101
|
else if (targetType === 'Set' && sourceType === 'Set') {
|
|
85
102
|
result = deepMergeSets(target, source, options);
|
|
86
|
-
|
|
103
|
+
mergeType = 'Set';
|
|
87
104
|
}
|
|
88
105
|
else if (targetType === 'Map' && sourceType === 'Map') {
|
|
89
106
|
result = deepMergeMaps(target, source, options);
|
|
90
|
-
|
|
107
|
+
mergeType = 'Map';
|
|
91
108
|
}
|
|
92
109
|
else {
|
|
93
110
|
flag = false;
|
|
94
111
|
result = target;
|
|
95
112
|
}
|
|
96
|
-
|
|
113
|
+
options?.onAfterMerge?.({ result, target, source, targetType, sourceType, mergeType, parent: opts.parent });
|
|
114
|
+
return { result, flag, mergeType };
|
|
97
115
|
},
|
|
98
116
|
// Special handling for objects with enable property
|
|
99
117
|
// 对具有enable属性的对象进行特殊处理
|
|
@@ -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) {
|
|
@@ -144,8 +161,8 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
144
161
|
}
|
|
145
162
|
else {
|
|
146
163
|
//类型相同
|
|
147
|
-
if (resp.
|
|
148
|
-
if (resp.
|
|
164
|
+
if (resp.mergeType) {
|
|
165
|
+
if (resp.mergeType === 'Object') {
|
|
149
166
|
//如果遇上对象则深度复制
|
|
150
167
|
result[k] = resp.result;
|
|
151
168
|
}
|
|
@@ -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) {
|
|
@@ -229,20 +242,18 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
229
242
|
}
|
|
230
243
|
else {
|
|
231
244
|
// If both target and source are objects, merge them recursively
|
|
232
|
-
resp.
|
|
245
|
+
resp.mergeType === 'Object' && result.set(key, resp.result);
|
|
233
246
|
}
|
|
234
247
|
}
|
|
235
248
|
else {
|
|
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;
|