Skip to content

Commit

Permalink
feat: pass namespace to custom merger (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
pooya parsa committed Nov 9, 2020
1 parent 934d736 commit 6bd7ef5
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 14 deletions.
14 changes: 7 additions & 7 deletions src/defu.ts
Expand Up @@ -5,9 +5,9 @@ function isObject (val: any) {
}

// Base function to apply defaults
function _defu<T> (baseObj: T, defaults: any, merger?: Merger): T {
function _defu<T> (baseObj: T, defaults: any, namespace: string = '.', merger?: Merger): T {
if (!isObject(defaults)) {
return _defu(baseObj, {}, merger)
return _defu(baseObj, {}, namespace, merger)
}

const obj = Object.assign({}, defaults)
Expand All @@ -23,14 +23,14 @@ function _defu<T> (baseObj: T, defaults: any, merger?: Merger): T {
continue
}

if (merger && merger(obj, key, val)) {
if (merger && merger(obj, key, val, namespace)) {
continue
}

if (Array.isArray(val) && Array.isArray(obj[key])) {
obj[key] = obj[key].concat(val)
} else if (isObject(val) && isObject(obj[key])) {
obj[key] = _defu(val, obj[key], merger)
obj[key] = _defu(val, obj[key], (namespace ? `${namespace}.` : '') + key.toString(), merger)
} else {
obj[key] = val
}
Expand All @@ -41,22 +41,22 @@ function _defu<T> (baseObj: T, defaults: any, merger?: Merger): T {

// Create defu wrapper with optional merger and multi arg support
function extend (merger?: Merger): DefuFn {
return (...args) => args.reduce((p, c) => _defu(p, c, merger), {} as any)
return (...args) => args.reduce((p, c) => _defu(p, c, '', merger), {} as any)
}

// Basic version
const defu = extend() as Defu

// Custom version with function merge support
defu.fn = extend((obj, key, currentValue) => {
defu.fn = extend((obj, key, currentValue, _namespace) => {
if (typeof obj[key] !== 'undefined' && typeof currentValue === 'function') {
obj[key] = currentValue(obj[key])
return true
}
})

// Custom version with function merge support only for defined arrays
defu.arrayFn = extend((obj, key, currentValue) => {
defu.arrayFn = extend((obj, key, currentValue, _namespace) => {
if (Array.isArray(obj[key]) && typeof currentValue === 'function') {
obj[key] = currentValue(obj[key])
return true
Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
@@ -1,7 +1,8 @@
export type Merger = <T extends Input, K extends keyof T>(
obj: T,
key: keyof T,
value: T[K]
value: T[K],
namespace: string
) => any;

export type DefuFn = <Source extends Input, Defaults extends Input>(
Expand Down
28 changes: 22 additions & 6 deletions test/defu.test.ts
Expand Up @@ -25,15 +25,15 @@ describe('defu', () => {
it('should copy nested values', () => {
const result = defu({ a: { b: 'c' } }, { a: { d: 'e' } })
expect(result).toEqual({
a: { b: 'c', d: 'e' },
a: { b: 'c', d: 'e' }
})
expectTypeOf(result).toEqualTypeOf<{ a: { b: string, d: string } }>()
})

it('should concat array values by default', () => {
const result = defu({ array: ['b', 'c'] }, { array: ['a'] })
expect(result).toEqual({
array: ['a', 'b', 'c'],
array: ['a', 'b', 'c']
})
expectTypeOf(result).toEqualTypeOf<{ array: string[] }>()
})
Expand All @@ -52,7 +52,7 @@ describe('defu', () => {

const result = defu({ a: fn }, { a: re })
expect(result).toEqual({ a: fn })
expectTypeOf(result).toEqualTypeOf<{ a: (() => number) | RegExp }>()
expectTypeOf(result).toEqualTypeOf<{ a:(() => number) | RegExp }>()
})

it('should handle non object first param', () => {
Expand All @@ -74,7 +74,7 @@ describe('defu', () => {
expect(result).toEqual({
a: 1,
b: 2,
c: 3,
c: 3
})
expectTypeOf(result).toEqualTypeOf<{ a: string | number, b: string | number, c?: number }>()
})
Expand All @@ -94,7 +94,7 @@ describe('defu', () => {
// @ts-expect-error
expect(defu(null, { foo: 1 }, false, 123, { bar: 2 })).toEqual({
foo: 1,
bar: 2,
bar: 2
})
})

Expand All @@ -113,7 +113,7 @@ describe('defu', () => {
expect(
defu.fn(
{
ignore: (val) => val.filter((i) => i !== 'dist'),
ignore: val => val.filter(i => i !== 'dist'),
num,
ignored: num
},
Expand Down Expand Up @@ -142,4 +142,20 @@ describe('defu', () => {
num
})
})

it('custom merger with namespace', () => {
const ext = defu.extend((obj, key, val, namespace) => {
// console.log({ obj, key, val, namespace })
if (key === 'modules') {
// TODO: It is not possible to override types with extend()
// @ts-ignore
obj[key] = namespace + ':' + [...val, ...obj[key]].sort().join(',')
return true
}
})

const obj1 = { modules: ['A'], foo: { bar: { modules: ['X'] } } }
const obj2 = { modules: ['B'], foo: { bar: { modules: ['Y'] } } }
expect(ext(obj1, obj2)).toEqual({ modules: ':A,B', foo: { bar: { modules: 'foo.bar:X,Y' } } })
})
})

0 comments on commit 6bd7ef5

Please sign in to comment.