├── .babelrc ├── .csscomb.json ├── .csslintrc ├── .editorconfig ├── .eslintrc ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .jscsrc ├── .travis.yml ├── LICENSE.txt ├── README.md ├── changeLog.md ├── client ├── app.js ├── components │ ├── Button │ │ ├── Button.js │ │ ├── Button.less │ │ └── package.json │ └── MarkDown │ │ ├── MarkDown.js │ │ └── package.json ├── containers │ ├── App │ │ ├── App.js │ │ ├── App.less │ │ └── package.json │ ├── DevTools │ │ ├── DevTools.js │ │ └── package.json │ ├── GlobalMenu │ │ ├── GlobalMenu.js │ │ ├── GlobalMenu.less │ │ ├── iconfont-dashboard.svg │ │ ├── iconfont-setting.svg │ │ ├── iconfont-user.svg │ │ └── package.json │ ├── Header │ │ ├── Header.js │ │ ├── Header.less │ │ ├── logo.png │ │ └── package.json │ ├── NotFound │ │ ├── NotFound.js │ │ └── package.json │ └── SignIn │ │ ├── SignIn.js │ │ └── package.json ├── favicon.ico ├── index.html ├── reducers │ ├── index.js │ └── user.js ├── route │ ├── childRoutes.js │ ├── dashboard │ │ ├── actions │ │ │ └── index.js │ │ ├── components │ │ │ ├── Template │ │ │ │ ├── Template.js │ │ │ │ ├── Template.less │ │ │ │ └── package.json │ │ │ └── index.js │ │ ├── containers │ │ │ └── App │ │ │ │ ├── App.js │ │ │ │ ├── App.less │ │ │ │ ├── markdown.md │ │ │ │ └── package.json │ │ ├── helpers │ │ │ └── auth.js │ │ ├── index.js │ │ └── reducers │ │ │ └── index.js │ ├── setting │ │ ├── actions │ │ │ └── index.js │ │ ├── components │ │ │ ├── Template │ │ │ │ ├── Template.js │ │ │ │ ├── Template.less │ │ │ │ └── package.json │ │ │ └── index.js │ │ ├── containers │ │ │ └── App │ │ │ │ ├── App.js │ │ │ │ ├── App.less │ │ │ │ └── package.json │ │ ├── helpers │ │ │ └── auth.js │ │ ├── index.js │ │ └── reducers │ │ │ └── index.js │ └── user │ │ ├── actions │ │ └── index.js │ │ ├── components │ │ ├── Template │ │ │ ├── Template.js │ │ │ ├── Template.less │ │ │ └── package.json │ │ └── index.js │ │ ├── containers │ │ └── App │ │ │ ├── App.js │ │ │ ├── App.less │ │ │ └── package.json │ │ ├── helpers │ │ └── auth.js │ │ ├── index.js │ │ └── reducers │ │ └── index.js ├── routes.js ├── store │ └── configureStore.js └── theme │ ├── package.json │ └── variable.less ├── humans.txt ├── package.json ├── processes.json ├── server ├── api │ └── mock.json ├── remote │ ├── api │ │ └── db.json │ └── remoteServer.js └── server.js └── tasks ├── .eslintrc ├── browserSync.js ├── bundle.js ├── clean.js ├── deploy.js ├── publish.js ├── run.js ├── server.js ├── start.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react", "stage-0", "es2015"], 3 | "plugins": ["transform-class-properties", 4 | "transform-object-assign", 5 | "transform-class-constructor-call", 6 | "transform-decorators-legacy"], 7 | "env": { 8 | "development": { 9 | "plugins": [ 10 | ["react-transform", { 11 | "transforms": [{ 12 | "transform": "react-transform-hmr", 13 | "imports": ["react"], 14 | "locals": ["module"] 15 | }, { 16 | "transform": "react-transform-catch-errors", 17 | "imports": ["react", "redbox-react"] 18 | }] 19 | }] 20 | ] 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.csscomb.json: -------------------------------------------------------------------------------- 1 | { 2 | "always-semicolon": true, 3 | "block-indent": 2, 4 | "color-case": "lower", 5 | "color-shorthand": true, 6 | "eof-newline": true, 7 | "leading-zero": false, 8 | "remove-empty-rulesets": true, 9 | "space-after-colon": 1, 10 | "space-after-combinator": 1, 11 | "space-before-selector-delimiter": 0, 12 | "space-between-declarations": "\n", 13 | "space-after-opening-brace": "\n", 14 | "space-before-closing-brace": "\n", 15 | "space-before-colon": 0, 16 | "space-before-combinator": 1, 17 | "space-before-opening-brace": 1, 18 | "strip-spaces": true, 19 | "unitless-zero": true, 20 | "vendor-prefix-align": true, 21 | "sort-order": [ 22 | [ 23 | "position", 24 | "top", 25 | "right", 26 | "bottom", 27 | "left", 28 | "z-index", 29 | "display", 30 | "float", 31 | "width", 32 | "min-width", 33 | "max-width", 34 | "height", 35 | "min-height", 36 | "max-height", 37 | "-webkit-box-sizing", 38 | "-moz-box-sizing", 39 | "box-sizing", 40 | "-webkit-appearance", 41 | "padding", 42 | "padding-top", 43 | "padding-right", 44 | "padding-bottom", 45 | "padding-left", 46 | "margin", 47 | "margin-top", 48 | "margin-right", 49 | "margin-bottom", 50 | "margin-left", 51 | "overflow", 52 | "overflow-x", 53 | "overflow-y", 54 | "-webkit-overflow-scrolling", 55 | "-ms-overflow-x", 56 | "-ms-overflow-y", 57 | "-ms-overflow-style", 58 | "clip", 59 | "clear", 60 | "font", 61 | "font-family", 62 | "font-size", 63 | "font-style", 64 | "font-weight", 65 | "font-variant", 66 | "font-size-adjust", 67 | "font-stretch", 68 | "font-effect", 69 | "font-emphasize", 70 | "font-emphasize-position", 71 | "font-emphasize-style", 72 | "font-smooth", 73 | "-webkit-hyphens", 74 | "-moz-hyphens", 75 | "hyphens", 76 | "line-height", 77 | "color", 78 | "text-align", 79 | "-webkit-text-align-last", 80 | "-moz-text-align-last", 81 | "-ms-text-align-last", 82 | "text-align-last", 83 | "text-emphasis", 84 | "text-emphasis-color", 85 | "text-emphasis-style", 86 | "text-emphasis-position", 87 | "text-decoration", 88 | "text-indent", 89 | "text-justify", 90 | "text-outline", 91 | "-ms-text-overflow", 92 | "text-overflow", 93 | "text-overflow-ellipsis", 94 | "text-overflow-mode", 95 | "text-shadow", 96 | "text-transform", 97 | "text-wrap", 98 | "-webkit-text-size-adjust", 99 | "-ms-text-size-adjust", 100 | "letter-spacing", 101 | "-ms-word-break", 102 | "word-break", 103 | "word-spacing", 104 | "-ms-word-wrap", 105 | "word-wrap", 106 | "-moz-tab-size", 107 | "-o-tab-size", 108 | "tab-size", 109 | "white-space", 110 | "vertical-align", 111 | "list-style", 112 | "list-style-position", 113 | "list-style-type", 114 | "list-style-image", 115 | "pointer-events", 116 | "-ms-touch-action", 117 | "touch-action", 118 | "cursor", 119 | "visibility", 120 | "zoom", 121 | "flex-direction", 122 | "flex-order", 123 | "flex-pack", 124 | "flex-align", 125 | "table-layout", 126 | "empty-cells", 127 | "caption-side", 128 | "border-spacing", 129 | "border-collapse", 130 | "content", 131 | "quotes", 132 | "counter-reset", 133 | "counter-increment", 134 | "resize", 135 | "-webkit-user-select", 136 | "-moz-user-select", 137 | "-ms-user-select", 138 | "-o-user-select", 139 | "user-select", 140 | "nav-index", 141 | "nav-up", 142 | "nav-right", 143 | "nav-down", 144 | "nav-left", 145 | "background", 146 | "background-color", 147 | "background-image", 148 | "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient", 149 | "filter:progid:DXImageTransform.Microsoft.gradient", 150 | "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader", 151 | "filter", 152 | "background-repeat", 153 | "background-attachment", 154 | "background-position", 155 | "background-position-x", 156 | "background-position-y", 157 | "-webkit-background-clip", 158 | "-moz-background-clip", 159 | "background-clip", 160 | "background-origin", 161 | "-webkit-background-size", 162 | "-moz-background-size", 163 | "-o-background-size", 164 | "background-size", 165 | "border", 166 | "border-color", 167 | "border-style", 168 | "border-width", 169 | "border-top", 170 | "border-top-color", 171 | "border-top-style", 172 | "border-top-width", 173 | "border-right", 174 | "border-right-color", 175 | "border-right-style", 176 | "border-right-width", 177 | "border-bottom", 178 | "border-bottom-color", 179 | "border-bottom-style", 180 | "border-bottom-width", 181 | "border-left", 182 | "border-left-color", 183 | "border-left-style", 184 | "border-left-width", 185 | "border-radius", 186 | "border-top-left-radius", 187 | "border-top-right-radius", 188 | "border-bottom-right-radius", 189 | "border-bottom-left-radius", 190 | "-webkit-border-image", 191 | "-moz-border-image", 192 | "-o-border-image", 193 | "border-image", 194 | "-webkit-border-image-source", 195 | "-moz-border-image-source", 196 | "-o-border-image-source", 197 | "border-image-source", 198 | "-webkit-border-image-slice", 199 | "-moz-border-image-slice", 200 | "-o-border-image-slice", 201 | "border-image-slice", 202 | "-webkit-border-image-width", 203 | "-moz-border-image-width", 204 | "-o-border-image-width", 205 | "border-image-width", 206 | "-webkit-border-image-outset", 207 | "-moz-border-image-outset", 208 | "-o-border-image-outset", 209 | "border-image-outset", 210 | "-webkit-border-image-repeat", 211 | "-moz-border-image-repeat", 212 | "-o-border-image-repeat", 213 | "border-image-repeat", 214 | "outline", 215 | "outline-width", 216 | "outline-style", 217 | "outline-color", 218 | "outline-offset", 219 | "-webkit-box-shadow", 220 | "-moz-box-shadow", 221 | "box-shadow", 222 | "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity", 223 | "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha", 224 | "opacity", 225 | "-ms-interpolation-mode", 226 | "-webkit-transition", 227 | "-moz-transition", 228 | "-ms-transition", 229 | "-o-transition", 230 | "transition", 231 | "-webkit-transition-delay", 232 | "-moz-transition-delay", 233 | "-ms-transition-delay", 234 | "-o-transition-delay", 235 | "transition-delay", 236 | "-webkit-transition-timing-function", 237 | "-moz-transition-timing-function", 238 | "-ms-transition-timing-function", 239 | "-o-transition-timing-function", 240 | "transition-timing-function", 241 | "-webkit-transition-duration", 242 | "-moz-transition-duration", 243 | "-ms-transition-duration", 244 | "-o-transition-duration", 245 | "transition-duration", 246 | "-webkit-transition-property", 247 | "-moz-transition-property", 248 | "-ms-transition-property", 249 | "-o-transition-property", 250 | "transition-property", 251 | "-webkit-transform", 252 | "-moz-transform", 253 | "-ms-transform", 254 | "-o-transform", 255 | "transform", 256 | "-webkit-transform-origin", 257 | "-moz-transform-origin", 258 | "-ms-transform-origin", 259 | "-o-transform-origin", 260 | "transform-origin", 261 | "-webkit-animation", 262 | "-moz-animation", 263 | "-ms-animation", 264 | "-o-animation", 265 | "animation", 266 | "-webkit-animation-name", 267 | "-moz-animation-name", 268 | "-ms-animation-name", 269 | "-o-animation-name", 270 | "animation-name", 271 | "-webkit-animation-duration", 272 | "-moz-animation-duration", 273 | "-ms-animation-duration", 274 | "-o-animation-duration", 275 | "animation-duration", 276 | "-webkit-animation-play-state", 277 | "-moz-animation-play-state", 278 | "-ms-animation-play-state", 279 | "-o-animation-play-state", 280 | "animation-play-state", 281 | "-webkit-animation-timing-function", 282 | "-moz-animation-timing-function", 283 | "-ms-animation-timing-function", 284 | "-o-animation-timing-function", 285 | "animation-timing-function", 286 | "-webkit-animation-delay", 287 | "-moz-animation-delay", 288 | "-ms-animation-delay", 289 | "-o-animation-delay", 290 | "animation-delay", 291 | "-webkit-animation-iteration-count", 292 | "-moz-animation-iteration-count", 293 | "-ms-animation-iteration-count", 294 | "-o-animation-iteration-count", 295 | "animation-iteration-count", 296 | "-webkit-animation-direction", 297 | "-moz-animation-direction", 298 | "-ms-animation-direction", 299 | "-o-animation-direction", 300 | "animation-direction" 301 | ] 302 | ] 303 | } 304 | -------------------------------------------------------------------------------- /.csslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "adjoining-classes": false, 3 | "box-sizing": false, 4 | "box-model": false, 5 | "compatible-vendor-prefixes": false, 6 | "floats": false, 7 | "font-sizes": false, 8 | "gradients": false, 9 | "important": false, 10 | "known-properties": false, 11 | "outline-none": false, 12 | "qualified-headings": false, 13 | "regex-selectors": false, 14 | "shorthand": false, 15 | "text-indent": false, 16 | "unique-headings": false, 17 | "universal-selector": false, 18 | "unqualified-attributes": false 19 | } 20 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true 6 | }, 7 | "globals": { 8 | "__DEV__": true 9 | }, 10 | "rules": { 11 | "no-eval": 1 12 | }, 13 | "plugins": [ 14 | "react" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/build 3 | .*/config 4 | .*/node_modules 5 | .*/gulpfile.js 6 | 7 | [include] 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Automatically normalize line endings for all text-based files 2 | # http://git-scm.com/docs/gitattributes#_end_of_line_conversion 3 | * text=auto 4 | 5 | # For the following file types, normalize line endings to LF on 6 | # checkin and prevent conversion to CRLF when they are checked out 7 | # (this is required in order to prevent newline related issues like, 8 | # for example, after the build script is run) 9 | .* text eol=lf 10 | *.css text eol=lf 11 | *.html text eol=lf 12 | *.jade text eol=lf 13 | *.js text eol=lf 14 | *.json text eol=lf 15 | *.less text eol=lf 16 | *.md text eol=lf 17 | *.sh text eol=lf 18 | *.txt text eol=lf 19 | *.xml text eol=lf 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Read about how to use .gitignore: https://help.github.com/articles/ignoring-files 2 | # project-specific 3 | temp 4 | build 5 | 6 | # OS and IDE 7 | .emacs* 8 | .c9 9 | *.flymake 10 | *~ 11 | .#* 12 | .idea 13 | *.sublime-* 14 | 15 | # bower 16 | bower_components 17 | 18 | # npm 19 | node_modules 20 | npm-debug.log 21 | 22 | # grunt & gulp 23 | .grunt 24 | .gulp 25 | 26 | # gradle 27 | .gradle 28 | .htaccess 29 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "airbnb", 3 | "excludeFiles": ["build/**", "node_modules/**"], 4 | "validateQuoteMarks": null 5 | } 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '5.0.0' 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2016 marchen. 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## React-workflow 2 | 3 | ### Why 4 | ``` 5 | Large SPA application is hard to design, because a lot of things need to consider 6 | The boilerplate had solved the difficulty for you: 7 | 8 | > ES6 9 | > Modular 10 | > Component 11 | > Dynamic routing 12 | > Base64 encoding 13 | > File hash cache 14 | > incremental load 15 | > Compression combined 16 | > Time traveling debugger 17 | > Unidirectional data flow 18 | > ... 19 | ``` 20 | 21 | ### What 22 | > React-workflow is a large SPA 23 | > boilerplate for web development built on top of Facebook's 24 | > [React](https://facebook.github.io/react/) library,use 25 | > [redux](https://github.com/rackt/redux) architecture, 26 | > [react-router]() 27 | > [Node.js](https://nodejs.org/) / [Express](http://expressjs.com/) server. 28 | > Containing modern web development tools such as [Webpack](http://webpack.github.io/), 29 | > [Babel](http://babeljs.io/) and [BrowserSync](http://www.browsersync.io/), 30 | > [nodemon](https://github.com/remy/nodemon) and 31 | > [gh-pages](https://github.com/tschaub/gh-pages) to deploy your site to git branch. 32 | > Helping you make site faster and modern. 33 | > For beginner and professional developer provide the starting point of a professional high-level react boilerplate. 34 | > This is not a isomorphic application (′⌒`). 35 | 36 | ### Getting Started 37 | 38 | Just clone the repo and run : 39 | 40 | > Note: your node version need >= 5.0.0 41 | 42 | ```shell 43 | $ npm install 44 | ... 45 | $ npm start 46 | ``` 47 | 48 | ### Other Commands 49 | 50 | ```shell 51 | $ npm run lint (lint your js files) 52 | $ npm run start (start develop model) 53 | $ npm run publish (copy file and publish your site to git `gh-pages` branch) 54 | $ npm run deploy (clean bundle server and deploy your application) 55 | ... 56 | ``` 57 | 58 | 59 | ### Directory Layout 60 | 61 | ``` 62 | . 63 | ├── /node_modules/ # 3rd-party libraries and utilities 64 | ├── /build/ # build files 65 | ├── /client/ # The source code of the application for client 66 | ├── /server/ # The source code of express server 67 | ├── /tasks/ # Build automation scripts and utilities 68 | │ ├── /bundle.js # Bundles the web resources into package(s) through Webpack 69 | │ ├── /clean.js # Cleans up the output (build) folder 70 | │ ├── /webpack.config.js # Webpack configuration for application bundles 71 | │ ├── /server.js # Launches the Node.js/Express web server 72 | │ └── /deploy.js # bundle and deploy build files to git branch 73 | │ └── /publish.js # deploy build files to git branch 74 | │ └── /browserSync.js # browserSync tools and webpack middleware 75 | │ └── /start.js # Launches the development web server with "live reload" 76 | │── package.json # The list of 3rd party libraries and utilities 77 | └── processes.json # production settings for PM2 78 | ``` 79 | 80 | 81 | ### Learn More 82 | 83 | * [React.js Questions on StackOverflow](http://stackoverflow.com/questions/tagged/reactjs) 84 | * [React.js Discussion Board](https://discuss.reactjs.org/) 85 | * [Getting Started with React.js](http://facebook.github.io/react/) 86 | * [Flux Architecture for Building User Interfaces](http://facebook.github.io/flux/) 87 | * [Flow - A static type checker for JavaScript](http://flowtype.org/) 88 | * [The Future of React](https://github.com/reactjs/react-future) 89 | * [Learn ES6](https://babeljs.io/docs/learn-es6/) 90 | * [ES6 Features](https://github.com/lukehoban/es6features#readme) 91 | * [Css Modules](https://github.com/css-modules/css-modules) 92 | 93 | ### Make Better 94 | 95 | * if you find bug or have new feature requirements, please let me know 96 | * welcome PR 97 | 98 | ### Support 99 | 100 | * Email:844033231@qq.com 101 | 102 | ### License 103 | 104 | Copyright © 2014-2015 marchen. This source code is licensed under the MIT 105 | license found in the [LICENSE.txt](https://github.com/chen844033231/react-workflow/blob/master/LICENSE.txt) 106 | file. The documentation to the project is licensed under the 107 | [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/) license. 108 | -------------------------------------------------------------------------------- /changeLog.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | # 版本1.1.2 (2016/12/21) 3 | * 去除constructor和无用代码 4 | 5 | # 版本1.1.1 (2016/4/22) 6 | * 本次更新目的,去除多余插件,框架,本项目突出react,redux框架的执行,其他不参与 7 | * 去除jest测试框架,不强制测试框架 8 | * 精简npm包,去除无用npm包 9 | 10 | # 版本1.1.0 (2016/3/7) 11 | * 更新依赖,修复细节问题 12 | 13 | # 版本1.0.8 (2016/2/4) 14 | * html-webpack-plugin缓存问题修复,可以热修改less(sass,css)文件 15 | * MarkDown组件完成 16 | * webpack添加搜索路径(client/component),可以全局引入这个路径下的组件 17 | 18 | # 版本1.0.8-pre (2016/1/19) 19 | * 将redux-router替换成redux-simple-router 20 | 21 | # 版本1.0.7 (2016/1/19) 22 | * 去除多余的static静态声明 23 | * 升级版本依赖 24 | 25 | # 版本1.0.6 (2015/12/30) 26 | * 去除多余的doc捆绑操作 27 | 28 | # 版本1.0.5 (2015/12/21) 29 | * 切换主题为通过cms模板样式 30 | 31 | # 版本1.0.4 (2015/12/19) 32 | * 修复bundle会在console里面打印2次的问题 33 | * 开发的时候使用style-loader,而发布的时候单独提取css文件 34 | 35 | # 版本1.0.3 (2015/12/14) 36 | * 增量加载reducers 37 | * 解决热刷新reducers问题(考虑增量加载的情况) 38 | 39 | # 版本1.0.2 (2015/12/11) 40 | * 升级redux-devtools到3.0.0 41 | * 启用react-transform插件来热刷新react组件 42 | 43 | # 版本1.0.1 (2015/12/9) 44 | * 添加eslint和csslint代码风格检测工具 45 | * 升级代码依赖 46 | 47 | # 版本1.0.0(-_-) (2015/12/8) 48 | * 添加greenkeeper来保持最新的npm包依赖 49 | 50 | # 版本0.0.9(-_-) (2015/12/6) 51 | * 修复html发布的hash问题,采用`html-webpack-plugin`来动态产生html的`link`和`script`标签. 52 | * 新增发布功能,命令 `npm run deploy`, 这个命令会hash编译文件到build目录,然后把里面的代码发布到本仓库的`production`分支.(可在`tasks/publish.js`里面配置) 53 | 54 | 55 | 56 | 57 | 58 | --------------------------------------------------- 59 | # 已知问题 60 | * redux-router的activeStyle没有用,初始有用,但是点击后失效,待解决(redux connect连接的组件需要传入router参数,否则会失效,尤其是导航) 61 | * 目前只能热刷新reducers的改变,不能热刷新react组件的改变.(因为react-transform还不支持babel6,等待支持babel6,就替换上去支持组件的热替换了)(版本1.0.3支持) 62 | * 还不支持hash发布.(文件version)(版本1.0.1支持) 63 | * 没有采用同构,而是用的html文件.(打算后面动态产生html文件,从而采用非覆盖式发布代码)(已经动态产生index.html) 64 | * 由于采用了style-loader,发现在chrome浏览器中,大的图片显示不出来.(bug)(压缩发布后问题解决)(chrome浏览器bug) 65 | * 由于采用的style-loader,会在加载dom元素后,在去加载样式,从而样式有延迟.不过只是第一次加载会有延迟.(代码压缩后,解析速度变快,问题解决) 66 | * 目前增量热刷新需要手动添加module.hot判断语句,不是太方便 67 | * 采用动态创建的index.html文件,在每次捆绑时都会发生变化,导致bs不能动态刷新样式文件.所以采用开发的时候样式写在style里面.发布的时候,样式单独打包. 68 | 69 | 70 | 71 | --------------------------------------------------- 72 | # todo 73 | * 等待gh-pages包可以选取数组形式的文件,从而可以指定发布的文件夹下的东西和文件(用copy任务代替) 74 | * 等待react-router升级,目前采用history@1.13.1 75 | * 等待redux-router修复初始化警告bug 76 | * 做mock数据的处理(采用json-server来mock数据,采用node-proxy来代理远端的API) 77 | * 编写通用组件 (developing) 78 | * demo网站样式从数据赢家迁移到通用网站样式(已完成) 79 | 80 | 81 | 82 | 83 | --------------------------------------------------- 84 | # 改变 85 | 1. 原先采用提取出css文件,合并为一个文件的样式架构,更改为动态应用样式文件.也就是css-loader 86 | 87 | 原因: 如果是比较小的项目,可以把整个网站的样式合并成一个.但是对于比较大型的项目,需要增量加载组件和样式文件.不然初次加载会比较慢. 88 | 89 | 而在采用css-inline还是sass,less这类预编译语言的时候,考虑到css-inline还不是太成熟.而且更改样式不是太方便.所以采用sass,less这类来写.(不过未来的趋势是采用css-inline来做,因为可以做到css module.这样样式不会被其他类覆盖). 所以后期打算迁移成css module这样的方式. 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------- 98 | # 编程经验 99 | 1. 如果用箭头函数来写回调方法,需要先让组件有constructor(props). 100 | ```js 101 | class Button extends Component { 102 | constructor(props) { 103 | super(props) 104 | } 105 | 106 | // 需要首先添加constructor函数 107 | clickHandle = () => { 108 | 109 | } 110 | } 111 | 112 | 2. 如果用上面的,回报出错误''this' is not allowed before super()'. 解决方法是因为babel 6插件`babel-plugin-transform-class-constructor-call`没有安装. 113 | ``` 114 | -------------------------------------------------------------------------------- /client/app.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import normalize from 'normalize.css' 4 | import {Provider} from 'react-redux' 5 | import {Router, browserHistory as history} from 'react-router' 6 | import routes from './routes' 7 | import configureStore from './store/configureStore' 8 | 9 | const store = window.store = configureStore() 10 | 11 | if(__DEV__) { 12 | const DevTools = require('./containers/DevTools').DevTools 13 | ReactDOM.render( 14 | 15 |
16 | 17 | 18 |
19 |
, 20 | document.getElementById('app')) 21 | } else { 22 | ReactDOM.render( 23 | 24 | 25 | , 26 | document.getElementById('app')) 27 | } 28 | -------------------------------------------------------------------------------- /client/components/Button/Button.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import styles from './Button.less' 3 | 4 | class Button extends Component { 5 | 6 | render() { 7 | return ( 8 |
9 | buttons 10 |
11 | ) 12 | } 13 | 14 | } 15 | 16 | export default Button 17 | -------------------------------------------------------------------------------- /client/components/Button/Button.less: -------------------------------------------------------------------------------- 1 | @import '../../theme/variable.less'; 2 | 3 | .@{namespace}-button-base { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /client/components/Button/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Button", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Button.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/components/MarkDown/MarkDown.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import marked from 'marked' 3 | import highlightCss from 'highlight.js/styles/xcode.css' 4 | 5 | marked.setOptions({ 6 | highlight: function (code) { 7 | return require('highlight.js').highlightAuto(code).value 8 | } 9 | }) 10 | 11 | class MarkDown extends Component { 12 | 13 | static propTypes = { 14 | option: PropTypes.object, 15 | children: PropTypes.string, 16 | }; 17 | 18 | static defaultProps = { 19 | option: { 20 | renderer: new marked.Renderer(), 21 | gfm: true, 22 | tables: true, 23 | breaks: false, 24 | pedantic: false, 25 | sanitize: true, 26 | smartLists: true, 27 | smartypants: false 28 | }, 29 | children: '', 30 | }; 31 | 32 | rawMarkup() { 33 | return { 34 | __html: marked(this.props.children) 35 | } 36 | } 37 | 38 | render() { 39 | return ( 40 |
41 |
42 | ) 43 | } 44 | 45 | } 46 | 47 | export default MarkDown 48 | -------------------------------------------------------------------------------- /client/components/MarkDown/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MarkDown", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./MarkDown.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/containers/App/App.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import {connect} from 'react-redux' 3 | import styles from './App.less' 4 | import Header from '../Header' 5 | import GlobalMenu from '../GlobalMenu' 6 | 7 | class App extends Component { 8 | 9 | render() { 10 | return( 11 |
12 |
13 | 14 |
15 | {this.props.children} 16 |
17 |
18 | ) 19 | } 20 | 21 | } 22 | 23 | export default App 24 | -------------------------------------------------------------------------------- /client/containers/App/App.less: -------------------------------------------------------------------------------- 1 | @import '../../theme/variable.less'; 2 | html,body { 3 | width: 100%; 4 | height: 100%; 5 | margin: 0; 6 | font-family: "Helvetica Neue", Helvetica, STHeiTi, sans-serif; 7 | transform: translate3d(0, 0, 0); 8 | font-style: 16px; 9 | background-color: @site-bg-color; 10 | } 11 | 12 | // 消除Link组件点击后颜色,去除下划线 13 | a { 14 | text-decoration: none; 15 | } 16 | a:link { 17 | color: blue; 18 | } 19 | a:visited { 20 | color: blue; 21 | } 22 | a:hover { 23 | color: blue; 24 | } 25 | a:active { 26 | color: blue; 27 | } 28 | 29 | ul { 30 | list-style: none; 31 | margin: 0; 32 | padding: 0; 33 | } 34 | 35 | .App { 36 | height: 100%; 37 | .App-children { 38 | box-sizing: border-box; 39 | position: fixed; 40 | width: 100%; 41 | height: 100%; 42 | padding-left: 50px; 43 | z-index: -1; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /client/containers/App/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./App.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/containers/DevTools/DevTools.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import LogMonitor from 'redux-devtools-log-monitor' 3 | import DockMonitor from 'redux-devtools-dock-monitor' 4 | import {createDevTools, persistState} from 'redux-devtools' 5 | 6 | const DevTools = createDevTools( 7 | 11 | 12 | 13 | ) 14 | 15 | function getDebugSessionKey() { 16 | // You can write custom logic here! 17 | // By default we try to read the key from ?debug_session= in the address bar 18 | const matches = window.location.href.match(/[?&]debug_session=([^&]+)\b/) 19 | return (matches && matches.length > 0)? matches[1] : null 20 | } 21 | 22 | exports.DevTools = DevTools // DevTools ui component 23 | exports.persistState = persistState 24 | exports.getDebugSessionKey = getDebugSessionKey 25 | -------------------------------------------------------------------------------- /client/containers/DevTools/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "DevTools", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./DevTools.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/containers/GlobalMenu/GlobalMenu.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes, Component} from 'react' 2 | import {Link, IndexLink} from 'react-router' 3 | import style from './GlobalMenu.less' 4 | 5 | class GlobalMenu extends Component { 6 | 7 | render() { 8 | 9 | const {dispatch, user} = this.props 10 | 11 | return( 12 | 19 | ) 20 | } 21 | } 22 | 23 | export default GlobalMenu 24 | -------------------------------------------------------------------------------- /client/containers/GlobalMenu/GlobalMenu.less: -------------------------------------------------------------------------------- 1 | @import '../../theme/variable.less'; 2 | 3 | .GlobalMenu { 4 | position: fixed; 5 | width: 50px; 6 | height: 100%; 7 | background-color: @site-black-color; 8 | .icon { 9 | display: block; 10 | width: 100%; 11 | height: 60px; 12 | color: white; 13 | } 14 | .dashboard { 15 | background: url('./iconfont-dashboard.svg') no-repeat center center transparent; 16 | background-size: 55%; 17 | } 18 | .user { 19 | background: url('./iconfont-user.svg') no-repeat center center transparent; 20 | background-size: 55%; 21 | } 22 | .setting { 23 | background: url('./iconfont-setting.svg') no-repeat center center transparent; 24 | background-size: 55%; 25 | } 26 | .GlobalMenu-active { 27 | background-color: @site-light-blue-color; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /client/containers/GlobalMenu/iconfont-dashboard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/containers/GlobalMenu/iconfont-setting.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/containers/GlobalMenu/iconfont-user.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /client/containers/GlobalMenu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GlobalMenu", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./GlobalMenu.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/containers/Header/Header.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes, Component} from 'react' 2 | import {Link, IndexLink} from 'react-router' 3 | import styles from './Header.less' 4 | 5 | class Header extends Component { 6 | 7 | render() { 8 | 9 | const {dispatch} = this.props 10 | 11 | return ( 12 |
13 | 14 |
15 | ) 16 | } 17 | } 18 | 19 | export default Header 20 | -------------------------------------------------------------------------------- /client/containers/Header/Header.less: -------------------------------------------------------------------------------- 1 | @import '../../theme/variable.less'; 2 | 3 | .Header { 4 | width: 100%; 5 | height: 50px; 6 | background-color: @site-black-color; 7 | .Header-logo { 8 | display: inline-block; 9 | width: 223px; 10 | height: 50px; 11 | background: url('./logo.png') no-repeat center center transparent; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/containers/Header/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfroster/react-workflow/a396099ef6dc486a13ca9ae55e8e824f08b04b39/client/containers/Header/logo.png -------------------------------------------------------------------------------- /client/containers/Header/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Header", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Header.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/containers/NotFound/NotFound.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes, Component} from 'react' 2 | import {Link, IndexLink} from 'react-router' 3 | 4 | class NotFound extends Component { 5 | 6 | render() { 7 | 8 | return ( 9 |
10 |
11 |
404 not found
12 | Sign in 13 |
14 |
15 | ) 16 | } 17 | } 18 | 19 | export default NotFound 20 | -------------------------------------------------------------------------------- /client/containers/NotFound/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NotFound", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./NotFound.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/containers/SignIn/SignIn.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import {browserHistory} from 'react-router' 3 | 4 | class SignIn extends Component { 5 | 6 | signIn(e) { 7 | //this.props.dispatch({type: 'sign-in'}) 8 | browserHistory.push('/') 9 | } 10 | 11 | render() { 12 | return( 13 |
14 |
15 | 16 |
17 |
18 | 19 |
20 | 21 |
22 | ) 23 | } 24 | 25 | } 26 | 27 | export default SignIn 28 | -------------------------------------------------------------------------------- /client/containers/SignIn/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SignIn", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./SignIn.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfroster/react-workflow/a396099ef6dc486a13ca9ae55e8e824f08b04b39/client/favicon.ico -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 32 | 33 | 38 | App 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /client/reducers/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux' 2 | import {routeReducer as router} from 'redux-simple-router' 3 | import user from './user' 4 | 5 | const rootCombineReducer = window.rootCombineReducer = { 6 | router, 7 | user, 8 | } 9 | 10 | const rootReducer = combineReducers(rootCombineReducer) 11 | 12 | export default rootReducer 13 | -------------------------------------------------------------------------------- /client/reducers/user.js: -------------------------------------------------------------------------------- 1 | const initState = { 2 | userName: 'admin', 3 | isSignIn: true, 4 | } 5 | 6 | function user(state = initState, action) { 7 | switch(action.type) { 8 | case 'sign-in': 9 | return Object.assign({}, state, {isSignIn: true}) 10 | case 'sign-out': 11 | return Object.assign({}, state, {isSignIn: false}) 12 | default: 13 | return state 14 | } 15 | } 16 | 17 | export default user 18 | -------------------------------------------------------------------------------- /client/route/childRoutes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * inclued all childRoutes, Array Type 3 | */ 4 | import dashboard from './dashboard' 5 | import user from './user' 6 | import setting from './setting' 7 | 8 | const childRoutes = [ 9 | dashboard, 10 | user, 11 | setting, 12 | ] 13 | 14 | export default childRoutes 15 | -------------------------------------------------------------------------------- /client/route/dashboard/actions/index.js: -------------------------------------------------------------------------------- 1 | export default function $TIME(data) { 2 | window.store.dispatch({type:'TIME'}) 3 | } 4 | -------------------------------------------------------------------------------- /client/route/dashboard/components/Template/Template.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import { Motion, spring } from 'react-motion' 3 | import style from './Template.less' 4 | 5 | class Template extends Component { 6 | 7 | render() { 8 | return ( 9 | 10 | { process => 11 |
12 | {process.x} 13 |
14 | } 15 |
16 | ) 17 | } 18 | 19 | } 20 | 21 | export default Template 22 | -------------------------------------------------------------------------------- /client/route/dashboard/components/Template/Template.less: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: white; 3 | } 4 | -------------------------------------------------------------------------------- /client/route/dashboard/components/Template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Template", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Template.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/route/dashboard/components/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfroster/react-workflow/a396099ef6dc486a13ca9ae55e8e824f08b04b39/client/route/dashboard/components/index.js -------------------------------------------------------------------------------- /client/route/dashboard/containers/App/App.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import {Link, IndexLink, browserHistory} from 'react-router' 3 | import {connect} from 'react-redux' 4 | import style from './App.less' 5 | import MarkDown from 'MarkDown' 6 | import rawMarkdown from './markdown.md' 7 | 8 | let timeHander 9 | 10 | class App extends Component { 11 | 12 | componentWillUnmount() { 13 | clearInterval(timeHander) 14 | } 15 | 16 | clickHandle(e) { 17 | clearInterval(timeHander) 18 | timeHander = setInterval(() => {window.store.dispatch({type:'TIME'})},1000) 19 | } 20 | 21 | render() { 22 | 23 | const {time, welcome} = this.props 24 | 25 | return( 26 |
27 |
28 |
welcome
29 |
DASHBOARD
30 |
31 | 32 | {rawMarkdown} 33 | 34 |
35 | ) 36 | } 37 | 38 | } 39 | 40 | App = connect((state) => { 41 | return { 42 | time : state.dashboard.time, 43 | welcome : state.dashboard.welcome, 44 | } 45 | })(App) 46 | 47 | export default App 48 | -------------------------------------------------------------------------------- /client/route/dashboard/containers/App/App.less: -------------------------------------------------------------------------------- 1 | @import '../../../../theme/variable.less'; 2 | 3 | .dashboard-container { 4 | padding: @site-padding; 5 | .dashboard-banner { 6 | padding: @site-weight-padding; 7 | background-color: @site-blue-color; 8 | } 9 | .dashboard-tip { 10 | font-size: 1.2rem; 11 | color: @site-light-white-color; 12 | } 13 | .dashboard-title { 14 | font-size: 2rem; 15 | color: @site-white-color; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/route/dashboard/containers/App/markdown.md: -------------------------------------------------------------------------------- 1 | # ctrl + q to change devtools position 2 | # ctrl + h to toggle devtools show 3 | ```shell 4 | $ npm start 5 | $ npm run deploy 6 | $ npm run publish 7 | $ npm run lint 8 | ``` 9 | -------------------------------------------------------------------------------- /client/route/dashboard/containers/App/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./App.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/route/dashboard/helpers/auth.js: -------------------------------------------------------------------------------- 1 | export let loginState = false 2 | 3 | export function logIn() { 4 | loginState = true 5 | } 6 | 7 | export function logOut(nextState, replaceState) { 8 | loginState = false 9 | replaceState(null, '/login') 10 | } 11 | 12 | export function check(nextState, replaceState) { 13 | if(!loginState) { 14 | alert('你还没有登录, 请登录') 15 | replaceState({nextPathName: nextState.location.pathName}, '/login') 16 | } 17 | if(loginState && nextState.location.pathName === '/login') { 18 | alert('你已经登录, 将跳转到dashboard页面') 19 | replaceState({nextPathName: nextState.location.pathName}, '/') 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/route/dashboard/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux' 2 | 3 | export default { 4 | path: '/', 5 | getComponent: (location, cb) => { 6 | require.ensure([], (require) => { 7 | 8 | // ensure not init reducers again 9 | if(!window.rootCombineReducer.dashboard) { 10 | window.rootCombineReducer.dashboard = require('./reducers').default 11 | const nextReducer = combineReducers(window.rootCombineReducer) 12 | window.store.replaceReducer(nextReducer) 13 | } 14 | cb(null, require('./containers/App').default) 15 | 16 | }) 17 | }, 18 | } 19 | 20 | if (module.hot) { 21 | // Enable Webpack hot module replacement for reducers 22 | module.hot.accept('./reducers', () => { 23 | window.rootCombineReducer.dashboard = require('./reducers').default 24 | const nextReducer = combineReducers(window.rootCombineReducer) 25 | window.store.replaceReducer(nextReducer) 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /client/route/dashboard/reducers/index.js: -------------------------------------------------------------------------------- 1 | const initState = { 2 | time: new Date().toLocaleTimeString(), 3 | welcome: 'welcome, this is a large app!, all code is incremental loading.', 4 | } 5 | 6 | function dashboard(state = initState, action) { 7 | 8 | switch(action.type) { 9 | 10 | case 'TIME': 11 | return Object.assign({}, state, {time: new Date().toLocaleTimeString()}) 12 | 13 | default: 14 | return state 15 | 16 | } 17 | } 18 | 19 | export default dashboard 20 | -------------------------------------------------------------------------------- /client/route/setting/actions/index.js: -------------------------------------------------------------------------------- 1 | export default function $TIME(data) { 2 | window.store.dispatch({type:'TIME'}) 3 | } 4 | -------------------------------------------------------------------------------- /client/route/setting/components/Template/Template.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import { Motion, spring } from 'react-motion' 3 | import style from './Template.less' 4 | 5 | class Template extends Component { 6 | 7 | render() { 8 | return ( 9 | 10 | { process => 11 |
12 | {process.x} 13 |
14 | } 15 |
16 | ) 17 | } 18 | 19 | } 20 | 21 | export default Template 22 | -------------------------------------------------------------------------------- /client/route/setting/components/Template/Template.less: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: white; 3 | } 4 | -------------------------------------------------------------------------------- /client/route/setting/components/Template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Template", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Template.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/route/setting/components/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfroster/react-workflow/a396099ef6dc486a13ca9ae55e8e824f08b04b39/client/route/setting/components/index.js -------------------------------------------------------------------------------- /client/route/setting/containers/App/App.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import {connect} from 'react-redux' 3 | import style from './App.less' 4 | 5 | class App extends Component { 6 | 7 | render() { 8 | 9 | const {time, welcome} = this.props 10 | 11 | return( 12 |
13 |

