Files
cypress/npm/webpack-dev-server/src/devServer.ts
Zachary Williams f39eb1c19e fix: remove last mounted component upon subsequent mount calls (#24470)
BREAKING CHANGE: remove last mounted component upon subsequent mount calls of mount
2022-11-03 12:19:29 -05:00

169 lines
5.1 KiB
TypeScript

/// <reference types="cypress" />
import type WebpackDevServer from 'webpack-dev-server'
import type { Compiler, Configuration } from 'webpack'
import { createWebpackDevServer } from './createWebpackDevServer'
import type { AddressInfo } from 'net'
import debugLib from 'debug'
import type { Server } from 'http'
import { vueCliHandler } from './helpers/vueCliHandler'
import { nuxtHandler } from './helpers/nuxtHandler'
import { createReactAppHandler } from './helpers/createReactAppHandler'
import { nextHandler } from './helpers/nextHandler'
import { sourceDefaultWebpackDependencies, SourceRelativeWebpackResult } from './helpers/sourceRelativeWebpackModules'
import { angularHandler } from './helpers/angularHandler'
const debug = debugLib('cypress:webpack-dev-server:devServer')
export type Frameworks = Extract<Cypress.DevServerConfigOptions, { bundler: 'webpack' }>['framework']
type FrameworkConfig = {
framework?: Exclude<Frameworks, 'angular'>
} | {
framework: 'angular'
options?: {
projectConfig: Cypress.AngularDevServerProjectConfig
}
}
export type ConfigHandler =
Partial<Configuration>
| (() => Partial<Configuration> | Promise<Partial<Configuration>>)
export type WebpackDevServerConfig = {
specs: Cypress.Spec[]
cypressConfig: Cypress.PluginConfigOptions
devServerEvents: NodeJS.EventEmitter
onConfigNotFound?: (devServer: 'webpack', cwd: string, lookedIn: string[]) => void
webpackConfig?: ConfigHandler // Derived from the user's webpack config
} & FrameworkConfig
/**
* @internal
*/
type DevServerCreateResult = {
version: 3
server: Server
compiler: Compiler
} | {
version: 4
server: WebpackDevServer
compiler: Compiler
}
/**
* import { devServer } from '@cypress/webpack-dev-server'
*
* Creates & returns a WebpackDevServer for serving files related
* to Cypress Component Testing
*
* @param config
*/
export function devServer (devServerConfig: WebpackDevServerConfig): Promise<Cypress.ResolvedDevServerConfig> {
return new Promise(async (resolve, reject) => {
const result = await devServer.create(devServerConfig) as DevServerCreateResult
if (result.version === 3) {
const srv = result.server.listen(0, '127.0.0.1', () => {
const port = (srv.address() as AddressInfo).port
debug('Component testing webpack server 3 started on port %s', port)
resolve({
port,
// Close is for unit testing only. We kill this child process which will handle the closing of the server
close: (done) => {
srv.close((err) => {
if (err) {
debug('closing dev server, with error', err)
}
debug('closed dev server')
done?.(err)
})
},
})
})
return
}
result.server.start().then(() => {
if (!result.server.options.port) {
return reject(new Error(`Expected port ${result.server.options.port} to be a number`))
}
debug('Component testing webpack server 4 started on port %s', result.server.options.port)
resolve({
port: result.server.options.port as number,
// Close is for unit testing only. We kill this child process which will handle the closing of the server
close: async (done) => {
debug('closing dev server')
result.server.stop().then(() => done?.()).catch(done).finally(() => {
debug('closed dev server')
})
},
})
}).catch(reject)
})
}
export type PresetHandlerResult = { frameworkConfig: Configuration, sourceWebpackModulesResult: SourceRelativeWebpackResult }
type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>
async function getPreset (devServerConfig: WebpackDevServerConfig): Promise<Optional<PresetHandlerResult, 'frameworkConfig'>> {
switch (devServerConfig.framework) {
case 'create-react-app':
return createReactAppHandler(devServerConfig)
case 'nuxt':
return await nuxtHandler(devServerConfig)
case 'vue-cli':
return await vueCliHandler(devServerConfig)
case 'next':
return await nextHandler(devServerConfig)
case 'angular':
return await angularHandler(devServerConfig)
case 'react':
case 'vue':
case 'svelte':
case undefined:
return { sourceWebpackModulesResult: sourceDefaultWebpackDependencies(devServerConfig) }
default:
throw new Error(`Unexpected framework ${(devServerConfig as any).framework}, please visit https://on.cypress.io/component-framework-configuration to see a list of supported frameworks`)
}
}
/**
* Synchronously create the webpack server instance, without starting.
* Useful for testing
*
* @internal
*/
devServer.create = async function (devServerConfig: WebpackDevServerConfig) {
const { frameworkConfig, sourceWebpackModulesResult } = await getPreset(devServerConfig)
const { server, compiler } = await createWebpackDevServer({
devServerConfig,
frameworkConfig,
sourceWebpackModulesResult,
})
const result = {
server,
compiler,
version: sourceWebpackModulesResult.webpackDevServer.majorVersion,
}
return result
}
export default devServer