forked from bryan-hunter/deepmerge
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Rebecca Stevens
committed
Nov 16, 2020
1 parent
b726634
commit 9a5d906
Showing
5 changed files
with
341 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export { default, deepmergeAll } from "./deepmerge" | ||
export type { Options } from "./options" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,89 @@ | ||
import isPlainObj from "is-plain-obj" | ||
|
||
import type { Property } from "./types" | ||
import { cloneUnlessOtherwiseSpecified } from "./utils" | ||
|
||
function defaultIsMergeable(value) { | ||
/** | ||
* Deep merge options. | ||
*/ | ||
export type Options = Partial<{ | ||
arrayMerge?: ArrayMerge | ||
clone?: boolean | ||
customMerge?: ObjectMerge | ||
isMergeable?: IsMergeable | ||
}> | ||
|
||
/** | ||
* Deep merge options with explicit keys. | ||
*/ | ||
export type ExplicitOptions<O extends Options = Options> = { | ||
[K in keyof Options]-?: undefined extends O[K] ? never : O[K] | ||
} | ||
|
||
/** | ||
* Deep merge options with defaults applied. | ||
*/ | ||
export type FullOptions<O extends Options = Options> = { | ||
arrayMerge: O["arrayMerge"] extends undefined | ||
? typeof defaultArrayMerge | ||
: NonNullable<O["arrayMerge"]> | ||
clone: O["arrayMerge"] extends undefined ? true : NonNullable<O["clone"]> | ||
customMerge?: O["customMerge"] | ||
isMergeable: O["arrayMerge"] extends undefined | ||
? typeof defaultIsMergeable | ||
: NonNullable<O["isMergeable"]> | ||
cloneUnlessOtherwiseSpecified: <T>(value: T, options: FullOptions) => T | ||
} | ||
|
||
/** | ||
* A function that determins if a type is mergable. | ||
*/ | ||
export type IsMergeable = (value: any) => boolean | ||
|
||
/** | ||
* A function that merges any 2 arrays. | ||
*/ | ||
export type ArrayMerge<T1 = any, T2 = any> = (target: T1[], source: T2[], options: FullOptions) => any | ||
|
||
/** | ||
* A function that merges any 2 non-arrays values. | ||
*/ | ||
export type ObjectMerge<K = any> = ( | ||
key: K | ||
) => ((target: any, source: any, options: FullOptions) => any) | undefined | ||
|
||
function defaultIsMergeable(value: unknown): value is Record<Property, unknown> | Array<unknown> { | ||
return Array.isArray(value) || isPlainObj(value) | ||
} | ||
|
||
function defaultArrayMerge(target, source, options) { | ||
function defaultArrayMerge<T1 extends unknown, T2 extends unknown>( | ||
target: readonly T1[], | ||
source: readonly T2[], | ||
options: FullOptions | ||
) { | ||
return [...target, ...source].map((element) => | ||
cloneUnlessOtherwiseSpecified(element, options) | ||
) | ||
) as T1 extends readonly [...infer E1] | ||
? T2 extends readonly [...infer E2] | ||
? [...E1, ...E2] | ||
: never | ||
: never | ||
} | ||
|
||
export function getFullOptions(options) { | ||
export function getFullOptions<O extends Options>(options?: O) { | ||
const overrides = | ||
options === undefined | ||
? undefined | ||
: (Object.fromEntries( | ||
// Filter out keys explicitly set to undefined. | ||
Object.entries(options).filter(([key, value]) => value !== undefined) | ||
)) | ||
) as O) | ||
|
||
return { | ||
arrayMerge: defaultArrayMerge, | ||
isMergeable: defaultIsMergeable, | ||
clone: true, | ||
...overrides, | ||
cloneUnlessOtherwiseSpecified | ||
}; | ||
...overrides, | ||
cloneUnlessOtherwiseSpecified, | ||
} as FullOptions<O> | ||
} |
Oops, something went wrong.