@@ -13,7 +13,8 @@ const path = require('path');
13
13
const webpack = require ( 'webpack' ) ;
14
14
const HtmlWebpackPlugin = require ( 'html-webpack-plugin' ) ;
15
15
const UglifyJsPlugin = require ( 'uglifyjs-webpack-plugin' ) ;
16
- const ExtractTextPlugin = require ( 'extract-text-webpack-plugin' ) ;
16
+ const MiniCssExtractPlugin = require ( 'mini-css-extract-plugin' ) ;
17
+ const OptimizeCSSAssetsPlugin = require ( 'optimize-css-assets-webpack-plugin' ) ;
17
18
const ManifestPlugin = require ( 'webpack-manifest-plugin' ) ;
18
19
const InterpolateHtmlPlugin = require ( 'react-dev-utils/InterpolateHtmlPlugin' ) ;
19
20
const SWPrecacheWebpackPlugin = require ( 'sw-precache-webpack-plugin' ) ;
@@ -26,9 +27,6 @@ const getClientEnvironment = require('./env');
26
27
// Webpack uses `publicPath` to determine where the app is being served from.
27
28
// It requires a trailing slash, or the file assets will get an incorrect path.
28
29
const publicPath = paths . servedPath ;
29
- // Some apps do not use client-side routing with pushState.
30
- // For these, "homepage" can be set to "." to enable relative asset paths.
31
- const shouldUseRelativeAssetPaths = publicPath === './' ;
32
30
// Source maps are resource heavy and can cause out of memory issue for large source files.
33
31
const shouldUseSourceMap = process . env . GENERATE_SOURCEMAP !== 'false' ;
34
32
// `publicUrl` is just like `publicPath`, but we will provide it to our app
@@ -44,34 +42,6 @@ if (env.stringified['process.env'].NODE_ENV !== '"production"') {
44
42
throw new Error ( 'Production builds must have NODE_ENV=production.' ) ;
45
43
}
46
44
47
- // Note: defined here because it will be used more than once.
48
- const cssFilename = 'static/css/[name].[contenthash:8].css' ;
49
-
50
- // ExtractTextPlugin expects the build output to be flat.
51
- // (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
52
- // However, our output is structured with css, js and media folders.
53
- // To have this structure working with relative paths, we have to use custom options.
54
- const extractTextPluginOptions = shouldUseRelativeAssetPaths
55
- ? // Making sure that the publicPath goes back to to build folder.
56
- { publicPath : Array ( cssFilename . split ( '/' ) . length ) . join ( '../' ) }
57
- : { } ;
58
-
59
- // Options for PostCSS as we reference these options twice
60
- // Adds vendor prefixing based on your specified browser support in
61
- // package.json
62
- const postCSSLoaderOptions = {
63
- // Necessary for external CSS imports to work
64
- // https://github.com/facebook/create-react-app/issues/2677
65
- ident : 'postcss' ,
66
- plugins : ( ) => [
67
- require ( 'postcss-flexbugs-fixes' ) ,
68
- autoprefixer ( {
69
- flexbox : 'no-2009' ,
70
- } ) ,
71
- ] ,
72
- sourceMap : shouldUseSourceMap ,
73
- } ;
74
-
75
45
// style files regexes
76
46
const cssRegex = / \. c s s $ / ;
77
47
const cssModuleRegex = / \. m o d u l e \. c s s $ / ;
@@ -81,13 +51,28 @@ const sassModuleRegex = /\.module\.(scss|sass)$/;
81
51
// common function to get style loaders
82
52
const getStyleLoaders = ( cssOptions , preProcessor ) => {
83
53
const loaders = [
54
+ MiniCssExtractPlugin . loader ,
84
55
{
85
56
loader : require . resolve ( 'css-loader' ) ,
86
57
options : cssOptions ,
87
58
} ,
88
59
{
60
+ // Options for PostCSS as we reference these options twice
61
+ // Adds vendor prefixing based on your specified browser support in
62
+ // package.json
89
63
loader : require . resolve ( 'postcss-loader' ) ,
90
- options : postCSSLoaderOptions ,
64
+ options : {
65
+ // Necessary for external CSS imports to work
66
+ // https://github.com/facebook/create-react-app/issues/2677
67
+ ident : 'postcss' ,
68
+ plugins : ( ) => [
69
+ require ( 'postcss-flexbugs-fixes' ) ,
70
+ autoprefixer ( {
71
+ flexbox : 'no-2009' ,
72
+ } ) ,
73
+ ] ,
74
+ sourceMap : shouldUseSourceMap ,
75
+ } ,
91
76
} ,
92
77
] ;
93
78
if ( preProcessor ) {
@@ -98,26 +83,14 @@ const getStyleLoaders = (cssOptions, preProcessor) => {
98
83
} ,
99
84
} ) ;
100
85
}
101
- return ExtractTextPlugin . extract (
102
- Object . assign (
103
- {
104
- fallback : {
105
- loader : require . resolve ( 'style-loader' ) ,
106
- options : {
107
- hmr : false ,
108
- } ,
109
- } ,
110
- use : loaders ,
111
- } ,
112
- extractTextPluginOptions
113
- )
114
- ) ;
86
+ return loaders ;
115
87
} ;
116
88
117
89
// This is the production configuration.
118
90
// It compiles slowly and is focused on producing a fast and minimal bundle.
119
91
// The development configuration is different and lives in a separate file.
120
92
module . exports = {
93
+ mode : 'production' ,
121
94
// Don't attempt to continue if there are any errors.
122
95
bail : true ,
123
96
// We generate sourcemaps in production. This is slow but gives good results.
@@ -141,6 +114,58 @@ module.exports = {
141
114
. relative ( paths . appSrc , info . absoluteResourcePath )
142
115
. replace ( / \\ / g, '/' ) ,
143
116
} ,
117
+ optimization : {
118
+ minimizer : [
119
+ new UglifyJsPlugin ( {
120
+ uglifyOptions : {
121
+ parse : {
122
+ // we want uglify-js to parse ecma 8 code. However, we don't want it
123
+ // to apply any minfication steps that turns valid ecma 5 code
124
+ // into invalid ecma 5 code. This is why the 'compress' and 'output'
125
+ // sections only apply transformations that are ecma 5 safe
126
+ // https://github.com/facebook/create-react-app/pull/4234
127
+ ecma : 8 ,
128
+ } ,
129
+ compress : {
130
+ ecma : 5 ,
131
+ warnings : false ,
132
+ // Disabled because of an issue with Uglify breaking seemingly valid code:
133
+ // https://github.com/facebook/create-react-app/issues/2376
134
+ // Pending further investigation:
135
+ // https://github.com/mishoo/UglifyJS2/issues/2011
136
+ comparisons : false ,
137
+ } ,
138
+ mangle : {
139
+ safari10 : true ,
140
+ } ,
141
+ output : {
142
+ ecma : 5 ,
143
+ comments : false ,
144
+ // Turned on because emoji and regex is not minified properly using default
145
+ // https://github.com/facebook/create-react-app/issues/2488
146
+ ascii_only : true ,
147
+ } ,
148
+ } ,
149
+ // Use multi-process parallel running to improve the build speed
150
+ // Default number of concurrent runs: os.cpus().length - 1
151
+ parallel : true ,
152
+ // Enable file caching
153
+ cache : true ,
154
+ sourceMap : shouldUseSourceMap ,
155
+ } ) ,
156
+ new OptimizeCSSAssetsPlugin ( ) ,
157
+ ] ,
158
+ // Automatically split vendor and commons
159
+ // https://twitter.com/wSokra/status/969633336732905474
160
+ // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
161
+ splitChunks : {
162
+ chunks : 'all' ,
163
+ name : 'vendors' ,
164
+ } ,
165
+ // Keep the runtime chunk seperated to enable long term caching
166
+ // https://twitter.com/wSokra/status/969679223278505985
167
+ runtimeChunk : true ,
168
+ } ,
144
169
resolve : {
145
170
// This allows you to set a fallback for where Webpack should look for modules.
146
171
// We placed these paths second because we want `node_modules` to "win"
@@ -284,41 +309,29 @@ module.exports = {
284
309
} ,
285
310
] ,
286
311
} ,
287
- // The notation here is somewhat confusing.
288
312
// "postcss" loader applies autoprefixer to our CSS.
289
313
// "css" loader resolves paths in CSS and adds assets as dependencies.
290
- // "style" loader normally turns CSS into JS modules injecting <style>,
291
- // but unlike in development configuration, we do something different.
292
- // `ExtractTextPlugin` first applies the "postcss" and "css" loaders
293
- // (second argument), then grabs the result CSS and puts it into a
294
- // separate file in our build process. This way we actually ship
295
- // a single CSS file in production instead of JS code injecting <style>
296
- // tags. If you use code splitting, however, any async bundles will still
297
- // use the "style" loader inside the async code so CSS from them won't be
298
- // in the main CSS file.
314
+ // `MiniCSSExtractPlugin` extracts styles into CSS
315
+ // files. If you use code splitting, async bundles will have their own separate CSS chunk file.
299
316
// By default we support CSS Modules with the extension .module.css
300
317
{
301
318
test : cssRegex ,
302
319
exclude : cssModuleRegex ,
303
320
loader : getStyleLoaders ( {
304
321
importLoaders : 1 ,
305
- minimize : true ,
306
322
sourceMap : shouldUseSourceMap ,
307
323
} ) ,
308
- // Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
309
324
} ,
310
325
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
311
326
// using the extension .module.css
312
327
{
313
328
test : cssModuleRegex ,
314
329
loader : getStyleLoaders ( {
315
330
importLoaders : 1 ,
316
- minimize : true ,
317
331
sourceMap : shouldUseSourceMap ,
318
332
modules : true ,
319
333
getLocalIdent : getCSSModuleLocalIdent ,
320
334
} ) ,
321
- // Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
322
335
} ,
323
336
// Opt-in support for SASS. The logic here is somewhat similar
324
337
// as in the CSS routine, except that "sass-loader" runs first
@@ -331,12 +344,10 @@ module.exports = {
331
344
loader : getStyleLoaders (
332
345
{
333
346
importLoaders : 2 ,
334
- minimize : true ,
335
347
sourceMap : shouldUseSourceMap ,
336
348
} ,
337
349
'sass-loader'
338
350
) ,
339
- // Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
340
351
} ,
341
352
// Adds support for CSS Modules, but using SASS
342
353
// using the extension .module.scss or .module.sass
@@ -345,14 +356,12 @@ module.exports = {
345
356
loader : getStyleLoaders (
346
357
{
347
358
importLoaders : 2 ,
348
- minimize : true ,
349
359
sourceMap : shouldUseSourceMap ,
350
360
modules : true ,
351
361
getLocalIdent : getCSSModuleLocalIdent ,
352
362
} ,
353
363
'sass-loader'
354
364
) ,
355
- // Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
356
365
} ,
357
366
// The GraphQL loader preprocesses GraphQL queries in .graphql files.
358
367
{
@@ -381,12 +390,6 @@ module.exports = {
381
390
] ,
382
391
} ,
383
392
plugins : [
384
- // Makes some environment variables available in index.html.
385
- // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
386
- // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
387
- // In production, it will be an empty string unless you specify "homepage"
388
- // in `package.json`, in which case it will be the pathname of that URL.
389
- new InterpolateHtmlPlugin ( env . raw ) ,
390
393
// Generates an `index.html` file with the <script> injected.
391
394
new HtmlWebpackPlugin ( {
392
395
inject : true ,
@@ -404,52 +407,22 @@ module.exports = {
404
407
minifyURLs : true ,
405
408
} ,
406
409
} ) ,
410
+ // Makes some environment variables available in index.html.
411
+ // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
412
+ // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
413
+ // In production, it will be an empty string unless you specify "homepage"
414
+ // in `package.json`, in which case it will be the pathname of that URL.
415
+ new InterpolateHtmlPlugin ( env . raw ) ,
407
416
// Makes some environment variables available to the JS code, for example:
408
417
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
409
418
// It is absolutely essential that NODE_ENV was set to production here.
410
419
// Otherwise React will be compiled in the very slow development mode.
411
420
new webpack . DefinePlugin ( env . stringified ) ,
412
- // Minify the code.
413
- new UglifyJsPlugin ( {
414
- uglifyOptions : {
415
- parse : {
416
- // we want uglify-js to parse ecma 8 code. However, we don't want it
417
- // to apply any minfication steps that turns valid ecma 5 code
418
- // into invalid ecma 5 code. This is why the 'compress' and 'output'
419
- // sections only apply transformations that are ecma 5 safe
420
- // https://github.com/facebook/create-react-app/pull/4234
421
- ecma : 8 ,
422
- } ,
423
- compress : {
424
- ecma : 5 ,
425
- warnings : false ,
426
- // Disabled because of an issue with Uglify breaking seemingly valid code:
427
- // https://github.com/facebook/create-react-app/issues/2376
428
- // Pending further investigation:
429
- // https://github.com/mishoo/UglifyJS2/issues/2011
430
- comparisons : false ,
431
- } ,
432
- mangle : {
433
- safari10 : true ,
434
- } ,
435
- output : {
436
- ecma : 5 ,
437
- comments : false ,
438
- // Turned on because emoji and regex is not minified properly using default
439
- // https://github.com/facebook/create-react-app/issues/2488
440
- ascii_only : true ,
441
- } ,
442
- } ,
443
- // Use multi-process parallel running to improve the build speed
444
- // Default number of concurrent runs: os.cpus().length - 1
445
- parallel : true ,
446
- // Enable file caching
447
- cache : true ,
448
- sourceMap : shouldUseSourceMap ,
449
- } ) ,
450
- // Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
451
- new ExtractTextPlugin ( {
452
- filename : cssFilename ,
421
+ new MiniCssExtractPlugin ( {
422
+ // Options similar to the same options in webpackOptions.output
423
+ // both options are optional
424
+ filename : 'static/css/[name].[contenthash:8].css' ,
425
+ chunkFilename : 'static/css/[name].[contenthash:8].chunk.css' ,
453
426
} ) ,
454
427
// Generate a manifest file which contains a mapping of all asset filenames
455
428
// to their corresponding output file so that tools can pick it up without
@@ -503,4 +476,7 @@ module.exports = {
503
476
tls : 'empty' ,
504
477
child_process : 'empty' ,
505
478
} ,
479
+ // Turn off performance processing because we utilize
480
+ // our own hints via the FileSizeReporter
481
+ performance : false ,
506
482
} ;
0 commit comments