├── app
├── vendors
│ └── .gitkeep
├── assets
│ ├── scss
│ │ └── main.scss
│ └── images
│ │ └── react_logo_512x512.png
├── index.html
├── config
│ └── Root.js
├── components
│ └── App.js
└── main.js
├── config
└── jest
│ ├── shim.js
│ └── assetsTransformer.js
├── .babelrc
├── .github
└── ISSUE_TEMPLATE.md
├── .travis.yml
├── tests
└── components
│ └── App.test.js
├── .eslintrc
├── LICENSE
├── .gitignore
├── README.md
├── package.json
├── webpack.production.config.js
└── webpack.config.js
/app/vendors/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/assets/scss/main.scss:
--------------------------------------------------------------------------------
1 | $color: #262828;
2 |
3 | body {
4 | color: $color;
5 | }
--------------------------------------------------------------------------------
/app/assets/images/react_logo_512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/KleoPetroff/react-webpack-boilerplate/HEAD/app/assets/images/react_logo_512x512.png
--------------------------------------------------------------------------------
/config/jest/shim.js:
--------------------------------------------------------------------------------
1 | // A solution for React 16 complaining of missing rAF.
2 |
3 | global.requestAnimationFrame = function(callback) {
4 | setTimeout(callback, 0);
5 | };
6 |
--------------------------------------------------------------------------------
/config/jest/assetsTransformer.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | process(src, filename, config, options) {
5 | return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/app/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ReactJS Boilerplate
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-react",
4 | [ "@babel/preset-env", {
5 | "targets": {
6 | "browsers": "> 2%"
7 | }
8 | }]
9 | ],
10 |
11 | "plugins": [
12 | "react-hot-loader/babel"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## The problem
2 |
3 | Briefly describe the issue you are experiencing (or the change you are proposing). Tell what you are trying to do and what happened instead.
4 |
5 | ## Details
6 |
7 | Describe in more detail the problem you have been experiencing, if necessary. If you are proposing a change, describe the intensions and gains of your propose.
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | cache:
4 | directories:
5 | - node_modules
6 | branches:
7 | only:
8 | - master
9 | - /^greenkeeper/.*$/
10 | notifications:
11 | email: false
12 | node_js:
13 | - "node"
14 | - "lts/*"
15 | before_install:
16 | - npm i -g npm
17 | before_script:
18 | - npm prune
19 | script:
20 | - npm run test
21 | install: npm install
--------------------------------------------------------------------------------
/app/config/Root.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
3 | import App from '../components/App';
4 |
5 | const Root = () => {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | );
13 | };
14 |
15 | export default Root;
16 |
17 |
--------------------------------------------------------------------------------
/app/components/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import img from '../assets/images/react_logo_512x512.png';
3 |
4 | const App = () => {
5 | return (
6 |
7 |
Hello ReactJS
8 |

16 |
17 | );
18 | };
19 |
20 | export default App;
21 |
--------------------------------------------------------------------------------
/app/main.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import { AppContainer } from 'react-hot-loader';
4 |
5 | import Root from './config/Root';
6 |
7 | const render = (Component) => {
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | document.getElementById('root'),
13 | );
14 | };
15 |
16 | render(Root);
17 |
18 | if (module.hot) {
19 | module.hot.accept('./config/Root', () => {
20 | const newApp = require('./config/Root').default;
21 | render(newApp);
22 | });
23 | }
24 |
--------------------------------------------------------------------------------
/tests/components/App.test.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { shallow, configure } from 'enzyme';
3 | import Adapter from 'enzyme-adapter-react-16';
4 |
5 | import App from '../../app/components/App';
6 |
7 | configure({ adapter: new Adapter() });
8 |
9 | describe('App Component', () => {
10 | let wrapper;
11 |
12 | beforeEach(() => {
13 | wrapper = shallow();
14 | });
15 |
16 | it('should exist', () => {
17 | expect(wrapper).toBeTruthy();
18 | });
19 |
20 | it('should have one heading', () => {
21 | expect(wrapper.find('#heading').type()).toEqual('h2');
22 | });
23 | });
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "airbnb",
3 | "parser": "babel-eslint",
4 | "parserOptions": {
5 | "ecmaVersion": 6,
6 | "ecmaFeatures": {
7 | "jsx": true
8 | }
9 | },
10 | "env": {
11 | "browser": true,
12 | "node": true,
13 | "mocha": true,
14 | "es6": true
15 | },
16 | "rules": {
17 | "space-before-function-paren": "off",
18 | "react/prefer-stateless-function": "warn",
19 | "react/jsx-one-expression-per-line": "off",
20 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
21 | "linebreak-style": "off",
22 | "global-require": "off",
23 | "semi": "warn",
24 | "arrow-body-style": "off",
25 | "no-multiple-empty-lines": ["warn", {"max": 1}]
26 | }
27 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Kliment Petrov
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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### JetBrains template
3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
4 |
5 | *.iml
6 |
7 | ## Directory-based project format:
8 | .idea/
9 | # if you remove the above rule, at least ignore the following:
10 |
11 | # User-specific stuff:
12 | # .idea/workspace.xml
13 | # .idea/tasks.xml
14 | # .idea/dictionaries
15 |
16 | # Sensitive or high-churn files:
17 | # .idea/dataSources.ids
18 | # .idea/dataSources.xml
19 | # .idea/sqlDataSources.xml
20 | # .idea/dynamic.xml
21 | # .idea/uiDesigner.xml
22 |
23 | # Gradle:
24 | # .idea/gradle.xml
25 | # .idea/libraries
26 |
27 | # Mongo Explorer plugin:
28 | # .idea/mongoSettings.xml
29 |
30 | ## File-based project format:
31 | *.ipr
32 | *.iws
33 |
34 | ## Plugin-specific files:
35 |
36 | # IntelliJ
37 | /out/
38 |
39 | # mpeltonen/sbt-idea plugin
40 | .idea_modules/
41 |
42 | # JIRA plugin
43 | atlassian-ide-plugin.xml
44 |
45 | # Crashlytics plugin (for Android Studio and IntelliJ)
46 | com_crashlytics_export_strings.xml
47 | crashlytics.properties
48 | crashlytics-build.properties
49 |
50 | # NodeJS modules folder
51 | node_modules
52 |
53 | # npm debug log
54 | npm-debug.log
55 |
56 | # vs code
57 | .vscode
58 |
59 | # coverage
60 |
61 | /coverage
62 |
63 | # Dist folder
64 | dist/
65 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ES6 React boilerplate using Webpack
2 |
3 | [](https://github.com/KleoPetroff/react-webpack-boilerplate) [](http://makeapullrequest.com)
4 |
5 | :warning: :warning: :warning:
6 |
7 | As of 29.12.2018, the project is **DEPRECATED** and no further development is planned. If you are looking for an alternative, check [create-react-app](https://github.com/facebook/create-react-app).
8 |
9 | Simple and optimized React boilerplate. It includes:
10 |
11 | - [x] React 16.5.2
12 | - [x] ECMAScript 6+ and JSX support
13 | - [x] React Router v4
14 | - [x] Component testing using [Enzyme](https://github.com/airbnb/enzyme) and [Jest](https://facebook.github.io/jest)
15 | - [x] Code Coverage
16 | - [x] Latest Webpack (v.4.16.5), Babel 7 and Webpack Dev Server (v.4.19.1) with Scope Hoisting enabled
17 | - [x] Hot Module Replacement using [react-hot-loader](https://github.com/gaearon/react-hot-loader)
18 | - [x] ES6 linting with continuous linting on file change
19 | - [x] SASS support
20 | - [x] Separate CSS stylesheets generation
21 | - [x] Automatic HTML generation
22 | - [x] Production Config
23 | - [x] Custom Babel Preset with Decorators, Class Properties, Rest/Spread operator support
24 | - [x] Export Separate Vendor Files
25 |
26 | ## Starting the dev server
27 |
28 | Make sure you have the latest Stable or LTS version of Node.js installed.
29 |
30 | 1. `git clone https://github.com/KleoPetroff/react-webpack-boilerplate.git`
31 | 2. Run `npm install` or `yarn install`
32 | 3. Start the dev server using `npm start`
33 | 3. Open [http://localhost:8080](http://localhost:8080)
34 |
35 | ## Available Commands
36 |
37 | - `npm start` - start the dev server
38 | - `npm clean` - delete the dist folder
39 | - `npm run production` - create a production ready build in `dist` folder
40 | - `npm run lint` - execute an eslint check
41 | - `npm test` - run all tests
42 | - `npm run test:watch` - run all tests in watch mode
43 | - `npm run coverage` - generate code coverage report in the `coverage` folder
44 |
45 | ## Vendor Exporting
46 |
47 | You can export specific vendors in separate files and load them. All vendors should be included in `app/vendors` and will be exported in a `vendors` folder under `dist`. The main idea is to serve independent JavaScript and CSS libraries, though currently all file formats are supported.
48 |
49 | ! Don't forget to add the vendors in `app/index.html` and `build/index.html`.
50 |
51 | ## Code Coverage
52 |
53 | The project is using the Jest Code Coverage tool. The reports are generated by running `npm run coverage`. All configurations are located in `package.json`, inside the `jest` object.
54 |
55 | The coverage report consists of an HTML reporter, which can be viewed in the browser and some helper coverage files like the coverage json and xml file.
56 |
57 | ## Production code
58 |
59 | Run `npm run production`. The production-ready code will be located under `dist` folder.
60 |
61 | ## Licence
62 |
63 | _react-webpack-boilerplate_ is available under MIT.
64 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-webpack-boilerplate",
3 | "version": "2.1.0",
4 | "description": "Minimalistic ES6 React boilerplate",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "webpack-dev-server",
8 | "clean": "rimraf dist",
9 | "production": "cross-env npm run clean && webpack --config webpack.production.config.js --progress --profile --colors",
10 | "lint": "eslint ./app/**/**.js",
11 | "test": "jest",
12 | "test:watch": "jest --watch",
13 | "coverage": "jest --coverage",
14 | "precommit": "cross-env npm run lint && npm test",
15 | "prepush": "cross-env npm run lint && npm test",
16 | "webpack:dev": "webpack --colors",
17 | "webpack:prod": "webpack --config webpack.production.config.js --colors",
18 | "heroku-postbuild": "npm run production"
19 | },
20 | "author": "Kliment Petrov ",
21 | "license": "MIT",
22 | "jest": {
23 | "setupFiles": [
24 | "/config/jest/shim.js"
25 | ],
26 | "collectCoverageFrom": [
27 | "app/**/*.{js,jsx,ts,tsx}"
28 | ],
29 | "moduleNameMapper": {
30 | "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/config/jest/assetsTransformer.js",
31 | "\\.(css|less)$": "/config/jest/assetsTransformer.js"
32 | },
33 | "testMatch": [
34 | "**/?(*.)(spec|test).js?(x)"
35 | ],
36 | "transform": {
37 | "\\.js$": "babel-jest"
38 | }
39 | },
40 | "dependencies": {
41 | "cross-env": "5.2.0",
42 | "react": "16.6.1",
43 | "react-dom": "16.6.1",
44 | "react-hot-loader": "4.6.3",
45 | "react-router-dom": "4.3.0"
46 | },
47 | "devDependencies": {
48 | "@babel/cli": "7.2.3",
49 | "@babel/core": "^7.2.2",
50 | "@babel/preset-env": "7.2.3",
51 | "@babel/preset-react": "7.0.0",
52 | "@babel/preset-stage-2": "7.0.0",
53 | "@babel/register": "7.0.0",
54 | "babel-core": "^7.0.0-bridge.0",
55 | "babel-eslint": "9.0.0",
56 | "babel-jest": "^23.6.0",
57 | "babel-loader": "8.0.3",
58 | "babel-preset-react-hmre": "1.1.1",
59 | "copy-webpack-plugin": "4.6.0",
60 | "css-hot-loader": "1.4.3",
61 | "css-loader": "2.1.0",
62 | "enzyme": "3.7.0",
63 | "enzyme-adapter-react-16": "1.7.1",
64 | "eslint": "5.0.0",
65 | "eslint-config-airbnb": "17.1.0",
66 | "eslint-loader": "2.1.1",
67 | "eslint-plugin-import": "2.13.0",
68 | "eslint-plugin-jsx-a11y": "6.1.2",
69 | "eslint-plugin-react": "7.11.1",
70 | "extract-text-webpack-plugin": "4.0.0-beta.0",
71 | "file-loader": "3.0.1",
72 | "html-webpack-plugin": "3.2.0",
73 | "husky": "1.3.1",
74 | "jest": "23.6.0",
75 | "node-sass": "4.11.0",
76 | "open-browser-webpack-plugin": "0.0.5",
77 | "react-addons-test-utils": "15.6.2",
78 | "react-test-renderer": "16.6.0",
79 | "regenerator-runtime": "0.13.0",
80 | "rimraf": "2.6.2",
81 | "sass-loader": "7.1.0",
82 | "style-loader": "0.23.1",
83 | "uglifyjs-webpack-plugin": "2.1.1",
84 | "url-loader": "1.1.2",
85 | "webpack": "4.27.0",
86 | "webpack-cli": "3.1.2",
87 | "url-loader": "1.1.2",
88 | "webpack": "4.27.0",
89 | "webpack-cli": "3.1.2",
90 | "webpack-dev-server": "3.1.10"
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/webpack.production.config.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path');
2 | const webpack = require('webpack');
3 | const HtmlWebpackPlugin = require('html-webpack-plugin');
4 | const CopyWebpackPlugin = require('copy-webpack-plugin');
5 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
6 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
7 |
8 | const config = {
9 | stats: {
10 | maxModules: 0
11 | },
12 | mode: 'production',
13 | devtool: 'cheap-module-source-map',
14 |
15 | entry: [
16 | './main.js',
17 | './assets/scss/main.scss',
18 | ],
19 |
20 | context: resolve(__dirname, 'app'),
21 |
22 | output: {
23 | filename: '[name].[chunkhash].js',
24 | chunkFilename: '[name].[chunkhash].js',
25 | path: resolve(__dirname, 'dist'),
26 | publicPath: '',
27 | },
28 |
29 | plugins: [
30 | new webpack.optimize.ModuleConcatenationPlugin(),
31 | new HtmlWebpackPlugin({
32 | template: `${__dirname}/app/index.html`,
33 | filename: 'index.html',
34 | inject: 'body',
35 | }),
36 | new webpack.optimize.OccurrenceOrderPlugin(),
37 | new webpack.LoaderOptionsPlugin({
38 | minimize: true,
39 | debug: false,
40 | }),
41 | new webpack.DefinePlugin({ 'process.env': { NODE_ENV: JSON.stringify('production') } }),
42 | new ExtractTextPlugin({ filename: './styles/style.css', disable: false, allChunks: true }),
43 | new CopyWebpackPlugin([{ from: './vendors', to: 'vendors' }]),
44 | ],
45 |
46 | optimization: {
47 | runtimeChunk: false,
48 | splitChunks: {
49 | cacheGroups: {
50 | commons: {
51 | test: /[\\/]node_modules[\\/]/,
52 | name: 'vendors',
53 | chunks: 'all',
54 | },
55 | },
56 | },
57 | minimizer: [
58 | new UglifyJsPlugin({
59 | cache: true,
60 | parallel: true,
61 | sourceMap: true
62 | })
63 | ]
64 | },
65 |
66 | resolve: {
67 | extensions: ['.js', '.jsx'],
68 | },
69 |
70 | module: {
71 | rules: [
72 | {
73 | test: /\.jsx?$/,
74 | exclude: /node_modules/,
75 | loader: 'babel-loader',
76 | },
77 | {
78 | test: /\.scss$/,
79 | exclude: /node_modules/,
80 | use: ExtractTextPlugin.extract({
81 | fallback: 'style-loader',
82 | use: [
83 | 'css-loader',
84 | { loader: 'sass-loader', query: { sourceMap: false } },
85 | ],
86 | publicPath: '../'
87 | }),
88 | },
89 | {
90 | test: /\.(png|jpg|gif)$/,
91 | use: [
92 | {
93 | loader: 'url-loader',
94 | options: {
95 | limit: 8192,
96 | mimetype: 'image/png',
97 | name: 'images/[name].[ext]',
98 | }
99 | }
100 | ],
101 | },
102 | {
103 | test: /\.eot(\?v=\d+.\d+.\d+)?$/,
104 | use: [
105 | {
106 | loader: 'file-loader',
107 | options: {
108 | name: 'fonts/[name].[ext]'
109 | }
110 | }
111 | ],
112 | },
113 | {
114 | test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
115 | use: [
116 | {
117 | loader: 'url-loader',
118 | options: {
119 | limit: 8192,
120 | mimetype: 'application/font-woff',
121 | name: 'fonts/[name].[ext]',
122 | }
123 | }
124 | ],
125 | },
126 | {
127 | test: /\.[ot]tf(\?v=\d+.\d+.\d+)?$/,
128 | use: [
129 | {
130 | loader: 'url-loader',
131 | options: {
132 | limit: 8192,
133 | mimetype: 'application/octet-stream',
134 | name: 'fonts/[name].[ext]',
135 | }
136 | }
137 | ],
138 | },
139 | {
140 | test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
141 | use: [
142 | {
143 | loader: 'url-loader',
144 | options: {
145 | limit: 8192,
146 | mimetype: 'image/svg+xml',
147 | name: 'images/[name].[ext]',
148 | }
149 | }
150 | ],
151 | },
152 | ]
153 | },
154 | };
155 |
156 | module.exports = config;
157 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path');
2 |
3 | const webpack = require('webpack');
4 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
5 | const CopyWebpackPlugin = require('copy-webpack-plugin');
6 | const OpenBrowserPlugin = require('open-browser-webpack-plugin');
7 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
8 |
9 | const config = {
10 | stats: {
11 | maxModules: 0
12 | },
13 | mode: 'development',
14 | devtool: 'cheap-module-eval-source-map',
15 |
16 | entry: [
17 | 'react-hot-loader/patch',
18 | 'webpack-dev-server/client?http://localhost:8080',
19 | 'webpack/hot/only-dev-server',
20 | './main.js',
21 | './assets/scss/main.scss',
22 | ],
23 |
24 | output: {
25 | filename: 'bundle.js',
26 | path: resolve(__dirname, 'dist'),
27 | publicPath: '',
28 | },
29 |
30 | context: resolve(__dirname, 'app'),
31 |
32 | devServer: {
33 | hot: true,
34 | contentBase: resolve(__dirname, 'build'),
35 | historyApiFallback: true,
36 | publicPath: '/'
37 | },
38 |
39 | resolve: {
40 | extensions: ['.js', '.jsx'],
41 | },
42 |
43 | module: {
44 | rules: [
45 | {
46 | enforce: "pre",
47 | test: /\.jsx?$/,
48 | exclude: /node_modules/,
49 | loader: "eslint-loader"
50 | },
51 | {
52 | test: /\.jsx?$/,
53 | loaders: [
54 | 'babel-loader',
55 | ],
56 | exclude: /node_modules/,
57 | },
58 | {
59 | test: /\.scss$/,
60 | exclude: /node_modules/,
61 | use: ['css-hot-loader'].concat(ExtractTextPlugin.extract({
62 | fallback: 'style-loader',
63 | use: [
64 | 'css-loader',
65 | {
66 | loader: 'sass-loader',
67 | query: {
68 | sourceMap: false,
69 | },
70 | },
71 | ],
72 | publicPath: '../'
73 | })),
74 | },
75 | {
76 | test: /\.(png|jpg|gif)$/,
77 | use: [
78 | {
79 | loader: 'url-loader',
80 | options: {
81 | limit: 8192,
82 | mimetype: 'image/png',
83 | name: 'images/[name].[ext]',
84 | }
85 | }
86 | ],
87 | },
88 | {
89 | test: /\.eot(\?v=\d+.\d+.\d+)?$/,
90 | use: [
91 | {
92 | loader: 'file-loader',
93 | options: {
94 | name: 'fonts/[name].[ext]'
95 | }
96 | }
97 | ],
98 | },
99 | {
100 | test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
101 | use: [
102 | {
103 | loader: 'url-loader',
104 | options: {
105 | limit: 8192,
106 | mimetype: 'application/font-woff',
107 | name: 'fonts/[name].[ext]',
108 | }
109 | }
110 | ],
111 | },
112 | {
113 | test: /\.[ot]tf(\?v=\d+.\d+.\d+)?$/,
114 | use: [
115 | {
116 | loader: 'url-loader',
117 | options: {
118 | limit: 8192,
119 | mimetype: 'application/octet-stream',
120 | name: 'fonts/[name].[ext]',
121 | }
122 | }
123 | ],
124 | },
125 | {
126 | test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
127 | use: [
128 | {
129 | loader: 'url-loader',
130 | options: {
131 | limit: 8192,
132 | mimetype: 'image/svg+xml',
133 | name: 'images/[name].[ext]',
134 | }
135 | }
136 | ],
137 | },
138 | ]
139 | },
140 |
141 | plugins: [
142 | new webpack.NamedModulesPlugin(),
143 | new webpack.LoaderOptionsPlugin({
144 | test: /\.jsx?$/,
145 | options: {
146 | eslint: {
147 | configFile: resolve(__dirname, '.eslintrc'),
148 | cache: false,
149 | }
150 | },
151 | }),
152 | new webpack.optimize.ModuleConcatenationPlugin(),
153 | new ExtractTextPlugin({ filename: './styles/style.css', disable: false, allChunks: true }),
154 | new CopyWebpackPlugin([{ from: 'vendors', to: 'vendors' }]),
155 | new OpenBrowserPlugin({ url: 'http://localhost:8080' }),
156 | new webpack.HotModuleReplacementPlugin(),
157 | ]
158 | };
159 |
160 | module.exports = config;
161 |
--------------------------------------------------------------------------------