import * as webpack from "webpack"; import { paths } from "../../../config/paths"; const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const Fiber = require("fibers"); // Source maps are resource heavy and can cause out of memory issue for large source files. const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false"; // style files regexes const cssRegex = /\.css$/; const cssModuleRegex = /\.module\.css$/; const sassRegex = /\.(scss|sass)$/; const sassModuleRegex = /\.module\.(scss|sass)$/; interface CssOptions { importLoaders?: number; sourceMap?: boolean; modules: true; getLocalIdent?: any; } export const generateRule = ({ isEnvProduction, isEnvDevelopment, shouldUseRelativeAssetPaths, }: { isEnvProduction: boolean; isEnvDevelopment: boolean; shouldUseRelativeAssetPaths: boolean; }): webpack.Rule => { // common function to get style loaders const getStyleLoaders = (cssOptions: CssOptions, preProcessor: string | undefined = undefined): webpack.RuleSetUse => { const loaders = [ isEnvDevelopment && require.resolve("style-loader"), isEnvProduction && { loader: MiniCssExtractPlugin.loader, options: { ...(shouldUseRelativeAssetPaths ? { publicPath: "../../" } : undefined) }, }, { loader: require.resolve("css-loader"), options: { ...cssOptions, camelCase: true, localIdentName: "___[local]___[hash:base64:5]", }, }, { // Options for PostCSS as we reference these options twice // Adds vendor prefixing based on your specified browser support in // package.json loader: require.resolve("postcss-loader"), options: { // Necessary for external CSS imports to work // https://github.com/facebook/create-react-app/issues/2677 ident: "postcss", plugins: () => [ require("postcss-flexbugs-fixes"), require("postcss-preset-env")({ autoprefixer: { flexbox: "no-2009", }, stage: 3, }), ], sourceMap: isEnvProduction && shouldUseSourceMap, }, }, ].filter(Boolean) as webpack.RuleSetUseItem[]; if (preProcessor) { loaders.push({ loader: require.resolve(preProcessor), options: { implementation: require("sass"), sourceMap: isEnvProduction && shouldUseSourceMap, fiber: Fiber, }, }); } return loaders; }; return { // "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 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: 10000, 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)$/, 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,+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, cacheCompression: isEnvProduction, compact: isEnvProduction, }, }, // Process any JS outside of the app with Babel. // Unlike the application JS, we only compile the standard ES features. { 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, cacheCompression: isEnvProduction, // If an error happens in a package, it's possible to be // because it was compiled. Thus, we don't want the browser // debugger to show the original code. Instead, the code // being evaluated would be much more helpful. sourceMaps: false, }, }, // "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