From 434f45c97c6917c741543f4a03be6d38363beba5 Mon Sep 17 00:00:00 2001 From: Andrey Okonetchnikov Date: Wed, 26 Oct 2016 19:51:35 +0200 Subject: [PATCH] Webpack optimizations (#140) * Less repetition in webpack configs. Minify CSS classnames in production. * Ignore all optional deps of moment.js. Fixes #138 * Added target to webpack config * Automatically extract all 3rd party modules into a separate 'vendor' chunk * Inline only assets that are smaller than 10KB * Added autoprefixer options * Replaced sinfle babel transforms with the stage-1 preset. Cleaned up webpack configs. * Do not include hot module replacement in production --- .babelrc | 4 ++-- package.json | 4 +--- src/index.js | 2 +- src/store/configureStore.js | 2 +- webpack.base.js | 36 ++++++++++++++---------------------- webpack.dev.js | 33 ++++++++++++++++++++++++++------- webpack.prod.js | 34 +++++++++++++++++++++------------- yarn.lock | 12 +++--------- 8 files changed, 69 insertions(+), 58 deletions(-) diff --git a/.babelrc b/.babelrc index 36c73acb..990f92a8 100644 --- a/.babelrc +++ b/.babelrc @@ -1,4 +1,4 @@ { - "presets": ["react", "es2015"], - "plugins": ["transform-class-properties", "transform-object-assign", "transform-object-rest-spread", "lodash"] + "presets": ["es2015", "stage-1", "react"], + "plugins": ["lodash"] } diff --git a/package.json b/package.json index 49b4ac9d..9f123f68 100644 --- a/package.json +++ b/package.json @@ -50,11 +50,9 @@ "babel-jest": "^15.0.0", "babel-loader": "^6.2.2", "babel-plugin-lodash": "^3.2.0", - "babel-plugin-transform-class-properties": "^6.5.2", - "babel-plugin-transform-object-assign": "^6.5.0", - "babel-plugin-transform-object-rest-spread": "^6.5.0", "babel-preset-es2015": "^6.5.0", "babel-preset-react": "^6.5.0", + "babel-preset-stage-1": "^6.16.0", "babel-runtime": "^6.5.0", "css-loader": "^0.23.1", "enzyme": "^2.4.1", diff --git a/src/index.js b/src/index.js index ba980758..a9dc35ad 100644 --- a/src/index.js +++ b/src/index.js @@ -22,7 +22,7 @@ render(( ), el); -if (module.hot) { +if (process.env.NODE_ENV !== 'production' && module.hot) { module.hot.accept('./root', () => { const NextRoot = require('./root').default; render(( diff --git a/src/store/configureStore.js b/src/store/configureStore.js index 19fe8a45..e49488de 100644 --- a/src/store/configureStore.js +++ b/src/store/configureStore.js @@ -8,7 +8,7 @@ export default function configureStore(initialState) { window.devToolsExtension ? window.devToolsExtension() : f => f )); - if (module.hot) { + if (process.env.NODE_ENV !== 'production' && module.hot) { // Enable Webpack hot module replacement for reducers module.hot.accept('../reducers/combinedReducer', () => { const nextReducer = require('../reducers/combinedReducer') // eslint-disable-line diff --git a/webpack.base.js b/webpack.base.js index cf66559c..8f906c19 100644 --- a/webpack.base.js +++ b/webpack.base.js @@ -1,13 +1,14 @@ /* eslint global-require: 0 */ const webpack = require('webpack'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = { module: { loaders: [ { test: /\.(png|eot|woff|woff2|ttf|svg|gif)(\?v=\d+\.\d+\.\d+)?$/, - loader: 'url-loader?limit=100000', + loader: 'url-loader?limit=10000', }, { test: /\.json$/, @@ -15,39 +16,30 @@ module.exports = { }, { test: /\.scss$/, - loader: 'style!css?modules!sass', + loader: ExtractTextPlugin.extract('style', 'css?modules!sass'), }, { test: /\.css$/, - loader: 'style!css?modules&importLoaders=1&&localIdentName=cms__[name]__[local]!postcss', + loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&&localIdentName=cms__[name]__[local]!postcss'), }, { loader: 'babel', test: /\.js?$/, - exclude: /(node_modules|bower_components)/, - query: { - cacheDirectory: true, - presets: ['react', 'es2015'], - plugins: [ - 'transform-class-properties', - 'transform-object-assign', - 'transform-object-rest-spread', - 'lodash', - 'react-hot-loader/babel', - ], - }, + exclude: /node_modules/, }, ], }, postcss: [ require('postcss-import')({ addDependencyTo: webpack }), - require('postcss-cssnext'), - ], - plugins: [ - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: JSON.stringify(process.env.NODE_ENV), - }, + require('postcss-cssnext')({ + browsers: ['last 2 versions', 'IE > 10'], }), ], + plugins: [ + new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), // Ignore all optional deps of moment.js + new webpack.ProvidePlugin({ + fetch: 'imports?this=>global!exports?global.fetch!whatwg-fetch', + }), + ], + target: 'web', // Make web variables accessible to webpack, e.g. window }; diff --git a/webpack.dev.js b/webpack.dev.js index 894dfef0..45975e9f 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -2,6 +2,8 @@ const path = require('path'); const webpack = require('webpack'); const merge = require('webpack-merge'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); + const HOST = 'localhost'; const PORT = '8080'; @@ -9,29 +11,46 @@ module.exports = merge.smart(require('./webpack.base.js'), { entry: { cms: [ 'webpack/hot/dev-server', - `webpack-dev-server/client?http://${HOST}:${PORT}/`, + `webpack-dev-server/client?http://${ HOST }:${ PORT }/`, 'react-hot-loader/patch', - './index' + './index', ], }, output: { path: path.join(__dirname, 'dist'), filename: '[name].js', - publicPath: `http://${HOST}:${PORT}/`, + publicPath: `http://${ HOST }:${ PORT }/`, }, context: path.join(__dirname, 'src'), + module: { + loaders: [ + { + loader: 'babel', + test: /\.js?$/, + exclude: /node_modules/, + query: { + plugins: [ + 'react-hot-loader/babel', + ], + }, + }, + ], + }, plugins: [ + new webpack.DefinePlugin({ + 'process.env': { + NODE_ENV: JSON.stringify('development'), + }, + }), new webpack.optimize.OccurenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), - new webpack.ProvidePlugin({ - 'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch' - }) + new ExtractTextPlugin('[name].css', { disable: true }), ], devServer: { hot: true, contentBase: 'example/', historyApiFallback: true, - devTool: 'cheap-module-source-map' + devTool: 'cheap-module-source-map', }, }); diff --git a/webpack.prod.js b/webpack.prod.js index c9f5b26f..ca79b8e6 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -6,9 +6,7 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin'); module.exports = merge.smart(require('./webpack.base.js'), { entry: { - cms: [ - './index', - ], + cms: './index', }, output: { path: path.join(__dirname, 'dist'), @@ -16,26 +14,36 @@ module.exports = merge.smart(require('./webpack.base.js'), { }, module: { loaders: [ - { - test: /\.scss$/, - loader: ExtractTextPlugin.extract('style', 'css?modules!sass'), - }, { test: /\.css$/, - loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1&&localIdentName=cms__[name]__[local]!postcss'), + loader: ExtractTextPlugin.extract('style', 'css?modules&importLoaders=1!postcss'), // Use minified class names on production }, ], }, context: path.join(__dirname, 'src'), plugins: [ new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) - }), - new webpack.ProvidePlugin({ - 'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch' + 'process.env': { + NODE_ENV: JSON.stringify('production'), + }, }), new webpack.optimize.OccurenceOrderPlugin(), - new webpack.optimize.UglifyJsPlugin(), + + // Minify and optimize the JavaScript + new webpack.optimize.UglifyJsPlugin({ + compress: { + // ...but do not show warnings in the console (there is a lot of them) + warnings: false, + }, + }), + + // Extract CSS new ExtractTextPlugin('[name].css', { allChunks: true }), + + // Automatically extract all 3rd party modules into a separate 'vendor' chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks: ({ resource }) => /node_modules/.test(resource), + }), ], }); diff --git a/yarn.lock b/yarn.lock index 8136f192..7d29e640 100644 --- a/yarn.lock +++ b/yarn.lock @@ -659,7 +659,7 @@ babel-plugin-transform-class-constructor-call@^6.3.13: babel-runtime "^6.0.0" babel-template "^6.8.0" -babel-plugin-transform-class-properties@^6.16.0, babel-plugin-transform-class-properties@^6.5.2: +babel-plugin-transform-class-properties@^6.16.0: version "6.16.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.16.0.tgz#969bca24d34e401d214f36b8af5c1346859bc904" dependencies: @@ -889,13 +889,7 @@ babel-plugin-transform-global-system-wrapper@0.0.1: dependencies: babel-template "^6.9.0" -babel-plugin-transform-object-assign@^6.5.0: - version "6.8.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-assign/-/babel-plugin-transform-object-assign-6.8.0.tgz#76e17f2dc0f36f14f548b9afd7aaef58d29ebb75" - dependencies: - babel-runtime "^6.0.0" - -babel-plugin-transform-object-rest-spread@^6.16.0, babel-plugin-transform-object-rest-spread@^6.5.0: +babel-plugin-transform-object-rest-spread@^6.16.0: version "6.16.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.16.0.tgz#db441d56fffc1999052fdebe2e2f25ebd28e36a9" dependencies: @@ -1018,7 +1012,7 @@ babel-preset-stage-0@^6.5.0: babel-plugin-transform-function-bind "^6.3.13" babel-preset-stage-1 "^6.16.0" -babel-preset-stage-1@^6.16.0: +babel-preset-stage-1, babel-preset-stage-1@^6.16.0: version "6.16.0" resolved "https://registry.yarnpkg.com/babel-preset-stage-1/-/babel-preset-stage-1-6.16.0.tgz#9d31fbbdae7b17c549fd3ac93e3cf6902695e479" dependencies: