Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .babelrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ module.exports = {
[
'@babel/env',
{
modules: BABEL_ENV === 'commonjs' ? 'cjs' : false,
modules: (BABEL_ENV === 'commonjs' || BABEL_ENV === 'wepy') ? 'cjs' : false,
loose: true,
targets: NODE_ENV === 'test' ? { node: 'current' } : {}
}
]
],
plugins: [
'@babel/plugin-transform-runtime',
BABEL_ENV !== 'wepy' && '@babel/plugin-transform-runtime',
// don't use `loose` mode here - need to copy symbols when spreading
'@babel/plugin-proposal-object-rest-spread',
NODE_ENV === 'test' && '@babel/transform-modules-commonjs'
Expand Down
36 changes: 28 additions & 8 deletions __tests__/model.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,35 +26,55 @@ describe('model', () => {
test('should dynamic load model', () => {
const app = balloon();
app.model({
namespace: 'a',
state: { count: 0 },
namespace: 'views.a',
state: { count: 1 },
reducers: {
'COUNT_A_ADD': (state, { payload }) => {
return Object.assign({}, state, { count: state.count + payload });
}
},
actions: {
addCountForA: ['COUNT_A_ADD']
},
selectors: () => {
console.log('a selectors');
const getA = state => state.views.a;

return { getA };
}
});
app.run();

console.log('app.state1:', app.getState());
console.log('app.selectors.getA:', app.selectors.getA(app.getState()));

app.model({
namespace: 'b',
state: { count: 0 },
namespace: 'views.b',
state: { count: 2 },
reducers: {
'COUNT_B_ADD': (state, { payload }) => {
return Object.assign({}, state, { count: state.count + payload });
}
},
actions: {
addCountForB: ['COUNT_B_ADD']
},
selectors: () => {
console.log('b selectors');
const getB = state => state.views.b;

return { getB };
}
});
const { store, actions } = app;

expect(store.getState()).toEqual({ a: { count: 0 }, b: { count: 0 } });
store.dispatch(actions.addCountForB(4));
expect(store.getState()).toEqual({ a: { count: 0 }, b: { count: 4 } });
console.log('app.state2:', app.getState());
console.log('app.selectors.getB:', app.selectors.getB(app.getState()));

// const { store, actions } = app;
//
// expect(store.getState()).toEqual({ a: { count: 0 }, b: { count: 0 } });
// store.dispatch(actions.addCountForB(4));
// expect(store.getState()).toEqual({ a: { count: 0 }, b: { count: 4 } });
});

test('should unload model', () => {
Expand Down
29 changes: 12 additions & 17 deletions __tests__/sagaModules.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { runSaga } from 'redux-saga';
import { runSaga, stdChannel } from 'redux-saga';
import EventEmitter from 'events';
import {
addSagaModule,
delSagaModule,
Expand Down Expand Up @@ -41,21 +42,15 @@ describe('sagaModules', () => {

describe('run sagas', () => {
function createEmitter() {
const listeners = [];
const emitter = new EventEmitter();
const channel = stdChannel();
emitter.on('action', channel.put);

return {
subscribe(callback) {
listeners.push(callback);
return () => {
const index = listeners.indexOf(callback);
if (index >= 0) {
listeners.splice(index, 1);
}
};
},
channel,

emit(action) {
listeners.forEach(callback => callback(action));
emitter.emit('action', action);
}
};
}
Expand Down Expand Up @@ -83,10 +78,10 @@ describe('sagaModules', () => {

test('should run sagas: sagas is plain object', (done) => {
const dispatched = [];
const { subscribe, emit } = createEmitter();
const { channel, emit } = createEmitter();
const runSagaMock = (saga) => {
return runSaga({
subscribe,
channel,
dispatch: (action) => {
dispatched.push(action);
}
Expand Down Expand Up @@ -121,16 +116,16 @@ describe('sagaModules', () => {
]));
done();
},
20
100
);
});

test('should run sagas: sagas is function, and return plain object', (done) => {
const dispatched = [];
const { subscribe, emit } = createEmitter();
const { channel, emit } = createEmitter();
const runSagaMock = (saga) => {
return runSaga({
subscribe,
channel,
dispatch: (action) => {
dispatched.push(action);
}
Expand Down
2 changes: 1 addition & 1 deletion examples/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"react-dom": "^16.6.3",
"react-redux": "^6.0.0",
"react-router-dom": "^4.3.1",
"redux-balloon": "^0.13.0",
"redux-balloon": "^1.0.0",
"seamless-immutable": "^7.1.4",
"serve-favicon": "^2.5.0",
"throttle-debounce": "^2.0.1",
Expand Down
30 changes: 26 additions & 4 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,40 @@ const replace = require('gulp-replace');

gulp.task('buildWePY', function (callback) {
runSequence(
'buildBabel',
'copySrc',
'replaceSagaReference',
'buildBabel',
callback
);
});

gulp.task('buildBabel', shell.task([
'cross-env BABEL_ENV=commonjs babel src --out-dir wepy'
gulp.task('copySrc', shell.task([
'cp -r src wepy'
]));

gulp.task('replaceSagaReference', function () {
gulp.src('./wepy/sagaImports.js')
.pipe(replace('redux-saga', 'redux-saga/dist/redux-saga'))
.pipe(
replace(
'import * as ReduxSaga from \'redux-saga\';',
'import * as ReduxSaga from \'redux-saga/dist/redux-saga.umd\';'
)
)
.pipe(
replace(
'import * as effects from \'redux-saga/effects\';',
''
)
)
.pipe(
replace(
'const SagaEffects = effects;',
'const SagaEffects = ReduxSaga.effects;'
)
)
.pipe(gulp.dest('./wepy'));
});

gulp.task('buildBabel', shell.task([
'cross-env BABEL_ENV=wepy babel wepy --out-dir wepy'
]));
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "redux-balloon",
"version": "0.13.0",
"version": "1.0.0",
"description": "Lightweight business framework for Redux apps",
"license": "MIT",
"repository": {
Expand Down Expand Up @@ -54,7 +54,7 @@
"invariant": "^2.2.4",
"redux": "^4.0.1",
"redux-actions": "^2.6.4",
"redux-saga": "^0.16.2",
"redux-saga": "^1.1.3",
"reselect": "^4.0.0",
"seamless-immutable": "^7.1.4"
},
Expand Down
5 changes: 4 additions & 1 deletion src/sagaImports.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import * as ReduxSaga from 'redux-saga';
import * as effects from 'redux-saga/effects';

const SagaEffects = effects;
const createSagaMiddleware = ReduxSaga.default;
const reduxSagaExport = { ...ReduxSaga, effects: SagaEffects };

export {
createSagaMiddleware,
ReduxSaga
reduxSagaExport as ReduxSaga
};
30 changes: 19 additions & 11 deletions src/sagaModules.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function runSagaModules(modules, runSaga, opts, extras) {
forEachObjIndexed((mod, namespace) => {
const sagas = mod[0];
const saga = createSaga(sagas, namespace, opts, _extras);
runSaga(saga).done.catch(err => {
runSaga(saga).toPromise().catch(err => {
if (!(err instanceof SagaError)) {
err = new SagaError(err, { namespace });
}
Expand Down Expand Up @@ -69,21 +69,30 @@ function createWatcher(sagas, namespace, opts, extras) {
}

return function* () {
const { takeEvery, takeLatest, throttle } = sagaEffects;
const typeWhiteList = [
'takeEvery',
'takeLatest',
'takeLeading',
'throttle',
'debounce'
];
const keys = Object.keys(sagasObj);

for (const key of keys) {
// takeEvery is default
let type = 'takeEvery';
let ms;
let saga = sagasObj[key];
let opts;

if (isArray(saga)) {
saga = sagasObj[key][0];
const opts = sagasObj[key][1];
opts = sagasObj[key][1];
type = opts.type;

if (type === 'throttle') {
ms = opts.ms;
if (!typeWhiteList.includes(type)) {
throw new Error(
`only support these types: [${typeWhiteList}], but got: ${type}. namespace: ${namespace}, key: ${key}`
);
}
}
const handler = handleActionForHelper(
Expand All @@ -94,14 +103,13 @@ function createWatcher(sagas, namespace, opts, extras) {
);

switch (type) {
case 'takeLatest':
yield takeLatest(key, handler);
break;
case 'throttle':
yield throttle(ms, key, handler);
case 'debounce':
yield sagaEffects[type](opts.ms, key, handler);
break;
default:
yield takeEvery(key, handler);
// takeEvery, takeLatest, takeLeading
yield sagaEffects[type](key, handler);
}
}
};
Expand Down