react-create-app 中 webapck配置 解析
阿新 • • 發佈:2020-11-17
調出隱藏的webpack配置檔案
npm run eject
發射出配置檔案之後會出現兩個資料夾,一個是config資料夾,一個是scripts資料夾。
script 資料夾下 start.js
首先關注script資料夾的start.js 檔案 - 定義環境變數為development,然後再應用webpack配置,啟動webpack-dev-server
// start.js 'use strict'; // 定義環境變數 process.env.BABEL_ENV = 'development'; process.env.NODE_ENV = 'development'; process.on('unhandledRejection', err => { throw err; }); // 載入環境變數 require('../config/env'); const fs = require('fs'); const chalk = require('react-dev-utils/chalk'); const webpack = require('webpack'); const WebpackDevServer = require('webpack-dev-server'); const clearConsole = require('react-dev-utils/clearConsole'); const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); const { choosePort, createCompiler, prepareProxy, prepareUrls, } = require('react-dev-utils/WebpackDevServerUtils'); const openBrowser = require('react-dev-utils/openBrowser'); const paths = require('../config/paths'); const configFactory = require('../config/webpack.config'); const createDevServerConfig = require('../config/webpackDevServer.config'); const useYarn = fs.existsSync(paths.yarnLockFile); // 是否使用yarn const isInteractive = process.stdout.isTTY; // 判斷檔案是否存在,不存在就退出 (是否包含必要檔案) if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { process.exit(1); } // 定義預設埠號 const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; const HOST = process.env.HOST || '0.0.0.0'; if (process.env.HOST) { console.log( chalk.cyan( `Attempting to bind to HOST environment variable: ${chalk.yellow( chalk.bold(process.env.HOST) )}` ) ); console.log( `If this was unintentional, check that you haven't mistakenly set it in your shell.` ); console.log( `Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}` ); console.log(); } const { checkBrowsers } = require('react-dev-utils/browsersHelper'); // 檢查瀏覽器 checkBrowsers(paths.appPath, isInteractive) .then(() => { // We attempt to use the default port but if it is busy, we offer the user to // run on a different port. `choosePort()` Promise resolves to the next free port. return choosePort(HOST, DEFAULT_PORT); }) .then(port => { if (port == null) { // We have not found a port. return; } // webpack 開發環境配置 (重點) const config = configFactory('development'); const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; const appName = require(paths.appPackageJson).name; const useTypeScript = fs.existsSync(paths.appTsConfig); const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true'; const urls = prepareUrls( protocol, HOST, port, paths.publicUrlOrPath.slice(0, -1) ); const devSocket = { warnings: warnings => devServer.sockWrite(devServer.sockets, 'warnings', warnings), errors: errors => devServer.sockWrite(devServer.sockets, 'errors', errors), }; // 建立編譯器 const compiler = createCompiler({ appName, config, devSocket, urls, useYarn, useTypeScript, tscCompileOnError, webpack, }); // 在package.json 中的 proxy 代理配置 const proxySetting = require(paths.appPackageJson).proxy; const proxyConfig = prepareProxy( proxySetting, paths.appPublic, paths.publicUrlOrPath ); // 建立webpack-dev-server的配置 const serverConfig = createDevServerConfig( proxyConfig, urls.lanUrlForConfig ); const devServer = new WebpackDevServer(compiler, serverConfig); // Launch WebpackDevServer. devServer.listen(port, HOST, err => { if (err) { return console.log(err); } if (isInteractive) { clearConsole(); } if (process.env.NODE_PATH) { console.log( chalk.yellow( 'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.' ) ); console.log(); } console.log(chalk.cyan('Starting the development server...\n')); openBrowser(urls.localUrlForBrowser); }); ['SIGINT', 'SIGTERM'].forEach(function(sig) { process.on(sig, function() { devServer.close(); process.exit(); }); }); if (isInteractive || process.env.CI !== 'true') { // Gracefully exit when stdin ends process.stdin.on('end', function() { devServer.close(); process.exit(); }); process.stdin.resume(); } }) .catch(err => { if (err && err.message) { console.log(err.message); } process.exit(1); });
config 資料夾下 path.js
再來看看 config資料夾內的paths.js 檔案 ,該檔案主要作用是 輸出模組的絕對路徑,防止後續多次查詢路徑,輸出模組副檔名
// path.js 'use strict'; // 處理路徑的模組 const path = require('path'); const fs = require('fs'); const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath'); // 專案更目錄 const appDirectory = fs.realpathSync(process.cwd()); // 生成絕對路徑的方法 const resolveApp = relativePath => path.resolve(appDirectory, relativePath); // 所有資源的公共防衛 : / const publicUrlOrPath = getPublicUrlOrPath( process.env.NODE_ENV === 'development', require(resolveApp('package.json')).homepage, process.env.PUBLIC_URL ); // 副檔名 const moduleFileExtensions = [ 'web.mjs', 'mjs', 'web.js', 'js', 'web.ts', 'ts', 'web.tsx', 'tsx', 'json', 'web.jsx', 'jsx', ]; // 解析模組的方法 // Resolve file paths in the same order as webpack const resolveModule = (resolveFn, filePath) => { const extension = moduleFileExtensions.find(extension => fs.existsSync(resolveFn(`${filePath}.${extension}`)) ); if (extension) { return resolveFn(`${filePath}.${extension}`); } return resolveFn(`${filePath}.js`); }; // 獲取檔案 module.exports = { dotenv: resolveApp('.env'), appPath: resolveApp('.'), appBuild: resolveApp('build'), appPublic: resolveApp('public'), appHtml: resolveApp('public/index.html'), appIndexJs: resolveModule(resolveApp, 'src/index'), appPackageJson: resolveApp('package.json'), appSrc: resolveApp('src'), appTsConfig: resolveApp('tsconfig.json'), appJsConfig: resolveApp('jsconfig.json'), yarnLockFile: resolveApp('yarn.lock'), testsSetup: resolveModule(resolveApp, 'src/setupTests'), proxySetup: resolveApp('src/setupProxy.js'), appNodeModules: resolveApp('node_modules'), publicUrlOrPath, }; module.exports.moduleFileExtensions = moduleFileExtensions;
最最最重要的檔案 webpack.config.js
'use strict'; 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'); const postcssNormalize = require('postcss-normalize'); const appPackageJson = require(paths.appPackageJson); // 預設是生成source-map,如果定義環境變數 為false,就不適用source-map, // 通過cross-env 去修改 const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; // 是否內聯runtime檔案 const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false'; const isExtendingEslintConfig = process.env.EXTEND_ESLINT === 'true'; // 最小轉換為base64的圖片大小,10kb左右 const imageInlineSizeLimit = parseInt( process.env.IMAGE_INLINE_SIZE_LIMIT || '10000' ); // 判斷是否使用 typescript const useTypeScript = fs.existsSync(paths.appTsConfig); // 樣式檔案的正則 const cssRegex = /\.css$/; const cssModuleRegex = /\.module\.css$/; const sassRegex = /\.(scss|sass)$/; const sassModuleRegex = /\.module\.(scss|sass)$/; // 生成最終webpack開發或生成環境配置的函式 module.exports = function(webpackEnv) { const isEnvDevelopment = webpackEnv === 'development'; const isEnvProduction = webpackEnv === 'production'; // Variable used for enabling profiling in Production // passed into alias object. Uses a flag if passed into the build command const isEnvProductionProfile = isEnvProduction && process.argv.includes('--profile'); // 獲取環境變數的方法 // 載入.env檔案的環境變數,REACT_APP 開頭 const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1)); // 處理樣式檔案loader的配置 const getStyleLoaders = (cssOptions, preProcessor) => { const loaders = [ isEnvDevelopment && require.resolve('style-loader'), isEnvProduction && { loader: MiniCssExtractPlugin.loader, // css is located in `static/css`, use '../../' to locate index.html folder // in production `paths.publicUrlOrPath` can be a relative path options: paths.publicUrlOrPath.startsWith('.') ? { publicPath: '../../' } : {}, }, { loader: require.resolve('css-loader'), options: cssOptions, }, { loader: require.resolve('postcss-loader'), options: { ident: 'postcss', plugins: () => [ require('postcss-flexbugs-fixes'), require('postcss-preset-env')({ autoprefixer: { flexbox: 'no-2009', }, stage: 3, }), postcssNormalize(), ], sourceMap: isEnvProduction && shouldUseSourceMap, }, }, ].filter(Boolean); if (preProcessor) { loaders.push( { loader: require.resolve('resolve-url-loader'), options: { sourceMap: isEnvProduction && shouldUseSourceMap, }, }, { loader: require.resolve(preProcessor), options: { sourceMap: true, }, } ); } return loaders; }; // webpack 配置物件 (核心) return { mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development', bail: isEnvProduction, // 生成環境打包出錯就中止打包,開發環境中不用中止 devtool: isEnvProduction ? shouldUseSourceMap ? 'source-map' // 開發環境 : false : isEnvDevelopment && 'cheap-module-source-map', // 生產環境 entry: [ isEnvDevelopment && require.resolve('react-dev-utils/webpackHotDevClient'), // 開發環境下使用 熱載入模組 // app 下的index.js 作為入口檔案開始打包 paths.appIndexJs, ].filter(Boolean), output: { // The build folder. path: isEnvProduction ? paths.appBuild : undefined, // 添加註釋 到檔案中 pathinfo: isEnvDevelopment, // 輸出的檔名,使用contenthash 進行快取 filename: isEnvProduction ? 'static/js/[name].[contenthash:8].js' : isEnvDevelopment && 'static/js/bundle.js', // 程式碼分割 出來的檔案會以 chunkFilename進行命名 chunkFilename: isEnvProduction ? 'static/js/[name].[contenthash:8].chunk.js' : isEnvDevelopment && 'static/js/[name].chunk.js', // 預設為 / ,可通過package.json中的homepage進行配置 publicPath: paths.publicUrlOrPath, // Point sourcemap entries to original disk location (format as URL on Windows) devtoolModuleFilenameTemplate: isEnvProduction ? info => path .relative(paths.appSrc, info.absoluteResourcePath) .replace(/\\/g, '/') : isEnvDevelopment && (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')), jsonpFunction: `webpackJsonp${appPackageJson.name}`, // 多模組 不會產生衝突 // 全域性物件 window 或者 global 使用 this globalObject: 'this', }, // 啟動壓縮 ,優化 optimization: { minimize: isEnvProduction, minimizer: [ // 壓縮js new TerserPlugin({ terserOptions: { parse: { // 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 // 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 // Pending further investigation: // https://github.com/terser-js/terser/issues/120 inline: 2, }, mangle: { safari10: true, }, // Added for profiling in devtools keep_classnames: isEnvProductionProfile, keep_fnames: isEnvProductionProfile, 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, }), // 壓縮 css 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, }, cssProcessorPluginOptions: { preset: ['default', { minifyFontValues: { removeQuotes: false } }], }, }), ], // 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 // 程式碼分割:將node_modules 單獨成一個chunk,超過20kb重新分割。懶載入,預載入檔案進行分割打包 splitChunks: { chunks: 'all', name: false, }, // 解決 contenthash 快取失效問題,比如a檔案引用b檔案,b檔案發生改變,導致a檔案的快取也失效的問題。runtime檔案只儲存檔案的contenthash值,檔案較小 runtimeChunk: { name: entrypoint => `runtime-${entrypoint.name}`, }, }, // 定義解析規則 resolve: { modules: ['node_modules', paths.appNodeModules].concat( modules.additionalModulePaths || [] ), extensions: paths.moduleFileExtensions .map(ext => `.${ext}`) .filter(ext => useTypeScript || !ext.includes('ts')), alias: { 'react-native': 'react-native-web', ...(isEnvProductionProfile && { 'react-dom$': 'react-dom/profiling', 'scheduler/tracing': 'scheduler/tracing-profiling', }), ...(modules.webpackAliases || {}), }, plugins: [ PnpWebpackPlugin, // 模組解析策略,限制查詢定定義模組的範圍,只能在src內部 new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), ], }, resolveLoader: { plugins: [ PnpWebpackPlugin.moduleLoader(module), ], }, // loader的配置 module: { strictExportPresence: true, // 嚴格匯出 rules: [ { parser: { requireEnsure: false } }, // First, run the linter. // It's important to do this before Babel processes the JS. { test: /\.(js|mjs|jsx|ts|tsx)$/, enforce: 'pre', use: [ { options: { cache: true, formatter: require.resolve('react-dev-utils/eslintFormatter'), eslintPath: require.resolve('eslint'), resolvePluginsRelativeTo: __dirname, }, loader: require.resolve('eslint-loader'), }, ], include: paths.appSrc, // 只包含src內的檔案 }, { // 陣列內的loader 有且只有一個指行到 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$/], loader: require.resolve('url-loader'), options: { limit: imageInlineSizeLimit, // 轉化為base64 的圖片就不用再進行請求,整體請求數就會少一點,對伺服器壓力會小一點。20kb一下能夠接受 name: 'static/media/[name].[hash:8].[ext]', }, }, // Process application JS with Babel. // The preset includes JSX, Flow, TypeScript, and some ESnext features. { test: /\.(js|mjs|jsx|ts|tsx)$/, include: paths.appSrc, loader: require.resolve('babel-loader'), options: { customize: require.resolve( 'babel-preset-react-app/webpack-overrides' ), plugins: [ [ require.resolve('babel-plugin-named-asset-import'), { loaderMap: { svg: { ReactComponent: '@svgr/webpack?-svgo,+titleProp,+ref![path]', }, }, }, ], ], // 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, // See #6846 for context on why cacheCompression is disabled cacheCompression: false, compact: isEnvProduction, }, }, // Process any JS outside of the app with Babel. // 除了src以為的js檔案,比如node_modules中js檔案 { test: /\.(js|mjs)$/, exclude: /@babel(?:\/|\\{1,2})runtime/, loader: require.resolve('babel-loader'), options: { babelrc: false, configFile: false, compact: false, presets: [ [ require.resolve('babel-preset-react-app/dependencies'), { helpers: true }, ], ], cacheDirectory: true, // 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, }, }, // "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, }), sideEffects: true, // 樣式檔案不會進行tree shaking }, // react中使用 css模組化的方案,增加唯一標識 (.module.css結尾) { test: cssModuleRegex, use: getStyleLoaders({ importLoaders: 1, sourceMap: isEnvProduction && shouldUseSourceMap, modules: { getLocalIdent: getCSSModuleLocalIdent, }, }), }, // 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( { importLoaders: 3, sourceMap: isEnvProduction && shouldUseSourceMap, }, 'sass-loader' ), // 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( { importLoaders: 3, sourceMap: isEnvProduction && shouldUseSourceMap, modules: { getLocalIdent: getCSSModuleLocalIdent, }, }, 'sass-loader' ), }, // "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. { loader: require.resolve('file-loader'), // 什麼樣的檔案 輸入,就以什麼樣子格式輸出,一般作為最後一個檔案輸出 // 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$/,/.scss$/], 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: [ // 處理html 外掛 new HtmlWebpackPlugin( Object.assign( {}, { inject: true, template: paths.appHtml, }, isEnvProduction ? { // 生產環境多一個 html的壓縮 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. // https://github.com/facebook/create-react-app/issues/5358 isEnvProduction && shouldInlineRuntimeChunk && new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]), // 是否內聯runtime檔案,作用就是少發一個請求,通過cross-env 將環境變數設定為true // 解析index.html 中 public url new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw), // 模組找不到,給出更好的提示 new ModuleNotFoundPlugin(paths.appPath), // 定義環境變數 new webpack.DefinePlugin(env.stringified), // 開發環境下,HMR功能 - 熱模組替換 isEnvDevelopment && new webpack.HotModuleReplacementPlugin(), // 檔案路徑 嚴格區分大小寫 isEnvDevelopment && new CaseSensitivePathsPlugin(), // 監視node_modules,一旦發生變化會重啟dev-server,不需要自己手動的進行重啟了。 模組找不到,不會重啟 webpack-dev-server isEnvDevelopment && new WatchMissingNodeModulesPlugin(paths.appNodeModules), // 生產環境下提取css樣式檔案為一個單獨的檔案 isEnvProduction && new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output // both options are optional filename: 'static/css/[name].[contenthash:8].css', chunkFilename: 'static/css/[name].[contenthash:8].chunk.css', }), // 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 new ManifestPlugin({ fileName: 'asset-manifest.json', publicPath: paths.publicUrlOrPath, generate: (seed, files, entrypoints) => { const manifestFiles = files.reduce((manifest, file) => { manifest[file.name] = file.path; return manifest; }, seed); const entrypointFiles = entrypoints.main.filter( fileName => !fileName.endsWith('.map') ); return { files: manifestFiles, entrypoints: entrypointFiles, }; }, }), // 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$/), // moment 中 locale 這個庫太大了,不打包進來。用日期庫可以使用datejs // Generate a service worker script that will precache, and keep up to date, // the HTML & assets that are part of the webpack build. isEnvProduction && new WorkboxWebpackPlugin.GenerateSW({ clientsClaim: true, exclude: [/\.map$/, /asset-manifest\.json$/], importWorkboxFrom: 'cdn', navigateFallback: paths.publicUrlOrPath + 'index.html', navigateFallbackBlacklist: [ // Exclude URLs starting with /_, as they're likely an API call new RegExp('^/_'), // 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). new RegExp('/[^/?]+\\.[^/]+$'), ], }), // TypeScript type checking useTypeScript && new ForkTsCheckerWebpackPlugin({ typescript: resolve.sync('typescript', { basedir: paths.appNodeModules, }), async: isEnvDevelopment, useTypescriptIncrementalApi: true, checkSyntacticErrors: true, resolveModuleNameModule: process.versions.pnp ? `${__dirname}/pnpTs.js` : undefined, resolveTypeReferenceDirectiveModule: process.versions.pnp ? `${__dirname}/pnpTs.js` : undefined, tsconfig: paths.appTsConfig, reportFiles: [ '**', '!**/__tests__/**', '!**/?(*.)(spec|test).*', '!**/src/setupProxy.*', '!**/src/setupTests.*', ], 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. // Tell webpack to provide empty mocks for them so importing them works. node: { module: 'empty', dgram: 'empty', dns: 'mock', fs: 'empty', http2: 'empty', net: 'empty', tls: 'empty', child_process: 'empty', }, // Turn off performance processing because we utilize // our own hints via the FileSizeReporter performance: false, }; };