easy-peasy
Version:
Vegetarian friendly state for React
232 lines (203 loc) • 6.83 kB
JavaScript
import {
actionOnSymbol,
actionSymbol,
computedSymbol,
effectOnSymbol,
persistSymbol,
reducerSymbol,
thunkOnSymbol,
thunkSymbol,
} from './constants';
import { get, isPlainObject, set } from './lib';
import { extractPersistConfig } from './persistence';
import { createActionCreator } from './actions';
import { createThunkHandler, createThunkActionsCreator } from './thunks';
import { bindListenerdefs } from './listeners';
import { createComputedPropertyBinder } from './computed-properties';
import { createEffectHandler, createEffectActionsCreator } from './effects';
export default function extractDataFromModel(
model,
initialState,
injections,
_r,
) {
const _dS = initialState;
const _aCD = {};
const _aC = {};
const _aRD = {};
const _cP = [];
const _cR = [];
const _e = [];
const _lAC = {};
const _lAM = {};
const listenerdefs = [];
let _persistenceConfig = [];
const _cS = {
isInReducer: false,
};
const recursiveExtractFromModel = (current, parentPath) =>
Object.keys(current).forEach((key) => {
const value = current[key];
const path = [...parentPath, key];
const meta = {
parent: parentPath,
path,
key,
};
const handleValueAsState = () => {
const initialParentRef = get(parentPath, initialState);
if (initialParentRef && key in initialParentRef) {
set(path, _dS, initialParentRef[key]);
} else {
set(path, _dS, value);
}
};
if (key === persistSymbol) {
_persistenceConfig.push(extractPersistConfig(parentPath, value));
return;
}
if (value != null && typeof value === 'object') {
if (value[actionSymbol] || value[actionOnSymbol]) {
const def = { ...value };
// Determine the category of the action
const category = def[actionSymbol] ? '@action' : '@actionOn';
// Establish the meta data describing the action
def.meta = {
actionName: meta.key,
category,
type: `${category}.${meta.path.join('.')}`,
parent: meta.parent,
path: meta.path,
};
// Create the "action creator" function
def.actionCreator = createActionCreator(def, _r);
// Create a bidirectional relationship of the def/actionCreator
def.actionCreator.def = def;
// Create a bidirectional relationship of the def/reducer
def.fn.def = def;
// Add the action creator to lookup map
_aCD[def.meta.type] = def.actionCreator;
// Add the reducer to lookup map
_aRD[def.meta.type] = def.fn;
// We don't want to expose the internal action to consumers
if (meta.key !== 'ePRS') {
// Set the action creator in the "actions" object tree for
// either the listeners object tree, or the standard actions/thunks
// object tree
if (def[actionOnSymbol]) {
listenerdefs.push(def);
set(path, _lAC, def.actionCreator);
} else {
set(path, _aC, def.actionCreator);
}
}
} else if (value[thunkSymbol] || value[thunkOnSymbol]) {
const def = { ...value };
// Determine the category of the thunk
const category = def[thunkSymbol] ? '@thunk' : '@thunkOn';
// Establish the meta data describing the thunk
const type = `${category}.${meta.path.join('.')}`;
def.meta = {
actionName: meta.key,
parent: meta.parent,
path: meta.path,
type,
startType: `${type}(start)`,
successType: `${type}(success)`,
failType: `${type}(fail)`,
};
// Create the function that will handle, i.e. be executed, when
// the thunk action is created/dispatched
def.thunkHandler = createThunkHandler(def, _r, injections, _aC);
// Create the "action creator" function
def.actionCreator = createThunkActionsCreator(def, _r);
// Create a bidirectional relationship of the def/actionCreator
def.actionCreator.def = def;
// Register the action creator within the lookup map
_aCD[def.meta.type] = def.actionCreator;
// Set the action creator in the "actions" object tree for
// either the listeners object tree, or the standard actions/thunks
// object tree
if (def[thunkOnSymbol]) {
listenerdefs.push(def);
set(path, _lAC, def.actionCreator);
} else {
set(path, _aC, def.actionCreator);
}
} else if (value[computedSymbol]) {
const parent = get(parentPath, _dS);
const bindComputedProperty = createComputedPropertyBinder(
key,
value,
_r,
);
bindComputedProperty(parent, _dS);
_cP.push({ key, parentPath, bindComputedProperty });
} else if (value[reducerSymbol]) {
_cR.push({
key,
parentPath,
reducer: value.fn,
config: value.config,
});
} else if (value[effectOnSymbol]) {
const def = { ...value };
// Establish the meta data describing the effect
const type = `@effectOn.${meta.path.join('.')}`;
def.meta = {
type,
actionName: meta.key,
parent: meta.parent,
path: meta.path,
startType: `${type}(start)`,
successType: `${type}(success)`,
failType: `${type}(fail)`,
};
const effectHandler = createEffectHandler(def, _r, injections, _aC);
const actionCreator = createEffectActionsCreator(
def,
_r,
effectHandler,
);
def.actionCreator = actionCreator;
_e.push(def);
} else if (isPlainObject(value)) {
const existing = get(path, _dS);
if (existing == null) {
set(path, _dS, {});
}
recursiveExtractFromModel(value, path);
} else {
handleValueAsState();
}
} else {
handleValueAsState();
}
});
_persistenceConfig = _persistenceConfig.sort((a, b) => {
const aPath = a.path.join('.');
const bPath = b.path.join('.');
if (aPath < bPath) {
return -1;
}
if (aPath > bPath) {
return 1;
}
return 0;
});
recursiveExtractFromModel(model, []);
bindListenerdefs(listenerdefs, _aC, _aCD, _lAM);
return {
_aCD,
_aC,
_aRD,
_cP,
_cR,
_cS,
_dS,
_e,
_lAC,
_lAM,
_persistenceConfig,
};
}