/
next-start.ts
150 lines (133 loc) · 3.7 KB
/
next-start.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
import path from 'path'
import fs from 'fs-extra'
import { NextInstance } from './base'
import { spawn, SpawnOptions } from 'child_process'
export class NextStartInstance extends NextInstance {
private _buildId: string
private _cliOutput: string
private spawnOpts: SpawnOptions
public get buildId() {
return this._buildId
}
public get cliOutput() {
return this._cliOutput
}
public async setup() {
await super.createTestDir()
}
private handleStdio = (childProcess) => {
childProcess.stdout.on('data', (chunk) => {
const msg = chunk.toString()
process.stdout.write(chunk)
this._cliOutput += msg
this.emit('stdout', [msg])
})
childProcess.stderr.on('data', (chunk) => {
const msg = chunk.toString()
process.stderr.write(chunk)
this._cliOutput += msg
this.emit('stderr', [msg])
})
}
public async start() {
if (this.childProcess) {
throw new Error('next already started')
}
this.spawnOpts = {
cwd: this.testDir,
stdio: ['ignore', 'pipe', 'pipe'],
shell: false,
env: {
...process.env,
NODE_ENV: '' as any,
__NEXT_TEST_MODE: '1',
__NEXT_RAND_PORT: '1',
},
}
let buildArgs = ['yarn', 'next', 'build']
let startArgs = ['yarn', 'next', 'start']
if (this.buildCommand) {
buildArgs = this.buildCommand.split(' ')
}
if (this.startCommand) {
startArgs = this.startCommand.split(' ')
}
await new Promise<void>((resolve, reject) => {
console.log('running', buildArgs.join(' '))
this.childProcess = spawn(
buildArgs[0],
buildArgs.slice(1),
this.spawnOpts
)
this.handleStdio(this.childProcess)
this.childProcess.on('exit', (code, signal) => {
if (code || signal)
reject(
new Error(`next build failed with code/signal ${code || signal}`)
)
else resolve()
})
})
this._buildId = (
await fs.readFile(
path.join(
this.testDir,
this.nextConfig?.distDir || '.next',
'BUILD_ID'
),
'utf8'
)
).trim()
console.log('running', startArgs.join(' '))
await new Promise<void>((resolve) => {
this.childProcess = spawn(
startArgs[0],
startArgs.slice(1),
this.spawnOpts
)
this.handleStdio(this.childProcess)
this.childProcess.on('close', (code, signal) => {
if (this.isStopping) return
if (code || signal) {
throw new Error(
`next start exited unexpectedly with code/signal ${code || signal}`
)
}
})
const readyCb = (msg) => {
if (msg.includes('started server on') && msg.includes('url:')) {
this._url = msg.split('url: ').pop().trim()
this._parsedUrl = new URL(this._url)
this.off('stdout', readyCb)
resolve()
}
}
this.on('stdout', readyCb)
})
}
public async export() {
return new Promise((resolve) => {
const curOutput = this._cliOutput.length
const exportArgs = ['yarn', 'next', 'export']
if (this.childProcess) {
throw new Error(
`can not run export while server is running, use next.stop() first`
)
}
console.log('running', exportArgs.join(' '))
this.childProcess = spawn(
exportArgs[0],
exportArgs.slice(1),
this.spawnOpts
)
this.handleStdio(this.childProcess)
this.childProcess.on('exit', (code, signal) => {
this.childProcess = undefined
resolve({
exitCode: signal || code,
cliOutput: this.cliOutput.slice(curOutput),
})
})
})
}
}