From c83443c95f52e79f18d0846309841bd486471645 Mon Sep 17 00:00:00 2001 From: Yanick Champoux Date: Tue, 22 Oct 2019 18:15:08 -0400 Subject: [PATCH] add dist directory --- dist/actions.test.js | 60 +++++++++ dist/buildActions/index.js | 32 +++++ dist/buildCreateStore.js | 24 ++++ dist/buildInitial/index.js | 14 ++ dist/buildInitial/index.test-d.js | 1 + dist/buildInitial/test-d.js | 1 + dist/buildMiddleware.js | 27 ++++ dist/buildMutations/index.js | 52 ++++++++ dist/buildUpreducer/index.js | 19 +++ dist/index.js | 18 +++ dist/middleware.test.js | 92 ++++++++++++++ dist/splat.test.js | 79 ++++++++++++ dist/test.js | 205 ++++++++++++++++++++++++++++++ dist/updux.js | 48 +++++++ 14 files changed, 672 insertions(+) create mode 100644 dist/actions.test.js create mode 100644 dist/buildActions/index.js create mode 100644 dist/buildCreateStore.js create mode 100644 dist/buildInitial/index.js create mode 100644 dist/buildInitial/index.test-d.js create mode 100644 dist/buildInitial/test-d.js create mode 100644 dist/buildMiddleware.js create mode 100644 dist/buildMutations/index.js create mode 100644 dist/buildUpreducer/index.js create mode 100644 dist/index.js create mode 100644 dist/middleware.test.js create mode 100644 dist/splat.test.js create mode 100644 dist/test.js create mode 100644 dist/updux.js diff --git a/dist/actions.test.js b/dist/actions.test.js new file mode 100644 index 0000000..6107b20 --- /dev/null +++ b/dist/actions.test.js @@ -0,0 +1,60 @@ +"use strict"; + +var _ = _interopRequireDefault(require(".")); + +var _updeep = _interopRequireDefault(require("updeep")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +test('actions defined in effects and mutations, multi-level', () => { + const { + actions + } = (0, _.default)({ + effects: { + foo: api => next => action => {} + }, + mutations: { + bar: () => () => null + }, + subduxes: { + mysub: { + effects: { + baz: api => next => action => {} + }, + mutations: { + quux: () => () => null + }, + actions: { + foo: limit => ({ + limit + }) + } + }, + myothersub: { + effects: { + foo: () => () => () => {} + } + } + } + }); + const types = Object.keys(actions); + types.sort(); + expect(types).toEqual(['bar', 'baz', 'foo', 'quux']); + expect(actions.bar()).toEqual({ + type: 'bar' + }); + expect(actions.bar('xxx')).toEqual({ + type: 'bar', + payload: 'xxx' + }); + expect(actions.bar(undefined, 'yyy')).toEqual({ + type: 'bar', + meta: 'yyy' + }); + expect(actions.foo(12)).toEqual({ + type: 'foo', + payload: { + limit: 12 + } + }); +}); \ No newline at end of file diff --git a/dist/buildActions/index.js b/dist/buildActions/index.js new file mode 100644 index 0000000..28e627a --- /dev/null +++ b/dist/buildActions/index.js @@ -0,0 +1,32 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = buildActions; + +var _fp = _interopRequireDefault(require("lodash/fp")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function actionFor(type) { + const creator = (payload = undefined, meta = undefined) => _fp.default.pickBy(v => v !== undefined)({ + type, + payload, + meta + }); + + creator._genericAction = true; + return creator; +} + +function buildActions(creators = {}, mutations = {}, effects = {}, subActions = []) { + // priority => generics => generic subs => craft subs => creators + const [crafted, generic] = _fp.default.partition(([type, f]) => !f._genericAction)(_fp.default.flatten(subActions.map(x => Object.entries(x))).filter(([_, f]) => f)); + + const actions = [...[...Object.keys(mutations), ...Object.keys(effects)].map(type => [type, actionFor(type)]), ...generic, ...crafted, ...Object.entries(creators).map(([type, payload]) => [type, (...args) => ({ + type, + payload: payload(...args) + })])]; + return _fp.default.fromPairs(actions); +} \ No newline at end of file diff --git a/dist/buildCreateStore.js b/dist/buildCreateStore.js new file mode 100644 index 0000000..6ffe6d5 --- /dev/null +++ b/dist/buildCreateStore.js @@ -0,0 +1,24 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = buildCreateStore; + +var _redux = require("redux"); + +function buildCreateStore(reducer, initial, middleware, actions) { + return () => { + const store = (0, _redux.createStore)(reducer, initial, (0, _redux.applyMiddleware)(middleware)); + + for (let a in actions) { + store.dispatch[a] = (...args) => { + store.dispatch(actions[a](...args)); + }; + } + + return store; + }; +} + +; \ No newline at end of file diff --git a/dist/buildInitial/index.js b/dist/buildInitial/index.js new file mode 100644 index 0000000..6492ffa --- /dev/null +++ b/dist/buildInitial/index.js @@ -0,0 +1,14 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = buildInitial; + +var _fp = _interopRequireDefault(require("lodash/fp")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function buildInitial(initial = {}, subduxes = {}) { + return _fp.default.isPlainObject(initial) ? _fp.default.mergeAll([subduxes, initial]) : initial; +} \ No newline at end of file diff --git a/dist/buildInitial/index.test-d.js b/dist/buildInitial/index.test-d.js new file mode 100644 index 0000000..9a390c3 --- /dev/null +++ b/dist/buildInitial/index.test-d.js @@ -0,0 +1 @@ +"use strict"; \ No newline at end of file diff --git a/dist/buildInitial/test-d.js b/dist/buildInitial/test-d.js new file mode 100644 index 0000000..9a390c3 --- /dev/null +++ b/dist/buildInitial/test-d.js @@ -0,0 +1 @@ +"use strict"; \ No newline at end of file diff --git a/dist/buildMiddleware.js b/dist/buildMiddleware.js new file mode 100644 index 0000000..533c417 --- /dev/null +++ b/dist/buildMiddleware.js @@ -0,0 +1,27 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = buildMiddleware; + +var _fp = _interopRequireDefault(require("lodash/fp")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const MiddlewareFor = (type, mw) => api => next => action => { + if (type !== '*' && action.type !== type) return next(action); + return mw(api)(next)(action); +}; + +function buildMiddleware(effects = {}, actions = {}, subduxes = {}) { + return api => { + for (let type in actions) { + api.dispatch[type] = (...args) => api.dispatch(actions[type](...args)); + } + + return original_next => { + return [..._fp.default.toPairs(effects).map(([type, effect]) => MiddlewareFor(type, effect)), ..._fp.default.map('middleware', subduxes)].filter(x => x).reduceRight((next, mw) => mw(api)(next), original_next); + }; + }; +} \ No newline at end of file diff --git a/dist/buildMutations/index.js b/dist/buildMutations/index.js new file mode 100644 index 0000000..15e95bc --- /dev/null +++ b/dist/buildMutations/index.js @@ -0,0 +1,52 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = buildMutations; + +var _fp = _interopRequireDefault(require("lodash/fp")); + +var _updeep = _interopRequireDefault(require("updeep")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const composeMutations = mutations => mutations.reduce((m1, m2) => (payload = null, action = {}) => state => m2(payload, action)(m1(payload, action)(state))); + +function buildMutations(mutations = {}, subduxes = {}) { + // we have to differentiate the subduxes with '*' than those + // without, as the root '*' is not the same as any sub-'*' + const actions = _fp.default.uniq(Object.keys(mutations).concat(...Object.values(subduxes).map(({ + mutations = {} + }) => Object.keys(mutations)))); + + let mergedMutations = {}; + + let [globby, nonGlobby] = _fp.default.partition(([_, { + mutations = {} + }]) => mutations['*'], Object.entries(subduxes)); + + globby = _fp.default.flow([_fp.default.fromPairs, _fp.default.mapValues(({ + reducer + }) => (_, action = {}) => state => reducer(state, action))])(globby); + + const globbyMutation = (payload, action) => (0, _updeep.default)(_fp.default.mapValues(mut => mut(payload, action))(globby)); + + actions.forEach(action => { + mergedMutations[action] = [globbyMutation]; + }); + nonGlobby.forEach(([slice, { + mutations = {}, + reducer = {} + }]) => { + Object.entries(mutations).forEach(([type, mutation]) => { + const localized = (payload = null, action = {}) => _updeep.default.updateIn(slice)(mutation(payload, action)); + + mergedMutations[type].push(localized); + }); + }); + Object.entries(mutations).forEach(([type, mutation]) => { + mergedMutations[type].push(mutation); + }); + return _fp.default.mapValues(composeMutations)(mergedMutations); +} \ No newline at end of file diff --git a/dist/buildUpreducer/index.js b/dist/buildUpreducer/index.js new file mode 100644 index 0000000..76fdc4e --- /dev/null +++ b/dist/buildUpreducer/index.js @@ -0,0 +1,19 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = buildUpreducer; + +var _fp = _interopRequireDefault(require("lodash/fp")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function buildUpreducer(initial, mutations) { + return (action = {}) => state => { + if (state === null) state = initial; + const a = mutations[action.type] || mutations['*']; + if (!a) return state; + return a(action.payload, action)(state); + }; +} \ No newline at end of file diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 0000000..1891074 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,18 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = updux; + +var _fp = _interopRequireDefault(require("lodash/fp")); + +var _updeep = _interopRequireDefault(require("updeep")); + +var _updux = _interopRequireDefault(require("./updux")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function updux(config) { + return new _updux.default(config); +} \ No newline at end of file diff --git a/dist/middleware.test.js b/dist/middleware.test.js new file mode 100644 index 0000000..97a1ff5 --- /dev/null +++ b/dist/middleware.test.js @@ -0,0 +1,92 @@ +"use strict"; + +var _ = _interopRequireDefault(require(".")); + +var _updeep = _interopRequireDefault(require("updeep")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +test('simple effect', () => { + const tracer = jest.fn(); + const store = (0, _.default)({ + effects: { + foo: api => next => action => { + tracer(); + next(action); + } + } + }).createStore(); + expect(tracer).not.toHaveBeenCalled(); + store.dispatch({ + type: 'bar' + }); + expect(tracer).not.toHaveBeenCalled(); + store.dispatch.foo(); + expect(tracer).toHaveBeenCalled(); +}); +test('effect and sub-effect', () => { + const tracer = jest.fn(); + + const tracerEffect = signature => api => next => action => { + tracer(signature); + next(action); + }; + + const store = (0, _.default)({ + effects: { + foo: tracerEffect('root') + }, + subduxes: { + zzz: (0, _.default)({ + effects: { + foo: tracerEffect('child') + } + }) + } + }).createStore(); + expect(tracer).not.toHaveBeenCalled(); + store.dispatch({ + type: 'bar' + }); + expect(tracer).not.toHaveBeenCalled(); + store.dispatch.foo(); + expect(tracer).toHaveBeenNthCalledWith(1, 'root'); + expect(tracer).toHaveBeenNthCalledWith(2, 'child'); +}); +test('"*" effect', () => { + const tracer = jest.fn(); + const store = (0, _.default)({ + effects: { + '*': api => next => action => { + tracer(); + next(action); + } + } + }).createStore(); + expect(tracer).not.toHaveBeenCalled(); + store.dispatch({ + type: 'bar' + }); + expect(tracer).toHaveBeenCalled(); +}); +test('async effect', async () => { + function timeout(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); + } + + const tracer = jest.fn(); + const store = (0, _.default)({ + effects: { + foo: api => next => async action => { + next(action); + await timeout(1000); + tracer(); + } + } + }).createStore(); + expect(tracer).not.toHaveBeenCalled(); + store.dispatch.foo(); + expect(tracer).not.toHaveBeenCalled(); + await timeout(1000); + expect(tracer).toHaveBeenCalled(); +}); \ No newline at end of file diff --git a/dist/splat.test.js b/dist/splat.test.js new file mode 100644 index 0000000..423552a --- /dev/null +++ b/dist/splat.test.js @@ -0,0 +1,79 @@ +"use strict"; + +var _ = _interopRequireDefault(require(".")); + +var _updeep = _interopRequireDefault(require("updeep")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const tracer = chr => (0, _updeep.default)({ + tracer: s => (s || '') + chr +}); + +test('mutations, simple', () => { + const dux = (0, _.default)({ + mutations: { + foo: () => tracer('a'), + '*': (p, a) => tracer('b') + } + }); + const store = dux.createStore(); + expect(store.getState()).toEqual({ + tracer: 'b' + }); + store.dispatch.foo(); + expect(store.getState()).toEqual({ + tracer: 'ba' + }); + store.dispatch({ + type: 'bar' + }); + expect(store.getState()).toEqual({ + tracer: 'bab' + }); +}); +test('with subduxes', () => { + const dux = (0, _.default)({ + mutations: { + foo: () => tracer('a'), + '*': (dummy, a) => tracer('b'), + bar: () => ({ + bar + }) => ({ + bar, + tracer: bar.tracer + }) + }, + subduxes: { + bar: (0, _.default)({ + mutations: { + foo: () => tracer('d'), + '*': (dummy, a) => tracer('e') + } + }) + } + }); + const store = dux.createStore(); + expect(store.getState()).toEqual({ + tracer: 'b', + bar: { + tracer: 'e' + } + }); + store.dispatch.foo(); + expect(store.getState()).toEqual({ + tracer: 'ba', + bar: { + tracer: 'ed' + } + }); + store.dispatch({ + type: 'bar' + }); + expect(store.getState()).toEqual({ + tracer: 'ede', + bar: { + tracer: 'ede' + } + }); +}); \ No newline at end of file diff --git a/dist/test.js b/dist/test.js new file mode 100644 index 0000000..5aaa6df --- /dev/null +++ b/dist/test.js @@ -0,0 +1,205 @@ +"use strict"; + +var _ = _interopRequireDefault(require(".")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +test('actions from mutations', () => { + const { + actions: { + foo, + bar + } + } = (0, _.default)({ + mutations: { + foo: () => x => x + } + }); + expect(foo()).toEqual({ + type: 'foo' + }); + expect(foo(true)).toEqual({ + type: 'foo', + payload: true + }); + expect(foo({ + bar: 2 + }, { + timestamp: 613 + })).toEqual({ + type: 'foo', + payload: { + bar: 2 + }, + meta: { + timestamp: 613 + } + }); +}); +test('reducer', () => { + const { + actions, + reducer + } = (0, _.default)({ + initial: { + counter: 1 + }, + mutations: { + inc: () => ({ + counter + }) => ({ + counter: counter + 1 + }) + } + }); + let state = reducer(null, {}); + expect(state).toEqual({ + counter: 1 + }); + state = reducer(state, actions.inc()); + expect(state).toEqual({ + counter: 2 + }); +}); +test('sub reducers', () => { + const foo = (0, _.default)({ + initial: 1, + mutations: { + doFoo: () => x => x + 1, + doAll: () => x => x + 10 + } + }); + const bar = (0, _.default)({ + initial: 'a', + mutations: { + doBar: () => x => x + 'a', + doAll: () => x => x + 'b' + } + }); + const { + initial, + actions, + reducer + } = (0, _.default)({ + subduxes: { + foo, + bar + } + }); + expect(initial).toEqual({ + foo: 1, + bar: 'a' + }); + expect(Object.keys(actions)).toHaveLength(3); + let state = reducer(null, {}); + expect(state).toEqual({ + foo: 1, + bar: 'a' + }); + state = reducer(state, actions.doFoo()); + expect(state).toEqual({ + foo: 2, + bar: 'a' + }); + state = reducer(state, actions.doBar()); + expect(state).toEqual({ + foo: 2, + bar: 'aa' + }); + state = reducer(state, actions.doAll()); + expect(state).toEqual({ + foo: 12, + bar: 'aab' + }); +}); +test('precedence between root and sub-reducers', () => { + const { + initial, + reducer, + actions + } = (0, _.default)({ + initial: { + foo: { + bar: 4 + } + }, + mutations: { + inc: () => state => { + return { ...state, + surprise: state.foo.bar + }; + } + }, + subduxes: { + foo: (0, _.default)({ + initial: { + bar: 2, + quux: 3 + }, + mutations: { + inc: () => state => ({ ...state, + bar: state.bar + 1 + }) + } + }) + } + }); + expect(initial).toEqual({ + foo: { + bar: 4, + quux: 3 + } + }); + expect(reducer(null, actions.inc())).toEqual({ + foo: { + bar: 5, + quux: 3 + }, + surprise: 5 + }); +}); + +function timeout(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +test('middleware', async () => { + const { + middleware, + createStore + } = (0, _.default)({ + initial: "", + mutations: { + inc: addition => state => state + addition, + doEeet: () => state => { + return state + 'Z'; + } + }, + effects: { + doEeet: api => next => async action => { + api.dispatch.inc('a'); + next(action); + await timeout(1000); + api.dispatch.inc('c'); + } + }, + subduxes: { + foo: (0, _.default)({ + effects: { + doEeet: api => next => action => { + api.dispatch({ + type: 'inc', + payload: 'b' + }); + next(action); + } + } + }) + } + }); + const store = createStore(); + store.dispatch.doEeet(); + expect(store.getState()).toEqual('abZ'); + await timeout(1000); + expect(store.getState()).toEqual('abZc'); +}); \ No newline at end of file diff --git a/dist/updux.js b/dist/updux.js new file mode 100644 index 0000000..6263609 --- /dev/null +++ b/dist/updux.js @@ -0,0 +1,48 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = exports.Updux = void 0; + +var _fp = _interopRequireDefault(require("lodash/fp")); + +var _buildActions = _interopRequireDefault(require("./buildActions")); + +var _buildInitial = _interopRequireDefault(require("./buildInitial")); + +var _buildMutations = _interopRequireDefault(require("./buildMutations")); + +var _buildCreateStore = _interopRequireDefault(require("./buildCreateStore")); + +var _buildMiddleware = _interopRequireDefault(require("./buildMiddleware")); + +var _buildUpreducer = _interopRequireDefault(require("./buildUpreducer")); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +class Updux { + constructor(config) { + this.subduxes = _fp.default.mapValues(value => _fp.default.isPlainObject(value) ? new Updux(value) : value)(_fp.default.getOr({}, 'subduxes', config)); + this.actions = (0, _buildActions.default)(config.actions, config.mutations, config.effects, Object.values(this.subduxes).map(({ + actions + }) => actions)); + this.initial = (0, _buildInitial.default)(config.initial, _fp.default.mapValues(({ + initial + }) => initial)(this.subduxes)); + this.mutations = (0, _buildMutations.default)(config.mutations, this.subduxes); + this.upreducer = (0, _buildUpreducer.default)(this.initial, this.mutations); + + this.reducer = (state, action) => { + return this.upreducer(action)(state); + }; + + this.middleware = (0, _buildMiddleware.default)(config.effects, this.actions, config.subduxes); + this.createStore = (0, _buildCreateStore.default)(this.reducer, this.initial, this.middleware, this.actions); + } + +} + +exports.Updux = Updux; +var _default = Updux; +exports.default = _default; \ No newline at end of file