From 17b9a50fae699ac58b5bae16e7a376b98a3f1c19 Mon Sep 17 00:00:00 2001 From: Martin Man Date: Mon, 30 Sep 2024 16:18:13 +0200 Subject: [PATCH 01/10] sync with CRA --- config/env.js | 68 ++++++------- config/getHttpsConfig.js | 58 ++++++----- config/modules.js | 84 ++++++++-------- config/paths.js | 90 +++++++++-------- scripts/build.js | 206 ++++++++++++++++++++++----------------- scripts/start.js | 177 ++++++++++++++++++--------------- scripts/test.js | 47 +++++---- 7 files changed, 403 insertions(+), 327 deletions(-) diff --git a/config/env.js b/config/env.js index fae806a74..ffa7e496a 100644 --- a/config/env.js +++ b/config/env.js @@ -1,13 +1,17 @@ -const fs = require("fs") -const path = require("path") -const paths = require("./paths") +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const paths = require('./paths'); // Make sure that including paths.js after env.js will read .env variables. -delete require.cache[require.resolve("./paths")] +delete require.cache[require.resolve('./paths')]; -const NODE_ENV = process.env.NODE_ENV +const NODE_ENV = process.env.NODE_ENV; if (!NODE_ENV) { - throw new Error("The NODE_ENV environment variable is required but was not specified.") + throw new Error( + 'The NODE_ENV environment variable is required but was not specified.' + ); } // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use @@ -16,25 +20,25 @@ const dotenvFiles = [ // Don't include `.env.local` for `test` environment // since normally you expect tests to produce the same // results for everyone - NODE_ENV !== "test" && `${paths.dotenv}.local`, + NODE_ENV !== 'test' && `${paths.dotenv}.local`, `${paths.dotenv}.${NODE_ENV}`, paths.dotenv, -].filter(Boolean) +].filter(Boolean); // Load environment variables from .env* files. Suppress warnings using silent // if this file is missing. dotenv will never modify any environment variables // that have already been set. Variable expansion is supported in .env files. // https://github.com/motdotla/dotenv // https://github.com/motdotla/dotenv-expand -dotenvFiles.forEach((dotenvFile) => { +dotenvFiles.forEach(dotenvFile => { if (fs.existsSync(dotenvFile)) { - require("dotenv-expand")( - require("dotenv").config({ + require('dotenv-expand')( + require('dotenv').config({ path: dotenvFile, }) - ) + ); } -}) +}); // We support resolving modules according to `NODE_PATH`. // This lets you use absolute paths in imports inside large monorepos: @@ -45,29 +49,29 @@ dotenvFiles.forEach((dotenvFile) => { // Otherwise, we risk importing Node.js core modules into an app instead of webpack shims. // https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421 // We also resolve them to make sure all tools using them work consistently. -const appDirectory = fs.realpathSync(process.cwd()) -process.env.NODE_PATH = (process.env.NODE_PATH || "") +const appDirectory = fs.realpathSync(process.cwd()); +process.env.NODE_PATH = (process.env.NODE_PATH || '') .split(path.delimiter) - .filter((folder) => folder && !path.isAbsolute(folder)) - .map((folder) => path.resolve(appDirectory, folder)) - .join(path.delimiter) + .filter(folder => folder && !path.isAbsolute(folder)) + .map(folder => path.resolve(appDirectory, folder)) + .join(path.delimiter); // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be // injected into the application via DefinePlugin in webpack configuration. -const REACT_APP = /^REACT_APP_/i +const REACT_APP = /^REACT_APP_/i; function getClientEnvironment(publicUrl) { const raw = Object.keys(process.env) - .filter((key) => REACT_APP.test(key)) + .filter(key => REACT_APP.test(key)) .reduce( (env, key) => { - env[key] = process.env[key] - return env + env[key] = process.env[key]; + return env; }, { // Useful for determining whether we’re running in production mode. // Most importantly, it switches React into the correct mode. - NODE_ENV: process.env.NODE_ENV || "development", + NODE_ENV: process.env.NODE_ENV || 'development', // Useful for resolving the correct path to static assets in `public`. // For example, . // This should only be used as an escape hatch. Normally you would put @@ -82,21 +86,19 @@ function getClientEnvironment(publicUrl) { WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH, WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT, // Whether or not react-refresh is enabled. - // react-refresh is not 100% stable at this time, - // which is why it's disabled by default. // It is defined here so it is available in the webpackHotDevClient. - FAST_REFRESH: process.env.FAST_REFRESH !== "false", + FAST_REFRESH: process.env.FAST_REFRESH !== 'false', } - ) + ); // Stringify all values so we can feed into webpack DefinePlugin const stringified = { - "process.env": Object.keys(raw).reduce((env, key) => { - env[key] = JSON.stringify(raw[key]) - return env + 'process.env': Object.keys(raw).reduce((env, key) => { + env[key] = JSON.stringify(raw[key]); + return env; }, {}), - } + }; - return { raw, stringified } + return { raw, stringified }; } -module.exports = getClientEnvironment +module.exports = getClientEnvironment; diff --git a/config/getHttpsConfig.js b/config/getHttpsConfig.js index 7efa29a82..013d493c1 100644 --- a/config/getHttpsConfig.js +++ b/config/getHttpsConfig.js @@ -1,25 +1,33 @@ -const fs = require("fs") -const path = require("path") -const crypto = require("crypto") -const chalk = require("react-dev-utils/chalk") -const paths = require("./paths") +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const crypto = require('crypto'); +const chalk = require('react-dev-utils/chalk'); +const paths = require('./paths'); // Ensure the certificate and key provided are valid and if not // throw an easy to debug error function validateKeyAndCerts({ cert, key, keyFile, crtFile }) { - let encrypted + let encrypted; try { // publicEncrypt will throw an error with an invalid cert - encrypted = crypto.publicEncrypt(cert, Buffer.from("test")) + encrypted = crypto.publicEncrypt(cert, Buffer.from('test')); } catch (err) { - throw new Error(`The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`) + throw new Error( + `The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}` + ); } try { // privateDecrypt will throw an error with an invalid key - crypto.privateDecrypt(key, encrypted) + crypto.privateDecrypt(key, encrypted); } catch (err) { - throw new Error(`The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${err.message}`) + throw new Error( + `The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${ + err.message + }` + ); } } @@ -27,30 +35,32 @@ function validateKeyAndCerts({ cert, key, keyFile, crtFile }) { function readEnvFile(file, type) { if (!fs.existsSync(file)) { throw new Error( - `You specified ${chalk.cyan(type)} in your env, but the file "${chalk.yellow(file)}" can't be found.` - ) + `You specified ${chalk.cyan( + type + )} in your env, but the file "${chalk.yellow(file)}" can't be found.` + ); } - return fs.readFileSync(file) + return fs.readFileSync(file); } // Get the https config // Return cert files if provided in env, otherwise just true or false function getHttpsConfig() { - const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env - const isHttps = HTTPS === "true" + const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env; + const isHttps = HTTPS === 'true'; if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) { - const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE) - const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE) + const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE); + const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE); const config = { - cert: readEnvFile(crtFile, "SSL_CRT_FILE"), - key: readEnvFile(keyFile, "SSL_KEY_FILE"), - } + cert: readEnvFile(crtFile, 'SSL_CRT_FILE'), + key: readEnvFile(keyFile, 'SSL_KEY_FILE'), + }; - validateKeyAndCerts({ ...config, keyFile, crtFile }) - return config + validateKeyAndCerts({ ...config, keyFile, crtFile }); + return config; } - return isHttps + return isHttps; } -module.exports = getHttpsConfig +module.exports = getHttpsConfig; diff --git a/config/modules.js b/config/modules.js index 6fcf390c2..d63e41d78 100644 --- a/config/modules.js +++ b/config/modules.js @@ -1,8 +1,10 @@ -const fs = require("fs") -const path = require("path") -const paths = require("./paths") -const chalk = require("react-dev-utils/chalk") -const resolve = require("resolve") +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const paths = require('./paths'); +const chalk = require('react-dev-utils/chalk'); +const resolve = require('resolve'); /** * Get additional module paths based on the baseUrl of a compilerOptions object. @@ -10,23 +12,23 @@ const resolve = require("resolve") * @param {Object} options */ function getAdditionalModulePaths(options = {}) { - const baseUrl = options.baseUrl + const baseUrl = options.baseUrl; if (!baseUrl) { - return "" + return ''; } - const baseUrlResolved = path.resolve(paths.appPath, baseUrl) + const baseUrlResolved = path.resolve(paths.appPath, baseUrl); // We don't need to do anything if `baseUrl` is set to `node_modules`. This is // the default behavior. - if (path.relative(paths.appNodeModules, baseUrlResolved) === "") { - return null + if (path.relative(paths.appNodeModules, baseUrlResolved) === '') { + return null; } // Allow the user set the `baseUrl` to `appSrc`. - if (path.relative(paths.appSrc, baseUrlResolved) === "") { - return [paths.appSrc] + if (path.relative(paths.appSrc, baseUrlResolved) === '') { + return [paths.appSrc]; } // If the path is equal to the root directory we ignore it here. @@ -34,17 +36,17 @@ function getAdditionalModulePaths(options = {}) { // not transpiled outside of `src`. We do allow importing them with the // absolute path (e.g. `src/Components/Button.js`) but we set that up with // an alias. - if (path.relative(paths.appPath, baseUrlResolved) === "") { - return null + if (path.relative(paths.appPath, baseUrlResolved) === '') { + return null; } // Otherwise, throw an error. throw new Error( chalk.red.bold( "Your project's `baseUrl` can only be set to `src` or `node_modules`." + - " Create React App does not support other values at this time." + ' Create React App does not support other values at this time.' ) - ) + ); } /** @@ -53,18 +55,18 @@ function getAdditionalModulePaths(options = {}) { * @param {*} options */ function getWebpackAliases(options = {}) { - const baseUrl = options.baseUrl + const baseUrl = options.baseUrl; if (!baseUrl) { - return {} + return {}; } - const baseUrlResolved = path.resolve(paths.appPath, baseUrl) + const baseUrlResolved = path.resolve(paths.appPath, baseUrl); - if (path.relative(paths.appPath, baseUrlResolved) === "") { + if (path.relative(paths.appPath, baseUrlResolved) === '') { return { src: paths.appSrc, - } + }; } } @@ -74,59 +76,59 @@ function getWebpackAliases(options = {}) { * @param {*} options */ function getJestAliases(options = {}) { - const baseUrl = options.baseUrl + const baseUrl = options.baseUrl; if (!baseUrl) { - return {} + return {}; } - const baseUrlResolved = path.resolve(paths.appPath, baseUrl) + const baseUrlResolved = path.resolve(paths.appPath, baseUrl); - if (path.relative(paths.appPath, baseUrlResolved) === "") { + if (path.relative(paths.appPath, baseUrlResolved) === '') { return { - "^src/(.*)$": "/src/$1", - } + '^src/(.*)$': '/src/$1', + }; } } function getModules() { // Check if TypeScript is setup - const hasTsConfig = fs.existsSync(paths.appTsConfig) - const hasJsConfig = fs.existsSync(paths.appJsConfig) + const hasTsConfig = fs.existsSync(paths.appTsConfig); + const hasJsConfig = fs.existsSync(paths.appJsConfig); if (hasTsConfig && hasJsConfig) { throw new Error( - "You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file." - ) + 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.' + ); } - let config + let config; // If there's a tsconfig.json we assume it's a // TypeScript project and set up the config // based on tsconfig.json if (hasTsConfig) { - const ts = require(resolve.sync("typescript", { + const ts = require(resolve.sync('typescript', { basedir: paths.appNodeModules, - })) - config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config + })); + config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config; // Otherwise we'll check if there is jsconfig.json // for non TS projects. } else if (hasJsConfig) { - config = require(paths.appJsConfig) + config = require(paths.appJsConfig); } - config = config || {} - const options = config.compilerOptions || {} + config = config || {}; + const options = config.compilerOptions || {}; - const additionalModulePaths = getAdditionalModulePaths(options) + const additionalModulePaths = getAdditionalModulePaths(options); return { additionalModulePaths: additionalModulePaths, webpackAliases: getWebpackAliases(options), jestAliases: getJestAliases(options), hasTsConfig, - } + }; } -module.exports = getModules() +module.exports = getModules(); diff --git a/config/paths.js b/config/paths.js index f73f7846e..f0a6cd9c9 100644 --- a/config/paths.js +++ b/config/paths.js @@ -1,11 +1,13 @@ -const path = require("path") -const fs = require("fs") -const getPublicUrlOrPath = require("react-dev-utils/getPublicUrlOrPath") +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath'); // Make sure any symlinks in the project folder are resolved: // https://github.com/facebook/create-react-app/issues/637 -const appDirectory = fs.realpathSync(process.cwd()) -const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath) +const appDirectory = fs.realpathSync(process.cwd()); +const resolveApp = relativePath => path.resolve(appDirectory, relativePath); // We use `PUBLIC_URL` environment variable or "homepage" field to infer // "public path" at which the app is served. @@ -14,56 +16,62 @@ const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath) // We can't use a relative path in HTML because we don't want to load something // like /todos/42/static/js/bundle.7289d.js. We have to know the root. const publicUrlOrPath = getPublicUrlOrPath( - process.env.NODE_ENV === "development", - require(resolveApp("package.json")).homepage, + process.env.NODE_ENV === 'development', + require(resolveApp('package.json')).homepage, process.env.PUBLIC_URL -) +); -const buildPath = process.env.BUILD_PATH || "build" +const buildPath = process.env.BUILD_PATH || 'build'; const moduleFileExtensions = [ - "web.mjs", - "mjs", - "web.js", - "js", - "web.ts", - "ts", - "web.tsx", - "tsx", - "json", - "web.jsx", - "jsx", -] + '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}`))) + const extension = moduleFileExtensions.find(extension => + fs.existsSync(resolveFn(`${filePath}.${extension}`)) + ); if (extension) { - return resolveFn(`${filePath}.${extension}`) + return resolveFn(`${filePath}.${extension}`); } - return resolveFn(`${filePath}.js`) -} + return resolveFn(`${filePath}.js`); +}; // config after eject: we're in ./config/ module.exports = { - dotenv: resolveApp(".env"), - appPath: resolveApp("."), + dotenv: resolveApp('.env'), + appPath: resolveApp('.'), appBuild: resolveApp(buildPath), - 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"), - swSrc: resolveModule(resolveApp, "src/service-worker"), + 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'), + appWebpackCache: resolveApp('node_modules/.cache'), + appTsBuildInfoFile: resolveApp('node_modules/.cache/tsconfig.tsbuildinfo'), + swSrc: resolveModule(resolveApp, 'src/service-worker'), publicUrlOrPath, -} +}; + + -module.exports.moduleFileExtensions = moduleFileExtensions +module.exports.moduleFileExtensions = moduleFileExtensions; diff --git a/scripts/build.js b/scripts/build.js index b2290ad10..8a9acaaf7 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -1,191 +1,217 @@ +'use strict'; + // Do this as the first thing so that any code reading it knows the right env. -process.env.BABEL_ENV = "production" -process.env.NODE_ENV = "production" -process.env.GENERATE_SOURCEMAP = "false" +process.env.BABEL_ENV = 'production'; +process.env.NODE_ENV = 'production'; // Makes the script crash on unhandled rejections instead of silently // ignoring them. In the future, promise rejections that are not handled will // terminate the Node.js process with a non-zero exit code. -process.on("unhandledRejection", (err) => { - throw err -}) +process.on('unhandledRejection', err => { + throw err; +}); // Ensure environment variables are read. -require("../config/env") - -const path = require("path") -const chalk = require("react-dev-utils/chalk") -const fs = require("fs-extra") -const bfj = require("bfj") -const webpack = require("webpack") -const configFactory = require("../config/webpack.config") -const paths = require("../config/paths") -const checkRequiredFiles = require("react-dev-utils/checkRequiredFiles") -const formatWebpackMessages = require("react-dev-utils/formatWebpackMessages") -const printHostingInstructions = require("react-dev-utils/printHostingInstructions") -const FileSizeReporter = require("react-dev-utils/FileSizeReporter") -const printBuildError = require("react-dev-utils/printBuildError") - -const measureFileSizesBeforeBuild = FileSizeReporter.measureFileSizesBeforeBuild -const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild -const useYarn = fs.existsSync(paths.yarnLockFile) +require('../config/env'); + +const path = require('path'); +const chalk = require('react-dev-utils/chalk'); +const fs = require('fs-extra'); +const bfj = require('bfj'); +const webpack = require('webpack'); +const configFactory = require('../config/webpack.config'); +const paths = require('../config/paths'); +const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); +const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); +const printHostingInstructions = require('react-dev-utils/printHostingInstructions'); +const FileSizeReporter = require('react-dev-utils/FileSizeReporter'); +const printBuildError = require('react-dev-utils/printBuildError'); + +const measureFileSizesBeforeBuild = + FileSizeReporter.measureFileSizesBeforeBuild; +const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; +const useYarn = fs.existsSync(paths.yarnLockFile); // These sizes are pretty large. We'll warn for bundles exceeding them. -const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024 -const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024 +const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; +const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024; -const isInteractive = process.stdout.isTTY +const isInteractive = process.stdout.isTTY; // Warn and crash if required files are missing if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { - process.exit(1) + process.exit(1); } -const argv = process.argv.slice(2) -const writeStatsJson = argv.indexOf("--stats") !== -1 +const argv = process.argv.slice(2); +const writeStatsJson = argv.indexOf('--stats') !== -1; // Generate configuration -const config = configFactory("production") +const config = configFactory('production'); // We require that you explicitly set browsers and do not fall back to // browserslist defaults. -const { checkBrowsers } = require("react-dev-utils/browsersHelper") +const { checkBrowsers } = require('react-dev-utils/browsersHelper'); checkBrowsers(paths.appPath, isInteractive) .then(() => { // First, read the current file sizes in build directory. // This lets us display how much they changed later. - return measureFileSizesBeforeBuild(paths.appBuild) + return measureFileSizesBeforeBuild(paths.appBuild); }) - .then((previousFileSizes) => { + .then(previousFileSizes => { // Remove all content but keep the directory so that // if you're in it, you don't end up in Trash - fs.emptyDirSync(paths.appBuild) + fs.emptyDirSync(paths.appBuild); // Merge with the public folder - copyPublicFolder() + copyPublicFolder(); // Start the webpack build - return build(previousFileSizes) + return build(previousFileSizes); }) .then( ({ stats, previousFileSizes, warnings }) => { if (warnings.length) { - console.log(chalk.yellow("Compiled with warnings.\n")) - console.log(warnings.join("\n\n")) + console.log(chalk.yellow('Compiled with warnings.\n')); + console.log(warnings.join('\n\n')); + console.log( + '\nSearch for the ' + + chalk.underline(chalk.yellow('keywords')) + + ' to learn more about each warning.' + ); console.log( - "\nSearch for the " + chalk.underline(chalk.yellow("keywords")) + " to learn more about each warning." - ) - console.log("To ignore, add " + chalk.cyan("// eslint-disable-next-line") + " to the line before.\n") + 'To ignore, add ' + + chalk.cyan('// eslint-disable-next-line') + + ' to the line before.\n' + ); } else { - console.log(chalk.green("Compiled successfully.\n")) + console.log(chalk.green('Compiled successfully.\n')); } - console.log("File sizes after gzip:\n") + console.log('File sizes after gzip:\n'); printFileSizesAfterBuild( stats, previousFileSizes, paths.appBuild, WARN_AFTER_BUNDLE_GZIP_SIZE, WARN_AFTER_CHUNK_GZIP_SIZE - ) - console.log() - - const appPackage = require(paths.appPackageJson) - const publicUrl = paths.publicUrlOrPath - const publicPath = config.output.publicPath - const buildFolder = path.relative(process.cwd(), paths.appBuild) - printHostingInstructions(appPackage, publicUrl, publicPath, buildFolder, useYarn) + ); + console.log(); + + const appPackage = require(paths.appPackageJson); + const publicUrl = paths.publicUrlOrPath; + const publicPath = config.output.publicPath; + const buildFolder = path.relative(process.cwd(), paths.appBuild); + printHostingInstructions( + appPackage, + publicUrl, + publicPath, + buildFolder, + useYarn + ); }, - (err) => { - const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === "true" + err => { + const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true'; if (tscCompileOnError) { console.log( chalk.yellow( - "Compiled with the following type errors (you may want to check these before deploying your app):\n" + 'Compiled with the following type errors (you may want to check these before deploying your app):\n' ) - ) - printBuildError(err) + ); + printBuildError(err); } else { - console.log(chalk.red("Failed to compile.\n")) - printBuildError(err) - process.exit(1) + console.log(chalk.red('Failed to compile.\n')); + printBuildError(err); + process.exit(1); } } ) - .catch((err) => { + .catch(err => { if (err && err.message) { - console.log(err.message) + console.log(err.message); } - process.exit(1) - }) + process.exit(1); + }); // Create the production build and print the deployment instructions. function build(previousFileSizes) { - console.log("Creating an optimized production build...") + console.log('Creating an optimized production build...'); - const compiler = webpack(config) + const compiler = webpack(config); return new Promise((resolve, reject) => { compiler.run((err, stats) => { - let messages + let messages; if (err) { if (!err.message) { - return reject(err) + return reject(err); } - let errMessage = err.message + let errMessage = err.message; // Add additional information for postcss errors - if (Object.prototype.hasOwnProperty.call(err, "postcssNode")) { - errMessage += "\nCompileError: Begins at CSS selector " + err["postcssNode"].selector + if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) { + errMessage += + '\nCompileError: Begins at CSS selector ' + + err['postcssNode'].selector; } messages = formatWebpackMessages({ errors: [errMessage], warnings: [], - }) + }); } else { - messages = formatWebpackMessages(stats.toJson({ all: false, warnings: true, errors: true })) + messages = formatWebpackMessages( + stats.toJson({ all: false, warnings: true, errors: true }) + ); } if (messages.errors.length) { // Only keep the first error. Others are often indicative // of the same problem, but confuse the reader with noise. if (messages.errors.length > 1) { - messages.errors.length = 1 + messages.errors.length = 1; } - return reject(new Error(messages.errors.join("\n\n"))) + return reject(new Error(messages.errors.join('\n\n'))); } if ( process.env.CI && - (typeof process.env.CI !== "string" || process.env.CI.toLowerCase() !== "false") && + (typeof process.env.CI !== 'string' || + process.env.CI.toLowerCase() !== 'false') && messages.warnings.length ) { - console.log( - chalk.yellow( - "\nTreating warnings as errors because process.env.CI = true.\n" + "Most CI servers set it automatically.\n" - ) - ) - return reject(new Error(messages.warnings.join("\n\n"))) + // Ignore sourcemap warnings in CI builds. See #8227 for more info. + const filteredWarnings = messages.warnings.filter( + w => !/Failed to parse source map/.test(w) + ); + if (filteredWarnings.length) { + console.log( + chalk.yellow( + '\nTreating warnings as errors because process.env.CI = true.\n' + + 'Most CI servers set it automatically.\n' + ) + ); + return reject(new Error(filteredWarnings.join('\n\n'))); + } } const resolveArgs = { stats, previousFileSizes, warnings: messages.warnings, - } + }; if (writeStatsJson) { return bfj - .write(paths.appBuild + "/bundle-stats.json", stats.toJson()) + .write(paths.appBuild + '/bundle-stats.json', stats.toJson()) .then(() => resolve(resolveArgs)) - .catch((error) => reject(new Error(error))) + .catch(error => reject(new Error(error))); } - return resolve(resolveArgs) - }) - }) + return resolve(resolveArgs); + }); + }); } function copyPublicFolder() { fs.copySync(paths.appPublic, paths.appBuild, { dereference: true, - filter: (file) => file !== paths.appHtml, - }) + filter: file => file !== paths.appHtml, + }); } diff --git a/scripts/start.js b/scripts/start.js index e9c33b842..41047988f 100644 --- a/scripts/start.js +++ b/scripts/start.js @@ -1,133 +1,154 @@ +'use strict'; + // Do this as the first thing so that any code reading it knows the right env. -process.env.BABEL_ENV = "development" -process.env.NODE_ENV = "development" +process.env.BABEL_ENV = 'development'; +process.env.NODE_ENV = 'development'; // Makes the script crash on unhandled rejections instead of silently // ignoring them. In the future, promise rejections that are not handled will // terminate the Node.js process with a non-zero exit code. -process.on("unhandledRejection", (err) => { - throw err -}) +process.on('unhandledRejection', err => { + throw err; +}); // Ensure environment variables are read. -require("../config/env") +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 semver = require("semver") -const paths = require("../config/paths") -const configFactory = require("../config/webpack.config") -const createDevServerConfig = require("../config/webpackDevServer.config") -const getClientEnvironment = require("../config/env") -const react = require(require.resolve("react", { paths: [paths.appPath] })) +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 semver = require('semver'); +const paths = require('../config/paths'); +const configFactory = require('../config/webpack.config'); +const createDevServerConfig = require('../config/webpackDevServer.config'); +const getClientEnvironment = require('../config/env'); +const react = require(require.resolve('react', { paths: [paths.appPath] })); -const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1)) -const useYarn = fs.existsSync(paths.yarnLockFile) -const isInteractive = process.stdout.isTTY +const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1)); +const useYarn = fs.existsSync(paths.yarnLockFile); +const isInteractive = process.stdout.isTTY; // Warn and crash if required files are missing if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { - process.exit(1) + process.exit(1); } // Tools like Cloud9 rely on this. -const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000 -const HOST = process.env.HOST || "0.0.0.0" +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://cra.link/advanced-config")}`) - 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://cra.link/advanced-config')}` + ); + console.log(); } // We require that you explicitly set browsers and do not fall back to // browserslist defaults. -const { checkBrowsers } = require("react-dev-utils/browsersHelper") +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) + return choosePort(HOST, DEFAULT_PORT); }) - .then((port) => { + .then(port => { if (port == null) { // We have not found a port. - return + return; } - const config = configFactory("development") - const protocol = process.env.HTTPS === "true" ? "https" : "http" - const appName = require(paths.appPackageJson).name + 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 useTypeScript = fs.existsSync(paths.appTsConfig); + const urls = prepareUrls( + protocol, + HOST, + port, + paths.publicUrlOrPath.slice(0, -1) + ); // Create a webpack compiler that is configured with custom messages. - //todo: replace with original webpack compiler - // const compiler = webpack(config); const compiler = createCompiler({ appName, config, - devSocket, urls, useYarn, useTypeScript, - tscCompileOnError, webpack, - }) + }); // Load proxy config - const proxySetting = require(paths.appPackageJson).proxy - const proxyConfig = prepareProxy(proxySetting, paths.appPublic, paths.publicUrlOrPath) + const proxySetting = require(paths.appPackageJson).proxy; + const proxyConfig = prepareProxy( + proxySetting, + paths.appPublic, + paths.publicUrlOrPath + ); // Serve webpack assets generated by the compiler over a web server. - const serverConfig = createDevServerConfig(proxyConfig, urls.lanUrlForConfig) - const devServer = new WebpackDevServer({...serverConfig.devServer}, compiler) + const serverConfig = { + ...createDevServerConfig(proxyConfig, urls.lanUrlForConfig), + host: HOST, + port, + }; + const devServer = new WebpackDevServer(serverConfig, compiler); // Launch WebpackDevServer. - devServer.listen(port, HOST, (err) => { - if (err) { - return console.log(err) - } + devServer.startCallback(() => { if (isInteractive) { - clearConsole() + clearConsole(); } - if (env.raw.FAST_REFRESH && semver.lt(react.version, "16.10.0")) { - console.log(chalk.yellow(`Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.`)) + if (env.raw.FAST_REFRESH && semver.lt(react.version, '16.10.0')) { + console.log( + chalk.yellow( + `Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.` + ) + ); } - console.log(chalk.cyan("Starting the development server...\n")) - openBrowser(urls.localUrlForBrowser) - }) - ;["SIGINT", "SIGTERM"].forEach(function (sig) { + 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() - }) - }) + devServer.close(); + process.exit(); + }); + }); - if (process.env.CI !== "true") { + if (process.env.CI !== 'true') { // Gracefully exit when stdin ends - process.stdin.on("end", function () { - devServer.close() - process.exit() - }) + process.stdin.on('end', function () { + devServer.close(); + process.exit(); + }); } }) - .catch((err) => { + .catch(err => { if (err && err.message) { - console.log(err.message) + console.log(err.message); } - process.exit(1) - }) + process.exit(1); + }); diff --git a/scripts/test.js b/scripts/test.js index 0df2b03b6..a38c855c5 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -1,45 +1,52 @@ +'use strict'; + // Do this as the first thing so that any code reading it knows the right env. -process.env.BABEL_ENV = "test" -process.env.NODE_ENV = "test" -process.env.PUBLIC_URL = "" +process.env.BABEL_ENV = 'test'; +process.env.NODE_ENV = 'test'; +process.env.PUBLIC_URL = ''; // Makes the script crash on unhandled rejections instead of silently // ignoring them. In the future, promise rejections that are not handled will // terminate the Node.js process with a non-zero exit code. -process.on("unhandledRejection", (err) => { - throw err -}) +process.on('unhandledRejection', err => { + throw err; +}); // Ensure environment variables are read. -require("../config/env") +require('../config/env'); -const jest = require("jest") -const execSync = require("child_process").execSync -let argv = process.argv.slice(2) +const jest = require('jest'); +const execSync = require('child_process').execSync; +let argv = process.argv.slice(2); function isInGitRepository() { try { - execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" }) - return true + execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' }); + return true; } catch (e) { - return false + return false; } } function isInMercurialRepository() { try { - execSync("hg --cwd . root", { stdio: "ignore" }) - return true + execSync('hg --cwd . root', { stdio: 'ignore' }); + return true; } catch (e) { - return false + return false; } } // Watch unless on CI or explicitly running all tests -if (!process.env.CI && argv.indexOf("--watchAll") === -1 && argv.indexOf("--watchAll=false") === -1) { +if ( + !process.env.CI && + argv.indexOf('--watchAll') === -1 && + argv.indexOf('--watchAll=false') === -1 +) { // https://github.com/facebook/create-react-app/issues/5210 - const hasSourceControl = isInGitRepository() || isInMercurialRepository() - argv.push(hasSourceControl ? "--watch" : "--watchAll") + const hasSourceControl = isInGitRepository() || isInMercurialRepository(); + argv.push(hasSourceControl ? '--watch' : '--watchAll'); } -jest.run(argv) + +jest.run(argv); From cf6cc9a6edb6d136b1a94b49e3705438157d536b Mon Sep 17 00:00:00 2001 From: Martin Man Date: Mon, 30 Sep 2024 16:21:01 +0200 Subject: [PATCH 02/10] sync with CRA --- config/webpack.config.js | 598 +++++++++++++++++------------- config/webpackDevServer.config.js | 224 +++++------ 2 files changed, 455 insertions(+), 367 deletions(-) diff --git a/config/webpack.config.js b/config/webpack.config.js index 8436e6e33..d0353fd84 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -1,115 +1,175 @@ -const fs = require("fs") -const path = require("path") -const webpack = require("webpack") -const resolve = require("resolve") -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 CssMinimizerPlugin = require("css-minimizer-webpack-plugin") -const { WebpackManifestPlugin } = require("webpack-manifest-plugin") -const InterpolateHtmlPlugin = require("react-dev-utils/InterpolateHtmlPlugin") -const WorkboxWebpackPlugin = require("workbox-webpack-plugin") -const ModuleScopePlugin = require("react-dev-utils/ModuleScopePlugin") -const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent") -const ESLintPlugin = require("eslint-webpack-plugin") -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 ReactRefreshWebpackPlugin = require("@pmmmwh/react-refresh-webpack-plugin") -const CompressionPlugin = require("compression-webpack-plugin"); +'use strict'; -const appPackageJson = require(paths.appPackageJson) +const fs = require('fs'); +const path = require('path'); +const webpack = require('webpack'); +const resolve = require('resolve'); +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 CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); +const { WebpackManifestPlugin } = require('webpack-manifest-plugin'); +const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); +const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); +const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); +const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent'); +const ESLintPlugin = require('eslint-webpack-plugin'); +const paths = require('./paths'); +const modules = require('./modules'); +const getClientEnvironment = require('./env'); +const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin'); +const ForkTsCheckerWebpackPlugin = + process.env.TSC_COMPILE_ON_ERROR === 'true' + ? require('react-dev-utils/ForkTsCheckerWarningWebpackPlugin') + : require('react-dev-utils/ForkTsCheckerWebpackPlugin'); +const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); + +const createEnvironmentHash = require('./webpack/persistentCache/createEnvironmentHash'); // Source maps are resource heavy and can cause out of memory issue for large source files. -const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== "false" +const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; -const webpackDevClientEntry = require.resolve("react-dev-utils/webpackHotDevClient") -const reactRefreshOverlayEntry = require.resolve("react-dev-utils/refreshOverlayInterop") +const reactRefreshRuntimeEntry = require.resolve('react-refresh/runtime'); +const reactRefreshWebpackPluginRuntimeEntry = require.resolve( + '@pmmmwh/react-refresh-webpack-plugin' +); +const babelRuntimeEntry = require.resolve('babel-preset-react-app'); +const babelRuntimeEntryHelpers = require.resolve( + '@babel/runtime/helpers/esm/assertThisInitialized', + { paths: [babelRuntimeEntry] } +); +const babelRuntimeRegenerator = require.resolve('@babel/runtime/regenerator', { + paths: [babelRuntimeEntry], +}); // Some apps do not need the benefits of saving a web request, so not inlining the chunk // makes for a smoother build process. -const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== "false" +const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false'; -const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === "true" -const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === "true" +const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === 'true'; +const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === 'true'; -const imageInlineSizeLimit = parseInt(process.env.IMAGE_INLINE_SIZE_LIMIT || "10000") +const imageInlineSizeLimit = parseInt( + process.env.IMAGE_INLINE_SIZE_LIMIT || '10000' +); // Check if TypeScript is setup -const useTypeScript = fs.existsSync(paths.appTsConfig) +const useTypeScript = fs.existsSync(paths.appTsConfig); + +// Check if Tailwind config exists +const useTailwind = fs.existsSync( + path.join(paths.appPath, 'tailwind.config.js') +); // Get the path to the uncompiled service worker (if it exists). -const swSrc = paths.swSrc +const swSrc = paths.swSrc; // style files regexes -const cssRegex = /\.css$/ -const cssModuleRegex = /\.module\.css$/ -const sassRegex = /\.(scss|sass)$/ -const sassModuleRegex = /\.module\.(scss|sass)$/ +const cssRegex = /\.css$/; +const cssModuleRegex = /\.module\.css$/; +const sassRegex = /\.(scss|sass)$/; +const sassModuleRegex = /\.module\.(scss|sass)$/; const hasJsxRuntime = (() => { - if (process.env.DISABLE_NEW_JSX_TRANSFORM === "true") { - return false + if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') { + return false; } try { - require.resolve("react/jsx-runtime") - return true + require.resolve('react/jsx-runtime'); + return true; } catch (e) { - return false + return false; } -})() +})(); // This is the production and development configuration. // It is focused on developer experience, fast rebuilds, and a minimal bundle. module.exports = function (webpackEnv) { - const isEnvDevelopment = webpackEnv === "development" - const isEnvProduction = webpackEnv === "production" + 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") + const isEnvProductionProfile = + isEnvProduction && process.argv.includes('--profile'); // We will provide `paths.publicUrlOrPath` 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. // Get environment variables to inject into our app. - const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1)) + const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1)); - const shouldUseReactRefresh = env.raw.FAST_REFRESH + const shouldUseReactRefresh = env.raw.FAST_REFRESH; // common function to get style loaders const getStyleLoaders = (cssOptions, preProcessor) => { const loaders = [ - isEnvDevelopment && require.resolve("style-loader"), + 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: "../../" } : {}, + options: paths.publicUrlOrPath.startsWith('.') + ? { publicPath: '../../' } + : {}, }, { - loader: require.resolve("css-loader"), + loader: require.resolve('css-loader'), options: cssOptions, }, { // 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"), + loader: require.resolve('postcss-loader'), options: { + postcssOptions: { + // Necessary for external CSS imports to work + // https://github.com/facebook/create-react-app/issues/2677 + ident: 'postcss', + config: false, + plugins: !useTailwind + ? [ + 'postcss-flexbugs-fixes', + [ + 'postcss-preset-env', + { + autoprefixer: { + flexbox: 'no-2009', + }, + stage: 3, + }, + ], + // Adds PostCSS Normalize as the reset css with default options, + // so that it honors browserslist config in package.json + // which in turn let's users customize the target behavior as per their needs. + 'postcss-normalize', + ] + : [ + 'tailwindcss', + 'postcss-flexbugs-fixes', + [ + 'postcss-preset-env', + { + autoprefixer: { + flexbox: 'no-2009', + }, + stage: 3, + }, + ], + ], + }, sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, }, }, - ].filter(Boolean) + ].filter(Boolean); if (preProcessor) { loaders.push( { - loader: require.resolve("resolve-url-loader"), + loader: require.resolve('resolve-url-loader'), options: { sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, root: paths.appSrc, @@ -121,77 +181,69 @@ module.exports = function (webpackEnv) { sourceMap: true, }, } - ) + ); } - return loaders - } + return loaders; + }; return { - mode: isEnvProduction ? "production" : isEnvDevelopment && "development", - target: "web", + target: ['browserslist'], + // Webpack noise constrained to errors and warnings + stats: 'errors-warnings', + mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development', // Stop compilation early in production bail: isEnvProduction, devtool: isEnvProduction ? shouldUseSourceMap - ? "source-map" + ? 'source-map' : false - : isEnvDevelopment && "cheap-module-source-map", + : isEnvDevelopment && 'cheap-module-source-map', // These are the "entry points" to our application. // This means they will be the "root" imports that are included in JS bundle. - entry: - isEnvDevelopment && !shouldUseReactRefresh - ? [ - // Include an alternative client for WebpackDevServer. A client's job is to - // connect to WebpackDevServer by a socket and get notified about changes. - // When you save a file, the client will either apply hot updates (in case - // of CSS changes), or refresh the page (in case of JS changes). When you - // make a syntax error, this client will display a syntax error overlay. - // Note: instead of the default WebpackDevServer client, we use a custom one - // to bring better experience for Create React App users. You can replace - // the line below with these two lines if you prefer the stock client: - // - // require.resolve('webpack-dev-server/client') + '?/', - // require.resolve('webpack/hot/dev-server'), - // - // When using the experimental react-refresh integration, - // the webpack plugin takes care of injecting the dev client for us. - webpackDevClientEntry, - // Finally, this is your app's code: - paths.appIndexJs, - // We include the app code last so that if there is a runtime error during - // initialization, it doesn't blow up the WebpackDevServer client, and - // changing JS code would still trigger a refresh. - ] - : paths.appIndexJs, + entry: paths.appIndexJs, output: { // The build folder. - path: isEnvProduction ? paths.appBuild : undefined, + path: paths.appBuild, // Add /* filename */ comments to generated require()s in the output. pathinfo: isEnvDevelopment, // There will be one main bundle, and one file per asynchronous chunk. // In development, it does not produce real files. filename: isEnvProduction - ? "static/js/[name].[contenthash:8].js" - : isEnvDevelopment && "static/js/[name].bundle.js", + ? 'static/js/[name].[contenthash:8].js' + : isEnvDevelopment && 'static/js/bundle.js', // There are also additional JS chunk files if you use code splitting. chunkFilename: isEnvProduction - ? "static/js/[name].[contenthash:8].chunk.js" - : isEnvDevelopment && "static/js/[name].chunk.js", - assetModuleFilename: "static/media/[name].[hash][ext]", + ? 'static/js/[name].[contenthash:8].chunk.js' + : isEnvDevelopment && 'static/js/[name].chunk.js', + assetModuleFilename: 'static/media/[name].[hash][ext]', // 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. // We inferred the "public path" (such as / or /my-project) from 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, "/")), - // Prevents conflicts when multiple webpack runtimes (from different apps) - // are used on the same page. - chunkLoadingGlobal: `webpackJsonp${appPackageJson.name}`, - // this defaults to 'window', but by setting it to 'this' then - // module chunks which are built will work in web workers as well. - globalObject: "this", + ? info => + path + .relative(paths.appSrc, info.absoluteResourcePath) + .replace(/\\/g, '/') + : isEnvDevelopment && + (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')), + }, + cache: { + type: 'filesystem', + version: createEnvironmentHash(env.raw), + cacheDirectory: paths.appWebpackCache, + store: 'pack', + buildDependencies: { + defaultWebpack: ['webpack/lib/'], + config: [__filename], + tsconfig: [paths.appTsConfig, paths.appJsConfig].filter(f => + fs.existsSync(f) + ), + }, + }, + infrastructureLogging: { + level: 'none', }, optimization: { minimize: isEnvProduction, @@ -235,42 +287,19 @@ module.exports = function (webpackEnv) { ascii_only: true, }, }, - sourceMap: shouldUseSourceMap, - }), - new CssMinimizerPlugin({ - minimizerOptions: { - // Other options are available https://webpack.js.org/plugins/css-minimizer-webpack-plugin/#minify - preset: [ - "default", - { - discardComments: { removeAll: true }, - minifyFontValues: { removeQuotes: false }, - }, - ], - }, }), + // This is only used in production mode + new CssMinimizerPlugin(), ], - // 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: false, - }, - // Keep the runtime chunk separated to enable long term caching - // https://twitter.com/wSokra/status/969679223278505985 - // https://github.com/facebook/create-react-app/issues/5358 - runtimeChunk: { - name: (entrypoint) => `runtime-${entrypoint.name}`, - }, }, 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", paths.appNodeModules].concat(modules.additionalModulePaths || []), - preferRelative: true, + modules: ['node_modules', paths.appNodeModules].concat( + modules.additionalModulePaths || [] + ), // 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: @@ -278,96 +307,106 @@ module.exports = function (webpackEnv) { // `web` extension prefixes have been added for better support // for React Native Web. extensions: paths.moduleFileExtensions - .map((ext) => `.${ext}`) - .filter((ext) => useTypeScript || !ext.includes("ts")), + .map(ext => `.${ext}`) + .filter(ext => useTypeScript || !ext.includes('ts')), alias: { // 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", + 'react-native': 'react-native-web', // Allows for better profiling with ReactDevTools ...(isEnvProductionProfile && { - "react-dom$": "react-dom/profiling", - "scheduler/tracing": "scheduler/tracing-profiling", + 'react-dom$': 'react-dom/profiling', + 'scheduler/tracing': 'scheduler/tracing-profiling', }), ...(modules.webpackAliases || {}), }, plugins: [ - // Adds support for installing with Plug'n'Play, leading to faster installs and adding - // guards against forgotten dependencies and such. // 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, reactRefreshOverlayEntry]), + new ModuleScopePlugin(paths.appSrc, [ + paths.appPackageJson, + reactRefreshRuntimeEntry, + reactRefreshWebpackPluginRuntimeEntry, + babelRuntimeEntry, + babelRuntimeEntryHelpers, + babelRuntimeRegenerator, + ]), ], + // VICTRON: BEGIN // 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. fallback: { - module: false, - dgram: false, - fs: false, - http2: false, - net: false, - tls: false, - child_process: false, - stream: require.resolve("stream-browserify"), + 'process/browser': require.resolve('process/browser'), buffer: require.resolve("buffer"), - url: require.resolve("url/") - }, + } + // VICTRON: END }, module: { strictExportPresence: true, rules: [ - // Disable require.ensure as it's not a standard language feature. - { - // The new webpack5 parser rules applies both for Js and Json, but Json module - // hasn't `requireEnsure` parameter in config, therefore we apply this rule only for Js - test: /\.[cm]?js$/, - parser: { requireEnsure: false }, + // Handle node_modules packages that contain sourcemaps + shouldUseSourceMap && { + enforce: 'pre', + exclude: /@babel(?:\/|\\{1,2})runtime/, + test: /\.(js|mjs|jsx|ts|tsx|css)$/, + loader: require.resolve('source-map-loader'), }, { // "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: [ - { - test: /\.svg$/, - // inline SVGs only for Marine2 app - include: [paths.appSrc + "/app/Marine2"], - use: [ - { loader: require.resolve("babel-loader") }, - { - loader: require.resolve("react-svg-loader"), - options: { jsx: true }, - }, - ], - }, - // TODO: Merge this config once `image/avif` is in the mime-db // https://github.com/jshttp/mime-db { test: [/\.avif$/], - loader: require.resolve("url-loader"), - options: { - limit: imageInlineSizeLimit, - mimetype: "image/avif", - name: "static/media/[name].[hash:8].[ext]", + type: 'asset', + mimetype: 'image/avif', + parser: { + dataUrlCondition: { + maxSize: imageInlineSizeLimit, + }, }, }, - { - test: /\.(woff|woff2|eot|ttf|otf)$/i, - type: "asset/resource", - }, // "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, - name: "static/media/[name].[hash:8].[ext]", + type: 'asset', + parser: { + dataUrlCondition: { + maxSize: imageInlineSizeLimit, + }, + }, + }, + { + test: /\.svg$/, + use: [ + { + loader: require.resolve('@svgr/webpack'), + options: { + prettier: false, + svgo: false, + svgoConfig: { + plugins: [{ removeViewBox: false }], + }, + titleProp: true, + ref: true, + }, + }, + { + loader: require.resolve('file-loader'), + options: { + name: 'static/media/[name].[hash].[ext]', + }, + }, + ], + issuer: { + and: [/\.(ts|tsx|js|jsx|md|mdx)$/], }, }, // Process application JS with Babel. @@ -375,32 +414,25 @@ module.exports = function (webpackEnv) { { test: /\.(js|mjs|jsx|ts|tsx)$/, include: paths.appSrc, - loader: require.resolve("babel-loader"), + loader: require.resolve('babel-loader'), options: { - customize: require.resolve("babel-preset-react-app/webpack-overrides"), + customize: require.resolve( + 'babel-preset-react-app/webpack-overrides' + ), presets: [ [ - require.resolve("babel-preset-react-app"), + require.resolve('babel-preset-react-app'), { - runtime: hasJsxRuntime ? "automatic" : "classic", + runtime: hasJsxRuntime ? 'automatic' : 'classic', }, ], ], - + plugins: [ - [ - require.resolve("babel-plugin-named-asset-import"), - { - loaderMap: { - svg: { - ReactComponent: "@svgr/webpack?-svgo,+titleProp,+ref![path]", - }, - }, - }, - ], - isEnvDevelopment && shouldUseReactRefresh && require.resolve("react-refresh/babel"), + isEnvDevelopment && + shouldUseReactRefresh && + require.resolve('react-refresh/babel'), ].filter(Boolean), - // 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. @@ -415,16 +447,21 @@ module.exports = function (webpackEnv) { { test: /\.(js|mjs)$/, exclude: /@babel(?:\/|\\{1,2})runtime/, - loader: require.resolve("babel-loader"), + loader: require.resolve('babel-loader'), options: { babelrc: false, configFile: false, compact: false, - presets: [[require.resolve("babel-preset-react-app/dependencies"), { helpers: true }]], + 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. @@ -444,7 +481,12 @@ module.exports = function (webpackEnv) { exclude: cssModuleRegex, use: getStyleLoaders({ importLoaders: 1, - sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, + sourceMap: isEnvProduction + ? shouldUseSourceMap + : isEnvDevelopment, + modules: { + mode: 'icss', + }, }), // Don't consider CSS imports dead code even if the // containing package claims to have no side effects. @@ -458,8 +500,11 @@ module.exports = function (webpackEnv) { test: cssModuleRegex, use: getStyleLoaders({ importLoaders: 1, - sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, + sourceMap: isEnvProduction + ? shouldUseSourceMap + : isEnvDevelopment, modules: { + mode: 'local', getLocalIdent: getCSSModuleLocalIdent, }, }), @@ -473,9 +518,14 @@ module.exports = function (webpackEnv) { use: getStyleLoaders( { importLoaders: 3, - sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, + sourceMap: isEnvProduction + ? shouldUseSourceMap + : isEnvDevelopment, + modules: { + mode: 'icss', + }, }, - "sass-loader" + 'sass-loader' ), // Don't consider CSS imports dead code even if the // containing package claims to have no side effects. @@ -490,12 +540,15 @@ module.exports = function (webpackEnv) { use: getStyleLoaders( { importLoaders: 3, - sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment, + sourceMap: isEnvProduction + ? shouldUseSourceMap + : isEnvDevelopment, modules: { + mode: 'local', getLocalIdent: getCSSModuleLocalIdent, }, }, - "sass-loader" + 'sass-loader' ), }, // "file" loader makes sure those assets get served by WebpackDevServer. @@ -504,21 +557,18 @@ module.exports = function (webpackEnv) { // 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$/], - options: { - name: "static/media/[name].[hash:8].[ext]", - }, + exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/], + type: 'asset/resource', }, // ** STOP ** Are you adding a new loader? // Make sure to add the new loader(s) before the "file" loader. ], }, - ], + ].filter(Boolean), }, plugins: [ // Generates an `index.html` file with the