setting

14 | {this.props.children} 15 |
16 | ) 17 | } 18 | 19 | } 20 | 21 | export default App 22 | -------------------------------------------------------------------------------- /client/route/setting/containers/App/App.less: -------------------------------------------------------------------------------- 1 | .setting-container { 2 | } 3 | 4 | -------------------------------------------------------------------------------- /client/route/setting/containers/App/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./App.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/route/setting/helpers/auth.js: -------------------------------------------------------------------------------- 1 | export let loginState = false 2 | 3 | export function logIn() { 4 | loginState = true 5 | } 6 | 7 | export function logOut(nextState, replaceState) { 8 | loginState = false 9 | replaceState(null, '/login') 10 | } 11 | 12 | export function check(nextState, replaceState) { 13 | if(!loginState) { 14 | alert('你还没有登录, 请登录') 15 | replaceState({nextPathName: nextState.location.pathName}, '/login') 16 | } 17 | if(loginState && nextState.location.pathName === '/login') { 18 | alert('你已经登录, 将跳转到dashboard页面') 19 | replaceState({nextPathName: nextState.location.pathName}, '/') 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/route/setting/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux' 2 | 3 | export default { 4 | path: '/setting', 5 | getComponent: (location, cb) => { 6 | require.ensure([], (require) => { 7 | 8 | // ensure not init reducers again 9 | if(!window.rootCombineReducer.setting) { 10 | window.rootCombineReducer.setting = require('./reducers').default 11 | const nextReducer = combineReducers(window.rootCombineReducer) 12 | window.store.replaceReducer(nextReducer) 13 | } 14 | cb(null, require('./containers/App').default) 15 | 16 | }) 17 | }, 18 | } 19 | 20 | if (module.hot) { 21 | // Enable Webpack hot module replacement for reducers 22 | module.hot.accept('./reducers', () => { 23 | window.rootCombineReducer.setting = require('./reducers').default 24 | const nextReducer = combineReducers(window.rootCombineReducer) 25 | window.store.replaceReducer(nextReducer) 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /client/route/setting/reducers/index.js: -------------------------------------------------------------------------------- 1 | const initState = { 2 | time: new Date().toLocaleTimeString(), 3 | welcome: 'welcome, this is a large app!, all code is incremental loading.', 4 | } 5 | 6 | function dashboard(state = initState, action) { 7 | 8 | switch(action.type) { 9 | 10 | case 'TIME': 11 | return Object.assign({}, state, {time: new Date().toLocaleTimeString()}) 12 | 13 | default: 14 | return state 15 | 16 | } 17 | } 18 | 19 | export default dashboard 20 | -------------------------------------------------------------------------------- /client/route/user/actions/index.js: -------------------------------------------------------------------------------- 1 | export default function $TIME(data) { 2 | window.store.dispatch({type:'TIME'}) 3 | } 4 | -------------------------------------------------------------------------------- /client/route/user/components/Template/Template.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import { Motion, spring } from 'react-motion' 3 | import style from './Template.less' 4 | 5 | class Template extends Component { 6 | 7 | render() { 8 | return ( 9 | 10 | { process => 11 |
12 | {process.x} 13 |
14 | } 15 |
16 | ) 17 | } 18 | 19 | } 20 | 21 | export default Template 22 | -------------------------------------------------------------------------------- /client/route/user/components/Template/Template.less: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: white; 3 | } 4 | -------------------------------------------------------------------------------- /client/route/user/components/Template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Template", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./Template.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/route/user/components/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudfroster/react-workflow/a396099ef6dc486a13ca9ae55e8e824f08b04b39/client/route/user/components/index.js -------------------------------------------------------------------------------- /client/route/user/containers/App/App.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes, Component } from 'react' 2 | import style from './App.less' 3 | 4 | class App extends Component { 5 | 6 | render() { 7 | 8 | return( 9 |
10 |

