-
-
Save joepie91/d7912b405b010c499986ae9233deb284 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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