@codady/utils 0.0.15 → 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 +28 -24
- package/dist/utils.cjs.min.js +3 -3
- package/dist/utils.esm.js +28 -24
- package/dist/utils.esm.min.js +3 -3
- package/dist/utils.umd.js +28 -24
- 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 +25 -15
- package/src/deepMerge.ts +70 -31
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
All changes to Utils including new features, updates, and removals are documented here.
|
|
4
4
|
|
|
5
|
+
## [v0.0.16] - 2025-12-26
|
|
6
|
+
|
|
7
|
+
### Distribution Files
|
|
8
|
+
* **JS**: https://unpkg.com/@codady/utils@0.0.16/dist/js/utils.js
|
|
9
|
+
* **Zip**:https://unpkg.com/@codady/utils@0.0.16/dist.zip
|
|
10
|
+
|
|
11
|
+
### Changes
|
|
12
|
+
|
|
13
|
+
#### Fixed
|
|
14
|
+
* Null
|
|
15
|
+
|
|
16
|
+
#### Added
|
|
17
|
+
* Modified the `deepMerge` function to include an `interceptor` parameter, which can be used to intercept the merging process and handle it manually when needed.修改了`deepMerge`函数,对参数增加`interceptor`属性,用来拦截合并,当需要手动处理合并时可使用该参数。
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
#### Removed
|
|
21
|
+
* Null
|
|
5
22
|
|
|
6
23
|
## [v0.0.14] - 2025-12-26
|
|
7
24
|
|
|
@@ -16,6 +33,8 @@ All changes to Utils including new features, updates, and removals are documente
|
|
|
16
33
|
|
|
17
34
|
#### Added
|
|
18
35
|
* The `onBeforeClone` and `onAfterClone` callbacks of the `deepClone` function now include a new `parent` property, which can temporarily store the parent object being cloned. deepClone函数的onBeforeClone和onAfterClone新增属性parent,可临时存储被拷贝的父对象。
|
|
36
|
+
* Modified the `deepMerge` function to include an `interceptor` parameter, which can be used to intercept the merging process and handle it manually when needed.修改了`deepMerge`函数,对参数增加`interceptor`属性,用来拦截合并,当需要手动处理合并时可使用该参数。
|
|
37
|
+
|
|
19
38
|
|
|
20
39
|
#### Removed
|
|
21
40
|
* Null
|
package/dist/utils.cjs.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}
|
|
@@ -61,16 +61,10 @@ const deepClone = (data, options = {}) => {
|
|
|
61
61
|
}, options);
|
|
62
62
|
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
63
63
|
if (opts.interceptor && typeof opts.interceptor === 'function') {
|
|
64
|
-
let result = opts.interceptor(data, dataType);
|
|
64
|
+
let result = opts.interceptor({ input: data, type: dataType, parent: opts.parent });
|
|
65
65
|
if ((result ?? false)) {
|
|
66
66
|
// Call onAfterClone if set
|
|
67
|
-
|
|
68
|
-
output: result,
|
|
69
|
-
input: data,
|
|
70
|
-
type: dataType,
|
|
71
|
-
cloned: result !== data,
|
|
72
|
-
parent: opts.parent
|
|
73
|
-
});
|
|
67
|
+
|
|
74
68
|
return result;
|
|
75
69
|
}
|
|
76
70
|
// If interceptor returns null/undefined, continue with normal cloning process
|
|
@@ -532,8 +526,25 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
532
526
|
}, opts),
|
|
533
527
|
// Main helper function for recursive merging
|
|
534
528
|
// 递归合并的主辅助函数
|
|
535
|
-
|
|
529
|
+
smartMerger = (target, source, options) => {
|
|
536
530
|
let targetType = getDataType(target), sourceType = getDataType(source), flag = true, type, result;
|
|
531
|
+
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
532
|
+
if (options.interceptor && typeof options.interceptor === 'function') {
|
|
533
|
+
let interceptorResult = options.interceptor({ target, source, parent: options.parent });
|
|
534
|
+
if ((interceptorResult ?? false)) {
|
|
535
|
+
//如果不是返回{target,source},那么直接返回interceptorResult
|
|
536
|
+
if (interceptorResult?.target === null || interceptorResult?.source === null) {
|
|
537
|
+
return interceptorResult;
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
//interceptorResult={target,source}
|
|
541
|
+
target = interceptorResult.target;
|
|
542
|
+
source = interceptorResult.source;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
// If interceptor returns null/undefined, continue with normal cloning process
|
|
546
|
+
}
|
|
547
|
+
options?.onBeforeMerge?.({ target, source, parent: options.parent });
|
|
537
548
|
// Determine the type and perform appropriate merging
|
|
538
549
|
// 确定类型并执行相应的合并
|
|
539
550
|
if (targetType === 'Object' && sourceType === 'Object') {
|
|
@@ -556,6 +567,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
556
567
|
flag = false;
|
|
557
568
|
result = target;
|
|
558
569
|
}
|
|
570
|
+
options?.onAfterMerge?.({ result, target, source, type, parent: opts.parent });
|
|
559
571
|
return { result, flag, type };
|
|
560
572
|
},
|
|
561
573
|
// Special handling for objects with enable property
|
|
@@ -584,7 +596,6 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
584
596
|
if (targetType !== 'Object' || sourceType !== 'Object') {
|
|
585
597
|
return target;
|
|
586
598
|
}
|
|
587
|
-
opts?.onBeforeMerge?.(target, source);
|
|
588
599
|
const options = Object.assign({ inheritMissing: true, targetClone: false, useEnable: true }, opts);
|
|
589
600
|
let result = {};
|
|
590
601
|
// If cloning is enabled, clone the target first
|
|
@@ -592,7 +603,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
592
603
|
result = options.targetClone ? shallowCopy(target) : target;
|
|
593
604
|
for (let k in source) {
|
|
594
605
|
if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
|
|
595
|
-
let resp =
|
|
606
|
+
let resp = smartMerger(result[k], source[k], { ...opts, parent: result });
|
|
596
607
|
//resp={result,flag,type}
|
|
597
608
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
598
609
|
if (!resp.flag) {
|
|
@@ -632,13 +643,11 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
632
643
|
result[k] = source[k];
|
|
633
644
|
}
|
|
634
645
|
}
|
|
635
|
-
options?.onAfterMerge?.(result, target, source);
|
|
636
646
|
return result;
|
|
637
647
|
}, deepMergeArrays = (target, source, options = {}) => {
|
|
638
648
|
// Ensure both target and source are arrays
|
|
639
649
|
if (!Array.isArray(target) || !Array.isArray(source))
|
|
640
650
|
return target;
|
|
641
|
-
options?.onBeforeMerge?.(target, source);
|
|
642
651
|
// Merge options, with default values
|
|
643
652
|
const opts = Object.assign({ dataMode: 'clear', inheritMissing: true, targetClone: false }, options),
|
|
644
653
|
// If cloning is enabled, create a deep copy of the target array
|
|
@@ -651,7 +660,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
651
660
|
// 如果不允许添加超过长度
|
|
652
661
|
if (!opts.inheritMissing && i >= result.length)
|
|
653
662
|
break;
|
|
654
|
-
let resp =
|
|
663
|
+
let resp = smartMerger(result[i], source[i], { ...opts, parent: result });
|
|
655
664
|
//resp={result,flag,type}
|
|
656
665
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
657
666
|
if (!resp.flag) {
|
|
@@ -668,13 +677,11 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
668
677
|
result.length = 0;
|
|
669
678
|
result.push(...source);
|
|
670
679
|
}
|
|
671
|
-
options?.onAfterMerge?.(result, target, source);
|
|
672
680
|
return result;
|
|
673
681
|
}, deepMergeMaps = (target, source, options = {}) => {
|
|
674
682
|
// Ensure both target and source are Maps
|
|
675
683
|
if (!(target instanceof Map) || !(source instanceof Map))
|
|
676
684
|
return target;
|
|
677
|
-
options?.onBeforeMerge?.(target, source);
|
|
678
685
|
// Merge options, with default values
|
|
679
686
|
const opts = Object.assign({ inheritMissing: true, targetClone: false, useEnable: true }, options),
|
|
680
687
|
// If cloning is enabled, create a deep copy of the target Map
|
|
@@ -683,7 +690,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
683
690
|
for (const [key, value] of source.entries())
|
|
684
691
|
// Check if the key already exists in the target Map
|
|
685
692
|
if (result.has(key)) {
|
|
686
|
-
const _target = result.get(key), _source = value, resp =
|
|
693
|
+
const _target = result.get(key), _source = value, resp = smartMerger(_target, _source, opts);
|
|
687
694
|
//resp={result,flag,type}
|
|
688
695
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
689
696
|
if (!resp.flag) {
|
|
@@ -699,13 +706,11 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
699
706
|
// If the key doesn't exist in the target, add the entry from the source Map
|
|
700
707
|
options.inheritMissing && result.set(key, value);
|
|
701
708
|
}
|
|
702
|
-
options?.onAfterMerge?.(result, target, source);
|
|
703
709
|
return result;
|
|
704
710
|
}, deepMergeSets = (target, source, options = {}) => {
|
|
705
711
|
// Ensure both target and source are Sets
|
|
706
712
|
if (!(target instanceof Set) || !(source instanceof Set))
|
|
707
713
|
return target;
|
|
708
|
-
options?.onBeforeMerge?.(target, source);
|
|
709
714
|
// Merge options, with default values
|
|
710
715
|
const opts = Object.assign({ dataMode: 'clear', inheritMissing: true, targetClone: false, useEnable: true }, options),
|
|
711
716
|
// If cloning is enabled, create a deep copy of the target Set
|
|
@@ -713,7 +718,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
713
718
|
// Handle different merge strategies based on dataMode
|
|
714
719
|
if (opts.dataMode === 'replace') {
|
|
715
720
|
// Replace mode: recursively merge items in the Sets
|
|
716
|
-
const _result = [...result], _source = [...source], resp =
|
|
721
|
+
const _result = [...result], _source = [...source], resp = smartMerger(_result, _source, opts);
|
|
717
722
|
result.clear();
|
|
718
723
|
for (let item of resp.result)
|
|
719
724
|
result.add(item);
|
|
@@ -729,10 +734,9 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
729
734
|
for (let item of source)
|
|
730
735
|
result.add(item);
|
|
731
736
|
}
|
|
732
|
-
options?.onAfterMerge?.(result, target, source);
|
|
733
737
|
return result;
|
|
734
738
|
};
|
|
735
|
-
return
|
|
739
|
+
return smartMerger(target, source, options).result;
|
|
736
740
|
};
|
|
737
741
|
|
|
738
742
|
const utils = {
|
package/dist/utils.cjs.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
|
-
"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),
|
|
15
|
+
"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),a=Object.assign({cloneSet:!0,cloneMap:!0,cloneObject:!0,cloneArray:!0,cloneDate:!0,cloneRegex:!0},t);if(a.interceptor&&"function"==typeof a.interceptor){let t=a.interceptor({input:e,type:r,parent:a.parent});if(t)return t}a.onBeforeClone?.({input:e,type:r,parent:a.parent});let o,n=!0;if("Object"===r&&a.cloneObject){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r],a);if(r.length>0)for(const o of r)t[o]=deepClone(e[o],{...a,parent:e});o=t}else if("Array"===r&&a.cloneArray)o=e.map(t=>deepClone(t,{...a,parent:e}));else if("Map"===r&&a.cloneMap){const t=new Map;for(const[r,o]of e)t.set(deepClone(r,a),deepClone(o,{...a,parent:e}));o=t}else if("Set"===r&&a.cloneSet){const t=new Set;for(const r of e)t.add(deepClone(r,{...a,parent:e}));o=t}else if("Date"===r&&a.cloneDate)o=new Date(e.getTime());else if("RegExp"===r&&a.cloneRegex){const t=e;o=new RegExp(t.source,t.flags)}else o=e,n=!1;return a.onAfterClone?.({output:o,input:e,type:r,cloned:n,parent:a.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},arrayMutableMethods=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"],wrapArrayMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a,props:o={}})=>{if(!Array.isArray(e))throw new TypeError("The 'target' parameter must be an array.");a&&!a?.length||(a=arrayMutableMethods);const n={};for(let s of a)n[s]=function(...a){const n={},l=e.length;switch(s){case"push":case"unshift":n.addedItems=[...a];break;case"pop":n.poppedItem=e[l-1];break;case"shift":n.shiftedItem=e[0];break;case"splice":const[t,r]=a,o=t<0?Math.max(l+t,0):Math.min(t,l),s=void 0===r?l-o:r;n.deletedItems=e.slice(o,o+s);break;case"sort":case"reverse":n.oldSnapshot=[...e];break;case"fill":case"copyWithin":const i=a[1]||0,p=void 0===a[2]?l:a[2];n.oldItems=e.slice(i,p),n.start=i,n.end=p}t?.(n);const i=Array.prototype[s].apply(e,a),p={value:i,key:s,args:a,context:n,target:e,...o};return r?.(p),i};return n},requireTypes=(e,t,r)=>{let a=Array.isArray(t)?t:[t],o=getDataType(e),n=o.toLowerCase(),s=a.map(e=>e.toLowerCase()),l=n.includes("html")?"element":n;if(r)try{if(!s.includes(l))throw new TypeError(`Expected data type(s): [${s.join(", ")}], but got: ${l}`)}catch(e){r(e,o)}else if(!s.includes(l))throw new TypeError(`Expected data type(s): [${s.join(", ")}], but got: ${l}`);return o},getUniqueId=(e={})=>{const t=e.prefix,r=e.suffix,a=e.base10,o=e.base36;return`${t?t+"-":""}${Date.now()}${o?"-"+Math.random().toString(36).substring(2,11):""}${a?"-"+Math.floor(1e4*Math.random()).toString().padStart(4,"0"):""}${r?"-"+r:""}`},setMutableMethods=["add","delete","clear"],mapMutableMethods=["set","delete","clear"],wrapSetMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a=setMutableMethods,props:o={}})=>{if(!(e instanceof Set))throw new TypeError("The 'target' parameter must be a Set.");const n={},createWrappedMethod=a=>function(...n){const s={};switch(a){case"add":{const[t]=n;s.addedItem=t,s.existed=e.has(t);break}case"delete":{const[t]=n;s.existed=e.has(t),s.deletedItem=s.existed?t:void 0;break}case"clear":s.clearedItems=Array.from(e),s.previousSize=e.size}t(s);const l=e[a].apply(e,n),i={method:a,result:l,args:n,context:s,target:e,...o};return r(i),l};for(const e of a)setMutableMethods.includes(e)&&(n[e]=createWrappedMethod(e));return Object.defineProperty(n,"target",{get:()=>e,enumerable:!1,configurable:!1}),n},wrapMapMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a=mapMutableMethods,props:o={}})=>{if(!(e instanceof Map))throw new TypeError("The 'target' parameter must be a Map.");const n={},createWrappedMethod=a=>function(...n){const s={};switch(a){case"set":{const[t,r]=n;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]=n;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 l=e[a].apply(e,n),i={method:a,result:l,args:n,context:s,target:e,...o};return r(i),l};for(const e of a)mapMutableMethods.includes(e)&&(n[e]=createWrappedMethod(e));return Object.defineProperty(n,"target",{get:()=>e,enumerable:!1,configurable:!1}),n},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},deepMerge=(e,t,r={})=>{const a=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0,useSymbol:!0,deepClone:{},onBeforeMerge:void 0,onAfterMerge:void 0},r),smartMerger=(e,t,a)=>{let o,n,s=getDataType(e),l=getDataType(t),i=!0;if(a.interceptor&&"function"==typeof a.interceptor){let r=a.interceptor({target:e,source:t,parent:a.parent});if(r){if(null===r?.target||null===r?.source)return r;e=r.target,t=r.source}}return a?.onBeforeMerge?.({target:e,source:t,parent:a.parent}),"Object"===s&&"Object"===l?(n=deepMergeObjects(e,t,a),o="Object"):"Array"===s&&"Array"===l?(n=deepMergeArrays(e,t,a),o="Array"):"Set"===s&&"Set"===l?(n=deepMergeSets(e,t,a),o="Set"):"Map"===s&&"Map"===l?(n=deepMergeMaps(e,t,a),o="Map"):(i=!1,n=e),a?.onAfterMerge?.({result:n,target:e,source:t,type:o,parent:r.parent}),{result:n,flag:i,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 a=getDataType(e),o=getDataType(t);if("Object"!==a||"Object"!==o)return e;const n=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r);let s={};s=n.targetClone?shallowCopy(e):e;for(let e in t)if(t.hasOwnProperty(e)&&s.hasOwnProperty(e)){let a=smartMerger(s[e],t[e],{...r,parent:s});a.flag?a.type?"Object"===a.type&&(s[e]=a.result):s[e]=t[e]:n.useEnable?s[e]=mergeEnableObject(s[e],t[e]):s[e]=t[e]}else t.hasOwnProperty(e)&&!s.hasOwnProperty(e)&&n.inheritMissing&&(s[e]=t[e]);if(n.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 a=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1},r),o=a.targetClone?[...e]:e;if("replace"===a.dataMode)for(let e=0;e<t.length&&(a.inheritMissing||!(e>=o.length));e++){smartMerger(o[e],t[e],{...a,parent:o}).flag||(o[e]=t[e])}else"concat"===a.dataMode||(o.length=0),o.push(...t);return o},deepMergeMaps=(e,t,r={})=>{if(!(e instanceof Map&&t instanceof Map))return e;const a=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r),o=a.targetClone?new Map([...e]):e;for(const[e,n]of t.entries())if(o.has(e)){const t=o.get(e),r=n,s=smartMerger(t,r,a);s.flag?"Object"===s.type&&o.set(e,s.result):o.set(e,r)}else r.inheritMissing&&o.set(e,n);return o},deepMergeSets=(e,t,r={})=>{if(!(e instanceof Set&&t instanceof Set))return e;const a=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0},r),o=a.targetClone?new Set(...e):e;if("replace"===a.dataMode){const e=[...o],r=[...t],n=smartMerger(e,r,a);o.clear();for(let e of n.result)o.add(e)}else if("concat"===a.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,a).result},utils={getDataType:getDataType,requireTypes:requireTypes,deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:wrapArrayMethods,arrayMutableMethods:arrayMutableMethods,setMutableMethods:setMutableMethods,mapMutableMethods:mapMutableMethods,wrapSetMethods:wrapSetMethods,wrapMapMethods:wrapMapMethods,getUniqueId:getUniqueId,deepMerge:deepMerge,shallowCopy:shallowCopy,copyObjectWithSymbol:copyObjectWithSymbol};module.exports=utils;
|
package/dist/utils.esm.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}
|
|
@@ -59,16 +59,10 @@ const deepClone = (data, options = {}) => {
|
|
|
59
59
|
}, options);
|
|
60
60
|
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
61
61
|
if (opts.interceptor && typeof opts.interceptor === 'function') {
|
|
62
|
-
let result = opts.interceptor(data, dataType);
|
|
62
|
+
let result = opts.interceptor({ input: data, type: dataType, parent: opts.parent });
|
|
63
63
|
if ((result ?? false)) {
|
|
64
64
|
// Call onAfterClone if set
|
|
65
|
-
|
|
66
|
-
output: result,
|
|
67
|
-
input: data,
|
|
68
|
-
type: dataType,
|
|
69
|
-
cloned: result !== data,
|
|
70
|
-
parent: opts.parent
|
|
71
|
-
});
|
|
65
|
+
|
|
72
66
|
return result;
|
|
73
67
|
}
|
|
74
68
|
// If interceptor returns null/undefined, continue with normal cloning process
|
|
@@ -530,8 +524,25 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
530
524
|
}, opts),
|
|
531
525
|
// Main helper function for recursive merging
|
|
532
526
|
// 递归合并的主辅助函数
|
|
533
|
-
|
|
527
|
+
smartMerger = (target, source, options) => {
|
|
534
528
|
let targetType = getDataType(target), sourceType = getDataType(source), flag = true, type, result;
|
|
529
|
+
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
530
|
+
if (options.interceptor && typeof options.interceptor === 'function') {
|
|
531
|
+
let interceptorResult = options.interceptor({ target, source, parent: options.parent });
|
|
532
|
+
if ((interceptorResult ?? false)) {
|
|
533
|
+
//如果不是返回{target,source},那么直接返回interceptorResult
|
|
534
|
+
if (interceptorResult?.target === null || interceptorResult?.source === null) {
|
|
535
|
+
return interceptorResult;
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
//interceptorResult={target,source}
|
|
539
|
+
target = interceptorResult.target;
|
|
540
|
+
source = interceptorResult.source;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
// If interceptor returns null/undefined, continue with normal cloning process
|
|
544
|
+
}
|
|
545
|
+
options?.onBeforeMerge?.({ target, source, parent: options.parent });
|
|
535
546
|
// Determine the type and perform appropriate merging
|
|
536
547
|
// 确定类型并执行相应的合并
|
|
537
548
|
if (targetType === 'Object' && sourceType === 'Object') {
|
|
@@ -554,6 +565,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
554
565
|
flag = false;
|
|
555
566
|
result = target;
|
|
556
567
|
}
|
|
568
|
+
options?.onAfterMerge?.({ result, target, source, type, parent: opts.parent });
|
|
557
569
|
return { result, flag, type };
|
|
558
570
|
},
|
|
559
571
|
// Special handling for objects with enable property
|
|
@@ -582,7 +594,6 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
582
594
|
if (targetType !== 'Object' || sourceType !== 'Object') {
|
|
583
595
|
return target;
|
|
584
596
|
}
|
|
585
|
-
opts?.onBeforeMerge?.(target, source);
|
|
586
597
|
const options = Object.assign({ inheritMissing: true, targetClone: false, useEnable: true }, opts);
|
|
587
598
|
let result = {};
|
|
588
599
|
// If cloning is enabled, clone the target first
|
|
@@ -590,7 +601,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
590
601
|
result = options.targetClone ? shallowCopy(target) : target;
|
|
591
602
|
for (let k in source) {
|
|
592
603
|
if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
|
|
593
|
-
let resp =
|
|
604
|
+
let resp = smartMerger(result[k], source[k], { ...opts, parent: result });
|
|
594
605
|
//resp={result,flag,type}
|
|
595
606
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
596
607
|
if (!resp.flag) {
|
|
@@ -630,13 +641,11 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
630
641
|
result[k] = source[k];
|
|
631
642
|
}
|
|
632
643
|
}
|
|
633
|
-
options?.onAfterMerge?.(result, target, source);
|
|
634
644
|
return result;
|
|
635
645
|
}, deepMergeArrays = (target, source, options = {}) => {
|
|
636
646
|
// Ensure both target and source are arrays
|
|
637
647
|
if (!Array.isArray(target) || !Array.isArray(source))
|
|
638
648
|
return target;
|
|
639
|
-
options?.onBeforeMerge?.(target, source);
|
|
640
649
|
// Merge options, with default values
|
|
641
650
|
const opts = Object.assign({ dataMode: 'clear', inheritMissing: true, targetClone: false }, options),
|
|
642
651
|
// If cloning is enabled, create a deep copy of the target array
|
|
@@ -649,7 +658,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
649
658
|
// 如果不允许添加超过长度
|
|
650
659
|
if (!opts.inheritMissing && i >= result.length)
|
|
651
660
|
break;
|
|
652
|
-
let resp =
|
|
661
|
+
let resp = smartMerger(result[i], source[i], { ...opts, parent: result });
|
|
653
662
|
//resp={result,flag,type}
|
|
654
663
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
655
664
|
if (!resp.flag) {
|
|
@@ -666,13 +675,11 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
666
675
|
result.length = 0;
|
|
667
676
|
result.push(...source);
|
|
668
677
|
}
|
|
669
|
-
options?.onAfterMerge?.(result, target, source);
|
|
670
678
|
return result;
|
|
671
679
|
}, deepMergeMaps = (target, source, options = {}) => {
|
|
672
680
|
// Ensure both target and source are Maps
|
|
673
681
|
if (!(target instanceof Map) || !(source instanceof Map))
|
|
674
682
|
return target;
|
|
675
|
-
options?.onBeforeMerge?.(target, source);
|
|
676
683
|
// Merge options, with default values
|
|
677
684
|
const opts = Object.assign({ inheritMissing: true, targetClone: false, useEnable: true }, options),
|
|
678
685
|
// If cloning is enabled, create a deep copy of the target Map
|
|
@@ -681,7 +688,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
681
688
|
for (const [key, value] of source.entries())
|
|
682
689
|
// Check if the key already exists in the target Map
|
|
683
690
|
if (result.has(key)) {
|
|
684
|
-
const _target = result.get(key), _source = value, resp =
|
|
691
|
+
const _target = result.get(key), _source = value, resp = smartMerger(_target, _source, opts);
|
|
685
692
|
//resp={result,flag,type}
|
|
686
693
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
687
694
|
if (!resp.flag) {
|
|
@@ -697,13 +704,11 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
697
704
|
// If the key doesn't exist in the target, add the entry from the source Map
|
|
698
705
|
options.inheritMissing && result.set(key, value);
|
|
699
706
|
}
|
|
700
|
-
options?.onAfterMerge?.(result, target, source);
|
|
701
707
|
return result;
|
|
702
708
|
}, deepMergeSets = (target, source, options = {}) => {
|
|
703
709
|
// Ensure both target and source are Sets
|
|
704
710
|
if (!(target instanceof Set) || !(source instanceof Set))
|
|
705
711
|
return target;
|
|
706
|
-
options?.onBeforeMerge?.(target, source);
|
|
707
712
|
// Merge options, with default values
|
|
708
713
|
const opts = Object.assign({ dataMode: 'clear', inheritMissing: true, targetClone: false, useEnable: true }, options),
|
|
709
714
|
// If cloning is enabled, create a deep copy of the target Set
|
|
@@ -711,7 +716,7 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
711
716
|
// Handle different merge strategies based on dataMode
|
|
712
717
|
if (opts.dataMode === 'replace') {
|
|
713
718
|
// Replace mode: recursively merge items in the Sets
|
|
714
|
-
const _result = [...result], _source = [...source], resp =
|
|
719
|
+
const _result = [...result], _source = [...source], resp = smartMerger(_result, _source, opts);
|
|
715
720
|
result.clear();
|
|
716
721
|
for (let item of resp.result)
|
|
717
722
|
result.add(item);
|
|
@@ -727,10 +732,9 @@ const deepMerge = (target, source, opts = {}) => {
|
|
|
727
732
|
for (let item of source)
|
|
728
733
|
result.add(item);
|
|
729
734
|
}
|
|
730
|
-
options?.onAfterMerge?.(result, target, source);
|
|
731
735
|
return result;
|
|
732
736
|
};
|
|
733
|
-
return
|
|
737
|
+
return smartMerger(target, source, options).result;
|
|
734
738
|
};
|
|
735
739
|
|
|
736
740
|
const utils = {
|
package/dist/utils.esm.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
|
-
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),a=Object.assign({cloneSet:!0,cloneMap:!0,cloneObject:!0,cloneArray:!0,cloneDate:!0,cloneRegex:!0},t);if(a.interceptor&&"function"==typeof a.interceptor){let t=a.interceptor(
|
|
15
|
+
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),a=Object.assign({cloneSet:!0,cloneMap:!0,cloneObject:!0,cloneArray:!0,cloneDate:!0,cloneRegex:!0},t);if(a.interceptor&&"function"==typeof a.interceptor){let t=a.interceptor({input:e,type:r,parent:a.parent});if(t)return t}a.onBeforeClone?.({input:e,type:r,parent:a.parent});let o,n=!0;if("Object"===r&&a.cloneObject){const t={},r=Object.getOwnPropertySymbols(e);for(const r in e)t[r]=deepClone(e[r],a);if(r.length>0)for(const o of r)t[o]=deepClone(e[o],{...a,parent:e});o=t}else if("Array"===r&&a.cloneArray)o=e.map(t=>deepClone(t,{...a,parent:e}));else if("Map"===r&&a.cloneMap){const t=new Map;for(const[r,o]of e)t.set(deepClone(r,a),deepClone(o,{...a,parent:e}));o=t}else if("Set"===r&&a.cloneSet){const t=new Set;for(const r of e)t.add(deepClone(r,{...a,parent:e}));o=t}else if("Date"===r&&a.cloneDate)o=new Date(e.getTime());else if("RegExp"===r&&a.cloneRegex){const t=e;o=new RegExp(t.source,t.flags)}else o=e,n=!1;return a.onAfterClone?.({output:o,input:e,type:r,cloned:n,parent:a.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},arrayMutableMethods=["push","pop","shift","unshift","splice","sort","reverse","copyWithin","fill"],wrapArrayMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a,props:o={}})=>{if(!Array.isArray(e))throw new TypeError("The 'target' parameter must be an array.");a&&!a?.length||(a=arrayMutableMethods);const n={};for(let s of a)n[s]=function(...a){const n={},l=e.length;switch(s){case"push":case"unshift":n.addedItems=[...a];break;case"pop":n.poppedItem=e[l-1];break;case"shift":n.shiftedItem=e[0];break;case"splice":const[t,r]=a,o=t<0?Math.max(l+t,0):Math.min(t,l),s=void 0===r?l-o:r;n.deletedItems=e.slice(o,o+s);break;case"sort":case"reverse":n.oldSnapshot=[...e];break;case"fill":case"copyWithin":const i=a[1]||0,p=void 0===a[2]?l:a[2];n.oldItems=e.slice(i,p),n.start=i,n.end=p}t?.(n);const i=Array.prototype[s].apply(e,a),p={value:i,key:s,args:a,context:n,target:e,...o};return r?.(p),i};return n},requireTypes=(e,t,r)=>{let a=Array.isArray(t)?t:[t],o=getDataType(e),n=o.toLowerCase(),s=a.map(e=>e.toLowerCase()),l=n.includes("html")?"element":n;if(r)try{if(!s.includes(l))throw new TypeError(`Expected data type(s): [${s.join(", ")}], but got: ${l}`)}catch(e){r(e,o)}else if(!s.includes(l))throw new TypeError(`Expected data type(s): [${s.join(", ")}], but got: ${l}`);return o},getUniqueId=(e={})=>{const t=e.prefix,r=e.suffix,a=e.base10,o=e.base36;return`${t?t+"-":""}${Date.now()}${o?"-"+Math.random().toString(36).substring(2,11):""}${a?"-"+Math.floor(1e4*Math.random()).toString().padStart(4,"0"):""}${r?"-"+r:""}`},setMutableMethods=["add","delete","clear"],mapMutableMethods=["set","delete","clear"],wrapSetMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a=setMutableMethods,props:o={}})=>{if(!(e instanceof Set))throw new TypeError("The 'target' parameter must be a Set.");const n={},createWrappedMethod=a=>function(...n){const s={};switch(a){case"add":{const[t]=n;s.addedItem=t,s.existed=e.has(t);break}case"delete":{const[t]=n;s.existed=e.has(t),s.deletedItem=s.existed?t:void 0;break}case"clear":s.clearedItems=Array.from(e),s.previousSize=e.size}t(s);const l=e[a].apply(e,n),i={method:a,result:l,args:n,context:s,target:e,...o};return r(i),l};for(const e of a)setMutableMethods.includes(e)&&(n[e]=createWrappedMethod(e));return Object.defineProperty(n,"target",{get:()=>e,enumerable:!1,configurable:!1}),n},wrapMapMethods=({target:e,onBeforeMutate:t=()=>{},onAfterMutate:r=()=>{},allowList:a=mapMutableMethods,props:o={}})=>{if(!(e instanceof Map))throw new TypeError("The 'target' parameter must be a Map.");const n={},createWrappedMethod=a=>function(...n){const s={};switch(a){case"set":{const[t,r]=n;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]=n;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 l=e[a].apply(e,n),i={method:a,result:l,args:n,context:s,target:e,...o};return r(i),l};for(const e of a)mapMutableMethods.includes(e)&&(n[e]=createWrappedMethod(e));return Object.defineProperty(n,"target",{get:()=>e,enumerable:!1,configurable:!1}),n},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},deepMerge=(e,t,r={})=>{const a=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0,useSymbol:!0,deepClone:{},onBeforeMerge:void 0,onAfterMerge:void 0},r),smartMerger=(e,t,a)=>{let o,n,s=getDataType(e),l=getDataType(t),i=!0;if(a.interceptor&&"function"==typeof a.interceptor){let r=a.interceptor({target:e,source:t,parent:a.parent});if(r){if(null===r?.target||null===r?.source)return r;e=r.target,t=r.source}}return a?.onBeforeMerge?.({target:e,source:t,parent:a.parent}),"Object"===s&&"Object"===l?(n=deepMergeObjects(e,t,a),o="Object"):"Array"===s&&"Array"===l?(n=deepMergeArrays(e,t,a),o="Array"):"Set"===s&&"Set"===l?(n=deepMergeSets(e,t,a),o="Set"):"Map"===s&&"Map"===l?(n=deepMergeMaps(e,t,a),o="Map"):(i=!1,n=e),a?.onAfterMerge?.({result:n,target:e,source:t,type:o,parent:r.parent}),{result:n,flag:i,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 a=getDataType(e),o=getDataType(t);if("Object"!==a||"Object"!==o)return e;const n=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r);let s={};s=n.targetClone?shallowCopy(e):e;for(let e in t)if(t.hasOwnProperty(e)&&s.hasOwnProperty(e)){let a=smartMerger(s[e],t[e],{...r,parent:s});a.flag?a.type?"Object"===a.type&&(s[e]=a.result):s[e]=t[e]:n.useEnable?s[e]=mergeEnableObject(s[e],t[e]):s[e]=t[e]}else t.hasOwnProperty(e)&&!s.hasOwnProperty(e)&&n.inheritMissing&&(s[e]=t[e]);if(n.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 a=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1},r),o=a.targetClone?[...e]:e;if("replace"===a.dataMode)for(let e=0;e<t.length&&(a.inheritMissing||!(e>=o.length));e++){smartMerger(o[e],t[e],{...a,parent:o}).flag||(o[e]=t[e])}else"concat"===a.dataMode||(o.length=0),o.push(...t);return o},deepMergeMaps=(e,t,r={})=>{if(!(e instanceof Map&&t instanceof Map))return e;const a=Object.assign({inheritMissing:!0,targetClone:!1,useEnable:!0},r),o=a.targetClone?new Map([...e]):e;for(const[e,n]of t.entries())if(o.has(e)){const t=o.get(e),r=n,s=smartMerger(t,r,a);s.flag?"Object"===s.type&&o.set(e,s.result):o.set(e,r)}else r.inheritMissing&&o.set(e,n);return o},deepMergeSets=(e,t,r={})=>{if(!(e instanceof Set&&t instanceof Set))return e;const a=Object.assign({dataMode:"clear",inheritMissing:!0,targetClone:!1,useEnable:!0},r),o=a.targetClone?new Set(...e):e;if("replace"===a.dataMode){const e=[...o],r=[...t],n=smartMerger(e,r,a);o.clear();for(let e of n.result)o.add(e)}else if("concat"===a.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,a).result},utils={getDataType:getDataType,requireTypes:requireTypes,deepClone:deepClone,deepCloneToJSON:deepCloneToJSON,wrapArrayMethods:wrapArrayMethods,arrayMutableMethods:arrayMutableMethods,setMutableMethods:setMutableMethods,mapMutableMethods:mapMutableMethods,wrapSetMethods:wrapSetMethods,wrapMapMethods:wrapMapMethods,getUniqueId:getUniqueId,deepMerge:deepMerge,shallowCopy:shallowCopy,copyObjectWithSymbol:copyObjectWithSymbol};export{utils as default};
|
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
|
|
@@ -536,8 +530,25 @@
|
|
|
536
530
|
}, opts),
|
|
537
531
|
// Main helper function for recursive merging
|
|
538
532
|
// 递归合并的主辅助函数
|
|
539
|
-
|
|
533
|
+
smartMerger = (target, source, options) => {
|
|
540
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 });
|
|
541
552
|
// Determine the type and perform appropriate merging
|
|
542
553
|
// 确定类型并执行相应的合并
|
|
543
554
|
if (targetType === 'Object' && sourceType === 'Object') {
|
|
@@ -560,6 +571,7 @@
|
|
|
560
571
|
flag = false;
|
|
561
572
|
result = target;
|
|
562
573
|
}
|
|
574
|
+
options?.onAfterMerge?.({ result, target, source, type, parent: opts.parent });
|
|
563
575
|
return { result, flag, type };
|
|
564
576
|
},
|
|
565
577
|
// Special handling for objects with enable property
|
|
@@ -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) {
|
|
@@ -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) {
|
|
@@ -703,13 +710,11 @@
|
|
|
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 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
|
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 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;
|
package/src/deepMerge.ts
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.
|
|
@@ -59,20 +59,42 @@ export interface DeepMergeOptions {
|
|
|
59
59
|
// 启用克隆时传递给deepClone函数的选项
|
|
60
60
|
deepClone?: Record<string, any>;
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Interceptor function. If returns a non-null value, use it as the merging result.
|
|
64
|
+
* 拦截器函数。如果返回非null值,则将其用作合并结果。
|
|
65
|
+
*/
|
|
66
|
+
interceptor?: (params: {
|
|
67
|
+
target: any;
|
|
68
|
+
source: any;
|
|
69
|
+
parent?: any;
|
|
70
|
+
}) => any;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Callback function executed before merging each data structure
|
|
74
|
+
* 在每个数据结构合并前执行的回调函数
|
|
75
|
+
*/
|
|
76
|
+
onBeforeMerge?: (params: {
|
|
77
|
+
target: any;
|
|
78
|
+
source: any;
|
|
79
|
+
parent?: any;
|
|
80
|
+
}) => void;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Callback function executed after merging each data structure
|
|
84
|
+
* 在每个数据结构合并后执行的回调函数
|
|
85
|
+
*/
|
|
86
|
+
onAfterMerge?: (params: {
|
|
87
|
+
result: any;
|
|
88
|
+
target: any;
|
|
89
|
+
source: any;
|
|
90
|
+
parent?: any;
|
|
91
|
+
type?: '' | 'Object' | 'Array' | 'Map' | 'Set';
|
|
92
|
+
}) => void;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 深度合并时的临时保存的父对象
|
|
96
|
+
*/
|
|
97
|
+
parent?: any;
|
|
76
98
|
}
|
|
77
99
|
export type EnableObject = {
|
|
78
100
|
enable: boolean;
|
|
@@ -122,12 +144,31 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
|
|
|
122
144
|
}, opts),
|
|
123
145
|
// Main helper function for recursive merging
|
|
124
146
|
// 递归合并的主辅助函数
|
|
125
|
-
|
|
147
|
+
smartMerger = (target: any, source: any, options: DeepMergeOptions): any => {
|
|
126
148
|
let targetType = getDataType(target),
|
|
127
149
|
sourceType = getDataType(source),
|
|
128
150
|
flag = true,
|
|
129
|
-
type,
|
|
151
|
+
type: any,
|
|
130
152
|
result;
|
|
153
|
+
|
|
154
|
+
// Check interceptor - if it returns a value (not null/undefined), use it directly
|
|
155
|
+
if (options.interceptor && typeof options.interceptor === 'function') {
|
|
156
|
+
let interceptorResult = options.interceptor({ target, source, parent: options.parent });
|
|
157
|
+
if ((interceptorResult ?? false)) {
|
|
158
|
+
//如果不是返回{target,source},那么直接返回interceptorResult
|
|
159
|
+
if (interceptorResult?.target === null || interceptorResult?.source === null) {
|
|
160
|
+
return interceptorResult;
|
|
161
|
+
} else {
|
|
162
|
+
//interceptorResult={target,source}
|
|
163
|
+
target = interceptorResult.target;
|
|
164
|
+
source = interceptorResult.source;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// If interceptor returns null/undefined, continue with normal cloning process
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
options?.onBeforeMerge?.({ target, source, parent: options.parent });
|
|
171
|
+
|
|
131
172
|
// Determine the type and perform appropriate merging
|
|
132
173
|
// 确定类型并执行相应的合并
|
|
133
174
|
if (targetType === 'Object' && sourceType === 'Object') {
|
|
@@ -146,6 +187,7 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
|
|
|
146
187
|
flag = false;
|
|
147
188
|
result = target;
|
|
148
189
|
}
|
|
190
|
+
options?.onAfterMerge?.({ result, target, source, type, parent: opts.parent });
|
|
149
191
|
return { result, flag, type };
|
|
150
192
|
},
|
|
151
193
|
// Special handling for objects with enable property
|
|
@@ -174,7 +216,6 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
|
|
|
174
216
|
if (targetType !== 'Object' || sourceType !== 'Object') {
|
|
175
217
|
return target;
|
|
176
218
|
}
|
|
177
|
-
opts?.onBeforeMerge?.(target, source);
|
|
178
219
|
|
|
179
220
|
const options = Object.assign({ inheritMissing: true, targetClone: false, useEnable: true }, opts);
|
|
180
221
|
let result: any = {};
|
|
@@ -185,7 +226,7 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
|
|
|
185
226
|
for (let k in source) {
|
|
186
227
|
if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
|
|
187
228
|
|
|
188
|
-
let resp =
|
|
229
|
+
let resp = smartMerger((result as any)[k], source[k], { ...opts, parent: result });
|
|
189
230
|
//resp={result,flag,type}
|
|
190
231
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
191
232
|
if (!resp.flag) {
|
|
@@ -221,13 +262,12 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
|
|
|
221
262
|
for (let k of symbols) (result as any)[k] = source[k];
|
|
222
263
|
}
|
|
223
264
|
}
|
|
224
|
-
options?.onAfterMerge?.(result, target, source);
|
|
225
265
|
return result;
|
|
226
266
|
},
|
|
227
267
|
deepMergeArrays = (target: any[], source: any[], options: DeepMergeOptions = {}): any[] => {
|
|
228
268
|
// Ensure both target and source are arrays
|
|
229
269
|
if (!Array.isArray(target) || !Array.isArray(source)) return target;
|
|
230
|
-
|
|
270
|
+
|
|
231
271
|
// Merge options, with default values
|
|
232
272
|
const opts = Object.assign({ dataMode: 'clear', inheritMissing: true, targetClone: false }, options),
|
|
233
273
|
|
|
@@ -241,7 +281,7 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
|
|
|
241
281
|
// If not allowed to add beyond length
|
|
242
282
|
// 如果不允许添加超过长度
|
|
243
283
|
if (!opts.inheritMissing && i >= result.length) break;
|
|
244
|
-
let resp =
|
|
284
|
+
let resp = smartMerger(result[i], source[i], { ...opts, parent: result });
|
|
245
285
|
//resp={result,flag,type}
|
|
246
286
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
247
287
|
if (!resp.flag) {
|
|
@@ -256,13 +296,12 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
|
|
|
256
296
|
result.length = 0;
|
|
257
297
|
result.push(...source);
|
|
258
298
|
}
|
|
259
|
-
options?.onAfterMerge?.(result, target, source);
|
|
260
299
|
return result;
|
|
261
300
|
},
|
|
262
301
|
deepMergeMaps = (target: Map<any, any>, source: Map<any, any>, options: DeepMergeOptions = {}): Map<any, any> => {
|
|
263
302
|
// Ensure both target and source are Maps
|
|
264
303
|
if (!(target instanceof Map) || !(source instanceof Map)) return target;
|
|
265
|
-
|
|
304
|
+
|
|
266
305
|
// Merge options, with default values
|
|
267
306
|
const opts = Object.assign({ inheritMissing: true, targetClone: false, useEnable: true }, options),
|
|
268
307
|
|
|
@@ -275,7 +314,7 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
|
|
|
275
314
|
if (result.has(key)) {
|
|
276
315
|
const _target = result.get(key),
|
|
277
316
|
_source = value,
|
|
278
|
-
resp =
|
|
317
|
+
resp = smartMerger(_target, _source, opts);
|
|
279
318
|
//resp={result,flag,type}
|
|
280
319
|
//flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
|
|
281
320
|
if (!resp.flag) {
|
|
@@ -289,13 +328,12 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
|
|
|
289
328
|
// If the key doesn't exist in the target, add the entry from the source Map
|
|
290
329
|
options.inheritMissing && result.set(key, value);
|
|
291
330
|
}
|
|
292
|
-
options?.onAfterMerge?.(result, target, source);
|
|
293
331
|
return result;
|
|
294
332
|
},
|
|
295
333
|
deepMergeSets = (target: Set<any>, source: Set<any>, options: DeepMergeOptions = {}): Set<any> => {
|
|
296
334
|
// Ensure both target and source are Sets
|
|
297
335
|
if (!(target instanceof Set) || !(source instanceof Set)) return target;
|
|
298
|
-
|
|
336
|
+
|
|
299
337
|
// Merge options, with default values
|
|
300
338
|
const opts = Object.assign({ dataMode: 'clear', inheritMissing: true, targetClone: false, useEnable: true }, options),
|
|
301
339
|
|
|
@@ -307,11 +345,10 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
|
|
|
307
345
|
// Replace mode: recursively merge items in the Sets
|
|
308
346
|
const _result = [...result],
|
|
309
347
|
_source = [...source],
|
|
310
|
-
resp =
|
|
348
|
+
resp = smartMerger(_result, _source, opts);
|
|
311
349
|
result.clear();
|
|
312
350
|
for (let item of resp.result) result.add(item);
|
|
313
351
|
|
|
314
|
-
|
|
315
352
|
} else if (opts.dataMode === 'concat') {
|
|
316
353
|
// Concatenate mode: add all items from the source Set to the target Set
|
|
317
354
|
for (let item of source) result.add(item);
|
|
@@ -320,9 +357,11 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
|
|
|
320
357
|
result.clear();
|
|
321
358
|
for (let item of source) result.add(item);
|
|
322
359
|
}
|
|
323
|
-
|
|
360
|
+
|
|
324
361
|
return result;
|
|
325
362
|
};
|
|
326
|
-
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
return smartMerger(target, source, options).result;
|
|
327
366
|
}
|
|
328
367
|
export default deepMerge;
|