UNPKG

24 kBJavaScriptView Raw
1// @remove-on-eject-begin
2/**
3 * Copyright (c) 2015-present, Facebook, Inc.
4 *
5 * This source code is licensed under the MIT license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8// @remove-on-eject-end
9'use strict';
10
11const path = require('path');
12const webpack = require('webpack');
13const PnpWebpackPlugin = require('pnp-webpack-plugin');
14const HtmlWebpackPlugin = require('html-webpack-plugin');
15const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
16const TerserPlugin = require('terser-webpack-plugin');
17const MiniCssExtractPlugin = require('mini-css-extract-plugin');
18const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
19const safePostCssParser = require('postcss-safe-parser');
20const ManifestPlugin = require('webpack-manifest-plugin');
21const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
22const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
23const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
24const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
25const paths = require('./paths');
26const getClientEnvironment = require('./env');
27const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
28// @remove-on-eject-begin
29const getCacheIdentifier = require('react-dev-utils/getCacheIdentifier');
30// @remove-on-eject-end
31const CircularDependencyPlugin = require('circular-dependency-plugin');
32
33// Webpack uses `publicPath` to determine where the app is being served from.
34// It requires a trailing slash, or the file assets will get an incorrect path.
35const publicPath = paths.servedPath;
36// Some apps do not use client-side routing with pushState.
37// For these, "homepage" can be set to "." to enable relative asset paths.
38const shouldUseRelativeAssetPaths = publicPath === './';
39// Source maps are resource heavy and can cause out of memory issue for large source files.
40const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
41// Some apps do not need the benefits of saving a web request, so not inlining the chunk
42// makes for a smoother build process.
43const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
44// `publicUrl` is just like `publicPath`, but we will provide it to our app
45// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
46// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
47const publicUrl = publicPath.slice(0, -1);
48// Get environment variables to inject into our app.
49const env = getClientEnvironment(publicUrl);
50
51// Assert this just to be safe.
52// Development builds of React are slow and not intended for production.
53if (env.stringified['process.env'].NODE_ENV !== '"production"') {
54 throw new Error('Production builds must have NODE_ENV=production.');
55}
56
57// style files regexes
58const cssModuleRegex = /\.css$/;
59const cssGlobalRegex = /\.global\.css$/;
60const sassModuleRegex = /\.(scss|sass)$/;
61const sassGlobalRegex = /\.global\.(scss|sass)$/;
62
63const babelCacheDirectory = process.env.BABEL_CACHE_DIRECTORY ? path.join(paths.appPath, process.env.BABEL_CACHE_DIRECTORY) : true;
64const terserCacheDirectory = process.env.TERSER_CACHE_DIRECTORY ? path.join(paths.appPath, process.env.TERSER_CACHE_DIRECTORY) : true;
65
66// common function to get style loaders
67const getStyleLoaders = (cssOptions, preProcessor) => {
68 const loaders = [
69 {
70 loader: MiniCssExtractPlugin.loader,
71 options: Object.assign(
72 {},
73 shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined
74 ),
75 },
76 {
77 loader: require.resolve('css-loader'),
78 options: cssOptions,
79 },
80 {
81 // Options for PostCSS as we reference these options twice
82 // Adds vendor prefixing based on your specified browser support in
83 // package.json
84 loader: require.resolve('postcss-loader'),
85 options: {
86 // Necessary for external CSS imports to work
87 // https://github.com/facebook/create-react-app/issues/2677
88 ident: 'postcss',
89 plugins: () => [
90 require('postcss-flexbugs-fixes'),
91 require('postcss-preset-env')({
92 autoprefixer: {
93 flexbox: 'no-2009',
94 },
95 stage: 3,
96 }),
97 ],
98 sourceMap: shouldUseSourceMap,
99 },
100 },
101 ];
102 if (preProcessor) {
103 loaders.push({
104 loader: require.resolve(preProcessor),
105 options: {
106 sourceMap: shouldUseSourceMap,
107 },
108 });
109 }
110 return loaders;
111};
112
113// This is the production configuration.
114// It compiles slowly and is focused on producing a fast and minimal bundle.
115// The development configuration is different and lives in a separate file.
116module.exports = {
117 mode: 'production',
118 // Don't attempt to continue if there are any errors.
119 bail: true,
120 // We generate sourcemaps in production. This is slow but gives good results.
121 // You can exclude the *.map files from the build during deployment.
122 devtool: shouldUseSourceMap ? 'source-map' : false,
123 // In production, we only want to load the app code.
124 entry: [paths.appIndexJs],
125 output: {
126 // The build folder.
127 path: paths.appBuild,
128 // Generated JS file names (with nested folders).
129 // There will be one main bundle, and one file per asynchronous chunk.
130 // We don't currently advertise code splitting but Webpack supports it.
131 filename: 'static/js/[name].[chunkhash:8].js',
132 chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
133 // We inferred the "public path" (such as / or /my-project) from homepage.
134 publicPath: publicPath,
135 // Point sourcemap entries to original disk location (format as URL on Windows)
136 devtoolModuleFilenameTemplate: info =>
137 path
138 .relative(paths.appSrc, info.absoluteResourcePath)
139 .replace(/\\/g, '/'),
140 },
141 optimization: {
142 minimizer: [
143 new TerserPlugin({
144 terserOptions: {
145 parse: {
146 // we want terser to parse ecma 8 code. However, we don't want it
147 // to apply any minfication steps that turns valid ecma 5 code
148 // into invalid ecma 5 code. This is why the 'compress' and 'output'
149 // sections only apply transformations that are ecma 5 safe
150 // https://github.com/facebook/create-react-app/pull/4234
151 ecma: 8,
152 },
153 compress: {
154 ecma: 5,
155 warnings: false,
156 // Disabled because of an issue with Uglify breaking seemingly valid code:
157 // https://github.com/facebook/create-react-app/issues/2376
158 // Pending further investigation:
159 // https://github.com/mishoo/UglifyJS2/issues/2011
160 comparisons: false,
161 // Disabled because of an issue with Terser breaking valid code:
162 // https://github.com/facebook/create-react-app/issues/5250
163 // Pending futher investigation:
164 // https://github.com/terser-js/terser/issues/120
165 inline: 2,
166 },
167 mangle: {
168 safari10: true,
169 },
170 output: {
171 ecma: 5,
172 comments: false,
173 // Turned on because emoji and regex is not minified properly using default
174 // https://github.com/facebook/create-react-app/issues/2488
175 ascii_only: true,
176 },
177 },
178 // Use multi-process parallel running to improve the build speed
179 // Default number of concurrent runs: os.cpus().length - 1
180 parallel: true,
181 // Enable file caching
182 cache: terserCacheDirectory,
183 sourceMap: shouldUseSourceMap,
184 }),
185 new OptimizeCSSAssetsPlugin({
186 cssProcessorOptions: {
187 parser: safePostCssParser,
188 map: shouldUseSourceMap
189 ? {
190 // `inline: false` forces the sourcemap to be output into a
191 // separate file
192 inline: false,
193 // `annotation: true` appends the sourceMappingURL to the end of
194 // the css file, helping the browser find the sourcemap
195 annotation: true,
196 }
197 : false,
198 },
199 }),
200 ],
201 // Automatically split vendor and commons
202 // https://twitter.com/wSokra/status/969633336732905474
203 // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
204 splitChunks: process.env.RUNTIME_COVERAGE_REPORT ? false : {
205 chunks: 'all',
206 name: false,
207 },
208 // Keep the runtime chunk seperated to enable long term caching
209 // https://twitter.com/wSokra/status/969679223278505985
210 runtimeChunk: !process.env.RUNTIME_COVERAGE_REPORT,
211 },
212 resolve: {
213 // This allows you to set a fallback for where Webpack should look for modules.
214 // We placed these paths second because we want `node_modules` to "win"
215 // if there are any conflicts. This matches Node resolution mechanism.
216 // https://github.com/facebook/create-react-app/issues/253
217 modules: ['node_modules'].concat(
218 // It is guaranteed to exist because we tweak it in `env.js`
219 process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
220 ),
221 // These are the reasonable defaults supported by the Node ecosystem.
222 // We also include JSX as a common component filename extension to support
223 // some tools, although we do not recommend using it, see:
224 // https://github.com/facebook/create-react-app/issues/290
225 // `web` extension prefixes have been added for better support
226 // for React Native Web.
227 extensions: ['.mjs', '.web.js', '.js', '.json', '.web.jsx', '.jsx'],
228 alias: {
229 // Support React Native Web
230 // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
231 'react-native': 'react-native-web',
232 },
233 plugins: [
234 // Adds support for installing with Plug'n'Play, leading to faster installs and adding
235 // guards against forgotten dependencies and such.
236 PnpWebpackPlugin,
237 // Prevents users from importing files from outside of src/ (or node_modules/).
238 // This often causes confusion because we only process files within src/ with babel.
239 // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
240 // please link the files into your node_modules/ and let module-resolution kick in.
241 // Make sure your source files are compiled, as they will not be processed in any way.
242 new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
243 ],
244 },
245 resolveLoader: {
246 plugins: [
247 // Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
248 // from the current package.
249 PnpWebpackPlugin.moduleLoader(module),
250 ],
251 },
252 module: {
253 strictExportPresence: true,
254 rules: [
255 // Disable require.ensure as it's not a standard language feature.
256 { parser: { requireEnsure: false } },
257
258 // First, run the linter.
259 // It's important to do this before Babel processes the JS.
260 !process.env.NO_LINT && {
261 test: /\.(js|mjs|jsx)$/,
262 enforce: 'pre',
263 use: [
264 {
265 options: {
266 formatter: require.resolve('react-dev-utils/eslintFormatter'),
267 eslintPath: require.resolve('eslint'),
268 // @remove-on-eject-begin
269 // TODO: consider separate config for production,
270 // e.g. to enable no-console and no-debugger only in production.
271 baseConfig: {
272 extends: [require.resolve('@saritasa/eslint-config-react-app')],
273 settings: { react: { version: '999.999.999' } },
274 },
275 ignore: false,
276 useEslintrc: false,
277 // @remove-on-eject-end
278 },
279 loader: require.resolve('eslint-loader'),
280 },
281 ],
282 include: paths.appSrc,
283 },
284 process.env.RUNTIME_COVERAGE_REPORT ? {
285 test: /\.js$|\.jsx$/,
286 use: {
287 loader: 'istanbul-instrumenter-loader',
288 options: {
289 esModules: true,
290 produceSourceMap: false,
291 compact: false,
292 },
293 },
294 enforce: 'post',
295 exclude: /node_modules|__mocks__|\/libs\/|\.spec\.js$|\.unit\.js$|\.stories\.js$/,
296 } : null,
297 {
298 // "oneOf" will traverse all following loaders until one will
299 // match the requirements. When no loader matches it will fall
300 // back to the "file" loader at the end of the loader list.
301 oneOf: [
302 // "url" loader works just like "file" loader but it also embeds
303 // assets smaller than specified size as data URLs to avoid requests.
304 {
305 test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
306 loader: require.resolve('url-loader'),
307 options: {
308 limit: 10000,
309 name: 'static/media/[name].[hash:8].[ext]',
310 },
311 },
312 // Process application JS with Babel.
313 // The preset includes JSX, Flow, and some ESnext features.
314 {
315 test: /\.(js|mjs|jsx)$/,
316 include: paths.appSrc,
317
318 loader: require.resolve('babel-loader'),
319 options: {
320 customize: require.resolve(
321 'babel-preset-react-app/webpack-overrides'
322 ),
323 // @remove-on-eject-begin
324 babelrc: false,
325 configFile: false,
326 presets: [require.resolve('babel-preset-react-app')],
327 // Make sure we have a unique cache identifier, erring on the
328 // side of caution.
329 // We remove this when the user ejects because the default
330 // is sane and uses Babel options. Instead of options, we use
331 // the react-scripts and babel-preset-react-app versions.
332 cacheIdentifier: getCacheIdentifier('production', [
333 'babel-plugin-named-asset-import',
334 'babel-preset-react-app',
335 'react-dev-utils',
336 'react-scripts',
337 ]),
338 // @remove-on-eject-end
339 plugins: [
340 [
341 require.resolve('babel-plugin-named-asset-import'),
342 {
343 loaderMap: {
344 svg: {
345 ReactComponent: '@svgr/webpack?-prettier,-svgo![path]',
346 },
347 },
348 },
349 ],
350 ],
351 cacheDirectory: babelCacheDirectory,
352 // Save disk space when time isn't as important
353 cacheCompression: true,
354 compact: true,
355 },
356 },
357 // Process any JS outside of the app with Babel.
358 // Unlike the application JS, we only compile the standard ES features.
359 {
360 test: /\.(js|mjs)$/,
361 exclude: /@babel(?:\/|\\{1,2})runtime/,
362 loader: require.resolve('babel-loader'),
363 options: {
364 babelrc: false,
365 configFile: false,
366 compact: false,
367 presets: [
368 [
369 require.resolve('babel-preset-react-app/dependencies'),
370 { helpers: true },
371 ],
372 ],
373 cacheDirectory: true,
374 // Save disk space when time isn't as important
375 cacheCompression: true,
376 // @remove-on-eject-begin
377 cacheIdentifier: getCacheIdentifier('production', [
378 'babel-plugin-named-asset-import',
379 'babel-preset-react-app',
380 'react-dev-utils',
381 'react-scripts',
382 ]),
383 // @remove-on-eject-end
384 // If an error happens in a package, it's possible to be
385 // because it was compiled. Thus, we don't want the browser
386 // debugger to show the original code. Instead, the code
387 // being evaluated would be much more helpful.
388 sourceMaps: false,
389 },
390 },
391 // "postcss" loader applies autoprefixer to our CSS.
392 // "css" loader resolves paths in CSS and adds assets as dependencies.
393 // `MiniCSSExtractPlugin` extracts styles into CSS
394 // files. If you use code splitting, async bundles will have their own separate CSS chunk file.
395 // By default we support CSS Modules with the extension .css
396 {
397 test: cssModuleRegex,
398 exclude: cssGlobalRegex,
399 loader: getStyleLoaders({
400 importLoaders: 1,
401 sourceMap: shouldUseSourceMap,
402 modules: true,
403 getLocalIdent: getCSSModuleLocalIdent,
404 }),
405 // Don't consider CSS imports dead code even if the
406 // containing package claims to have no side effects.
407 // Remove this when webpack adds a warning or an error for this.
408 // See https://github.com/webpack/webpack/issues/6571
409 sideEffects: true,
410 },
411 // Adds support for global CSS
412 // using the extension .global.css
413 {
414 test: cssModuleRegex,
415 loader: getStyleLoaders({
416 importLoaders: 1,
417 sourceMap: shouldUseSourceMap,
418 }),
419 },
420 // Opt-in support for SASS. The logic here is somewhat similar
421 // as in the CSS routine, except that "sass-loader" runs first
422 // to compile SASS files into CSS.
423 // By default we support SASS Modules with the
424 // extensions .scss or .sass
425 {
426 test: sassModuleRegex,
427 exclude: sassGlobalRegex,
428 loader: getStyleLoaders(
429 {
430 importLoaders: 2,
431 sourceMap: shouldUseSourceMap,
432 modules: true,
433 getLocalIdent: getCSSModuleLocalIdent,
434 },
435 'sass-loader'
436 ),
437 // Don't consider CSS imports dead code even if the
438 // containing package claims to have no side effects.
439 // Remove this when webpack adds a warning or an error for this.
440 // See https://github.com/webpack/webpack/issues/6571
441 sideEffects: true,
442 },
443 // Adds support for global CSS Modules, but using SASS
444 // using the extension .global.scss or .global.sass
445 {
446 test: sassModuleRegex,
447 loader: getStyleLoaders(
448 {
449 importLoaders: 2,
450 sourceMap: shouldUseSourceMap,
451 },
452 'sass-loader'
453 ),
454 },
455 // "file" loader makes sure assets end up in the `build` folder.
456 // When you `import` an asset, you get its filename.
457 // This loader doesn't use a "test" so it will catch all modules
458 // that fall through the other loaders.
459 {
460 loader: require.resolve('file-loader'),
461 // Exclude `js` files to keep "css" loader working as it injects
462 // it's runtime that would otherwise be processed through "file" loader.
463 // Also exclude `html` and `json` extensions so they get processed
464 // by webpacks internal loaders.
465 exclude: [/\.(js|mjs|jsx)$/, /\.html$/, /\.json$/],
466 options: {
467 name: 'static/media/[name].[hash:8].[ext]',
468 },
469 },
470 // ** STOP ** Are you adding a new loader?
471 // Make sure to add the new loader(s) before the "file" loader.
472 ],
473 },
474 ].filter(Boolean),
475 },
476 plugins: [
477 // Detects circular dependencies
478 new CircularDependencyPlugin({
479 // exclude detection of files based on a RegExp
480 exclude: /node_modules/,
481 // add errors to webpack instead of warnings
482 failOnError: true,
483 // allow import cycles that include an asyncronous import,
484 // e.g. via import(/* webpackMode: "weak" */ './file.js')
485 allowAsyncCycles: false,
486 // set the current working directory for displaying module paths
487 cwd: process.cwd(),
488 }),
489 // Generates an `index.html` file with the <script> injected.
490 new HtmlWebpackPlugin({
491 inject: true,
492 template: paths.appHtml,
493 minify: {
494 removeComments: true,
495 collapseWhitespace: true,
496 removeRedundantAttributes: true,
497 useShortDoctype: true,
498 removeEmptyAttributes: true,
499 removeStyleLinkTypeAttributes: true,
500 keepClosingSlash: true,
501 minifyJS: true,
502 minifyCSS: true,
503 minifyURLs: true,
504 },
505 }),
506 // Inlines the webpack runtime script. This script is too small to warrant
507 // a network request.
508 shouldInlineRuntimeChunk && new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime~.+[.]js/]),
509 // Makes some environment variables available in index.html.
510 // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
511 // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
512 // In production, it will be an empty string unless you specify "homepage"
513 // in `package.json`, in which case it will be the pathname of that URL.
514 new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
515 // This gives some necessary context to module not found errors, such as
516 // the requesting resource.
517 new ModuleNotFoundPlugin(paths.appPath),
518 // Makes some environment variables available to the JS code, for example:
519 // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
520 // It is absolutely essential that NODE_ENV was set to production here.
521 // Otherwise React will be compiled in the very slow development mode.
522 new webpack.DefinePlugin(env.stringified),
523 new MiniCssExtractPlugin({
524 // Options similar to the same options in webpackOptions.output
525 // both options are optional
526 filename: 'static/css/[name].[contenthash:8].css',
527 chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
528 }),
529 // Generate a manifest file which contains a mapping of all asset filenames
530 // to their corresponding output file so that tools can pick it up without
531 // having to parse `index.html`.
532 new ManifestPlugin({
533 fileName: 'asset-manifest.json',
534 publicPath: publicPath,
535 }),
536 // Moment.js is an extremely popular library that bundles large locale files
537 // by default due to how Webpack interprets its code. This is a practical
538 // solution that requires the user to opt into importing specific locales.
539 // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
540 // You can remove this if you don't use Moment.js:
541 new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
542 // Generate a service worker script that will precache, and keep up to date,
543 // the HTML & assets that are part of the Webpack build.
544 new WorkboxWebpackPlugin.GenerateSW({
545 clientsClaim: true,
546 skipWaiting: Boolean(process.env.WORKBOX_SKIP_WAITING),
547 exclude: [/\.map$/, /asset-manifest\.json$/],
548 importWorkboxFrom: 'cdn',
549 navigateFallback: publicUrl + '/index.html',
550 navigateFallbackBlacklist: [
551 // Exclude URLs starting with /_, as they're likely an API call
552 new RegExp('^/_'),
553 // Exclude URLs containing a dot, as they're likely a resource in
554 // public/ and not a SPA route
555 new RegExp('/[^/]+\\.[^/]+$'),
556 ],
557 }),
558 ].filter(Boolean),
559 // Some libraries import Node modules but don't use them in the browser.
560 // Tell Webpack to provide empty mocks for them so importing them works.
561 node: {
562 dgram: 'empty',
563 fs: 'empty',
564 net: 'empty',
565 tls: 'empty',
566 child_process: 'empty',
567 },
568 // Turn off performance processing because we utilize
569 // our own hints via the FileSizeReporter
570 performance: false,
571};