UNPKG

wj-config

Version:

Javascript configuration module for NodeJS and browser frameworks such as React that works like ASP.net configuration where data sources are specified (usually JSON files) and environment variables can contribute/overwrite values by following a naming con

93 lines (92 loc) 3.71 kB
import { forEachProperty, isArray, isConfigNode } from "./helpers.js"; function mergeTwo(obj1, obj2, trace) { let recursiveTrace; // Add the properties of obj2. forEachProperty(obj2, (key, value) => { const value1 = obj1[key]; if (value1 !== undefined) { // If it is a scalar/array value, the value in object 2 must also be a scalar or array. // If it is an object value, then value in object 2 must also be an object. if (isConfigNode(value1) && !isConfigNode(value)) { throw new Error(`The destination value of property "${key}" is an object, but the second object is not providing an object value.`); } if (!isConfigNode(value1) && isConfigNode(value)) { throw new Error(`The destination value of property "${key}" is a scalar/array value, but the second object is not providing a scalar/array value.`); } if (isConfigNode(value1)) { // Recursively merge obj2 into obj1. if (trace) { recursiveTrace = { trace: (trace.trace[key] = trace.trace[key] ?? {}), dataSource: trace.dataSource }; } mergeTwo(value1, value, recursiveTrace); } else { obj1[key] = value; if (trace) { trace.trace[key] = trace.dataSource.trace(); } } } else { if (trace && isConfigNode(value)) { // Must trace, so merge. obj1[key] = {}; recursiveTrace = { trace: (trace.trace[key] = trace.trace[key] ?? {}), dataSource: trace.dataSource }; mergeTwo(obj1[key], value, recursiveTrace); } else { obj1[key] = value; if (!isConfigNode(value) && trace) { // Update the trace. trace.trace[key] = trace.dataSource.trace(); } } } }); return obj1; } export default function merge(objects, dataSources) { if (!isArray(objects)) { throw new Error('The provided value is not an array of objects.'); } // There must be at least one object. if (objects.length === 0 || !isConfigNode(objects[0]) || objects[0] === null || objects[0] === undefined) { throw new Error('The first element of the array is required and must be a suitable configuration object.'); } // If there are data sources, the number of these must match the number of provided objects. if (dataSources && objects.length !== dataSources?.length) { throw new Error('The number of provided objects differs from the number of provided data sources.'); } let result = {}; let trace; if (dataSources) { trace = { trace: {}, dataSource: dataSources[0] }; } for (let idx = 0; idx < objects.length; ++idx) { let nextObject = objects[idx]; // If null or undefined, just substitute for an empty object. if (nextObject === null || nextObject === undefined) { nextObject = {}; } else if (!isConfigNode(nextObject)) { throw new Error(`Configuration object at index ${idx} is not of the appropriate type.`); } if (trace) { trace.dataSource = dataSources[idx]; } mergeTwo(result, nextObject, trace); } if (trace) { result._trace = trace.trace; } return result; }