tests are passing
This commit is contained in:
parent
aa130e33e5
commit
6168720c47
30
src/buildActions/index.ts
Normal file
30
src/buildActions/index.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import fp from 'lodash/fp';
|
||||
|
||||
function actionFor(type) {
|
||||
return (payload = null, meta = null) => {
|
||||
return fp.pickBy(v => v !== null)({type, payload, meta});
|
||||
};
|
||||
}
|
||||
|
||||
export default function buildActions(
|
||||
mutations = {},
|
||||
effects = {},
|
||||
subActions = {},
|
||||
) {
|
||||
|
||||
let actions = { ...subActions };
|
||||
|
||||
Object.keys(mutations).forEach(type => {
|
||||
if (!actions[type]) {
|
||||
actions[type] = actionFor(type);
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(effects).forEach(type => {
|
||||
if (!actions[type]) {
|
||||
actions[type] = actionFor(type);
|
||||
}
|
||||
});
|
||||
|
||||
return actions;
|
||||
}
|
17
src/buildCreateStore.ts
Normal file
17
src/buildCreateStore.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { createStore as reduxCreateStore, applyMiddleware } from 'redux';
|
||||
|
||||
export default function buildCreateStore( reducer, initial, middleware,
|
||||
actions ) {
|
||||
return () => {
|
||||
const store = reduxCreateStore( reducer, initial,
|
||||
applyMiddleware( middleware)
|
||||
);
|
||||
for ( let a in actions ) {
|
||||
store.dispatch[a] = (...args) => {
|
||||
store.dispatch(actions[a](...args))
|
||||
};
|
||||
}
|
||||
|
||||
return store as any;
|
||||
}
|
||||
};
|
11
src/buildInitial/index.ts
Normal file
11
src/buildInitial/index.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import fp from 'lodash/fp';
|
||||
|
||||
export default function buildInitial(initial : any = {}, subduxes = {}) {
|
||||
let state = initial;
|
||||
|
||||
if (fp.isPlainObject(initial)) {
|
||||
initial = fp.mergeAll([ subduxes, initial, ]);
|
||||
}
|
||||
|
||||
return initial;
|
||||
}
|
55
src/buildMutations/index.ts
Normal file
55
src/buildMutations/index.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import fp from 'lodash/fp';
|
||||
import u from 'updeep';
|
||||
|
||||
import { Mutation, Mutations } from '../types';
|
||||
|
||||
const composeMutations = (mutations:Mutation[]) =>
|
||||
mutations.reduce( (m1,m2) =>
|
||||
(payload=null,action={}) => state => m2(payload,action)(
|
||||
m1(payload,action)(state) ));
|
||||
|
||||
export default function buildMutations(mutations = {}, subduxes= {}) :Mutations{
|
||||
// we have to differentiate the subduxes with '*' than those
|
||||
// without, as the root '*' is not the same as any sub-'*'
|
||||
|
||||
const actions = fp.uniq( Object.keys(mutations).concat(
|
||||
...Object.values( subduxes ).map( ({mutations = {}}:any) => Object.keys(mutations) )
|
||||
) );
|
||||
|
||||
let mergedMutations = {};
|
||||
|
||||
let [ globby, nonGlobby ] = fp.partition(
|
||||
([_,{mutations={}}]:any) => mutations['*'],
|
||||
Object.entries(subduxes)
|
||||
);
|
||||
|
||||
globby =
|
||||
fp.flow([
|
||||
fp.fromPairs,
|
||||
fp.mapValues(
|
||||
({reducer}) => (_,action={}) => state =>
|
||||
reducer(state,action) ),
|
||||
])(globby);
|
||||
|
||||
const globbyMutation = (payload,action) => u(
|
||||
fp.mapValues( (mut:any) => mut(payload,action) )(globby)
|
||||
);
|
||||
|
||||
actions.forEach( action => {
|
||||
mergedMutations[action] = [ globbyMutation ]
|
||||
});
|
||||
|
||||
nonGlobby.forEach( ([slice, {mutations={},reducer={}}]:any) => {
|
||||
Object.entries(mutations).forEach(([type,mutation]) => {
|
||||
const localized = (payload=null,action={}) => u.updateIn( slice )( (mutation as any)(payload,action) );
|
||||
|
||||
mergedMutations[type].push(localized);
|
||||
})
|
||||
});
|
||||
|
||||
Object.entries(mutations).forEach(([type,mutation]) => {
|
||||
mergedMutations[type].push(mutation);
|
||||
});
|
||||
|
||||
return fp.mapValues( composeMutations )(mergedMutations) as Mutations;
|
||||
}
|
18
src/buildUpreducer/index.ts
Normal file
18
src/buildUpreducer/index.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import fp from 'lodash/fp';
|
||||
import {Mutations} from '../types';
|
||||
|
||||
type Upreducer = <S>(action:any) => (state:S) => S;
|
||||
|
||||
export default function buildUpreducer<S>(initial: S, mutations: Mutations): Upreducer {
|
||||
return (action = {}) => (state:any) => {
|
||||
if (state === null) state = initial;
|
||||
|
||||
const a =
|
||||
mutations[(action as any).type] ||
|
||||
mutations['*'];
|
||||
|
||||
if(!a) return state;
|
||||
|
||||
return a((action as any).payload, action)(state) as S;
|
||||
};
|
||||
}
|
151
src/index.ts
151
src/index.ts
@ -1,149 +1,34 @@
|
||||
import fp from 'lodash/fp';
|
||||
import u from 'updeep';
|
||||
|
||||
import { createStore as reduxCreateStore, applyMiddleware } from 'redux';
|
||||
import Updux from './updux';
|
||||
|
||||
import buildMiddleware from './buildMiddleware';
|
||||
|
||||
function actionFor(type) {
|
||||
return (payload = null, meta = null) => {
|
||||
return fp.pickBy(v => v !== null)({type, payload, meta});
|
||||
};
|
||||
}
|
||||
|
||||
function buildInitial({initial = {}, subduxes = {}}) {
|
||||
let state = initial;
|
||||
|
||||
if (fp.isPlainObject(initial)) {
|
||||
initial = fp.mergeAll([
|
||||
fp.mapValues(fp.getOr({}, 'initial'), subduxes),
|
||||
initial,
|
||||
]);
|
||||
}
|
||||
|
||||
return initial;
|
||||
}
|
||||
|
||||
function buildActions({mutations = {}, effects = {}, subduxes = {}}) {
|
||||
let actions = fp.mergeAll(fp.map(fp.getOr({}, 'actions'), subduxes)) || {};
|
||||
|
||||
Object.keys(mutations).forEach(type => {
|
||||
if (!actions[type]) {
|
||||
actions[type] = actionFor(type);
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(effects).forEach(type => {
|
||||
if (!actions[type]) {
|
||||
actions[type] = actionFor(type);
|
||||
}
|
||||
});
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
const composeMutations = mutations =>
|
||||
mutations.reduce( (m1,m2) =>
|
||||
(payload=null,action={}) => state => m2(payload,action)(
|
||||
m1(payload,action)(state) ));
|
||||
|
||||
function buildMutations({mutations = {}, subduxes= {}}: any) {
|
||||
// we have to differentiate the subduxes with '*' than those
|
||||
// without, as the root '*' is not the same as any sub-'*'
|
||||
|
||||
const actions = fp.uniq( Object.keys(mutations).concat(
|
||||
...Object.values( subduxes ).map( ({mutations = {}}:any) => Object.keys(mutations) )
|
||||
) );
|
||||
|
||||
let mergedMutations = {};
|
||||
|
||||
let [ globby, nonGlobby ] = fp.partition(
|
||||
([_,{mutations={}}]:any) => mutations['*'],
|
||||
Object.entries(subduxes)
|
||||
);
|
||||
|
||||
globby =
|
||||
fp.flow([
|
||||
fp.fromPairs,
|
||||
fp.mapValues(
|
||||
({reducer}) => (_,action={}) => state =>
|
||||
reducer(state,action) ),
|
||||
])(globby);
|
||||
|
||||
const globbyMutation = (payload,action) => u(
|
||||
fp.mapValues( (mut:any) => mut(payload,action) )(globby)
|
||||
);
|
||||
|
||||
actions.forEach( action => {
|
||||
mergedMutations[action] = [ globbyMutation ]
|
||||
});
|
||||
|
||||
nonGlobby.forEach( ([slice, {mutations={},reducer={}}]:any) => {
|
||||
Object.entries(mutations).forEach(([type,mutation]) => {
|
||||
const localized = (payload=null,action={}) => u.updateIn( slice )( (mutation as any)(payload,action) );
|
||||
|
||||
mergedMutations[type].push(localized);
|
||||
})
|
||||
});
|
||||
|
||||
Object.entries(mutations).forEach(([type,mutation]) => {
|
||||
mergedMutations[type].push(mutation);
|
||||
});
|
||||
|
||||
return fp.mapValues( composeMutations )(mergedMutations);
|
||||
|
||||
}
|
||||
|
||||
function updux(config) {
|
||||
const actions = buildActions(config);
|
||||
return new Updux(config);
|
||||
// const actions = buildActions(
|
||||
// config.mutations,
|
||||
// config.effects,
|
||||
// fp.flatten( ( config.subduxes||{}).map( ({ actions }) => actions ) )
|
||||
// );
|
||||
|
||||
const initial = buildInitial(config);
|
||||
// const initial = buildInitial(config);
|
||||
|
||||
const mutations = buildMutations(config);
|
||||
// const mutations = buildMutations(config.mutations,config.subduxes);
|
||||
|
||||
const upreducer = (action={}) => state => {
|
||||
if (state === null) state = initial;
|
||||
|
||||
const a =
|
||||
mutations[(action as any).type] ||
|
||||
mutations['*'] ||
|
||||
(() => state => state);
|
||||
|
||||
return a((action as any).payload, action)(state);
|
||||
};
|
||||
|
||||
const reducer = (state, action) => {
|
||||
return upreducer(action)(state);
|
||||
};
|
||||
|
||||
const middleware = buildMiddleware(
|
||||
config.effects,
|
||||
actions,
|
||||
config.subduxes,
|
||||
);
|
||||
|
||||
const createStore = () => {
|
||||
const store = reduxCreateStore( reducer, initial,
|
||||
applyMiddleware( middleware)
|
||||
);
|
||||
for ( let a in actions ) {
|
||||
store.dispatch[a] = (...args) => {
|
||||
store.dispatch(actions[a](...args))
|
||||
};
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
return {
|
||||
reducer,
|
||||
upreducer,
|
||||
middleware,
|
||||
createStore,
|
||||
actions,
|
||||
mutations,
|
||||
initial,
|
||||
};
|
||||
// return {
|
||||
// reducer,
|
||||
// upreducer,
|
||||
// middleware,
|
||||
// createStore,
|
||||
// actions: ( actions as any ),
|
||||
// mutations,
|
||||
// initial,
|
||||
// };
|
||||
}
|
||||
|
||||
export default updux;
|
||||
|
7
src/types.ts
Normal file
7
src/types.ts
Normal file
@ -0,0 +1,7 @@
|
||||
|
||||
export type Dictionary<T> = { [key: string]: T };
|
||||
|
||||
export type Mutation = (payload: any, action: any) => (state:any) => any;
|
||||
|
||||
export type Mutations = Dictionary<Mutation>;
|
||||
|
78
src/updux.ts
Normal file
78
src/updux.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import fp from 'lodash/fp';
|
||||
import buildActions from './buildActions';
|
||||
import buildInitial from './buildInitial';
|
||||
import buildMutations from './buildMutations';
|
||||
|
||||
import { Dictionary, Mutation } from './types';
|
||||
import buildCreateStore from './buildCreateStore';
|
||||
import buildMiddleware from './buildMiddleware';
|
||||
import buildUpreducer from './buildUpreducer';
|
||||
|
||||
type UpduxConfig = {
|
||||
initial?: any,
|
||||
mutations?: any,
|
||||
effects?: any,
|
||||
subduxes?: {
|
||||
[ slice: string ]: UpduxConfig | Updux
|
||||
}
|
||||
};
|
||||
|
||||
export class Updux {
|
||||
actions: any;
|
||||
|
||||
subduxes: Dictionary<Updux>;
|
||||
|
||||
initial: any;
|
||||
|
||||
mutations: Dictionary<Mutation>;
|
||||
|
||||
createStore: Function;
|
||||
|
||||
upreducer: (action:any)=>(state:any)=>any;
|
||||
|
||||
reducer: <S>(state:S,action:any) => S;
|
||||
|
||||
middleware: (api:any) => (next: Function) => (action: any) => any;
|
||||
|
||||
constructor(config: UpduxConfig) {
|
||||
|
||||
this.subduxes = fp.mapValues(
|
||||
value => fp.isPlainObject(value) ? new Updux(value as UpduxConfig) : value )(fp.getOr({},'subduxes',config)
|
||||
) as Dictionary<Updux>;
|
||||
|
||||
|
||||
this.actions = buildActions(
|
||||
config.mutations,
|
||||
config.effects,
|
||||
fp.mergeAll( Object.values( this.subduxes ).map( ({ actions }) =>
|
||||
actions ) )
|
||||
)
|
||||
|
||||
this.initial = buildInitial(
|
||||
config.initial, fp.mapValues( ({initial}) => initial )(this.subduxes)
|
||||
);
|
||||
|
||||
this.mutations = buildMutations(
|
||||
config.mutations, this.subduxes
|
||||
);
|
||||
|
||||
this.upreducer = buildUpreducer(
|
||||
this.initial, this.mutations
|
||||
);
|
||||
|
||||
this.reducer = (state,action) => {
|
||||
return this.upreducer(action)(state);
|
||||
}
|
||||
|
||||
this.middleware = buildMiddleware(
|
||||
config.effects,
|
||||
this.actions,
|
||||
config.subduxes,
|
||||
);
|
||||
|
||||
this.createStore = buildCreateStore(this.reducer,this.initial,this.middleware,this.actions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default Updux;
|
@ -4,7 +4,7 @@
|
||||
"incremental": true, /* Enable incremental compilation */
|
||||
"target": "ES2019", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
"lib": [ "dom", "es2016" ], /* Specify library files to be included in the compilation. */
|
||||
"lib": [ "dom", "es2017" ], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
@ -12,7 +12,7 @@
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
// "outDir": "./", /* Redirect output structure to the directory. */
|
||||
"outDir": "./dist", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
|
Loading…
Reference in New Issue
Block a user