UNPKG

map-transform

Version:

Map and transform objects with mapping definitions

165 lines 7.25 kB
import { getStateValue, setStateValue } from './stateHelpers.js'; import modifyOperationObject from './modifyOperationObject.js'; import { noopNext } from '../utils/stateHelpers.js'; import { isObject } from './is.js'; import { get } from '../operations/getSet.js'; import props from '../operations/props.js'; import iterate from '../operations/iterate.js'; import transform from '../operations/transform.js'; import filter from '../operations/filter.js'; import ifelse from '../operations/ifelse.js'; import apply from '../operations/apply.js'; import alt from '../operations/alt.js'; import { fwd, rev } from '../operations/directionals.js'; import { concat, concatRev } from '../operations/concat.js'; import { lookup, lookdown } from '../operations/lookup.js'; import pipe from '../operations/pipe.js'; import { unescapeValue } from './escape.js'; import { ensureArray } from './array.js'; const passStateThroughNext = (next) => async (state) => next(state); const nonOperatorKeys = [ '$iterate', '$modify', '$noDefaults', '$flip', '$direction', ]; const isOperatorKey = (key) => key[0] === '$' && !nonOperatorKeys.includes(key); const isOperationObject = (def) => isObject(def) && Object.keys(def).filter(isOperatorKey).length > 0; export const isOperationType = (def, prop) => def.hasOwnProperty(prop); const pipeIfArray = (operations) => (Array.isArray(operations) ? pipe(operations) : operations); export const isPath = (def) => typeof def === 'string'; export const isTransformObject = (def) => isObject(def) && !isOperationObject(def); export const isPipeline = (def) => Array.isArray(def); export const isOperation = (def) => typeof def === 'function'; export const isTransformDefinition = (def) => isPath(def) || isObject(def) || isPipeline(def) || isOperation(def); const wrapInNoDefaults = (fn) => (options) => (next) => { const stateMapper = fn(options)(next); return async (state) => { const stateWithNoDefaults = { ...state, noDefaults: true }; return stateMapper(stateWithNoDefaults); }; }; function wrapFromDefinition(ops, def) { const opsWithNoDefaults = def.$noDefaults === true ? wrapInNoDefaults(pipeIfArray(ops)) : ops; const fn = def.$iterate === true ? iterate(opsWithNoDefaults) : opsWithNoDefaults; return (options) => { const dir = def.$direction; if (typeof dir === 'string') { if (dir === 'rev' || dir === options.revAlias) { return rev(fn)(options); } else if (dir === 'fwd' || dir === options.fwdAlias) { return fwd(fn)(options); } } return Array.isArray(fn) ? pipe(fn, true)(options) : fn(options); }; } const humanizeOperatorName = (operatorProp) => `${operatorProp[1].toUpperCase()}${operatorProp.slice(2)}`; const createOperation = (operationFn, fnProp, def) => (options) => { const { [fnProp]: fnId, ...props } = def; let transformFn; if (typeof fnId === 'function') { transformFn = fnId; } else { if (typeof fnId !== 'string' && typeof fnId !== 'symbol') { throw new Error(`${humanizeOperatorName(fnProp)} operator was given no transformer id or an invalid transformer id`); } const fn = options.transformers && options.transformers[fnId]; if (typeof fn !== 'function') { throw new Error(`${humanizeOperatorName(fnProp)} operator was given the unknown transformer id '${String(fnId)}'`); } transformFn = fn(props); } return typeof transformFn === 'function' ? wrapFromDefinition(operationFn(transformFn), def)(options) : passStateThroughNext; }; const createTransformOperation = (def) => createOperation(transform, '$transform', def); const createFilterOperation = (def) => createOperation(filter, '$filter', def); const setNoneValuesOnOptions = (options, nonvalues) => Array.isArray(nonvalues) ? { ...options, nonvalues: nonvalues.map(unescapeValue) } : options; const createAltOperation = (operationFn, def) => (options) => { const { $alt: defs, $undefined: nonvalues } = def; return Array.isArray(defs) ? wrapFromDefinition(operationFn(...defs), def)(setNoneValuesOnOptions(options, nonvalues)) : passStateThroughNext; }; const createIfOperation = (def) => (options) => { const { $if: conditionPipeline, then: thenPipeline, else: elsePipeline, } = def; return wrapFromDefinition(ifelse(conditionPipeline, thenPipeline, elsePipeline), def)(options); }; function createApplyOperation(operationFn, def) { const pipelineId = def.$apply; return wrapFromDefinition(operationFn(pipelineId), def); } function createConcatOperation(operationFn, pipeline) { const pipelines = ensureArray(pipeline); return operationFn(...pipelines); } function createLookupOperation(operationFn, def, arrayPath) { const { path: propPath, ...props } = def; return wrapFromDefinition(operationFn({ ...props, arrayPath, propPath }), def); } function operationFromObject(defRaw, options) { const def = modifyOperationObject(defRaw, options.modifyOperationObject); if (isOperationObject(def)) { if (isOperationType(def, '$transform')) { return createTransformOperation(def); } else if (isOperationType(def, '$filter')) { return createFilterOperation(def); } else if (isOperationType(def, '$if')) { return createIfOperation(def); } else if (isOperationType(def, '$apply')) { return createApplyOperation(apply, def); } else if (isOperationType(def, '$alt')) { return createAltOperation(alt, def); } else if (isOperationType(def, '$concat')) { return createConcatOperation(concat, def.$concat); } else if (isOperationType(def, '$concatRev')) { return createConcatOperation(concatRev, def.$concatRev); } else if (isOperationType(def, '$lookup')) { return createLookupOperation(lookup, def, def.$lookup); } else if (isOperationType(def, '$lookdown')) { return createLookupOperation(lookdown, def, def.$lookdown); } else { return () => () => async (value) => value; } } else { return props(def); } } export const defToOperations = (def, options) => isPipeline(def) ? def.flatMap((def) => defToOperations(def, options)) : isObject(def) ? operationFromObject(def, options) : isPath(def) ? get(def) : isOperation(def) ? def : () => () => async (value) => value; export function defToOperation(def, options) { const operations = isPipeline(def) ? def : defToOperations(def, options); return pipeIfArray(operations); } export function operationToDataMapper(operation, options) { const fn = operation(options)(noopNext); return async (value, state) => getStateValue(await fn(setStateValue(state, value))); } export function defToDataMapper(def, options = {}) { return operationToDataMapper(defToOperation(def, options), options); } //# sourceMappingURL=definitionHelpers.js.map