Skip to content

Commit 1b24656

Browse files
authored
fix(utils): handle array of primitives correctly (#6737)
1 parent 696a0d5 commit 1b24656

File tree

3 files changed

+77
-52
lines changed

3 files changed

+77
-52
lines changed

.changeset/three-houses-cry.md

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@graphql-tools/utils': patch
3+
---
4+
5+
Handle array of primitives correctly
6+
7+
The bug was following;
8+
```ts
9+
mergeDeep([
10+
{ options: ['$a', '$b'] },
11+
{ options: ['$c'] },
12+
{ options: ['$d', '$e'] },
13+
])
14+
15+
// results in { options: [{}, {}] }
16+
```

packages/utils/src/mergeDeep.ts

+55-52
Original file line numberDiff line numberDiff line change
@@ -12,39 +12,50 @@ export function mergeDeep<S extends any[]>(
1212
respectArrays = false,
1313
respectArrayLength = false,
1414
): UnboxIntersection<UnionToIntersection<BoxedTupleTypes<S>>> & any {
15-
if (respectArrays && respectArrayLength) {
16-
let expectedLength: number | undefined;
17-
const areArraysInTheSameLength = sources.every(source => {
18-
if (Array.isArray(source)) {
19-
if (expectedLength === undefined) {
20-
expectedLength = source.length;
21-
return true;
22-
} else if (expectedLength === source.length) {
23-
return true;
24-
}
15+
let expectedLength: number | undefined;
16+
let allArrays = true;
17+
const areArraysInTheSameLength = sources.every(source => {
18+
if (Array.isArray(source)) {
19+
if (expectedLength === undefined) {
20+
expectedLength = source.length;
21+
return true;
22+
} else if (expectedLength === source.length) {
23+
return true;
2524
}
26-
return false;
27-
});
28-
29-
if (areArraysInTheSameLength) {
30-
return new Array(expectedLength).fill(null).map((_, index) =>
31-
mergeDeep(
32-
sources.map(source => source[index]),
33-
respectPrototype,
34-
respectArrays,
35-
respectArrayLength,
36-
),
37-
);
25+
} else {
26+
allArrays = false;
3827
}
28+
return false;
29+
});
30+
31+
if (respectArrayLength && areArraysInTheSameLength) {
32+
return new Array(expectedLength).fill(null).map((_, index) =>
33+
mergeDeep(
34+
sources.map(source => source[index]),
35+
respectPrototype,
36+
respectArrays,
37+
respectArrayLength,
38+
),
39+
);
40+
}
41+
if (allArrays) {
42+
return sources.flat(1);
3943
}
4044

41-
const output = {};
45+
let output: any;
46+
let firstObjectSource: any;
4247
if (respectPrototype) {
43-
Object.setPrototypeOf(output, Object.create(Object.getPrototypeOf(sources[0])));
48+
firstObjectSource = sources.find(source => isObject(source));
49+
if (output == null) {
50+
output = {};
51+
}
52+
if (firstObjectSource) {
53+
Object.setPrototypeOf(output, Object.create(Object.getPrototypeOf(firstObjectSource)));
54+
}
4455
}
4556
for (const source of sources) {
4657
if (isObject(source)) {
47-
if (respectPrototype) {
58+
if (firstObjectSource) {
4859
const outputPrototype = Object.getPrototypeOf(output);
4960
const sourcePrototype = Object.getPrototypeOf(source);
5061
if (sourcePrototype) {
@@ -58,36 +69,28 @@ export function mergeDeep<S extends any[]>(
5869
}
5970

6071
for (const key in source) {
61-
if (isObject(source[key])) {
62-
if (!(key in output)) {
63-
Object.assign(output, { [key]: source[key] });
64-
} else {
65-
output[key] = mergeDeep(
66-
[output[key], source[key]] as S,
67-
respectPrototype,
68-
respectArrays,
69-
respectArrayLength,
70-
);
71-
}
72-
} else if (respectArrays && Array.isArray(output[key])) {
73-
if (Array.isArray(source[key])) {
74-
if (respectArrayLength && output[key].length === source[key].length) {
75-
output[key] = mergeDeep(
76-
[output[key], source[key]] as S,
77-
respectPrototype,
78-
respectArrays,
79-
respectArrayLength,
80-
);
81-
} else {
82-
output[key].push(...source[key]);
83-
}
84-
} else {
85-
output[key].push(source[key]);
86-
}
72+
if (output == null) {
73+
output = {};
74+
}
75+
if (key in output) {
76+
output[key] = mergeDeep(
77+
[output[key], source[key]],
78+
respectPrototype,
79+
respectArrays,
80+
respectArrayLength,
81+
);
8782
} else {
88-
Object.assign(output, { [key]: source[key] });
83+
output[key] = source[key];
8984
}
9085
}
86+
} else if (Array.isArray(source)) {
87+
if (!Array.isArray(output)) {
88+
output = source;
89+
} else {
90+
output = mergeDeep([output, source], respectPrototype, respectArrays, respectArrayLength);
91+
}
92+
} else {
93+
output = source;
9194
}
9295
}
9396
return output;

packages/utils/tests/mergeDeep.test.ts

+6
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,10 @@ describe('mergeDeep', () => {
6666
{ b: 2, d: 4 },
6767
]);
6868
});
69+
70+
it('merges string arrays', () => {
71+
const a = { options: ['$A', '$B'] };
72+
const b = { options: ['$A', '$B'] };
73+
expect(mergeDeep([a, b], undefined, true, true)).toEqual({ options: ['$A', '$B'] });
74+
});
6975
});

0 commit comments

Comments
 (0)