@codady/utils 0.0.16 → 0.0.18

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 CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All changes to Utils including new features, updates, and removals are documented here.
4
4
 
5
+
6
+ ## [v0.0.18] - 2025-12-27
7
+
8
+ ### Distribution Files
9
+ * **JS**: https://unpkg.com/@codady/utils@0.0.18/dist/js/utils.js
10
+ * **Zip**:https://unpkg.com/@codady/utils@0.0.18/dist.zip
11
+
12
+ ### Changes
13
+
14
+ #### Fixed
15
+ * Null
16
+
17
+ #### Added
18
+ * Modified the `deepMerge` function to add `nullBehavior` and `undefinedBehavior` properties to the parameters for handling `null` and `undefined` values.修改了`deepMerge`函数,对参数增加`nullBehavior`和`undefinedBehavior`属性,用来处理null和undefined值。
19
+
20
+
21
+ #### Removed
22
+ * Null
23
+
24
+
5
25
  ## [v0.0.16] - 2025-12-26
6
26
 
7
27
  ### Distribution Files
package/dist/utils.cjs.js CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
- * @since Last modified: 2025-12-26 15:47:37
3
+ * @since Last modified: 2025-12-27 14:35:7
4
4
  * @name Utils for web front-end.
5
- * @version 0.0.16
5
+ * @version 0.0.18
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}
@@ -514,6 +514,8 @@ const deepMerge = (target, source, opts = {}) => {
514
514
  // Whether to allow merging key=symbol key-value pairs when target is {} type.
515
515
  // target是{}类型时,是否允许合并key=symbol的键值对。
516
516
  useSymbol: true,
517
+ nullBehavior: 'preserve',
518
+ undefinedBehavior: 'preserve',
517
519
  // Options passed to the deepClone function when targetClone is true
518
520
  // 当targetClone为true时传递给deepClone函数的选项
519
521
  deepClone: {},
@@ -527,10 +529,10 @@ const deepMerge = (target, source, opts = {}) => {
527
529
  // Main helper function for recursive merging
528
530
  // 递归合并的主辅助函数
529
531
  smartMerger = (target, source, options) => {
530
- let targetType = getDataType(target), sourceType = getDataType(source), flag = true, type, result;
532
+ let targetType = getDataType(target), sourceType = getDataType(source), flag = true, mergeType, result;
531
533
  // Check interceptor - if it returns a value (not null/undefined), use it directly
532
534
  if (options.interceptor && typeof options.interceptor === 'function') {
533
- let interceptorResult = options.interceptor({ target, source, parent: options.parent });
535
+ let interceptorResult = options.interceptor({ target, source, targetType, sourceType, parent: options.parent });
534
536
  if ((interceptorResult ?? false)) {
535
537
  //如果不是返回{target,source},那么直接返回interceptorResult
536
538
  if (interceptorResult?.target === null || interceptorResult?.source === null) {
@@ -544,31 +546,31 @@ const deepMerge = (target, source, opts = {}) => {
544
546
  }
545
547
  // If interceptor returns null/undefined, continue with normal cloning process
546
548
  }
547
- options?.onBeforeMerge?.({ target, source, parent: options.parent });
549
+ options?.onBeforeMerge?.({ target, source, targetType, sourceType, parent: options.parent });
548
550
  // Determine the type and perform appropriate merging
549
551
  // 确定类型并执行相应的合并
550
552
  if (targetType === 'Object' && sourceType === 'Object') {
551
553
  result = deepMergeObjects(target, source, options);
552
- type = 'Object';
554
+ mergeType = 'Object';
553
555
  }
554
556
  else if (targetType === 'Array' && sourceType === 'Array') {
555
557
  result = deepMergeArrays(target, source, options);
556
- type = 'Array';
558
+ mergeType = 'Array';
557
559
  }
558
560
  else if (targetType === 'Set' && sourceType === 'Set') {
559
561
  result = deepMergeSets(target, source, options);
560
- type = 'Set';
562
+ mergeType = 'Set';
561
563
  }
562
564
  else if (targetType === 'Map' && sourceType === 'Map') {
563
565
  result = deepMergeMaps(target, source, options);
564
- type = 'Map';
566
+ mergeType = 'Map';
565
567
  }
566
568
  else {
567
569
  flag = false;
568
570
  result = target;
569
571
  }
570
- options?.onAfterMerge?.({ result, target, source, type, parent: opts.parent });
571
- return { result, flag, type };
572
+ options?.onAfterMerge?.({ result, target, source, targetType, sourceType, mergeType, parent: opts.parent });
573
+ return { result, flag, mergeType };
572
574
  },
573
575
  // Special handling for objects with enable property
574
576
  // 对具有enable属性的对象进行特殊处理
@@ -602,37 +604,57 @@ const deepMerge = (target, source, opts = {}) => {
602
604
  // 如果是复制方法,则先复制target
603
605
  result = options.targetClone ? shallowCopy(target) : target;
604
606
  for (let k in source) {
607
+ const _result = result[k], _source = source[k];
605
608
  if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
606
- let resp = smartMerger(result[k], source[k], { ...opts, parent: result });
609
+ const resp = smartMerger(_result, _source, { ...opts, parent: result });
607
610
  //resp={result,flag,type}
608
- //flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
611
+ //flag=true表示类型一致(object/array/map/set)并完成了合并,false表示并没有合并需要直接赋值
609
612
  if (!resp.flag) {
610
613
  //类型不同则直接覆盖
611
- if (options.useEnable) {
612
- result[k] = mergeEnableObject(result[k], source[k]);
614
+ let tmp = options.useEnable ? mergeEnableObject(_result, _source) : _source;
615
+ if (_result !== tmp && tmp === null) {
616
+ if (options.nullBehavior === 'ignore') ;
617
+ else if (options.nullBehavior === 'delete') {
618
+ //删除属性
619
+ Reflect.deleteProperty(result, k);
620
+ }
621
+ else {
622
+ //替换
623
+ result[k] = tmp;
624
+ }
625
+ }
626
+ else if (_result !== tmp && tmp === undefined) {
627
+ if (options.undefinedBehavior === 'ignore') ;
628
+ else if (options.undefinedBehavior === 'delete') {
629
+ //删除属性
630
+ Reflect.deleteProperty(result, k);
631
+ }
632
+ else {
633
+ //替换
634
+ result[k] = _source;
635
+ }
613
636
  }
614
637
  else {
615
- //完全替换
616
- result[k] = source[k];
638
+ result[k] = _source;
617
639
  }
618
640
  }
619
641
  else {
620
642
  //类型相同
621
- if (resp.type) {
622
- if (resp.type === 'Object') {
643
+ if (resp.mergeType) {
644
+ if (resp.mergeType === 'Object') {
623
645
  //如果遇上对象则深度复制
624
646
  result[k] = resp.result;
625
647
  }
626
648
  }
627
649
  else {
628
650
  //其他类型则直接覆盖
629
- result[k] = source[k];
651
+ result[k] = _source;
630
652
  }
631
653
  }
632
654
  }
633
655
  else if (source.hasOwnProperty(k) && !result.hasOwnProperty(k) && options.inheritMissing) {
634
656
  //如果source有属性,result没有该属性,但是options允许追加属性则直接赋值
635
- result[k] = source[k];
657
+ result[k] = _source;
636
658
  }
637
659
  }
638
660
  //Symbol键直接追加,因为Symbol是唯一,结果同Object.assign
@@ -699,7 +721,7 @@ const deepMerge = (target, source, opts = {}) => {
699
721
  }
700
722
  else {
701
723
  // If both target and source are objects, merge them recursively
702
- resp.type === 'Object' && result.set(key, resp.result);
724
+ resp.mergeType === 'Object' && result.set(key, resp.result);
703
725
  }
704
726
  }
705
727
  else {
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * @since Last modified: 2025-12-26 15:47:37
2
+ * @since Last modified: 2025-12-27 14:35:7
3
3
  * @name Utils for web front-end.
4
- * @version 0.0.16
4
+ * @version 0.0.18
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),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;
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,nullBehavior:"preserve",undefinedBehavior:"preserve",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,targetType:s,sourceType:l,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,targetType:s,sourceType:l,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,targetType:s,sourceType:l,mergeType:o,parent:r.parent}),{result:n,flag:i,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 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){const a=s[e],o=t[e];if(t.hasOwnProperty(e)&&s.hasOwnProperty(e)){const t=smartMerger(a,o,{...r,parent:s});if(t.flag)t.mergeType?"Object"===t.mergeType&&(s[e]=t.result):s[e]=o;else{let t=n.useEnable?mergeEnableObject(a,o):o;a!==t&&null===t?"ignore"===n.nullBehavior||("delete"===n.nullBehavior?Reflect.deleteProperty(s,e):s[e]=t):a!==t&&void 0===t?"ignore"===n.undefinedBehavior||("delete"===n.undefinedBehavior?Reflect.deleteProperty(s,e):s[e]=o):s[e]=o}}else t.hasOwnProperty(e)&&!s.hasOwnProperty(e)&&n.inheritMissing&&(s[e]=o)}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.mergeType&&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 15:47:37
3
+ * @since Last modified: 2025-12-27 14:35:7
4
4
  * @name Utils for web front-end.
5
- * @version 0.0.16
5
+ * @version 0.0.18
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}
@@ -512,6 +512,8 @@ const deepMerge = (target, source, opts = {}) => {
512
512
  // Whether to allow merging key=symbol key-value pairs when target is {} type.
513
513
  // target是{}类型时,是否允许合并key=symbol的键值对。
514
514
  useSymbol: true,
515
+ nullBehavior: 'preserve',
516
+ undefinedBehavior: 'preserve',
515
517
  // Options passed to the deepClone function when targetClone is true
516
518
  // 当targetClone为true时传递给deepClone函数的选项
517
519
  deepClone: {},
@@ -525,10 +527,10 @@ const deepMerge = (target, source, opts = {}) => {
525
527
  // Main helper function for recursive merging
526
528
  // 递归合并的主辅助函数
527
529
  smartMerger = (target, source, options) => {
528
- let targetType = getDataType(target), sourceType = getDataType(source), flag = true, type, result;
530
+ let targetType = getDataType(target), sourceType = getDataType(source), flag = true, mergeType, result;
529
531
  // Check interceptor - if it returns a value (not null/undefined), use it directly
530
532
  if (options.interceptor && typeof options.interceptor === 'function') {
531
- let interceptorResult = options.interceptor({ target, source, parent: options.parent });
533
+ let interceptorResult = options.interceptor({ target, source, targetType, sourceType, parent: options.parent });
532
534
  if ((interceptorResult ?? false)) {
533
535
  //如果不是返回{target,source},那么直接返回interceptorResult
534
536
  if (interceptorResult?.target === null || interceptorResult?.source === null) {
@@ -542,31 +544,31 @@ const deepMerge = (target, source, opts = {}) => {
542
544
  }
543
545
  // If interceptor returns null/undefined, continue with normal cloning process
544
546
  }
545
- options?.onBeforeMerge?.({ target, source, parent: options.parent });
547
+ options?.onBeforeMerge?.({ target, source, targetType, sourceType, parent: options.parent });
546
548
  // Determine the type and perform appropriate merging
547
549
  // 确定类型并执行相应的合并
548
550
  if (targetType === 'Object' && sourceType === 'Object') {
549
551
  result = deepMergeObjects(target, source, options);
550
- type = 'Object';
552
+ mergeType = 'Object';
551
553
  }
552
554
  else if (targetType === 'Array' && sourceType === 'Array') {
553
555
  result = deepMergeArrays(target, source, options);
554
- type = 'Array';
556
+ mergeType = 'Array';
555
557
  }
556
558
  else if (targetType === 'Set' && sourceType === 'Set') {
557
559
  result = deepMergeSets(target, source, options);
558
- type = 'Set';
560
+ mergeType = 'Set';
559
561
  }
560
562
  else if (targetType === 'Map' && sourceType === 'Map') {
561
563
  result = deepMergeMaps(target, source, options);
562
- type = 'Map';
564
+ mergeType = 'Map';
563
565
  }
564
566
  else {
565
567
  flag = false;
566
568
  result = target;
567
569
  }
568
- options?.onAfterMerge?.({ result, target, source, type, parent: opts.parent });
569
- return { result, flag, type };
570
+ options?.onAfterMerge?.({ result, target, source, targetType, sourceType, mergeType, parent: opts.parent });
571
+ return { result, flag, mergeType };
570
572
  },
571
573
  // Special handling for objects with enable property
572
574
  // 对具有enable属性的对象进行特殊处理
@@ -600,37 +602,57 @@ const deepMerge = (target, source, opts = {}) => {
600
602
  // 如果是复制方法,则先复制target
601
603
  result = options.targetClone ? shallowCopy(target) : target;
602
604
  for (let k in source) {
605
+ const _result = result[k], _source = source[k];
603
606
  if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
604
- let resp = smartMerger(result[k], source[k], { ...opts, parent: result });
607
+ const resp = smartMerger(_result, _source, { ...opts, parent: result });
605
608
  //resp={result,flag,type}
606
- //flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
609
+ //flag=true表示类型一致(object/array/map/set)并完成了合并,false表示并没有合并需要直接赋值
607
610
  if (!resp.flag) {
608
611
  //类型不同则直接覆盖
609
- if (options.useEnable) {
610
- result[k] = mergeEnableObject(result[k], source[k]);
612
+ let tmp = options.useEnable ? mergeEnableObject(_result, _source) : _source;
613
+ if (_result !== tmp && tmp === null) {
614
+ if (options.nullBehavior === 'ignore') ;
615
+ else if (options.nullBehavior === 'delete') {
616
+ //删除属性
617
+ Reflect.deleteProperty(result, k);
618
+ }
619
+ else {
620
+ //替换
621
+ result[k] = tmp;
622
+ }
623
+ }
624
+ else if (_result !== tmp && tmp === undefined) {
625
+ if (options.undefinedBehavior === 'ignore') ;
626
+ else if (options.undefinedBehavior === 'delete') {
627
+ //删除属性
628
+ Reflect.deleteProperty(result, k);
629
+ }
630
+ else {
631
+ //替换
632
+ result[k] = _source;
633
+ }
611
634
  }
612
635
  else {
613
- //完全替换
614
- result[k] = source[k];
636
+ result[k] = _source;
615
637
  }
616
638
  }
617
639
  else {
618
640
  //类型相同
619
- if (resp.type) {
620
- if (resp.type === 'Object') {
641
+ if (resp.mergeType) {
642
+ if (resp.mergeType === 'Object') {
621
643
  //如果遇上对象则深度复制
622
644
  result[k] = resp.result;
623
645
  }
624
646
  }
625
647
  else {
626
648
  //其他类型则直接覆盖
627
- result[k] = source[k];
649
+ result[k] = _source;
628
650
  }
629
651
  }
630
652
  }
631
653
  else if (source.hasOwnProperty(k) && !result.hasOwnProperty(k) && options.inheritMissing) {
632
654
  //如果source有属性,result没有该属性,但是options允许追加属性则直接赋值
633
- result[k] = source[k];
655
+ result[k] = _source;
634
656
  }
635
657
  }
636
658
  //Symbol键直接追加,因为Symbol是唯一,结果同Object.assign
@@ -697,7 +719,7 @@ const deepMerge = (target, source, opts = {}) => {
697
719
  }
698
720
  else {
699
721
  // If both target and source are objects, merge them recursively
700
- resp.type === 'Object' && result.set(key, resp.result);
722
+ resp.mergeType === 'Object' && result.set(key, resp.result);
701
723
  }
702
724
  }
703
725
  else {
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * @since Last modified: 2025-12-26 15:47:37
2
+ * @since Last modified: 2025-12-27 14:35:7
3
3
  * @name Utils for web front-end.
4
- * @version 0.0.16
4
+ * @version 0.0.18
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({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};
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,nullBehavior:"preserve",undefinedBehavior:"preserve",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,targetType:s,sourceType:l,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,targetType:s,sourceType:l,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,targetType:s,sourceType:l,mergeType:o,parent:r.parent}),{result:n,flag:i,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 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){const a=s[e],o=t[e];if(t.hasOwnProperty(e)&&s.hasOwnProperty(e)){const t=smartMerger(a,o,{...r,parent:s});if(t.flag)t.mergeType?"Object"===t.mergeType&&(s[e]=t.result):s[e]=o;else{let t=n.useEnable?mergeEnableObject(a,o):o;a!==t&&null===t?"ignore"===n.nullBehavior||("delete"===n.nullBehavior?Reflect.deleteProperty(s,e):s[e]=t):a!==t&&void 0===t?"ignore"===n.undefinedBehavior||("delete"===n.undefinedBehavior?Reflect.deleteProperty(s,e):s[e]=o):s[e]=o}}else t.hasOwnProperty(e)&&!s.hasOwnProperty(e)&&n.inheritMissing&&(s[e]=o)}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.mergeType&&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 15:47:37
3
+ * @since Last modified: 2025-12-27 14:35:7
4
4
  * @name Utils for web front-end.
5
- * @version 0.0.16
5
+ * @version 0.0.18
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}
@@ -518,6 +518,8 @@
518
518
  // Whether to allow merging key=symbol key-value pairs when target is {} type.
519
519
  // target是{}类型时,是否允许合并key=symbol的键值对。
520
520
  useSymbol: true,
521
+ nullBehavior: 'preserve',
522
+ undefinedBehavior: 'preserve',
521
523
  // Options passed to the deepClone function when targetClone is true
522
524
  // 当targetClone为true时传递给deepClone函数的选项
523
525
  deepClone: {},
@@ -531,10 +533,10 @@
531
533
  // Main helper function for recursive merging
532
534
  // 递归合并的主辅助函数
533
535
  smartMerger = (target, source, options) => {
534
- let targetType = getDataType(target), sourceType = getDataType(source), flag = true, type, result;
536
+ let targetType = getDataType(target), sourceType = getDataType(source), flag = true, mergeType, result;
535
537
  // Check interceptor - if it returns a value (not null/undefined), use it directly
536
538
  if (options.interceptor && typeof options.interceptor === 'function') {
537
- let interceptorResult = options.interceptor({ target, source, parent: options.parent });
539
+ let interceptorResult = options.interceptor({ target, source, targetType, sourceType, parent: options.parent });
538
540
  if ((interceptorResult ?? false)) {
539
541
  //如果不是返回{target,source},那么直接返回interceptorResult
540
542
  if (interceptorResult?.target === null || interceptorResult?.source === null) {
@@ -548,31 +550,31 @@
548
550
  }
549
551
  // If interceptor returns null/undefined, continue with normal cloning process
550
552
  }
551
- options?.onBeforeMerge?.({ target, source, parent: options.parent });
553
+ options?.onBeforeMerge?.({ target, source, targetType, sourceType, parent: options.parent });
552
554
  // Determine the type and perform appropriate merging
553
555
  // 确定类型并执行相应的合并
554
556
  if (targetType === 'Object' && sourceType === 'Object') {
555
557
  result = deepMergeObjects(target, source, options);
556
- type = 'Object';
558
+ mergeType = 'Object';
557
559
  }
558
560
  else if (targetType === 'Array' && sourceType === 'Array') {
559
561
  result = deepMergeArrays(target, source, options);
560
- type = 'Array';
562
+ mergeType = 'Array';
561
563
  }
562
564
  else if (targetType === 'Set' && sourceType === 'Set') {
563
565
  result = deepMergeSets(target, source, options);
564
- type = 'Set';
566
+ mergeType = 'Set';
565
567
  }
566
568
  else if (targetType === 'Map' && sourceType === 'Map') {
567
569
  result = deepMergeMaps(target, source, options);
568
- type = 'Map';
570
+ mergeType = 'Map';
569
571
  }
570
572
  else {
571
573
  flag = false;
572
574
  result = target;
573
575
  }
574
- options?.onAfterMerge?.({ result, target, source, type, parent: opts.parent });
575
- return { result, flag, type };
576
+ options?.onAfterMerge?.({ result, target, source, targetType, sourceType, mergeType, parent: opts.parent });
577
+ return { result, flag, mergeType };
576
578
  },
577
579
  // Special handling for objects with enable property
578
580
  // 对具有enable属性的对象进行特殊处理
@@ -606,37 +608,57 @@
606
608
  // 如果是复制方法,则先复制target
607
609
  result = options.targetClone ? shallowCopy(target) : target;
608
610
  for (let k in source) {
611
+ const _result = result[k], _source = source[k];
609
612
  if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
610
- let resp = smartMerger(result[k], source[k], { ...opts, parent: result });
613
+ const resp = smartMerger(_result, _source, { ...opts, parent: result });
611
614
  //resp={result,flag,type}
612
- //flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
615
+ //flag=true表示类型一致(object/array/map/set)并完成了合并,false表示并没有合并需要直接赋值
613
616
  if (!resp.flag) {
614
617
  //类型不同则直接覆盖
615
- if (options.useEnable) {
616
- result[k] = mergeEnableObject(result[k], source[k]);
618
+ let tmp = options.useEnable ? mergeEnableObject(_result, _source) : _source;
619
+ if (_result !== tmp && tmp === null) {
620
+ if (options.nullBehavior === 'ignore') ;
621
+ else if (options.nullBehavior === 'delete') {
622
+ //删除属性
623
+ Reflect.deleteProperty(result, k);
624
+ }
625
+ else {
626
+ //替换
627
+ result[k] = tmp;
628
+ }
629
+ }
630
+ else if (_result !== tmp && tmp === undefined) {
631
+ if (options.undefinedBehavior === 'ignore') ;
632
+ else if (options.undefinedBehavior === 'delete') {
633
+ //删除属性
634
+ Reflect.deleteProperty(result, k);
635
+ }
636
+ else {
637
+ //替换
638
+ result[k] = _source;
639
+ }
617
640
  }
618
641
  else {
619
- //完全替换
620
- result[k] = source[k];
642
+ result[k] = _source;
621
643
  }
622
644
  }
623
645
  else {
624
646
  //类型相同
625
- if (resp.type) {
626
- if (resp.type === 'Object') {
647
+ if (resp.mergeType) {
648
+ if (resp.mergeType === 'Object') {
627
649
  //如果遇上对象则深度复制
628
650
  result[k] = resp.result;
629
651
  }
630
652
  }
631
653
  else {
632
654
  //其他类型则直接覆盖
633
- result[k] = source[k];
655
+ result[k] = _source;
634
656
  }
635
657
  }
636
658
  }
637
659
  else if (source.hasOwnProperty(k) && !result.hasOwnProperty(k) && options.inheritMissing) {
638
660
  //如果source有属性,result没有该属性,但是options允许追加属性则直接赋值
639
- result[k] = source[k];
661
+ result[k] = _source;
640
662
  }
641
663
  }
642
664
  //Symbol键直接追加,因为Symbol是唯一,结果同Object.assign
@@ -703,7 +725,7 @@
703
725
  }
704
726
  else {
705
727
  // If both target and source are objects, merge them recursively
706
- resp.type === 'Object' && result.set(key, resp.result);
728
+ resp.mergeType === 'Object' && result.set(key, resp.result);
707
729
  }
708
730
  }
709
731
  else {
@@ -1,7 +1,7 @@
1
1
  /*!
2
- * @since Last modified: 2025-12-26 15:47:37
2
+ * @since Last modified: 2025-12-27 14:35:7
3
3
  * @name Utils for web front-end.
4
- * @version 0.0.16
4
+ * @version 0.0.18
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({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}});
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 l=o[1]||0,c=void 0===o[2]?i:o[2];s.oldItems=t.slice(l,c),s.start=l,s.end=c}r?.(s);const l=Array.prototype[e].apply(t,o),c={value:l,key:e,args:o,context:s,target:t,...a};return n?.(c),l};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),l={method:t,result:i,args:o,context:s,target:e,...a};return n(l),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),l={method:r,result:i,args:o,context:s,target:e,...a};return n(l),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,nullBehavior:"preserve",undefinedBehavior:"preserve",deepClone:{},onBeforeMerge:void 0,onAfterMerge:void 0},r),smartMerger=(e,t,n)=>{let o,a,s=getDataType(e),i=getDataType(t),l=!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"):(l=!1,a=e),n?.onAfterMerge?.({result:a,target:e,source:t,targetType:s,sourceType:i,mergeType:o,parent:r.parent}),{result:a,flag:l,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){const n=s[e],o=t[e];if(t.hasOwnProperty(e)&&s.hasOwnProperty(e)){const t=smartMerger(n,o,{...r,parent:s});if(t.flag)t.mergeType?"Object"===t.mergeType&&(s[e]=t.result):s[e]=o;else{let t=a.useEnable?mergeEnableObject(n,o):o;n!==t&&null===t?"ignore"===a.nullBehavior||("delete"===a.nullBehavior?Reflect.deleteProperty(s,e):s[e]=t):n!==t&&void 0===t?"ignore"===a.undefinedBehavior||("delete"===a.undefinedBehavior?Reflect.deleteProperty(s,e):s[e]=o):s[e]=o}}else t.hasOwnProperty(e)&&!s.hasOwnProperty(e)&&a.inheritMissing&&(s[e]=o)}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.16",
3
+ "version": "0.0.18",
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 15:13:54
2
+ * @since Last modified: 2025/12/27 14:34:57
3
3
  * Deep clone an array, object, or other cloneable data types.
4
4
  *
5
5
  * Features:
package/src/deepClone.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @since Last modified: 2025/12/26 15:13:54
2
+ * @since Last modified: 2025/12/27 14:34:57
3
3
  * Deep clone an array, object, or other cloneable data types.
4
4
  *
5
5
  * Features:
package/src/deepMerge.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @since Last modified: 2025/12/26 15:40:50
2
+ * @since Last modified: 2025/12/27 14:35:01
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.
@@ -57,6 +57,8 @@ const deepMerge = (target, source, opts = {}) => {
57
57
  // Whether to allow merging key=symbol key-value pairs when target is {} type.
58
58
  // target是{}类型时,是否允许合并key=symbol的键值对。
59
59
  useSymbol: true,
60
+ nullBehavior: 'preserve',
61
+ undefinedBehavior: 'preserve',
60
62
  // Options passed to the deepClone function when targetClone is true
61
63
  // 当targetClone为true时传递给deepClone函数的选项
62
64
  deepClone: {},
@@ -70,10 +72,10 @@ const deepMerge = (target, source, opts = {}) => {
70
72
  // Main helper function for recursive merging
71
73
  // 递归合并的主辅助函数
72
74
  smartMerger = (target, source, options) => {
73
- let targetType = getDataType(target), sourceType = getDataType(source), flag = true, type, result;
75
+ let targetType = getDataType(target), sourceType = getDataType(source), flag = true, mergeType, result;
74
76
  // Check interceptor - if it returns a value (not null/undefined), use it directly
75
77
  if (options.interceptor && typeof options.interceptor === 'function') {
76
- let interceptorResult = options.interceptor({ target, source, parent: options.parent });
78
+ let interceptorResult = options.interceptor({ target, source, targetType, sourceType, parent: options.parent });
77
79
  if ((interceptorResult ?? false)) {
78
80
  //如果不是返回{target,source},那么直接返回interceptorResult
79
81
  if (interceptorResult?.target === null || interceptorResult?.source === null) {
@@ -87,31 +89,31 @@ const deepMerge = (target, source, opts = {}) => {
87
89
  }
88
90
  // If interceptor returns null/undefined, continue with normal cloning process
89
91
  }
90
- options?.onBeforeMerge?.({ target, source, parent: options.parent });
92
+ options?.onBeforeMerge?.({ target, source, targetType, sourceType, parent: options.parent });
91
93
  // Determine the type and perform appropriate merging
92
94
  // 确定类型并执行相应的合并
93
95
  if (targetType === 'Object' && sourceType === 'Object') {
94
96
  result = deepMergeObjects(target, source, options);
95
- type = 'Object';
97
+ mergeType = 'Object';
96
98
  }
97
99
  else if (targetType === 'Array' && sourceType === 'Array') {
98
100
  result = deepMergeArrays(target, source, options);
99
- type = 'Array';
101
+ mergeType = 'Array';
100
102
  }
101
103
  else if (targetType === 'Set' && sourceType === 'Set') {
102
104
  result = deepMergeSets(target, source, options);
103
- type = 'Set';
105
+ mergeType = 'Set';
104
106
  }
105
107
  else if (targetType === 'Map' && sourceType === 'Map') {
106
108
  result = deepMergeMaps(target, source, options);
107
- type = 'Map';
109
+ mergeType = 'Map';
108
110
  }
109
111
  else {
110
112
  flag = false;
111
113
  result = target;
112
114
  }
113
- options?.onAfterMerge?.({ result, target, source, type, parent: opts.parent });
114
- return { result, flag, type };
115
+ options?.onAfterMerge?.({ result, target, source, targetType, sourceType, mergeType, parent: opts.parent });
116
+ return { result, flag, mergeType };
115
117
  },
116
118
  // Special handling for objects with enable property
117
119
  // 对具有enable属性的对象进行特殊处理
@@ -145,37 +147,61 @@ const deepMerge = (target, source, opts = {}) => {
145
147
  // 如果是复制方法,则先复制target
146
148
  result = options.targetClone ? shallowCopy(target) : target;
147
149
  for (let k in source) {
150
+ const _result = result[k], _source = source[k];
148
151
  if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
149
- let resp = smartMerger(result[k], source[k], { ...opts, parent: result });
152
+ const resp = smartMerger(_result, _source, { ...opts, parent: result });
150
153
  //resp={result,flag,type}
151
- //flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
154
+ //flag=true表示类型一致(object/array/map/set)并完成了合并,false表示并没有合并需要直接赋值
152
155
  if (!resp.flag) {
153
156
  //类型不同则直接覆盖
154
- if (options.useEnable) {
155
- result[k] = mergeEnableObject(result[k], source[k]);
157
+ let tmp = options.useEnable ? mergeEnableObject(_result, _source) : _source;
158
+ if (_result !== tmp && tmp === null) {
159
+ if (options.nullBehavior === 'ignore') {
160
+ //不处理
161
+ }
162
+ else if (options.nullBehavior === 'delete') {
163
+ //删除属性
164
+ Reflect.deleteProperty(result, k);
165
+ }
166
+ else {
167
+ //替换
168
+ result[k] = tmp;
169
+ }
170
+ }
171
+ else if (_result !== tmp && tmp === undefined) {
172
+ if (options.undefinedBehavior === 'ignore') {
173
+ //不处理
174
+ }
175
+ else if (options.undefinedBehavior === 'delete') {
176
+ //删除属性
177
+ Reflect.deleteProperty(result, k);
178
+ }
179
+ else {
180
+ //替换
181
+ result[k] = _source;
182
+ }
156
183
  }
157
184
  else {
158
- //完全替换
159
- result[k] = source[k];
185
+ result[k] = _source;
160
186
  }
161
187
  }
162
188
  else {
163
189
  //类型相同
164
- if (resp.type) {
165
- if (resp.type === 'Object') {
190
+ if (resp.mergeType) {
191
+ if (resp.mergeType === 'Object') {
166
192
  //如果遇上对象则深度复制
167
193
  result[k] = resp.result;
168
194
  }
169
195
  }
170
196
  else {
171
197
  //其他类型则直接覆盖
172
- result[k] = source[k];
198
+ result[k] = _source;
173
199
  }
174
200
  }
175
201
  }
176
202
  else if (source.hasOwnProperty(k) && !result.hasOwnProperty(k) && options.inheritMissing) {
177
203
  //如果source有属性,result没有该属性,但是options允许追加属性则直接赋值
178
- result[k] = source[k];
204
+ result[k] = _source;
179
205
  }
180
206
  }
181
207
  //Symbol键直接追加,因为Symbol是唯一,结果同Object.assign
@@ -242,7 +268,7 @@ const deepMerge = (target, source, opts = {}) => {
242
268
  }
243
269
  else {
244
270
  // If both target and source are objects, merge them recursively
245
- resp.type === 'Object' && result.set(key, resp.result);
271
+ resp.mergeType === 'Object' && result.set(key, resp.result);
246
272
  }
247
273
  }
248
274
  else {
package/src/deepMerge.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @since Last modified: 2025/12/26 15:40:50
2
+ * @since Last modified: 2025/12/27 14:35:01
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.
@@ -42,6 +42,10 @@ export interface DeepMergeOptions {
42
42
  // Mode for handling array items
43
43
  // 处理数组成员的模式
44
44
  dataMode?: 'clear' | 'replace' | 'concat';
45
+ //如果source值=null,target如何处理,ignore表示不理会null保持target;preserve将覆盖target,delete将删除target(key)
46
+ nullBehavior?: 'ignore' | 'preserve' | 'delete';
47
+ //如果source值=undefined,target如何处理,ignore表示不理会undefined保持target;preserve将覆盖target,delete将删除target(key)
48
+ undefinedBehavior?: 'ignore' | 'preserve' | 'delete';
45
49
  // Whether to append properties (for objects)
46
50
  // 是否追加属性(针对对象)
47
51
  inheritMissing?: boolean;
@@ -67,6 +71,8 @@ export interface DeepMergeOptions {
67
71
  target: any;
68
72
  source: any;
69
73
  parent?: any;
74
+ targetType: string;
75
+ sourceType: string;
70
76
  }) => any;
71
77
 
72
78
  /**
@@ -77,6 +83,8 @@ export interface DeepMergeOptions {
77
83
  target: any;
78
84
  source: any;
79
85
  parent?: any;
86
+ targetType: string;
87
+ sourceType: string;
80
88
  }) => void;
81
89
 
82
90
  /**
@@ -88,7 +96,9 @@ export interface DeepMergeOptions {
88
96
  target: any;
89
97
  source: any;
90
98
  parent?: any;
91
- type?: '' | 'Object' | 'Array' | 'Map' | 'Set';
99
+ targetType: string;
100
+ sourceType: string;
101
+ mergeType?: '' | 'Object' | 'Array' | 'Map' | 'Set';
92
102
  }) => void;
93
103
 
94
104
  /**
@@ -130,6 +140,10 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
130
140
  // target是{}类型时,是否允许合并key=symbol的键值对。
131
141
  useSymbol: true,
132
142
 
143
+ nullBehavior:'preserve',
144
+
145
+ undefinedBehavior:'preserve',
146
+
133
147
  // Options passed to the deepClone function when targetClone is true
134
148
  // 当targetClone为true时传递给deepClone函数的选项
135
149
  deepClone: {},
@@ -148,12 +162,12 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
148
162
  let targetType = getDataType(target),
149
163
  sourceType = getDataType(source),
150
164
  flag = true,
151
- type: any,
165
+ mergeType: any,
152
166
  result;
153
167
 
154
168
  // Check interceptor - if it returns a value (not null/undefined), use it directly
155
169
  if (options.interceptor && typeof options.interceptor === 'function') {
156
- let interceptorResult = options.interceptor({ target, source, parent: options.parent });
170
+ let interceptorResult = options.interceptor({ target, source, targetType, sourceType, parent: options.parent });
157
171
  if ((interceptorResult ?? false)) {
158
172
  //如果不是返回{target,source},那么直接返回interceptorResult
159
173
  if (interceptorResult?.target === null || interceptorResult?.source === null) {
@@ -167,28 +181,28 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
167
181
  // If interceptor returns null/undefined, continue with normal cloning process
168
182
  }
169
183
 
170
- options?.onBeforeMerge?.({ target, source, parent: options.parent });
184
+ options?.onBeforeMerge?.({ target, source, targetType, sourceType, parent: options.parent });
171
185
 
172
186
  // Determine the type and perform appropriate merging
173
187
  // 确定类型并执行相应的合并
174
188
  if (targetType === 'Object' && sourceType === 'Object') {
175
189
  result = deepMergeObjects(target, source, options);
176
- type = 'Object';
190
+ mergeType = 'Object';
177
191
  } else if (targetType === 'Array' && sourceType === 'Array') {
178
192
  result = deepMergeArrays(target, source, options);
179
- type = 'Array';
193
+ mergeType = 'Array';
180
194
  } else if (targetType === 'Set' && sourceType === 'Set') {
181
195
  result = deepMergeSets(target, source, options);
182
- type = 'Set';
196
+ mergeType = 'Set';
183
197
  } else if (targetType === 'Map' && sourceType === 'Map') {
184
198
  result = deepMergeMaps(target, source, options);
185
- type = 'Map';
199
+ mergeType = 'Map';
186
200
  } else {
187
201
  flag = false;
188
202
  result = target;
189
203
  }
190
- options?.onAfterMerge?.({ result, target, source, type, parent: opts.parent });
191
- return { result, flag, type };
204
+ options?.onAfterMerge?.({ result, target, source, targetType, sourceType, mergeType, parent: opts.parent });
205
+ return { result, flag, mergeType };
192
206
  },
193
207
  // Special handling for objects with enable property
194
208
  // 对具有enable属性的对象进行特殊处理
@@ -224,35 +238,55 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
224
238
  result = options.targetClone ? shallowCopy(target) : target;
225
239
 
226
240
  for (let k in source) {
241
+ const _result = (result as any)[k],
242
+ _source = source[k];
227
243
  if (source.hasOwnProperty(k) && result.hasOwnProperty(k)) {
228
244
 
229
- let resp = smartMerger((result as any)[k], source[k], { ...opts, parent: result });
245
+ const resp = smartMerger(_result, _source, { ...opts, parent: result });
230
246
  //resp={result,flag,type}
231
- //flag=true表示类型一致并完成了合并,false表示并没有合并需要直接赋值
247
+ //flag=true表示类型一致(object/array/map/set)并完成了合并,false表示并没有合并需要直接赋值
232
248
  if (!resp.flag) {
233
249
  //类型不同则直接覆盖
234
- if (options.useEnable) {
235
- (result as any)[k] = mergeEnableObject((result as any)[k], source[k]);
250
+ let tmp = options.useEnable ? mergeEnableObject(_result, _source) : _source;
251
+ if (_result !== tmp && tmp === null) {
252
+ if (options.nullBehavior === 'ignore') {
253
+ //不处理
254
+ } else if (options.nullBehavior === 'delete') {
255
+ //删除属性
256
+ Reflect.deleteProperty(result, k);
257
+ } else {
258
+ //替换
259
+ (result as any)[k] = tmp;
260
+ }
261
+ } else if (_result !== tmp && tmp === undefined) {
262
+ if (options.undefinedBehavior === 'ignore') {
263
+ //不处理
264
+ } else if (options.undefinedBehavior === 'delete') {
265
+ //删除属性
266
+ Reflect.deleteProperty(result, k);
267
+ } else {
268
+ //替换
269
+ (result as any)[k] = _source;
270
+ }
236
271
  } else {
237
- //完全替换
238
- (result as any)[k] = source[k];
272
+ (result as any)[k] = _source;
239
273
  }
240
274
  } else {
241
275
  //类型相同
242
- if (resp.type) {
243
- if (resp.type === 'Object') {
276
+ if (resp.mergeType) {
277
+ if (resp.mergeType === 'Object') {
244
278
  //如果遇上对象则深度复制
245
279
  (result as any)[k] = resp.result;
246
280
  }
247
281
  } else {
248
282
  //其他类型则直接覆盖
249
- (result as any)[k] = source[k];
283
+ (result as any)[k] = _source;
250
284
  }
251
285
  }
252
286
 
253
287
  } else if (source.hasOwnProperty(k) && !result.hasOwnProperty(k) && options.inheritMissing) {
254
288
  //如果source有属性,result没有该属性,但是options允许追加属性则直接赋值
255
- (result as any)[k] = source[k];
289
+ (result as any)[k] = _source;
256
290
  }
257
291
  }
258
292
  //Symbol键直接追加,因为Symbol是唯一,结果同Object.assign
@@ -322,7 +356,7 @@ const deepMerge = (target: Object | Array<any> | Map<any, any> | Set<any>, sourc
322
356
  result.set(key, _source);
323
357
  } else {
324
358
  // If both target and source are objects, merge them recursively
325
- resp.type === 'Object' && result.set(key, resp.result);
359
+ resp.mergeType === 'Object' && result.set(key, resp.result);
326
360
  }
327
361
  } else {
328
362
  // If the key doesn't exist in the target, add the entry from the source Map