@@ -11,6 +11,7 @@ import { CypressEnv } from './CypressEnv'
1111import { autoBindDebug } from '../util/autoBindDebug'
1212import type { EventRegistrar } from './EventRegistrar'
1313import type { DataContext } from '../DataContext'
14+ import { DependencyToInstall , inPkgJson , WIZARD_BUNDLERS , WIZARD_DEPENDENCIES , WIZARD_FRAMEWORKS } from '@packages/scaffold-config'
1415
1516const debug = debugLib ( `cypress:lifecycle:ProjectConfigManager` )
1617
@@ -153,8 +154,84 @@ export class ProjectConfigManager {
153154 if ( this . _registeredEventsTarget && this . _testingType !== this . _registeredEventsTarget ) {
154155 this . options . refreshLifecycle ( ) . catch ( this . onLoadError )
155156 } else if ( this . _eventsIpc && ! this . _registeredEventsTarget && this . _cachedLoadConfig ) {
156- this . setupNodeEvents ( this . _cachedLoadConfig ) . catch ( this . onLoadError )
157+ this . setupNodeEvents ( this . _cachedLoadConfig )
158+ . then ( ( ) => {
159+ if ( this . _testingType === 'component' ) {
160+ this . checkDependenciesForComponentTesting ( )
161+ }
162+ } )
163+ . catch ( this . onLoadError )
164+ }
165+ }
166+
167+ checkDependenciesForComponentTesting ( ) {
168+ // if it's a function, for example, the user is created their own dev server,
169+ // and not using one of our presets. Assume they know what they are doing and
170+ // what dependencies they require.
171+ if ( typeof this . _cachedLoadConfig ?. initialConfig ?. component ?. devServer !== 'object' ) {
172+ return
173+ }
174+
175+ const devServerOptions = this . _cachedLoadConfig . initialConfig . component . devServer
176+
177+ const bundler = WIZARD_BUNDLERS . find ( ( x ) => x . type === devServerOptions . bundler )
178+
179+ // Use a map since sometimes the same dependency can appear in `bundler` and `framework`,
180+ // for example webpack appears in both `bundler: 'webpack', framework: 'react-scripts'`
181+ const unsupportedDeps = new Map < DependencyToInstall [ 'dependency' ] [ 'type' ] , DependencyToInstall > ( )
182+
183+ if ( ! bundler ) {
184+ return
185+ }
186+
187+ const result = inPkgJson ( bundler , this . options . projectRoot )
188+
189+ if ( ! result . satisfied ) {
190+ unsupportedDeps . set ( result . dependency . type , result )
191+ }
192+
193+ const isFrameworkSatisfied = ( bundler : typeof WIZARD_BUNDLERS [ number ] , framework : typeof WIZARD_FRAMEWORKS [ number ] ) => {
194+ for ( const dep of framework . dependencies ( bundler . type , this . options . projectRoot ) ) {
195+ const res = inPkgJson ( dep . dependency , this . options . projectRoot )
196+
197+ if ( ! res . satisfied ) {
198+ return false
199+ }
200+ }
201+
202+ return true
203+ }
204+
205+ const frameworks = WIZARD_FRAMEWORKS . filter ( ( x ) => x . configFramework === devServerOptions . framework )
206+
207+ const mismatchedFrameworkDeps = new Map < typeof WIZARD_DEPENDENCIES [ number ] [ 'type' ] , DependencyToInstall > ( )
208+
209+ let isSatisfied = false
210+
211+ for ( const framework of frameworks ) {
212+ if ( isFrameworkSatisfied ( bundler , framework ) ) {
213+ isSatisfied = true
214+ break
215+ } else {
216+ for ( const dep of framework . dependencies ( bundler . type , this . options . projectRoot ) ) {
217+ mismatchedFrameworkDeps . set ( dep . dependency . type , dep )
218+ }
219+ }
220+ }
221+
222+ if ( ! isSatisfied ) {
223+ for ( const dep of Array . from ( mismatchedFrameworkDeps . values ( ) ) ) {
224+ if ( ! dep . satisfied ) {
225+ unsupportedDeps . set ( dep . dependency . type , dep )
226+ }
227+ }
157228 }
229+
230+ if ( unsupportedDeps . size === 0 ) {
231+ return
232+ }
233+
234+ this . options . ctx . onWarning ( getError ( 'COMPONENT_TESTING_MISMATCHED_DEPENDENCIES' , Array . from ( unsupportedDeps . values ( ) ) ) )
158235 }
159236
160237 private async setupNodeEvents ( loadConfigReply : LoadConfigReply ) : Promise < void > {
0 commit comments