├── .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 | [![Build Status](https://travis-ci.org/KarlPurk/redux-decorators.svg?branch=master)](https://travis-ci.org/KarlPurk/redux-decorators) [![Code Climate](https://codeclimate.com/github/KarlPurk/redux-decorators/badges/gpa.svg)](https://codeclimate.com/github/KarlPurk/redux-decorators) [![npm version](https://badge.fury.io/js/redux-decorators.svg)](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 | --------------------------------------------------------------------------------