├── .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 |
7 |
--------------------------------------------------------------------------------
/client/containers/GlobalMenu/iconfont-setting.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/client/containers/GlobalMenu/iconfont-user.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
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 |
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 |
--------------------------------------------------------------------------------