Merge branch '21224-project-details'
[arvados.git] / services / workbench2 / config / webpack.config.js
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 'use strict';
6
7 const fs = require('fs');
8 const path = require('path');
9 const webpack = require('webpack');
10 const resolve = require('resolve');
11 const HtmlWebpackPlugin = require('html-webpack-plugin');
12 const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
13 const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
14 const TerserPlugin = require('terser-webpack-plugin');
15 const MiniCssExtractPlugin = require('mini-css-extract-plugin');
16 const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
17 const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
18 const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
19 const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
20 const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
21 const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
22 const ESLintPlugin = require('eslint-webpack-plugin');
23 const paths = require('./paths');
24 const modules = require('./modules');
25 const getClientEnvironment = require('./env');
26 const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
27 const ForkTsCheckerWebpackPlugin =
28   process.env.TSC_COMPILE_ON_ERROR === 'true'
29     ? require('react-dev-utils/ForkTsCheckerWarningWebpackPlugin')
30     : require('react-dev-utils/ForkTsCheckerWebpackPlugin');
31 const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
32
33 const createEnvironmentHash = require('./webpack/persistentCache/createEnvironmentHash');
34
35 // Source maps are resource heavy and can cause out of memory issue for large source files.
36 const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
37
38 const reactRefreshRuntimeEntry = require.resolve('react-refresh/runtime');
39 const reactRefreshWebpackPluginRuntimeEntry = require.resolve(
40   '@pmmmwh/react-refresh-webpack-plugin'
41 );
42 const babelRuntimeEntry = require.resolve('babel-preset-react-app');
43 const babelRuntimeEntryHelpers = require.resolve(
44   '@babel/runtime/helpers/esm/assertThisInitialized',
45   { paths: [babelRuntimeEntry] }
46 );
47 const babelRuntimeRegenerator = require.resolve('@babel/runtime/regenerator', {
48   paths: [babelRuntimeEntry],
49 });
50
51 // Some apps do not need the benefits of saving a web request, so not inlining the chunk
52 // makes for a smoother build process.
53 const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
54
55 const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === 'true';
56 const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === 'true';
57
58 const imageInlineSizeLimit = parseInt(
59   process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
60 );
61
62 // Check if TypeScript is setup
63 const useTypeScript = fs.existsSync(paths.appTsConfig);
64
65 // Check if Tailwind config exists
66 const useTailwind = fs.existsSync(
67   path.join(paths.appPath, 'tailwind.config.js')
68 );
69
70 // Get the path to the uncompiled service worker (if it exists).
71 const swSrc = paths.swSrc;
72
73 // style files regexes
74 const cssRegex = /\.css$/;
75 const cssModuleRegex = /\.module\.css$/;
76 const sassRegex = /\.(scss|sass)$/;
77 const sassModuleRegex = /\.module\.(scss|sass)$/;
78
79 const hasJsxRuntime = (() => {
80   if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
81     return false;
82   }
83
84   try {
85     require.resolve('react/jsx-runtime');
86     return true;
87   } catch (e) {
88     return false;
89   }
90 })();
91
92 // This is the production and development configuration.
93 // It is focused on developer experience, fast rebuilds, and a minimal bundle.
94 module.exports = function (webpackEnv) {
95   const isEnvDevelopment = webpackEnv === 'development';
96   const isEnvProduction = webpackEnv === 'production';
97
98   // Variable used for enabling profiling in Production
99   // passed into alias object. Uses a flag if passed into the build command
100   const isEnvProductionProfile =
101     isEnvProduction && process.argv.includes('--profile');
102
103   // We will provide `paths.publicUrlOrPath` to our app
104   // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
105   // Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
106   // Get environment variables to inject into our app.
107   const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
108
109   const shouldUseReactRefresh = env.raw.FAST_REFRESH;
110
111   // common function to get style loaders
112   const getStyleLoaders = (cssOptions, preProcessor) => {
113     const loaders = [
114       isEnvDevelopment && require.resolve('style-loader'),
115       isEnvProduction && {
116         loader: MiniCssExtractPlugin.loader,
117         // css is located in `static/css`, use '../../' to locate index.html folder
118         // in production `paths.publicUrlOrPath` can be a relative path
119         options: paths.publicUrlOrPath.startsWith('.')
120           ? { publicPath: '../../' }
121           : {},
122       },
123       {
124         loader: require.resolve('css-loader'),
125         options: cssOptions,
126       },
127       {
128         // Options for PostCSS as we reference these options twice
129         // Adds vendor prefixing based on your specified browser support in
130         // package.json
131         loader: require.resolve('postcss-loader'),
132         options: {
133           postcssOptions: {
134             // Necessary for external CSS imports to work
135             // https://github.com/facebook/create-react-app/issues/2677
136             ident: 'postcss',
137             config: false,
138             plugins: !useTailwind
139               ? [
140                   'postcss-flexbugs-fixes',
141                   [
142                     'postcss-preset-env',
143                     {
144                       autoprefixer: {
145                         flexbox: 'no-2009',
146                       },
147                       stage: 3,
148                     },
149                   ],
150                   // Adds PostCSS Normalize as the reset css with default options,
151                   // so that it honors browserslist config in package.json
152                   // which in turn let's users customize the target behavior as per their needs.
153                   'postcss-normalize',
154                 ]
155               : [
156                   'tailwindcss',
157                   'postcss-flexbugs-fixes',
158                   [
159                     'postcss-preset-env',
160                     {
161                       autoprefixer: {
162                         flexbox: 'no-2009',
163                       },
164                       stage: 3,
165                     },
166                   ],
167                 ],
168           },
169           sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
170         },
171       },
172     ].filter(Boolean);
173     if (preProcessor) {
174       loaders.push(
175         {
176           loader: require.resolve('resolve-url-loader'),
177           options: {
178             sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
179             root: paths.appSrc,
180           },
181         },
182         {
183           loader: require.resolve(preProcessor),
184           options: {
185             sourceMap: true,
186           },
187         }
188       );
189     }
190     return loaders;
191   };
192
193   return {
194     target: ['browserslist'],
195     mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
196     // Stop compilation early in production
197     bail: isEnvProduction,
198     devtool: isEnvProduction
199       ? shouldUseSourceMap
200         ? 'source-map'
201         : false
202       : isEnvDevelopment && 'cheap-module-source-map',
203     // These are the "entry points" to our application.
204     // This means they will be the "root" imports that are included in JS bundle.
205     entry: paths.appIndexJs,
206     output: {
207       // The build folder.
208       path: paths.appBuild,
209       // Add /* filename */ comments to generated require()s in the output.
210       pathinfo: isEnvDevelopment,
211       // There will be one main bundle, and one file per asynchronous chunk.
212       // In development, it does not produce real files.
213       filename: isEnvProduction
214         ? 'static/js/[name].[contenthash:8].js'
215         : isEnvDevelopment && 'static/js/bundle.js',
216       // There are also additional JS chunk files if you use code splitting.
217       chunkFilename: isEnvProduction
218         ? 'static/js/[name].[contenthash:8].chunk.js'
219         : isEnvDevelopment && 'static/js/[name].chunk.js',
220       assetModuleFilename: 'static/media/[name].[hash][ext]',
221       // webpack uses `publicPath` to determine where the app is being served from.
222       // It requires a trailing slash, or the file assets will get an incorrect path.
223       // We inferred the "public path" (such as / or /my-project) from homepage.
224       publicPath: paths.publicUrlOrPath,
225       // Point sourcemap entries to original disk location (format as URL on Windows)
226       devtoolModuleFilenameTemplate: isEnvProduction
227         ? info =>
228             path
229               .relative(paths.appSrc, info.absoluteResourcePath)
230               .replace(/\\/g, '/')
231         : isEnvDevelopment &&
232           (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
233     },
234     cache: {
235       type: 'filesystem',
236       version: createEnvironmentHash(env.raw),
237       cacheDirectory: paths.appWebpackCache,
238       store: 'pack',
239       buildDependencies: {
240         defaultWebpack: ['webpack/lib/'],
241         config: [__filename],
242         tsconfig: [paths.appTsConfig, paths.appJsConfig].filter(f =>
243           fs.existsSync(f)
244         ),
245       },
246     },
247     infrastructureLogging: {
248       level: 'none',
249     },
250     optimization: {
251       minimize: isEnvProduction,
252       minimizer: [
253         // This is only used in production mode
254         new TerserPlugin({
255           terserOptions: {
256             parse: {
257               // We want terser to parse ecma 8 code. However, we don't want it
258               // to apply any minification steps that turns valid ecma 5 code
259               // into invalid ecma 5 code. This is why the 'compress' and 'output'
260               // sections only apply transformations that are ecma 5 safe
261               // https://github.com/facebook/create-react-app/pull/4234
262               ecma: 8,
263             },
264             compress: {
265               ecma: 5,
266               warnings: false,
267               // Disabled because of an issue with Uglify breaking seemingly valid code:
268               // https://github.com/facebook/create-react-app/issues/2376
269               // Pending further investigation:
270               // https://github.com/mishoo/UglifyJS2/issues/2011
271               comparisons: false,
272               // Disabled because of an issue with Terser breaking valid code:
273               // https://github.com/facebook/create-react-app/issues/5250
274               // Pending further investigation:
275               // https://github.com/terser-js/terser/issues/120
276               inline: 2,
277             },
278             mangle: {
279               safari10: true,
280             },
281             // Added for profiling in devtools
282             keep_classnames: isEnvProductionProfile,
283             keep_fnames: isEnvProductionProfile,
284             output: {
285               ecma: 5,
286               comments: false,
287               // Turned on because emoji and regex is not minified properly using default
288               // https://github.com/facebook/create-react-app/issues/2488
289               ascii_only: true,
290             },
291           },
292         }),
293         // This is only used in production mode
294         new CssMinimizerPlugin(),
295       ],
296     },
297     resolve: {
298       // This allows you to set a fallback for where webpack should look for modules.
299       // We placed these paths second because we want `node_modules` to "win"
300       // if there are any conflicts. This matches Node resolution mechanism.
301       // https://github.com/facebook/create-react-app/issues/253
302       modules: ['node_modules', paths.appNodeModules].concat(
303         modules.additionalModulePaths || []
304       ),
305       fallback: { "path": require.resolve("path-browserify") },
306       // These are the reasonable defaults supported by the Node ecosystem.
307       // We also include JSX as a common component filename extension to support
308       // some tools, although we do not recommend using it, see:
309       // https://github.com/facebook/create-react-app/issues/290
310       // `web` extension prefixes have been added for better support
311       // for React Native Web.
312       extensions: paths.moduleFileExtensions
313         .map(ext => `.${ext}`)
314         .filter(ext => useTypeScript || !ext.includes('ts')),
315       alias: {
316         // Support React Native Web
317         // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
318         'react-native': 'react-native-web',
319         // Allows for better profiling with ReactDevTools
320         ...(isEnvProductionProfile && {
321           'react-dom$': 'react-dom/profiling',
322           'scheduler/tracing': 'scheduler/tracing-profiling',
323         }),
324         ...(modules.webpackAliases || {}),
325       },
326       plugins: [
327         // Prevents users from importing files from outside of src/ (or node_modules/).
328         // This often causes confusion because we only process files within src/ with babel.
329         // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
330         // please link the files into your node_modules/ and let module-resolution kick in.
331         // Make sure your source files are compiled, as they will not be processed in any way.
332         new ModuleScopePlugin(paths.appSrc, [
333           paths.appPackageJson,
334           reactRefreshRuntimeEntry,
335           reactRefreshWebpackPluginRuntimeEntry,
336           babelRuntimeEntry,
337           babelRuntimeEntryHelpers,
338           babelRuntimeRegenerator,
339         ]),
340       ],
341     },
342     module: {
343       strictExportPresence: true,
344       rules: [
345         // Handle node_modules packages that contain sourcemaps
346         shouldUseSourceMap && {
347           enforce: 'pre',
348           exclude: /@babel(?:\/|\\{1,2})runtime/,
349           test: /\.(js|mjs|jsx|ts|tsx|css)$/,
350           loader: require.resolve('source-map-loader'),
351         },
352         {
353           // "oneOf" will traverse all following loaders until one will
354           // match the requirements. When no loader matches it will fall
355           // back to the "file" loader at the end of the loader list.
356           oneOf: [
357             // TODO: Merge this config once `image/avif` is in the mime-db
358             // https://github.com/jshttp/mime-db
359             {
360               test: [/\.avif$/],
361               type: 'asset',
362               mimetype: 'image/avif',
363               parser: {
364                 dataUrlCondition: {
365                   maxSize: imageInlineSizeLimit,
366                 },
367               },
368             },
369             // "url" loader works like "file" loader except that it embeds assets
370             // smaller than specified limit in bytes as data URLs to avoid requests.
371             // A missing `test` is equivalent to a match.
372             {
373               test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
374               type: 'asset',
375               parser: {
376                 dataUrlCondition: {
377                   maxSize: imageInlineSizeLimit,
378                 },
379               },
380             },
381             {
382               test: /\.svg$/,
383               use: [
384                 {
385                   loader: require.resolve('@svgr/webpack'),
386                   options: {
387                     prettier: false,
388                     svgo: false,
389                     svgoConfig: {
390                       plugins: [{ removeViewBox: false }],
391                     },
392                     titleProp: true,
393                     ref: true,
394                   },
395                 },
396                 {
397                   loader: require.resolve('file-loader'),
398                   options: {
399                     name: 'static/media/[name].[hash].[ext]',
400                   },
401                 },
402               ],
403               issuer: {
404                 and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
405               },
406             },
407             // Process application JS with Babel.
408             // The preset includes JSX, Flow, TypeScript, and some ESnext features.
409             {
410               test: /\.(js|mjs|jsx|ts|tsx)$/,
411               include: paths.appSrc,
412               loader: require.resolve('babel-loader'),
413               options: {
414                 customize: require.resolve(
415                   'babel-preset-react-app/webpack-overrides'
416                 ),
417                 presets: [
418                   [
419                     require.resolve('babel-preset-react-app'),
420                     {
421                       runtime: hasJsxRuntime ? 'automatic' : 'classic',
422                     },
423                   ],
424                 ],
425                 
426                 plugins: [
427                   isEnvDevelopment &&
428                     shouldUseReactRefresh &&
429                     require.resolve('react-refresh/babel'),
430                 ].filter(Boolean),
431                 // This is a feature of `babel-loader` for webpack (not Babel itself).
432                 // It enables caching results in ./node_modules/.cache/babel-loader/
433                 // directory for faster rebuilds.
434                 cacheDirectory: true,
435                 // See #6846 for context on why cacheCompression is disabled
436                 cacheCompression: false,
437                 compact: isEnvProduction,
438               },
439             },
440             // Process any JS outside of the app with Babel.
441             // Unlike the application JS, we only compile the standard ES features.
442             {
443               test: /\.(js|mjs)$/,
444               exclude: /@babel(?:\/|\\{1,2})runtime/,
445               loader: require.resolve('babel-loader'),
446               options: {
447                 babelrc: false,
448                 configFile: false,
449                 compact: false,
450                 presets: [
451                   [
452                     require.resolve('babel-preset-react-app/dependencies'),
453                     { helpers: true },
454                   ],
455                 ],
456                 cacheDirectory: true,
457                 // See #6846 for context on why cacheCompression is disabled
458                 cacheCompression: false,
459                 
460                 // Babel sourcemaps are needed for debugging into node_modules
461                 // code.  Without the options below, debuggers like VSCode
462                 // show incorrect code and set breakpoints on the wrong lines.
463                 sourceMaps: shouldUseSourceMap,
464                 inputSourceMap: shouldUseSourceMap,
465               },
466             },
467             // "postcss" loader applies autoprefixer to our CSS.
468             // "css" loader resolves paths in CSS and adds assets as dependencies.
469             // "style" loader turns CSS into JS modules that inject <style> tags.
470             // In production, we use MiniCSSExtractPlugin to extract that CSS
471             // to a file, but in development "style" loader enables hot editing
472             // of CSS.
473             // By default we support CSS Modules with the extension .module.css
474             {
475               test: cssRegex,
476               exclude: cssModuleRegex,
477               use: getStyleLoaders({
478                 importLoaders: 1,
479                 sourceMap: isEnvProduction
480                   ? shouldUseSourceMap
481                   : isEnvDevelopment,
482                 modules: {
483                   mode: 'icss',
484                 },
485               }),
486               // Don't consider CSS imports dead code even if the
487               // containing package claims to have no side effects.
488               // Remove this when webpack adds a warning or an error for this.
489               // See https://github.com/webpack/webpack/issues/6571
490               sideEffects: true,
491             },
492             // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
493             // using the extension .module.css
494             {
495               test: cssModuleRegex,
496               use: getStyleLoaders({
497                 importLoaders: 1,
498                 sourceMap: isEnvProduction
499                   ? shouldUseSourceMap
500                   : isEnvDevelopment,
501                 modules: {
502                   mode: 'local',
503                   getLocalIdent: getCSSModuleLocalIdent,
504                 },
505               }),
506             },
507             // Opt-in support for SASS (using .scss or .sass extensions).
508             // By default we support SASS Modules with the
509             // extensions .module.scss or .module.sass
510             {
511               test: sassRegex,
512               exclude: sassModuleRegex,
513               use: getStyleLoaders(
514                 {
515                   importLoaders: 3,
516                   sourceMap: isEnvProduction
517                     ? shouldUseSourceMap
518                     : isEnvDevelopment,
519                   modules: {
520                     mode: 'icss',
521                   },
522                 },
523                 'sass-loader'
524               ),
525               // Don't consider CSS imports dead code even if the
526               // containing package claims to have no side effects.
527               // Remove this when webpack adds a warning or an error for this.
528               // See https://github.com/webpack/webpack/issues/6571
529               sideEffects: true,
530             },
531             // Adds support for CSS Modules, but using SASS
532             // using the extension .module.scss or .module.sass
533             {
534               test: sassModuleRegex,
535               use: getStyleLoaders(
536                 {
537                   importLoaders: 3,
538                   sourceMap: isEnvProduction
539                     ? shouldUseSourceMap
540                     : isEnvDevelopment,
541                   modules: {
542                     mode: 'local',
543                     getLocalIdent: getCSSModuleLocalIdent,
544                   },
545                 },
546                 'sass-loader'
547               ),
548             },
549             // "file" loader makes sure those assets get served by WebpackDevServer.
550             // When you `import` an asset, you get its (virtual) filename.
551             // In production, they would get copied to the `build` folder.
552             // This loader doesn't use a "test" so it will catch all modules
553             // that fall through the other loaders.
554             {
555               // Exclude `js` files to keep "css" loader working as it injects
556               // its runtime that would otherwise be processed through "file" loader.
557               // Also exclude `html` and `json` extensions so they get processed
558               // by webpacks internal loaders.
559               exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
560               type: 'asset/resource',
561             },
562             // ** STOP ** Are you adding a new loader?
563             // Make sure to add the new loader(s) before the "file" loader.
564           ],
565         },
566       ].filter(Boolean),
567     },
568     plugins: [
569       // Generates an `index.html` file with the <script> injected.
570       new HtmlWebpackPlugin(
571         Object.assign(
572           {},
573           {
574             inject: true,
575             template: paths.appHtml,
576           },
577           isEnvProduction
578             ? {
579                 minify: {
580                   removeComments: true,
581                   collapseWhitespace: true,
582                   removeRedundantAttributes: true,
583                   useShortDoctype: true,
584                   removeEmptyAttributes: true,
585                   removeStyleLinkTypeAttributes: true,
586                   keepClosingSlash: true,
587                   minifyJS: true,
588                   minifyCSS: true,
589                   minifyURLs: true,
590                 },
591               }
592             : undefined
593         )
594       ),
595       // Inlines the webpack runtime script. This script is too small to warrant
596       // a network request.
597       // https://github.com/facebook/create-react-app/issues/5358
598       isEnvProduction &&
599         shouldInlineRuntimeChunk &&
600         new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
601       // Makes some environment variables available in index.html.
602       // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
603       // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
604       // It will be an empty string unless you specify "homepage"
605       // in `package.json`, in which case it will be the pathname of that URL.
606       new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
607       // This gives some necessary context to module not found errors, such as
608       // the requesting resource.
609       new ModuleNotFoundPlugin(paths.appPath),
610       // Makes some environment variables available to the JS code, for example:
611       // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
612       // It is absolutely essential that NODE_ENV is set to production
613       // during a production build.
614       // Otherwise React will be compiled in the very slow development mode.
615       new webpack.DefinePlugin(env.stringified),
616       // Experimental hot reloading for React .
617       // https://github.com/facebook/react/tree/main/packages/react-refresh
618       isEnvDevelopment &&
619         shouldUseReactRefresh &&
620         new ReactRefreshWebpackPlugin({
621           overlay: false,
622         }),
623       // Watcher doesn't work well if you mistype casing in a path so we use
624       // a plugin that prints an error when you attempt to do this.
625       // See https://github.com/facebook/create-react-app/issues/240
626       isEnvDevelopment && new CaseSensitivePathsPlugin(),
627       isEnvProduction &&
628         new MiniCssExtractPlugin({
629           // Options similar to the same options in webpackOptions.output
630           // both options are optional
631           filename: 'static/css/[name].[contenthash:8].css',
632           chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
633         }),
634       // Generate an asset manifest file with the following content:
635       // - "files" key: Mapping of all asset filenames to their corresponding
636       //   output file so that tools can pick it up without having to parse
637       //   `index.html`
638       // - "entrypoints" key: Array of files which are included in `index.html`,
639       //   can be used to reconstruct the HTML if necessary
640       new WebpackManifestPlugin({
641         fileName: 'asset-manifest.json',
642         publicPath: paths.publicUrlOrPath,
643         generate: (seed, files, entrypoints) => {
644           const manifestFiles = files.reduce((manifest, file) => {
645             manifest[file.name] = file.path;
646             return manifest;
647           }, seed);
648           const entrypointFiles = entrypoints.main.filter(
649             fileName => !fileName.endsWith('.map')
650           );
651
652           return {
653             files: manifestFiles,
654             entrypoints: entrypointFiles,
655           };
656         },
657       }),
658       // Moment.js is an extremely popular library that bundles large locale files
659       // by default due to how webpack interprets its code. This is a practical
660       // solution that requires the user to opt into importing specific locales.
661       // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
662       // You can remove this if you don't use Moment.js:
663       new webpack.IgnorePlugin({
664         resourceRegExp: /^\.\/locale$/,
665         contextRegExp: /moment$/,
666       }),
667       // Generate a service worker script that will precache, and keep up to date,
668       // the HTML & assets that are part of the webpack build.
669       isEnvProduction &&
670         fs.existsSync(swSrc) &&
671         new WorkboxWebpackPlugin.InjectManifest({
672           swSrc,
673           dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
674           exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
675           // Bump up the default maximum size (2mb) that's precached,
676           // to make lazy-loading failure scenarios less likely.
677           // See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270
678           maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
679         }),
680       // TypeScript type checking
681       useTypeScript &&
682         new ForkTsCheckerWebpackPlugin({
683           async: isEnvDevelopment,
684           typescript: {
685             typescriptPath: resolve.sync('typescript', {
686               basedir: paths.appNodeModules,
687             }),
688             configOverwrite: {
689               compilerOptions: {
690                 sourceMap: isEnvProduction
691                   ? shouldUseSourceMap
692                   : isEnvDevelopment,
693                 skipLibCheck: true,
694                 inlineSourceMap: false,
695                 declarationMap: false,
696                 noEmit: true,
697                 incremental: true,
698                 tsBuildInfoFile: paths.appTsBuildInfoFile,
699               },
700             },
701             context: paths.appPath,
702             diagnosticOptions: {
703               syntactic: true,
704             },
705             mode: 'write-references',
706             // profile: true,
707           },
708           issue: {
709             // This one is specifically to match during CI tests,
710             // as micromatch doesn't match
711             // '../cra-template-typescript/template/src/App.tsx'
712             // otherwise.
713             include: [
714               { file: '../**/src/**/*.{ts,tsx}' },
715               { file: '**/src/**/*.{ts,tsx}' },
716             ],
717             exclude: [
718               { file: '**/src/**/__tests__/**' },
719               { file: '**/src/**/?(*.){spec|test}.*' },
720               { file: '**/src/setupProxy.*' },
721               { file: '**/src/setupTests.*' },
722             ],
723           },
724           logger: {
725             infrastructure: 'silent',
726           },
727         }),
728       !disableESLintPlugin &&
729         new ESLintPlugin({
730           // Plugin options
731           extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
732           formatter: require.resolve('react-dev-utils/eslintFormatter'),
733           eslintPath: require.resolve('eslint'),
734           failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
735           context: paths.appSrc,
736           cache: true,
737           cacheLocation: path.resolve(
738             paths.appNodeModules,
739             '.cache/.eslintcache'
740           ),
741           // ESLint class options
742           cwd: paths.appPath,
743           resolvePluginsRelativeTo: __dirname,
744           baseConfig: {
745             extends: [require.resolve('eslint-config-react-app/base')],
746             rules: {
747               ...(!hasJsxRuntime && {
748                 'react/react-in-jsx-scope': 'error',
749               }),
750             },
751           },
752         }),
753     ].filter(Boolean),
754     // Turn off performance processing because we utilize
755     // our own hints via the FileSizeReporter
756     performance: false,
757   };
758 };