Skip to content

Commit 3334c6b

Browse files
authored
feat(core): add mechanism for left shallow merge (#3849)
Refs #3845
1 parent 375419a commit 3334c6b

File tree

6 files changed

+114
-11
lines changed

6 files changed

+114
-11
lines changed

packages/apidom-core/README.md

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ transcluder.transclude(search, replace); // => ArrayElement<[1, 4, 3]>
106106

107107
## Shallow merging
108108

109-
`mergeRight` functions merged members of two or more ObjectElements shallowly
109+
`mergeRight` and `mergeLeft` functions merge members of two or more ObjectElements shallowly
110110
and handles shallow merging of ArrayElements as well.
111111

112112
### API
@@ -155,6 +155,53 @@ const output = mergeRight.all([ foobar, foobaz, bar ]);
155155
// => ObjectElement({ foo: { baz: 4 }, bar: 'yay!' })
156156
```
157157

158+
#### mergeLeft(source, target, [options])
159+
160+
Merges two ApiDOM elements source and target shallowly, returning a new merged ApiDOM element with the elements
161+
from both target and source. If an element at the same key is present for both target and source,
162+
the value from source will appear in the result. Merging creates a new ApiDOM element,
163+
so that neither target nor source is modified (operation is immutable).
164+
165+
```js
166+
import { mergeLeft, ObjectElement } from '@swagger-api/apidom-core';
167+
168+
const x = new ObjectElement({
169+
foo: { bar: 3 },
170+
});
171+
172+
const y = new ObjectElement({
173+
foo: { baz: 4 },
174+
quux: 5,
175+
});
176+
177+
const output = mergeLeft(x, y);
178+
// =>
179+
// ObjectElement({
180+
// foo: ObjectElement({
181+
// bar: 3,
182+
// }),
183+
// quux: 5,
184+
// })
185+
```
186+
187+
#### mergeLeft.all([element1, element2, ...], [options])
188+
189+
Merges shallowly any number of ApiDOM elements into a single ApiDOM element.
190+
191+
```js
192+
import { mergeLeft, ObjectElement } from '@swagger-api/apidom-core';
193+
194+
const foobar = new ObjectElement({ foo: { bar: 3 } });
195+
const foobaz = new ObjectElement({ foo: { baz: 4 } });
196+
const bar = new ObjectElement({ bar: 'yay!' });
197+
198+
const output = mergeLeft.all([ foobar, foobaz, bar ]);
199+
// => ObjectElement({ foo: { baz: 3 }, bar: 'yay!' })
200+
```
201+
202+
### Shallow merge Options
203+
204+
`mergeRight` and `mergeLeft` take the same options as [deepmerge](#deepmerge-options), except for `customMerge` and `clone`.
158205

159206
## Deep merging
160207

@@ -238,7 +285,7 @@ const output = deepmerge.all([ foobar, foobaz, bar ]);
238285
// => ObjectElement({ foo: { bar: 3, baz: 4 }, bar: 'yay!' })
239286
```
240287

241-
### Options
288+
### Deepmerge Options
242289

243290
#### arrayElementMerge
244291

packages/apidom-core/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ export { default as sexprs } from './transformers/sexprs';
105105

106106
export { default as deepmerge } from './deepmerge';
107107
export type { DeepMergeUserOptions, ObjectOrArrayElement } from './deepmerge';
108-
109108
export { default as mergeRight } from './merge/merge-right';
110109
export type { MergeRightOptions } from './merge/merge-right';
110+
export { default as mergeLeft } from './merge/merge-left';
111+
export type { MergeRightOptions as MergeLeftOptions } from './merge/merge-right';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import mergeRight from './merge-right';
2+
3+
const mergeLeft = (...[sourceElement, targetElement, options]: Parameters<typeof mergeRight>) => {
4+
return mergeRight(targetElement, sourceElement, options);
5+
};
6+
7+
mergeLeft.all = (...[list, options]: Parameters<typeof mergeRight.all>) => {
8+
return mergeRight.all([...list].reverse(), options);
9+
};
10+
11+
export default mergeLeft;

packages/apidom-core/test/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import 'mocha';
21
import * as fs from 'node:fs';
32
import * as path from 'node:path';
3+
import { assert } from 'chai';
44
import openapi3_1 from '@swagger-api/apidom-ns-openapi-3-1';
55
import ApiDOMParser from '@swagger-api/apidom-parser';
66
import * as openapi3_1Adapter from '@swagger-api/apidom-parser-adapter-openapi-json-3-1';
@@ -15,6 +15,6 @@ describe('apidom', function () {
1515
it('test', async function () {
1616
const parseResult = await parser.parse(spec);
1717

18-
console.dir(apiDOM.dehydrate(parseResult, namespace));
18+
assert.isDefined(apiDOM.dehydrate(parseResult, namespace));
1919
});
2020
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { assert } from 'chai';
2+
3+
import { toValue, ObjectElement, mergeLeft } from '../../src';
4+
5+
/**
6+
* mergeLeft is just a specialization of mergeRight. We don't need tests here
7+
* as they would become redundant with mergeRight tests.
8+
*/
9+
describe('mergeLeft', function () {
10+
it('should merge existing simple keys in target at the roots', function () {
11+
const source = new ObjectElement({ key1: 'changed', key2: 'value2' });
12+
const target = new ObjectElement({ key1: 'value1', key3: 'value3' });
13+
const merged = mergeLeft(target, source);
14+
const expected = {
15+
key1: 'value1',
16+
key2: 'value2',
17+
key3: 'value3',
18+
};
19+
20+
assert.deepEqual(
21+
toValue(target),
22+
{ key1: 'value1', key3: 'value3' },
23+
'merge should be immutable',
24+
);
25+
assert.deepEqual(toValue(merged), expected);
26+
});
27+
28+
it('mergeLeft.all', function () {
29+
const source = new ObjectElement({ key1: 'changed', key2: 'value2' });
30+
const target = new ObjectElement({ key1: 'value1', key3: 'value3' });
31+
const merged = mergeLeft.all([target, source]);
32+
const expected = {
33+
key1: 'value1',
34+
key2: 'value2',
35+
key3: 'value3',
36+
};
37+
38+
assert.deepEqual(
39+
toValue(target),
40+
{ key1: 'value1', key3: 'value3' },
41+
'merge should be immutable',
42+
);
43+
assert.deepEqual(toValue(merged), expected);
44+
});
45+
});

packages/apidom-core/test/merge/merge-right.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { assert } from 'chai';
22

33
import {
4-
deepmerge,
54
toValue,
65
Element,
76
ObjectElement,
@@ -24,7 +23,7 @@ describe('mergeRight', function () {
2423
it('should merge existing simple keys in target at the roots', function () {
2524
const source = new ObjectElement({ key1: 'changed', key2: 'value2' });
2625
const target = new ObjectElement({ key1: 'value1', key3: 'value3' });
27-
const merged = deepmerge(target, source);
26+
const merged = mergeRight(target, source);
2827
const expected = {
2928
key1: 'changed',
3029
key2: 'value2',
@@ -117,7 +116,7 @@ describe('mergeRight', function () {
117116
assert.deepEqual(toValue(merged), expected);
118117
});
119118

120-
it('should clone source and target', function () {
119+
it('should not clone source and target', function () {
121120
const source = new ObjectElement({
122121
b: {
123122
c: 'foo',
@@ -128,7 +127,7 @@ describe('mergeRight', function () {
128127
d: 'bar',
129128
},
130129
});
131-
const merged = deepmerge(target, source) as ObjectElement;
130+
const merged = mergeRight(target, source) as ObjectElement;
132131
const expected = {
133132
a: {
134133
d: 'bar',
@@ -139,8 +138,8 @@ describe('mergeRight', function () {
139138
};
140139

141140
assert.deepEqual(toValue(merged), expected);
142-
assert.notStrictEqual(merged.get('a'), target.get('a'));
143-
assert.notStrictEqual(merged.get('b'), source.get('b'));
141+
assert.strictEqual(merged.get('a'), target.get('a'));
142+
assert.strictEqual(merged.get('b'), source.get('b'));
144143
});
145144

146145
it('should replace object with simple key in target', function () {

0 commit comments

Comments
 (0)