Skip to content

Commit 566a7b1

Browse files
tgriesserlmiller1990ZachJW34
authored
fix: UNIFY-1774 error if component config is not sourced for webpack/vite (#21563)
* fix: UNIFY-1774, throw error if we do not detect a webpackConfig for the user * remove dead code, rely on auto-sourcing webpack config * fix error styles, exit the devServer process when config is missing, guard against empty remoteState * Add missing webpack for todos project * Update npm/webpack-dev-server/src/makeWebpackConfig.ts Co-authored-by: Lachlan Miller <[email protected]> Co-authored-by: Zachary Williams <[email protected]>
1 parent 996823c commit 566a7b1

File tree

32 files changed

+2311
-88
lines changed

32 files changed

+2311
-88
lines changed

npm/vite-dev-server/src/devServer.ts

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export type ViteDevServerConfig = {
1010
specs: Cypress.Spec[]
1111
cypressConfig: Cypress.PluginConfigOptions
1212
devServerEvents: NodeJS.EventEmitter
13+
onConfigNotFound?: (devServer: 'vite', cwd: string, lookedIn: string[]) => void
1314
} & {
1415
framework?: typeof ALL_FRAMEWORKS[number] // Add frameworks here as we implement
1516
viteConfig?: unknown // Derived from the user's webpack

npm/vite-dev-server/src/resolveConfig.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import type { Vite } from './getVite'
1717
const debug = debugFn('cypress:vite-dev-server:resolve-config')
1818

1919
export const createViteDevServerConfig = async (config: ViteDevServerConfig, vite: Vite) => {
20-
const { specs, cypressConfig, viteConfig: viteOverrides = {} } = config
20+
const { specs, cypressConfig, viteConfig: viteOverrides } = config
2121
const root = cypressConfig.projectRoot
2222
const { default: findUp } = await importModule('find-up')
2323
const configFile = await findUp(configFiles, { cwd: root } as { cwd: string })
@@ -28,11 +28,16 @@ export const createViteDevServerConfig = async (config: ViteDevServerConfig, vit
2828
if (configFile) {
2929
debug('resolved config file at', configFile, 'using root', root)
3030
} else if (viteOverrides) {
31-
debug('Couldn\'t find a Vite config file, however we received a custom viteConfig', viteOverrides)
31+
debug(`Couldn't find a Vite config file, however we received a custom viteConfig`, viteOverrides)
3232
} else {
33-
debug(`
34-
Didn\'t resolve a Vite config AND the user didn\'t pass in a custom viteConfig.
35-
Falling back to Vite\'s defaults.`)
33+
if (config.onConfigNotFound) {
34+
config.onConfigNotFound('vite', root, configFiles)
35+
// The config process will be killed from the parent, but we want to early exit so we don't get
36+
// any additional errors related to not having a config
37+
process.exit(0)
38+
} else {
39+
throw new Error(`Your component devServer config for vite is missing a required viteConfig property, since we could not automatically detect one.\n Please add one to your ${config.cypressConfig.configFile}`)
40+
}
3641
}
3742

3843
// Vite caches its output in the .vite directory in the node_modules where vite lives.

npm/webpack-dev-server/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
"test-unit": "mocha -r ts-node/register/transpile-only --config ./test/.mocharc.js"
1919
},
2020
"dependencies": {
21+
"find-up": "6.3.0",
2122
"fs-extra": "9.1.0",
2223
"html-webpack-plugin-4": "npm:html-webpack-plugin@^4",
2324
"html-webpack-plugin-5": "npm:html-webpack-plugin@^5",
25+
"local-pkg": "0.4.1",
2426
"speed-measure-webpack-plugin": "1.4.2",
2527
"tslib": "^2.3.1",
2628
"webpack-dev-server": "^4.7.4",
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const configFiles = [
2+
'webpack.config.ts',
3+
'webpack.config.js',
4+
'webpack.config.mjs',
5+
'webpack.config.cjs',
6+
]

npm/webpack-dev-server/src/createWebpackDevServer.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export interface CreateFinalWebpackConfig {
2828
frameworkConfig?: unknown
2929
}
3030

31-
export function createWebpackDevServer (
31+
export async function createWebpackDevServer (
3232
config: CreateFinalWebpackConfig,
3333
) {
3434
const {
@@ -42,7 +42,7 @@ export function createWebpackDevServer (
4242
},
4343
} = config
4444

45-
const finalWebpackConfig = makeWebpackConfig(config)
45+
const finalWebpackConfig = await makeWebpackConfig(config)
4646
const webpackCompiler = webpack(finalWebpackConfig)
4747

4848
if (webpackDevServerMajorVersion === 4) {

npm/webpack-dev-server/src/devServer.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export type WebpackDevServerConfig = {
1919
specs: Cypress.Spec[]
2020
cypressConfig: Cypress.PluginConfigOptions
2121
devServerEvents: NodeJS.EventEmitter
22+
onConfigNotFound?: (devServer: 'webpack', cwd: string, lookedIn: string[]) => void
2223
} & {
2324
framework?: typeof ALL_FRAMEWORKS[number] // Add frameworks here as we implement
2425
webpackConfig?: unknown // Derived from the user's webpack
@@ -131,7 +132,7 @@ async function getPreset (devServerConfig: WebpackDevServerConfig): Promise<Pres
131132
devServer.create = async function (devServerConfig: WebpackDevServerConfig) {
132133
const { frameworkConfig, sourceWebpackModulesResult } = await getPreset(devServerConfig)
133134

134-
const { server, compiler } = createWebpackDevServer({
135+
const { server, compiler } = await createWebpackDevServer({
135136
devServerConfig,
136137
frameworkConfig,
137138
sourceWebpackModulesResult,

npm/webpack-dev-server/src/makeWebpackConfig.ts

+40-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { debug as debugFn } from 'debug'
22
import * as path from 'path'
33
import { merge } from 'webpack-merge'
4+
import { importModule } from 'local-pkg'
45
import type { Configuration } from 'webpack'
56
import { makeDefaultWebpackConfig } from './makeDefaultWebpackConfig'
67
import { CypressCTWebpackPlugin } from './CypressCTWebpackPlugin'
78
import type { CreateFinalWebpackConfig } from './createWebpackDevServer'
9+
import { configFiles } from './constants'
810

911
const debug = debugFn('cypress:webpack-dev-server:makeWebpackConfig')
1012

@@ -74,16 +76,12 @@ function modifyWebpackConfigForCypress (webpackConfig: Partial<Configuration>) {
7476
* Creates a webpack 4/5 compatible webpack "configuration"
7577
* to pass to the sourced webpack function
7678
*/
77-
export function makeWebpackConfig (
79+
export async function makeWebpackConfig (
7880
config: CreateFinalWebpackConfig,
7981
) {
8082
const { module: webpack } = config.sourceWebpackModulesResult.webpack
81-
const userWebpackConfig = config.devServerConfig.webpackConfig as Partial<Configuration>
83+
let userWebpackConfig = config.devServerConfig.webpackConfig as Partial<Configuration>
8284
const frameworkWebpackConfig = config.frameworkConfig as Partial<Configuration>
83-
const userAndFrameworkWebpackConfig = modifyWebpackConfigForCypress(
84-
merge(frameworkWebpackConfig ?? {}, userWebpackConfig ?? {}),
85-
)
86-
8785
const {
8886
cypressConfig: {
8987
projectRoot,
@@ -94,6 +92,42 @@ export function makeWebpackConfig (
9492
devServerEvents,
9593
} = config.devServerConfig
9694

95+
let configFile: string | undefined = undefined
96+
97+
if (!userWebpackConfig && !frameworkWebpackConfig) {
98+
debug('Not user or framework webpack config received. Trying to automatically source it')
99+
100+
const { default: findUp } = await importModule('find-up')
101+
102+
configFile = await findUp(configFiles, { cwd: projectRoot } as { cwd: string })
103+
104+
if (configFile) {
105+
debug('found webpack config %s', configFile)
106+
const sourcedConfig = await importModule(configFile)
107+
108+
debug('config contains %o', sourcedConfig)
109+
if (sourcedConfig && typeof sourcedConfig === 'object') {
110+
userWebpackConfig = sourcedConfig.default ?? sourcedConfig
111+
}
112+
}
113+
114+
if (!userWebpackConfig) {
115+
debug('could not find webpack.config!')
116+
if (config.devServerConfig?.onConfigNotFound) {
117+
config.devServerConfig.onConfigNotFound('webpack', projectRoot, configFiles)
118+
// The config process will be killed from the parent, but we want to early exit so we don't get
119+
// any additional errors related to not having a config
120+
process.exit(0)
121+
} else {
122+
throw new Error(`Your Cypress devServer config is missing a required webpackConfig property, since we could not automatically detect one.\nPlease add one to your ${config.devServerConfig.cypressConfig.configFile}`)
123+
}
124+
}
125+
}
126+
127+
const userAndFrameworkWebpackConfig = modifyWebpackConfigForCypress(
128+
merge(frameworkWebpackConfig ?? {}, userWebpackConfig ?? {}),
129+
)
130+
97131
debug(`User passed in user and framework webpack config with values %o`, userAndFrameworkWebpackConfig)
98132
debug(`New webpack entries %o`, files)
99133
debug(`Project root`, projectRoot)

npm/webpack-dev-server/test/devServer-unit.spec.ts

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('devServer', function () {
2828
const result = await devServer.create({
2929
specs: [],
3030
cypressConfig,
31+
webpackConfig: {},
3132
devServerEvents: new EventEmitter(),
3233
})
3334

@@ -49,6 +50,7 @@ describe('devServer', function () {
4950
const result = await devServer.create({
5051
specs: [],
5152
cypressConfig,
53+
webpackConfig: {},
5254
devServerEvents: new EventEmitter(),
5355
})
5456

@@ -70,6 +72,7 @@ describe('devServer', function () {
7072
const result = await devServer.create({
7173
specs: [],
7274
cypressConfig,
75+
webpackConfig: {},
7376
devServerEvents: new EventEmitter(),
7477
})
7578

packages/app/cypress/e2e/subscriptions/specChange-subscription.cy.ts

-3
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ component: {
137137
devServer: {
138138
framework: 'react',
139139
bundler: 'webpack',
140-
webpackConfig: require('./webpack.config')
141140
}
142141
},
143142
e2e: {
@@ -267,7 +266,6 @@ component: {
267266
devServer: {
268267
framework: 'react',
269268
bundler: 'webpack',
270-
webpackConfig: require('./webpack.config')
271269
}
272270
},
273271
e2e: {
@@ -377,7 +375,6 @@ component: {
377375
devServer: {
378376
framework: 'react',
379377
bundler: 'webpack',
380-
webpackConfig: require('./webpack.config')
381378
}
382379
},
383380
e2e: {

packages/data-context/src/data/ProjectConfigIpc.ts

+1
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ export class ProjectConfigIpc extends EventEmitter {
199199
})
200200

201201
this.once('setupTestingType:error', (err) => {
202+
this.onError(err)
202203
reject(err)
203204
})
204205

packages/errors/__snapshot-html__/DEV_SERVER_CONFIG_FILE_NOT_FOUND.html

+49
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/errors/src/errors.ts

+17
Original file line numberDiff line numberDiff line change
@@ -1530,6 +1530,23 @@ export const AllCypressErrors = {
15301530
https://on.cypress.io/migration-guide
15311531
`
15321532
},
1533+
1534+
DEV_SERVER_CONFIG_FILE_NOT_FOUND: (devServer: 'vite' | 'webpack', root: string, searchedFor: string[]) => {
1535+
const devServerConfigFile = `${devServer}Config`
1536+
1537+
return errTemplate`\
1538+
You are using ${fmt.highlight(devServer)} for your dev server, but a configuration file was not found. We traversed upwards from:
1539+
1540+
${fmt.highlightSecondary(root)}
1541+
1542+
looking for a file named:
1543+
1544+
${fmt.listItems(searchedFor, { prefix: ' - ' })}
1545+
1546+
Add your ${fmt.highlight(devServer)} config at one of the above paths, or import your configuration file and provide it to
1547+
the devServer config as a ${fmt.highlight(devServerConfigFile)} option.
1548+
`
1549+
},
15331550
} as const
15341551

15351552
// eslint-disable-next-line @typescript-eslint/no-unused-vars

packages/errors/test/unit/visualSnapshotErrors_spec.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1162,5 +1162,10 @@ describe('visual error templates', () => {
11621162
default: [],
11631163
}
11641164
},
1165+
DEV_SERVER_CONFIG_FILE_NOT_FOUND: () => {
1166+
return {
1167+
default: ['vite', '/dev/project', ['vite.config.js', 'vite.config.ts']],
1168+
}
1169+
},
11651170
})
11661171
})

packages/frontend-shared/windi.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const defaultConfig: FullConfig = {
4646
include: [
4747
'index.html',
4848
'**/*.{vue,html,tsx}',
49-
path.resolve(__dirname, '../frontend-shared/**/*.{vue,html,tsx,svg}'),
49+
path.resolve(__dirname, '../frontend-shared/**/*.{vue,html,tsx,svg,ts}'),
5050
path.resolve(__dirname, '../app/**/*.{vue,html,tsx,svg}'),
5151
path.resolve(__dirname, '../launchpad/**/*.{vue,html,tsx,svg}'),
5252
],

packages/graphql/schemas/schema.graphql

+1
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ enum ErrorTypeEnum {
556556
DASHBOARD_UNKNOWN_INVALID_REQUEST
557557
DEFAULT_SUPPORT_FILE_NOT_FOUND
558558
DEPRECATED_BEFORE_BROWSER_LAUNCH_ARGS
559+
DEV_SERVER_CONFIG_FILE_NOT_FOUND
559560
DUPLICATE_TASK_KEY
560561
ERROR_READING_FILE
561562
ERROR_WRITING_FILE

0 commit comments

Comments
 (0)