UNPKG

chai-latte

Version:

Build expressive & readable fluent interface libraries.

130 lines 5.54 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.combine = void 0; const getPrototypeChain_1 = require("./lib/getPrototypeChain"); const ConfigurableCallback_1 = require("./lib/ConfigurableCallback"); const register_1 = require("./register"); const combine = (...expressions) => { const sharedArgs = []; const combinedFluentAPI = {}; // share argument between all functions in the subtree. Array.from(ConfigurableCallback_1.ConfigurableCallback.configByCallback).forEach(([_, configurableCallback]) => { configurableCallback.args = sharedArgs; }); expressions.forEach((expression, i) => { expression.forEach((registeredAPI) => { registeredAPI.expression.setExpressionIdx(i); }); }); expressions.forEach((expression, i) => { expression.forEach((registeredAPI) => { mergeSubtree(registeredAPI.api, combinedFluentAPI); }); }); return combinedFluentAPI; }; exports.combine = combine; const mergeSubtree = (source, target) => { const isSourceObj = typeof source === 'object'; const isTargetObj = typeof target === 'object'; if (isSourceObj && isTargetObj) { // console.log('merging {} with {}', source, target); return mergeObjectWithObject(source, target); } const isSourceFn = typeof source === 'function'; const isTargetFn = typeof target === 'function'; if (isSourceFn && isTargetFn) { // console.log('merging callbacks', source, target) return mergeCallbackWithCallback(source, target); } if (isSourceObj && isTargetFn) { // console.log('merging {} with callback', source, target); return mergeObjectWithFunctionProps(source, target); } if (isSourceFn && isTargetObj) { // console.log('merging callback with {}', source, target); return mergeFunctionWithObject(source, target); } throw new Error(`Case handled src:${typeof source} target:${typeof target}`); }; const parentTargetByTarget = new Map(); const mergeObjectWithObject = (source, target) => { const [[sourceProp, sourceSubtree]] = Object.entries(source); const doesKeyExist = !!target[sourceProp]; parentTargetByTarget.set(target[sourceProp], { parent: target, prop: sourceProp }); if (!doesKeyExist) { target[sourceProp] = sourceSubtree; parentTargetByTarget.set(sourceSubtree, { parent: target, prop: sourceProp }); return; } mergeSubtree(sourceSubtree, target[sourceProp]); return target; }; const mergeCallbackWithCallback = (source, target) => { const targetCallbackBuilder = ConfigurableCallback_1.ConfigurableCallback.getBuilderOf(target); const srcCallbackBuilder = ConfigurableCallback_1.ConfigurableCallback.getBuilderOf(source); if (!srcCallbackBuilder || !targetCallbackBuilder) { return; } srcCallbackBuilder.returnByArg.forEach((returned, arg) => { if (!targetCallbackBuilder.returnByArg.get(arg)) { targetCallbackBuilder.setArgOrigin(arg, srcCallbackBuilder); targetCallbackBuilder.returnByArg.set(arg, returned); } else { mergeSubtree(returned, targetCallbackBuilder.returnByArg.get(arg)); } }); // port parent class api to all its extends subclasses; targetCallbackBuilder.returnByArg.forEach((retuned, arg) => { const argProtoChain = (0, getPrototypeChain_1.getPrototypeChain)(arg); argProtoChain.forEach((proto) => { if (targetCallbackBuilder.returnByArg.has(proto)) { mergeSubtree(targetCallbackBuilder.returnByArg.get(proto), retuned); } }); }); // console.log('srcCallbackBuilder.returnByArg', srcCallbackBuilder.returnByArg) // console.log('targetCallbackBuilder.returnByArg', targetCallbackBuilder.returnByArg) return target; }; const mergeObjectWithFunctionProps = (source, target) => { ensureExpressionsAreNotOveridden(source, target); const targetConfig = ConfigurableCallback_1.ConfigurableCallback.getBuilderOf(target); Object.entries(source).forEach(([srcKey, srcVal]) => { parentTargetByTarget.set(targetConfig.props[srcKey], { parent: targetConfig.props, prop: srcKey }); if (targetConfig.props[srcKey]) { return mergeSubtree(srcVal, targetConfig.props[srcKey]); } targetConfig.props[srcKey] = srcVal; parentTargetByTarget.set(srcVal, { parent: targetConfig.props, prop: srcKey }); }); return target; }; const mergeFunctionWithObject = (source, target) => { ensureExpressionsAreNotOveridden(source, target); const { parent, prop } = parentTargetByTarget.get(target); parent[prop] = source; parentTargetByTarget.set(parent[prop], { parent: parent, prop: prop, }); return mergeObjectWithFunctionProps(target, source); }; const ensureExpressionsAreNotOveridden = (source, target) => { const isSrcFinal = register_1.Expression.isFinalCallback(source); const isTargetFinal = register_1.Expression.isFinalCallback(target); if (isSrcFinal || isTargetFinal) { // should not override a preexisting API. // ex: defining two expressions like the.guy('xyz') and the.guy('xyz').are.incompatible(); // will cause a conflict, the latter overrides the former. throw new Error('Incompatible Fluent API'); } }; //# sourceMappingURL=combine.js.map