@25sprout/react-starter
Version:
25sprout web starter with React
268 lines (259 loc) • 6.52 kB
JavaScript
import path from 'path';
import webpack from 'webpack';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import TerserPlugin from 'terser-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import CssMinimizerPlugin from 'css-minimizer-webpack-plugin';
import CompressionPlugin from 'compression-webpack-plugin';
import media from './config/media';
import env from './config/env';
import endpoint from './config/endpoint';
const terserDevOptions = {
terserOptions: {
ecma: 5,
compress: {
warnings: false,
comparisons: false,
},
output: {
comments: false,
ascii_only: false,
},
},
};
const terserProductionOptions = {
terserOptions: {
ecma: 5,
parse: {},
compress: {
warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
// https://github.com/facebook/create-react-app/issues/2376
// Pending further investigation:
// https://github.com/mishoo/UglifyJS2/issues/2011
comparisons: false,
drop_console: true,
},
mangle: true,
output: {
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebook/create-react-app/issues/2488
ascii_only: true,
},
},
// Use multi-process parallel running to improve the build speed
// Default number of concurrent runs: os.cpus().length - 1
parallel: true,
};
const webpackProdConfig = {
devtool: 'source-map',
mode: process.env.NODE_ENV,
entry: {
app: ['core-js/modules/es.array.iterator', './src/index.js'],
},
output: {
path: path.join(__dirname, 'public'),
filename: '[name].[chunkhash].js',
chunkFilename: '[name].[chunkhash].chunk.js',
publicPath: '/',
},
plugins: [
new webpack.DefinePlugin({
'process.env': { ...env, ...endpoint },
}),
new HtmlWebpackPlugin({
template: './src/index.html',
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
removeScriptTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
inject: true,
showErrors: false,
filename: 'index.html',
chunksSortMode: 'auto',
}),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: '[name].[contenthash].css',
chunkFilename: '[id].[contenthash].css',
}),
],
optimization: {
moduleIds: 'deterministic',
minimizer: [
new TerserPlugin(
process.env.NODE_ENV === 'production' ? terserProductionOptions : terserDevOptions,
),
new CssMinimizerPlugin(),
],
// Automatically split vendor and commons
// https://hackernoon.com/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758
splitChunks: {
chunks: 'all',
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
// cacheGroupKey here is `commons` as the key of the cacheGroup
name(module, chunks, cacheGroupKey) {
const moduleFileName = module
.identifier()
.split('/')
.reduceRight(item => item);
const allChunksNames = chunks.map(item => item.name).join('~');
return `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`;
},
},
},
},
runtimeChunk: 'single',
},
module: {
rules: [
{
test: /\.js?$/,
include: [path.join(__dirname, 'src')],
exclude: path.join(__dirname, 'node_modules'),
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }],
'@babel/preset-react',
],
plugins: [
['module-resolver', { root: ['./src'] }],
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-syntax-import-meta',
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-json-strings',
'@babel/plugin-transform-react-constant-elements',
],
babelrc: false,
},
},
{
test: /\.css$/,
include: [path.join(__dirname, 'src'), path.join(__dirname, 'config')],
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: process.env.NODE_ENV !== 'production',
modules: {
exportLocalsConvention: 'camelCase',
localIdentName: '[name]__[local]___[[hash:base64:5]',
},
importLoaders: 1,
},
},
{
loader: 'postcss-loader',
options: {
sourceMap: process.env.NODE_ENV !== 'production',
postcssOptions: {
plugins: [
'postcss-import',
[
'postcss-preset-env',
{
stage: 0,
importFrom: [
'./config/palette.css',
{
customMedia: media,
},
],
preserve: false,
features: {
'custom-properties': {
disableDeprecationNotice: true,
},
},
},
],
],
},
},
},
],
},
{
test: /\.css$/,
include: path.join(__dirname, 'node_modules'),
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: process.env.NODE_ENV !== 'production',
},
},
],
},
{
test: /\.(jpe?g|png|gif)$/,
include: path.join(__dirname, 'src'),
loader: 'url-loader',
options: {
limit: 10000,
name: './assets/[name]__[contenthash].[ext]',
},
},
{
test: /^(?!.*\.inline\.svg$).*\.svg$/,
include: path.join(__dirname, 'src'),
use: [
'@svgr/webpack',
{
loader: 'url-loader',
options: {
limit: 10000,
name: './assets/[name]__[contenthash].[ext]',
},
},
],
},
{
test: /\.inline.svg$/,
include: path.join(__dirname, 'src'),
loader: '@svgr/webpack',
options: {
svgoConfig: {
plugins: [
{
name: 'preset-default',
params: {
overrides: {
cleanupIDs: false,
removeViewBox: false,
},
},
},
],
},
},
},
],
},
resolve: {
modules: ['node_modules'],
},
};
// Minify and optimize the CSS
if (process.env.NODE_ENV === 'production') {
webpackProdConfig.plugins.push(new CompressionPlugin({ test: /\.(js|css|html)$/ }));
}
export default webpackProdConfig;