UNPKG

hypertune

Version:

[Hypertune](https://www.hypertune.com/) is the most flexible platform for feature flags, A/B testing, analytics and app configuration. Built with full end-to-end type-safety, Git-style version control and local, synchronous, in-memory flag evaluation. Opt

73 lines (68 loc) 2.15 kB
import { DeepPartial, ObjectValue, Value } from "../shared/types"; /** * merge recursively merges sources into value, returning a value. */ export default function merge<T extends Value>( value: T, ...sources: DeepPartial<T>[] ): T { return sources.reduce((currentValue, source) => { return mergeSource(currentValue, source); }, value); } function mergeSource<T extends Value>(value: T, source: DeepPartial<T>): T { if (source === null || source === undefined) { return value; // No override specified so we just return the value. } if (Array.isArray(value) && Array.isArray(source)) { return mergeArraySource(value, source) as T; } if ( !Array.isArray(value) && !Array.isArray(source) && typeof value === "object" && typeof source === "object" ) { return mergeObjectSource(value, source) as T; } return source as T; } function mergeArraySource<T extends Value>( value: T[], source: DeepPartial<T>[] ): T[] { // No null or undefined values indicate that we should override the list length. const overrideLength = !source.some( (item) => item === undefined || item === null ); if (overrideLength) { return source.map((itemOverride, index) => mergeSource(value[index], itemOverride) ); } // Add extra items to the end in case override array is longer than value array. return ( [...value, ...source.slice(value.length)] .map((itemValue, index) => mergeSource(itemValue as T, source[index])) // Remove any null or undefined values that are beyond the original list length. .filter((item) => item !== null && item !== undefined) ); } function mergeObjectSource<T extends ObjectValue>( value: T, override: DeepPartial<T> ): T { return Object.fromEntries( Object.entries(value) .map(([fieldName, field]) => [ fieldName, mergeSource(field, (override as T)[fieldName]), ]) .concat( // Add any additional fields from the override to the object. Object.entries(override as ObjectValue).filter( ([fieldName]) => !(fieldName in value) ) ) ) as T; }