暫無描述

webpack.config.prod.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. const autoprefixer = require('autoprefixer');
  2. const path = require('path');
  3. const webpack = require('webpack');
  4. const HtmlWebpackPlugin = require('html-webpack-plugin');
  5. const ExtractTextPlugin = require('extract-text-webpack-plugin');
  6. const ManifestPlugin = require('webpack-manifest-plugin');
  7. const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
  8. const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
  9. const eslintFormatter = require('react-dev-utils/eslintFormatter');
  10. const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
  11. const paths = require('./paths');
  12. const getClientEnvironment = require('./env');
  13. // Webpack uses `publicPath` to determine where the app is being served from.
  14. // It requires a trailing slash, or the file assets will get an incorrect path.
  15. const publicPath = paths.servedPath;
  16. // Some apps do not use client-side routing with pushState.
  17. // For these, "homepage" can be set to "." to enable relative asset paths.
  18. const shouldUseRelativeAssetPaths = publicPath === './';
  19. // Source maps are resource heavy and can cause out of memory issue for large source files.
  20. const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
  21. // `publicUrl` is just like `publicPath`, but we will provide it to our app
  22. // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
  23. // Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
  24. const publicUrl = publicPath.slice(0, -1);
  25. // Get environment variables to inject into our app.
  26. const env = getClientEnvironment(publicUrl);
  27. // Assert this just to be safe.
  28. // Development builds of React are slow and not intended for production.
  29. if (env.stringified['process.env'].NODE_ENV !== '"production"') {
  30. throw new Error('Production builds must have NODE_ENV=production.');
  31. }
  32. // Note: defined here because it will be used more than once.
  33. const cssFilename = 'static/css/[name].[contenthash:8].css';
  34. // ExtractTextPlugin expects the build output to be flat.
  35. // (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
  36. // However, our output is structured with css, js and media folders.
  37. // To have this structure working with relative paths, we have to use custom options.
  38. const extractTextPluginOptions = shouldUseRelativeAssetPaths
  39. ? // Making sure that the publicPath goes back to to build folder.
  40. { publicPath: Array(cssFilename.split('/').length).join('../') }
  41. : {};
  42. // This is the production configuration.
  43. // It compiles slowly and is focused on producing a fast and minimal bundle.
  44. // The development configuration is different and lives in a separate file.
  45. module.exports = {
  46. // Don't attempt to continue if there are any errors.
  47. bail: true,
  48. // We generate sourcemaps in production. This is slow but gives good results.
  49. // You can exclude the *.map files from the build during deployment.
  50. devtool: shouldUseSourceMap ? 'source-map' : false,
  51. // In production, we only want to load the polyfills and the app code.
  52. entry: [require.resolve('./polyfills'), paths.appIndexJs],
  53. output: {
  54. // The build folder.
  55. path: paths.appBuild,
  56. // Generated JS file names (with nested folders).
  57. // There will be one main bundle, and one file per asynchronous chunk.
  58. // We don't currently advertise code splitting but Webpack supports it.
  59. filename: 'static/js/[name].[chunkhash:8].js',
  60. chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
  61. // We inferred the "public path" (such as / or /my-project) from homepage.
  62. publicPath: publicPath,
  63. // Point sourcemap entries to original disk location (format as URL on Windows)
  64. devtoolModuleFilenameTemplate: info =>
  65. path
  66. .relative(paths.appSrc, info.absoluteResourcePath)
  67. .replace(/\\/g, '/'),
  68. },
  69. resolve: {
  70. // This allows you to set a fallback for where Webpack should look for modules.
  71. // We placed these paths second because we want `node_modules` to "win"
  72. // if there are any conflicts. This matches Node resolution mechanism.
  73. // https://github.com/facebookincubator/create-react-app/issues/253
  74. modules: ['node_modules', paths.appNodeModules].concat(
  75. // It is guaranteed to exist because we tweak it in `env.js`
  76. process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
  77. ),
  78. // These are the reasonable defaults supported by the Node ecosystem.
  79. // We also include JSX as a common component filename extension to support
  80. // some tools, although we do not recommend using it, see:
  81. // https://github.com/facebookincubator/create-react-app/issues/290
  82. // `web` extension prefixes have been added for better support
  83. // for React Native Web.
  84. extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'],
  85. alias: {
  86. // Support React Native Web
  87. // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
  88. 'react-native': 'react-native-web',
  89. },
  90. plugins: [
  91. // Prevents users from importing files from outside of src/ (or node_modules/).
  92. // This often causes confusion because we only process files within src/ with babel.
  93. // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
  94. // please link the files into your node_modules/ and let module-resolution kick in.
  95. // Make sure your source files are compiled, as they will not be processed in any way.
  96. new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
  97. ],
  98. },
  99. module: {
  100. strictExportPresence: true,
  101. rules: [
  102. // TODO: Disable require.ensure as it's not a standard language feature.
  103. // We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
  104. // { parser: { requireEnsure: false } },
  105. // First, run the linter.
  106. // It's important to do this before Babel processes the JS.
  107. {
  108. test: /\.(js|jsx|mjs)$/,
  109. enforce: 'pre',
  110. use: [
  111. {
  112. options: {
  113. formatter: eslintFormatter,
  114. eslintPath: require.resolve('eslint'),
  115. },
  116. loader: require.resolve('eslint-loader'),
  117. },
  118. ],
  119. include: paths.appSrc,
  120. },
  121. {
  122. // "oneOf" will traverse all following loaders until one will
  123. // match the requirements. When no loader matches it will fall
  124. // back to the "file" loader at the end of the loader list.
  125. oneOf: [
  126. // "url" loader works just like "file" loader but it also embeds
  127. // assets smaller than specified size as data URLs to avoid requests.
  128. {
  129. test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
  130. loader: require.resolve('url-loader'),
  131. options: {
  132. limit: 10000,
  133. name: 'static/media/[name].[hash:8].[ext]',
  134. },
  135. },
  136. // Process JS with Babel.
  137. {
  138. test: /\.(js|jsx|mjs)$/,
  139. include: paths.appSrc,
  140. loader: require.resolve('babel-loader'),
  141. options: {
  142. compact: true,
  143. },
  144. },
  145. // The notation here is somewhat confusing.
  146. // "postcss" loader applies autoprefixer to our CSS.
  147. // "css" loader resolves paths in CSS and adds assets as dependencies.
  148. // "style" loader normally turns CSS into JS modules injecting <style>,
  149. // but unlike in development configuration, we do something different.
  150. // `ExtractTextPlugin` first applies the "postcss" and "css" loaders
  151. // (second argument), then grabs the result CSS and puts it into a
  152. // separate file in our build process. This way we actually ship
  153. // a single CSS file in production instead of JS code injecting <style>
  154. // tags. If you use code splitting, however, any async bundles will still
  155. // use the "style" loader inside the async code so CSS from them won't be
  156. // in the main CSS file.
  157. {
  158. test: /\.css$/,
  159. loader: ExtractTextPlugin.extract(
  160. Object.assign(
  161. {
  162. fallback: {
  163. loader: require.resolve('style-loader'),
  164. options: {
  165. hmr: false,
  166. },
  167. },
  168. use: [
  169. {
  170. loader: require.resolve('css-loader'),
  171. options: {
  172. importLoaders: 1,
  173. minimize: true,
  174. sourceMap: shouldUseSourceMap,
  175. },
  176. },
  177. {
  178. loader: require.resolve('postcss-loader'),
  179. options: {
  180. // Necessary for external CSS imports to work
  181. // https://github.com/facebookincubator/create-react-app/issues/2677
  182. ident: 'postcss',
  183. plugins: () => [
  184. require('postcss-flexbugs-fixes'),
  185. autoprefixer({
  186. browsers: [
  187. '>1%',
  188. 'last 4 versions',
  189. 'Firefox ESR',
  190. 'not ie < 9', // React doesn't support IE8 anyway
  191. ],
  192. flexbox: 'no-2009',
  193. }),
  194. ],
  195. },
  196. },
  197. ],
  198. },
  199. extractTextPluginOptions
  200. )
  201. ),
  202. // Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
  203. },
  204. // "file" loader makes sure assets end up in the `build` folder.
  205. // When you `import` an asset, you get its filename.
  206. // This loader doesn't use a "test" so it will catch all modules
  207. // that fall through the other loaders.
  208. {
  209. loader: require.resolve('file-loader'),
  210. // Exclude `js` files to keep "css" loader working as it injects
  211. // it's runtime that would otherwise processed through "file" loader.
  212. // Also exclude `html` and `json` extensions so they get processed
  213. // by webpacks internal loaders.
  214. exclude: [/\.(js|jsx|mjs)$/, /\.html$/, /\.json$/],
  215. options: {
  216. name: 'static/media/[name].[hash:8].[ext]',
  217. },
  218. },
  219. // ** STOP ** Are you adding a new loader?
  220. // Make sure to add the new loader(s) before the "file" loader.
  221. ],
  222. },
  223. ],
  224. },
  225. plugins: [
  226. // Makes some environment variables available in index.html.
  227. // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
  228. // <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
  229. // In production, it will be an empty string unless you specify "homepage"
  230. // in `package.json`, in which case it will be the pathname of that URL.
  231. new InterpolateHtmlPlugin(env.raw),
  232. // Generates an `index.html` file with the <script> injected.
  233. new HtmlWebpackPlugin({
  234. inject: true,
  235. template: paths.appHtml,
  236. minify: {
  237. removeComments: true,
  238. collapseWhitespace: true,
  239. removeRedundantAttributes: true,
  240. useShortDoctype: true,
  241. removeEmptyAttributes: true,
  242. removeStyleLinkTypeAttributes: true,
  243. keepClosingSlash: true,
  244. minifyJS: true,
  245. minifyCSS: true,
  246. minifyURLs: true,
  247. },
  248. }),
  249. // Makes some environment variables available to the JS code, for example:
  250. // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
  251. // It is absolutely essential that NODE_ENV was set to production here.
  252. // Otherwise React will be compiled in the very slow development mode.
  253. new webpack.DefinePlugin(env.stringified),
  254. // Minify the code.
  255. new webpack.optimize.UglifyJsPlugin({
  256. compress: {
  257. warnings: false,
  258. // Disabled because of an issue with Uglify breaking seemingly valid code:
  259. // https://github.com/facebookincubator/create-react-app/issues/2376
  260. // Pending further investigation:
  261. // https://github.com/mishoo/UglifyJS2/issues/2011
  262. comparisons: false,
  263. },
  264. mangle: {
  265. safari10: true,
  266. },
  267. output: {
  268. comments: false,
  269. // Turned on because emoji and regex is not minified properly using default
  270. // https://github.com/facebookincubator/create-react-app/issues/2488
  271. ascii_only: true,
  272. },
  273. sourceMap: shouldUseSourceMap,
  274. }),
  275. // Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
  276. new ExtractTextPlugin({
  277. filename: cssFilename,
  278. }),
  279. // Generate a manifest file which contains a mapping of all asset filenames
  280. // to their corresponding output file so that tools can pick it up without
  281. // having to parse `index.html`.
  282. new ManifestPlugin({
  283. fileName: 'asset-manifest.json',
  284. }),
  285. // Generate a service worker script that will precache, and keep up to date,
  286. // the HTML & assets that are part of the Webpack build.
  287. new SWPrecacheWebpackPlugin({
  288. // By default, a cache-busting query parameter is appended to requests
  289. // used to populate the caches, to ensure the responses are fresh.
  290. // If a URL is already hashed by Webpack, then there is no concern
  291. // about it being stale, and the cache-busting can be skipped.
  292. dontCacheBustUrlsMatching: /\.\w{8}\./,
  293. filename: 'service-worker.js',
  294. logger(message) {
  295. if (message.indexOf('Total precache size is') === 0) {
  296. // This message occurs for every build and is a bit too noisy.
  297. return;
  298. }
  299. if (message.indexOf('Skipping static resource') === 0) {
  300. // This message obscures real errors so we ignore it.
  301. // https://github.com/facebookincubator/create-react-app/issues/2612
  302. return;
  303. }
  304. console.log(message);
  305. },
  306. minify: true,
  307. // For unknown URLs, fallback to the index page
  308. navigateFallback: publicUrl + '/index.html',
  309. // Ignores URLs starting from /__ (useful for Firebase):
  310. // https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
  311. navigateFallbackWhitelist: [/^(?!\/__).*/],
  312. // Don't precache sourcemaps (they're large) and build asset manifest:
  313. staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
  314. }),
  315. // Moment.js is an extremely popular library that bundles large locale files
  316. // by default due to how Webpack interprets its code. This is a practical
  317. // solution that requires the user to opt into importing specific locales.
  318. // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
  319. // You can remove this if you don't use Moment.js:
  320. new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
  321. ],
  322. // Some libraries import Node modules but don't use them in the browser.
  323. // Tell Webpack to provide empty mocks for them so importing them works.
  324. node: {
  325. dgram: 'empty',
  326. fs: 'empty',
  327. net: 'empty',
  328. tls: 'empty',
  329. child_process: 'empty',
  330. },
  331. };