UNPKG

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