@jsopen/objects 2.1.1 → 2.2.1

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/README.md CHANGED
@@ -7,6 +7,24 @@
7
7
 
8
8
  A 'swiss army knife' solution for working with javascript objects.
9
9
 
10
+ ## Functions
11
+
12
+ ### [merge](docs/merge.md)
13
+ Is a powerful, flexible tool for merging objects, arrays, and their nested properties.
14
+
15
+ ### [clone / deepClone](docs/clone.md)
16
+ Easy ways to create shallow or deep copies of objects and arrays.
17
+
18
+ ### [omit / omitUndefined / omitNull / omitNullish](docs/omit.md)
19
+ Easily exclude specific keys or nullish values from objects.
20
+
21
+ ### [updateErrorMessage](docs/update-error-message.md)
22
+ Update an Error object's message while correctly refreshing the stack trace.
23
+
24
+ ### [Utilities](docs/utils.md)
25
+ Various utility functions for object and type checking.
26
+
27
+
10
28
  ## Installation
11
29
 
12
30
  `$ npm install @jsopen/objects`
package/merge.d.ts CHANGED
@@ -18,8 +18,8 @@ export declare namespace merge {
18
18
  /**
19
19
  * Optional variable that determines the depth of an operation or inclusion behavior.
20
20
  *
21
- * - If set to `true`, it enables a deep operation for only native js objects, excluding classes.
22
- * - If set to `'full'`, it enables a deep operation for all objects, including classes, excluding built-in objects
21
+ * - If set to `true`, it enables a deep operation for only plain objects and arrays. Non-plain objects (class instances) are assigned by reference.
22
+ * - If set to `'full'`, it enables a deep operation for all objects, including classes, excluding built-in objects.
23
23
  * - If assigned a `NodeCallback` function, it provides a custom callback mechanism for handling the operation.
24
24
  *
25
25
  * This variable can be used to define the level of depth or customization for a given process.
package/merge.js CHANGED
@@ -12,20 +12,20 @@ export function merge(targetObject, sourceObject, options) {
12
12
  Array.isArray(sourceObject))) {
13
13
  throw new TypeError('"target" argument must be an object or array of objects');
14
14
  }
15
- const keepExisting = options?.keepExisting;
16
- const keepExistingFn = typeof options?.keepExisting === 'function'
15
+ const optsKeepExisting = !!options?.keepExisting;
16
+ const optsKeepExistingFn = typeof options?.keepExisting === 'function'
17
17
  ? options?.keepExisting
18
18
  : undefined;
19
- const filterFn = options?.filter;
20
- const ignoreUndefined = options?.ignoreUndefined ?? true;
21
- const ignoreNulls = options?.ignoreNulls;
22
- const deep = options?.deep;
23
- const deepFull = deep === 'full';
24
- const deepFn = typeof options?.deep === 'function' ? options?.deep : undefined;
25
- const copyDescriptors = options?.copyDescriptors;
26
- const mergeArrays = options?.mergeArrays;
27
- const mergeArraysUnique = options?.mergeArrays === 'unique';
28
- const mergeArraysFn = typeof options?.mergeArrays === 'function'
19
+ const optsFilterFn = options?.filter;
20
+ const optsIgnoreUndefined = options?.ignoreUndefined ?? true;
21
+ const optsIgnoreNulls = options?.ignoreNulls;
22
+ const optsDeep = options?.deep;
23
+ const optsDeepFull = optsDeep === 'full';
24
+ const optsDeepFn = typeof options?.deep === 'function' ? options?.deep : undefined;
25
+ const optsCopyDescriptors = options?.copyDescriptors;
26
+ const optsMergeArrays = !!options?.mergeArrays;
27
+ const optsMergeArraysUnique = options?.mergeArrays === 'unique';
28
+ const optsMergeArraysFn = typeof options?.mergeArrays === 'function'
29
29
  ? options?.mergeArrays
30
30
  : undefined;
31
31
  const _merge = (target, source, parentPath = '') => {
@@ -37,18 +37,25 @@ export function merge(targetObject, sourceObject, options) {
37
37
  let key;
38
38
  let descriptor;
39
39
  let srcVal;
40
- let _goDeep = false;
40
+ let trgVal;
41
+ let goDeep;
42
+ let srcIsPlainObject;
43
+ let srcIsArray;
44
+ let srcIsBuiltIn;
45
+ let trgIsArray;
46
+ let curPath;
47
+ let keepExisting;
41
48
  if (isPlainObject(target))
42
49
  Object.setPrototypeOf(target, Object.getPrototypeOf(source));
43
50
  const ignoreFn = options?.ignoreSource;
44
- let i = 0;
51
+ let i;
45
52
  const len = keys.length;
46
53
  for (i = 0; i < len; i++) {
47
54
  key = keys[i];
48
55
  /** Should not overwrite __proto__ and constructor properties */
49
56
  if (key === '__proto__' || key === 'constructor')
50
57
  continue;
51
- if (copyDescriptors) {
58
+ if (optsCopyDescriptors) {
52
59
  descriptor = Object.getOwnPropertyDescriptor(source, key);
53
60
  if (descriptor?.get || descriptor?.set) {
54
61
  Object.defineProperty(target, key, descriptor);
@@ -56,6 +63,7 @@ export function merge(targetObject, sourceObject, options) {
56
63
  }
57
64
  }
58
65
  srcVal = source[key];
66
+ /** Check if the property should be ignored */
59
67
  if (ignoreFn?.(srcVal, {
60
68
  key,
61
69
  source,
@@ -64,76 +72,99 @@ export function merge(targetObject, sourceObject, options) {
64
72
  })) {
65
73
  continue;
66
74
  }
67
- _goDeep = !!(deep &&
68
- typeof srcVal === 'object' &&
69
- (!isBuiltIn(srcVal) || Array.isArray(srcVal)));
70
- if (_goDeep) {
71
- if (deepFn)
72
- _goDeep = deepFn(srcVal, {
73
- key,
74
- source,
75
- target,
76
- path: parentPath + (parentPath ? '.' : '') + String(key),
77
- });
78
- else
79
- _goDeep = deepFull || isPlainObject(srcVal) || Array.isArray(srcVal);
80
- }
81
- if (!_goDeep && keepExisting && hasOwnProperty.call(target, key)) {
82
- if (!keepExistingFn)
83
- continue;
84
- if (keepExistingFn(srcVal, {
75
+ srcIsPlainObject = isPlainObject(srcVal);
76
+ srcIsArray = Array.isArray(srcVal);
77
+ srcIsBuiltIn = isBuiltIn(srcVal) && !srcIsArray;
78
+ trgVal = target[key];
79
+ trgIsArray = Array.isArray(trgVal);
80
+ curPath = parentPath + (parentPath ? '.' : '') + String(key);
81
+ if (optsFilterFn &&
82
+ !optsFilterFn(srcVal, {
85
83
  key,
86
84
  source,
87
85
  target,
88
- path: parentPath + (parentPath ? '.' : '') + String(key),
86
+ path: curPath,
89
87
  })) {
90
- continue;
91
- }
88
+ continue;
92
89
  }
93
- if (filterFn &&
94
- !filterFn(srcVal, {
90
+ /** Determine if we should go deeper into the object */
91
+ goDeep = !!(optsDeep &&
92
+ !srcIsBuiltIn &&
93
+ /** Source value should be an object */
94
+ typeof srcVal === 'object' &&
95
+ /** deep full or plain object */
96
+ (optsDeepFull || srcIsPlainObject || srcIsArray));
97
+ keepExisting =
98
+ optsKeepExisting &&
99
+ hasOwnProperty.call(target, key) &&
100
+ (!optsKeepExistingFn ||
101
+ optsKeepExistingFn(srcVal, {
102
+ key,
103
+ source,
104
+ target,
105
+ path: curPath,
106
+ }));
107
+ if (goDeep && optsDeepFn) {
108
+ goDeep = optsDeepFn(srcVal, {
95
109
  key,
96
110
  source,
97
111
  target,
98
- path: parentPath + (parentPath ? '.' : '') + String(key),
99
- })) {
100
- continue;
112
+ path: curPath,
113
+ });
101
114
  }
102
- if (ignoreUndefined && srcVal === undefined) {
115
+ if (optsIgnoreUndefined && srcVal === undefined) {
103
116
  continue;
104
117
  }
105
- if (ignoreNulls && srcVal === null) {
118
+ if (optsIgnoreNulls && srcVal === null) {
106
119
  continue;
107
120
  }
108
- if (_goDeep) {
121
+ if (goDeep) {
122
+ // if (keepExisting) &&
109
123
  /** Array */
110
- if (Array.isArray(srcVal)) {
111
- if (Array.isArray(target[key]) &&
112
- (mergeArrays ||
113
- mergeArraysFn?.(srcVal, {
114
- key,
115
- source,
116
- target,
117
- path: parentPath + (parentPath ? '.' : '') + String(key),
118
- }))) {
119
- target[key] = _arrayClone(target[key], parentPath + (parentPath ? '.' : '') + String(key));
124
+ if (srcIsArray) {
125
+ /** If the target value is not an array, we do not need a deep merge operation */
126
+ if (!trgIsArray) {
127
+ if (keepExisting)
128
+ continue;
129
+ srcVal = _arrayClone(srcVal, curPath);
130
+ }
131
+ else {
132
+ srcVal = _arrayClone(srcVal, curPath);
133
+ if (optsMergeArrays &&
134
+ (!optsMergeArraysFn ||
135
+ optsMergeArraysFn?.(srcVal, {
136
+ key,
137
+ source,
138
+ target,
139
+ path: curPath,
140
+ }))) {
141
+ srcVal = [...trgVal, ...srcVal];
142
+ if (optsMergeArraysUnique)
143
+ target[key] = Array.from(new Set(srcVal));
144
+ else
145
+ target[key] = srcVal;
146
+ continue;
147
+ }
148
+ else {
149
+ if (optsMergeArraysUnique)
150
+ srcVal = Array.from(new Set(srcVal));
151
+ }
120
152
  }
121
- else
122
- target[key] = [];
123
- target[key].push(..._arrayClone(srcVal, parentPath + (parentPath ? '.' : '') + String(key)));
124
- if (mergeArraysUnique)
125
- target[key] = Array.from(new Set(target[key]));
126
- continue;
127
153
  }
128
154
  else {
129
155
  /** Object */
130
- if (!isObject(target[key]))
156
+ if (!isObject(target[key])) {
157
+ if (keepExisting)
158
+ continue;
131
159
  target[key] = {};
132
- _merge(target[key], srcVal, parentPath + (parentPath ? '.' : '') + String(key));
160
+ }
161
+ _merge(target[key], srcVal, curPath);
133
162
  continue;
134
163
  }
135
164
  }
136
- if (copyDescriptors) {
165
+ if (keepExisting)
166
+ continue;
167
+ if (optsCopyDescriptors) {
137
168
  descriptor = { ...Object.getOwnPropertyDescriptor(source, key) };
138
169
  descriptor.value = srcVal;
139
170
  Object.defineProperty(target, key, descriptor);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jsopen/objects",
3
3
  "description": "Helper utilities for working with JavaScript objects and arrays",
4
- "version": "2.1.1",
4
+ "version": "2.2.1",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
7
7
  "dependencies": {
@@ -3,10 +3,4 @@
3
3
  * @param err
4
4
  * @param newMessage
5
5
  */
6
- export declare function updateErrorMessage(err: Error, newMessage: string): Error | undefined;
7
- /**
8
- * Updates the error message and stack trace at sametime.
9
- * @param err
10
- * @param newMessage
11
- */
12
- export declare function updateErrorMessageFallback(err: Error, newMessage: string): Error;
6
+ export declare function updateErrorMessage(err: Error, newMessage: string): Error;
@@ -5,22 +5,6 @@
5
5
  */
6
6
  export function updateErrorMessage(err, newMessage) {
7
7
  err.message = String(newMessage);
8
- /** V8 */
9
- if (typeof Error.captureStackTrace === 'function') {
10
- Error.captureStackTrace(err);
11
- return;
12
- }
13
- /** Other engines */
14
- return updateErrorMessageFallback(err, newMessage);
15
- }
16
- /**
17
- * Updates the error message and stack trace at sametime.
18
- * @param err
19
- * @param newMessage
20
- */
21
- export function updateErrorMessageFallback(err, newMessage) {
22
- err.message = String(newMessage);
23
- /** Other engines */
24
8
  const stack = typeof err.stack === 'string' ? err.stack : null;
25
9
  if (!stack)
26
10
  return err;