-
Notifications
You must be signed in to change notification settings - Fork 3.3k
/
Copy pathdevServer.ts
189 lines (154 loc) · 5.76 KB
/
devServer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/// <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
// @ts-expect-error
const { port } = result.server?.options
if (result.version === 3) {
const srv = result.server.listen(port || 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>
const thirdPartyDefinitionPrefixes = {
// matches @org/cypress-ct-*
namespacedPrefixRe: /^@.+?\/cypress-ct-.+/,
globalPrefix: 'cypress-ct-',
}
export function isThirdPartyDefinition (framework: string) {
return framework.startsWith(thirdPartyDefinitionPrefixes.globalPrefix) ||
thirdPartyDefinitionPrefixes.namespacedPrefixRe.test(framework)
}
async function getPreset (devServerConfig: WebpackDevServerConfig): Promise<Optional<PresetHandlerResult, 'frameworkConfig'>> {
const defaultWebpackModules = () => ({ sourceWebpackModulesResult: sourceDefaultWebpackDependencies(devServerConfig) })
// Third party library (eg solid-js, lit, etc)
if (devServerConfig.framework && isThirdPartyDefinition(devServerConfig.framework)) {
return defaultWebpackModules()
}
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 defaultWebpackModules()
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