Skip to content

Instantly share code, notes, and snippets.

@davidpfahler
Created July 19, 2016 15:31
Show Gist options
  • Save davidpfahler/3e5b0f9bc76421545c12d92cd92da747 to your computer and use it in GitHub Desktop.
Save davidpfahler/3e5b0f9bc76421545c12d92cd92da747 to your computer and use it in GitHub Desktop.
const fs = require('fs')
const path = require('path')
const colors = require('colors/safe')
const webpack = require('webpack')
const WebpackBuildNotifierPlugin = require('webpack-build-notifier')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const compact = require('lodash.compact')
const without = require('lodash.without')
// This webpack config does not only support two different entry points
// (see below), but also production and development bundling.
// This works for both entry points! This config defaults to development.
// Set the environment variable `NODE_ENV=production` to optimize the bundle.
const isProd = process.env.NODE_ENV === 'production'
const isDev = !isProd
// This webpack config can build both vendor files or the remaining app.
// This helps decrease build time as vendor files are seldom changed.
// The config defaults to the entry for the remaining app.
// Note that we can not use two entry points in the same config at any time,
// because the DLLPlugin/DLLReferencePlugin cannot work inside the same config.
// But we can dynamically change the value of the one and only entry point,
// which is what we do here.
// Note that it would be the job of a build script to first call webpack with the
// vendor config and then with the remaining app config.
// Set the environment variable `ENTRY=vendor` to build vendor.js.
const { ENTRY = 'app' } = process.env
const isApp = ENTRY === 'app'
const isVendor = ENTRY === 'vendor'
let entry = compact([
isDev && isApp && 'react-hot-loader/patch',
isDev && isApp && 'webpack-dev-server/client?http://localhost:3000',
isDev && isApp && 'webpack/hot/only-dev-server',
isApp && './src/index',
])
// In production builds, we use react and react-dom from the facebook cdn
if (isVendor) {
const dependencies = Object.keys(require('./package.json').dependencies)
entry = entry.concat((isProd
? without(dependencies, 'react', 'react-dom')
: dependencies
))
}
const externals = isProd
? ({
react: 'React',
'react-dom': 'ReactDOM',
})
: null
// Check if vendor bundle exists, when building the remaining app. If not, panic.
// TODO: This should probably be done in separate builds/make file.
if (ENTRY === 'app') {
try {
fs.accessSync(path.join(__dirname, 'dist', 'vendor.js'))
} catch (_) {
const cmd = isProd
? 'NODE_ENV=production ENTRY=vendor ./node_modules/.bin/webpack'
: 'npm run dev:vendor'
throw console.error(colors.red(
`Error: You must first build \`vendor\` before you can build \`app\`.
Use \`${cmd}\` to build the \`vendor\` bundle.`
))
}
}
// shared between devServer and output properties:
const publicPath = isDev ? '/static/' : null
module.exports = {
cache: true,
devtool: isProd ? 'source-map' : 'eval',
devServer: {
host: '0.0.0.0',
port: 3000,
publicPath,
hot: true,
historyApiFallback: true,
stats: {
colors: true,
},
},
entry: {
[ENTRY]: entry,
},
externals,
output: {
path: path.join(__dirname, 'dist'),
pathinfo: isDev,
filename: '[name].js',
publicPath,
devtoolModuleFilenameTemplate: '/[absolute-resource-path]',
library: isVendor && '[name]_lib',
},
plugins: compact([
isVendor && new webpack.DllPlugin({
path: path.join(__dirname, 'dist', '[name]-manifest.json'),
name: '[name]_lib',
}),
isApp && new webpack.DllReferencePlugin({
context: '.',
manifest: require('./dist/vendor-manifest.json'),
}),
isProd && new webpack.optimize.OccurrenceOrderPlugin(true),
isProd && new webpack.optimize.DedupePlugin(),
isProd && new webpack.DefinePlugin({
'process.env': {
'NODE_ENV': JSON.stringify('production'),
},
}),
isProd && new webpack.optimize.UglifyJsPlugin({
output: {
comments: false,
},
compress: {
warnings: false,
screw_ie8: true,
},
}),
isProd && isApp && new HtmlWebpackPlugin({
template: './src/index.prod.ejs',
}),
isDev && isApp && new webpack.HotModuleReplacementPlugin(),
isDev && new webpack.NoErrorsPlugin(),
isDev && new WebpackBuildNotifierPlugin({
suppressSuccess: true,
}),
]),
resolve: {
extensions: ['', '.js'],
},
modules: [path.resolve(__dirname, 'src'), 'node_modules'],
module: {
preLoaders: [{
test: /\.js$/,
loader: 'eslint-loader',
exclude: /node_modules/,
query: {
fix: true,
},
}],
loaders: [{
test: /\.(js)$/,
loader: 'babel',
include: path.resolve(__dirname, 'src'),
query: {
cacheDirectory: true,
presets: ['es2015-webpack', 'react'],
plugins: [
'react-hot-loader/babel',
'transform-object-rest-spread',
'transform-class-properties',
'react-require',
],
},
}, {
test: /\.json$/,
loader: 'json',
include: [
/node_modules/,
],
}],
},
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment