├── .atomignore
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE.md
├── dist
├── redux-decorators.js
└── redux-decorators.js.map
├── index.d.ts
├── package.json
├── readme.md
├── src
├── initial-state.decorator.ts
├── reducer.decorator.ts
├── redux-decorators.ts
├── slice-state.helper.ts
├── slice.decorator.ts
├── state.decorator.ts
├── store.decorator.ts
└── store.decorator
│ ├── angular2.binding.ts
│ └── general.binding.ts
├── test
├── all-tests.ts
├── must.ts
├── reducer.decorator.spec.ts
├── sinon.ts
├── slice-state.helper.spec.ts
├── slice.decorator.spec.ts
├── slice.reducer.decorators.spec.ts
├── store.decorator.spec.ts
└── store.decorator
│ ├── angular2.binding.spec.ts
│ └── general.binding.spec.ts
├── tsconfig.json
├── tsd.json
└── webpack.config.js
/.atomignore:
--------------------------------------------------------------------------------
1 | /dist/
2 | /typings/
3 | /node_modules/
4 | .atomignore
5 | package.json
6 | tsconfig.json
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /dist/*spec.*
2 | /typings/
3 | /node_modules/
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "4"
4 | - "5"
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 0.4.3
2 |
3 | - Fixed structure of d.ts file and renamed to index.d.ts
4 |
5 | # 0.4.2
6 |
7 | - Fixed @Slice signature in ts.d file
8 |
9 | # 0.4.1
10 |
11 | - Added @InitialState decorator, reverting the breaking change
12 |
13 | # 0.4.0 (BREAKING CHANGES)
14 |
15 | - Refactored @Reducer decorator
16 | - Added new @Slice decorator
17 | - **Breaking Change:** Removed @InitialState decorator
18 | - Fix sourcemaps for dist builds
19 | - Cleaned up devDependencies
20 |
21 | # 0.3.0
22 |
23 | - Fixed bug with subscription handler context
24 | - Added Angular 2 binding tests
25 | - Added general binding tests
26 | - Added ts.d file
27 |
28 | # 0.2.1
29 |
30 | - Improved action reducer decorator syntax
31 |
32 | # 0.2.0
33 |
34 | - Added reducer decorator tests
35 | - Added store decorator tests
36 |
37 | # 0.1.2
38 |
39 | - Added plunkr example
40 | - Fixed bug with data not being passed to action reducers
41 |
42 | # 0.1.1
43 |
44 | - Initial release.
45 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Karl Purkhardt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/dist/redux-decorators.js:
--------------------------------------------------------------------------------
1 | (function webpackUniversalModuleDefinition(root, factory) {
2 | if(typeof exports === 'object' && typeof module === 'object')
3 | module.exports = factory(require("redux"));
4 | else if(typeof define === 'function' && define.amd)
5 | define(["redux"], factory);
6 | else if(typeof exports === 'object')
7 | exports["ReduxDecorators"] = factory(require("redux"));
8 | else
9 | root["ReduxDecorators"] = factory(root["redux"]);
10 | })(this, function(__WEBPACK_EXTERNAL_MODULE_5__) {
11 | return /******/ (function(modules) { // webpackBootstrap
12 | /******/ // The module cache
13 | /******/ var installedModules = {};
14 | /******/
15 | /******/ // The require function
16 | /******/ function __webpack_require__(moduleId) {
17 | /******/
18 | /******/ // Check if module is in cache
19 | /******/ if(installedModules[moduleId])
20 | /******/ return installedModules[moduleId].exports;
21 | /******/
22 | /******/ // Create a new module (and put it into the cache)
23 | /******/ var module = installedModules[moduleId] = {
24 | /******/ exports: {},
25 | /******/ id: moduleId,
26 | /******/ loaded: false
27 | /******/ };
28 | /******/
29 | /******/ // Execute the module function
30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31 | /******/
32 | /******/ // Flag the module as loaded
33 | /******/ module.loaded = true;
34 | /******/
35 | /******/ // Return the exports of the module
36 | /******/ return module.exports;
37 | /******/ }
38 | /******/
39 | /******/
40 | /******/ // expose the modules object (__webpack_modules__)
41 | /******/ __webpack_require__.m = modules;
42 | /******/
43 | /******/ // expose the module cache
44 | /******/ __webpack_require__.c = installedModules;
45 | /******/
46 | /******/ // __webpack_public_path__
47 | /******/ __webpack_require__.p = "";
48 | /******/
49 | /******/ // Load entry module and return exports
50 | /******/ return __webpack_require__(0);
51 | /******/ })
52 | /************************************************************************/
53 | /******/ ([
54 | /* 0 */
55 | /***/ function(module, exports, __webpack_require__) {
56 |
57 | ///
58 | var reducer_decorator_1 = __webpack_require__(1);
59 | exports.Reducer = reducer_decorator_1.Reducer;
60 | var state_decorator_1 = __webpack_require__(2);
61 | exports.State = state_decorator_1.State;
62 | var store_decorator_1 = __webpack_require__(3);
63 | exports.Store = store_decorator_1.Store;
64 | var initial_state_decorator_1 = __webpack_require__(7);
65 | exports.InitialState = initial_state_decorator_1.InitialState;
66 | var slice_decorator_1 = __webpack_require__(8);
67 | exports.Slice = slice_decorator_1.Slice;
68 | // reducer decorator module exports
69 | // state decorator module exports
70 | // store decorator module exports
71 | // initial-state decorator module exports
72 |
73 |
74 | /***/ },
75 | /* 1 */
76 | /***/ function(module, exports) {
77 |
78 | var rootReducer;
79 | var initialState = {};
80 | //------------------------------------------------------------------------------
81 | // Initial state
82 | //------------------------------------------------------------------------------
83 | function setInitialState(state) {
84 | initialState = state;
85 | }
86 | exports.setInitialState = setInitialState;
87 | //------------------------------------------------------------------------------
88 | // Root reducer
89 | //------------------------------------------------------------------------------
90 | function setReducer(reducer) {
91 | rootReducer = reducer;
92 | }
93 | exports.setReducer = setReducer;
94 | function getReducer() {
95 | return rootReducer || DefaultReducer.prototype.reducer;
96 | }
97 | exports.getReducer = getReducer;
98 | var DefaultReducer = (function () {
99 | function DefaultReducer() {
100 | }
101 | DefaultReducer.prototype.reducer = function (state, action) {
102 | if (state === void 0) { state = initialState; }
103 | var matchingActionTypeOnly = function (actionReducer) {
104 | return actionReducer.type === action.type;
105 | };
106 | if (action.type === '@@redux/INIT') {
107 | return actionReducers.reduce(function (nextState, reducer) {
108 | if (!reducer.owner.getInitialState) {
109 | return state;
110 | }
111 | var initialState = reducer.owner.getInitialState(reducer.type);
112 | var slice = reducer.owner.getSlice ? reducer.owner.getSlice(reducer.methodName) : null;
113 | if (initialState !== undefined && slice) {
114 | nextState[slice] = initialState;
115 | }
116 | return nextState;
117 | }, initialState);
118 | }
119 | var filteredActionReducers = actionReducers.filter(matchingActionTypeOnly);
120 | if (!filteredActionReducers.length) {
121 | return state;
122 | }
123 | var createNextState = function (state, _a) {
124 | var owner = _a.owner, methodName = _a.methodName;
125 | var reducer = owner[methodName];
126 | var slice, nextState;
127 | slice = owner.getSlice ? owner.getSlice(methodName) : null;
128 | if (slice) {
129 | var inputState = state.hasOwnProperty(slice) ? state[slice] : undefined;
130 | nextState = state;
131 | nextState[slice] = reducer.apply(void 0, [inputState].concat(action.data));
132 | }
133 | else {
134 | nextState = reducer.apply(void 0, [state].concat(action.data));
135 | }
136 | return Object.assign(state, nextState);
137 | };
138 | return filteredActionReducers.reduce(createNextState, state);
139 | };
140 | return DefaultReducer;
141 | })();
142 | exports.DefaultReducer = DefaultReducer;
143 | var actionReducers = [];
144 | function addActionReducer(type, owner, methodName) {
145 | actionReducers.push({ type: type, owner: owner, methodName: methodName });
146 | }
147 | exports.addActionReducer = addActionReducer;
148 | function getActionReducers() {
149 | return actionReducers;
150 | }
151 | exports.getActionReducers = getActionReducers;
152 | function removeActionReducers() {
153 | actionReducers = [];
154 | }
155 | exports.removeActionReducers = removeActionReducers;
156 | //------------------------------------------------------------------------------
157 | // Decorator
158 | //------------------------------------------------------------------------------
159 | var handleActionReducer = function (owner, methodName) {
160 | addActionReducer(methodName, owner, methodName);
161 | };
162 | var handleRootReducer = function (owner, methodNames) {
163 | if (owner.prototype.reducer) {
164 | rootReducer = owner.prototype.reducer;
165 | }
166 | var mapMethodNames = function (methodName) { return { type: methodName, owner: owner.prototype, methodName: methodName }; };
167 | actionReducers = actionReducers.concat(methodNames.map(mapMethodNames));
168 | };
169 | function Reducer() {
170 | var methodNames = [];
171 | for (var _i = 0; _i < arguments.length; _i++) {
172 | methodNames[_i - 0] = arguments[_i];
173 | }
174 | return function (owner, methodName) {
175 | if (!owner.prototype) {
176 | handleActionReducer(owner, methodName);
177 | return;
178 | }
179 | handleRootReducer(owner, methodNames);
180 | };
181 | }
182 | exports.Reducer = Reducer;
183 |
184 |
185 | /***/ },
186 | /* 2 */
187 | /***/ function(module, exports) {
188 |
189 | function State() {
190 | return function (target) {
191 | var props = [];
192 | for (var _i = 1; _i < arguments.length; _i++) {
193 | props[_i - 1] = arguments[_i];
194 | }
195 | if (target.stateProperties === undefined) {
196 | target.stateProperties = [];
197 | }
198 | if (props.length) {
199 | target.stateProperties = target.stateProperties.concat(props);
200 | }
201 | };
202 | }
203 | exports.State = State;
204 |
205 |
206 | /***/ },
207 | /* 3 */
208 | /***/ function(module, exports, __webpack_require__) {
209 |
210 | var general_binding_1 = __webpack_require__(4);
211 | exports.getStore = general_binding_1.getStore;
212 | var angular2_binding_1 = __webpack_require__(6);
213 | function Store() {
214 | var stateProperties = [];
215 | for (var _i = 0; _i < arguments.length; _i++) {
216 | stateProperties[_i - 0] = arguments[_i];
217 | }
218 | return function (target) {
219 | angular2_binding_1.angular2Binding(general_binding_1.generalBinding(target, stateProperties));
220 | };
221 | }
222 | exports.Store = Store;
223 |
224 |
225 | /***/ },
226 | /* 4 */
227 | /***/ function(module, exports, __webpack_require__) {
228 |
229 | var redux_1 = __webpack_require__(5);
230 | var reducer_decorator_1 = __webpack_require__(1);
231 | var appStore;
232 | function getStore() {
233 | if (!appStore) {
234 | appStore = new Promise(function (resolve) {
235 | var interval = setInterval(function () {
236 | if (reducer_decorator_1.getReducer()) {
237 | clearInterval(interval);
238 | resolve(redux_1.createStore(reducer_decorator_1.getReducer()));
239 | }
240 | });
241 | });
242 | }
243 | return appStore;
244 | }
245 | exports.getStore = getStore;
246 | function updateStateProperties(target, state, properties) {
247 | if (properties === void 0) { properties = 'stateProperties'; }
248 | target[properties].forEach(function (prop) {
249 | target[prop] = state[prop];
250 | });
251 | }
252 | exports.updateStateProperties = updateStateProperties;
253 | function generalBinding(target, stateProperties) {
254 | // Add stateProperties to the target
255 | if (target.prototype.stateProperties === undefined) {
256 | target.prototype.stateProperties = [];
257 | }
258 | target.prototype.stateProperties = target.prototype.stateProperties.concat(stateProperties);
259 | // Add a dispatch method to the target
260 | target.prototype.dispatch = function (action) {
261 | var data = [];
262 | for (var _i = 1; _i < arguments.length; _i++) {
263 | data[_i - 1] = arguments[_i];
264 | }
265 | this.appStore.dispatch({ type: action, data: data });
266 | };
267 | // Add a generic store update handler
268 | target.prototype.storeUpdateHandler = function () {
269 | updateStateProperties(this, this.appStore.getState());
270 | };
271 | // Add a generic storeInit method
272 | target.prototype.storeInit = function () {
273 | var _this = this;
274 | return this.getStore().then(function () {
275 | _this.unsubscribe = _this.appStore.subscribe(_this.storeUpdateHandler.bind(_this));
276 | // Apply the default state to all listeners
277 | _this.storeUpdateHandler();
278 | });
279 | };
280 | // Add a generic storeDestroy method
281 | target.prototype.storeDestroy = function () {
282 | this.unsubscribe();
283 | };
284 | // Add a get store method
285 | target.prototype.getStore = function () {
286 | var _this = this;
287 | if (this.appStore) {
288 | return this.appStore.then ? this.appStore : Promise.resolve(this.appStore);
289 | }
290 | return getStore().then(function (store) { return _this.appStore = store; });
291 | };
292 | // Add a set store method for testing purposes
293 | target.prototype.setStore = function (store) {
294 | this.appStore = store;
295 | };
296 | return target;
297 | }
298 | exports.generalBinding = generalBinding;
299 |
300 |
301 | /***/ },
302 | /* 5 */
303 | /***/ function(module, exports) {
304 |
305 | module.exports = require("redux");
306 |
307 | /***/ },
308 | /* 6 */
309 | /***/ function(module, exports) {
310 |
311 | function angular2Binding(target) {
312 | var existingNgOnInit = target.prototype.ngOnInit;
313 | var existingNgOnDestroy = target.prototype.ngOnDestroy;
314 | target.prototype.ngOnInit = function () {
315 | this.storeInit();
316 | !existingNgOnInit || existingNgOnInit.call(this);
317 | };
318 | target.prototype.ngOnDestroy = function () {
319 | this.storeDestroy();
320 | !existingNgOnDestroy || existingNgOnDestroy.call(this);
321 | };
322 | return target;
323 | }
324 | exports.angular2Binding = angular2Binding;
325 |
326 |
327 | /***/ },
328 | /* 7 */
329 | /***/ function(module, exports, __webpack_require__) {
330 |
331 | var reducer_decorator_1 = __webpack_require__(1);
332 | function InitialState(initialState) {
333 | reducer_decorator_1.setInitialState(initialState);
334 | return function (target) { };
335 | }
336 | exports.InitialState = InitialState;
337 |
338 |
339 | /***/ },
340 | /* 8 */
341 | /***/ function(module, exports, __webpack_require__) {
342 |
343 | var slice_state_helper_1 = __webpack_require__(9);
344 | var addGetSliceMethod = function (target) {
345 | if (target.getSlice) {
346 | return;
347 | }
348 | target.getSlice = function (actionType) {
349 | if (!target.stateSliceAffected) {
350 | return undefined;
351 | }
352 | if (target.stateSliceAffected.hasOwnProperty(actionType)) {
353 | return target.stateSliceAffected[actionType];
354 | }
355 | if (target.stateSliceAffected.hasOwnProperty('default')) {
356 | return target.stateSliceAffected.default;
357 | }
358 | return undefined;
359 | };
360 | };
361 | var addStateSliceAffectedProperty = function (target) {
362 | if (target.hasOwnProperty('stateSliceAffected')) {
363 | return;
364 | }
365 | target.stateSliceAffected = {};
366 | };
367 | function Slice(slice, initialState) {
368 | return function (target, method) {
369 | var isInstance = !target.prototype;
370 | if (isInstance) {
371 | addStateSliceAffectedProperty(target);
372 | addGetSliceMethod(target);
373 | target.stateSliceAffected[method] = slice;
374 | slice_state_helper_1.setSliceState(initialState, target, method);
375 | return;
376 | }
377 | addStateSliceAffectedProperty(target.prototype);
378 | addGetSliceMethod(target.prototype);
379 | target.prototype.stateSliceAffected.default = slice;
380 | slice_state_helper_1.setSliceState(initialState, target);
381 | };
382 | }
383 | exports.Slice = Slice;
384 |
385 |
386 | /***/ },
387 | /* 9 */
388 | /***/ function(module, exports) {
389 |
390 | var addGetInitialStateMethod = function (target) {
391 | if (target.getInitialState) {
392 | return;
393 | }
394 | target.getInitialState = function (actionType) {
395 | if (!target.initialState) {
396 | return undefined;
397 | }
398 | if (target.initialState.hasOwnProperty(actionType)) {
399 | return target.initialState[actionType];
400 | }
401 | if (target.initialState.hasOwnProperty('default')) {
402 | return target.initialState.default;
403 | }
404 | return undefined;
405 | };
406 | };
407 | var addInitialStateProperty = function (target) {
408 | if (target.hasOwnProperty('initialState')) {
409 | return;
410 | }
411 | target.initialState = {};
412 | };
413 | function setSliceState(initialState, target, methodName) {
414 | var isInstance = !target.prototype;
415 | var targetRef = isInstance ? target : target.prototype;
416 | addInitialStateProperty(targetRef);
417 | addGetInitialStateMethod(targetRef);
418 | methodName = methodName ? methodName : 'default';
419 | targetRef.initialState[methodName] = initialState;
420 | }
421 | exports.setSliceState = setSliceState;
422 |
423 |
424 | /***/ }
425 | /******/ ])
426 | });
427 | ;
428 | //# sourceMappingURL=redux-decorators.js.map
--------------------------------------------------------------------------------
/dist/redux-decorators.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../webpack/universalModuleDefinition","../webpack/bootstrap 9808956a721bbec68aa2",".././src/redux-decorators.ts",".././src/reducer.decorator.ts",".././src/state.decorator.ts",".././src/store.decorator.ts",".././src/store.decorator/general.binding.ts","../external \"redux\"",".././src/store.decorator/angular2.binding.ts",".././src/initial-state.decorator.ts",".././src/slice.decorator.ts",".././src/slice-state.helper.ts"],"names":["setInitialState","setReducer","getReducer","DefaultReducer","DefaultReducer.constructor","DefaultReducer.reducer","addActionReducer","getActionReducers","removeActionReducers","Reducer","State","Store","getStore","updateStateProperties","generalBinding","angular2Binding","InitialState","Slice","setSliceState"],"mappings":"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","file":"./dist/redux-decorators.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"redux\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"redux\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ReduxDecorators\"] = factory(require(\"redux\"));\n\telse\n\t\troot[\"ReduxDecorators\"] = factory(root[\"redux\"]);\n})(this, function(__WEBPACK_EXTERNAL_MODULE_5__) {\nreturn \n\n\n/** WEBPACK FOOTER **\n ** webpack/universalModuleDefinition\n **/"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n/** WEBPACK FOOTER **\n ** webpack/bootstrap 9808956a721bbec68aa2\n **/","/// \n\nimport {Reducer, RootReducer} from './reducer.decorator';\nimport {State} from './state.decorator';\nimport {IStore, Store} from './store.decorator';\nimport {InitialState} from './initial-state.decorator';\nexport {Slice} from './slice.decorator';\n\n// reducer decorator module exports\nexport {RootReducer};\nexport {Reducer};\n\n// state decorator module exports\nexport {State};\n\n// store decorator module exports\nexport {Store};\nexport {IStore};\n\n// initial-state decorator module exports\nexport {InitialState};\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/redux-decorators.ts\n **/","import {Reducer} from 'redux';\n\nlet rootReducer: Reducer;\nlet initialState: any = {};\n\n//------------------------------------------------------------------------------\n// Initial state\n//------------------------------------------------------------------------------\n\nexport function setInitialState(state: any): any {\n initialState = state;\n}\n\n//------------------------------------------------------------------------------\n// Root reducer\n//------------------------------------------------------------------------------\n\nexport function setReducer(reducer: Reducer): void {\n rootReducer = reducer;\n}\n\nexport function getReducer(): Reducer {\n return rootReducer || DefaultReducer.prototype.reducer;\n}\n\nexport interface RootReducer {\n prototype?: any;// I shouldn't need this\n reducer(state: any, action: any): any;\n}\n\nexport class DefaultReducer implements RootReducer {\n reducer(state: any = initialState, action: any): any {\n\n let matchingActionTypeOnly = (actionReducer) => {\n return actionReducer.type === action.type;\n };\n\n if (action.type === '@@redux/INIT') {\n return actionReducers.reduce((nextState, reducer) => {\n if (!reducer.owner.getInitialState) {\n return state;\n }\n const initialState = reducer.owner.getInitialState(reducer.type);\n const slice = reducer.owner.getSlice ? reducer.owner.getSlice(reducer.methodName) : null;\n if (initialState !== undefined && slice) {\n nextState[slice] = initialState;\n }\n return nextState;\n }, initialState);\n }\n\n let filteredActionReducers = actionReducers.filter(matchingActionTypeOnly);\n\n if (!filteredActionReducers.length) {\n return state;\n }\n\n let createNextState = (state, {owner, methodName}) => {\n const reducer = owner[methodName];\n let slice, nextState;\n slice = owner.getSlice ? owner.getSlice(methodName) : null;\n if (slice) {\n const inputState = state.hasOwnProperty(slice) ? state[slice] : undefined;\n nextState = state;\n nextState[slice] = reducer(inputState, ...action.data);\n }\n else {\n nextState = reducer(state, ...action.data);\n }\n return Object.assign(state, nextState);\n }\n\n return filteredActionReducers.reduce(createNextState, state);\n }\n}\n\n//------------------------------------------------------------------------------\n// Action reducers\n//------------------------------------------------------------------------------\n\ninterface ActionReducer {\n type: string;\n owner: any;\n methodName: string;\n}\n\nlet actionReducers: ActionReducer[] = [];\n\nexport function addActionReducer(type: string, owner: Function, methodName: string): void {\n actionReducers.push({type, owner, methodName});\n}\n\nexport function getActionReducers(): ActionReducer[] {\n return actionReducers;\n}\n\nexport function removeActionReducers(): void {\n actionReducers = [];\n}\n\n//------------------------------------------------------------------------------\n// Decorator\n//------------------------------------------------------------------------------\n\nlet handleActionReducer = function(owner: any, methodName: string): void {\n addActionReducer(methodName, owner, methodName);\n}\n\nlet handleRootReducer = function(owner: any, methodNames: string[]): void {\n if (owner.prototype.reducer) {\n rootReducer = owner.prototype.reducer;\n }\n let mapMethodNames = (methodName) => { return { type: methodName, owner: owner.prototype, methodName } };\n actionReducers = actionReducers.concat(methodNames.map(mapMethodNames));\n}\n\nexport function Reducer(...methodNames: string[]): Function {\n return function(owner: any, methodName?: string): void {\n if (!owner.prototype) {\n handleActionReducer(owner, methodName);\n return;\n }\n handleRootReducer(owner, methodNames);\n }\n}\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/reducer.decorator.ts\n **/","\nexport function State(): Function {\n return function(target: any, ...props: string[]): void {\n if (target.stateProperties === undefined) {\n target.stateProperties = [];\n }\n if (props.length) {\n target.stateProperties = target.stateProperties.concat(props);\n }\n }\n}\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/state.decorator.ts\n **/","import {getStore, generalBinding} from './store.decorator/general.binding';\nimport {angular2Binding} from './store.decorator/angular2.binding';\nexport {getStore};\n\nexport interface IStore {\n dispatch(action: string);\n}\n\nexport function Store(...stateProperties: string[]) {\n return function(target: any) {\n angular2Binding(generalBinding(target, stateProperties));\n }\n}\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/store.decorator.ts\n **/","import {createStore, Store} from 'redux';\nimport {getReducer} from './../reducer.decorator';\n\nlet appStore;\n\nexport function getStore(): Promise {\n if (!appStore) {\n appStore = new Promise((resolve) => {\n var interval = setInterval(() => {\n if (getReducer()) {\n clearInterval(interval);\n resolve(createStore(getReducer()));\n }\n });\n });\n }\n return appStore;\n}\n\nexport function updateStateProperties(target: any, state: any, properties: string = 'stateProperties'): void {\n target[properties].forEach(prop => {\n target[prop] = state[prop];\n });\n}\n\nexport function generalBinding(target: any, stateProperties: string[]): void {\n\n // Add stateProperties to the target\n if (target.prototype.stateProperties === undefined) {\n target.prototype.stateProperties = [];\n }\n target.prototype.stateProperties = target.prototype.stateProperties.concat(stateProperties);\n\n // Add a dispatch method to the target\n target.prototype.dispatch = function(action, ...data) {\n this.appStore.dispatch({type: action, data: data});\n };\n\n // Add a generic store update handler\n target.prototype.storeUpdateHandler = function() {\n updateStateProperties(this, this.appStore.getState());\n };\n\n // Add a generic storeInit method\n target.prototype.storeInit = function() {\n return this.getStore().then(() => {\n this.unsubscribe = this.appStore.subscribe(this.storeUpdateHandler.bind(this));\n // Apply the default state to all listeners\n this.storeUpdateHandler();\n });\n }\n\n // Add a generic storeDestroy method\n target.prototype.storeDestroy = function() {\n this.unsubscribe();\n };\n\n // Add a get store method\n target.prototype.getStore = function() {\n if (this.appStore) {\n return this.appStore.then ? this.appStore : Promise.resolve(this.appStore);\n }\n return getStore().then(store => this.appStore = store);\n };\n\n // Add a set store method for testing purposes\n target.prototype.setStore = function(store) {\n this.appStore = store;\n };\n\n return target;\n}\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/store.decorator/general.binding.ts\n **/","module.exports = require(\"redux\");\n\n\n/*****************\n ** WEBPACK FOOTER\n ** external \"redux\"\n ** module id = 5\n ** module chunks = 0\n **/","export function angular2Binding(target: any) {\n const existingNgOnInit = target.prototype.ngOnInit;\n const existingNgOnDestroy = target.prototype.ngOnDestroy;\n target.prototype.ngOnInit = function() {\n this.storeInit();\n !existingNgOnInit || existingNgOnInit.call(this);\n }\n target.prototype.ngOnDestroy = function() {\n this.storeDestroy();\n !existingNgOnDestroy || existingNgOnDestroy.call(this);\n }\n return target;\n}\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/store.decorator/angular2.binding.ts\n **/","import {setInitialState} from './reducer.decorator';\n\nexport function InitialState(initialState: any): Function {\n setInitialState(initialState);\n return function(target: any): void {}\n}\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/initial-state.decorator.ts\n **/","import { setSliceState } from './slice-state.helper';\n\nconst addGetSliceMethod = (target) => {\n if (target.getSlice) {\n return;\n }\n target.getSlice = (actionType) => {\n if (!target.stateSliceAffected) {\n return undefined;\n }\n if (target.stateSliceAffected.hasOwnProperty(actionType)) {\n return target.stateSliceAffected[actionType];\n }\n if (target.stateSliceAffected.hasOwnProperty('default')) {\n return target.stateSliceAffected.default;\n }\n return undefined;\n }\n};\n\nconst addStateSliceAffectedProperty = (target) => {\n if (target.hasOwnProperty('stateSliceAffected')) {\n return;\n }\n target.stateSliceAffected = {};\n};\n\nexport function Slice(slice: string, initialState?: any): Function {\n return function(target: any, method?: string): void {\n const isInstance = !target.prototype;\n if (isInstance) {\n addStateSliceAffectedProperty(target);\n addGetSliceMethod(target);\n target.stateSliceAffected[method] = slice;\n setSliceState(initialState, target, method);\n return;\n }\n addStateSliceAffectedProperty(target.prototype);\n addGetSliceMethod(target.prototype);\n target.prototype.stateSliceAffected.default = slice;\n setSliceState(initialState, target);\n }\n}\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/slice.decorator.ts\n **/","const addGetInitialStateMethod = (target) => {\n if (target.getInitialState) {\n return;\n }\n target.getInitialState = (actionType) => {\n if (!target.initialState) {\n return undefined;\n }\n if (target.initialState.hasOwnProperty(actionType)) {\n return target.initialState[actionType];\n }\n if (target.initialState.hasOwnProperty('default')) {\n return target.initialState.default;\n }\n return undefined;\n }\n};\n\nconst addInitialStateProperty = (target) => {\n if (target.hasOwnProperty('initialState')) {\n return;\n }\n target.initialState = {};\n};\n\nexport function setSliceState(initialState: any, target: any, methodName?: string): void {\n const isInstance = !target.prototype;\n var targetRef = isInstance ? target : target.prototype;\n addInitialStateProperty(targetRef);\n addGetInitialStateMethod(targetRef);\n methodName = methodName ? methodName : 'default';\n targetRef.initialState[methodName] = initialState;\n}\n\n\n\n/** WEBPACK FOOTER **\n ** ./src/slice-state.helper.ts\n **/"],"sourceRoot":""}
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | export interface RootReducer {
2 | reduce(state: any, action: any);
3 | }
4 | export function Slice(slice: string, initialState?: any): Function;
5 | export function InitialState(state: any): Function;
6 | export function Reducer(...name: string[]): Function;
7 | export function Store(...name: string[]): Function;
8 | export interface IStore {
9 | dispatch(action: string, ...data: any[])
10 | }
11 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redux-decorators",
3 | "version": "0.4.3",
4 | "scripts": {
5 | "build": "webpack",
6 | "test": "webpack --env=test > /dev/null && mocha dist/redux-decorators.spec.js"
7 | },
8 | "license": "MIT",
9 | "dependencies": {
10 | "es6-shim": "^0.33.3",
11 | "redux": "^3.2.1"
12 | },
13 | "devDependencies": {
14 | "mocha": "^2.4.5",
15 | "must": "^0.13.1",
16 | "sinon": "^1.17.3",
17 | "typescript": "^1.7.5",
18 | "ts-loader": "^0.8.0",
19 | "webpack": "^1.12.12",
20 | "yargs": "^3.32.0"
21 | },
22 | "description": "A ridiculously good syntax for working with Redux and TypeScript. Currently limited to Angular 2 but could potentially be used elsewhere.",
23 | "main": "src/redux-decorators.js",
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/KarlPurk/redux-decorators.git"
27 | },
28 | "keywords": [
29 | "redux",
30 | "typescript",
31 | "angular2"
32 | ],
33 | "author": "Karl Purkhardt",
34 | "bugs": {
35 | "url": "https://github.com/KarlPurk/redux-decorators/issues"
36 | },
37 | "homepage": "https://github.com/KarlPurk/redux-decorators"
38 | }
39 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/KarlPurk/redux-decorators) [](https://codeclimate.com/github/KarlPurk/redux-decorators) [](https://badge.fury.io/js/redux-decorators)
2 |
3 | # Redux Decorators
4 |
5 | A ridiculously good syntax for working with Redux using decorators in ES7 / TypeScript. Currently limited to Angular 2 but could potentially be used elsewhere.
6 |
7 | Try a live example on plunkr
8 |
9 | # Demo Repositories
10 |
11 | Check out the following repositories for working examples:
12 |
13 | - Angular 2 demo
14 |
15 | # Installation
16 |
17 | ```
18 | npm i redux-decorators
19 | ```
20 |
21 | # Example Usage (Angular 2)
22 |
23 | **app.reducer.ts**
24 |
25 | ```js
26 | import {InitialState, Reducer} from 'redux-decorators';
27 |
28 | @Slice('count', 0)
29 | @Reducer('add', 'remove')
30 | export class AppReducer {
31 | add(count) { return count + 1; }
32 | remove(count) { return count - 1; }
33 | }
34 | ```
35 |
36 | In the above example we create a new class that will hold our action reducers. We use the `@Slice` decorator to specify which slice these action reducers receive. We also specify `0` for the default value of `count`.
37 |
38 | We then register two action reducers with the `@Reducer('add', 'remove')` decorator. Anytime an `add` or `remove` action is dispatched the corresponding method will be called on the `AppReducer` class, allowing the method to update the state for that particular action.
39 |
40 | **count.component.ts**
41 |
42 | ```js
43 | import {Component} from 'angular2/core';
44 | import {Store} from 'redux-decorators';
45 |
46 | @Component({
47 | selector: 'counter',
48 | template: `
49 | Count: {{count}}
50 |
51 |
52 | `
53 | })
54 |
55 | @Store('count')
56 | export class CounterComponent {}
57 | ```
58 |
59 | In the above example we used the `@Store()` decorator to register the `CounterComponent` as a store observer. We also registered the `count` property with the store which means that any changes to the `count` property in the application state will be automatically pushed through to the `count` property of this component.
60 |
61 | Notice also the `dispatch()` method in the template. This method is provided by the `@Store()` decorator and can be used to easily dispatch an action.
62 |
63 | **boot.ts**
64 |
65 | ```js
66 | import {bootstrap} from 'angular2/platform/browser';
67 | import {AppComponent} from './app.component';
68 | import './app.reducer';
69 |
70 | bootstrap(AppComponent);
71 | ```
72 |
73 | In the above example we imported the `app.reducer` as a side-effect only module - that's all we need to do.
74 |
75 | # API
76 |
77 | ## Decorators
78 |
79 | ### @Slice(slice: string, initialState?: any)
80 |
81 | The `@Slice` decorator is used to specify what slice of the state tree action reducers receive. This allows your action reducers to be passed the data for the slice that they manipulate. The `@Slice` decorator can also be passed the default value for the specified state slice as the second argument.
82 |
83 | ```js
84 | @Slice('count', 0)
85 | @Reducer('increment')
86 | class MyActionReducers {
87 | increment(count) { return count + 1; } // increment is passed the value of the count slice
88 | }
89 | ```
90 |
91 | In the above example we use the `@Slice` decorator to specify that the `MyActionReducers` class operates on the `count` slice of the state tree. We also used the second argument to specify that `count` should be initialised with a default value of `0`.
92 |
93 | **Advanced Usage**
94 |
95 | `@Slice` can be used with classes and methods. This means a single class of action reducers can work against multiple slices of the state tree. However, this approach is not advised. Action reducer classes should operate on a single slice of the state tree.
96 |
97 | ```js
98 | @Slice('count', 0)
99 | @Reducer('increment', 'addItem')
100 | class MyActionReducers {
101 | increment(count) { return count + 1; } // Initial state is 0 for the count slice
102 |
103 | @Slice('items')
104 | addItem(items) { return [...items, item]; } // Initial state is [] for the items slice
105 | }
106 | ```
107 |
108 | Above we have extended the previous example and used the `@Slice` decorator to specify that the `addItems` reducer should receive the value of the `items` slice from the state tree. This is an example of a single class operating on multiple state slices.
109 |
110 | ### @Reducer([actionReducer1, actionReducer2, ...])
111 |
112 | The `Reducer()` decorator is used to identify a root reducer, however it can also be used as a convenience method for setting multiple action reducers in a single call.
113 |
114 | The `@Reducer()` decorator registers a new root reducer if the class you are decorating contains a reducer method.
115 |
116 | **Root Reducer**
117 | ```js
118 | @Reducer()
119 | class MyRootReducer implements IReducer {
120 | reducer(state = initialState, action) {
121 | ...
122 | }
123 | }
124 | ```
125 |
126 | In the above example, the `MyRootReducer` class contains a `reducer` method, this means that this `class` will be registered as the root reducer - this will overwrite the default root reducer and prevent action reducers from working out of the box.
127 |
128 | **Action Reducers**
129 | We can mark individual methods as action reducers.
130 | ```js
131 | class MyReducers {
132 | @Reducer() add(state): { return { count: state.count + 1; } }
133 | @Reducer() remove(state): { return { count: state.count - 1; } }
134 | }
135 | ```
136 |
137 | Alternatively we can mark multiple methods at once using `@Reducer()`:
138 |
139 | ```js
140 | @Reducer('add', 'remove')
141 | class MyReducers {
142 | add(state): { return { count: state.count + 1; } }
143 | remove(state): { return { count: state.count - 1; } }
144 | }
145 | ```
146 |
147 | ### @Store([stateProp1, stateProp2, ...])
148 |
149 | The `@Store()` decorator is used to identify a store component. A store component is automatically subscribed to the application store and receives registered state updates when the store is updated.
150 |
151 | ```js
152 | @Store()
153 | class TodoListComponent {
154 | ...
155 | }
156 | ```
157 |
158 | You'll also need to declare which properties are updated by the application store. You can do that by explicitly decorating each property with the `@State()` decorator, or you can declare these properties when you declare the `@Store()` decorator:
159 |
160 | ```js
161 | @Store('todos')
162 | class TodoListComponent {
163 | ...
164 | }
165 | ```
166 |
167 | In the above example we are declaring that the `todos` property of the
168 | `TodoListComponent` should be automatically updated whenever the application store's `todos` property is changed.
169 |
170 | ### @State()
171 |
172 | The `@State()` decorator is used to identify a state property in the application store. Identifying state properties allow the property to be automatically updated when the application store's property changes.
173 |
174 | ```js
175 | @Store()
176 | class TodoListComponent {
177 | @State() todos:Todo[] = [];
178 | ...
179 | }
180 | ```
181 |
182 | In the above example we are declaring that the `todos` property of the
183 | `TodoListComponent` should be automatically updated whenever the application store's `todos` property is changed. Please also refer to the `@Store()` equivalent.
184 |
185 | ### @InitialState(state: any)
186 |
187 | The `@InitialState` decorator is used for setting the initial state of the
188 | application store.
189 |
190 | This decorator accepts a single object `state` that describes the initial state of the application.
191 |
192 | ```js
193 | @InitialState({
194 | count: 0
195 | })
196 | ```
197 |
198 | # License
199 |
200 | MIT
201 |
--------------------------------------------------------------------------------
/src/initial-state.decorator.ts:
--------------------------------------------------------------------------------
1 | import {setInitialState} from './reducer.decorator';
2 |
3 | export function InitialState(initialState: any): Function {
4 | setInitialState(initialState);
5 | return function(target: any): void {}
6 | }
7 |
--------------------------------------------------------------------------------
/src/reducer.decorator.ts:
--------------------------------------------------------------------------------
1 | import {Reducer} from 'redux';
2 |
3 | let rootReducer: Reducer;
4 | let initialState: any = {};
5 |
6 | //------------------------------------------------------------------------------
7 | // Initial state
8 | //------------------------------------------------------------------------------
9 |
10 | export function setInitialState(state: any): any {
11 | initialState = state;
12 | }
13 |
14 | //------------------------------------------------------------------------------
15 | // Root reducer
16 | //------------------------------------------------------------------------------
17 |
18 | export function setReducer(reducer: Reducer): void {
19 | rootReducer = reducer;
20 | }
21 |
22 | export function getReducer(): Reducer {
23 | return rootReducer || DefaultReducer.prototype.reducer;
24 | }
25 |
26 | export interface RootReducer {
27 | prototype?: any;// I shouldn't need this
28 | reducer(state: any, action: any): any;
29 | }
30 |
31 | export class DefaultReducer implements RootReducer {
32 | reducer(state: any = initialState, action: any): any {
33 |
34 | let matchingActionTypeOnly = (actionReducer) => {
35 | return actionReducer.type === action.type;
36 | };
37 |
38 | if (action.type === '@@redux/INIT') {
39 | return actionReducers.reduce((nextState, reducer) => {
40 | if (!reducer.owner.getInitialState) {
41 | return state;
42 | }
43 | const initialState = reducer.owner.getInitialState(reducer.type);
44 | const slice = reducer.owner.getSlice ? reducer.owner.getSlice(reducer.methodName) : null;
45 | if (initialState !== undefined && slice) {
46 | nextState[slice] = initialState;
47 | }
48 | return nextState;
49 | }, initialState);
50 | }
51 |
52 | let filteredActionReducers = actionReducers.filter(matchingActionTypeOnly);
53 |
54 | if (!filteredActionReducers.length) {
55 | return state;
56 | }
57 |
58 | let createNextState = (state, {owner, methodName}) => {
59 | const reducer = owner[methodName];
60 | let slice, nextState;
61 | slice = owner.getSlice ? owner.getSlice(methodName) : null;
62 | if (slice) {
63 | const inputState = state.hasOwnProperty(slice) ? state[slice] : undefined;
64 | nextState = state;
65 | nextState[slice] = reducer(inputState, ...action.data);
66 | }
67 | else {
68 | nextState = reducer(state, ...action.data);
69 | }
70 | return Object.assign(state, nextState);
71 | }
72 |
73 | return filteredActionReducers.reduce(createNextState, state);
74 | }
75 | }
76 |
77 | //------------------------------------------------------------------------------
78 | // Action reducers
79 | //------------------------------------------------------------------------------
80 |
81 | interface ActionReducer {
82 | type: string;
83 | owner: any;
84 | methodName: string;
85 | }
86 |
87 | let actionReducers: ActionReducer[] = [];
88 |
89 | export function addActionReducer(type: string, owner: Function, methodName: string): void {
90 | actionReducers.push({type, owner, methodName});
91 | }
92 |
93 | export function getActionReducers(): ActionReducer[] {
94 | return actionReducers;
95 | }
96 |
97 | export function removeActionReducers(): void {
98 | actionReducers = [];
99 | }
100 |
101 | //------------------------------------------------------------------------------
102 | // Decorator
103 | //------------------------------------------------------------------------------
104 |
105 | let handleActionReducer = function(owner: any, methodName: string): void {
106 | addActionReducer(methodName, owner, methodName);
107 | }
108 |
109 | let handleRootReducer = function(owner: any, methodNames: string[]): void {
110 | if (owner.prototype.reducer) {
111 | rootReducer = owner.prototype.reducer;
112 | }
113 | let mapMethodNames = (methodName) => { return { type: methodName, owner: owner.prototype, methodName } };
114 | actionReducers = actionReducers.concat(methodNames.map(mapMethodNames));
115 | }
116 |
117 | export function Reducer(...methodNames: string[]): Function {
118 | return function(owner: any, methodName?: string): void {
119 | if (!owner.prototype) {
120 | handleActionReducer(owner, methodName);
121 | return;
122 | }
123 | handleRootReducer(owner, methodNames);
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/src/redux-decorators.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import {Reducer, RootReducer} from './reducer.decorator';
4 | import {State} from './state.decorator';
5 | import {IStore, Store} from './store.decorator';
6 | import {InitialState} from './initial-state.decorator';
7 | export {Slice} from './slice.decorator';
8 |
9 | // reducer decorator module exports
10 | export {RootReducer};
11 | export {Reducer};
12 |
13 | // state decorator module exports
14 | export {State};
15 |
16 | // store decorator module exports
17 | export {Store};
18 | export {IStore};
19 |
20 | // initial-state decorator module exports
21 | export {InitialState};
22 |
--------------------------------------------------------------------------------
/src/slice-state.helper.ts:
--------------------------------------------------------------------------------
1 | const addGetInitialStateMethod = (target) => {
2 | if (target.getInitialState) {
3 | return;
4 | }
5 | target.getInitialState = (actionType) => {
6 | if (!target.initialState) {
7 | return undefined;
8 | }
9 | if (target.initialState.hasOwnProperty(actionType)) {
10 | return target.initialState[actionType];
11 | }
12 | if (target.initialState.hasOwnProperty('default')) {
13 | return target.initialState.default;
14 | }
15 | return undefined;
16 | }
17 | };
18 |
19 | const addInitialStateProperty = (target) => {
20 | if (target.hasOwnProperty('initialState')) {
21 | return;
22 | }
23 | target.initialState = {};
24 | };
25 |
26 | export function setSliceState(initialState: any, target: any, methodName?: string): void {
27 | const isInstance = !target.prototype;
28 | var targetRef = isInstance ? target : target.prototype;
29 | addInitialStateProperty(targetRef);
30 | addGetInitialStateMethod(targetRef);
31 | methodName = methodName ? methodName : 'default';
32 | targetRef.initialState[methodName] = initialState;
33 | }
34 |
--------------------------------------------------------------------------------
/src/slice.decorator.ts:
--------------------------------------------------------------------------------
1 | import { setSliceState } from './slice-state.helper';
2 |
3 | const addGetSliceMethod = (target) => {
4 | if (target.getSlice) {
5 | return;
6 | }
7 | target.getSlice = (actionType) => {
8 | if (!target.stateSliceAffected) {
9 | return undefined;
10 | }
11 | if (target.stateSliceAffected.hasOwnProperty(actionType)) {
12 | return target.stateSliceAffected[actionType];
13 | }
14 | if (target.stateSliceAffected.hasOwnProperty('default')) {
15 | return target.stateSliceAffected.default;
16 | }
17 | return undefined;
18 | }
19 | };
20 |
21 | const addStateSliceAffectedProperty = (target) => {
22 | if (target.hasOwnProperty('stateSliceAffected')) {
23 | return;
24 | }
25 | target.stateSliceAffected = {};
26 | };
27 |
28 | export function Slice(slice: string, initialState?: any): Function {
29 | return function(target: any, method?: string): void {
30 | const isInstance = !target.prototype;
31 | if (isInstance) {
32 | addStateSliceAffectedProperty(target);
33 | addGetSliceMethod(target);
34 | target.stateSliceAffected[method] = slice;
35 | setSliceState(initialState, target, method);
36 | return;
37 | }
38 | addStateSliceAffectedProperty(target.prototype);
39 | addGetSliceMethod(target.prototype);
40 | target.prototype.stateSliceAffected.default = slice;
41 | setSliceState(initialState, target);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/state.decorator.ts:
--------------------------------------------------------------------------------
1 |
2 | export function State(): Function {
3 | return function(target: any, ...props: string[]): void {
4 | if (target.stateProperties === undefined) {
5 | target.stateProperties = [];
6 | }
7 | if (props.length) {
8 | target.stateProperties = target.stateProperties.concat(props);
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/store.decorator.ts:
--------------------------------------------------------------------------------
1 | import {getStore, generalBinding} from './store.decorator/general.binding';
2 | import {angular2Binding} from './store.decorator/angular2.binding';
3 | export {getStore};
4 |
5 | export interface IStore {
6 | dispatch(action: string);
7 | }
8 |
9 | export function Store(...stateProperties: string[]) {
10 | return function(target: any) {
11 | angular2Binding(generalBinding(target, stateProperties));
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/store.decorator/angular2.binding.ts:
--------------------------------------------------------------------------------
1 | export function angular2Binding(target: any) {
2 | const existingNgOnInit = target.prototype.ngOnInit;
3 | const existingNgOnDestroy = target.prototype.ngOnDestroy;
4 | target.prototype.ngOnInit = function() {
5 | this.storeInit();
6 | !existingNgOnInit || existingNgOnInit.call(this);
7 | }
8 | target.prototype.ngOnDestroy = function() {
9 | this.storeDestroy();
10 | !existingNgOnDestroy || existingNgOnDestroy.call(this);
11 | }
12 | return target;
13 | }
14 |
--------------------------------------------------------------------------------
/src/store.decorator/general.binding.ts:
--------------------------------------------------------------------------------
1 | import {createStore, Store} from 'redux';
2 | import {getReducer} from './../reducer.decorator';
3 |
4 | let appStore;
5 |
6 | export function getStore(): Promise {
7 | if (!appStore) {
8 | appStore = new Promise((resolve) => {
9 | var interval = setInterval(() => {
10 | if (getReducer()) {
11 | clearInterval(interval);
12 | resolve(createStore(getReducer()));
13 | }
14 | });
15 | });
16 | }
17 | return appStore;
18 | }
19 |
20 | export function updateStateProperties(target: any, state: any, properties: string = 'stateProperties'): void {
21 | target[properties].forEach(prop => {
22 | target[prop] = state[prop];
23 | });
24 | }
25 |
26 | export function generalBinding(target: any, stateProperties: string[]): void {
27 |
28 | // Add stateProperties to the target
29 | if (target.prototype.stateProperties === undefined) {
30 | target.prototype.stateProperties = [];
31 | }
32 | target.prototype.stateProperties = target.prototype.stateProperties.concat(stateProperties);
33 |
34 | // Add a dispatch method to the target
35 | target.prototype.dispatch = function(action, ...data) {
36 | this.appStore.dispatch({type: action, data: data});
37 | };
38 |
39 | // Add a generic store update handler
40 | target.prototype.storeUpdateHandler = function() {
41 | updateStateProperties(this, this.appStore.getState());
42 | };
43 |
44 | // Add a generic storeInit method
45 | target.prototype.storeInit = function() {
46 | return this.getStore().then(() => {
47 | this.unsubscribe = this.appStore.subscribe(this.storeUpdateHandler.bind(this));
48 | // Apply the default state to all listeners
49 | this.storeUpdateHandler();
50 | });
51 | }
52 |
53 | // Add a generic storeDestroy method
54 | target.prototype.storeDestroy = function() {
55 | this.unsubscribe();
56 | };
57 |
58 | // Add a get store method
59 | target.prototype.getStore = function() {
60 | if (this.appStore) {
61 | return this.appStore.then ? this.appStore : Promise.resolve(this.appStore);
62 | }
63 | return getStore().then(store => this.appStore = store);
64 | };
65 |
66 | // Add a set store method for testing purposes
67 | target.prototype.setStore = function(store) {
68 | this.appStore = store;
69 | };
70 |
71 | return target;
72 | }
73 |
--------------------------------------------------------------------------------
/test/all-tests.ts:
--------------------------------------------------------------------------------
1 | import './reducer.decorator.spec';
2 | import './store.decorator.spec';
3 | import './store.decorator/general.binding.spec';
4 | import './store.decorator/angular2.binding.spec';
5 | import './slice.decorator.spec';
6 | import './slice.reducer.decorators.spec';
7 | import './slice-state.helper.spec';
8 |
--------------------------------------------------------------------------------
/test/must.ts:
--------------------------------------------------------------------------------
1 | // We should be able to import must without this proxy - need to remove this
2 | export let expect = require('must');
3 |
--------------------------------------------------------------------------------
/test/reducer.decorator.spec.ts:
--------------------------------------------------------------------------------
1 | import 'es6-shim';
2 | import {sinon} from './sinon';
3 | import {expect} from './must';
4 | import {Reducer, DefaultReducer, setReducer, addActionReducer, removeActionReducers, getActionReducers} from '../src/reducer.decorator';
5 | import {getStore} from '../src/store.decorator';
6 |
7 | describe('@Reducer', function() {
8 |
9 | afterEach(function () {
10 | removeActionReducers();
11 | })
12 |
13 | describe('DefaultReducer', function() {
14 |
15 | it('must pass data through to reducer methods', function() {
16 | let state = {}, values = [1, 'test'], type = 'add';
17 | let actionReducer = { add: () => {} };
18 | let defaultReducer = new DefaultReducer();
19 | let spy = sinon.spy(actionReducer, 'add');
20 | addActionReducer(type, actionReducer, 'add');
21 | defaultReducer.reducer(state, {type: type, data: values});
22 | expect(spy.calledWithExactly(state, values[0], values[1])).to.be.true();
23 | })
24 |
25 | });
26 |
27 | describe('root reducer', function() {
28 |
29 | it('must pass state and action when calling reducer method', function() {
30 |
31 | @Reducer()
32 | class RootReducer { reducer(state, action) {} }
33 | let spy = sinon.spy(RootReducer.prototype, 'reducer');
34 | getStore().then(function(store) {
35 | store.dispatch('action', 1, 2, 3);
36 | expect(spy.calledWithExactly('action', [1, 2, 3]))
37 | });
38 | setReducer(null);
39 |
40 | });
41 |
42 | });
43 |
44 | describe('action reducers (class)', function() {
45 |
46 | it('must allow multiple action reducers to be registered with @Reducer', function() {
47 |
48 | @Reducer('one', 'two', 'three')
49 | class ActionReducers { one() {} two() {} three() {} }
50 | let [one, two, three] = getActionReducers();
51 |
52 | expect(one).property('type', 'one');
53 | expect(one).property('owner', ActionReducers.prototype);
54 | expect(one).property('methodName', 'one');
55 |
56 | expect(two).property('type', 'two');
57 | expect(two).property('owner', ActionReducers.prototype);
58 | expect(two).property('methodName', 'two');
59 |
60 | expect(three).property('type', 'three');
61 | expect(three).property('owner', ActionReducers.prototype);
62 | expect(three).property('methodName', 'three');
63 |
64 | });
65 |
66 | })
67 |
68 | describe('action reducers (function)', function() {
69 |
70 | it('must allow multiple action reducers to be registered with @Reducer', function() {
71 |
72 | class ActionReducers {
73 | @Reducer() one() {}
74 | @Reducer() two() {}
75 | three() {}
76 | }
77 | let [one, two, three] = getActionReducers();
78 |
79 | expect(one).property('type', 'one');
80 | expect(one).property('owner', ActionReducers.prototype);
81 | expect(one).property('methodName', 'one');
82 |
83 | expect(two).property('type', 'two');
84 | expect(two).property('owner', ActionReducers.prototype);
85 | expect(two).property('methodName', 'two');
86 |
87 | expect(three).be.undefined();
88 |
89 | });
90 |
91 | })
92 |
93 | });
94 |
--------------------------------------------------------------------------------
/test/sinon.ts:
--------------------------------------------------------------------------------
1 | // We should be able to import sinon without this proxy - need to remove this
2 | export let sinon = require('sinon');
3 |
--------------------------------------------------------------------------------
/test/slice-state.helper.spec.ts:
--------------------------------------------------------------------------------
1 | import 'es6-shim';
2 | import { expect } from './must';
3 | import { setSliceState } from '../src/slice-state.helper';
4 |
5 | describe('slice state helper', function() {
6 |
7 | it('must add initial state information to a class', function() {
8 | class Component { }
9 | setSliceState({count: 1}, Component);
10 | expect(Component.prototype.initialState.default).to.eql({ count: 1 });
11 | });
12 |
13 | it('must add initial state information to a method', function() {
14 | class Component {
15 | method() { }
16 | }
17 | setSliceState({ count: 1 }, Component, 'method');
18 | const instance = new Component();
19 | expect(instance.initialState.method).to.eql({ count: 1 });
20 | });
21 |
22 | it('must support both class and method slice', function() {
23 | class Component {
24 | method1() { }
25 | method2() { }
26 | }
27 | setSliceState({ count: 0 }, Component);
28 | setSliceState({ count1: 1 }, Component, 'method1');
29 | const instance = new Component();
30 | expect(Component.prototype.initialState.default).to.eql({ count: 0 });
31 | expect(instance.initialState.method1).to.eql({ count1: 1 });
32 | expect(instance.initialState.default).to.eql({ count: 0 });
33 | });
34 |
35 | });
36 |
--------------------------------------------------------------------------------
/test/slice.decorator.spec.ts:
--------------------------------------------------------------------------------
1 | import 'es6-shim';
2 | import {expect} from './must';
3 | import {Slice} from '../src/slice.decorator';
4 |
5 | describe('@Slice', function() {
6 |
7 | it('must add slice information to a class', function() {
8 | @Slice('class-slice')
9 | class Component {}
10 | expect(Component.prototype.stateSliceAffected.default).to.equal('class-slice');
11 | });
12 |
13 | it('must add slice information to a method', function() {
14 | class Component {
15 | @Slice('method-slice')
16 | method() {}
17 | }
18 | const instance = new Component();
19 | expect(instance.stateSliceAffected.method).to.equal('method-slice');
20 | });
21 |
22 | it('must support both class and method slice', function() {
23 | @Slice('class-slice')
24 | class Component {
25 | @Slice('method-slice')
26 | method1() {}
27 | method2() {}
28 | }
29 | const instance = new Component();
30 | expect(Component.prototype.stateSliceAffected.default).to.equal('class-slice');
31 | expect(instance.stateSliceAffected.method1).to.equal('method-slice');
32 | expect(instance.stateSliceAffected.default).to.equal('class-slice');
33 | });
34 |
35 | it('must allow an initial value to be passed as the 2nd argument (class)', () => {
36 |
37 | @Slice('default', 0)
38 | class Component {}
39 | expect(Component.prototype.stateSliceAffected.default).to.equal('default');
40 | expect(Component.prototype.getInitialState('default')).to.equal(0);
41 |
42 | });
43 |
44 | it('must allow an initial value to be passed as the 2nd argument (method)', () => {
45 |
46 | class Component {
47 | @Slice('default', 0)
48 | method() {}
49 | }
50 | expect(Component.prototype.stateSliceAffected['method']).to.equal('default');
51 | expect(Component.prototype.getInitialState('method')).to.equal(0);
52 |
53 | });
54 |
55 | });
56 |
--------------------------------------------------------------------------------
/test/slice.reducer.decorators.spec.ts:
--------------------------------------------------------------------------------
1 | import 'es6-shim';
2 | import {expect} from './must';
3 | import {Slice} from '../src/slice.decorator';
4 | import {Reducer, DefaultReducer, removeActionReducers} from '../src/reducer.decorator';
5 |
6 | describe('@Slice and @Reducer together', function() {
7 |
8 | beforeEach(() => removeActionReducers());
9 |
10 | it('must add slice information to a class', function() {
11 | @Slice('count')
12 | @Reducer('add')
13 | class Component {
14 | add(count) { return count + 1; }
15 | }
16 | let state = {count: 1}, type = 'add';
17 | let defaultReducer = new DefaultReducer();
18 | let nextState = defaultReducer.reducer(state, {type: type});
19 | expect(nextState).property('count', 2);
20 | });
21 |
22 | it('must use default value when undefined is passed', function() {
23 | @Slice('count')
24 | @Reducer('add')
25 | class Component {
26 | add(count = 0) { return count + 1; }
27 | }
28 | let state = {}, type = 'add';
29 | let defaultReducer = new DefaultReducer();
30 | let nextState = defaultReducer.reducer(state, {type: type});
31 | expect(nextState).property('count', 1);
32 | });
33 |
34 | });
35 |
--------------------------------------------------------------------------------
/test/store.decorator.spec.ts:
--------------------------------------------------------------------------------
1 | import 'es6-shim';
2 | import {expect} from './must';
3 | import {getStore, createStore, Store} from '../src/store.decorator';
4 | import {updateStateProperties} from '../src/store.decorator/general.binding';
5 | import {setInitialState} from '../src/reducer.decorator';
6 |
7 | describe('@Store', function() {
8 |
9 | it('must allow multiple state properties to be bound with @State', function() {
10 |
11 | @Store('one', 'two', 'three')
12 | class StoreComponent {}
13 | expect(StoreComponent.prototype.stateProperties).to.contain('one');
14 | expect(StoreComponent.prototype.stateProperties).to.contain('two');
15 | expect(StoreComponent.prototype.stateProperties).to.contain('three');
16 |
17 | });
18 |
19 | it('must update local properties to match state', function() {
20 |
21 | let state = {one: 1, two: 2, three: 3};
22 | @Store('one', 'two', 'three')
23 | class StoreComponent {}
24 | updateStateProperties(StoreComponent.prototype, state)
25 | expect(StoreComponent.prototype.one).to.equal(state.one);
26 | expect(StoreComponent.prototype.two).to.equal(state.two);
27 | expect(StoreComponent.prototype.three).to.equal(state.three);
28 |
29 | });
30 |
31 | });
32 |
--------------------------------------------------------------------------------
/test/store.decorator/angular2.binding.spec.ts:
--------------------------------------------------------------------------------
1 | import 'es6-shim';
2 | import {sinon} from './../sinon';
3 | import {expect} from './../must';
4 | import {angular2Binding} from '../../src/store.decorator/angular2.binding';
5 |
6 | describe('angular2Binding', function() {
7 |
8 | describe('ngOnInut', function() {
9 |
10 | it('must add ngOnInit method to the target', function() {
11 | let target = function() {};
12 | let output = angular2Binding(target, {});
13 | expect(output.prototype.ngOnInit).a.function();
14 | });
15 |
16 | it('must call storeInit method when called', function() {
17 | let target = function() {};
18 | target.prototype.storeInit = sinon.spy();
19 | let output = angular2Binding(target, {});
20 | let instance = new output()
21 | instance.ngOnInit();
22 | expect(instance.storeInit.calledOnce).to.be.true();
23 | });
24 |
25 | it('must call the existing ngOnInit method when called', function() {
26 | let target = function() {};
27 | let existingNgOnInit = sinon.spy();
28 | target.prototype.storeInit = sinon.stub();
29 | target.prototype.ngOnInit = existingNgOnInit;
30 | let output = angular2Binding(target, {});
31 | let instance = new output()
32 | instance.ngOnInit();
33 | expect(existingNgOnInit.calledOnce).to.be.true();
34 | });
35 |
36 | it('must call the existing ngOnInit with the correct context', function() {
37 | let target = function() { this.called = false; };
38 | let existingNgOnInit = function() { this.called = true; };
39 | target.prototype.storeInit = sinon.stub();
40 | target.prototype.ngOnInit = existingNgOnInit;
41 | let output = angular2Binding(target, {});
42 | let instance = new output()
43 | instance.ngOnInit();
44 | expect(instance.called).to.be.true();
45 | });
46 |
47 | });
48 |
49 | describe('ngOnDestroy', function() {
50 |
51 | it('must add ngOnDestroy method to the target', function() {
52 | let target = function() {};
53 | let output = angular2Binding(target, {});
54 | expect(output.prototype.ngOnDestroy).a.function();
55 | });
56 |
57 | it('must call storeDestroy method when called', function() {
58 | let target = function() {};
59 | target.prototype.storeDestroy = sinon.spy();
60 | let output = angular2Binding(target, {});
61 | let instance = new output()
62 | instance.ngOnDestroy();
63 | expect(instance.storeDestroy.calledOnce).to.be.true();
64 | });
65 |
66 | it('must call the existing ngOnDestroy method when called', function() {
67 | let target = function() {};
68 | let existingNgOnInit = sinon.spy();
69 | target.prototype.storeDestroy = sinon.stub();
70 | target.prototype.ngOnDestroy = existingNgOnInit;
71 | let output = angular2Binding(target, {});
72 | let instance = new output()
73 | instance.ngOnDestroy();
74 | expect(existingNgOnInit.calledOnce).to.be.true();
75 | });
76 |
77 | it('must call the existing ngOnDestroy with the correct context', function() {
78 | let target = function() { this.called = false; };
79 | let existingNgOnInit = function() { this.called = true; };
80 | target.prototype.storeDestroy = sinon.stub();
81 | target.prototype.ngOnDestroy = existingNgOnInit;
82 | let output = angular2Binding(target, {});
83 | let instance = new output()
84 | instance.ngOnDestroy();
85 | expect(instance.called).to.be.true();
86 | });
87 | });
88 |
89 | });
90 |
--------------------------------------------------------------------------------
/test/store.decorator/general.binding.spec.ts:
--------------------------------------------------------------------------------
1 | import 'es6-shim';
2 | import {sinon} from './../sinon';
3 | import {expect} from './../must';
4 | import {generalBinding} from '../../src/store.decorator/general.binding';
5 |
6 | describe('generalBinding', function() {
7 |
8 | describe('stateProperties', function() {
9 |
10 | it('must add state properties to the target', function() {
11 | let target = function() {};
12 | let props = ['one', 'two'];
13 | let output = generalBinding(target, props);
14 | expect(output.prototype.stateProperties).include('one');
15 | expect(output.prototype.stateProperties).include('two');
16 | });
17 |
18 | });
19 |
20 | describe('dispatch', function() {
21 |
22 | it('must add a dispatch method to the target', function() {
23 | let target = function() {};
24 | let output = generalBinding(target, {});
25 | expect(output.prototype.dispatch).a.function();
26 | });
27 |
28 | });
29 |
30 | describe('storeHandler', function() {
31 |
32 | it('must add a storeUpdateHandler method to the target', function() {
33 | let target = function() {};
34 | let output = generalBinding(target, {});
35 | expect(output.prototype.storeUpdateHandler).to.be.function();
36 | });
37 |
38 | it('must update state properties when called', function() {
39 | let appStore = {getState(){}};
40 | let props = ['one', 'two'];
41 | let state = {one: 1, two: 2};
42 | let stub = sinon.stub(appStore, 'getState').returns(state);
43 | let target = function() { this.appStore = appStore; };
44 | let output = generalBinding(target, ['one', 'two']);
45 | let instance = new output();
46 | instance.storeUpdateHandler();
47 | expect(instance.one).to.equal(1);
48 | expect(instance.two).to.equal(2);
49 | });
50 |
51 | });
52 |
53 | describe('storeInit', function() {
54 |
55 | it('must add a storeInit method to the target', function() {
56 | let target = function() {};
57 | let output = generalBinding(target, {});
58 | expect(output.prototype.storeInit).to.be.function();
59 | });
60 |
61 | it('must subscribe to appStore when called', function(done) {
62 | let target = function() {};
63 | let output = generalBinding(target);
64 | let instance = new output();
65 | let store = {subscribe(){}};
66 | sinon.spy(store, 'subscribe');
67 | // Don't know why spy doesn't work...
68 | // sinon.spy(instance, 'storeUpdateHandler');
69 | sinon.stub(instance, 'storeUpdateHandler');
70 | instance.setStore(store);
71 | instance.storeInit().then(() => {
72 | expect(store.subscribe.calledOnce).to.be.true();
73 | done();
74 | })
75 | .catch(done);
76 | });
77 |
78 | it('must add an unsubscribe method when called', function(done) {
79 | let target = function() {};
80 | let output = generalBinding(target);
81 | let instance = new output();
82 | let store = {subscribe(){}};
83 | sinon.stub(store, 'subscribe').returns(function() {});
84 | // Don't know why spy doesn't work...
85 | // sinon.spy(instance, 'storeUpdateHandler');
86 | sinon.stub(instance, 'storeUpdateHandler');
87 | instance.setStore(store);
88 | instance.storeInit().then(() => {
89 | expect(instance.unsubscribe).to.be.function();
90 | done();
91 | })
92 | .catch(done);
93 | });
94 |
95 |
96 | it('must call the updateStoreHandler when called', function(done) {
97 | let target = function() {};
98 | let output = generalBinding(target);
99 | let instance = new output();
100 | let store = {subscribe(){}};
101 | sinon.spy(store, 'subscribe');
102 | // Don't know why spy doesn't work...
103 | // sinon.spy(instance, 'storeUpdateHandler');
104 | sinon.stub(instance, 'storeUpdateHandler');
105 | instance.setStore(store);
106 | instance.storeInit().then(() => {
107 | expect(instance.storeUpdateHandler.calledOnce).to.be.true();
108 | done();
109 | })
110 | .catch(done);
111 | });
112 |
113 |
114 | });
115 |
116 | describe('storeDestroy', function() {
117 |
118 | it('must add a storeDestroy method to the target', function() {
119 | let target = function() {};
120 | let output = generalBinding(target, {});
121 | expect(output.prototype.storeDestroy).to.be.function();
122 | });
123 |
124 | it('must call unsubscribe on the appStore when called', function() {
125 | let target = function() { this.unsubscribe = () => {} };
126 | let output = generalBinding(target);
127 | let instance = new output();
128 | sinon.spy(instance, 'unsubscribe');
129 | instance.storeDestroy();
130 | expect(instance.unsubscribe.calledOnce).to.be.true();
131 | });
132 |
133 | });
134 |
135 | });
136 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "experimentalDecorators": true,
4 | "target": "es5",
5 | "sourceMap": true
6 | },
7 | "exclude": [
8 | "dist",
9 | "node_modules",
10 | "test",
11 | "typings"
12 | ],
13 | "compileOnSave": false,
14 | "buildOnSave": false
15 | }
16 |
--------------------------------------------------------------------------------
/tsd.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "v4",
3 | "repo": "borisyankov/DefinitelyTyped",
4 | "ref": "master",
5 | "path": "typings",
6 | "bundle": "typings/tsd.d.ts",
7 | "installed": {
8 | "redux/redux.d.ts": {
9 | "commit": "e69fe60f2d6377ea4fae539493997b098f52cad1"
10 | },
11 | "es6-shim/es6-shim.d.ts": {
12 | "commit": "e69fe60f2d6377ea4fae539493997b098f52cad1"
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require('webpack');
2 | var yargs = require('yargs').argv;
3 | var testing = yargs.env === 'test';
4 |
5 | //------------------------------------------------------------------------------
6 | // https://github.com/webpack/webpack/issues/839#issuecomment-177219660
7 | function getExternals() {
8 | var fs = require('fs');
9 | var nodeModules = {};
10 |
11 | fs.readdirSync('node_modules').forEach(function (module) {
12 | if (module !== '.bin') {
13 | nodeModules[module] = true;
14 | }
15 | });
16 |
17 | var nodeModulesTransform = function(context, request, callback) {
18 | // search for a '/' indicating a nested module
19 | var slashIndex = request.indexOf("/");
20 | var rootModuleName;
21 | if (slashIndex == -1) {
22 | rootModuleName = request;
23 | } else {
24 | rootModuleName = request.substr(0, slashIndex);
25 | }
26 |
27 | // Match for root modules that are in our node_modules
28 | if (nodeModules.hasOwnProperty(rootModuleName)) {
29 | callback(null, "commonjs " + request);
30 | } else {
31 | callback();
32 | }
33 | };
34 | return nodeModulesTransform;
35 | }
36 | //------------------------------------------------------------------------------
37 |
38 | var config = {
39 | entry: './src/redux-decorators.ts',
40 | resolve: {
41 | extensions: ['', '.ts', '.js']
42 | },
43 | module: {
44 | loaders: [
45 | { test: /\.ts$/, loader: 'ts-loader' }
46 | ]
47 | },
48 | output: {
49 | filename: './dist/redux-decorators.js',
50 | library: 'ReduxDecorators',
51 | libraryTarget: 'umd'
52 | },
53 | plugins: [
54 | new webpack.SourceMapDevToolPlugin(
55 | '[file].map', null,
56 | "../[resource-path]", "../[resource-path]")
57 | ],
58 | externals: getExternals()
59 | };
60 |
61 | var testingDecorator = function(config) {
62 | var path = require('path');
63 | config.entry = path.resolve('./test/all-tests.ts');
64 | config.target = 'node';
65 | config.output = {};
66 | config.output.filename = './dist/redux-decorators.spec.js';
67 | return config;
68 | };
69 |
70 | if (testing) {
71 | config = testingDecorator(config);
72 | }
73 |
74 | module.exports = config;
75 |
--------------------------------------------------------------------------------