Skip to content

Commit bfed5b0

Browse files
UzlopakmcollinaEomm
authored
Initial implementation (#1)
* initial implementation * remove examples * add all Option * 100% test coverage * write Readme.md * add Licensing Info to old deepmerge * fix descriptin * add CI-badge * add primitive benchmark * remove clone option in tests * Update LICENSE Co-authored-by: Matteo Collina <[email protected]> * improve benchmarks * add unit test * add test for prototype pollution * fix prototype pollution, improve performance of all * add use strict everywhere * improve perf by using less branches and less lookup methods * less branches * rename map to clone * add test for non-enumerable symbol keys * add with lint to ci.yml * Apply suggestions from code review Co-authored-by: Manuel Spigolon <[email protected]> * Update test/index.test.js Co-authored-by: Manuel Spigolon <[email protected]> * make bechmarks nicer * fix npm scrpits Co-authored-by: Matteo Collina <[email protected]> Co-authored-by: Manuel Spigolon <[email protected]>
1 parent d44f7cd commit bfed5b0

File tree

22 files changed

+1484
-37
lines changed

22 files changed

+1484
-37
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ on:
1313
jobs:
1414
test:
1515
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v3
16+
with:
17+
lint: true

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=false

.taprc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ts: false
2+
jsx: false
3+
coverage: true

LICENSE

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
MIT License
2+
3+
Copyright (c) The Fastify Team
4+
5+
The Fastify team members are listed at https://github.com/fastify/fastify#team
6+
and in the README file.
7+
8+
Permission is hereby granted, free of charge, to any person obtaining a copy
9+
of this software and associated documentation files (the "Software"), to deal
10+
in the Software without restriction, including without limitation the rights
11+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
copies of the Software, and to permit persons to whom the Software is
13+
furnished to do so, subject to the following conditions:
14+
15+
The above copyright notice and this permission notice shall be included in all
16+
copies or substantial portions of the Software.
17+
18+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
SOFTWARE.

README.md

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,65 @@
1-
# skeleton
2-
3-
Template repository to create standardized Fastify plugins.
4-
5-
# Getting started
6-
7-
- Click on `Use this template` above to create a new repository based on this repository.
8-
9-
# What's included?
10-
11-
1. Github CI Actions for installing, testing your package.
12-
2. Github CI Actions to validate different package managers.
13-
3. Dependabot V2 config to automate dependency updates.
14-
4. Template for the GitHub App [Stale](https://github.com/apps/stale) to mark issues as stale.
15-
5. Template for the GitHub App [tests-checker](https://github.com/apps/tests-checker) to check if a PR contains tests.
16-
17-
# Repository structure
18-
19-
```
20-
├── .github
21-
│ ├── workflows
22-
│ │ ├── ci.yml
23-
│ │ └── package-manager-ci.yml
24-
│ ├── .stale.yml
25-
│ ├── dependabot.yml
26-
│ └── tests_checker.yml
27-
28-
├── docs (Documentation)
29-
30-
├── examples (Code examples)
31-
32-
├── test (Application tests)
33-
34-
├── types (Typescript types)
35-
36-
└── README.md
37-
```
1+
# @fastify/deepmerge
2+
3+
![CI](https://github.com/fastify/deepmerge/workflows/CI/badge.svg)
4+
[![NPM version](https://img.shields.io/npm/v/@fastify/deepmerge.svg?style=flat)](https://www.npmjs.com/package/@fastify/deepmerge)
5+
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://standardjs.com/)
6+
7+
Merges the enumerable properties of two or more objects deeply. Fastest implementation of deepmerge, see section 'Benchmarks'.
8+
9+
### Install
10+
```
11+
npm i @fastify/deepmerge
12+
```
13+
14+
### Usage
15+
16+
The module exports a function, which provides a function to deepmerge Objects.
17+
18+
```
19+
deepmerge(options)
20+
```
21+
22+
`options` is optional and can contain following values
23+
24+
- `symbols` (`boolean`, optional) - should also merge object-keys which are symbols, default is false
25+
- `all` (`boolean`, optional) - merges all parameters, default is false
26+
27+
```js
28+
const deepmerge = require('@fastify/deepmegre')()
29+
const result = deepmerge({a: 'value'}, { b: 404 })
30+
console.log(result) // {a: 'value', b: 404 }
31+
```
32+
33+
```js
34+
const deepmerge = require('@fastify/deepmegre')({ all: true })
35+
const result = deepmerge({a: 'value'}, { b: 404 }, { a: 404 })
36+
console.log(result) // {a: 404, b: 404 }
37+
```
38+
39+
## Benchmarks
40+
41+
The benchmarks are available in the benchmark-folder.
42+
43+
`npm run bench` - benchmark various use cases of deepmerge:
44+
```
45+
@fastify/deepmerge: merge regex with date x 1,266,447,885 ops/sec ±0.14% (97 runs sampled)
46+
@fastify/deepmerge: merge object with a primitive x 1,266,435,016 ops/sec ±0.33% (97 runs sampled)
47+
@fastify/deepmerge: merge two arrays containing strings x 25,591,739 ops/sec ±0.24% (98 runs sampled)
48+
@fastify/deepmerge: two merge arrays containing objects x 976,182 ops/sec ±0.46% (98 runs sampled)
49+
@fastify/deepmerge: merge two flat objects x 10,027,879 ops/sec ±0.36% (94 runs sampled)
50+
@fastify/deepmerge: merge nested objects x 5,341,227 ops/sec ±0.67% (94 runs sampled)
51+
```
52+
53+
`npm run bench:compare` - comparison of @fastify/deepmerge with other popular deepmerge implementation:
54+
```
55+
@fastify/deepmerge x 403,777 ops/sec ±0.22% (98 runs sampled)
56+
deepmerge x 21,143 ops/sec ±0.83% (93 runs sampled)
57+
merge-deep x 89,447 ops/sec ±0.59% (95 runs sampled)
58+
ts-deepmerge x 185,601 ops/sec ±0.59% (96 runs sampled)
59+
deepmerge-ts x 185,310 ops/sec ±0.50% (92 runs sampled)
60+
lodash.merge x 89,053 ops/sec ±0.37% (99 runs sampled)
61+
```
62+
63+
## License
64+
65+
Licensed under [MIT](./LICENSE).

benchmark/.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=false

benchmark/bench.all.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict'
2+
3+
const Benchmark = require('benchmark')
4+
const deepmerge = require('..')({ symbol: false, all: true })
5+
6+
const sourceSimple = { key1: 'changed', key2: 'value2' }
7+
const targetSimple = { key1: 'value1', key3: 'value3' }
8+
9+
const sourceNested = {
10+
key1: {
11+
subkey1: 'subvalue1',
12+
subkey2: 'subvalue2'
13+
}
14+
}
15+
const targetNested = {
16+
key1: 'value1',
17+
key2: 'value2'
18+
}
19+
20+
const primitive = 'primitive'
21+
22+
const date = new Date()
23+
const regex = /a/g
24+
25+
const simpleArrayTarget = ['a1', 'a2', 'c1', 'f1', 'p1']
26+
const simpleArraySource = ['t1', 's1', 'c2', 'r1', 'p2', 'p3']
27+
28+
const complexArraySource = [{ ...sourceSimple }, { ...sourceSimple }, { ...sourceSimple }, { ...sourceSimple }, { ...sourceSimple }]
29+
const complexArrayTarget = [{ ...targetSimple }, { ...targetSimple }, { ...targetSimple }, { ...targetSimple }, { ...targetSimple }]
30+
31+
new Benchmark.Suite()
32+
.add('@fastify/deepmerge: merge regex with date', function () {
33+
deepmerge(regex, date)
34+
})
35+
.add('@fastify/deepmerge: merge object with a primitive', function () {
36+
deepmerge(targetSimple, primitive)
37+
})
38+
.add('@fastify/deepmerge: merge two arrays containing strings', function () {
39+
deepmerge(simpleArrayTarget, simpleArraySource)
40+
})
41+
.add('@fastify/deepmerge: two merge arrays containing objects', function () {
42+
deepmerge(complexArrayTarget, complexArraySource)
43+
})
44+
.add('@fastify/deepmerge: merge two flat objects', function () {
45+
deepmerge(targetSimple, sourceSimple)
46+
})
47+
.add('@fastify/deepmerge: merge nested objects', function () {
48+
deepmerge(targetNested, sourceNested)
49+
})
50+
.on('cycle', function (event) {
51+
console.log(String(event.target))
52+
})
53+
.run()
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
'use strict'
2+
3+
const Benchmark = require('benchmark')
4+
const fastifyDeepmerge = require('..')({ symbol: false })
5+
const deepmerge = require('deepmerge')
6+
const mergedeep = require('merge-deep')
7+
const tsDeepmerge = require('ts-deepmerge').default
8+
const deepmergeTs = require('deepmerge-ts').deepmerge
9+
const lodashMerge = require('lodash.merge')
10+
11+
const sourceSimple = { key1: 'changed', key2: 'value2' }
12+
const targetSimple = { key1: 'value1', key3: 'value3' }
13+
14+
const sourceNested = {
15+
key1: {
16+
subkey1: 'subvalue1',
17+
subkey2: 'subvalue2'
18+
}
19+
}
20+
const targetNested = {
21+
key1: 'value1',
22+
key2: 'value2'
23+
}
24+
25+
const primitive = 'primitive'
26+
27+
const date = new Date()
28+
const regex = /a/g
29+
30+
const simpleArrayTarget = ['a1', 'a2', 'c1', 'f1', 'p1']
31+
const simpleArraySource = ['t1', 's1', 'c2', 'r1', 'p2', 'p3']
32+
33+
const complexArraySource = [{ ...sourceSimple }, { ...sourceSimple }, { ...sourceSimple }, { ...sourceSimple }, { ...sourceSimple }]
34+
const complexArrayTarget = [{ ...targetSimple }, { ...targetSimple }, { ...targetSimple }, { ...targetSimple }, { ...targetSimple }]
35+
36+
new Benchmark.Suite()
37+
.add('@fastify/deepmerge: merge regex with date', function () {
38+
fastifyDeepmerge(regex, date)
39+
})
40+
.add('@fastify/deepmerge: merge object with a primitive', function () {
41+
fastifyDeepmerge(targetSimple, primitive)
42+
})
43+
.add('@fastify/deepmerge: merge two arrays containing strings', function () {
44+
fastifyDeepmerge(simpleArrayTarget, simpleArraySource)
45+
})
46+
.add('@fastify/deepmerge: two merge arrays containing objects', function () {
47+
fastifyDeepmerge(complexArrayTarget, complexArraySource)
48+
})
49+
.add('@fastify/deepmerge: merge two flat objects', function () {
50+
fastifyDeepmerge(targetSimple, sourceSimple)
51+
})
52+
.add('@fastify/deepmerge: merge nested objects', function () {
53+
fastifyDeepmerge(targetNested, sourceNested)
54+
})
55+
.add('deepmerge: merge regex with date', function () {
56+
deepmerge(regex, date)
57+
})
58+
.add('deepmerge: merge object with a primitive', function () {
59+
deepmerge(targetSimple, primitive)
60+
})
61+
.add('deepmerge: merge two arrays containing strings', function () {
62+
deepmerge(simpleArrayTarget, simpleArraySource)
63+
})
64+
.add('deepmerge: two merge arrays containing objects', function () {
65+
deepmerge(complexArrayTarget, complexArraySource)
66+
})
67+
.add('deepmerge: merge two flat objects', function () {
68+
deepmerge(targetSimple, sourceSimple)
69+
})
70+
.add('deepmerge: merge nested objects', function () {
71+
deepmerge(targetNested, sourceNested)
72+
})
73+
.add('merge-deep: merge regex with date', function () {
74+
mergedeep(regex, date)
75+
})
76+
.add('merge-deep: merge object with a primitive', function () {
77+
mergedeep(targetSimple, primitive)
78+
})
79+
.add('merge-deep: merge two arrays containing strings', function () {
80+
mergedeep(simpleArrayTarget, simpleArraySource)
81+
})
82+
.add('merge-deep: two merge arrays containing objects', function () {
83+
mergedeep(complexArrayTarget, complexArraySource)
84+
})
85+
.add('merge-deep: merge two flat objects', function () {
86+
mergedeep(targetSimple, sourceSimple)
87+
})
88+
.add('merge-deep: merge nested objects', function () {
89+
mergedeep(targetNested, sourceNested)
90+
})
91+
.add('ts-deepmerge: merge regex with date', function () {
92+
tsDeepmerge(regex, date)
93+
})
94+
.add('ts-deepmerge: merge object with a primitive', function () {
95+
tsDeepmerge(targetSimple, primitive)
96+
})
97+
.add('ts-deepmerge: merge two arrays containing strings', function () {
98+
tsDeepmerge(simpleArrayTarget, simpleArraySource)
99+
})
100+
.add('ts-deepmerge: two merge arrays containing objects', function () {
101+
tsDeepmerge(complexArrayTarget, complexArraySource)
102+
})
103+
.add('ts-deepmerge: merge two flat objects', function () {
104+
tsDeepmerge(targetSimple, sourceSimple)
105+
})
106+
.add('ts-deepmerge: merge nested objects', function () {
107+
tsDeepmerge(targetNested, sourceNested)
108+
})
109+
.add('deepmerge-ts: merge regex with date', function () {
110+
deepmergeTs(regex, date)
111+
})
112+
.add('deepmerge-ts: merge object with a primitive', function () {
113+
deepmergeTs(targetSimple, primitive)
114+
})
115+
.add('deepmerge-ts: merge two arrays containing strings', function () {
116+
deepmergeTs(simpleArrayTarget, simpleArraySource)
117+
})
118+
.add('deepmerge-ts: two merge arrays containing objects', function () {
119+
deepmergeTs(complexArrayTarget, complexArraySource)
120+
})
121+
.add('deepmerge-ts: merge two flat objects', function () {
122+
deepmergeTs(targetSimple, sourceSimple)
123+
})
124+
.add('deepmerge-ts: merge nested objects', function () {
125+
deepmergeTs(targetNested, sourceNested)
126+
})
127+
.add('lodash.merge: merge regex with date', function () {
128+
lodashMerge(regex, date)
129+
})
130+
.add('lodash.merge: merge object with a primitive', function () {
131+
lodashMerge(targetSimple, primitive)
132+
})
133+
.add('lodash.merge: merge two arrays containing strings', function () {
134+
lodashMerge(simpleArrayTarget, simpleArraySource)
135+
})
136+
.add('lodash.merge: two merge arrays containing objects', function () {
137+
lodashMerge(complexArrayTarget, complexArraySource)
138+
})
139+
.add('lodash.merge: merge two flat objects', function () {
140+
lodashMerge(targetSimple, sourceSimple)
141+
})
142+
.add('lodash.merge: merge nested objects', function () {
143+
lodashMerge(targetNested, sourceNested)
144+
})
145+
.on('cycle', function (event) {
146+
console.log(String(event.target))
147+
})
148+
.run()

0 commit comments

Comments
 (0)