|
const autoprefixer = require('autoprefixer'); |
|
const path = require('path'); |
|
const webpack = require('webpack'); |
|
const HtmlWebpackPlugin = require('html-webpack-plugin'); |
|
const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); |
|
const ExtractTextPlugin = require('extract-text-webpack-plugin'); |
|
const ManifestPlugin = require('webpack-manifest-plugin'); |
|
const InterpolateHtmlPlugin = require('interpolate-html-plugin'); |
|
const eslintFormatter = require('react-dev-utils/eslintFormatter'); |
|
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); |
|
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin'); |
|
|
|
const paths = require('./paths'); |
|
const getClientEnvironment = require('./env'); |
|
|
|
// 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. |
|
const publicPath = paths.servedPath; |
|
// Some apps do not use client-side routing with pushState. |
|
// For these, "homepage" can be set to "." to enable relative asset paths. |
|
const shouldUseRelativeAssetPaths = publicPath === './'; |
|
// Source maps are resource heavy and can cause out of memory issue for large source files. |
|
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; |
|
// `publicUrl` is just like `publicPath`, but we will provide it to our app |
|
// 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. |
|
const publicUrl = publicPath.slice(0, -1); |
|
// Get environment variables to inject into our app. |
|
const env = getClientEnvironment(publicUrl); |
|
|
|
// Assert this just to be safe. |
|
// Development builds of React are slow and not intended for production. |
|
if (env.stringified['process.env'].NODE_ENV !== '"production"') { |
|
throw new Error('Production builds must have NODE_ENV=production.'); |
|
} |
|
|
|
// Note: defined here because it will be used more than once. |
|
const cssFilename = 'static/css/[name].[md5:contenthash:hex:20].css'; |
|
|
|
// ExtractTextPlugin expects the build output to be flat. |
|
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27) |
|
// However, our output is structured with css, js and media folders. |
|
// To have this structure working with relative paths, we have to use custom options. |
|
const extractTextPluginOptions = shouldUseRelativeAssetPaths |
|
? // Making sure that the publicPath goes back to to build folder. |
|
{ publicPath: Array(cssFilename.split('/').length).join('../') } |
|
: {}; |
|
|
|
// Options for PostCSS as we reference these options twice |
|
// Adds vendor prefixing based on your specified browser support in |
|
// package.json |
|
const postCSSLoaderOptions = { |
|
// Necessary for external CSS imports to work |
|
// https://github.com/facebook/create-react-app/issues/2677 |
|
ident: 'postcss', |
|
plugins: () => [ |
|
require('postcss-flexbugs-fixes'), |
|
autoprefixer({ |
|
flexbox: 'no-2009', |
|
}), |
|
], |
|
}; |
|
|
|
// This is the production configuration. |
|
// It compiles slowly and is focused on producing a fast and minimal bundle. |
|
// The development configuration is different and lives in a separate file. |
|
module.exports = { |
|
mode: 'production', |
|
// Don't attempt to continue if there are any errors. |
|
bail: true, |
|
// We generate sourcemaps in production. This is slow but gives good results. |
|
// You can exclude the *.map files from the build during deployment. |
|
devtool: shouldUseSourceMap ? 'source-map' : false, |
|
// In production, we only want to load the polyfills and the app code. |
|
entry: [require.resolve('./polyfills'), paths.appIndexJs], |
|
output: { |
|
// The build folder. |
|
path: paths.appBuild, |
|
// Generated JS file names (with nested folders). |
|
// There will be one main bundle, and one file per asynchronous chunk. |
|
// We don't currently advertise code splitting but Webpack supports it. |
|
filename: 'static/js/[name].[chunkhash:8].js', |
|
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js', |
|
// We inferred the "public path" (such as / or /my-project) from homepage. |
|
publicPath, |
|
// Point sourcemap entries to original disk location (format as URL on Windows) |
|
devtoolModuleFilenameTemplate: info => |
|
path |
|
.relative(paths.appSrc, info.absoluteResourcePath) |
|
.replace(/\\/g, '/'), |
|
}, |
|
resolve: { |
|
// This allows you to set a fallback for where Webpack should look for modules. |
|
// 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 |
|
modules: ['node_modules'].concat( |
|
// It is guaranteed to exist because we tweak it in `env.js` |
|
process.env.NODE_PATH.split(path.delimiter).filter(Boolean) |
|
), |
|
// 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. |
|
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'], |
|
plugins: [ |
|
// 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]), |
|
], |
|
}, |
|
module: { |
|
strictExportPresence: true, |
|
rules: [ |
|
// Disable require.ensure as it's not a standard language feature. |
|
{ parser: { requireEnsure: false } }, |
|
{ |
|
test: /\.(js|jsx|mjs)$/, |
|
enforce: 'pre', |
|
use: [ |
|
{ |
|
options: { |
|
formatter: eslintFormatter, |
|
eslintPath: require.resolve('eslint'), |
|
// TODO: consider separate config for production, |
|
// e.g. to enable no-console and no-debugger only in production. |
|
}, |
|
loader: require.resolve('eslint-loader'), |
|
}, |
|
], |
|
include: paths.srcPaths, |
|
exclude: [/[/\\\\]node_modules[/\\\\]/], |
|
}, |
|
{ |
|
// "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 just like "file" loader but it also embeds |
|
// assets smaller than specified size as data URLs to avoid requests. |
|
{ |
|
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/], |
|
loader: require.resolve('url-loader'), |
|
options: { |
|
limit: 10000, |
|
name: 'static/media/[name].[hash:8].[ext]', |
|
}, |
|
}, |
|
// Process application JS with Babel. |
|
// The preset includes JSX, Flow, and some ESnext features. |
|
{ |
|
test: /\.(js|jsx|mjs)$/, |
|
include: paths.srcPaths, |
|
exclude: [/[/\\\\]node_modules[/\\\\]/], |
|
use: [ |
|
// This loader parallelizes code compilation, it is optional but |
|
// improves compile time on larger projects |
|
require.resolve('thread-loader'), |
|
{ |
|
loader: require.resolve('babel-loader'), |
|
options: { |
|
presets: [require.resolve('babel-preset-react-app')], |
|
plugins: [ |
|
[ |
|
require.resolve('babel-plugin-named-asset-import'), |
|
{ |
|
loaderMap: { |
|
svg: { |
|
ReactComponent: 'svgr/webpack![path]', |
|
}, |
|
}, |
|
}, |
|
], |
|
], |
|
compact: true, |
|
highlightCode: true, |
|
}, |
|
}, |
|
], |
|
}, |
|
// Process any JS outside of the app with Babel. |
|
// Unlike the application JS, we only compile the standard ES features. |
|
{ |
|
test: /\.js$/, |
|
use: [ |
|
// This loader parallelizes code compilation, it is optional but |
|
// improves compile time on larger projects |
|
require.resolve('thread-loader'), |
|
{ |
|
loader: require.resolve('babel-loader'), |
|
options: { |
|
babelrc: false, |
|
compact: false, |
|
presets: [ |
|
require.resolve('babel-preset-react-app/dependencies'), |
|
], |
|
cacheDirectory: true, |
|
highlightCode: true, |
|
}, |
|
}, |
|
], |
|
}, |
|
// The notation here is somewhat confusing. |
|
// "postcss" loader applies autoprefixer to our CSS. |
|
// "css" loader resolves paths in CSS and adds assets as dependencies. |
|
// "style" loader normally turns CSS into JS modules injecting <style>, |
|
// but unlike in development configuration, we do something different. |
|
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders |
|
// (second argument), then grabs the result CSS and puts it into a |
|
// separate file in our build process. This way we actually ship |
|
// a single CSS file in production instead of JS code injecting <style> |
|
// tags. If you use code splitting, however, any async bundles will still |
|
// use the "style" loader inside the async code so CSS from them won't be |
|
// in the main CSS file. |
|
// By default we support CSS Modules with the extension .module.css |
|
{ |
|
test: /\.css$/, |
|
exclude: /\.module\.css$/, |
|
loader: ExtractTextPlugin.extract( |
|
Object.assign( |
|
{ |
|
fallback: { |
|
loader: require.resolve('style-loader'), |
|
options: { |
|
hmr: false, |
|
}, |
|
}, |
|
use: [ |
|
{ |
|
loader: require.resolve('css-loader'), |
|
options: { |
|
importLoaders: 1, |
|
minimize: true, |
|
sourceMap: shouldUseSourceMap, |
|
}, |
|
}, |
|
{ |
|
loader: require.resolve('postcss-loader'), |
|
options: postCSSLoaderOptions, |
|
}, |
|
], |
|
}, |
|
extractTextPluginOptions |
|
) |
|
), |
|
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`. |
|
}, |
|
// Adds support for CSS Modules (https://github.com/css-modules/css-modules) |
|
// using the extension .module.css |
|
{ |
|
test: /\.module\.css$/, |
|
loader: ExtractTextPlugin.extract( |
|
Object.assign( |
|
{ |
|
fallback: { |
|
loader: require.resolve('style-loader'), |
|
options: { |
|
hmr: false, |
|
}, |
|
}, |
|
use: [ |
|
{ |
|
loader: require.resolve('css-loader'), |
|
options: { |
|
importLoaders: 1, |
|
minimize: true, |
|
sourceMap: shouldUseSourceMap, |
|
modules: true, |
|
localIdentName: '[path]__[name]___[local]', |
|
}, |
|
}, |
|
{ |
|
loader: require.resolve('postcss-loader'), |
|
options: postCSSLoaderOptions, |
|
}, |
|
], |
|
}, |
|
extractTextPluginOptions |
|
) |
|
), |
|
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`. |
|
}, |
|
// The GraphQL loader preprocesses GraphQL queries in .graphql files. |
|
{ |
|
test: /\.(graphql)$/, |
|
loader: 'graphql-tag/loader', |
|
}, |
|
// "file" loader makes sure assets end up in the `build` folder. |
|
// When you `import` an asset, you get its filename. |
|
// This loader doesn't use a "test" so it will catch all modules |
|
// that fall through the other loaders. |
|
{ |
|
loader: require.resolve('file-loader'), |
|
// Exclude `js` files to keep "css" loader working as it injects |
|
// it's 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|jsx|mjs)$/, /\.html$/, /\.json$/], |
|
options: { |
|
name: 'static/media/[name].[hash:8].[ext]', |
|
}, |
|
}, |
|
// ** STOP ** Are you adding a new loader? |
|
// Make sure to add the new loader(s) before the "file" loader. |
|
], |
|
}, |
|
], |
|
}, |
|
optimization: { |
|
splitChunks: { |
|
cacheGroups: { |
|
commons: { |
|
test: /[\\/]node_modules[\\/]/, |
|
name: 'vendor', |
|
chunks: 'initial', |
|
}, |
|
}, |
|
}, |
|
minimizer: [ |
|
// Minify the code. |
|
new UglifyJsPlugin({ |
|
uglifyOptions: { |
|
compress: { |
|
// 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, |
|
}, |
|
output: { |
|
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, |
|
}, |
|
}, |
|
// Use multi-process parallel running to improve the build speed |
|
// Default number of concurrent runs: os.cpus().length - 1 |
|
parallel: true, |
|
// Enable file caching |
|
cache: true, |
|
sourceMap: shouldUseSourceMap, |
|
}), |
|
], |
|
}, |
|
plugins: [ |
|
// TODO: vire - LoaderOptionsPlugin needs to be removed |
|
// see https://github.com/webpack/webpack/issues/6064 |
|
new webpack.LoaderOptionsPlugin({ |
|
options: { |
|
context: process.cwd(), // or the same value as `context` |
|
}, |
|
}), |
|
// Generates an `index.html` file with the <script> injected. |
|
new HtmlWebpackPlugin({ |
|
inject: true, |
|
template: paths.appHtml, |
|
minify: { |
|
removeComments: true, |
|
collapseWhitespace: true, |
|
removeRedundantAttributes: true, |
|
useShortDoctype: true, |
|
removeEmptyAttributes: true, |
|
removeStyleLinkTypeAttributes: true, |
|
keepClosingSlash: true, |
|
minifyJS: true, |
|
minifyCSS: true, |
|
minifyURLs: true, |
|
}, |
|
}), |
|
// Makes some environment variables available in index.html. |
|
// The public URL is available as %PUBLIC_URL% in index.html, e.g.: |
|
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"> |
|
// In production, it will be an empty string unless you specify "homepage" |
|
// in `package.json`, in which case it will be the pathname of that URL. |
|
new InterpolateHtmlPlugin(env.raw), |
|
// 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 was set to production here. |
|
// Otherwise React will be compiled in the very slow development mode. |
|
new webpack.DefinePlugin(env.stringified), |
|
|
|
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`. |
|
new ExtractTextPlugin({ |
|
filename: cssFilename, |
|
}), |
|
// Generate a manifest file which contains a mapping of all asset filenames |
|
// to their corresponding output file so that tools can pick it up without |
|
// having to parse `index.html`. |
|
new ManifestPlugin({ |
|
fileName: 'asset-manifest.json', |
|
publicPath, |
|
}), |
|
// Moment.js is an extremely popular library that bundles large locale files |
|
// by default due to how Webpack interprets its code. This is a practical |
|
// 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$/), |
|
new SpeedMeasurePlugin(), |
|
], |
|
// Some libraries import Node modules but don't use them in the browser. |
|
// Tell Webpack to provide empty mocks for them so importing them works. |
|
node: { |
|
dgram: 'empty', |
|
fs: 'empty', |
|
net: 'empty', |
|
tls: 'empty', |
|
child_process: 'empty', |
|
}, |
|
}; |