Skip to content

Instantly share code, notes, and snippets.

@TechnotronicOz
Created October 5, 2017 15:37
Show Gist options
  • Select an option

  • Save TechnotronicOz/dcd3622d38b96f4a4c82eb7793581b9c to your computer and use it in GitHub Desktop.

Select an option

Save TechnotronicOz/dcd3622d38b96f4a4c82eb7793581b9c to your computer and use it in GitHub Desktop.
import path from 'path';
import webpack from 'webpack';
import AssetsPlugin from 'assets-webpack-plugin';
import nodeExternals from 'webpack-node-externals';
import {BundleAnalyzerPlugin} from 'webpack-bundle-analyzer';
import overrideRules from './lib/overrideRules';
import pkg from '../package.json';
const isDebug = !process.argv.includes('--release');
const isVerbose = process.argv.includes('--verbose');
const isAnalyze =
process.argv.includes('--analyze') || process.argv.includes('--analyse');
const reScript = /\.jsx?$/;
const reStyle = /\.(css|less|scss|sss)$/;
const reImage = /\.(bmp|gif|jpe?g|png|svg)$/;
const staticAssetName = isDebug
? '[path][name].[ext]?[hash:8]'
: '[hash:8].[ext]';
//
// Common configuration chunk to be used for both
// client-side (client.js) and server-side (server.js) bundles
// -----------------------------------------------------------------------------
const config = {
context: path.resolve(__dirname, '..'),
output: {
path: path.resolve(__dirname, '../build/public/assets'),
publicPath: '/assets/',
pathinfo: isVerbose,
filename: isDebug ? '[name].js' : '[name].[chunkhash:8].js',
chunkFilename: isDebug
? '[name].chunk.js'
: '[name].[chunkhash:8].chunk.js',
devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath)
},
resolve: {
// Allow absolute paths in imports, e.g. import Button from 'components/Button'
// Keep in sync with .flowconfig and .eslintrc
modules: ['node_modules', 'src']
},
module: {
// Make missing exports an error instead of warning
strictExportPresence: true,
rules: [
// Rules for JS / JSX
{
test: reScript,
include: path.resolve(__dirname, '../src'),
loader: 'babel-loader',
options: {
// https://github.com/babel/babel-loader#options
cacheDirectory: isDebug,
// https://babeljs.io/docs/usage/options/
babelrc: false,
presets: [
// A Babel preset that can automatically determine the Babel plugins and polyfills
// https://github.com/babel/babel-preset-env
[
'env',
{
targets: {
browsers: pkg.browserslist,
uglify: true
},
modules: false,
useBuiltIns: false,
debug: false
}
],
// Experimental ECMAScript proposals
// https://babeljs.io/docs/plugins/#presets-stage-x-experimental-presets-
'stage-2',
// JSX, Flow
// https://github.com/babel/babel/tree/master/packages/babel-preset-react
'react',
// Optimize React code for the production build
// https://github.com/thejameskyle/babel-react-optimize
...(isDebug ? [] : ['react-optimize'])
],
plugins: [
// Adds component stack to warning messages
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-source
...(isDebug ? ['transform-react-jsx-source'] : []),
// Adds __self attribute to JSX which React will use for some warnings
// https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-react-jsx-self
...(isDebug ? ['transform-react-jsx-self'] : [])
]
}
},
// Rules for Style Sheets
{
test: reStyle,
rules: [
// Convert CSS into JS module
{
issuer: {not: [reStyle]},
use: 'isomorphic-style-loader'
},
// Process external/third-party styles
{
exclude: path.resolve(__dirname, '../src'),
loader: 'css-loader',
options: {
sourceMap: isDebug,
minimize: !isDebug,
discardComments: {removeAll: true}
}
},
// Process internal/project styles (from src folder)
{
include: path.resolve(__dirname, '../src'),
loader: 'css-loader',
options: {
// CSS Loader https://github.com/webpack/css-loader
importLoaders: 1,
sourceMap: isDebug,
// CSS Modules https://github.com/css-modules/css-modules
modules: true,
localIdentName: isDebug
? '[name]-[local]-[hash:base64:5]'
: '[hash:base64:5]',
// CSS Nano http://cssnano.co/options/
minimize: !isDebug,
discardComments: {removeAll: true}
}
},
// Apply PostCSS plugins including autoprefixer
{
loader: 'postcss-loader',
options: {
config: {
path: './tools/postcss.config.js'
}
}
}
// Compile Less to CSS
// https://github.com/webpack-contrib/less-loader
// Install dependencies before uncommenting: yarn add --dev less-loader less
// {
// test: /\.less$/,
// loader: 'less-loader',
// },
// Compile Sass to CSS
// https://github.com/webpack-contrib/sass-loader
// Install dependencies before uncommenting: yarn add --dev sass-loader node-sass
// {
// test: /\.scss$/,
// loader: 'sass-loader',
// },
]
},
// Rules for images
{
test: reImage,
oneOf: [
// Inline lightweight images into CSS
{
issuer: reStyle,
oneOf: [
// Inline lightweight SVGs as UTF-8 encoded DataUrl string
{
test: /\.svg$/,
loader: 'svg-url-loader',
options: {
name: staticAssetName,
limit: 4096 // 4kb
}
},
// Inline lightweight images as Base64 encoded DataUrl string
{
loader: 'url-loader',
options: {
name: staticAssetName,
limit: 4096 // 4kb
}
}
]
},
// Or return public URL to image resource
{
loader: 'file-loader',
options: {
name: staticAssetName
}
}
]
},
// Convert plain text into JS module
{
test: /\.txt$/,
loader: 'raw-loader'
},
// Convert Markdown into HTML
{
test: /\.md$/,
loader: path.resolve(__dirname, './lib/markdown-loader.js')
},
// Web fonts
{
test: /\.eot/,
loader: 'url-loader?mimetype=application/vnd.ms-fontobject'
},
{
test: /\.ttf/,
loader: 'url-loader?mimetype=application/x-font-ttf'
},
{
test: /\.woff/,
loader: 'url-loader?mimetype=application/font-woff'
},
{
test: /\.woff2/,
loader: 'url-loader?mimetype=application/font-woff2'
},
// Return public URL for all assets unless explicitly excluded
// DO NOT FORGET to update `exclude` list when you adding a new loader
{
exclude: [reScript, reStyle, reImage, /\.json$/, /\.txt$/, /\.md$/, /\.eot/, /\.ttf/, /\.woff/, /\.woff2/],
loader: 'file-loader',
options: {
name: staticAssetName
}
},
// Exclude dev modules from production build
...(isDebug
? []
: [
{
test: path.resolve(
__dirname,
'../node_modules/react-deep-force-update/lib/index.js'
),
loader: 'null-loader'
}
])
]
},
// Don't attempt to continue if there are any errors.
bail: !isDebug,
cache: isDebug,
// Specify what bundle information gets displayed
// https://webpack.js.org/configuration/stats/
stats: {
cached: isVerbose,
cachedAssets: isVerbose,
chunks: isVerbose,
chunkModules: isVerbose,
colors: true,
hash: isVerbose,
modules: isVerbose,
reasons: isDebug,
timings: true,
version: isVerbose
},
// Choose a developer tool to enhance debugging
// https://webpack.js.org/configuration/devtool/#devtool
devtool: isDebug ? 'cheap-module-inline-source-map' : 'source-map'
};
//
// Configuration for the client-side bundle (client.js)
// -----------------------------------------------------------------------------
const clientConfig = {
...config,
name: 'client',
target: 'web',
entry: {
client: ['babel-polyfill', './src/client.js']
},
plugins: [
// Define free variables
// https://webpack.js.org/plugins/define-plugin/
new webpack.DefinePlugin({
'process.env.NODE_ENV': isDebug ? '"development"' : '"production"',
'process.env.BROWSER': true,
__DEV__: isDebug
}),
// Emit a file with assets paths
// https://github.com/sporto/assets-webpack-plugin#options
new AssetsPlugin({
path: path.resolve(__dirname, '../build'),
filename: 'assets.json',
prettyPrint: true
}),
// Move modules that occur in multiple entry chunks to a new entry chunk (the commons chunk).
// https://webpack.js.org/plugins/commons-chunk-plugin/
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: module => /node_modules/.test(module.resource)
}),
...(isDebug
? []
: [
// Decrease script evaluation time
// https://github.com/webpack/webpack/blob/master/examples/scope-hoisting/README.md
new webpack.optimize.ModuleConcatenationPlugin(),
// Minimize all JavaScript output of chunks
// https://github.com/mishoo/UglifyJS2#compressor-options
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
screw_ie8: true, // React doesn't support IE8
warnings: isVerbose,
unused: true,
dead_code: true
},
mangle: {
screw_ie8: true
},
output: {
comments: false,
screw_ie8: true
}
})
]),
// Webpack Bundle Analyzer
// https://github.com/th0r/webpack-bundle-analyzer
...(isAnalyze ? [new BundleAnalyzerPlugin()] : [])
],
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
// https://webpack.js.org/configuration/node/
// https://github.com/webpack/node-libs-browser/tree/master/mock
node: {
fs: 'empty',
net: 'empty',
tls: 'empty'
}
};
//
// Configuration for the server-side bundle (server.js)
// -----------------------------------------------------------------------------
const serverConfig = {
...config,
name: 'server',
target: 'node',
entry: {
server: ['babel-polyfill', './src/server.js']
},
output: {
...config.output,
path: path.resolve(__dirname, '../build'),
filename: '[name].js',
chunkFilename: 'chunks/[name].js',
libraryTarget: 'commonjs2'
},
// Webpack mutates resolve object, so clone it to avoid issues
// https://github.com/webpack/webpack/issues/4817
resolve: {
...config.resolve
},
module: {
...config.module,
rules: overrideRules(config.module.rules, rule => {
// Override babel-preset-env configuration for Node.js
if (rule.loader === 'babel-loader') {
return {
...rule,
options: {
...rule.options,
presets: rule.options.presets.map(
preset =>
preset[0] !== 'env'
? preset
: [
'env',
{
targets: {
node: pkg.engines.node.match(/(\d+\.?)+/)[0]
},
modules: false,
useBuiltIns: false,
debug: false
}
]
)
}
};
}
// Override paths to static assets
if (
rule.loader === 'file-loader' ||
rule.loader === 'url-loader' ||
rule.loader === 'svg-url-loader'
) {
return {
...rule,
options: {
...rule.options,
name: `public/assets/${rule.options.name}`,
publicPath: url => url.replace(/^public/, '')
}
};
}
return rule;
})
},
externals: [
'./assets.json',
nodeExternals({
whitelist: [reStyle, reImage]
})
],
plugins: [
// Define free variables
// https://webpack.js.org/plugins/define-plugin/
new webpack.DefinePlugin({
'process.env.NODE_ENV': isDebug ? '"development"' : '"production"',
'process.env.BROWSER': false,
__DEV__: isDebug
}),
// Adds a banner to the top of each generated chunk
// https://webpack.js.org/plugins/banner-plugin/
new webpack.BannerPlugin({
banner: 'require("source-map-support").install();',
raw: true,
entryOnly: false
})
],
// Do not replace node globals with polyfills
// https://webpack.js.org/configuration/node/
node: {
console: false,
global: false,
process: false,
Buffer: false,
__filename: false,
__dirname: false
}
};
export default [clientConfig, serverConfig];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment