forked from netlify/cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtraffic-mesh.js
More file actions
202 lines (170 loc) · 5.45 KB
/
Copy pathtraffic-mesh.js
File metadata and controls
202 lines (170 loc) · 5.45 KB
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
190
191
192
193
194
195
196
197
198
199
200
201
202
const { EOL } = require('os')
const path = require('path')
const process = require('process')
const rl = require('readline')
const { getTrafficMeshForLocalSystem } = require('@netlify/traffic-mesh-agent')
const execa = require('execa')
const waitPort = require('wait-port')
const { getPathInProject } = require('../lib/settings')
const { clearSpinner, startSpinner, stopSpinner } = require('../lib/spinner')
const { createDeferred } = require('./deferred')
const { NETLIFYDEVLOG, NETLIFYDEVERR, NETLIFYDEVWARN } = require('./logo')
const EDGE_HANDLERS_BUNDLER_CLI_PATH = path.resolve(require.resolve('@netlify/plugin-edge-handlers'), '..', 'cli.js')
const startForwardProxy = async ({ port, frameworkPort, functionsPort, publishDir, log, debug, locationDb }) => {
const args = [
'start',
'local',
'--port',
port,
'--forward-proxy',
`http://localhost:${frameworkPort}`,
'--watch',
publishDir,
'--bundler',
EDGE_HANDLERS_BUNDLER_CLI_PATH,
'--log-file',
getPathInProject(['logs', 'traffic-mesh.log']),
'--progress',
]
if (functionsPort) {
args.push('--local-services-uri', `http://localhost:${functionsPort}`)
}
if (debug) {
args.push('--debug')
}
if (locationDb) {
args.push('--geo', locationDb)
}
const { subprocess } = runProcess({ log, args })
const { forwarder, firstBundleReady } = forwardMessagesToLog({ log, subprocess })
subprocess.on('close', process.exit)
subprocess.on('SIGINT', process.exit)
subprocess.on('SIGTERM', process.exit)
;['SIGINT', 'SIGTERM', 'SIGQUIT', 'SIGHUP', 'exit'].forEach((signal) => {
process.on(signal, () => {
forwarder.close()
const sig = signal === 'exit' ? 'SIGTERM' : signal
subprocess.kill(sig, {
forceKillAfterTimeout: PROXY_EXIT_TIMEOUT,
})
})
})
// Wait until the first traffic-mesh bundle is ready
//
// In case of errors, this delays the startup process until the errors are fixed
await firstBundleReady
try {
const open = await waitPort({ port, output: 'silent', timeout: PROXY_READY_TIMEOUT })
if (!open) {
throw new Error(`Timed out waiting for forward proxy to be ready on port '${port}'`)
}
return `http://localhost:${port}`
} catch (error) {
log(`${NETLIFYDEVERR}`, error)
}
}
const forwardMessagesToLog = ({ log, subprocess }) => {
const { promise: firstBundleReady, reject: firstBundleReject, resolve: firstBundleResolve } = createDeferred()
let currentId = null
let lastError = null
let lastWasSuccess = false
let spinner = null
const reset = () => {
currentId = null
spinner = null
}
const forwarder = rl.createInterface({
input: subprocess.stderr,
})
forwarder
.on('line', (line) => {
let data
try {
data = JSON.parse(line.trim())
} catch (error) {
log(`${NETLIFYDEVERR} Cannot parse log line as JSON: ${line.trim()}${EOL}${EOL}${error}`)
return
}
const { error, id, type } = data
switch (type) {
case 'bundle:start':
currentId = id
if (!spinner) {
spinner = startSpinner({ text: 'Processing request remaps, header rules and Edge Handlers...' })
}
break
case 'bundle:success':
if (currentId !== id) {
return
}
// Clear spinner if there already is a log line indicating success above,
// instead of appending another line of "Yay, your project was bundled!"
if (lastWasSuccess) {
clearSpinner({ spinner })
} else {
stopSpinner({
spinner,
error: false,
text: 'Done processing request remaps, header rules and Edge Handlers',
})
}
lastWasSuccess = true
lastError = null
firstBundleResolve()
reset()
break
case 'bundle:fail': {
if (currentId !== id) {
return
}
// Only show the error if it's new
const errorMsg = (error && error.msg) || 'Failed processing request remaps, header rules or Edge Handlers'
if (errorMsg === lastError) {
clearSpinner({ spinner })
} else {
stopSpinner({
spinner,
error: true,
text: errorMsg,
})
log(
`${NETLIFYDEVLOG} Change any project configuration file (netlify.toml, _headers, _redirects) or any Edge Handlers file to trigger a re-bundle`,
)
}
lastWasSuccess = false
lastError = errorMsg
reset()
break
}
default:
log(`${NETLIFYDEVWARN} Unknown mesh-forward event '${type}'`)
break
}
})
.on('close', () => {
if (spinner) {
// Hide the spinner
spinner.stop()
}
reset()
})
.on('error', (err) => {
stopSpinner({
spinner,
error: true,
text: `${NETLIFYDEVERR} An error occured while bundling processing the messages from mesh-forward: ${err}`,
})
firstBundleReject(err)
reset()
})
return { forwarder, firstBundleReady }
}
// 30 seconds
const PROXY_READY_TIMEOUT = 3e4
// 2 seconds
const PROXY_EXIT_TIMEOUT = 2e3
const runProcess = ({ args }) => {
const subprocess = execa(getTrafficMeshForLocalSystem(), args, { stdio: ['inherit', 'inherit', 'pipe'] })
return { subprocess }
}
module.exports = { runProcess, startForwardProxy }