Skip to content

Instantly share code, notes, and snippets.

@joepie91

joepie91/.js Secret

Created February 14, 2019 22:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joepie91/d7912b405b010c499986ae9233deb284 to your computer and use it in GitHub Desktop.
Save joepie91/d7912b405b010c499986ae9233deb284 to your computer and use it in GitHub Desktop.
"use strict";
const immutable = require("immutable");
const mapObj = require("map-obj");
const assureArray = require("assure-array");
const defaultValue = require("default-value");
const createLogic = require("./create-logic");
function reduceToObject(items, mapper = (item) => item) {
return items.reduce((object, item) => {
return Object.assign(object, mapper(item));
}, {});
}
function reduceToArray(items, mapper = (item) => item) {
return items.reduce((array, item) => {
return array.concat(assureArray(mapper(item)));
}, []);
}
function scopedKey(moduleName, key) {
if (key == null) {
return key;
} else if (key.startsWith("@")) {
if (moduleName != null) {
return key.slice(1);
} else {
throw new Error("Attempted to use a global key specifier (@) in a module without a name; this is not allowed");
}
} else {
if (moduleName != null) {
return `${moduleName}_${key}`;
} else {
return key;
}
}
}
function unscopedKey(moduleName, scopedKey) {
if (scopedKey == null) {
return scopedKey;
} else if (moduleName == null) {
return `@${scopedKey}`;
} else {
if (scopedKey.startsWith(`${moduleName}_`)) {
return scopedKey.slice(moduleName.length + 1);
} else {
return `@${scopedKey}`
}
}
}
function resolveMixins(module) {
if (module.mixins == null) {
return module;
} else {
let moduleClone = Object.assign({}, module);
delete moduleClone.mixins;
return module.mixins.reduce((resolved, mixin) => {
/* NOTE: Mutation occurs here, but we pass in a clone as initial value. */
return Object.assign(resolved, resolveMixins(mixin));
}, moduleClone);
}
}
module.exports = function loadModules(modules, unscoped) {
let resolvedModules = modules.map((module) => resolveMixins(module));
let stateModel = reduceToObject(resolvedModules.concat([unscoped]), (module) => {
if (module.name == null) {
/* Unscoped state */
return module.state;
} else {
// FIXME: Change this to an Immutable data structure?
return {
modules: {
[module.name]: module.state
}
};
}
});
let State = immutable.Record(stateModel);
/* FIXME: Add support for multiple state transformations (eg. the default transformation and a module-provided one) */
let scopedStateTransformations = reduceToObject(resolvedModules, (module) => {
return mapObj(module.stateTransformations, (key, reducer) => {
// MARKER: Modify here for state isolation
return [scopedKey(module.name, key), (state, action) => {
let scopedState = reducer(state.modules[module.name], action);
return state.setIn(["modules", module.name], scopedState);
}];
});
});
let stateTransformations = Object.assign(scopedStateTransformations, unscoped.stateTransformations);
/* FIXME: Flatten array instead */
let scopedLogicHandlers = reduceToArray(resolvedModules, (module) => {
return Object.keys(module.logicHandlers).map((actionName) => {
let options = module.logicHandlers[actionName];
return createLogic({
type: scopedKey(module.name, actionName),
errorType: options.errorType, /* This is not scoped here, because it's handled in the actionMapper on dispatch */
actionMapper: (action) => {
return Object.assign({}, action, {
type: scopedKey(module.name, action.type)
});
},
actionUnmapper: (action) => {
return Object.assign({}, action, {
type: unscopedKey(module.name, action.type)
});
},
dataMapper: (data) => {
return Object.assign({}, data, {
getGlobalState: data.getState,
getState: () => {
return data.getState().modules[module.name];
}
});
},
validate: options.validate,
transform: options.transform,
process: options.process,
success: options.success,
failure: options.failure
});
});
});
let unscopedLogicHandlers = Object.keys(defaultValue(unscoped.logicHandlers, {})).map((actionName) => {
let options = unscoped.logicHandlers[actionName];
return createLogic(Object.assign({}, options, {
type: actionName
}));
});
let logicHandlers = scopedLogicHandlers.concat(unscopedLogicHandlers);
return {
State, stateTransformations, logicHandlers
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment