Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add defu.arrayFn #21

Merged
merged 1 commit into from
Aug 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 41 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,24 +62,59 @@ ext({ cost: 15 }, { cost: 10 }) // { cost: 25 }

## Function Merger

Using `defu.fn`, if user provided a function, it will be called with default value instead of merging. Mostly useful for array merging.
Using `defu.fn`, if user provided a function, it will be called with default value instead of merging.

**Example:** Filter some items from defaults (array)
I can be useful for default values manipulation.

**Example:** Filter some items from defaults (array) and add 20 to the count default value.

```js

defu.fn({
ignore: (val) => val.filter(item => item !== 'dist'),
count: (count) => count + 20
}, {
ignore: ['node_modules','dist],
count: 10
})
/*
{
ignore: ['node_modules'],
count: 30
}
*/
```

**Note:** if the default value is not defined, the function defined won't be called and kept as value.

## Array Function Merger

`defu.arrayFn` is similar to `defu.fn` but **only applies to array values defined in defaults**.

**Example:** Filter some items from defaults (array) and add 20 to the count default value.

```js

defu.arrayFn({
ignore(val) => val.filter(i => i !== 'dist'),
num: () => 20
count: () => 20
}, {
ignore: [
'node_modules',
'dist
],
num: 10
}) // { ignore: ['node_modules'], num: 20 }
count: 10
})
/*
{
ignore: ['node_modules'],
count: () => 20
}
*/
```

**Note:** the function is called only if the value defined in defaults is an aray.

### Remarks

- `object` and `defaults` are not modified
Expand All @@ -88,7 +123,7 @@ defu.fn({
- Will concat `array` values (if default property is defined)
```js
console.log(defu({ array: ['b', 'c'] }, { array: ['a'] }))
// => { array: [a', 'b', 'c']}
// => { array: ['a', 'b', 'c']}
```

## License
Expand Down
15 changes: 12 additions & 3 deletions src/defu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ type DefuFn = <T>(...args: T | any) => T
interface Defu {
<T>(...args: T | any): T
fn: DefuFn
arrayFn: DefuFn
extend(merger?: Merger): DefuFn
}

Expand Down Expand Up @@ -55,9 +56,17 @@ function extend (merger?: Merger): DefuFn {
const defu = extend() as Defu

// Custom version with function merge support
defu.fn = extend((obj, key, value) => {
if (typeof value === 'function') {
obj[key] = value(obj[key])
defu.fn = extend((obj, key, currentValue) => {
if (typeof obj[key] !== 'undefined' && typeof currentValue === 'function') {
pi0 marked this conversation as resolved.
Show resolved Hide resolved
obj[key] = currentValue(obj[key])
return true
}
})

// Custom version with function merge support only for defined arrays
defu.arrayFn = extend((obj, key, currentValue) => {
if (Array.isArray(obj[key]) && typeof currentValue === 'function') {
obj[key] = currentValue(obj[key])
return true
}
})
Expand Down
68 changes: 45 additions & 23 deletions test/defu.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ describe('defu', () => {
})

it('should copy nested values', () => {
expect(defu({ a: { b: 'c' } }, { a: { d: 'e' } })).toEqual({ a: { b: 'c', d: 'e' } })
expect(defu({ a: { b: 'c' } }, { a: { d: 'e' } })).toEqual({
a: { b: 'c', d: 'e' },
})
})

it('should concat array values by default', () => {
expect(defu({ array: ['b', 'c'] }, { array: ['a'] })).toEqual({ array: ['a', 'b', 'c'] })
expect(defu({ array: ['b', 'c'] }, { array: ['a'] })).toEqual({
array: ['a', 'b', 'c'],
})
})

it('should handle non object first param', () => {
Expand All @@ -38,12 +42,14 @@ describe('defu', () => {
expect(defu({ a: 1 }, { b: 2, a: 'x' }, { c: 3, a: 'x', b: 'x' })).toEqual({
a: 1,
b: 2,
c: 3
c: 3,
})
})

it('should not override Object prototype', () => {
const payload = JSON.parse('{"constructor": {"prototype": {"isAdmin": true}}}')
const payload = JSON.parse(
'{"constructor": {"prototype": {"isAdmin": true}}}'
)
defu({}, payload)
defu(payload, {})
defu(payload, payload)
Expand All @@ -52,7 +58,10 @@ describe('defu', () => {
})

it('should ignore non-object arguments', () => {
expect(defu(null, { foo: 1 }, false, 123, { bar: 2 })).toEqual({ foo: 1, bar: 2 })
expect(defu(null, { foo: 1 }, false, 123, { bar: 2 })).toEqual({
foo: 1,
bar: 2,
})
})

it('custom merger', () => {
Expand All @@ -62,28 +71,41 @@ describe('defu', () => {
return true
}
})
expect(ext({ cost: 15 }, { cost: 10 }))
.toEqual({ cost: 25 })
expect(ext({ cost: 15 }, { cost: 10 })).toEqual({ cost: 25 })
})

it('custom merger with array', () => {
const ext = defu.extend((obj, key, currentValue) => {
if (Array.isArray(obj[key]) && typeof currentValue === 'function') {
obj[key] = currentValue(obj[key])
return true
}
})
expect(ext({ arr: () => ['c'] }, { arr: ['a', 'b'] }))
.toEqual({ arr: ['c'] })
it('defu.fn()', () => {
const num = () => 20
expect(ext({ num }, { num: 10 }))
.toEqual({ num })
expect(
defu.fn(
{
ignore: (val) => val.filter((i) => i !== 'dist'),
num,
ignored: num
},
{
ignore: ['node_modules', 'dist'],
num: 10
}
)
).toEqual({
ignore: ['node_modules'],
num: 20,
ignored: num
})
})

it('fn merger', () => {
expect(defu.fn({ ignore: val => val.filter(i => i !== 'dist') }, { ignore: ['node_modules', 'dist'] }))
.toEqual({ ignore: ['node_modules'] })
expect(defu.fn({ num: () => 20 }, { num: 10 }))
.toEqual({ num: 20 })
it('defu.arrayFn()', () => {
const num = () => 20
expect(defu.arrayFn({
arr: () => ['c'],
num
}, {
arr: ['a', 'b'],
num: 10
})).toEqual({
arr: ['c'],
num
})
})
})