From 415a7b149aaac37ae605dc1a11007bad29187dc5 Mon Sep 17 00:00:00 2001 From: Zachary Williams Date: Tue, 21 Dec 2021 13:18:03 -0600 Subject: [PATCH] feat: support create-react-app v5 (#19434) --- .../findReactScriptsWebpackConfig.js | 7 +++ .../plugins/react-scripts/reactScriptsFive.ts | 61 +++++++++++++++++++ npm/react/tsconfig.json | 3 +- 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 npm/react/plugins/react-scripts/reactScriptsFive.ts diff --git a/npm/react/plugins/react-scripts/findReactScriptsWebpackConfig.js b/npm/react/plugins/react-scripts/findReactScriptsWebpackConfig.js index 40a85619da4d..48c8402f7051 100644 --- a/npm/react/plugins/react-scripts/findReactScriptsWebpackConfig.js +++ b/npm/react/plugins/react-scripts/findReactScriptsWebpackConfig.js @@ -5,6 +5,7 @@ const { allowModuleSourceInPlace } = require('../utils/webpack-helpers') const { addCypressToWebpackEslintRulesInPlace } = require('../utils/eslint-helpers') const { getTranspileFolders } = require('../utils/get-transpile-folders') const { addFolderToBabelLoaderTranspileInPlace } = require('../utils/babel-helpers') +const { reactScriptsFiveModifications, isReactScripts5 } = require('../../dist/react-scripts/reactScriptsFive') module.exports = function findReactScriptsWebpackConfig (config, { webpackConfigPath, @@ -28,6 +29,12 @@ module.exports = function findReactScriptsWebpackConfig (config, { addFolderToBabelLoaderTranspileInPlace(cypressFolder, webpackConfig) }) + if (isReactScripts5) { + debug('Modifying configuration for react-scripts@5') + + reactScriptsFiveModifications(webpackConfig) + } + debug('resolved webpack config: %o', webpackConfig) return webpackConfig diff --git a/npm/react/plugins/react-scripts/reactScriptsFive.ts b/npm/react/plugins/react-scripts/reactScriptsFive.ts new file mode 100644 index 000000000000..141577833b9c --- /dev/null +++ b/npm/react/plugins/react-scripts/reactScriptsFive.ts @@ -0,0 +1,61 @@ +import _debug from 'debug' +import type { Configuration } from 'webpack' +import reactScriptsPackageJson from 'react-scripts/package.json' + +const debug = _debug('@cypress/react:react-scripts') + +type DefinePlugin = + | { definitions: Record> } + | undefined; +type ESLintWebpackPlugin = + | { options: { baseConfig?: { globals?: Record } } } + | undefined; + +export function reactScriptsFiveModifications (webpackConfig: Configuration) { + // React-Scripts sets the webpack target to ["browserslist"] which tells + // webpack to target the browsers found within the browserslist config + // depending on the environment (process.env.NODE_ENV). Since we set + // process.env.NODE_ENV = "test", webpack is unable to find any browsers and errors. + // We set BROWSERSLIST_ENV = "development" to override the default NODE_ENV search of browsers. + if (!process.env.BROWSERSLIST_ENV) { + process.env.BROWSERSLIST_ENV = 'development' + } + + // We use the "development" configuration of the react-scripts webpack config. + // There is a conflict when settings process.env.NODE_ENV = "test" since DefinePlugin + // uses the "development" configuration and expects process.env.NODE_ENV = "development". + const definePlugin: DefinePlugin = webpackConfig.plugins?.find( + (plugin) => plugin.constructor.name === 'DefinePlugin' + ) as unknown as DefinePlugin + + if (definePlugin) { + const processEnv = definePlugin.definitions['process.env'] + + processEnv.NODE_ENV = JSON.stringify('development') + + debug('Found "DefinePlugin", modified "process.env" definition %o', processEnv) + } + + // React-Scripts v5 no longers uses a loader to configure eslint, so we add globals + // to the plugin. + const eslintPlugin = webpackConfig.plugins?.find( + (plugin) => plugin.constructor.name === 'ESLintWebpackPlugin' + ) as unknown as ESLintWebpackPlugin + + if (eslintPlugin) { + const cypressGlobals = ['cy', 'Cypress', 'before', 'after', 'context'] + .reduce((acc, global) => ({ ...acc, [global]: 'writable' }), {}) + + eslintPlugin.options.baseConfig = { + ...eslintPlugin.options.baseConfig, + globals: { + ...eslintPlugin.options.baseConfig?.globals, + ...cypressGlobals, + }, + } + + debug('Found ESLintWebpackPlugin, modified eslint config %o', eslintPlugin.options.baseConfig) + } +} + +export const isReactScripts5 = Number(reactScriptsPackageJson.version[0]) >= 5 diff --git a/npm/react/tsconfig.json b/npm/react/tsconfig.json index fe178acffb2a..65957f64aafb 100644 --- a/npm/react/tsconfig.json +++ b/npm/react/tsconfig.json @@ -19,7 +19,8 @@ "cypress" ] /* Type declaration files to be included in compilation. */, "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, - "esModuleInterop": true + "esModuleInterop": true, + "resolveJsonModule": true }, "include": ["src/**/*.ts"], }