@tsed/core 8.44.5 → 8.44.7
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/lib/esm/utils/deepClone.js +22 -17
- package/lib/esm/utils/deepMerge.js +61 -22
- package/package.json +4 -4
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { classOf } from "./classOf.js";
|
|
2
2
|
import { isDate } from "./isDate.js";
|
|
3
|
-
import { isFunction } from "./isFunction.js";
|
|
4
|
-
import { isNil } from "./isNil.js";
|
|
5
|
-
import { isPrimitive } from "./isPrimitive.js";
|
|
6
3
|
import { isRegExp } from "./isRegExp.js";
|
|
7
|
-
import { isSymbol } from "./isSymbol.js";
|
|
8
|
-
const isBasicType = (source) => isNil(source) || isPrimitive(source) || isSymbol(source) || isFunction(source);
|
|
9
4
|
/**
|
|
10
5
|
* Creates a deep clone of the provided value, preserving prototype chains and handling circular references.
|
|
11
6
|
*
|
|
@@ -16,8 +11,8 @@ const isBasicType = (source) => isNil(source) || isPrimitive(source) || isSymbol
|
|
|
16
11
|
* @since v7.0.0
|
|
17
12
|
*/
|
|
18
13
|
export const deepClone = (source, stack = new WeakMap()) => {
|
|
19
|
-
// provides an early exit for simple cases
|
|
20
|
-
if (
|
|
14
|
+
// provides an early exit for simple cases. Inline typeof checks: this runs for every cloned value
|
|
15
|
+
if (source === null || typeof source !== "object" || source instanceof Boolean) {
|
|
21
16
|
return source;
|
|
22
17
|
}
|
|
23
18
|
const stacked = stack.get(source);
|
|
@@ -40,7 +35,12 @@ export const deepClone = (source, stack = new WeakMap()) => {
|
|
|
40
35
|
if (Array.isArray(source)) {
|
|
41
36
|
const clone = [];
|
|
42
37
|
stack.set(source, clone);
|
|
43
|
-
|
|
38
|
+
// the length is read once and holes are skipped, as forEach does
|
|
39
|
+
for (let i = 0, length = source.length; i < length; i++) {
|
|
40
|
+
if (i in source) {
|
|
41
|
+
clone[i] = deepClone(source[i], stack);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
44
|
return clone;
|
|
45
45
|
}
|
|
46
46
|
if (source instanceof Map) {
|
|
@@ -57,20 +57,25 @@ export const deepClone = (source, stack = new WeakMap()) => {
|
|
|
57
57
|
}
|
|
58
58
|
const clone = Object.create(Reflect.getPrototypeOf(source));
|
|
59
59
|
stack.set(source, clone);
|
|
60
|
-
Reflect.ownKeys(source)
|
|
60
|
+
const keys = Reflect.ownKeys(source);
|
|
61
|
+
for (let i = 0; i < keys.length; i++) {
|
|
62
|
+
const key = keys[i];
|
|
61
63
|
// respects property descriptors and the prototype chain more explicitly, which is important for objects with getter/setter.
|
|
62
|
-
const descriptor =
|
|
64
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(source, key);
|
|
63
65
|
if (descriptor) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
// the descriptor is a new object: the cloned value can be written into it. Accessors are kept as is
|
|
67
|
+
if ("value" in descriptor && typeof descriptor.value !== "function") {
|
|
68
|
+
descriptor.value = deepClone(descriptor.value, stack);
|
|
69
|
+
}
|
|
70
|
+
// a plain data property that nothing in the prototype chain intercepts (setter, read-only, __proto__):
|
|
71
|
+
// an assignment defines the same property, faster than defineProperty
|
|
72
|
+
if (descriptor.writable && descriptor.enumerable && descriptor.configurable && !(key in clone)) {
|
|
73
|
+
clone[key] = descriptor.value;
|
|
69
74
|
}
|
|
70
75
|
else {
|
|
71
|
-
|
|
76
|
+
Reflect.defineProperty(clone, key, descriptor);
|
|
72
77
|
}
|
|
73
78
|
}
|
|
74
|
-
}
|
|
79
|
+
}
|
|
75
80
|
return clone;
|
|
76
81
|
};
|
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { createInstance } from "./createInstance.js";
|
|
2
|
-
import { isFunction } from "./isFunction.js";
|
|
3
|
-
import { isPrimitive } from "./isPrimitive.js";
|
|
4
|
-
import { isSymbol } from "./isSymbol.js";
|
|
5
2
|
import { objectKeys } from "./objectKeys.js";
|
|
6
3
|
/**
|
|
7
4
|
* Creates a merge reducer using a custom comparator function.
|
|
@@ -19,18 +16,40 @@ export function mergeReducerBuilder(cb) {
|
|
|
19
16
|
return collection;
|
|
20
17
|
};
|
|
21
18
|
}
|
|
22
|
-
const
|
|
23
|
-
|
|
19
|
+
const hasEnumerableOwn = Object.prototype.propertyIsEnumerable;
|
|
20
|
+
/**
|
|
21
|
+
* Same result as `mergeReducerBuilder((a, b) => a === b)`, but appends in place:
|
|
22
|
+
* `merge()` gives it its own copy of the source array.
|
|
23
|
+
*/
|
|
24
|
+
function defaultInPlaceReducer(collection, value, options) {
|
|
25
|
+
const index = collection.indexOf(value);
|
|
26
|
+
if (index === -1) {
|
|
27
|
+
collection.push(value);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
collection[index] = deepMerge(collection[index], value, options);
|
|
31
|
+
}
|
|
32
|
+
return collection;
|
|
33
|
+
}
|
|
34
|
+
function getReducer(reducers, parentKey) {
|
|
24
35
|
if (!reducers) {
|
|
25
|
-
return
|
|
36
|
+
return defaultInPlaceReducer;
|
|
26
37
|
}
|
|
27
38
|
if (parentKey && reducers[parentKey]) {
|
|
28
39
|
return reducers[parentKey];
|
|
29
40
|
}
|
|
30
|
-
return reducers["default"] ||
|
|
41
|
+
return reducers["default"] || defaultInPlaceReducer;
|
|
31
42
|
}
|
|
32
43
|
function shouldReturnObj(obj, source) {
|
|
33
|
-
|
|
44
|
+
// inline typeof checks: this runs for every merged value
|
|
45
|
+
const type = typeof obj;
|
|
46
|
+
return (type === "string" ||
|
|
47
|
+
type === "number" ||
|
|
48
|
+
type === "boolean" ||
|
|
49
|
+
type === "symbol" ||
|
|
50
|
+
type === "function" ||
|
|
51
|
+
obj instanceof Boolean ||
|
|
52
|
+
source === undefined);
|
|
34
53
|
}
|
|
35
54
|
function shouldReturnSource(obj, source) {
|
|
36
55
|
return obj === undefined || obj === null || (obj === "" && source !== "");
|
|
@@ -45,6 +64,13 @@ function shouldReturnSource(obj, source) {
|
|
|
45
64
|
* @see DeepMergeOptions
|
|
46
65
|
*/
|
|
47
66
|
export function deepMerge(source, obj, options = {}) {
|
|
67
|
+
return merge(source, obj, options, options.parentKey, true);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The root call works with the caller's options. The nested merges share one copy of its own properties,
|
|
71
|
+
* the nested key travels as `parentKey`, and each nested reducer receives its own `{...options, parentKey}` copy.
|
|
72
|
+
*/
|
|
73
|
+
function merge(source, obj, options, parentKey, isRoot) {
|
|
48
74
|
if (shouldReturnSource(obj, source)) {
|
|
49
75
|
return source;
|
|
50
76
|
}
|
|
@@ -52,20 +78,33 @@ export function deepMerge(source, obj, options = {}) {
|
|
|
52
78
|
return obj;
|
|
53
79
|
}
|
|
54
80
|
if (Array.isArray(source)) {
|
|
55
|
-
const
|
|
56
|
-
|
|
81
|
+
const reducerOptions = isRoot ? options : { ...options, parentKey };
|
|
82
|
+
const reducer = getReducer(reducerOptions.reducers, parentKey);
|
|
83
|
+
return [].concat(obj).reduce((out, value) => reducer(out, value, reducerOptions), [...source]);
|
|
57
84
|
}
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (
|
|
66
|
-
|
|
85
|
+
const out = createInstance(source);
|
|
86
|
+
const sourceKeys = objectKeys(source);
|
|
87
|
+
const objKeys = objectKeys(obj);
|
|
88
|
+
const nestedOptions = isRoot ? { ...options } : options;
|
|
89
|
+
// the keys of obj missing from source, listed before any merge: alter() can add properties to source
|
|
90
|
+
const newKeys = [];
|
|
91
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
92
|
+
if (!hasEnumerableOwn.call(source, objKeys[i])) {
|
|
93
|
+
newKeys.push(objKeys[i]);
|
|
67
94
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
95
|
+
}
|
|
96
|
+
for (let i = 0; i < sourceKeys.length; i++) {
|
|
97
|
+
mergeKey(out, source, obj, sourceKeys[i], options, nestedOptions);
|
|
98
|
+
}
|
|
99
|
+
for (let i = 0; i < newKeys.length; i++) {
|
|
100
|
+
mergeKey(out, source, obj, newKeys[i], options, nestedOptions);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
function mergeKey(out, source, obj, key, options, nestedOptions) {
|
|
105
|
+
const value = merge(source && source[key], obj && obj[key], nestedOptions, key, false);
|
|
106
|
+
if (options.cleanUndefinedProps && value === undefined) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
out[key] = options.alter?.(key, value) ?? value;
|
|
71
110
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsed/core",
|
|
3
|
-
"version": "8.44.
|
|
3
|
+
"version": "8.44.7",
|
|
4
4
|
"description": "Core module for Ts.ED Framework",
|
|
5
5
|
"source": "./src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"tslib": ">=2.7.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@tsed/barrels": "8.44.
|
|
42
|
+
"@tsed/barrels": "8.44.7",
|
|
43
43
|
"@tsed/monorepo-utils": "3.3.3",
|
|
44
|
-
"@tsed/typescript": "8.44.
|
|
45
|
-
"@tsed/vitest": "8.44.
|
|
44
|
+
"@tsed/typescript": "8.44.7",
|
|
45
|
+
"@tsed/vitest": "8.44.7",
|
|
46
46
|
"temporal-polyfill": "1.0.1",
|
|
47
47
|
"typescript": "6.0.2",
|
|
48
48
|
"vitest": "4.1.4"
|