UNPKG

feature-redux-logic

Version:
166 lines (145 loc) 6.09 kB
import {createLogicMiddleware} from 'redux-logic'; // peerDependency import {createAspect, launchApp} from 'feature-u'; // peerDependency: import verify from './util/verify'; // our logger (integrated/activated via feature-u) const logf = launchApp.diag.logf.newLogger('- ***feature-redux-logic*** logicAspect: '); // NOTE: See README for complete description export default function createLogicAspect(name='logic') { // validate parameters const check = verify.prefix('createLogicAspect() parameter violation: '); check(name, 'name is required'); check(typeof name === 'string', 'name must be a string'); // NOTE: didn't want to introduce lodash.isstring dependancy (in the mix of everything else going on in the 1.0.0 upgrade) // create/promote our new aspect const logicAspect = createAspect({ name, validateFeatureContent, assembleFeatureContent, getReduxMiddleware, config: { allowNoLogic$: false, // PUBLIC: client override to: true || [{logicModules}] createLogicMiddleware$, // HIDDEN: createLogicMiddleware$(fassets, appLogic): reduxMiddleware }, }); return logicAspect; } /** * Validate self's aspect content on supplied feature. * * NOTE: To better understand the context in which any returned * validation messages are used, **feature-u** will prefix them * with: 'createFeature() parameter violation: ' * * @param {Feature} feature - the feature to validate, which is known * to contain this aspect. * * @return {string} an error message when the supplied feature * contains invalid content for this aspect (null when valid). * * @private */ function validateFeatureContent(feature) { const content = feature[this.name]; return Array.isArray(content) ? null : `${this.name} (when supplied) must be an array of redux-logic modules`; } /** * Interpret the supplied features, defining our redux middleware * in support of reduc-logic. * * @param {Fassets} fassets the Fassets object used in cross-feature-communication. * * @param {Feature[]} activeFeatures - The set of active (enabled) * features that comprise this application. * * @private */ function assembleFeatureContent(fassets, activeFeatures) { // accumulate logic modules across all features const hookSummary = []; let appLogic = activeFeatures.reduce( (accum, feature) => { if (feature[this.name]) { accum = [...accum, ...feature[this.name]]; hookSummary.push(`\n Feature.name:${feature.name} <-- promotes ${this.name} AspectContent`); } else { hookSummary.push(`\n Feature.name:${feature.name}`); } return accum; }, []); // report the accumulation of logic modules if (appLogic.length > 0) { logf(`assembleFeatureContent() gathered logic modules from the following Features: ${hookSummary}`); } // handle special case where NO logic modules were gathered from features else { // by default, this is an error condition (when NOT overridden by client) if (!this.config.allowNoLogic$) { throw new Error('***ERROR*** feature-redux-logic found NO logic modules within your features ' + `... did you forget to register Feature.${this.name} aspects in your features? ` + '(please refer to the feature-redux-logic docs to see how to override this behavior).'); } // when client override is an array, interpret it as logic modules if (Array.isArray(this.config.allowNoLogic$)) { logf.force(`WARNING: NO logic modules were found in your Features (i.e. Feature.${this.name}), ` + 'but client override (logicAspect.config.allowNoLogic$=[{logicModules}];) ' + 'directed a continuation WITH specified logic modules.'); appLogic = this.config.allowNoLogic$; } // otherwise, we simply disable redux-logic and continue on else { logf.force(`WARNING: NO logic modules were found in your Features (i.e. Feature.${this.name}), ` + 'but client override (logicAspect.config.allowNoLogic$=truthy;) ' + 'directed a continuation WITHOUT redux-logic.'); } } // define our redux middleware for redux-logic // ... conditionally when we have logic modules // ... retained in self for promotion to feature-redux plugin if (appLogic.length > 0) { // ... accomplished in internal config micro function (a defensive measure to allow easier overriding by client) this.logicMiddleware = this.config.createLogicMiddleware$(fassets, appLogic); } // if we have no logic ... we have no middleware else { this.logicMiddleware = null; } } /** * An internal config micro function that creates/returns the * redux middleware for redux-logic. * * This logic is broken out in this internal method as a defensive * measure to make it easier for a client to override (if needed for * some unknown reason). * * @param {Fassets} fassets the Fassets object used in cross-feature-communication. * This must be dependancy injected into redux-logic. * * @param {logicModuls[]} appLogic - an array of redux-logic * modules (gaurenteed to have at least one entry). * * @return {reduxMiddleware} the newly created redux middleware for * redux-logic. * * @private */ function createLogicMiddleware$(fassets, appLogic) { // define our redux middleware for redux-logic return createLogicMiddleware(appLogic, { // inject our fassets as a redux-logic dependancy fassets, }); } /** * Expose our redux middleware that activates redux-logic. * * This method is consumed by the feature-redux Aspect using an * "aspect cross-communication". * * @private */ function getReduxMiddleware() { return this.logicMiddleware; }