-
Notifications
You must be signed in to change notification settings - Fork 1
/
dev.js
122 lines (102 loc) · 3.22 KB
/
dev.js
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
/**
* Run `firebase serve` and `webpack-dev-server` together, to get
* firebase routes and function emulation alongside webpack-dev-server's
* hot loading.
*/
import { spawn as spawnChild } from 'child_process'
import chalk from 'chalk'
import debug from 'debug'
import thru from 'through2'
import stripAnsi from 'strip-ansi'
const colors = [chalk.cyan, chalk.green, chalk.magenta, chalk.blue]
let nextColorIdx = 0
let resolveFirebaseUrl = null
let hasStartedListening = false
const firebaseUrl = new Promise(r => (resolveFirebaseUrl = r)).then(stripAnsi)
// `firebase serve` prints a line that looks like this
// when it starts listening:
const localServerRe = /(?:Local server|Server listening): (.*)/
const env = vars => ({
env: Object.assign(vars, process.env),
})
const forceColor = env({ FORCE_COLOR: 3 })
// Build functions
spawn('🤖 build library', 'npm', ['run', 'watch'], forceColor).toConsole()
// Run `firebase serve`
const firebaseServe = spawn(
'🔥 firebase serve',
'npx',
['firebase', 'serve', '--only', 'hosting'],
forceColor
)
// Scan through its output...
firebaseServe.stdout
// We're looking for the line where firebase serve tells us
// what URL it's accessible at (usually localhost:5000, but it
// may have to pick another port).
.pipe(
thru(function (line, enc, cb) {
// To avoid confusion, we don't pass through stdout until
// after the "listening" line has passed.
cb(null, hasStartedListening ? line : debug('dev')('%s', line))
// Is this the line telling us where the local server is?
const match = line.toString().match(localServerRe)
// If so, resolve the firebase url promise with the url,
// and start passing through lines.
if (match) {
resolveFirebaseUrl(match[1].trim())
hasStartedListening = true
}
})
)
.pipe(process.stdout)
// Pipe stderr from firebase serve to our stderr.
firebaseServe.stderr.pipe(process.stderr)
// Once `firebase serve` has started, launch webpack-dev-server
// with the appropriate environment variables set.
firebaseUrl.then(FIREBASE_SERVE_URL =>
spawn(
'🌍 webpack dev server',
'npx',
['webpack', 'serve', '--hot', '--open', ...process.argv.slice(2)],
{
env: Object.assign(
{
NODE_ENV: 'development',
FORCE_COLOR: 3,
FIREBASE_SERVE_URL,
},
process.env
),
}
).toConsole()
)
function spawn(label, ...args) {
const child = spawnChild(...args)
const labeler = labelerFor(label)
child.on('exit', status => {
if (status) {
error(labeler('exited with status', status))
}
process.exit(status)
})
child.stdout = child.stdout.pipe(labelWith(labeler))
child.stderr = child.stderr.pipe(labelWith(labeler))
child.toConsole = () => {
child.stdout.pipe(process.stdout)
child.stderr.pipe(process.stderr)
}
return child
}
function labelerFor(label, color = colors[nextColorIdx++ % colors.length]) {
const coloredLabel = color(`[ ${label} ]\t`)
return (...message) => `${coloredLabel}${message.join(' ')}`
}
function labelWith(labeler) {
return thru(function (line, enc, cb) {
cb(null, labeler(line))
})
}
function error(...args) {
console.error(chalk.bold.red(...args))
}