UNPKG

6.35 kBJavaScriptView Raw
1import path from 'path';
2import webpack from 'webpack';
3import atImport from 'postcss-import';
4import postcssPresetEnv from 'postcss-preset-env';
5import HtmlWebpackPlugin from 'html-webpack-plugin';
6import TerserPlugin from 'terser-webpack-plugin';
7import MiniCssExtractPlugin from 'mini-css-extract-plugin';
8import OptimizeCSSAssetsPlugin from 'optimize-css-assets-webpack-plugin';
9import CompressionPlugin from 'compression-webpack-plugin';
10
11import palette from './config/palette';
12import media from './config/media';
13import env from './config/env';
14import endpoint from './config/endpoint';
15
16const terserDevOptions = {
17 terserOptions: {
18 ecma: 5,
19 compress: {
20 warnings: false,
21 comparisons: false,
22 },
23 output: {
24 comments: false,
25 ascii_only: false,
26 },
27 },
28};
29
30const terserProductionOptions = {
31 terserOptions: {
32 ecma: 5,
33 parse: {},
34 compress: {
35 warnings: false,
36 // Disabled because of an issue with Uglify breaking seemingly valid code:
37 // https://github.com/facebook/create-react-app/issues/2376
38 // Pending further investigation:
39 // https://github.com/mishoo/UglifyJS2/issues/2011
40 comparisons: false,
41 },
42 mangle: true,
43 output: {
44 comments: false,
45 // Turned on because emoji and regex is not minified properly using default
46 // https://github.com/facebook/create-react-app/issues/2488
47 ascii_only: true,
48 },
49 },
50 // Use multi-process parallel running to improve the build speed
51 // Default number of concurrent runs: os.cpus().length - 1
52 parallel: true,
53 // Enable file caching
54 cache: true,
55 sourceMap: false,
56};
57
58const webpackProdConfig = {
59 devtool: 'source-map',
60 mode: process.env.NODE_ENV,
61 entry: {
62 app: './src/index.js',
63 },
64 output: {
65 path: path.join(__dirname, '_public'),
66 filename: '[name].[chunkhash].js',
67 chunkFilename: '[name].[chunkhash].chunk.js',
68 publicPath: '/',
69 },
70 plugins: [
71 new webpack.DefinePlugin({
72 'process.env': { ...env, ...endpoint },
73 }),
74
75 new HtmlWebpackPlugin({
76 template: './src/index.html',
77 minify: {
78 removeComments: true,
79 collapseWhitespace: true,
80 removeRedundantAttributes: true,
81 useShortDoctype: true,
82 removeEmptyAttributes: true,
83 removeStyleLinkTypeAttributes: true,
84 removeScriptTypeAttributes: true,
85 keepClosingSlash: true,
86 minifyJS: true,
87 minifyCSS: true,
88 minifyURLs: true,
89 },
90 inject: true,
91 showErrors: false,
92 filename: 'index.html',
93 chunksSortMode: 'dependency',
94 }),
95
96 new MiniCssExtractPlugin({
97 // Options similar to the same options in webpackOptions.output
98 // both options are optional
99 filename: '[name].[hash].css',
100 chunkFilename: '[id].[hash].css',
101 }),
102
103 new webpack.HashedModuleIdsPlugin(),
104 ],
105 optimization: {
106 minimizer: [
107 new TerserPlugin(
108 process.env.NODE_ENV === 'production' ? terserProductionOptions : terserDevOptions,
109 ),
110 ],
111 // Automatically split vendor and commons
112 // https://hackernoon.com/the-100-correct-way-to-split-your-chunks-with-webpack-f8a9df5b7758
113 splitChunks: {
114 chunks: 'all',
115 maxInitialRequests: Infinity,
116 minSize: 0,
117 cacheGroups: {
118 vendor: {
119 test: /[\\/]node_modules[\\/]/,
120 name(module) {
121 // get the name. E.g. node_modules/packageName/not/this/part.js
122 // or node_modules/packageName
123 const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
124
125 // npm package names are URL-safe, but some servers don't like @ symbols
126 return `npm.${packageName.replace('@', '')}`;
127 },
128 },
129 },
130 },
131 runtimeChunk: 'single',
132 },
133 module: {
134 rules: [
135 {
136 test: /\.js?$/,
137 include: [path.join(__dirname, 'src')],
138 exclude: path.join(__dirname, 'node_modules'),
139 loader: 'babel-loader',
140 options: {
141 presets: [
142 ['@babel/preset-env', { loose: true, modules: false, useBuiltIns: 'usage', corejs: 3 }],
143 '@babel/preset-react',
144 ],
145 plugins: [
146 ['module-resolver', { root: ['./src'] }],
147 '@babel/plugin-syntax-dynamic-import',
148 '@babel/plugin-syntax-import-meta',
149 '@babel/plugin-proposal-class-properties',
150 '@babel/plugin-proposal-json-strings',
151 '@babel/plugin-transform-react-constant-elements',
152 ],
153 babelrc: false,
154 },
155 },
156 {
157 test: /\.css$/,
158 include: path.join(__dirname, 'src'),
159 use: [
160 MiniCssExtractPlugin.loader,
161 {
162 loader: 'css-loader',
163 options: {
164 sourceMap: process.env.NODE_ENV !== 'production',
165 localsConvention: 'camelCase',
166 modules: {
167 localIdentName: '[name]__[local]___[hash:base64:5]',
168 },
169 importLoaders: 1,
170 },
171 },
172 {
173 loader: 'postcss-loader',
174 options: {
175 sourceMap: process.env.NODE_ENV !== 'production' ? 'inline' : false,
176 plugins: () => [
177 atImport(),
178 postcssPresetEnv({
179 stage: 0,
180 importFrom: [
181 {
182 customMedia: media,
183 customProperties: palette,
184 },
185 ],
186 preserve: false,
187 }),
188 ],
189 },
190 },
191 ],
192 },
193 {
194 test: /\.css$/,
195 include: path.join(__dirname, 'node_modules'),
196 use: [
197 MiniCssExtractPlugin.loader,
198 {
199 loader: 'css-loader',
200 options: {
201 sourceMap: process.env.NODE_ENV !== 'production',
202 },
203 },
204 ],
205 },
206 {
207 test: /\.(jpe?g|png|gif)$/,
208 include: path.join(__dirname, 'src'),
209 loader: 'url-loader',
210 options: {
211 limit: 10000,
212 name: './assets/[name]__[hash].[ext]',
213 },
214 },
215 {
216 test: /^(?!.*\.inline\.svg$).*\.svg$/,
217 include: path.join(__dirname, 'src'),
218 use: [
219 '@svgr/webpack',
220 {
221 loader: 'url-loader',
222 options: {
223 limit: 10000,
224 name: './assets/[name]__[hash].[ext]',
225 },
226 },
227 ],
228 },
229 {
230 test: /\.inline.svg$/,
231 include: path.join(__dirname, 'src'),
232 loader: '@svgr/webpack',
233 options: {
234 svgoConfig: {
235 plugins: [{ cleanupIDs: false }, { removeViewBox: false }],
236 },
237 },
238 },
239 ],
240 },
241 node: {
242 fs: 'empty',
243 },
244 resolve: {
245 modules: ['node_modules'],
246 },
247};
248
249// Minify and optimize the CSS
250if (process.env.NODE_ENV === 'production') {
251 webpackProdConfig.plugins.push(new OptimizeCSSAssetsPlugin({}));
252 webpackProdConfig.plugins.push(new CompressionPlugin({ test: /\.(js|css|html)$/ }));
253}
254
255export default webpackProdConfig;