ducks
Version:
🦆🦆🦆 Ducks is a Reducer Bundles Manager that Implementing the Redux Ducks Modular Proposal with Great Convenience.
232 lines • 8.58 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Ducks = void 0;
/**
* Ducks - https://github.com/huan/ducks
*
* @copyright 2020 Huan LI (李卓桓) <https://github.com/huan>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
const redux_1 = require("redux");
const redux_observable_1 = require("redux-observable");
const config_js_1 = require("../config.js");
const bundle_js_1 = require("../bundle.js");
const combine_duckery_js_1 = require("./combine-duckery.js");
const insert_reducers_js_1 = require("./insert-reducers.js");
const noop_reducer_js_1 = require("./noop-reducer.js");
class Ducks {
/**
* Construct a `Ducks` manager for managing the Duck(s)
*
* @param duckery is a `DucksMapObject` which:
* 1. key is the reducer key (namespace)
* 2. value is a `Duck` instance
*/
constructor(duckery) {
this.duckery = duckery;
if (Object.keys(duckery).length <= 0) {
throw new Error('You need to provide at least one duck for the duckery');
}
this.asyncMiddlewares = {};
const ducksNest = {};
for (const [ns, duck] of Object.entries(duckery)) {
ducksNest[ns] = new bundle_js_1.Bundle(duck);
/**
* Huan(202006): Binding the Ducks to Duck,
* so that the Duck can use other Duck modules (if needed)
*/
if (duck.setDucks) {
duck.setDucks(this);
}
}
this.ducksNest = ducksNest;
}
get store() {
return this._store;
}
get reducer() {
return (0, combine_duckery_js_1.combineDuckery)(this.duckery);
}
get middlewares() {
const middlewareList = Object.values(this.duckery)
.map(duck => duck.middlewares)
.filter(Boolean)
.map(middlewares => Object.values(middlewares))
.flat();
return middlewareList;
}
ducksify(nsOrDuck) {
if (!nsOrDuck) {
return this.ducksNest;
}
if (typeof nsOrDuck === 'string') {
if (nsOrDuck in this.duckery) {
return this.ducksNest[nsOrDuck];
}
throw new Error('Ducks can not found the Duck for the namespace: ' + nsOrDuck);
}
if (typeof nsOrDuck === 'object') {
const namespaceList = Object.keys(this.duckery)
.filter(ns => this.duckery[ns] === nsOrDuck);
if (namespaceList.length <= 0) {
throw new Error('Duck not found: ' + nsOrDuck);
}
const namespace = namespaceList[0];
return this.ducksify(namespace);
}
throw new Error('unknown param: ' + nsOrDuck);
}
enhancer() {
if (!this.asyncMiddlewares.epicMiddleware && this.getRootEpic()) {
this.asyncMiddlewares.epicMiddleware = (0, redux_observable_1.createEpicMiddleware)();
}
if (!this.asyncMiddlewares.sagaMiddleware && this.getRootSaga()) {
/**
* Huan(202109): disable saga
* See: https://github.com/huan/ducks/issues/4
*/
// this.asyncMiddlewares.sagaMiddleware = require('redux-saga').default()
throw new Error('saga is disabled. See: https://github.com/huan/ducks/issues/4');
}
const asyncMiddlewareList = Object.values(this.asyncMiddlewares).filter(Boolean);
return (0, redux_1.compose)(
/**
* Huan(202005):
* the `this.storeEnhancer()` should be put before applyMiddleware
* (to initiate asyncMiddlewares before storeEnhancer)
*/
this.duckeryEnhancer(), (0, redux_1.applyMiddleware)(...asyncMiddlewareList, ...this.middlewares));
}
/**
* 1. Add Ducks Reducers to Store
* 2. Bind Store to Ducks
*/
duckeryEnhancer() {
const enhancer = next => (reducer, preloadedState) => {
const newReducer = (0, insert_reducers_js_1.insertReducers)(reducer, {
[config_js_1.DUCKS_NAMESPACE]: this.reducer, // Huan(202005) FIXME: any ?
});
// FIXME: any
const store = next(newReducer, preloadedState);
this.initializeDucks(store);
return store;
};
return enhancer;
}
/**
* A convenience way to initialize your store with Ducks with default settings.
*
* `ducks.configureStore()` only supports creating a store with Ducks.
*
* If you want to create a store that with other `reducer` and `enhances` (or `middlewares`),
* please use `createStore()` from `redux` module instead of this one.
*
* @param preloadedState
*/
configureStore(preloadedState) {
if (this._store) {
throw new Error('Ducks can be only configureStore() for once! If you need another store, you can create another Ducks to fullfil your needs.');
}
const store = (0, redux_1.createStore)(noop_reducer_js_1.noopReducer, preloadedState, this.enhancer());
return store;
}
/**
* Initialize ducks
*/
getRootEpic() {
const epics = Object.values(this.duckery)
.map(duck => duck.epics)
.filter(Boolean)
.map(epics => Object.values(epics))
.flat();
if (epics.length <= 0) {
return undefined;
}
/**
* Load Epics Combinator
*
* We are using `require` at here because we will only load `redux-observable` module when we need it
*/
return (0, redux_observable_1.combineEpics)(...epics);
}
getRootSaga() {
const sagas = Object.values(this.duckery)
.map(duck => duck.sagas)
.filter(Boolean)
.map(sagas => Object.values(sagas))
.flat();
if (sagas.length <= 0) {
return undefined;
}
/**
* Load Saga Effects
*
* We are using `require` at here because we will only load `redux-saga` module when we need it
*/
/**
* Huan(202109): disable saga
* See: https://github.com/huan/ducks/issues/4
*/
// const effects = require('redux-saga/effects')
// return function * rootSaga () {
// yield effects.all(
// sagas.map(
// saga => saga()
// )
// )
// } as Saga
throw new Error('Saga is disabled. See: https://github.com/huan/ducks/issues/4');
}
initializeDucks(store) {
if (this._store) {
throw new Error('store has already initialized');
}
this._store = store;
/**
* Configure Duck
*/
Object.keys(this.duckery).forEach(namespace => {
// console.info('initializeDucks() namespace', namespace)
// console.info('initializeDucks() state', this.store.getState())
this.ducksNest[namespace].setStore(store);
this.ducksNest[namespace].setNamespaces(config_js_1.DUCKS_NAMESPACE, namespace);
});
/**
* Run Epic
*/
const rootEpic = this.getRootEpic();
if (rootEpic) {
const epicMiddleware = this.asyncMiddlewares.epicMiddleware;
if (!epicMiddleware) {
throw new Error('epicMiddleware is required but not found in the this.asyncMiddlewares.');
}
epicMiddleware.run(rootEpic);
}
/**
* Run Saga
*/
const rootSaga = this.getRootSaga();
if (rootSaga) {
const sagaMiddleware = this.asyncMiddlewares.sagaMiddleware;
if (!sagaMiddleware) {
throw new Error('sagaMiddleware is required but not found in the this.asyncMiddlewares.');
}
sagaMiddleware.run(rootSaga);
}
}
}
exports.Ducks = Ducks;
Ducks.VERSION = config_js_1.VERSION;
//# sourceMappingURL=ducks.js.map