UNPKG

objectro

Version:

Transform and validate objects

123 lines (111 loc) 4.01 kB
import get from "lodash/get"; import set from "lodash/set"; import isString from "lodash/isString"; import isArray from "lodash/isArray"; import isPlainObject from "lodash/isPlainObject"; import isFunction from "lodash/isFunction"; import castArray from "lodash/castArray"; import { POJO, TransformFn } from "../lib/types"; import { has, isEmpty } from "./util"; const useInputOrOutputValue = ( propName: string, input: any, output: any ): any => has(input, propName) && !isEmpty(get(input, propName)) ? get(input, propName) : get(output, propName); /** * Take an object and output another object with different property * names (shallow destructuring) and additionally formatting the value * (which can allow deeper destructuring). * * Functions can be used in object maps which can format the input object's * value. * * Transform can also return non-object values. */ export function transform(input: any, ...props: any[]): any { let immutableInput = Object.freeze({ ...input }); let output: POJO = {}; if (!props || !props.length) { return { ...input }; } props.forEach( (processProps): void => { // A string represents a name from the input to map to the output if (isString(processProps)) { let useValue = useInputOrOutputValue(processProps, input, output); if (useValue !== undefined) { set(output, processProps, useValue); } } // An object represents a map from the input structure to a new structure else if (isPlainObject(processProps)) { Object.keys(processProps).forEach( (propName): void => { // Original value let useValue = useInputOrOutputValue(propName, input, output); // New name for prop on output object or a function to format the input // object's value let outputProp = get(processProps, propName); // console.log("objectro.transform output snapshot", { // input, // output: { ...output }, // propName, // useValue, // outputProp // }); // Format value using function if (isFunction(outputProp)) { // Use the input as the context and pass the value, propName // and the output object as parameters. // The function should return an object which will then be merged // into the output object. // We also prevent the function from mutating the input object // by making a copy of the input object and freezing it. let outputValue = (outputProp as TransformFn)( useValue, propName, immutableInput, output ); // If a function doesn't return anything, then we assume the function // has done the necessary manipulations to the output object itself if (outputValue !== undefined) { if (isPlainObject(outputValue)) { output = { ...output, ...outputValue }; } else { set(output, propName, outputValue); } } } // Nested transform map on nested input objects else if ( isPlainObject(useValue) && (isPlainObject(outputProp) || isArray(outputProp)) ) { set( output, propName, transform(useValue, ...castArray(outputProp)) ); } // Map input value to new prop in output else if (isString(outputProp) && useValue !== undefined) { set(output, outputProp, useValue); } } ); } } ); return output; } export default transform; /** * @typedef {Object} TransformMap */