user

11 |
12 | ) 13 | } 14 | 15 | } 16 | 17 | export default App 18 | -------------------------------------------------------------------------------- /client/route/user/containers/App/App.less: -------------------------------------------------------------------------------- 1 | .user-container { 2 | } 3 | 4 | -------------------------------------------------------------------------------- /client/route/user/containers/App/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "version": "0.0.0", 4 | "private": true, 5 | "main": "./App.js" 6 | } 7 | -------------------------------------------------------------------------------- /client/route/user/helpers/auth.js: -------------------------------------------------------------------------------- 1 | export let loginState = false 2 | 3 | export function logIn() { 4 | loginState = true 5 | } 6 | 7 | export function logOut(nextState, replaceState) { 8 | loginState = false 9 | replaceState(null, '/login') 10 | } 11 | 12 | export function check(nextState, replaceState) { 13 | if(!loginState) { 14 | alert('你还没有登录, 请登录') 15 | replaceState({nextPathName: nextState.location.pathName}, '/login') 16 | } 17 | if(loginState && nextState.location.pathName === '/login') { 18 | alert('你已经登录, 将跳转到dashboard页面') 19 | replaceState({nextPathName: nextState.location.pathName}, '/') 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /client/route/user/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from 'redux' 2 | 3 | export default { 4 | path: '/user', 5 | getComponent: (location, cb) => { 6 | require.ensure([], (require) => { 7 | 8 | // ensure not init reducers again 9 | if(!window.rootCombineReducer.user) { 10 | window.rootCombineReducer.user = require('./reducers').default 11 | const nextReducer = combineReducers(window.rootCombineReducer) 12 | window.store.replaceReducer(nextReducer) 13 | } 14 | cb(null, require('./containers/App').default) 15 | 16 | }) 17 | }, 18 | } 19 | 20 | if (module.hot) { 21 | // Enable Webpack hot module replacement for reducers 22 | module.hot.accept('./reducers', () => { 23 | window.rootCombineReducer.user = require('./reducers').default 24 | const nextReducer = combineReducers(window.rootCombineReducer) 25 | window.store.replaceReducer(nextReducer) 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /client/route/user/reducers/index.js: -------------------------------------------------------------------------------- 1 | const initState = { 2 | time: new Date().toLocaleTimeString(), 3 | welcome: 'welcome, this is a large app!, all code is incremental loading.', 4 | } 5 | 6 | function dashboard(state = initState, action) { 7 | 8 | switch(action.type) { 9 | 10 | case 'TIME': 11 | return Object.assign({}, state, {time: new Date().toLocaleTimeString()}) 12 | 13 | default: 14 | return state 15 | 16 | } 17 | } 18 | 19 | export default dashboard 20 | -------------------------------------------------------------------------------- /client/routes.js: -------------------------------------------------------------------------------- 1 | import React, {Component}from 'react' 2 | import {Router, browserHistory} from 'react-router' 3 | import SignIn from './containers/SignIn' 4 | import App from './containers/App' 5 | import NotFound from './containers/NotFound' 6 | import childRoutes from './route/childRoutes' 7 | 8 | const routes = { 9 | childRoutes: [ 10 | { 11 | path: '/signin', 12 | component: SignIn, 13 | }, 14 | { 15 | onEnter(nextState, replace) { 16 | if(!window.store.getState().user.isSignIn) { 17 | replace('/signin') 18 | } 19 | }, 20 | component: App, 21 | childRoutes, 22 | }, 23 | { 24 | path: '*', 25 | component: NotFound, 26 | } 27 | ] 28 | } 29 | 30 | export default routes 31 | -------------------------------------------------------------------------------- /client/store/configureStore.js: -------------------------------------------------------------------------------- 1 | import {createStore, combineReducers, applyMiddleware, compose} from 'redux' 2 | import {syncHistory} from 'redux-simple-router' 3 | import thunk from 'redux-thunk' 4 | import {browserHistory as history} from 'react-router' 5 | import rootReducer from '../reducers' 6 | 7 | function configureStore(initState) { 8 | 9 | let finalCreateStore 10 | 11 | // Sync dispatched route actions to the history 12 | const reduxRouterMiddleware = syncHistory(history) 13 | 14 | if(__DEV__) { 15 | //const createLogger = require('redux-logger') 16 | const allDevTools = require('../containers/DevTools') 17 | finalCreateStore = compose( 18 | applyMiddleware( 19 | reduxRouterMiddleware, 20 | thunk, 21 | /* createLogger(), */ 22 | ), 23 | allDevTools.DevTools.instrument(), 24 | allDevTools.persistState(allDevTools.getDebugSessionKey()), 25 | )(createStore) 26 | 27 | } else { 28 | finalCreateStore = compose( 29 | applyMiddleware( 30 | reduxRouterMiddleware, 31 | thunk, 32 | ), 33 | )(createStore) 34 | } 35 | 36 | const store = finalCreateStore(rootReducer, initState) 37 | 38 | if (module.hot) { 39 | // Enable Webpack hot module replacement for reducers 40 | module.hot.accept('../reducers', () => { 41 | window.rootCombineReducer.user = require('../reducers/user') 42 | const nextReducer = combineReducers(window.rootCombineReducer) 43 | store.replaceReducer(nextReducer) 44 | }) 45 | } 46 | 47 | return store 48 | } 49 | 50 | export default configureStore 51 | 52 | -------------------------------------------------------------------------------- /client/theme/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "variable.less", 3 | "version": "0.0.1", 4 | "private": true, 5 | "main": "./variable.less" 6 | } 7 | -------------------------------------------------------------------------------- /client/theme/variable.less: -------------------------------------------------------------------------------- 1 | // web site theme 2 | //-------------------------------------------------// 3 | //| 站点变量 4 | //-------------------------------------------------// 5 | @namespace: workflow; 6 | @site-bg-color: #EBECED; 7 | @site-black-color: #16191E; 8 | @site-label-color: #A7A9AC; 9 | @site-blue-color: #509ED8; 10 | @site-light-blue-color: #4B7CA3; 11 | @site-white-color: #ffffff; 12 | @site-light-white-color: #EBECED; 13 | @site-padding: 10px; 14 | @site-weight-padding: 15px; 15 | //-------------------------------------------------// 16 | //| 扩展maxin 17 | //-------------------------------------------------// 18 | 19 | //******************************************* 清楚浮动 *********************************************/ 20 | //清除浮动老版本 21 | // .clearfix() { 22 | // *zoom: 1; 23 | // &:after { 24 | // display: block; 25 | // clear: both; 26 | // content: ""; 27 | // visibility: hidden; 28 | // height: 0; 29 | // } 30 | // } 31 | //清除浮动新版本 32 | .clearfix() { 33 | *zoom: 1; 34 | &:after { 35 | content:"\200B"; 36 | display:block; 37 | height:0; 38 | clear:both; 39 | } 40 | } 41 | 42 | //******************************************* 小工具 *********************************************/ 43 | //长单词强制换行 44 | .wrap() { 45 | text-wrap: wrap; 46 | white-space: -moz-pre-wrap; 47 | white-space: pre-wrap; 48 | word-wrap: break-word; 49 | } 50 | //文字溢出省略号表示 51 | .text-autocut() { 52 | overflow: hidden; 53 | white-space: nowrap; 54 | -webkit-text-overflow:ellipsis; 55 | -khtml-text-overflow: ellipsis; 56 | -icab-text-overflow: ellipsis; 57 | -moz-text-overflow: ellipsis; 58 | -o-text-overflow: ellipsis; 59 | text-overflow: ellipsis; 60 | } 61 | //******************************************* 各种移动端bug修复 **********************************/ 62 | //去掉手持设备点击时出现的透明层(一般在头部做格式化) 63 | .delete-highlight(transparent){ 64 | a,button,input{ 65 | -webkit-tap-highlight-color: transparent; /* For some Androids */ 66 | tap-highlight-color: transparent; 67 | } 68 | } 69 | .delete-highlight(@_,@color:rgba(0,0,0,0)){ 70 | a,button,input{ 71 | -webkit-tap-highlight-color: @color; 72 | tap-highlight-color: @color; 73 | } 74 | } 75 | 76 | //长按页面时不触发系统菜单 77 | .no-callout(){ 78 | html,body { 79 | -webkit-touch-callout: none; 80 | touch-callout: none; 81 | } 82 | } 83 | 84 | //长按无法选择文本,不弹出复制,粘贴等选项 85 | .no-user-select(){ 86 | html,body { 87 | -webkit-user-select: none; 88 | user-select: none; 89 | } 90 | } 91 | 92 | //去掉点击(触摸)高光 93 | //.delete-highlight(rgba(1,1,10,5)); 94 | //.delete-highlight(transparent); 95 | //长按页面时不触发系统菜单 96 | //.no-callout(); 97 | //长按无法选择文本,不弹出复制,粘贴等选项 98 | //.no-user-select(); 99 | // 100 | -------------------------------------------------------------------------------- /humans.txt: -------------------------------------------------------------------------------- 1 | # humanstxt.org/ 2 | # The humans responsible & technology colophon 3 | 4 | # TEAM 5 | 6 | -- -- <844033231@qq.com> 7 | 8 | # THANKS 9 | 10 | 11 | 12 | # TECHNOLOGY COLOPHON 13 | 14 | react, redux, 15 | nodejs, nodemon, Normalize.css, less, babel, webpack 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-workflow", 3 | "environment": { 4 | "node": ">= 5.0.0", 5 | "npm": ">= 3.4.0" 6 | }, 7 | "version": "1.1.2", 8 | "description": "Large SPA boilerplate use react redux webpack babel es6 express browsync nodemon...", 9 | "main": "tasks/run.js", 10 | "scripts": { 11 | "start": "node tasks/run.js start", 12 | "deploy": "node tasks/run.js deploy", 13 | "publish": "node tasks/run.js publish", 14 | "clean": "node tasks/run.js clean", 15 | "bundle": "node tasks/run.js bundle", 16 | "browserSync": "node tasks/run.js browserSync", 17 | "server": "node tasks/run.js server", 18 | "lint": "eslint client && eslint server && csslint --quiet build", 19 | "csslint": "csslint --quiet build" 20 | }, 21 | "keywords": [ 22 | "react", 23 | "redux", 24 | "flux" 25 | ], 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/chen844033231/react-workflow.git" 29 | }, 30 | "author": "marchen", 31 | "license": "MIT", 32 | "devDependencies": { 33 | "autoprefixer": "~6.3.6", 34 | "babel-core": "~6.8.0", 35 | "babel-eslint": "~6.1.0", 36 | "babel-loader": "~6.2.4", 37 | "babel-plugin-react-transform": "~2.0.2", 38 | "babel-plugin-transform-class-constructor-call": "~6.8.0", 39 | "babel-plugin-transform-class-properties": "~6.11.5", 40 | "babel-plugin-transform-decorators": "~6.8.0", 41 | "babel-plugin-transform-decorators-legacy": "~1.3.4", 42 | "babel-plugin-transform-object-assign": "~6.8.0", 43 | "babel-preset-es2015": "~6.6.0", 44 | "babel-preset-react": "~6.11.1", 45 | "babel-preset-stage-0": "~6.5.0", 46 | "browser-sync": "~2.13.0", 47 | "chalk": "~1.1.3", 48 | "co": "~4.6.0", 49 | "css-loader": "~0.23.1", 50 | "csslint": "~0.10.0", 51 | "del": "~2.2.1", 52 | "eslint": "~2.13.0", 53 | "eslint-plugin-react": "~4.2.3", 54 | "extract-text-webpack-plugin": "~1.0.0", 55 | "file-loader": "~0.9.0", 56 | "gh-pages": "~0.11.0", 57 | "highlight.js": "~9.6.0", 58 | "html-loader": "~0.4.0", 59 | "html-webpack-plugin": "~2.21.0", 60 | "json-loader": "~0.5.4", 61 | "less": "~2.7.0", 62 | "less-loader": "~2.2.3", 63 | "marked": "~0.3.5", 64 | "nodemon": "~1.8.1", 65 | "postcss-loader": "~0.9.1", 66 | "pretty-error": "~2.0.0", 67 | "query-string": "~4.2.2", 68 | "raw-loader": "~0.5.1", 69 | "react-document-meta": "~2.0.3", 70 | "react-transform-catch-errors": "~1.0.2", 71 | "react-transform-hmr": "~1.0.4", 72 | "redbox-react": "~1.3.0", 73 | "redux-devtools": "~3.3.1", 74 | "redux-devtools-dock-monitor": "~1.0.1", 75 | "redux-devtools-log-monitor": "~1.0.10", 76 | "redux-logger": "~2.6.0", 77 | "redux-thunk": "~2.1.0", 78 | "serialize-javascript": "~1.2.0", 79 | "serve-favicon": "~2.3.0", 80 | "style-loader": "~0.13.1", 81 | "url-loader": "~0.5.7", 82 | "webpack": "~1.12.15", 83 | "webpack-dev-middleware": "~1.6.1", 84 | "webpack-hot-middleware": "~2.12.1" 85 | }, 86 | "dependencies": { 87 | "body-parser": "~1.15.1", 88 | "cookie-parser": "~1.4.1", 89 | "express": "~4.14.0", 90 | "history": "~1.17.0", 91 | "http-proxy": "~1.14.0", 92 | "json-server": "~0.8.9", 93 | "morgan": "~1.6.1", 94 | "normalize.css": "~4.2.0", 95 | "react": "~15.0.1", 96 | "react-dom": "~15.0.1", 97 | "react-motion": "~0.4.2", 98 | "react-redux": "~4.4.4", 99 | "react-router": "~2.5.1", 100 | "react-tap-event-plugin": "~1.0.0", 101 | "redux": "~3.5.2", 102 | "redux-simple-router": "~2.0.4", 103 | "superagent": "~1.8.3" 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /processes.json: -------------------------------------------------------------------------------- 1 | // configuration for pm2, https://github.com/Unitech/pm2 2 | { 3 | "name": "react-workflow", 4 | "script": "server/server.js", 5 | "node_args": "", 6 | "log_date_format": "YYYY-MM-DD HH:mm Z", 7 | "error_file": "/var/log/react-workflow/react-workflow.stderr.log", 8 | "out_file": "/var/log/react-workflow/react-workflow.stdout.log", 9 | "instances": 0, 10 | "exec_mode": "cluster_mode", 11 | "watch": true, 12 | "ignore_watch": "node_modules" 13 | } 14 | 15 | -------------------------------------------------------------------------------- /server/api/mock.json: -------------------------------------------------------------------------------- 1 | { 2 | "posts": [ 3 | { 4 | "id": 1, 5 | "title": "json-server", 6 | "author": "typicode" 7 | } 8 | ], 9 | "comments": [ 10 | { 11 | "id": 1, 12 | "body": "some comment", 13 | "postId": 1 14 | } 15 | ], 16 | "profile": { 17 | "name": "typicode" 18 | } 19 | } -------------------------------------------------------------------------------- /server/remote/api/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "posts": [ 3 | { 4 | "id": 1, 5 | "title": "db online data", 6 | "author": "marchen" 7 | } 8 | ], 9 | "comments": [ 10 | { 11 | "id": 1, 12 | "body": "some comment", 13 | "postId": 1 14 | } 15 | ], 16 | "profile": { 17 | "name": "marchen" 18 | } 19 | } -------------------------------------------------------------------------------- /server/remote/remoteServer.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------- 2 | // Remote server(virtual host) 3 | //--------------------------------------------------------------------- 4 | const path = require('path') 5 | const chalk = require('chalk') 6 | const logger = require('morgan') 7 | const express = require('express') 8 | const cookieParser = require('cookie-parser') 9 | const bodyParser = require('body-parser') 10 | const jsonServer = require('json-server') 11 | // Returns an Express server 12 | const app = express() 13 | 14 | app.set('port', (process.env.PORT || 5001)) 15 | 16 | // express middleware 17 | app.use(logger('dev')) 18 | app.use(bodyParser.json()) 19 | app.use(bodyParser.urlencoded({ extended: false })) 20 | app.use(cookieParser()) 21 | 22 | // Set default middlewares (logger, static, cors and no-cache) 23 | app.use(jsonServer.defaults()) 24 | app.use('/api', jsonServer.router(path.join(__dirname, './api/db.json'))) 25 | 26 | // start server 27 | app.listen(app.get('port'), () => { 28 | /* eslint-disable no-console */ 29 | console.log(chalk.cyan(`The remote server is running at http://localhost:${app.get('port')}`)) 30 | if (process.send) { 31 | process.send('online') 32 | } 33 | }) 34 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------- 2 | // use json-server set up express mock server at localhost:5000 3 | //--------------------------------------------------------------------- 4 | const path = require('path') 5 | const chalk = require('chalk') 6 | const logger = require('morgan') 7 | const express = require('express') 8 | const cookieParser = require('cookie-parser') 9 | const bodyParser = require('body-parser') 10 | const jsonServer = require('json-server') 11 | const http = require('http') 12 | const app = express() 13 | const server = http.createServer(app) 14 | //------------------------------------------------------------------- 15 | // set up virtual remote server proxy 16 | //------------------------------------------------------------------- 17 | const httpProxy = require('http-proxy') 18 | const isProxy = false 19 | const config = { 20 | apiHost: 'localhost', 21 | apiPort: 5001, 22 | } 23 | const proxy = httpProxy.createProxyServer({ 24 | target: `http://${config.apiHost}:${config.apiPort}`, 25 | ws: true, 26 | }) 27 | if(isProxy) { 28 | // start remote server 29 | require('./remote/remoteServer') 30 | app.all(/^\/api/, (req, res) => { 31 | proxy.web(req, res) 32 | }) 33 | // Listen for the `error` event on `proxy`. 34 | proxy.on('error', function (err, req, res) { 35 | res.writeHead(500, { 36 | 'Content-Type': 'text/plain' 37 | }) 38 | 39 | res.end('Something went wrong. The http-proxy has shut down') 40 | }) 41 | console.log(`The proxy server to proxy '/api' at http://${config.apiHost}:${config.apiPort}`) 42 | } else { 43 | app.use('/api', jsonServer.router(path.join(__dirname, './api/mock.json'))) 44 | } 45 | //------------------------------------------------------------------- 46 | // end 47 | //------------------------------------------------------------------- 48 | app.set('port', (process.env.PORT || 5000)) 49 | app.set('env', 'development') 50 | 51 | // static file 52 | app.use(express.static(path.join(__dirname, '../build'))) 53 | 54 | // express middleware 55 | app.use(logger('dev')) 56 | app.use(bodyParser.json()) 57 | app.use(bodyParser.urlencoded({ extended: false })) 58 | app.use(cookieParser()) 59 | 60 | // development error handler will print stacktrace 61 | if (app.get('env') === 'development') { 62 | app.use(function(err, req, res, next) { 63 | res.status(err.status || 500) 64 | res.render('error', { 65 | message: err.message, 66 | error: err 67 | }) 68 | }) 69 | } 70 | 71 | // jump to index.html 72 | app.get('*', (req, res) => { 73 | res.sendFile(path.join(__dirname, '../build/index.html')) 74 | }) 75 | 76 | 77 | // start server 78 | server.listen(app.get('port'), () => { 79 | /* eslint-disable no-console */ 80 | console.log(chalk.cyan(`The server is running at http://localhost:${app.get('port')}`)) 81 | if (process.send) { 82 | process.send('online') 83 | } 84 | }) 85 | -------------------------------------------------------------------------------- /tasks/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "no-console": 0 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tasks/browserSync.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------- 2 | // use browserSync to proxy development server 3 | //--------------------------------------------------------------- 4 | 5 | const bs = require('browser-sync').create() 6 | const webpack = require('webpack') 7 | const webpackDevMiddleware = require('webpack-dev-middleware') 8 | const webpackHotMiddleware = require('webpack-hot-middleware') 9 | const webpackConfig = require('./webpack.config') // Client-side bundle configuration 10 | const bundler = webpack(webpackConfig) 11 | 12 | function browserSync() { 13 | return new Promise((reslove, reject) => { 14 | 15 | try { 16 | bs.init({ 17 | proxy: { 18 | 19 | target: 'localhost:5000', 20 | 21 | middleware: [ 22 | webpackDevMiddleware(bundler, { 23 | // IMPORTANT: dev middleware can't access config, so we should 24 | // provide publicPath by ourselves 25 | publicPath: webpackConfig.output.publicPath, 26 | 27 | // display nothing to the console 28 | quiet: true, 29 | 30 | // Pretty colored output 31 | stats: webpackConfig.stats, 32 | 33 | // display no info to console (only warnings and errors) 34 | noInfo: true, 35 | 36 | // For other settings see 37 | // http://webpack.github.io/docs/webpack-dev-middleware.html 38 | }), 39 | 40 | // bundler should be the same as webpackDevMiddleware's bundler 41 | webpackHotMiddleware(bundler), 42 | ], 43 | }, 44 | 45 | // no need to watch '*.js' here, webpack will take care of it for us, 46 | // including full page reloads if HMR won't work 47 | files: [ 48 | './build/**/*.css', 49 | ], 50 | }) 51 | reslove() 52 | } catch (e) { 53 | reject(e) 54 | } 55 | 56 | }) 57 | } 58 | 59 | module.exports = browserSync 60 | -------------------------------------------------------------------------------- /tasks/bundle.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------- 2 | // Bundles JavaScript, CSS and images into one or more packages 3 | // ready to be used in a browser 4 | //--------------------------------------------------------------------- 5 | 6 | const webpack = require('webpack') 7 | const webpackConfig = require('./webpack.config') 8 | 9 | function bundle() { 10 | return new Promise((resolve, reject) => { 11 | 12 | const bundler = webpack(webpackConfig) 13 | 14 | function onComplete(err, stats) { 15 | if (err) { 16 | return reject(err) 17 | } 18 | 19 | console.log(stats.toString(webpackConfig.stats)) 20 | return resolve() 21 | } 22 | 23 | if (global.DEBUG) { 24 | bundler.watch(200, onComplete) 25 | } else { 26 | bundler.run(onComplete) 27 | } 28 | }) 29 | } 30 | 31 | module.exports = bundle 32 | -------------------------------------------------------------------------------- /tasks/clean.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------- 2 | // Cleans up the output (build) directory 3 | //--------------------------------------------------------------------- 4 | 5 | const del = require('del') 6 | 7 | function clean() { 8 | return new Promise((reslove, reject) => { 9 | try{ 10 | del(['.tmp', 'build/*'], { dot: true }) 11 | reslove() 12 | }catch(e) { 13 | reject(e) 14 | } 15 | }) 16 | } 17 | 18 | module.exports = clean 19 | -------------------------------------------------------------------------------- /tasks/deploy.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------- 2 | // deploly your site 3 | //----------------------------------------------- 4 | 5 | const run = require('./run') 6 | const co = require('co') 7 | 8 | // Production model 9 | global.DEBUG = false 10 | 11 | function deploy() { 12 | return co(function* () { 13 | run(require('./server')) 14 | yield run(require('./clean')) 15 | yield run(require('./bundle')) 16 | /* yield run(require('./publish')) */ 17 | }) 18 | } 19 | 20 | module.exports = deploy 21 | -------------------------------------------------------------------------------- /tasks/publish.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------- 2 | // publish build folder to github gh-pages branch 3 | //--------------------------------------------------------------------- 4 | 5 | 'use strict' 6 | const ghpages = require('gh-pages') 7 | const path = require('path') 8 | 9 | function publish() { 10 | return new Promise((reslove, reject) => { 11 | 12 | const options = { 13 | src: ['build/**/*', 'README.md', 'LICENSE.txt', 'processes.json'], 14 | dotfiles: true, 15 | //add: true, // never remove existing files 16 | //branch: 'production', 17 | //message: 'Updates', 18 | //repo: 'https://example.com/other/repo.git', 19 | //user: {name: '', email: ''}, 20 | //clone: 'temp', 21 | //push: true, 22 | //silent: true, 23 | logger: function(message){ 24 | console.log(message) 25 | }, 26 | //git: '/path/to/git', 27 | } 28 | 29 | ghpages.publish(path.join(__dirname, '../'), options, function(err) { 30 | if(err) { 31 | reject(err) 32 | } 33 | reslove() 34 | }) 35 | }) 36 | } 37 | 38 | module.exports = publish 39 | -------------------------------------------------------------------------------- /tasks/run.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------- 2 | // run task 3 | //--------------------------------------------------------------------- 4 | 5 | // make time pretty 6 | function format(time) { 7 | return time.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, '$1') 8 | } 9 | 10 | function run(fn, options) { 11 | const chalk = require('chalk') 12 | 13 | const start = new Date() 14 | console.log(`[${chalk.magenta(format(start))}] Starting '${chalk.cyan(fn.name)}'...`) 15 | return fn(options).then(() => { 16 | const end = new Date() 17 | const time = end.getTime() - start.getTime() 18 | console.log(`[${chalk.magenta(format(end))}] Finished '${chalk.cyan(fn.name)}' after ${time} ms`) 19 | }) 20 | } 21 | 22 | if (process.mainModule.children.length === 0 && process.argv.length > 2) { 23 | delete require.cache[__filename] 24 | const task = process.argv[2] 25 | run(require('./' + task + '.js')).catch(err => console.error(err.stack)) 26 | } 27 | 28 | module.exports = run 29 | -------------------------------------------------------------------------------- /tasks/server.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------- 2 | // Launches Node.js/Express web server in a separate (forked) process 3 | //--------------------------------------------------------------------- 4 | 5 | const path = require('path') 6 | const nodemon = require('nodemon') 7 | 8 | function startNodemon() { 9 | nodemon({ 10 | "restartable": "rs", 11 | "ignore": [ 12 | ".git", 13 | "node_modules/**/node_modules" 14 | ], 15 | "verbose": true, 16 | "execMap": { 17 | "js": "node" 18 | }, 19 | "script": path.join(__dirname, '../server/server.js'), 20 | "watch": [ 21 | path.join(__dirname, '../server') 22 | ], 23 | "env": { 24 | "NODE_ENV": "development" 25 | }, 26 | "ext": "js json" 27 | }).on('restart', () => {}) 28 | } 29 | 30 | function server() { 31 | function restart() { 32 | console.log("App restarted due to:\n'$FILENAME'\" with title \"nodemon\"'") 33 | } 34 | return new Promise((resolve, reject) => { 35 | global.DEBUG ? startNodemon() : require('../server/server.js') 36 | resolve() 37 | }) 38 | } 39 | 40 | module.exports = server 41 | -------------------------------------------------------------------------------- /tasks/start.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------- 2 | // Launches a development web server with "live reload" 3 | // functionality synchronizing URLs, interactions and 4 | // code changes across multiple devices 5 | //--------------------------------------------------------------- 6 | 7 | const run = require('./run') 8 | const co = require('co') 9 | 10 | // Development model 11 | global.DEBUG = true 12 | 13 | function start() { 14 | return co(function* () { 15 | run(require('./server')) 16 | yield run(require('./clean')) 17 | yield run(require('./bundle')) 18 | yield run(require('./browserSync')) 19 | }) 20 | } 21 | 22 | module.exports = start 23 | -------------------------------------------------------------------------------- /tasks/webpack.config.js: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------- 2 | // export webpack config 3 | //------------------------------------------------------------------------- 4 | 5 | const path = require('path') 6 | const webpack = require('webpack') 7 | const autoprefixer = require('autoprefixer') 8 | const HtmlWebpackPlugin = require('html-webpack-plugin') 9 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 10 | 11 | const AUTOPREFIXER = `{ 12 | browsers: [ 13 | 'Android >= 4', 14 | 'Chrome >= 35', 15 | 'Firefox >= 31', 16 | 'Explorer >= 9', 17 | 'iOS >= 7', 18 | 'Opera >= 12', 19 | 'Safari >= 7.1', 20 | ], 21 | }` 22 | 23 | // for babel and other tool env 24 | process.env.NODE_ENV = global.DEBUG ? 'development' : 'production' 25 | 26 | const GLOBALS = { 27 | 'process.env.NODE_ENV': JSON.stringify(global.DEBUG ? 'development' : 'production'), 28 | __DEV__: global.DEBUG, 29 | __PRO__: !global.DEBUG, 30 | } 31 | 32 | //------------------------------------------------------------------------- 33 | // Configuration for the client-side bundle (app.js) 34 | //------------------------------------------------------------------------- 35 | 36 | const appConfig = { 37 | 38 | entry: [ 39 | ...(global.DEBUG ? ['webpack-hot-middleware/client'] : []), 40 | path.join(__dirname, '../client/app.js'), 41 | ], 42 | 43 | output: { 44 | path: path.join(__dirname, '../build'), 45 | // if your want to cdn, just change here 46 | publicPath: '/', 47 | sourcePrefix: '', 48 | trunkFilename: global.DEBUG ? '[id].bundle.js' : '[id].[chunkhash].bundle.js', 49 | filename: global.DEBUG ? 'bundle.js' : '[chunkhash].[name].bundle.js', 50 | }, 51 | 52 | devtool: global.DEBUG ? 'cheap-module-eval-source-map' : false, 53 | 54 | resolve: { 55 | extensions: ['', '.webpack.js', '.web.js', '.js', '.jsx', '.less'], 56 | root: [ 57 | path.join(__dirname, '../client/components'), 58 | path.join(__dirname, '../client/theme'), 59 | ], 60 | }, 61 | 62 | plugins: [ 63 | // create index.html 64 | new HtmlWebpackPlugin({ 65 | filename: 'index.html', 66 | cache: true, 67 | inject: true, 68 | template: 'html!./client/index.html', 69 | favicon: './client/favicon.ico', 70 | minify: global.DEBUG ? {} : { 71 | removeComments: true, 72 | collapseWhitespace: true, 73 | removeRedundantAttributes: true, 74 | useShortDoctype: true, 75 | removeEmptyAttributes: true, 76 | removeStyleLinkTypeAttributes: true, 77 | keepClosingSlash: true, 78 | minifyJS: true, 79 | minifyCSS: true, 80 | minifyURLs: true, 81 | } 82 | }), 83 | 84 | new ExtractTextPlugin(global.DEBUG ? 'app.css' : '[chunkhash].app.css'), 85 | 86 | new webpack.optimize.OccurenceOrderPlugin(), 87 | 88 | new webpack.DefinePlugin(GLOBALS), 89 | 90 | new webpack.optimize.CommonsChunkPlugin({ 91 | name: "commons", 92 | filename: global.DEBUG ? 'commons.js' : '[chunkhash].[name].js', 93 | // (Modules must be shared between 2 entries) 94 | minChunks: 2, 95 | /* chunks: ["pageA", "pageB"], */ 96 | // (Only use these entries) 97 | }), 98 | 99 | ...(!global.DEBUG ? [ 100 | new webpack.optimize.UglifyJsPlugin({ 101 | compress: { 102 | warnings: false, 103 | }, 104 | }), 105 | //new webpack.optimize.AggressiveMergingPlugin(), 106 | ] : []), 107 | ...(global.DEBUG ? [ 108 | new webpack.HotModuleReplacementPlugin(), 109 | new webpack.NoErrorsPlugin(), 110 | ] : []), 111 | ], 112 | 113 | module: { 114 | loaders: [ 115 | { 116 | test: /\.js$/, 117 | exclude: /(node_modules)/, 118 | loader: 'babel-loader', 119 | }, { 120 | test: /\.json$/, 121 | loader: 'json-loader', 122 | }, { 123 | test: /\.(txt|md)$/, 124 | loader: 'raw-loader', 125 | }, { 126 | test: /\.(png|jpg|jpeg|gif|svg|woff|woff2)$/, 127 | loader: 'url-loader?limit=5000', 128 | }, { 129 | test: /\.(eot|ttf|wav|mp3)$/, 130 | loader: 'file-loader', 131 | }, { 132 | test: /\.css/, 133 | loader: ExtractTextPlugin.extract('style-loader', 'css-loader?' + (global.DEBUG ? 'sourceMap' : '') + `!postcss-loader?${AUTOPREFIXER}`), 134 | }, { 135 | test: /\.less$/, 136 | loader: ExtractTextPlugin.extract('style-loader', 'css-loader?' + (global.DEBUG ? 'sourceMap' : '') + `!postcss-loader?${AUTOPREFIXER}!less-loader?` + (global.DEBUG ? 'sourceMap' : '')), 137 | }, 138 | ], 139 | }, 140 | 141 | postcss: () => { 142 | return [autoprefixer] 143 | }, 144 | 145 | cache: global.DEBUG, 146 | debug: global.DEBUG, 147 | 148 | stats: { 149 | colors: true, 150 | reasons: global.DEBUG, 151 | hash: false, 152 | version: false, 153 | timings: true, 154 | chunks: false, 155 | chunkModules: false, 156 | cached: false, 157 | cachedAssets: false, 158 | }, 159 | 160 | } 161 | 162 | module.exports = appConfig 163 | --------------------------------------------------------------------------------