@jsopen/objects 1.6.2 → 2.0.0

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
@@ -1,7 +1,11 @@
1
1
  ## Changelog
2
2
 
3
- ### [v1.6.2](https://github.com/panates/jsopen-objects/compare/v1.6.1...v1.6.2) -
3
+ ### [v2.0.0](https://github.com/panates/jsopen-objects/compare/v1.6.3...v2.0.0) -
4
4
 
5
5
  #### 🚀 New Features
6
6
 
7
- - feat: Added options to deepClone @Eray Hanoğlu
7
+ - refactor: Refactored merge method. Stopping using function compilation. @Eray Hanoğlu
8
+
9
+ #### 🛠 Refactoring and Updates
10
+
11
+ - refactor: Refactored merge method. Stopping using function compilation. @Eray Hanoğlu
package/cjs/is-object.js CHANGED
@@ -7,7 +7,8 @@ function isObject(v) {
7
7
  return v && typeof v === 'object' && !Array.isArray(v);
8
8
  }
9
9
  function isPlainObject(obj) {
10
- if (typeof obj === 'object' &&
10
+ if (obj &&
11
+ typeof obj === 'object' &&
11
12
  Object.prototype.toString.call(obj) === '[object Object]') {
12
13
  const proto = Object.getPrototypeOf(obj);
13
14
  /* istanbul ignore next */
package/cjs/merge.js CHANGED
@@ -1,241 +1,163 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.merge = merge;
4
- exports.getMergeFunction = getMergeFunction;
5
4
  const is_object_js_1 = require("./is-object.js");
6
5
  const type_guards_js_1 = require("./type-guards.js");
7
- function merge(target, source, options) {
8
- if (!((0, is_object_js_1.isObject)(target) || typeof target === 'function')) {
9
- throw new TypeError('"target" argument must be an object');
10
- }
11
- source = source || {};
12
- if (!((0, is_object_js_1.isObject)(source) || typeof target === 'function')) {
13
- throw new TypeError('"target" argument must be an object');
14
- }
15
- const fn = getMergeFunction(options);
16
- return fn(target, source, options, '');
17
- }
18
- const functionCache = new Map();
19
- function getMergeFunction(options) {
20
- const cacheKey = [
21
- options?.deep,
22
- options?.moveArrays,
23
- options?.keepExisting,
24
- options?.copyDescriptors,
25
- options?.ignore,
26
- options?.ignoreUndefined,
27
- options?.ignoreNulls,
28
- options?.filter,
29
- ]
30
- .map(option => option == null
31
- ? 'n'
32
- : typeof option === 'function'
33
- ? 'f'
34
- : typeof option === 'string'
35
- ? option
36
- : option
37
- ? '1'
38
- : '0')
39
- .join();
40
- let fn = functionCache.get(cacheKey);
41
- if (!fn) {
42
- fn = buildMerge(options);
43
- functionCache.set(cacheKey, fn);
44
- }
45
- return fn;
46
- }
47
- function buildMerge(options) {
48
- const scriptL0 = [
49
- `
50
- const { merge, isObject, isPlainObject, deepTest, arrayClone } = context;
51
6
  const hasOwnProperty = Object.prototype.hasOwnProperty;
52
- const keys = Object.getOwnPropertyNames(source);
53
- keys.push(...Object.getOwnPropertySymbols(source));
54
- let key;
55
- let descriptor;
56
- let srcVal;
57
- let trgVal;
58
- `,
59
- ];
60
- // noinspection JSUnusedGlobalSymbols
61
- const context = {
62
- deepTest: is_object_js_1.isPlainObject,
63
- isPlainObject: is_object_js_1.isPlainObject,
64
- isObject: is_object_js_1.isObject,
65
- arrayClone,
66
- merge: null,
67
- };
68
- if (options?.deep) {
69
- if (options.deep === 'full') {
70
- context.deepTest = v => typeof v === 'object' && !(0, type_guards_js_1.isBuiltIn)(v);
71
- }
72
- scriptL0.push(`let subPath;`, `let _isArray;`);
73
- if (typeof options?.deep === 'function') {
74
- scriptL0.push(`const deepCallback = options.deep;`);
75
- }
76
- }
77
- if (typeof options?.ignore === 'function') {
78
- scriptL0.push('const ignoreCallback = options.ignore;');
79
- }
80
- if (typeof options?.filter === 'function') {
81
- scriptL0.push('const filterCallback = options.filter;');
82
- }
83
- if (typeof options?.copyDescriptors === 'function') {
84
- scriptL0.push(`const copyDescriptorsCallback = options.copyDescriptors;`);
85
- }
86
- if (typeof options?.moveArrays === 'function') {
87
- scriptL0.push(`const moveArraysCallback = options.moveArrays;`);
88
- }
89
- scriptL0.push(`
90
- if (isPlainObject(target)) Object.setPrototypeOf(target, Object.getPrototypeOf(source));
91
- let i = 0;
92
- const len = keys.length;
93
- for (i = 0; i < len; i++) {
94
- key = keys[i];
95
- /** Should not overwrite __proto__ and constructor properties */
96
- if (key === '__proto__' || key === 'constructor') continue;
97
- `);
98
- const scriptL1For = [];
99
- scriptL0.push(scriptL1For);
100
- scriptL0.push('}');
101
- /** ************* filter *****************/
102
- if (options?.filter) {
103
- scriptL1For.push(`
104
- if (!filterCallback(key, source, target, curPath)) {
105
- delete target[key];
106
- continue;
107
- }`);
108
- }
109
- /** ************* ignore *****************/
110
- if (typeof options?.ignore === 'function') {
111
- scriptL1For.push(`
112
- if (
113
- hasOwnProperty.call(target, key) &&
114
- ignoreCallback(key, source, target, curPath)
115
- ) continue;
116
- `);
117
- }
118
- /** ************* copyDescriptors *****************/
119
- if (options?.copyDescriptors) {
120
- let scriptL2Descriptors = scriptL1For;
121
- if (typeof options?.copyDescriptors === 'function') {
122
- scriptL1For.push('if (copyDescriptorsCallback(key, source, target, curPath)) {');
123
- scriptL2Descriptors = [];
124
- scriptL1For.push(scriptL2Descriptors);
125
- scriptL1For.push(`} else`);
126
- scriptL1For.push(` descriptor = {enumerable: true, configurable: true, writable: true}`);
127
- }
128
- scriptL2Descriptors.push(`
129
- descriptor = { ...Object.getOwnPropertyDescriptor(source, key) }
130
- if ((descriptor.get || descriptor.set)) {
131
- Object.defineProperty(target, key, descriptor);
132
- continue;
133
- }
134
- srcVal = source[key];`);
135
- }
136
- else {
137
- scriptL1For.push(`descriptor = {enumerable: true, configurable: true, writable: true}`, `srcVal = source[key];`);
138
- }
139
- /** ************* keepExisting *****************/
140
- if (options?.keepExisting) {
141
- scriptL1For.push(`if (hasOwnProperty.call(target, key)) continue;`);
142
- }
143
- /** ************* ignoreUndefined *****************/
144
- if (options?.ignoreUndefined ?? true) {
145
- scriptL1For.push(`if (srcVal === undefined) continue;`);
146
- }
147
- /** ************* ignoreNulls *****************/
148
- if (options?.ignoreNulls) {
149
- scriptL1For.push(`if (srcVal === null) continue;`);
7
+ function merge(targetObject, sourceObject, options) {
8
+ if (!((0, is_object_js_1.isObject)(targetObject) || typeof targetObject === 'function')) {
9
+ throw new TypeError('"target" argument must be an object');
150
10
  }
151
- const deepArray = !options?.moveArrays || typeof options?.moveArrays === 'function';
152
- /** ************* deep *****************/
153
- if (options?.deep) {
154
- if (deepArray) {
155
- scriptL1For.push(`
156
- _isArray = Array.isArray(srcVal);
157
- if (typeof key !== 'symbol' && (_isArray || deepTest(srcVal))) {`);
158
- }
159
- else {
160
- scriptL1For.push(`
161
- if (typeof key !== 'symbol' && deepTest(srcVal)) {
162
- subPath = curPath + (curPath ? '.' : '') + key;`);
163
- }
164
- scriptL1For.push(`subPath = curPath + (curPath ? '.' : '') + key;`);
165
- const scriptL2Deep = [];
166
- scriptL1For.push(scriptL2Deep);
167
- scriptL1For.push('}');
168
- let scriptL3Deep = scriptL2Deep;
169
- if (typeof options?.deep === 'function') {
170
- scriptL2Deep.push(`
171
- if (deepCallback(key, subPath, target, source)) {`);
172
- scriptL3Deep = [];
173
- scriptL2Deep.push(scriptL3Deep);
174
- scriptL2Deep.push('}');
175
- }
176
- /** ************* Array *****************/
177
- if (!options?.moveArrays || typeof options?.moveArrays === 'function') {
178
- scriptL3Deep.push(`if (_isArray) {`);
179
- const scriptL4IsArray = [];
180
- scriptL3Deep.push(scriptL4IsArray);
181
- scriptL3Deep.push('}');
182
- let scriptL5CloneArrays = scriptL4IsArray;
183
- if (typeof options?.moveArrays === 'function') {
184
- scriptL4IsArray.push(`
185
- if (moveArraysCallback(key, subPath, target, source)) {
186
- descriptor.value = srcVal;
187
- Object.defineProperty(target, key, descriptor);
188
- continue;
189
- } else {`);
190
- scriptL5CloneArrays = [];
191
- scriptL4IsArray.push(scriptL5CloneArrays);
192
- scriptL4IsArray.push('}');
11
+ if (sourceObject == null)
12
+ return targetObject;
13
+ if (!((0, is_object_js_1.isObject)(sourceObject) ||
14
+ typeof sourceObject === 'function' ||
15
+ Array.isArray(sourceObject))) {
16
+ throw new TypeError('"target" argument must be an object or array of objects');
17
+ }
18
+ const keepExisting = options?.keepExisting;
19
+ const keepExistingFn = typeof options?.keepExisting === 'function'
20
+ ? options?.keepExisting
21
+ : undefined;
22
+ const filterFn = options?.filter;
23
+ const ignoreUndefined = options?.ignoreUndefined ?? true;
24
+ const ignoreNulls = options?.ignoreNulls;
25
+ const deep = options?.deep;
26
+ const deepFull = deep === 'full';
27
+ const deepFn = typeof options?.deep === 'function' ? options?.deep : undefined;
28
+ const copyDescriptors = options?.copyDescriptors;
29
+ const mergeArrays = options?.mergeArrays;
30
+ const mergeArraysUnique = options?.mergeArrays === 'unique';
31
+ const mergeArraysFn = typeof options?.mergeArrays === 'function'
32
+ ? options?.mergeArrays
33
+ : undefined;
34
+ const _merge = (target, source, parentPath = '') => {
35
+ if (!(0, is_object_js_1.isObject)(source))
36
+ return;
37
+ const keys = Object.getOwnPropertyNames(source);
38
+ if (options?.symbolKeys)
39
+ keys.push(...Object.getOwnPropertySymbols(source));
40
+ let key;
41
+ let descriptor;
42
+ let srcVal;
43
+ let _goDeep = false;
44
+ if ((0, is_object_js_1.isPlainObject)(target))
45
+ Object.setPrototypeOf(target, Object.getPrototypeOf(source));
46
+ const ignoreFn = options?.ignoreSource;
47
+ let i = 0;
48
+ const len = keys.length;
49
+ for (i = 0; i < len; i++) {
50
+ key = keys[i];
51
+ /** Should not overwrite __proto__ and constructor properties */
52
+ if (key === '__proto__' || key === 'constructor')
53
+ continue;
54
+ if (copyDescriptors) {
55
+ descriptor = Object.getOwnPropertyDescriptor(source, key);
56
+ if (descriptor?.get || descriptor?.set) {
57
+ Object.defineProperty(target, key, descriptor);
58
+ continue;
59
+ }
60
+ }
61
+ srcVal = source[key];
62
+ if (ignoreFn?.(srcVal, {
63
+ key,
64
+ source,
65
+ target,
66
+ path: parentPath + (parentPath ? '.' : '') + String(key),
67
+ })) {
68
+ continue;
69
+ }
70
+ if (keepExisting && hasOwnProperty.call(target, key)) {
71
+ if (!keepExistingFn)
72
+ continue;
73
+ if (keepExistingFn(srcVal, {
74
+ key,
75
+ source,
76
+ target,
77
+ path: parentPath + (parentPath ? '.' : '') + String(key),
78
+ })) {
79
+ continue;
80
+ }
81
+ }
82
+ if (filterFn &&
83
+ !filterFn(srcVal, {
84
+ key,
85
+ source,
86
+ target,
87
+ path: parentPath + (parentPath ? '.' : '') + String(key),
88
+ })) {
89
+ continue;
193
90
  }
194
- scriptL5CloneArrays.push(`
195
- descriptor.value = arrayClone(srcVal, options, merge, subPath);
196
- Object.defineProperty(target, key, descriptor);
197
- continue;
198
- `);
91
+ if (ignoreUndefined && srcVal === undefined) {
92
+ continue;
93
+ }
94
+ if (ignoreNulls && srcVal === null) {
95
+ continue;
96
+ }
97
+ if (deep &&
98
+ typeof srcVal === 'object' &&
99
+ (!(0, type_guards_js_1.isBuiltIn)(srcVal) || Array.isArray(srcVal))) {
100
+ _goDeep =
101
+ (deepFn &&
102
+ deepFn(srcVal, {
103
+ key,
104
+ source,
105
+ target,
106
+ path: parentPath + (parentPath ? '.' : '') + String(key),
107
+ })) ||
108
+ (!deepFn &&
109
+ (deepFull || (0, is_object_js_1.isPlainObject)(srcVal) || Array.isArray(srcVal)));
110
+ if (_goDeep) {
111
+ /** Array */
112
+ if (Array.isArray(srcVal)) {
113
+ if (Array.isArray(target[key]) &&
114
+ (mergeArrays ||
115
+ mergeArraysFn?.(srcVal, {
116
+ key,
117
+ source,
118
+ target,
119
+ path: parentPath + (parentPath ? '.' : '') + String(key),
120
+ }))) {
121
+ target[key] = _arrayClone(target[key], parentPath + (parentPath ? '.' : '') + String(key));
122
+ }
123
+ else
124
+ target[key] = [];
125
+ target[key].push(..._arrayClone(srcVal, parentPath + (parentPath ? '.' : '') + String(key)));
126
+ if (mergeArraysUnique)
127
+ target[key] = Array.from(new Set(target[key]));
128
+ continue;
129
+ }
130
+ else {
131
+ /** Object */
132
+ if (!(0, is_object_js_1.isObject)(target[key]))
133
+ target[key] = {};
134
+ _merge(target[key], srcVal, parentPath + (parentPath ? '.' : '') + String(key));
135
+ continue;
136
+ }
137
+ }
138
+ }
139
+ if (copyDescriptors) {
140
+ descriptor = { ...Object.getOwnPropertyDescriptor(source, key) };
141
+ descriptor.value = srcVal;
142
+ Object.defineProperty(target, key, descriptor);
143
+ continue;
144
+ }
145
+ target[key] = srcVal;
199
146
  }
200
- /** ************* object *****************/
201
- scriptL3Deep.push(`
202
- trgVal = target[key];
203
- if (!isObject(trgVal)) {
204
- descriptor.value = trgVal = {};
205
- Object.defineProperty(target, key, descriptor);
206
- }
207
- merge(trgVal, srcVal, options, subPath);
208
- continue;`);
147
+ return target;
148
+ };
149
+ const _arrayClone = (arr, curPath) => {
150
+ return arr.map((x, index) => {
151
+ if (Array.isArray(x))
152
+ return _arrayClone(x, curPath + '[' + index + ']');
153
+ if (typeof x === 'object' && !(0, type_guards_js_1.isBuiltIn)(x))
154
+ return _merge({}, x, curPath + '[' + index + ']');
155
+ return x;
156
+ });
157
+ };
158
+ const sources = Array.isArray(sourceObject) ? sourceObject : [sourceObject];
159
+ for (const src of sources) {
160
+ _merge(targetObject, src);
209
161
  }
210
- /** ************* finalize *****************/
211
- scriptL1For.push(`
212
- descriptor.value = srcVal;
213
- Object.defineProperty(target, key, descriptor);`);
214
- scriptL0.push('return target;');
215
- const script = _flattenText(scriptL0);
216
- const fn = Function('target', 'source', 'options', 'curPath', 'context', script);
217
- context.merge = (target, source, opts, curPath) => fn(target, source, opts, curPath, context);
218
- return context.merge;
219
- }
220
- function arrayClone(arr, options, _merge, curPath) {
221
- return arr.map((x) => {
222
- if (Array.isArray(x))
223
- return arrayClone(x, options, _merge, curPath);
224
- if (typeof x === 'object' && !(0, type_guards_js_1.isBuiltIn)(x))
225
- return _merge({}, x, options, curPath);
226
- return x;
227
- });
228
- }
229
- function _flattenText(arr, level = 0) {
230
- const indent = ' '.repeat(level);
231
- return arr
232
- .map(v => {
233
- if (Array.isArray(v))
234
- return _flattenText(v, level + 1);
235
- return (indent +
236
- String(v)
237
- .trim()
238
- .replace(/\n/g, '\n' + indent));
239
- })
240
- .join('\n');
162
+ return targetObject;
241
163
  }
package/cjs/omit.js CHANGED
@@ -9,7 +9,7 @@ function omit(obj, keys) {
9
9
  const keysSet = new Set(keys);
10
10
  return (0, merge_js_1.merge)({}, obj, {
11
11
  deep: false,
12
- filter(key) {
12
+ filter(_, { key }) {
13
13
  return !keysSet.has(key);
14
14
  },
15
15
  });
@@ -1 +1 @@
1
- {"root":["../../src/clone.ts","../../src/index.ts","../../src/is-object.ts","../../src/merge.ts","../../src/omit.ts","../../src/type-guards.ts"],"version":"5.8.3"}
1
+ {"root":["../../src/clone.ts","../../src/index.ts","../../src/is-object.ts","../../src/merge.ts","../../src/omit.ts","../../src/type-guards.ts"],"version":"5.9.2"}
@@ -5,7 +5,8 @@ exports.isConstructor = isConstructor;
5
5
  exports.isIterable = isIterable;
6
6
  exports.isAsyncIterable = isAsyncIterable;
7
7
  function isBuiltIn(v) {
8
- return ((typeof v === 'object' &&
8
+ return ((v &&
9
+ typeof v === 'object' &&
9
10
  (v instanceof Date ||
10
11
  v instanceof RegExp ||
11
12
  v instanceof Map ||
package/esm/is-object.js CHANGED
@@ -3,7 +3,8 @@ export function isObject(v) {
3
3
  return v && typeof v === 'object' && !Array.isArray(v);
4
4
  }
5
5
  export function isPlainObject(obj) {
6
- if (typeof obj === 'object' &&
6
+ if (obj &&
7
+ typeof obj === 'object' &&
7
8
  Object.prototype.toString.call(obj) === '[object Object]') {
8
9
  const proto = Object.getPrototypeOf(obj);
9
10
  /* istanbul ignore next */
package/esm/merge.js CHANGED
@@ -1,237 +1,160 @@
1
1
  import { isObject, isPlainObject } from './is-object.js';
2
2
  import { isBuiltIn } from './type-guards.js';
3
- export function merge(target, source, options) {
4
- if (!(isObject(target) || typeof target === 'function')) {
5
- throw new TypeError('"target" argument must be an object');
6
- }
7
- source = source || {};
8
- if (!(isObject(source) || typeof target === 'function')) {
9
- throw new TypeError('"target" argument must be an object');
10
- }
11
- const fn = getMergeFunction(options);
12
- return fn(target, source, options, '');
13
- }
14
- const functionCache = new Map();
15
- export function getMergeFunction(options) {
16
- const cacheKey = [
17
- options?.deep,
18
- options?.moveArrays,
19
- options?.keepExisting,
20
- options?.copyDescriptors,
21
- options?.ignore,
22
- options?.ignoreUndefined,
23
- options?.ignoreNulls,
24
- options?.filter,
25
- ]
26
- .map(option => option == null
27
- ? 'n'
28
- : typeof option === 'function'
29
- ? 'f'
30
- : typeof option === 'string'
31
- ? option
32
- : option
33
- ? '1'
34
- : '0')
35
- .join();
36
- let fn = functionCache.get(cacheKey);
37
- if (!fn) {
38
- fn = buildMerge(options);
39
- functionCache.set(cacheKey, fn);
40
- }
41
- return fn;
42
- }
43
- function buildMerge(options) {
44
- const scriptL0 = [
45
- `
46
- const { merge, isObject, isPlainObject, deepTest, arrayClone } = context;
47
3
  const hasOwnProperty = Object.prototype.hasOwnProperty;
48
- const keys = Object.getOwnPropertyNames(source);
49
- keys.push(...Object.getOwnPropertySymbols(source));
50
- let key;
51
- let descriptor;
52
- let srcVal;
53
- let trgVal;
54
- `,
55
- ];
56
- // noinspection JSUnusedGlobalSymbols
57
- const context = {
58
- deepTest: isPlainObject,
59
- isPlainObject,
60
- isObject,
61
- arrayClone,
62
- merge: null,
63
- };
64
- if (options?.deep) {
65
- if (options.deep === 'full') {
66
- context.deepTest = v => typeof v === 'object' && !isBuiltIn(v);
67
- }
68
- scriptL0.push(`let subPath;`, `let _isArray;`);
69
- if (typeof options?.deep === 'function') {
70
- scriptL0.push(`const deepCallback = options.deep;`);
71
- }
72
- }
73
- if (typeof options?.ignore === 'function') {
74
- scriptL0.push('const ignoreCallback = options.ignore;');
75
- }
76
- if (typeof options?.filter === 'function') {
77
- scriptL0.push('const filterCallback = options.filter;');
78
- }
79
- if (typeof options?.copyDescriptors === 'function') {
80
- scriptL0.push(`const copyDescriptorsCallback = options.copyDescriptors;`);
81
- }
82
- if (typeof options?.moveArrays === 'function') {
83
- scriptL0.push(`const moveArraysCallback = options.moveArrays;`);
84
- }
85
- scriptL0.push(`
86
- if (isPlainObject(target)) Object.setPrototypeOf(target, Object.getPrototypeOf(source));
87
- let i = 0;
88
- const len = keys.length;
89
- for (i = 0; i < len; i++) {
90
- key = keys[i];
91
- /** Should not overwrite __proto__ and constructor properties */
92
- if (key === '__proto__' || key === 'constructor') continue;
93
- `);
94
- const scriptL1For = [];
95
- scriptL0.push(scriptL1For);
96
- scriptL0.push('}');
97
- /** ************* filter *****************/
98
- if (options?.filter) {
99
- scriptL1For.push(`
100
- if (!filterCallback(key, source, target, curPath)) {
101
- delete target[key];
102
- continue;
103
- }`);
104
- }
105
- /** ************* ignore *****************/
106
- if (typeof options?.ignore === 'function') {
107
- scriptL1For.push(`
108
- if (
109
- hasOwnProperty.call(target, key) &&
110
- ignoreCallback(key, source, target, curPath)
111
- ) continue;
112
- `);
113
- }
114
- /** ************* copyDescriptors *****************/
115
- if (options?.copyDescriptors) {
116
- let scriptL2Descriptors = scriptL1For;
117
- if (typeof options?.copyDescriptors === 'function') {
118
- scriptL1For.push('if (copyDescriptorsCallback(key, source, target, curPath)) {');
119
- scriptL2Descriptors = [];
120
- scriptL1For.push(scriptL2Descriptors);
121
- scriptL1For.push(`} else`);
122
- scriptL1For.push(` descriptor = {enumerable: true, configurable: true, writable: true}`);
123
- }
124
- scriptL2Descriptors.push(`
125
- descriptor = { ...Object.getOwnPropertyDescriptor(source, key) }
126
- if ((descriptor.get || descriptor.set)) {
127
- Object.defineProperty(target, key, descriptor);
128
- continue;
129
- }
130
- srcVal = source[key];`);
131
- }
132
- else {
133
- scriptL1For.push(`descriptor = {enumerable: true, configurable: true, writable: true}`, `srcVal = source[key];`);
134
- }
135
- /** ************* keepExisting *****************/
136
- if (options?.keepExisting) {
137
- scriptL1For.push(`if (hasOwnProperty.call(target, key)) continue;`);
138
- }
139
- /** ************* ignoreUndefined *****************/
140
- if (options?.ignoreUndefined ?? true) {
141
- scriptL1For.push(`if (srcVal === undefined) continue;`);
142
- }
143
- /** ************* ignoreNulls *****************/
144
- if (options?.ignoreNulls) {
145
- scriptL1For.push(`if (srcVal === null) continue;`);
4
+ export function merge(targetObject, sourceObject, options) {
5
+ if (!(isObject(targetObject) || typeof targetObject === 'function')) {
6
+ throw new TypeError('"target" argument must be an object');
146
7
  }
147
- const deepArray = !options?.moveArrays || typeof options?.moveArrays === 'function';
148
- /** ************* deep *****************/
149
- if (options?.deep) {
150
- if (deepArray) {
151
- scriptL1For.push(`
152
- _isArray = Array.isArray(srcVal);
153
- if (typeof key !== 'symbol' && (_isArray || deepTest(srcVal))) {`);
154
- }
155
- else {
156
- scriptL1For.push(`
157
- if (typeof key !== 'symbol' && deepTest(srcVal)) {
158
- subPath = curPath + (curPath ? '.' : '') + key;`);
159
- }
160
- scriptL1For.push(`subPath = curPath + (curPath ? '.' : '') + key;`);
161
- const scriptL2Deep = [];
162
- scriptL1For.push(scriptL2Deep);
163
- scriptL1For.push('}');
164
- let scriptL3Deep = scriptL2Deep;
165
- if (typeof options?.deep === 'function') {
166
- scriptL2Deep.push(`
167
- if (deepCallback(key, subPath, target, source)) {`);
168
- scriptL3Deep = [];
169
- scriptL2Deep.push(scriptL3Deep);
170
- scriptL2Deep.push('}');
171
- }
172
- /** ************* Array *****************/
173
- if (!options?.moveArrays || typeof options?.moveArrays === 'function') {
174
- scriptL3Deep.push(`if (_isArray) {`);
175
- const scriptL4IsArray = [];
176
- scriptL3Deep.push(scriptL4IsArray);
177
- scriptL3Deep.push('}');
178
- let scriptL5CloneArrays = scriptL4IsArray;
179
- if (typeof options?.moveArrays === 'function') {
180
- scriptL4IsArray.push(`
181
- if (moveArraysCallback(key, subPath, target, source)) {
182
- descriptor.value = srcVal;
183
- Object.defineProperty(target, key, descriptor);
184
- continue;
185
- } else {`);
186
- scriptL5CloneArrays = [];
187
- scriptL4IsArray.push(scriptL5CloneArrays);
188
- scriptL4IsArray.push('}');
8
+ if (sourceObject == null)
9
+ return targetObject;
10
+ if (!(isObject(sourceObject) ||
11
+ typeof sourceObject === 'function' ||
12
+ Array.isArray(sourceObject))) {
13
+ throw new TypeError('"target" argument must be an object or array of objects');
14
+ }
15
+ const keepExisting = options?.keepExisting;
16
+ const keepExistingFn = typeof options?.keepExisting === 'function'
17
+ ? options?.keepExisting
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'
29
+ ? options?.mergeArrays
30
+ : undefined;
31
+ const _merge = (target, source, parentPath = '') => {
32
+ if (!isObject(source))
33
+ return;
34
+ const keys = Object.getOwnPropertyNames(source);
35
+ if (options?.symbolKeys)
36
+ keys.push(...Object.getOwnPropertySymbols(source));
37
+ let key;
38
+ let descriptor;
39
+ let srcVal;
40
+ let _goDeep = false;
41
+ if (isPlainObject(target))
42
+ Object.setPrototypeOf(target, Object.getPrototypeOf(source));
43
+ const ignoreFn = options?.ignoreSource;
44
+ let i = 0;
45
+ const len = keys.length;
46
+ for (i = 0; i < len; i++) {
47
+ key = keys[i];
48
+ /** Should not overwrite __proto__ and constructor properties */
49
+ if (key === '__proto__' || key === 'constructor')
50
+ continue;
51
+ if (copyDescriptors) {
52
+ descriptor = Object.getOwnPropertyDescriptor(source, key);
53
+ if (descriptor?.get || descriptor?.set) {
54
+ Object.defineProperty(target, key, descriptor);
55
+ continue;
56
+ }
57
+ }
58
+ srcVal = source[key];
59
+ if (ignoreFn?.(srcVal, {
60
+ key,
61
+ source,
62
+ target,
63
+ path: parentPath + (parentPath ? '.' : '') + String(key),
64
+ })) {
65
+ continue;
66
+ }
67
+ if (keepExisting && hasOwnProperty.call(target, key)) {
68
+ if (!keepExistingFn)
69
+ continue;
70
+ if (keepExistingFn(srcVal, {
71
+ key,
72
+ source,
73
+ target,
74
+ path: parentPath + (parentPath ? '.' : '') + String(key),
75
+ })) {
76
+ continue;
77
+ }
78
+ }
79
+ if (filterFn &&
80
+ !filterFn(srcVal, {
81
+ key,
82
+ source,
83
+ target,
84
+ path: parentPath + (parentPath ? '.' : '') + String(key),
85
+ })) {
86
+ continue;
189
87
  }
190
- scriptL5CloneArrays.push(`
191
- descriptor.value = arrayClone(srcVal, options, merge, subPath);
192
- Object.defineProperty(target, key, descriptor);
193
- continue;
194
- `);
88
+ if (ignoreUndefined && srcVal === undefined) {
89
+ continue;
90
+ }
91
+ if (ignoreNulls && srcVal === null) {
92
+ continue;
93
+ }
94
+ if (deep &&
95
+ typeof srcVal === 'object' &&
96
+ (!isBuiltIn(srcVal) || Array.isArray(srcVal))) {
97
+ _goDeep =
98
+ (deepFn &&
99
+ deepFn(srcVal, {
100
+ key,
101
+ source,
102
+ target,
103
+ path: parentPath + (parentPath ? '.' : '') + String(key),
104
+ })) ||
105
+ (!deepFn &&
106
+ (deepFull || isPlainObject(srcVal) || Array.isArray(srcVal)));
107
+ if (_goDeep) {
108
+ /** Array */
109
+ if (Array.isArray(srcVal)) {
110
+ if (Array.isArray(target[key]) &&
111
+ (mergeArrays ||
112
+ mergeArraysFn?.(srcVal, {
113
+ key,
114
+ source,
115
+ target,
116
+ path: parentPath + (parentPath ? '.' : '') + String(key),
117
+ }))) {
118
+ target[key] = _arrayClone(target[key], parentPath + (parentPath ? '.' : '') + String(key));
119
+ }
120
+ else
121
+ target[key] = [];
122
+ target[key].push(..._arrayClone(srcVal, parentPath + (parentPath ? '.' : '') + String(key)));
123
+ if (mergeArraysUnique)
124
+ target[key] = Array.from(new Set(target[key]));
125
+ continue;
126
+ }
127
+ else {
128
+ /** Object */
129
+ if (!isObject(target[key]))
130
+ target[key] = {};
131
+ _merge(target[key], srcVal, parentPath + (parentPath ? '.' : '') + String(key));
132
+ continue;
133
+ }
134
+ }
135
+ }
136
+ if (copyDescriptors) {
137
+ descriptor = { ...Object.getOwnPropertyDescriptor(source, key) };
138
+ descriptor.value = srcVal;
139
+ Object.defineProperty(target, key, descriptor);
140
+ continue;
141
+ }
142
+ target[key] = srcVal;
195
143
  }
196
- /** ************* object *****************/
197
- scriptL3Deep.push(`
198
- trgVal = target[key];
199
- if (!isObject(trgVal)) {
200
- descriptor.value = trgVal = {};
201
- Object.defineProperty(target, key, descriptor);
202
- }
203
- merge(trgVal, srcVal, options, subPath);
204
- continue;`);
144
+ return target;
145
+ };
146
+ const _arrayClone = (arr, curPath) => {
147
+ return arr.map((x, index) => {
148
+ if (Array.isArray(x))
149
+ return _arrayClone(x, curPath + '[' + index + ']');
150
+ if (typeof x === 'object' && !isBuiltIn(x))
151
+ return _merge({}, x, curPath + '[' + index + ']');
152
+ return x;
153
+ });
154
+ };
155
+ const sources = Array.isArray(sourceObject) ? sourceObject : [sourceObject];
156
+ for (const src of sources) {
157
+ _merge(targetObject, src);
205
158
  }
206
- /** ************* finalize *****************/
207
- scriptL1For.push(`
208
- descriptor.value = srcVal;
209
- Object.defineProperty(target, key, descriptor);`);
210
- scriptL0.push('return target;');
211
- const script = _flattenText(scriptL0);
212
- const fn = Function('target', 'source', 'options', 'curPath', 'context', script);
213
- context.merge = (target, source, opts, curPath) => fn(target, source, opts, curPath, context);
214
- return context.merge;
215
- }
216
- function arrayClone(arr, options, _merge, curPath) {
217
- return arr.map((x) => {
218
- if (Array.isArray(x))
219
- return arrayClone(x, options, _merge, curPath);
220
- if (typeof x === 'object' && !isBuiltIn(x))
221
- return _merge({}, x, options, curPath);
222
- return x;
223
- });
224
- }
225
- function _flattenText(arr, level = 0) {
226
- const indent = ' '.repeat(level);
227
- return arr
228
- .map(v => {
229
- if (Array.isArray(v))
230
- return _flattenText(v, level + 1);
231
- return (indent +
232
- String(v)
233
- .trim()
234
- .replace(/\n/g, '\n' + indent));
235
- })
236
- .join('\n');
159
+ return targetObject;
237
160
  }
package/esm/omit.js CHANGED
@@ -3,7 +3,7 @@ export function omit(obj, keys) {
3
3
  const keysSet = new Set(keys);
4
4
  return merge({}, obj, {
5
5
  deep: false,
6
- filter(key) {
6
+ filter(_, { key }) {
7
7
  return !keysSet.has(key);
8
8
  },
9
9
  });
@@ -1 +1 @@
1
- {"root":["../../src/clone.ts","../../src/index.ts","../../src/is-object.ts","../../src/merge.ts","../../src/omit.ts","../../src/type-guards.ts"],"version":"5.8.3"}
1
+ {"root":["../../src/clone.ts","../../src/index.ts","../../src/is-object.ts","../../src/merge.ts","../../src/omit.ts","../../src/type-guards.ts"],"version":"5.9.2"}
@@ -1,5 +1,6 @@
1
1
  export function isBuiltIn(v) {
2
- return ((typeof v === 'object' &&
2
+ return ((v &&
3
+ typeof v === 'object' &&
3
4
  (v instanceof Date ||
4
5
  v instanceof RegExp ||
5
6
  v instanceof Map ||
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": "1.6.2",
4
+ "version": "2.0.0",
5
5
  "author": "Panates",
6
6
  "license": "MIT",
7
7
  "dependencies": {
package/types/merge.d.ts CHANGED
@@ -1,26 +1,71 @@
1
1
  export declare function merge<A, B>(target: A, source: B, options?: merge.Options): A & B;
2
- export declare function getMergeFunction(options?: merge.Options): Function;
2
+ export declare function merge<A, B, C>(target: A, source: [B, C], options?: merge.Options): A & B & C;
3
+ export declare function merge<A, B, C, D>(target: A, source: [B, C, D], options?: merge.Options): A & B & C & D;
4
+ export declare function merge<A, B, C, D, E>(target: A, source: [B, C, D, E], options?: merge.Options): A & B & C & D & E;
5
+ export declare function merge<A, B, C, D, F>(target: A, source: [B, C, D, F], options?: merge.Options): A & B & C & D & F;
6
+ /**
7
+ * @namespace
8
+ */
3
9
  export declare namespace merge {
4
- type NodeCallback = (key: string | symbol, source: any, target: any, path: string) => boolean;
10
+ type CallbackFn = (value: any, ctx: CallbackContext) => boolean;
11
+ interface CallbackContext {
12
+ source: any;
13
+ target: any;
14
+ key: string | symbol | number;
15
+ path: string;
16
+ }
5
17
  interface Options {
6
- deep?: boolean | 'full' | NodeCallback;
7
18
  /**
19
+ * Optional variable that determines the depth of an operation or inclusion behavior.
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
23
+ * - If assigned a `NodeCallback` function, it provides a custom callback mechanism for handling the operation.
24
+ *
25
+ * This variable can be used to define the level of depth or customization for a given process.
26
+ * @default false
8
27
  */
9
- moveArrays?: boolean | NodeCallback;
28
+ deep?: boolean | 'full' | CallbackFn;
10
29
  /**
11
- * Do not overwrite existing properties if set true
12
- * @default false
30
+ * Indicates whether symbol keys should be included.
31
+ * If set to `true`, properties with symbol keys will be considered.
32
+ * If `false` or `undefined`, symbol keys will be ignored.
13
33
  */
14
- keepExisting?: boolean;
34
+ symbolKeys?: boolean;
15
35
  /**
16
- * Copy property descriptors
17
- * @default false
36
+ * Specifies the behavior for merging arrays during a particular operation.
37
+ *
38
+ * When set to `true`, all array elements will be deeply merged, preserving all duplicates.
39
+ * When set to `'unique'`, only unique elements will be preserved in the merged array.
40
+ * If a callback function (`CallbackFn`) is provided, it determines the custom merging logic for the arrays.
18
41
  */
19
- copyDescriptors?: boolean | NodeCallback;
42
+ mergeArrays?: boolean | 'unique' | CallbackFn;
20
43
  /**
21
- * Do not copy source field if callback returns true
44
+ * Determines whether to retain pre-existing values.
45
+ * If set to `true`, existing entities are preserved without modification.
46
+ * If set to `false`, existing entities may be replaced or overridden by new ones.
47
+ * Alternatively, can be assigned a callback function (`CallbackFn`) that dynamically resolves whether to keep existing entities based on custom logic.
22
48
  */
23
- ignore?: NodeCallback;
49
+ keepExisting?: boolean | CallbackFn;
50
+ /**
51
+ * A boolean flag that determines whether property descriptors
52
+ * should be copied when transferring properties from one object
53
+ * to another.
54
+ *
55
+ * If set to true, both the value and descriptor metadata
56
+ * (e.g., writable, configurable, enumerable) of a property
57
+ * will be copied. If set to false or undefined, only the
58
+ * property values will be copied, without preserving descriptor
59
+ * details.
60
+ *
61
+ * This is typically used when needing to retain detailed control
62
+ * over property attributes during object manipulation.
63
+ */
64
+ copyDescriptors?: boolean;
65
+ /**
66
+ * Ignores the source field if callback returns true
67
+ */
68
+ ignoreSource?: CallbackFn;
24
69
  /**
25
70
  * Ignore fields which values are "undefined"
26
71
  * @default true
@@ -32,8 +77,8 @@ export declare namespace merge {
32
77
  */
33
78
  ignoreNulls?: boolean;
34
79
  /**
35
- *
80
+ * Ignores both target and source field if callback returns true
36
81
  */
37
- filter?: NodeCallback;
82
+ filter?: CallbackFn;
38
83
  }
39
84
  }