From 793a4ffda2d3864f9c95300cdd9bc417a24c30e4 Mon Sep 17 00:00:00 2001 From: Pavel Prichodko Date: Fri, 3 Aug 2018 13:50:24 +0200 Subject: [PATCH 1/3] Switch from uglify-es to terser --- packages/react-dev-utils/printBuildError.js | 4 ++-- packages/react-error-overlay/package.json | 2 +- packages/react-error-overlay/webpack.config.iframe.js | 6 +++--- packages/react-scripts/config/webpack.config.prod.js | 8 ++++---- packages/react-scripts/package.json | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/react-dev-utils/printBuildError.js b/packages/react-dev-utils/printBuildError.js index 0fe7aa9a552..1e70b4e64f4 100644 --- a/packages/react-dev-utils/printBuildError.js +++ b/packages/react-dev-utils/printBuildError.js @@ -13,11 +13,11 @@ module.exports = function printBuildError(err) { const message = err != null && err.message; const stack = err != null && err.stack; - // Add more helpful message for UglifyJs error + // Add more helpful message for Terser error if ( stack && typeof message === 'string' && - message.indexOf('from UglifyJs') !== -1 + message.indexOf('from Terser') !== -1 ) { try { const matched = /(.+)\[(.+):(.+),(.+)\]\[.+\]/.exec(stack); diff --git a/packages/react-error-overlay/package.json b/packages/react-error-overlay/package.json index 6e7e1f3e885..2b777b028f0 100644 --- a/packages/react-error-overlay/package.json +++ b/packages/react-error-overlay/package.json @@ -60,7 +60,7 @@ "rimraf": "^2.6.2", "settle-promise": "1.0.0", "source-map": "0.5.6", - "uglifyjs-webpack-plugin": "1.2.5", + "terser-webpack-plugin": "^1.0.0", "webpack": "^4.8.1" }, "jest": { diff --git a/packages/react-error-overlay/webpack.config.iframe.js b/packages/react-error-overlay/webpack.config.iframe.js index 3a806f362db..67157bad440 100644 --- a/packages/react-error-overlay/webpack.config.iframe.js +++ b/packages/react-error-overlay/webpack.config.iframe.js @@ -8,7 +8,7 @@ const path = require('path'); const webpack = require('webpack'); -const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); +const TerserPlugin = require('terser-webpack-plugin'); module.exports = { mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', @@ -49,8 +49,8 @@ module.exports = { minimizer: [ // This code is embedded as a string, so it would never be optimized // elsewhere. - new UglifyJsPlugin({ - uglifyOptions: { + new TerserPlugin({ + terserOptions: { compress: { warnings: false, comparisons: false, diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 86869748c66..6f9aeba80e1 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -12,7 +12,7 @@ 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 TerserPlugin = require('terser-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const ManifestPlugin = require('webpack-manifest-plugin'); @@ -116,10 +116,10 @@ module.exports = { }, optimization: { minimizer: [ - new UglifyJsPlugin({ - uglifyOptions: { + new TerserPlugin({ + terserOptions: { parse: { - // we want uglify-js to parse ecma 8 code. However, we don't want it + // we want terser to parse ecma 8 code. However, we don't want it // to apply any minfication steps that turns valid ecma 5 code // into invalid ecma 5 code. This is why the 'compress' and 'output' // sections only apply transformations that are ecma 5 safe diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index cc590619417..927bbb4d369 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -65,7 +65,7 @@ "svgr": "1.9.2", "sw-precache-webpack-plugin": "0.11.5", "thread-loader": "1.1.5", - "uglifyjs-webpack-plugin": "1.2.5", + "terser-webpack-plugin": "^1.0.0", "url-loader": "1.0.1", "webpack": "4.8.3", "webpack-dev-server": "3.1.7", From 58b78b05b635c30a658509cf90235ceb6d4ce84e Mon Sep 17 00:00:00 2001 From: Pavel Prichodko Date: Fri, 17 Aug 2018 13:46:52 +0200 Subject: [PATCH 2/3] Update babel-preset to support esmodules target --- packages/babel-preset-react-app/create.js | 58 +++++++++++++++-------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/packages/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js index 2ead09570f6..2d3e5592936 100644 --- a/packages/babel-preset-react-app/create.js +++ b/packages/babel-preset-react-app/create.js @@ -27,6 +27,7 @@ module.exports = function(api, opts, env) { var isEnvProduction = env === 'production'; var isEnvTest = env === 'test'; var isFlowEnabled = validateBoolOption('flow', opts.flow, true); + var isModern = validateBoolOption('modern', opts.modern, false); if (!isEnvDevelopment && !isEnvProduction && !isEnvTest) { throw new Error( @@ -38,7 +39,30 @@ module.exports = function(api, opts, env) { ); } - return { + function getEnvOptions({ isModern }) { + const defaultOpts = { + // `entry` transforms `@babel/polyfill` into individual requires for + // the targeted browsers. This is safer than `usage` which performs + // static code analysis to determine what's required. + // This is probably a fine default to help trim down bundles when + // end-users inevitably import '@babel/polyfill'. + useBuiltIns: 'entry', + // Do not transform modules to CJS + modules: false, + }; + + if (isModern) { + return Object.assign(defaultOpts, { + targets: { + esmodules: true, + }, + }); + } + + return defaultOpts; + } + + const babelPreset = { presets: [ isEnvTest && [ // ES features necessary for user's Node version @@ -52,16 +76,7 @@ module.exports = function(api, opts, env) { (isEnvProduction || isEnvDevelopment) && [ // Latest stable ECMAScript features require('@babel/preset-env').default, - { - // `entry` transforms `@babel/polyfill` into individual requires for - // the targeted browsers. This is safer than `usage` which performs - // static code analysis to determine what's required. - // This is probably a fine default to help trim down bundles when - // end-users inevitably import '@babel/polyfill'. - useBuiltIns: 'entry', - // Do not transform modules to CJS - modules: false, - }, + getEnvOptions({ isModern }), ], [ require('@babel/preset-react').default, @@ -103,7 +118,7 @@ module.exports = function(api, opts, env) { }, ], // Polyfills the runtime needed for async/await and generators - [ + !isModern && [ require('@babel/plugin-transform-runtime').default, { helpers: false, @@ -119,13 +134,14 @@ module.exports = function(api, opts, env) { }, ], // function* () { yield 42; yield 43; } - !isEnvTest && [ - require('@babel/plugin-transform-regenerator').default, - { - // Async functions are converted to generators by @babel/preset-env - async: false, - }, - ], + !isEnvTest && + !isModern && [ + require('@babel/plugin-transform-regenerator').default, + { + // Async functions are converted to generators by @babel/preset-env + async: false, + }, + ], // Adds syntax support for import() require('@babel/plugin-syntax-dynamic-import').default, isEnvTest && @@ -133,4 +149,8 @@ module.exports = function(api, opts, env) { require('babel-plugin-transform-dynamic-import').default, ].filter(Boolean), }; + + // console.log('PRESET', JSON.stringify(babelPreset) + + return babelPreset; }; From 8295dbd61aa57896984b36fdf5781f660549116c Mon Sep 17 00:00:00 2001 From: Pavel Prichodko Date: Tue, 4 Sep 2018 13:58:54 +0200 Subject: [PATCH 3/3] Add support for creating modern and legacy build --- .../config/webpack.config.prod.js | 762 +++++++++--------- .../webpack/html-webpack-esmodules-plugin.js | 75 ++ packages/react-scripts/scripts/build.js | 22 +- 3 files changed, 483 insertions(+), 376 deletions(-) create mode 100644 packages/react-scripts/config/webpack/html-webpack-esmodules-plugin.js diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 6f9aeba80e1..054b9b887e2 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -21,6 +21,7 @@ const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin'); const eslintFormatter = require('react-dev-utils/eslintFormatter'); const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent'); +const HtmlWebpackEsmodulesPlugin = require('./webpack/html-webpack-esmodules-plugin'); const paths = require('./paths'); const getClientEnvironment = require('./env'); @@ -89,394 +90,413 @@ const getStyleLoaders = (cssOptions, preProcessor) => { // 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: publicPath, - // Point sourcemap entries to original disk location (format as URL on Windows) - devtoolModuleFilenameTemplate: info => - path - .relative(paths.appSrc, info.absoluteResourcePath) - .replace(/\\/g, '/'), - }, - optimization: { - minimizer: [ - new TerserPlugin({ - terserOptions: { - parse: { - // we want terser to parse ecma 8 code. However, we don't want it - // to apply any minfication steps that turns valid ecma 5 code - // 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, - }, - mangle: { - safari10: true, - }, - 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, +function createWebpackConfig({ isModernBuild = false, modern = false } = {}) { + return { + 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: modern + ? paths.appIndexJs + : [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: modern + ? 'static/js/[name].[chunkhash:8].m.js' + : 'static/js/[name].[chunkhash:8].js', + chunkFilename: modern + ? 'static/js/[name].[chunkhash:8].chunk.m.js' + : 'static/js/[name].[chunkhash:8].chunk.js', + // We inferred the "public path" (such as / or /my-project) from homepage. + publicPath: publicPath, + // Point sourcemap entries to original disk location (format as URL on Windows) + devtoolModuleFilenameTemplate: info => + path + .relative(paths.appSrc, info.absoluteResourcePath) + .replace(/\\/g, '/'), + }, + optimization: { + minimizer: [ + new TerserPlugin({ + terserOptions: { + parse: { + // we want terser to parse ecma 8 code. However, we don't want it + // to apply any minfication steps that turns valid ecma 5 code + // 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, + }, + mangle: { + safari10: true, + }, + 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, + }, }, - }, - // 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, - }), - new OptimizeCSSAssetsPlugin({ cssProcessorOptions: { safe: true } }), - ], - // 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: { - chunks: 'all', - name: 'vendors', + // 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, + }), + new OptimizeCSSAssetsPlugin({ cssProcessorOptions: { safe: true } }), + ], + // 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: { + chunks: 'all', + name: 'vendors', + }, + // Keep the runtime chunk seperated to enable long term caching + // https://twitter.com/wSokra/status/969679223278505985 + runtimeChunk: true, }, - // Keep the runtime chunk seperated to enable long term caching - // https://twitter.com/wSokra/status/969679223278505985 - runtimeChunk: true, - }, - 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'], - alias: { - // @remove-on-eject-begin - // Resolve Babel runtime relative to react-scripts. - // It usually still works on npm 3 without this but it would be - // unfortunate to rely on, as react-scripts could be symlinked, - // and thus @babel/runtime might not be resolvable from the source. - '@babel/runtime': path.dirname( - require.resolve('@babel/runtime/package.json') + 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) ), - // @remove-on-eject-end - // Support React Native Web - // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ - 'react-native': 'react-native-web', + // 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'], + alias: { + // @remove-on-eject-begin + // Resolve Babel runtime relative to react-scripts. + // It usually still works on npm 3 without this but it would be + // unfortunate to rely on, as react-scripts could be symlinked, + // and thus @babel/runtime might not be resolvable from the source. + '@babel/runtime': path.dirname( + require.resolve('@babel/runtime/package.json') + ), + // @remove-on-eject-end + // Support React Native Web + // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ + 'react-native': 'react-native-web', + }, + 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]), + ], }, - 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 } }, + 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. - { - 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. - baseConfig: { - extends: [require.resolve('eslint-config-react-app')], + // First, run the linter. + // It's important to do this before Babel processes the JS. + { + 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. + baseConfig: { + extends: [require.resolve('eslint-config-react-app')], + }, + // @remove-on-eject-begin + ignore: false, + useEslintrc: false, + // @remove-on-eject-end }, - // @remove-on-eject-begin - ignore: false, - useEslintrc: false, - // @remove-on-eject-end + loader: require.resolve('eslint-loader'), }, - 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]', + ], + 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: { - // @remove-on-eject-begin - babelrc: false, - // @remove-on-eject-end - presets: [require.resolve('babel-preset-react-app')], - plugins: [ - [ - require.resolve('babel-plugin-named-asset-import'), - { - loaderMap: { - svg: { - ReactComponent: 'svgr/webpack![path]', + // 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: { + // @remove-on-eject-begin + babelrc: false, + // @remove-on-eject-end + presets: [ + [ + require.resolve('babel-preset-react-app'), + { modern: modern }, + ], + ], + plugins: [ + [ + require.resolve('babel-plugin-named-asset-import'), + { + loaderMap: { + svg: { + ReactComponent: 'svgr/webpack![path]', + }, }, }, - }, + ], ], - ], - compact: true, - highlightCode: true, + 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, + ], + }, + // 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, + }, }, - }, - ], - }, - // "postcss" loader applies autoprefixer to our CSS. - // "css" loader resolves paths in CSS and adds assets as dependencies. - // `MiniCSSExtractPlugin` extracts styles into CSS - // files. If you use code splitting, async bundles will have their own separate CSS chunk file. - // By default we support CSS Modules with the extension .module.css - { - test: cssRegex, - exclude: cssModuleRegex, - loader: getStyleLoaders({ - importLoaders: 1, - sourceMap: shouldUseSourceMap, - }), - }, - // Adds support for CSS Modules (https://github.com/css-modules/css-modules) - // using the extension .module.css - { - test: cssModuleRegex, - loader: getStyleLoaders({ - importLoaders: 1, - sourceMap: shouldUseSourceMap, - modules: true, - getLocalIdent: getCSSModuleLocalIdent, - }), - }, - // Opt-in support for SASS. The logic here is somewhat similar - // as in the CSS routine, except that "sass-loader" runs first - // to compile SASS files into CSS. - // By default we support SASS Modules with the - // extensions .module.scss or .module.sass - { - test: sassRegex, - exclude: sassModuleRegex, - loader: getStyleLoaders( - { - importLoaders: 2, + ], + }, + // "postcss" loader applies autoprefixer to our CSS. + // "css" loader resolves paths in CSS and adds assets as dependencies. + // `MiniCSSExtractPlugin` extracts styles into CSS + // files. If you use code splitting, async bundles will have their own separate CSS chunk file. + // By default we support CSS Modules with the extension .module.css + { + test: cssRegex, + exclude: cssModuleRegex, + loader: getStyleLoaders({ + importLoaders: 1, sourceMap: shouldUseSourceMap, - }, - 'sass-loader' - ), - }, - // Adds support for CSS Modules, but using SASS - // using the extension .module.scss or .module.sass - { - test: sassModuleRegex, - loader: getStyleLoaders( - { - importLoaders: 2, + }), + }, + // Adds support for CSS Modules (https://github.com/css-modules/css-modules) + // using the extension .module.css + { + test: cssModuleRegex, + loader: getStyleLoaders({ + importLoaders: 1, sourceMap: shouldUseSourceMap, modules: true, getLocalIdent: getCSSModuleLocalIdent, + }), + }, + // Opt-in support for SASS. The logic here is somewhat similar + // as in the CSS routine, except that "sass-loader" runs first + // to compile SASS files into CSS. + // By default we support SASS Modules with the + // extensions .module.scss or .module.sass + { + test: sassRegex, + exclude: sassModuleRegex, + loader: getStyleLoaders( + { + importLoaders: 2, + sourceMap: shouldUseSourceMap, + }, + 'sass-loader' + ), + }, + // Adds support for CSS Modules, but using SASS + // using the extension .module.scss or .module.sass + { + test: sassModuleRegex, + loader: getStyleLoaders( + { + importLoaders: 2, + sourceMap: shouldUseSourceMap, + modules: true, + getLocalIdent: getCSSModuleLocalIdent, + }, + 'sass-loader' + ), + }, + // 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]', }, - 'sass-loader' - ), - }, - // 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. - ], - }, - ], - }, - plugins: [ - // Generates an `index.html` file with the