Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(npm/react): allow custom webpack config path for react-scripts plugin #16644

Merged
merged 2 commits into from
May 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 4 additions & 80 deletions npm/react/plugins/react-scripts/findReactScriptsWebpackConfig.js
Original file line number Diff line number Diff line change
@@ -1,93 +1,17 @@
// @ts-check
const debug = require('debug')('@cypress/react')
const tryLoadWebpackConfig = require('../utils/tryLoadWebpackConfig')
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')

/**
* Finds the ModuleScopePlugin plugin and adds given folder
* to that list. This allows react-scripts to import folders
* outside of the default "/src" folder.
* WARNING modifies the input webpack options argument.
* @see https://github.com/bahmutov/cypress-react-unit-test/issues/289
* @param {string} folderName Folder to add, should be absolute
*/
function allowModuleSourceInPlace (folderName, webpackOptions) {
if (!folderName) {
return
}

if (!webpackOptions.resolve) {
return
}

if (!Array.isArray(webpackOptions.resolve.plugins)) {
return
}

const moduleSourcePlugin = webpackOptions.resolve.plugins.find((plugin) => {
return Array.isArray(plugin.appSrcs)
})

if (!moduleSourcePlugin) {
debug('cannot find module source plugin')

return
}

debug('found module source plugin %o', moduleSourcePlugin)
if (!moduleSourcePlugin.appSrcs.includes(folderName)) {
moduleSourcePlugin.appSrcs.push(folderName)
debug('added folder %s to allowed sources', folderName)
}
}

const addCypressToWebpackEslintRulesInPlace = (webpackOptions) => {
const globalsToAdd = ['cy', 'Cypress', 'before', 'after', 'context']

if (webpackOptions.module && Array.isArray(webpackOptions.module.rules)) {
const modulePre = webpackOptions.module.rules.find(
(rule) => rule.enforce === 'pre',
)

if (modulePre && Array.isArray(modulePre.use)) {
debug('found Pre block %o', modulePre)

const useEslintLoader = modulePre.use.find(
(use) => use.loader && use.loader.includes('eslint-loader'),
)

if (useEslintLoader) {
debug('found useEslintLoader %o', useEslintLoader)

if (useEslintLoader.options) {
if (Array.isArray(useEslintLoader.options.globals)) {
debug(
'adding cy to existing globals %o',
useEslintLoader.options.globals,
)

useEslintLoader.options.globals.push(...globalsToAdd)
} else {
debug('setting new list of globals with cy and Cypress')
useEslintLoader.options.globals = globalsToAdd
}

debug('updated globals %o', useEslintLoader.options.globals)
} else {
debug('eslint loader does not have options ⚠️')
}
}
}
}
}

module.exports = function findReactScriptsWebpackConfig (config) {
module.exports = function findReactScriptsWebpackConfig (config, { webpackConfigPath }) {
// this is required because
// 1) we use our own HMR and we don't need react-refresh transpiling overhead
// 2) it doesn't work with process.env=test @see https://github.com/cypress-io/cypress-realworld-app/pull/832
process.env.FAST_REFRESH = 'false'
const webpackConfig = tryLoadWebpackConfig('react-scripts/config/webpack.config')
const webpackConfig = tryLoadWebpackConfig(webpackConfigPath)

if (!webpackConfig) {
throw new Error('⚠️ Could not find Webpack options for react-scripts. Make sure that you have react-scripts module available.')
Expand Down
16 changes: 14 additions & 2 deletions npm/react/plugins/react-scripts/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
const { startDevServer } = require('@cypress/webpack-dev-server')
const findReactScriptsWebpackConfig = require('./findReactScriptsWebpackConfig')

module.exports = (on, config) => {
module.exports = (
on,
config, {
webpackConfigPath,
} = {
webpackConfigPath: 'react-scripts/config/webpack.config',
},
) => {
on('dev-server:start', async (options) => {
return startDevServer({ options, webpackConfig: findReactScriptsWebpackConfig(config) })
return startDevServer({
options,
webpackConfig: findReactScriptsWebpackConfig(config, {
webpackConfigPath,
}),
})
})

config.env.reactDevtools = true
Expand Down
43 changes: 43 additions & 0 deletions npm/react/plugins/utils/eslint-helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const debug = require('debug')('@cypress/react')

const addCypressToWebpackEslintRulesInPlace = (webpackOptions) => {
const globalsToAdd = ['cy', 'Cypress', 'before', 'after', 'context']

if (webpackOptions.module && Array.isArray(webpackOptions.module.rules)) {
const modulePre = webpackOptions.module.rules.find(
(rule) => rule.enforce === 'pre',
)

if (modulePre && Array.isArray(modulePre.use)) {
debug('found Pre block %o', modulePre)

const useEslintLoader = modulePre.use.find(
(use) => use.loader && use.loader.includes('eslint-loader'),
)

if (useEslintLoader) {
debug('found useEslintLoader %o', useEslintLoader)

if (useEslintLoader.options) {
if (Array.isArray(useEslintLoader.options.globals)) {
debug(
'adding cy to existing globals %o',
useEslintLoader.options.globals,
)

useEslintLoader.options.globals.push(...globalsToAdd)
} else {
debug('setting new list of globals with cy and Cypress')
useEslintLoader.options.globals = globalsToAdd
}

debug('updated globals %o', useEslintLoader.options.globals)
} else {
debug('eslint loader does not have options ⚠️')
}
}
}
}
}

module.exports = { addCypressToWebpackEslintRulesInPlace }
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,42 @@ function addImageRedirect (webpackOptions) {
}
}

module.exports = { addImageRedirect }
/**
* Finds the ModuleScopePlugin plugin and adds given folder
* to that list. This allows react-scripts to import folders
* outside of the default "/src" folder.
* WARNING modifies the input webpack options argument.
* @see https://github.com/bahmutov/cypress-react-unit-test/issues/289
* @param {string} folderName Folder to add, should be absolute
*/
function allowModuleSourceInPlace (folderName, webpackOptions) {
if (!folderName) {
return
}

if (!webpackOptions.resolve) {
return
}

if (!Array.isArray(webpackOptions.resolve.plugins)) {
return
}

const moduleSourcePlugin = webpackOptions.resolve.plugins.find((plugin) => {
return Array.isArray(plugin.appSrcs)
})

if (!moduleSourcePlugin) {
debug('cannot find module source plugin')

return
}

debug('found module source plugin %o', moduleSourcePlugin)
if (!moduleSourcePlugin.appSrcs.includes(folderName)) {
moduleSourcePlugin.appSrcs.push(folderName)
debug('added folder %s to allowed sources', folderName)
}
}

module.exports = { addImageRedirect, allowModuleSourceInPlace }