├── LICENSE
├── README.md
├── meta.js
└── template
├── .babelrc
├── .eslintrc.js
├── .gitignore
├── build
├── config.js
├── index.html
├── server.js
├── webpack.base.js
├── webpack.dev.js
└── webpack.prod.js
├── package.json
├── src
├── components
│ ├── Home
│ │ ├── Home.css
│ │ ├── Home.js
│ │ └── index.js
│ └── Test
│ │ ├── Test.css
│ │ ├── Test.js
│ │ └── index.js
├── containers
│ ├── App.js
│ ├── HomePage.js
│ └── TestPage.js
├── index.js
├── modules
│ ├── home.js
│ ├── index.js
│ └── test.js
├── routes.js
└── store
│ ├── index.js
│ ├── store.development.js
│ └── store.production.js
└── test
├── .eslintrc
├── index.js
├── karma.conf.js
└── specs
└── Hello.spec.js
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 xfcf1
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-webpack-starter
2 | Quick start with React and antd UI
3 |
4 | # How to use
5 |
6 | 1.global install magic-cli
7 |
8 | ```
9 | $ yarn global add magic-cli
10 | ```
11 | or
12 | ```
13 | $ npm install -g magic-cli
14 | ```
15 |
16 | 2.init project in directory
17 |
18 | ```
19 | $ magic new xfcf1/react-webpack-starter my_react_project
20 | ```
21 |
22 | or you can use alias
23 |
24 | ```
25 | $ magic alias aliasName xfcf1/react-webpack-starter
26 |
27 | $ magic new aliasName my_react_project
28 | ```
29 |
--------------------------------------------------------------------------------
/meta.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | prompts: {
3 | name: {
4 | type: 'string',
5 | required: true,
6 | message: 'Project name'
7 | },
8 | description: {
9 | type: 'string',
10 | required: false,
11 | message: 'Project description',
12 | default: 'My React Project'
13 | },
14 | author: {
15 | type: 'string',
16 | message: 'Author'
17 | },
18 | port: {
19 | type: 'string',
20 | required: false,
21 | message: 'Listening port',
22 | default: '4000'
23 | },
24 | theme: {
25 | type: 'list',
26 | required: true,
27 | message: 'Select which UI framework to install',
28 | choices: ['antd', 'antd-mobile']
29 | },
30 | eslint: {
31 | type: 'confirm',
32 | require: true,
33 | message: 'Use linting with ESLint?',
34 | default: true
35 | },
36 | eslintConfig: {
37 | when: 'eslint',
38 | type: 'list',
39 | message: 'Which eslint config would you like to use?',
40 | choices: [{
41 | name: 'Standard (https://github.com/feross/standard)',
42 | value: 'standard',
43 | short: 'Standard'
44 | }, {
45 | name: 'none (configure it yourself)',
46 | value: 'none',
47 | short: 'none'
48 | }]
49 | },
50 | unit: {
51 | type: 'confirm',
52 | require: true,
53 | message: 'Setup unit tests with Karma + Mocha?',
54 | default: true
55 | }
56 | },
57 | filters: {
58 | '.eslintignore': 'eslint',
59 | '.eslintrc.js': 'eslint',
60 | 'test/**/*': 'unit'
61 | },
62 | completeMessage: 'To get started:\n\n cd {{destDirName}}\n npm install\n npm run dev'
63 | }
64 |
--------------------------------------------------------------------------------
/template/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [["import", [
3 | { "libraryName": "{{theme}}", "style": true }
4 | ]]],
5 | "presets": ["es2015", "stage-0", "react"],
6 | "env": {
7 | "production": {
8 | "plugins": [
9 | "transform-react-constant-elements",
10 | "transform-react-remove-prop-types",
11 | "transform-react-pure-class-to-function"
12 | ]
13 | },
14 | "development": {
15 | "presets": [
16 | "react-hmre"
17 | ]
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/template/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "es6": true
5 | },
6 | "extends": "standard",
7 | "parserOptions": {
8 | "ecmaFeatures": {
9 | "experimentalObjectRestSpread": true,
10 | "jsx": true
11 | },
12 | "sourceType": "module"
13 | },
14 | "plugins": [
15 | "react"
16 | ],
17 | "rules": {
18 | "quotes": [
19 | "error",
20 | "single"
21 | ],
22 | "semi": [
23 | "error",
24 | "never"
25 | ],
26 | "react/jsx-uses-react": "error",
27 | "react/jsx-uses-vars": "error",
28 | 'no-debugger': 0
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/template/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | *.log
3 | .DS_Store
4 | dist
5 | test/coverage
6 | .idea
--------------------------------------------------------------------------------
/template/build/config.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | port: '{{port}}',
3 | title: '{{name}}',
4 | vendor: [
5 | 'react',
6 | 'react-dom',
7 | 'redux',
8 | 'react-redux',
9 | 'react-router',
10 | 'redux-thunk'
11 | ],
12 | postcss: [
13 | require('postcss-cssnext')({
14 | browsers: ['last 2 versions', 'ie > 8']
15 | })
16 | ]
17 | }
18 |
19 | module.exports = config
20 |
--------------------------------------------------------------------------------
/template/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | <%= htmlWebpackPlugin.options.title %>
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/template/build/server.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const express = require('express')
3 | const webpack = require('webpack')
4 | // const DashboardPlugin = require('webpack-dashboard/plugin')
5 |
6 | const webpackConfig = require('./webpack.dev')
7 | const config = require('./config')
8 |
9 | const app = express()
10 |
11 | webpackConfig.entry.client = [
12 | 'webpack-hot-middleware/client',
13 | webpackConfig.entry.client
14 | ]
15 |
16 | const compiler = webpack(webpackConfig)
17 |
18 | // compiler.apply(new DashboardPlugin())
19 |
20 | const devMiddleWare = require('webpack-dev-middleware')(compiler, {
21 | publicPath: webpackConfig.output.publicPath,
22 | stats: {
23 | colors: true,
24 | modules: false,
25 | children: false,
26 | chunks: false,
27 | chunkModules: false
28 | }
29 | })
30 | app.use(devMiddleWare)
31 | app.use(require('webpack-hot-middleware')(compiler))
32 |
33 | app.get('*', (req, res) => {
34 | const fs = devMiddleWare.fileSystem
35 | devMiddleWare.waitUntilValid(() => {
36 | const html = fs.readFileSync(path.join(webpackConfig.output.path, './index.html'))
37 | res.end(html)
38 | })
39 | })
40 |
41 | app.listen(config.port, () => {
42 | console.log(`Listening at http://localhost:${config.port}`)
43 | })
44 |
--------------------------------------------------------------------------------
/template/build/webpack.base.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 | const HtmlWebpackPlugin = require('html-webpack-plugin')
3 |
4 | const config = require('./config')
5 |
6 | module.exports = {
7 | entry: {
8 | client: './src/index.js'
9 | },
10 | output: {
11 | path: path.join(__dirname, '../dist'),
12 | filename: '[name].js',
13 | publicPath: './'
14 | },
15 | resolve: {
16 | {{#if_eq theme 'antd'}}
17 | extensions: ['.js', '.jsx', '.json']
18 | {{else}}
19 | extensions: ['.web.js', '.js', '.jsx', '.json']
20 | {{/if_eq}}
21 | },
22 | performance: {},
23 | module: {
24 | rules: [
25 | {{#if eslint}}
26 | {
27 | enforce: 'pre',
28 | test: /.js[x]?$/,
29 | loader: 'eslint-loader',
30 | exclude: /node_modules/
31 | },
32 | {{/if}}
33 | {
34 | test: /\.js[x]?$/,
35 | loader: 'babel-loader',
36 | exclude: [/node_modules/]
37 | },
38 | {
39 | test: /\.(ico|jpg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
40 | loader: 'file-loader?limit=8192'
41 | }
42 | ]
43 | },
44 | plugins: [
45 | new HtmlWebpackPlugin({
46 | title: config.title,
47 | template: path.join(__dirname, '/index.html'),
48 | filename: './index.html'
49 | })
50 | ]
51 | }
52 |
--------------------------------------------------------------------------------
/template/build/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack')
2 | const ProgressBarPlugin = require('progress-bar-webpack-plugin')
3 |
4 | const base = require('./webpack.base')
5 | const config = require('./config')
6 |
7 | base.devtool = 'eval-source-map'
8 | base.output.publicPath = '/assets/'
9 | base.performance.hints = false
10 |
11 | // Plugins Configuration
12 | base.plugins.push(
13 | new ProgressBarPlugin(),
14 | new webpack.DefinePlugin({
15 | 'process.env.NODE_ENV': JSON.stringify('development')
16 | }),
17 | new webpack.HotModuleReplacementPlugin(),
18 | new webpack.NoErrorsPlugin(),
19 | new webpack.LoaderOptionsPlugin({
20 | options: {
21 | postcss: config.postcss
22 | }
23 | })
24 | )
25 |
26 | base.module.rules.push(
27 | {
28 | test: /\.css$/,
29 | loaders: [
30 | 'style-loader',
31 | 'css-loader?modules&localIdentName=[name]__[local]___[hash:base64:5]',
32 | 'postcss-loader'
33 | ],
34 | exclude: /node_modules/
35 | },
36 | {
37 | test: /\.css$/,
38 | loaders: [
39 | 'style-loader',
40 | 'css-loader',
41 | 'postcss-loader'
42 | ],
43 | include: /node_modules/
44 | },
45 | {
46 | test: /\.less$/,
47 | loader: 'style-loader!css-loader!less-loader'
48 | }
49 | )
50 |
51 | module.exports = base
52 |
--------------------------------------------------------------------------------
/template/build/webpack.prod.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const exec = require('child_process').execSync
3 | const webpack = require('webpack')
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin')
5 | const ProgressBarPlugin = require('progress-bar-webpack-plugin')
6 |
7 | const base = require('./webpack.base')
8 | const config = require('./config')
9 |
10 | exec('rm -rf dist/')
11 |
12 | const extractCSS = new ExtractTextPlugin('[name].css')
13 |
14 | base.entry.vendor = config.vendor
15 | base.output.filename = '[name].[chunkhash:8].js'
16 |
17 | // Plugins Configuration
18 | base.plugins.push(
19 | new ProgressBarPlugin(),
20 | new ExtractTextPlugin('styles.[contenthash:8].css'),
21 | new webpack.DefinePlugin({
22 | 'process.env.NODE_ENV': JSON.stringify('production')
23 | }),
24 | new webpack.optimize.UglifyJsPlugin({
25 | compress: {
26 | warnings: false
27 | },
28 | output: {
29 | comments: false
30 | }
31 | }),
32 | new webpack.optimize.CommonsChunkPlugin({
33 | name: 'vendor',
34 | filename: 'vendor.[chunkhash:8].js'
35 | }),
36 | new webpack.LoaderOptionsPlugin({
37 | minimize: true,
38 | options: {
39 | postcss: config.postcss
40 | }
41 | })
42 | )
43 |
44 | base.module.rules.push(
45 | {
46 | test: /\.css$/,
47 | loader: extractCSS.extract(['css-loader?modules&localIdentName=[name]__[local]___[hash:base64:5]', 'postcss-loader']),
48 | exclude: /node_modules/
49 | },
50 | {
51 | test: /\.css$/,
52 | loader: extractCSS.extract(['css-loader', 'postcss-loader']),
53 | include: /node_modules/
54 | },
55 | {
56 | test: /\.less$/,
57 | loader: extractCSS.extract(['css-loader', 'less-loader'])
58 | }
59 | )
60 | base.plugins.push(
61 | extractCSS
62 | )
63 |
64 | module.exports = base
65 |
--------------------------------------------------------------------------------
/template/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "{{name}}",
3 | "version": "{{version}}",
4 | "description": "{{description}}",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "NODE_ENV='production' webpack --config build/webpack.prod.js",
8 | "dev": "node build/server.js"{{#unit}},
9 | "unit": "karma start test/karma.conf.js --single-run"{{/unit}}
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/xfcf1/react-webpack-starter.git"
14 | },
15 | "keywords": [
16 | "smart",
17 | "react",
18 | "webpack"
19 | ],
20 | "author": "{{author}}",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/xfcf1/react-webpack-starter/issues"
24 | },
25 | "homepage": "https://github.com/xfcf1/react-webpack-starter#readme",
26 | "devDependencies": {
27 | "babel-core": "^6.21.0",
28 | "babel-loader": "^6.2.10",
29 | "babel-plugin-import": "^1.1.0",
30 | "babel-preset-es2015": "^6.18.0",
31 | "babel-preset-react": "^6.16.0",
32 | "babel-preset-react-hmre": "^1.1.1",
33 | "babel-plugin-transform-react-constant-elements": "^6.9.1",
34 | "babel-plugin-transform-react-pure-class-to-function": "^1.0.1",
35 | "babel-plugin-transform-react-remove-prop-types": "^0.2.11",
36 | "babel-preset-stage-0": "^6.16.0",
37 | "css-loader": "^0.26.0",
38 | {{#eslint}}
39 | "eslint": "^3.12.2",
40 | "eslint-loader": "^1.6.1",
41 | "eslint-plugin-react": "^6.8.0",
42 | {{#if_eq eslintConfig 'standard'}}
43 | "eslint-config-standard": "^6.2.1",
44 | "eslint-plugin-promise": "^3.4.0",
45 | "eslint-plugin-standard": "^2.0.1",
46 | {{/if_eq}}
47 | {{/eslint}}
48 | "express": "^4.14.0",
49 | "extract-text-webpack-plugin": "^2.0.0-beta.4",
50 | "html-webpack-plugin": "^2.24.1",
51 | {{#unit}}
52 | "karma": "^1.3.0",
53 | "karma-coverage": "^1.1.1",
54 | "karma-mocha": "^1.2.0",
55 | "karma-phantomjs-launcher": "^1.0.0",
56 | "karma-sinon-chai": "^1.2.0",
57 | "karma-sourcemap-loader": "^0.3.7",
58 | "karma-spec-reporter": "0.0.26",
59 | "karma-webpack": "^1.7.0",
60 | "lolex": "^1.4.0",
61 | "mocha": "^3.1.0",
62 | "chai": "^3.5.0",
63 | "sinon": "^1.17.3",
64 | "sinon-chai": "^2.8.0",
65 | "inject-loader": "^3.0.0-beta2",
66 | "isparta-loader": "^2.0.0",
67 | "phantomjs-prebuilt": "^2.1.3",
68 | {{/unit}}
69 | "less": "^2.7.1",
70 | "less-loader": "^2.2.3",
71 | "postcss-cssnext": "^2.9.0",
72 | "postcss-loader": "^1.2.1",
73 | "progress-bar-webpack-plugin": "^1.9.1",
74 | "redux-logger": "^2.7.4",
75 | "standard": "^8.6.0",
76 | "style-loader": "^0.13.1",
77 | "url-loader": "^0.5.7",
78 | "webpack": "^2.2.0-rc.2",
79 | "webpack-dashboard": "^0.2.0",
80 | "webpack-dev-middleware": "^1.9.0",
81 | "webpack-hot-middleware": "^2.13.2"
82 | },
83 | "dependencies": {
84 | "{{theme}}": "latest",
85 | "react": "15.4.0",
86 | "react-dom": "15.4.0",
87 | "react-redux": "^5.0.1",
88 | "react-router": "^3.0.0",
89 | "react-router-redux": "^4.0.0",
90 | "redux": "^3.6.0",
91 | "redux-act": "latest",
92 | "redux-thunk": "latest"
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/template/src/components/Home/Home.css:
--------------------------------------------------------------------------------
1 | .blue {
2 | color: #0f87dd;
3 | }
--------------------------------------------------------------------------------
/template/src/components/Home/Home.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { withRouter } from 'react-router'
3 | import { Button } from '{{theme}}'
4 | import styles from './Home.css'
5 |
6 | class Home extends Component {
7 | clickHandle () {
8 | const { router } = this.props
9 | router.push('test')
10 | }
11 | render () {
12 | const { homeReducer, toggleClassName } = this.props
13 | const { className } = homeReducer
14 | return (
15 |
16 |
Hello
17 |
22 |
23 |
24 |
29 |
30 | )
31 | }
32 | }
33 |
34 | export default withRouter(Home)
35 |
--------------------------------------------------------------------------------
/template/src/components/Home/index.js:
--------------------------------------------------------------------------------
1 | import Home from './Home'
2 |
3 | export default Home
4 |
--------------------------------------------------------------------------------
/template/src/components/Test/Test.css:
--------------------------------------------------------------------------------
1 | .red {
2 | color: red;
3 | }
4 | h2 span {
5 | color: blue;
6 | }
7 | span {
8 | font-size: 18pt;
9 | }
10 |
--------------------------------------------------------------------------------
/template/src/components/Test/Test.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { Link } from 'react-router'
3 | import styles from './Test.css'
4 |
5 | class Test extends Component {
6 | render () {
7 | const { homeReducer, testReducer } = this.props
8 | const { className } = homeReducer
9 | const { text } = testReducer
10 | return (
11 |
12 |
this is router test page
13 | {text}{`${className}`}
14 | link back to home page
15 |
16 | )
17 | }
18 | }
19 |
20 | export default Test
21 |
--------------------------------------------------------------------------------
/template/src/components/Test/index.js:
--------------------------------------------------------------------------------
1 | import Test from './Test'
2 |
3 | export default Test
4 |
--------------------------------------------------------------------------------
/template/src/containers/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 |
3 | class App extends Component {
4 | render () {
5 | return (
6 |
7 | { this.props.children }
8 |
9 | )
10 | }
11 | }
12 |
13 | export default App
14 |
--------------------------------------------------------------------------------
/template/src/containers/HomePage.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import { bindActionCreators } from 'redux'
3 | import Home from '../components/Home'
4 | import * as HomeActions from '../modules/home'
5 |
6 | function mapStateToProps (state) {
7 | return {
8 | homeReducer: state.homeReducer
9 | }
10 | }
11 |
12 | function mapDispatchToProps (dispatch) {
13 | return bindActionCreators(HomeActions, dispatch)
14 | }
15 |
16 | export default connect(mapStateToProps, mapDispatchToProps)(Home)
17 |
--------------------------------------------------------------------------------
/template/src/containers/TestPage.js:
--------------------------------------------------------------------------------
1 | import { connect } from 'react-redux'
2 | import { bindActionCreators } from 'redux'
3 | import Test from '../components/Test'
4 | import * as TestActions from '../modules/test'
5 |
6 | function mapStateToProps (state) {
7 | return {
8 | homeReducer: state.homeReducer,
9 | testReducer: state.testReducer
10 | }
11 | }
12 |
13 | function mapDispatchToProps (dispatch) {
14 | return bindActionCreators(TestActions, dispatch)
15 | }
16 |
17 | export default connect(mapStateToProps, mapDispatchToProps)(Test)
18 |
--------------------------------------------------------------------------------
/template/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | import { Provider } from 'react-redux'
4 | import { Router, hashHistory } from 'react-router'
5 | import { syncHistoryWithStore } from 'react-router-redux'
6 |
7 | import configureStore from './store'
8 |
9 | import routes from './routes'
10 |
11 | const store = configureStore()
12 | const history = syncHistoryWithStore(hashHistory, store)
13 |
14 | render(
15 |
16 |
17 | ,
18 | document.getElementById('app')
19 | )
20 |
--------------------------------------------------------------------------------
/template/src/modules/home.js:
--------------------------------------------------------------------------------
1 | import { createAction, createReducer } from 'redux-act'
2 |
3 | export const toggleClassName = createAction('toggle class name')
4 |
5 | const initialState = {
6 | className: true
7 | }
8 |
9 | export default createReducer({
10 | [toggleClassName]: (state, data) => ({ ...state, className: data })
11 | }, initialState)
12 |
--------------------------------------------------------------------------------
/template/src/modules/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux'
2 | import { routerReducer as routing } from 'react-router-redux'
3 |
4 | import homeReducer from './home'
5 | import testReducer from './test'
6 |
7 | const rootReducer = combineReducers({
8 | routing,
9 | homeReducer,
10 | testReducer
11 | })
12 |
13 | export default rootReducer
14 |
--------------------------------------------------------------------------------
/template/src/modules/test.js:
--------------------------------------------------------------------------------
1 | import { createReducer } from 'redux-act'
2 |
3 | const initialState = {
4 | text: 'home page className value: '
5 | }
6 |
7 | export default createReducer({}, initialState)
8 |
--------------------------------------------------------------------------------
/template/src/routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Route, IndexRoute } from 'react-router'
3 |
4 | import App from './containers/App'
5 | import HomePage from './containers/HomePage'
6 | import TestPage from './containers/TestPage'
7 |
8 | export default (
9 |
10 |
11 |
12 |
13 | )
14 |
--------------------------------------------------------------------------------
/template/src/store/index.js:
--------------------------------------------------------------------------------
1 | if (process.env.NODE_ENV === 'production') {
2 | module.exports = require('./store.production')
3 | } else {
4 | module.exports = require('./store.development')
5 | }
6 |
--------------------------------------------------------------------------------
/template/src/store/store.development.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux'
2 | import thunk from 'redux-thunk'
3 | import createLogger from 'redux-logger'
4 | import { hashHistory } from 'react-router'
5 | import { routerMiddleware } from 'react-router-redux'
6 | import rootReducer from '../modules'
7 |
8 | const logger = () => {
9 | return createLogger({
10 | level: 'info',
11 | collapsed: true
12 | })
13 | }
14 |
15 | const router = routerMiddleware(hashHistory)
16 |
17 | const enhancer = applyMiddleware(thunk, router, logger())
18 |
19 | export default function configureStore (initialState) {
20 | const store = createStore(rootReducer, initialState, enhancer)
21 |
22 | if (module.hot) {
23 | module.hot.accept('../modules', () =>
24 | store.replaceReducer(require('../modules'))
25 | )
26 | }
27 |
28 | return store
29 | }
30 |
--------------------------------------------------------------------------------
/template/src/store/store.production.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux'
2 | import thunk from 'redux-thunk'
3 | import { hashHistory } from 'react-router'
4 | import { routerMiddleware } from 'react-router-redux'
5 | import rootReducer from '../modules'
6 |
7 | const router = routerMiddleware(hashHistory)
8 |
9 | const enhancer = applyMiddleware(thunk, router)
10 |
11 | export default function configureStore (initialState) {
12 | return createStore(rootReducer, initialState, enhancer)
13 | }
14 |
--------------------------------------------------------------------------------
/template/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | },
5 | "globals": {
6 | "expect": true,
7 | "sinon": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/template/test/index.js:
--------------------------------------------------------------------------------
1 | // Polyfill fn.bind() for PhantomJS
2 | /* eslint-disable no-extend-native */
3 | Function.prototype.bind = require('function-bind')
4 |
5 | // require all test files (files that ends with .spec.js)
6 | const testsContext = require.context('./specs', true, /\.spec$/)
7 | testsContext.keys().forEach(testsContext)
8 |
9 | // require all src files except main.js for coverage.
10 | // you can also change this to match only the subset of files that
11 | // you want coverage for.
12 | const srcContext = require.context('../src', true, /^\.\/(?!index(\.js)?$)/)
13 | srcContext.keys().forEach(srcContext)
14 |
--------------------------------------------------------------------------------
/template/test/karma.conf.js:
--------------------------------------------------------------------------------
1 | // This is a karma config file. For more details see
2 | // http://karma-runner.github.io/0.13/config/configuration-file.html
3 | // we are also using it with karma-webpack
4 | // https://github.com/webpack/karma-webpack
5 |
6 | const path = require('path')
7 | const webpack = require('webpack')
8 | const base = require('../build/webpack.base')
9 |
10 | const config = require('../build/config')
11 |
12 | base.plugins.push(
13 | new webpack.DefinePlugin({
14 | 'process.env.NODE_ENV': JSON.stringify('test')
15 | }),
16 | new webpack.LoaderOptionsPlugin({
17 | minimize: true,
18 | options: {
19 | babel: config.babel,
20 | postcss: config.postcss,
21 | vue: {
22 | loaders: {
23 | css: 'style-loader!css-loader!postcss-loader'
24 | },
25 | postcss: config.postcss
26 | }
27 | }
28 | })
29 | )
30 |
31 | base.module.rules.push({
32 | test: /\.css$/,
33 | loader: 'style-loader!css-loader!postcss-loader'
34 | })
35 |
36 | // no need for app entry during tests
37 | delete base.entry
38 |
39 | // make sure isparta loader is applied before eslint
40 | base.module.rules.unshift({
41 | enforce: 'pre',
42 | test: /\.js$/,
43 | loader: 'isparta-loader',
44 | include: path.resolve('../src')
45 | })
46 |
47 | // only apply babel for test files when using isparta
48 | base.module.rules.some((loader, i) => {
49 | if (loader.loader === 'babel') {
50 | loader.include = path.resolve('../test')
51 | return true
52 | }
53 | })
54 |
55 | module.exports = (config) => {
56 | config.set({
57 | // to run in additional browsers:
58 | // 1. install corresponding karma launcher
59 | // http://karma-runner.github.io/0.13/config/browsers.html
60 | // 2. add it to the `browsers` array below.
61 | browsers: ['PhantomJS'],
62 | frameworks: ['mocha', 'sinon-chai'],
63 | reporters: ['spec', 'coverage'],
64 | files: ['./index.js'],
65 | preprocessors: {
66 | './index.js': ['webpack', 'sourcemap']
67 | },
68 | webpack: base,
69 | webpackMiddleware: {
70 | noInfo: true
71 | },
72 | coverageReporter: {
73 | dir: './coverage',
74 | reporters: [
75 | { type: 'lcov', subdir: '.' },
76 | { type: 'text-summary' }
77 | ]
78 | }
79 | })
80 | }
81 |
--------------------------------------------------------------------------------
/template/test/specs/Hello.spec.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 | // import Home from 'components/Home'
4 |
5 | describe('Hello', () => {
6 | it('should render correct contents', () => {
7 | render(Hello
, document.querySelector('#app'))
8 |
9 | expect(document.querySelector('#hello').textContent)
10 | .to.equal('Hello')
11 | })
12 | })
13 |
--------------------------------------------------------------------------------