Skip to content

Commit

Permalink
fix(utils): handle array of primitives correctly (#6737)
Browse files Browse the repository at this point in the history
  • Loading branch information
ardatan authored Dec 4, 2024
1 parent 696a0d5 commit 1b24656
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 52 deletions.
16 changes: 16 additions & 0 deletions .changeset/three-houses-cry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'@graphql-tools/utils': patch
---

Handle array of primitives correctly

The bug was following;
```ts
mergeDeep([
{ options: ['$a', '$b'] },
{ options: ['$c'] },
{ options: ['$d', '$e'] },
])

// results in { options: [{}, {}] }
```
107 changes: 55 additions & 52 deletions packages/utils/src/mergeDeep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,50 @@ export function mergeDeep<S extends any[]>(
respectArrays = false,
respectArrayLength = false,
): UnboxIntersection<UnionToIntersection<BoxedTupleTypes<S>>> & any {
if (respectArrays && respectArrayLength) {
let expectedLength: number | undefined;
const areArraysInTheSameLength = sources.every(source => {
if (Array.isArray(source)) {
if (expectedLength === undefined) {
expectedLength = source.length;
return true;
} else if (expectedLength === source.length) {
return true;
}
let expectedLength: number | undefined;
let allArrays = true;
const areArraysInTheSameLength = sources.every(source => {
if (Array.isArray(source)) {
if (expectedLength === undefined) {
expectedLength = source.length;
return true;
} else if (expectedLength === source.length) {
return true;
}
return false;
});

if (areArraysInTheSameLength) {
return new Array(expectedLength).fill(null).map((_, index) =>
mergeDeep(
sources.map(source => source[index]),
respectPrototype,
respectArrays,
respectArrayLength,
),
);
} else {
allArrays = false;
}
return false;
});

if (respectArrayLength && areArraysInTheSameLength) {
return new Array(expectedLength).fill(null).map((_, index) =>
mergeDeep(
sources.map(source => source[index]),
respectPrototype,
respectArrays,
respectArrayLength,
),
);
}
if (allArrays) {
return sources.flat(1);
}

const output = {};
let output: any;
let firstObjectSource: any;
if (respectPrototype) {
Object.setPrototypeOf(output, Object.create(Object.getPrototypeOf(sources[0])));
firstObjectSource = sources.find(source => isObject(source));
if (output == null) {
output = {};
}
if (firstObjectSource) {
Object.setPrototypeOf(output, Object.create(Object.getPrototypeOf(firstObjectSource)));
}
}
for (const source of sources) {
if (isObject(source)) {
if (respectPrototype) {
if (firstObjectSource) {
const outputPrototype = Object.getPrototypeOf(output);
const sourcePrototype = Object.getPrototypeOf(source);
if (sourcePrototype) {
Expand All @@ -58,36 +69,28 @@ export function mergeDeep<S extends any[]>(
}

for (const key in source) {
if (isObject(source[key])) {
if (!(key in output)) {
Object.assign(output, { [key]: source[key] });
} else {
output[key] = mergeDeep(
[output[key], source[key]] as S,
respectPrototype,
respectArrays,
respectArrayLength,
);
}
} else if (respectArrays && Array.isArray(output[key])) {
if (Array.isArray(source[key])) {
if (respectArrayLength && output[key].length === source[key].length) {
output[key] = mergeDeep(
[output[key], source[key]] as S,
respectPrototype,
respectArrays,
respectArrayLength,
);
} else {
output[key].push(...source[key]);
}
} else {
output[key].push(source[key]);
}
if (output == null) {
output = {};
}
if (key in output) {
output[key] = mergeDeep(
[output[key], source[key]],
respectPrototype,
respectArrays,
respectArrayLength,
);
} else {
Object.assign(output, { [key]: source[key] });
output[key] = source[key];
}
}
} else if (Array.isArray(source)) {
if (!Array.isArray(output)) {
output = source;
} else {
output = mergeDeep([output, source], respectPrototype, respectArrays, respectArrayLength);
}
} else {
output = source;
}
}
return output;
Expand Down
6 changes: 6 additions & 0 deletions packages/utils/tests/mergeDeep.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,10 @@ describe('mergeDeep', () => {
{ b: 2, d: 4 },
]);
});

it('merges string arrays', () => {
const a = { options: ['$A', '$B'] };
const b = { options: ['$A', '$B'] };
expect(mergeDeep([a, b], undefined, true, true)).toEqual({ options: ['$A', '$B'] });
});
});

0 comments on commit 1b24656

Please sign in to comment.