Skip to content

Instantly share code, notes, and snippets.

@notafunction
Last active May 30, 2017 23:42
Show Gist options
  • Select an option

  • Save notafunction/93e29c2b475113753ddf6d5bf53513b9 to your computer and use it in GitHub Desktop.

Select an option

Save notafunction/93e29c2b475113753ddf6d5bf53513b9 to your computer and use it in GitHub Desktop.
Tree-shaking Webpack 2 configuration for React projects.
const webpack = require('webpack');
const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PreloadWebpackPlugin = require('preload-webpack-plugin');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const host = process.env.HOST || 'localhost';
const port = process.env.PORT || '3000';
const sourcePath = path.join(__dirname, 'src');
const buildPath = path.join(__dirname, 'build');
const stats = {
assets: true,
children: false,
chunks: false,
hash: false,
modules: false,
publicPath: false,
timings: true,
version: false,
warnings: true,
colors: {
green: '\u001b[32m',
},
};
module.exports = function(env) {
const nodeEnv = env && env.prod ? 'production' : 'development';
const isProd = env === 'production';
const serviceWorkerBuild = env && env.sw;
// Define common plugins
const plugins = [
new webpack.DefinePlugin({
'process.env': { NODE_ENV: JSON.stringify(nodeEnv) }
}),
new webpack.optimize.CommonsChunkPlugin({
async: true,
children: true,
minChunks: 2,
}),
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src', 'index.ejs'),
inject: true,
production: isProd,
minify: isProd && {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
new ScriptExtHtmlWebpackPlugin({
defaultAttribute: 'async',
}),
new PreloadWebpackPlugin(),
];
if (isProd) {
// Production plugins
plugins.push(
// minify remove some of the dead code
new UglifyJSPlugin({
compress: {
warnings: false,
screw_ie8: true,
conditionals: true,
unused: true,
comparisons: true,
sequences: true,
dead_code: true,
evaluate: true,
if_return: true,
join_vars: true,
},
})
);
// Define css loaders
cssLoader = ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
module: true, // css-loader 0.14.5 compatible
modules: true,
localIdentName: '[hash:base64:5]',
},
},
{
loader: 'sass-loader',
options: {
outputStyle: 'collapsed',
sourceMap: true,
includePaths: [sourcePath],
},
},
],
});
} else {
// Development plugins
plugins.push(
// make hot reloading work
new webpack.HotModuleReplacementPlugin(),
// show module names instead of numbers in webpack stats
new webpack.NamedModulesPlugin(),
// don't spit out any errors in compiled assets
new webpack.NoEmitOnErrorsPlugin()
);
cssLoader = [
{
loader: 'style',
},
{
loader: 'css',
options: {
module: true,
localIdentName: '[path][name]-[local]',
},
},
{
loader: 'sass',
options: {
outputStyle: 'expanded',
sourceMap: false,
includePaths: [sourcePath],
},
},
];
}
// Service worker cache
if (serviceWorkerBuild) {
plugins.push(
new SWPrecacheWebpackPlugin({
cacheId: 'portfolio',
filename: 'sw.js',
maximumFileSizeToCacheInBytes: 800000,
mergeStaticsConfig: true,
minify: true,
runtimeCaching: [
{
handler: 'cacheFirst',
urlPattern: /(.*?)/,
},
],
})
);
}
const entryPoint = isProd
? path.join(__dirname, 'src', 'entry.js')
: [
// Activate HMR for react
'react-hot-loader/patch',
// bundle the client for webpack-dev-server
// and connect to the provided endpoint
`webpack-dev-server/client?http://${host}:${port}`,
// bundle the client for hot reloading
// only- means to only hot reload for successful updates
'webpack/hot/only-dev-server',
// the entry point of our app
path.join(__dirname, 'src', 'entry.js'),
];
return {
devtool: isProd ? 'source-map' : 'cheap-module-source-map',
context: sourcePath,
entry: {
main: entryPoint,
},
output: {
path: buildPath,
publicPath: '/',
filename: '[name]-[hash:8].js',
chunkFilename: '[name]-[chunkhash:8].js',
},
module: {
rules: [
// Static assets loader
{
test: /\.(html|svg|jpe?g|png|ttf|woff2?)$/,
include: sourcePath,
use: {
loader: 'file',
options: {
name: 'static/[name]-[hash:8].[ext]'
},
},
},
// Sass loader
{
test: /\.scss$/,
include: sourcePath,
use: cssLoader,
},
// Js/jsx loader
{
test: /\.jsx?$/,
include: sourcePath,
use: ['babel']
}
]
},
resolve: {
extensions: ['.webpack-loader.js', '.web-loader.js', '.loader.js', '.js', '.jsx'],
modules: [path.resolve(__dirname, 'node_modules'), sourcePath]
},
plugins,
performance: isProd && {
maxAssetSize: 300000,
maxEntryPointSize: 300000,
hints: 'warning'
},
stats: stats,
devServer: {
contentBase: sourcePath,
publicPath: '/',
historyApiFallback: true,
port: port,
host: host,
hot: !isProd,
compress: isProd,
stats: stats
},
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment