webpack.config.prod.js 22 KB

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