webpack.config.js 28.8 KB
Newer Older
zhanghaozhe committed
1
"use strict"
2

zhanghaozhe committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
const fs = require("fs")
const path = require("path")
const webpack = require("webpack")
const resolve = require("resolve")
const PnpWebpackPlugin = require("pnp-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const CaseSensitivePathsPlugin = require("case-sensitive-paths-webpack-plugin")
const InlineChunkHtmlPlugin = require("react-dev-utils/InlineChunkHtmlPlugin")
const TerserPlugin = require("terser-webpack-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
const safePostCssParser = require("postcss-safe-parser")
const ManifestPlugin = require("webpack-manifest-plugin")
const InterpolateHtmlPlugin = require("react-dev-utils/InterpolateHtmlPlugin")
const WorkboxWebpackPlugin = require("workbox-webpack-plugin")
const WatchMissingNodeModulesPlugin = require("react-dev-utils/WatchMissingNodeModulesPlugin")
const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin")
const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent")
const paths = require("./paths")
const modules = require("./modules")
const getClientEnvironment = require("./env")
const ModuleNotFoundPlugin = require("react-dev-utils/ModuleNotFoundPlugin")
const ForkTsCheckerWebpackPlugin = require("react-dev-utils/ForkTsCheckerWebpackPlugin")
const typescriptFormatter = require("react-dev-utils/typescriptFormatter")
27

zhanghaozhe committed
28
const postcssNormalize = require("postcss-normalize")
zhanghaozhe committed
29 30

const appPackageJson = require(paths.appPackageJson)
31 32

// Source maps are resource heavy and can cause out of memory issue for large source files.
zhanghaozhe committed
33
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false"
34 35
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
// makes for a smoother build process.
zhanghaozhe committed
36
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== "false"
zhanghaozhe committed
37

zhanghaozhe committed
38
const isExtendingEslintConfig = process.env.EXTEND_ESLINT === "true"
zhanghaozhe committed
39

zhanghaozhe committed
40 41 42
const imageInlineSizeLimit = parseInt(
  process.env.IMAGE_INLINE_SIZE_LIMIT || "10000"
)
43 44

// Check if TypeScript is setup
zhanghaozhe committed
45
const useTypeScript = fs.existsSync(paths.appTsConfig)
46 47

// style files regexes
zhanghaozhe committed
48 49 50 51
const cssRegex = /\.css$/
const cssModuleRegex = /\.module\.css$/
const sassRegex = /\.(scss|sass)$/
const sassModuleRegex = /\.module\.(scss|sass)$/
52 53 54

// This is the production and development configuration.
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
zhanghaozhe committed
55
module.exports = function (webpackEnv) {
zhanghaozhe committed
56 57
  const isEnvDevelopment = webpackEnv === "development"
  const isEnvProduction = webpackEnv === "production"
58

zhanghaozhe committed
59 60
  // Variable used for enabling profiling in Production
  // passed into alias object. Uses a flag if passed into the build command
zhanghaozhe committed
61 62
  const isEnvProductionProfile =
    isEnvProduction && process.argv.includes("--profile")
63

zhanghaozhe committed
64
  // We will provide `paths.publicUrlOrPath` to our app
65 66 67
  // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
  // Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
  // Get environment variables to inject into our app.
zhanghaozhe committed
68
  const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1))
69 70 71 72

  // common function to get style loaders
  const getStyleLoaders = (cssOptions, preProcessor) => {
    const loaders = [
zhanghaozhe committed
73
      isEnvDevelopment && require.resolve("style-loader"),
74 75
      isEnvProduction && {
        loader: MiniCssExtractPlugin.loader,
zhanghaozhe committed
76 77
        // css is located in `mrstaticcss`, use '../../' to locate index.html folder
        // in production `paths.publicUrlOrPath` can be a relative path
zhanghaozhe committed
78 79 80
        options: paths.publicUrlOrPath.startsWith(".")
          ? { publicPath: "../../" }
          : {},
81 82
      },
      {
zhanghaozhe committed
83
        loader: require.resolve("css-loader"),
84 85 86 87 88 89
        options: cssOptions,
      },
      {
        // Options for PostCSS as we reference these options twice
        // Adds vendor prefixing based on your specified browser support in
        // package.json
zhanghaozhe committed
90
        loader: require.resolve("postcss-loader"),
91 92 93
        options: {
          // Necessary for external CSS imports to work
          // https://github.com/facebook/create-react-app/issues/2677
zhanghaozhe committed
94
          ident: "postcss",
95
          plugins: () => [
zhanghaozhe committed
96 97
            require("postcss-flexbugs-fixes"),
            require("postcss-preset-env")({
98
              autoprefixer: {
zhanghaozhe committed
99
                flexbox: "no-2009",
100 101 102
              },
              stage: 3,
            }),
zhanghaozhe committed
103
            require("postcss-px-to-viewport")({
zhanghaozhe committed
104 105
              viewportWidth: 375,
              unitPrecision: 6,
zhanghaozhe committed
106
              selectorBlackList: ["skip-vw"],
zhanghaozhe committed
107 108 109 110 111
            }),
            // Adds PostCSS Normalize as the reset css with default options,
            // so that it honors browserslist config in package.json
            // which in turn let's users customize the target behavior as per their needs.
            postcssNormalize(),
112 113 114 115
          ],
          sourceMap: isEnvProduction && shouldUseSourceMap,
        },
      },
zhanghaozhe committed
116
    ].filter(Boolean)
117
    if (preProcessor) {
zhanghaozhe committed
118 119
      loaders.push(
        {
zhanghaozhe committed
120
          loader: require.resolve("resolve-url-loader"),
zhanghaozhe committed
121 122 123
          options: {
            sourceMap: isEnvProduction && shouldUseSourceMap,
          },
124
        },
zhanghaozhe committed
125 126
        {
          loader: require.resolve(preProcessor),
127
          options: {
zhanghaozhe committed
128 129 130 131
            sourceMap: true,
          },
        }
      )
132
    }
zhanghaozhe committed
133 134
    return loaders
  }
135 136

  return {
zhanghaozhe committed
137
    mode: isEnvProduction ? "production" : isEnvDevelopment && "development",
138 139 140 141
    // Stop compilation early in production
    bail: isEnvProduction,
    devtool: isEnvProduction
      ? shouldUseSourceMap
zhanghaozhe committed
142
        ? "source-map"
143
        : false
zhanghaozhe committed
144
      : isEnvDevelopment && "cheap-module-source-map",
145 146 147 148 149 150 151 152 153 154 155 156 157
    // These are the "entry points" to our application.
    // This means they will be the "root" imports that are included in JS bundle.
    entry: [
      // Include an alternative client for WebpackDevServer. A client's job is to
      // connect to WebpackDevServer by a socket and get notified about changes.
      // When you save a file, the client will either apply hot updates (in case
      // of CSS changes), or refresh the page (in case of JS changes). When you
      // make a syntax error, this client will display a syntax error overlay.
      // Note: instead of the default WebpackDevServer client, we use a custom one
      // to bring better experience for Create React App users. You can replace
      // the line below with these two lines if you prefer the stock client:
      // require.resolve('webpack-dev-server/client') + '?/',
      // require.resolve('webpack/hot/dev-server'),
zhanghaozhe committed
158 159
      isEnvDevelopment &&
        require.resolve("react-dev-utils/webpackHotDevClient"),
160 161 162 163 164 165 166 167 168 169 170 171 172
      // Finally, this is your app's code:
      paths.appIndexJs,
      // We include the app code last so that if there is a runtime error during
      // initialization, it doesn't blow up the WebpackDevServer client, and
      // changing JS code would still trigger a refresh.
    ].filter(Boolean),
    output: {
      // The build folder.
      path: isEnvProduction ? paths.appBuild : undefined,
      // Add /* filename */ comments to generated require()s in the output.
      pathinfo: isEnvDevelopment,
      // There will be one main bundle, and one file per asynchronous chunk.
      // In development, it does not produce real files.
zhanghaozhe committed
173 174 175
      filename: isEnvProduction
        ? "mrstaticjs/[name].[contenthash:8].js"
        : isEnvDevelopment && "mrstaticjs/bundle.js",
zhanghaozhe committed
176 177
      // TODO: remove this when upgrading to webpack 5
      futureEmitAssets: true,
178 179
      // There are also additional JS chunk files if you use code splitting.
      chunkFilename: isEnvProduction
zhanghaozhe committed
180 181
        ? "mrstaticjs/[name].[contenthash:8].chunk.js"
        : isEnvDevelopment && "mrstaticjs/[name].chunk.js",
zhanghaozhe committed
182 183
      // webpack uses `publicPath` to determine where the app is being served from.
      // It requires a trailing slash, or the file assets will get an incorrect path.
184
      // We inferred the "public path" (such as / or /my-project) from homepage.
zhanghaozhe committed
185
      publicPath: paths.publicUrlOrPath,
186 187
      // Point sourcemap entries to original disk location (format as URL on Windows)
      devtoolModuleFilenameTemplate: isEnvProduction
zhanghaozhe committed
188 189 190 191 192 193 194
        ? (info) =>
            path
              .relative(paths.appSrc, info.absoluteResourcePath)
              .replace(/\\/g, "/")
        : isEnvDevelopment &&
          ((info) =>
            path.resolve(info.absoluteResourcePath).replace(/\\/g, "/")),
zhanghaozhe committed
195 196 197 198 199
      // Prevents conflicts when multiple webpack runtimes (from different apps)
      // are used on the same page.
      jsonpFunction: `webpackJsonp${appPackageJson.name}`,
      // this defaults to 'window', but by setting it to 'this' then
      // module chunks which are built will work in web workers as well.
zhanghaozhe committed
200
      globalObject: "this",
201 202 203 204 205 206 207 208
    },
    optimization: {
      minimize: isEnvProduction,
      minimizer: [
        // This is only used in production mode
        new TerserPlugin({
          terserOptions: {
            parse: {
zhanghaozhe committed
209 210
              // We want terser to parse ecma 8 code. However, we don't want it
              // to apply any minification steps that turns valid ecma 5 code
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
              // into invalid ecma 5 code. This is why the 'compress' and 'output'
              // sections only apply transformations that are ecma 5 safe
              // https://github.com/facebook/create-react-app/pull/4234
              ecma: 8,
            },
            compress: {
              ecma: 5,
              warnings: false,
              // Disabled because of an issue with Uglify breaking seemingly valid code:
              // https://github.com/facebook/create-react-app/issues/2376
              // Pending further investigation:
              // https://github.com/mishoo/UglifyJS2/issues/2011
              comparisons: false,
              // Disabled because of an issue with Terser breaking valid code:
              // https://github.com/facebook/create-react-app/issues/5250
zhanghaozhe committed
226
              // Pending further investigation:
227
              // https://github.com/terser-js/terser/issues/120
zhanghaozhe committed
228
              inline: 2,
229 230 231 232
            },
            mangle: {
              safari10: true,
            },
zhanghaozhe committed
233 234 235
            // Added for profiling in devtools
            keep_classnames: isEnvProductionProfile,
            keep_fnames: isEnvProductionProfile,
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
            output: {
              ecma: 5,
              comments: false,
              // Turned on because emoji and regex is not minified properly using default
              // https://github.com/facebook/create-react-app/issues/2488
              ascii_only: true,
            },
          },
          sourceMap: shouldUseSourceMap,
        }),
        // This is only used in production mode
        new OptimizeCSSAssetsPlugin({
          cssProcessorOptions: {
            parser: safePostCssParser,
            map: shouldUseSourceMap
              ? {
                  // `inline: false` forces the sourcemap to be output into a
                  // separate file
                  inline: false,
                  // `annotation: true` appends the sourceMappingURL to the end of
                  // the css file, helping the browser find the sourcemap
                  annotation: true,
                }
              : false,
          },
zhanghaozhe committed
261
          cssProcessorPluginOptions: {
zhanghaozhe committed
262
            preset: ["default", { minifyFontValues: { removeQuotes: false } }],
zhanghaozhe committed
263
          },
264 265 266 267 268 269
        }),
      ],
      // Automatically split vendor and commons
      // https://twitter.com/wSokra/status/969633336732905474
      // https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
      splitChunks: {
zhanghaozhe committed
270
        chunks: "all",
271 272 273 274
        name: false,
      },
      // Keep the runtime chunk separated to enable long term caching
      // https://twitter.com/wSokra/status/969679223278505985
zhanghaozhe committed
275 276
      // https://github.com/facebook/create-react-app/issues/5358
      runtimeChunk: {
zhanghaozhe committed
277
        name: (entrypoint) => `runtime-${entrypoint.name}`,
zhanghaozhe committed
278
      },
279 280
    },
    resolve: {
zhanghaozhe committed
281
      // This allows you to set a fallback for where webpack should look for modules.
282 283 284
      // We placed these paths second because we want `node_modules` to "win"
      // if there are any conflicts. This matches Node resolution mechanism.
      // https://github.com/facebook/create-react-app/issues/253
zhanghaozhe committed
285 286 287
      modules: ["node_modules", paths.appNodeModules].concat(
        modules.additionalModulePaths || []
      ),
288 289 290 291 292 293
      // These are the reasonable defaults supported by the Node ecosystem.
      // We also include JSX as a common component filename extension to support
      // some tools, although we do not recommend using it, see:
      // https://github.com/facebook/create-react-app/issues/290
      // `web` extension prefixes have been added for better support
      // for React Native Web.
zhanghaozhe committed
294 295 296
      extensions: paths.moduleFileExtensions
        .map((ext) => `.${ext}`)
        .filter((ext) => useTypeScript || !ext.includes("ts")),
297 298 299
      alias: {
        // Support React Native Web
        // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
zhanghaozhe committed
300
        "react-native": "react-native-web",
zhanghaozhe committed
301 302
        // Allows for better profiling with ReactDevTools
        ...(isEnvProductionProfile && {
zhanghaozhe committed
303 304
          "react-dom$": "react-dom/profiling",
          "scheduler/tracing": "scheduler/tracing-profiling",
zhanghaozhe committed
305 306
        }),
        ...(modules.webpackAliases || {}),
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
      },
      plugins: [
        // Adds support for installing with Plug'n'Play, leading to faster installs and adding
        // guards against forgotten dependencies and such.
        PnpWebpackPlugin,
        // Prevents users from importing files from outside of src/ (or node_modules/).
        // This often causes confusion because we only process files within src/ with babel.
        // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
        // please link the files into your node_modules/ and let module-resolution kick in.
        // Make sure your source files are compiled, as they will not be processed in any way.
        new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
      ],
    },
    resolveLoader: {
      plugins: [
zhanghaozhe committed
322
        // Also related to Plug'n'Play, but this time it tells webpack to load its loaders
323 324 325 326 327 328 329 330 331 332 333 334 335
        // from the current package.
        PnpWebpackPlugin.moduleLoader(module),
      ],
    },
    module: {
      strictExportPresence: true,
      rules: [
        // Disable require.ensure as it's not a standard language feature.
        { parser: { requireEnsure: false } },

        // First, run the linter.
        // It's important to do this before Babel processes the JS.
        {
zhanghaozhe committed
336
          test: /\.(js|mjs|jsx|ts|tsx)$/,
zhanghaozhe committed
337
          enforce: "pre",
338 339 340
          use: [
            {
              options: {
zhanghaozhe committed
341
                cache: true,
zhanghaozhe committed
342 343
                formatter: require.resolve("react-dev-utils/eslintFormatter"),
                eslintPath: require.resolve("eslint"),
zhanghaozhe committed
344
                resolvePluginsRelativeTo: __dirname,
345
              },
zhanghaozhe committed
346
              loader: require.resolve("eslint-loader"),
347 348 349 350 351 352 353 354 355 356 357 358 359 360
            },
          ],
          include: paths.appSrc,
        },
        {
          // "oneOf" will traverse all following loaders until one will
          // match the requirements. When no loader matches it will fall
          // back to the "file" loader at the end of the loader list.
          oneOf: [
            // "url" loader works like "file" loader except that it embeds assets
            // smaller than specified limit in bytes as data URLs to avoid requests.
            // A missing `test` is equivalent to a match.
            {
              test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
zhanghaozhe committed
361
              loader: require.resolve("url-loader"),
362
              options: {
zhanghaozhe committed
363
                limit: imageInlineSizeLimit,
zhanghaozhe committed
364
                name: "mrstaticmedia/[name].[hash:8].[ext]",
365 366 367 368 369 370 371
              },
            },
            // Process application JS with Babel.
            // The preset includes JSX, Flow, TypeScript, and some ESnext features.
            {
              test: /\.(js|mjs|jsx|ts|tsx)$/,
              include: paths.appSrc,
zhanghaozhe committed
372
              loader: require.resolve("babel-loader"),
373
              options: {
zhanghaozhe committed
374 375 376
                customize: require.resolve(
                  "babel-preset-react-app/webpack-overrides"
                ),
zhanghaozhe committed
377

378 379
                plugins: [
                  [
zhanghaozhe committed
380
                    require.resolve("babel-plugin-named-asset-import"),
381 382 383
                    {
                      loaderMap: {
                        svg: {
zhanghaozhe committed
384 385
                          ReactComponent:
                            "@svgr/webpack?-svgo,+titleProp,+ref![path]",
386 387 388 389 390 391 392 393 394
                        },
                      },
                    },
                  ],
                ],
                // This is a feature of `babel-loader` for webpack (not Babel itself).
                // It enables caching results in ./node_modules/.cache/babel-loader/
                // directory for faster rebuilds.
                cacheDirectory: true,
zhanghaozhe committed
395 396
                // See #6846 for context on why cacheCompression is disabled
                cacheCompression: false,
397 398 399 400 401 402 403 404
                compact: isEnvProduction,
              },
            },
            // Process any JS outside of the app with Babel.
            // Unlike the application JS, we only compile the standard ES features.
            {
              test: /\.(js|mjs)$/,
              exclude: /@babel(?:\/|\\{1,2})runtime/,
zhanghaozhe committed
405
              loader: require.resolve("babel-loader"),
406 407 408 409
              options: {
                babelrc: false,
                configFile: false,
                compact: false,
zhanghaozhe committed
410 411 412 413 414 415
                presets: [
                  [
                    require.resolve("babel-preset-react-app/dependencies"),
                    { helpers: true },
                  ],
                ],
416
                cacheDirectory: true,
zhanghaozhe committed
417 418 419 420 421 422 423 424
                // See #6846 for context on why cacheCompression is disabled
                cacheCompression: false,

                // Babel sourcemaps are needed for debugging into node_modules
                // code.  Without the options below, debuggers like VSCode
                // show incorrect code and set breakpoints on the wrong lines.
                sourceMaps: shouldUseSourceMap,
                inputSourceMap: shouldUseSourceMap,
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
              },
            },
            // "postcss" loader applies autoprefixer to our CSS.
            // "css" loader resolves paths in CSS and adds assets as dependencies.
            // "style" loader turns CSS into JS modules that inject <style> tags.
            // In production, we use MiniCSSExtractPlugin to extract that CSS
            // to a file, but in development "style" loader enables hot editing
            // of CSS.
            // By default we support CSS Modules with the extension .module.css
            {
              test: cssRegex,
              exclude: cssModuleRegex,
              use: getStyleLoaders({
                importLoaders: 1,
                sourceMap: isEnvProduction && shouldUseSourceMap,
              }),
              // Don't consider CSS imports dead code even if the
              // containing package claims to have no side effects.
              // Remove this when webpack adds a warning or an error for this.
              // See https://github.com/webpack/webpack/issues/6571
              sideEffects: true,
            },
            // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
            // using the extension .module.css
            {
              test: cssModuleRegex,
              use: getStyleLoaders({
                importLoaders: 1,
                sourceMap: isEnvProduction && shouldUseSourceMap,
zhanghaozhe committed
454 455 456
                modules: {
                  getLocalIdent: getCSSModuleLocalIdent,
                },
457 458 459 460 461 462 463 464 465 466
              }),
            },
            // Opt-in support for SASS (using .scss or .sass extensions).
            // By default we support SASS Modules with the
            // extensions .module.scss or .module.sass
            {
              test: sassRegex,
              exclude: sassModuleRegex,
              use: getStyleLoaders(
                {
zhanghaozhe committed
467
                  importLoaders: 3,
468 469
                  sourceMap: isEnvProduction && shouldUseSourceMap,
                },
zhanghaozhe committed
470
                "sass-loader"
471 472 473 474 475 476 477 478 479 480 481 482 483
              ),
              // Don't consider CSS imports dead code even if the
              // containing package claims to have no side effects.
              // Remove this when webpack adds a warning or an error for this.
              // See https://github.com/webpack/webpack/issues/6571
              sideEffects: true,
            },
            // Adds support for CSS Modules, but using SASS
            // using the extension .module.scss or .module.sass
            {
              test: sassModuleRegex,
              use: getStyleLoaders(
                {
zhanghaozhe committed
484
                  importLoaders: 3,
485
                  sourceMap: isEnvProduction && shouldUseSourceMap,
zhanghaozhe committed
486 487 488
                  modules: {
                    getLocalIdent: getCSSModuleLocalIdent,
                  },
489
                },
zhanghaozhe committed
490
                "sass-loader"
491 492 493 494 495 496 497 498
              ),
            },
            // "file" loader makes sure those assets get served by WebpackDevServer.
            // When you `import` an asset, you get its (virtual) filename.
            // In production, they would get copied to the `build` folder.
            // This loader doesn't use a "test" so it will catch all modules
            // that fall through the other loaders.
            {
zhanghaozhe committed
499
              loader: require.resolve("file-loader"),
500 501 502 503 504 505
              // Exclude `js` files to keep "css" loader working as it injects
              // its runtime that would otherwise be processed through "file" loader.
              // Also exclude `html` and `json` extensions so they get processed
              // by webpacks internal loaders.
              exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
              options: {
zhanghaozhe committed
506
                name: "mrstaticmedia/[name].[hash:8].[ext]",
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
              },
            },
            // ** STOP ** Are you adding a new loader?
            // Make sure to add the new loader(s) before the "file" loader.
          ],
        },
      ],
    },
    plugins: [
      // Generates an `index.html` file with the <script> injected.
      new HtmlWebpackPlugin(
        Object.assign(
          {},
          {
            inject: true,
            template: paths.appHtml,
          },
          isEnvProduction
            ? {
                minify: {
                  removeComments: true,
                  collapseWhitespace: true,
                  removeRedundantAttributes: true,
                  useShortDoctype: true,
                  removeEmptyAttributes: true,
                  removeStyleLinkTypeAttributes: true,
                  keepClosingSlash: true,
                  minifyJS: true,
                  minifyCSS: true,
                  minifyURLs: true,
                },
              }
            : undefined
        )
      ),
      // Inlines the webpack runtime script. This script is too small to warrant
      // a network request.
zhanghaozhe committed
544
      // https://github.com/facebook/create-react-app/issues/5358
zhanghaozhe committed
545 546 547
      isEnvProduction &&
        shouldInlineRuntimeChunk &&
        new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
548 549
      // Makes some environment variables available in index.html.
      // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
zhanghaozhe committed
550 551
      // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
      // It will be an empty string unless you specify "homepage"
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569
      // in `package.json`, in which case it will be the pathname of that URL.
      new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
      // This gives some necessary context to module not found errors, such as
      // the requesting resource.
      new ModuleNotFoundPlugin(paths.appPath),
      // Makes some environment variables available to the JS code, for example:
      // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
      // It is absolutely essential that NODE_ENV is set to production
      // during a production build.
      // Otherwise React will be compiled in the very slow development mode.
      new webpack.DefinePlugin(env.stringified),
      // This is necessary to emit hot updates (currently CSS only):
      isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
      // Watcher doesn't work well if you mistype casing in a path so we use
      // a plugin that prints an error when you attempt to do this.
      // See https://github.com/facebook/create-react-app/issues/240
      isEnvDevelopment && new CaseSensitivePathsPlugin(),
      // If you require a missing module and then `npm install` it, you still have
zhanghaozhe committed
570
      // to restart the development server for webpack to discover it. This plugin
571 572
      // makes the discovery automatic so you don't have to restart.
      // See https://github.com/facebook/create-react-app/issues/186
zhanghaozhe committed
573 574
      isEnvDevelopment &&
        new WatchMissingNodeModulesPlugin(paths.appNodeModules),
575 576 577 578
      isEnvProduction &&
        new MiniCssExtractPlugin({
          // Options similar to the same options in webpackOptions.output
          // both options are optional
zhanghaozhe committed
579 580
          filename: "mrstaticcss/[name].[contenthash:8].css",
          chunkFilename: "mrstaticcss/[name].[contenthash:8].chunk.css",
581
        }),
zhanghaozhe committed
582 583 584 585 586 587
      // Generate an asset manifest file with the following content:
      // - "files" key: Mapping of all asset filenames to their corresponding
      //   output file so that tools can pick it up without having to parse
      //   `index.html`
      // - "entrypoints" key: Array of files which are included in `index.html`,
      //   can be used to reconstruct the HTML if necessary
588
      new ManifestPlugin({
zhanghaozhe committed
589
        fileName: "asset-manifest.json",
zhanghaozhe committed
590 591 592 593 594 595
        publicPath: paths.publicUrlOrPath,
        generate: (seed, files, entrypoints) => {
          const manifestFiles = files.reduce((manifest, file) => {
            manifest[file.name] = file.path
            return manifest
          }, seed)
zhanghaozhe committed
596 597 598
          const entrypointFiles = entrypoints.main.filter(
            (fileName) => !fileName.endsWith(".map")
          )
zhanghaozhe committed
599 600 601 602 603 604

          return {
            files: manifestFiles,
            entrypoints: entrypointFiles,
          }
        },
605 606
      }),
      // Moment.js is an extremely popular library that bundles large locale files
zhanghaozhe committed
607
      // by default due to how webpack interprets its code. This is a practical
608 609 610 611 612
      // solution that requires the user to opt into importing specific locales.
      // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
      // You can remove this if you don't use Moment.js:
      new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
      // Generate a service worker script that will precache, and keep up to date,
zhanghaozhe committed
613
      // the HTML & assets that are part of the webpack build.
614 615 616 617
      isEnvProduction &&
        new WorkboxWebpackPlugin.GenerateSW({
          clientsClaim: true,
          exclude: [/\.map$/, /asset-manifest\.json$/],
zhanghaozhe committed
618 619
          importWorkboxFrom: "cdn",
          navigateFallback: paths.publicUrlOrPath + "index.html",
620 621
          navigateFallbackBlacklist: [
            // Exclude URLs starting with /_, as they're likely an API call
zhanghaozhe committed
622
            new RegExp("^/_"),
zhanghaozhe committed
623 624 625 626
            // Exclude any URLs whose last part seems to be a file extension
            // as they're likely a resource and not a SPA route.
            // URLs containing a "?" character won't be blacklisted as they're likely
            // a route with query params (e.g. auth callbacks).
zhanghaozhe committed
627
            new RegExp("/[^/?]+\\.[^/]+$"),
628 629 630 631 632
          ],
        }),
      // TypeScript type checking
      useTypeScript &&
        new ForkTsCheckerWebpackPlugin({
zhanghaozhe committed
633
          typescript: resolve.sync("typescript", {
634 635 636 637 638
            basedir: paths.appNodeModules,
          }),
          async: isEnvDevelopment,
          useTypescriptIncrementalApi: true,
          checkSyntacticErrors: true,
zhanghaozhe committed
639 640 641 642 643 644
          resolveModuleNameModule: process.versions.pnp
            ? `${__dirname}/pnpTs.js`
            : undefined,
          resolveTypeReferenceDirectiveModule: process.versions.pnp
            ? `${__dirname}/pnpTs.js`
            : undefined,
645 646
          tsconfig: paths.appTsConfig,
          reportFiles: [
zhanghaozhe committed
647 648 649 650 651
            "**",
            "!**/__tests__/**",
            "!**/?(*.)(spec|test).*",
            "!**/src/setupProxy.*",
            "!**/src/setupTests.*",
652 653 654 655 656 657 658
          ],
          silent: true,
          // The formatter is invoked directly in WebpackDevServerUtils during development
          formatter: isEnvProduction ? typescriptFormatter : undefined,
        }),
    ].filter(Boolean),
    // Some libraries import Node modules but don't use them in the browser.
zhanghaozhe committed
659
    // Tell webpack to provide empty mocks for them so importing them works.
660
    node: {
zhanghaozhe committed
661 662 663 664 665 666 667 668
      module: "empty",
      dgram: "empty",
      dns: "mock",
      fs: "empty",
      http2: "empty",
      net: "empty",
      tls: "empty",
      child_process: "empty",
669 670 671 672
    },
    // Turn off performance processing because we utilize
    // our own hints via the FileSizeReporter
    performance: false,
zhanghaozhe committed
673 674
  }
}