UNPKG

4.94 kBJavaScriptView Raw
1var ansi = require('ansi-escape-sequences')
2var differ = require('ansi-diff')
3var pretty = require('prettier-bytes')
4var keypress = require('keypress')
5var strip = require('strip-ansi')
6var nanoraf = require('nanoraf')
7
8var StartDelimiter = '|'
9var EndDelimiter = '|'
10var Filled = '█'
11var Empty = '░'
12var NewlineMatcher = /\n/g
13
14var files = [
15 'assets',
16 'documents',
17 'scripts',
18 'styles',
19 'manifest',
20 'service-worker'
21]
22
23module.exports = render
24
25function render (state) {
26 var diff = differ()
27
28 var render = nanoraf(onrender, raf)
29
30 process.stdout.on('resize', onresize)
31
32 if (process.stdin.isTTY) {
33 keypress(process.stdin)
34 process.stdin.setRawMode(true)
35 process.stdin.resume()
36 process.stdin.on('keypress', onkeypress)
37 }
38
39 return render
40
41 function onrender () {
42 process.stdout.write(diff.update(view(state)))
43 }
44
45 function onresize () {
46 diff.resize({width: process.stdout.columns, height: process.stdout.rows})
47 diff.update('')
48 render()
49 }
50
51 function onkeypress (ch, key) {
52 if (key && key.ctrl && key.name === 'c') {
53 process.exit()
54 } else if (ch === '1') {
55 // TODO: Switch to the main view.
56 render()
57 } else if (ch === '2') {
58 // TODO: Switch to the log view.
59 render()
60 } else if (ch === '3') {
61 // TODO: Switch to the stats view.
62 render()
63 }
64 }
65}
66
67function view (state) {
68 if (state.error) {
69 return '\x1b[33c' + state.error
70 }
71
72 var str = '\x1b[33c'
73 str += header(state)
74 str += '\n\n'
75 str += files.reduce(function (str, filename) {
76 var file = state.files[filename]
77 if (!file) return ''
78 var status = file.status
79 var count = status === 'done' ? String(state.count[filename]) : ''
80 if (status === 'done') status = clr(status, 'green')
81
82 // Make it so singular words aren't pluralized.
83 var name = count === '1'
84 ? file.name.replace(/s$/, '')
85 : file.name
86
87 str += clr(padLeft(count, 3), 'yellow') + ' '
88 str += padRight(clr(name, 'green'), 14)
89 var size = pretty(file.size).replace(' ', '')
90 str += pad(7 - size.length) + clr(size, 'magenta') + ' '
91 str += clr(file.timestamp, 'cyan') + ' '
92 str += progress(file.progress, 10) + ' '
93 str += status
94
95 return str + '\n'
96 }, '') + '\n'
97
98 var ssrState = 'Pending'
99
100 if (state.ssr) {
101 ssrState = state.ssr.success
102 ? 'Success'
103 : `Skipped - ${state.ssr.error.message} ${state.ssr.error.stack.split('\n')[1].trim()}`
104 }
105 str += 'Server Side Rendering: ' + ssrState + '\n'
106
107 str += footer(state)
108
109 // pad string with newlines to ensure old rendered lines are cleared
110 var padLines = Math.max(process.stdout.rows - str.match(NewlineMatcher).length - 1, 0)
111 str += '\n'.repeat(padLines)
112
113 return str
114}
115
116// header
117function header (state) {
118 var SSEStatus = state.sse > 0
119 ? clr('connected', 'green')
120 : state.port
121 ? 'ready'
122 : clr('starting', 'yellow')
123
124 var httpStatus = state.port
125 ? clr(clr('https://localhost:' + state.port, 'underline'), 'blue')
126 : clr('starting', 'yellow')
127
128 var left = `HTTP: ${httpStatus}`
129 var right = `Live Reload: ${SSEStatus}`
130 return spaceBetween(left, right)
131}
132
133// footer
134function footer (state) {
135 var size = Object.keys(state.files).reduce(function (num, filename) {
136 var file = state.files[filename]
137 return num + file.size
138 }, 0)
139 var bottomLeft = tabBar(1, 0)
140
141 var totalSize = clr(pretty(size).replace(' ', ''), 'magenta')
142 var bottomRight = `Total size: ${totalSize}`
143 return spaceBetween(bottomLeft, bottomRight)
144}
145
146function tabBar (count, curr) {
147 var str = ''
148 var tmp
149 for (var i = 0; i < count; i++) {
150 tmp = String(i + 1)
151 if (curr === i) {
152 tmp = `[ ${tmp} ]`
153 } else {
154 tmp = clr(tmp, 'gray')
155 if (i !== 0) tmp = ' ' + tmp
156 if (i !== count) tmp = tmp + ' '
157 }
158 str += tmp
159 }
160 return str
161}
162
163function clr (text, color) {
164 return process.stdout.isTTY ? ansi.format(text, color) : text
165}
166
167function padLeft (str, num, char) {
168 str = String(str)
169 var len = strip(str).length
170 return pad(num - len, char) + str
171}
172
173function padRight (str, num, char) {
174 str = String(str)
175 var len = strip(str).length
176 return str + pad(num - len, char)
177}
178
179function pad (len, char) {
180 char = String(char === undefined ? ' ' : char)
181 var res = ''
182 while (res.length < len) res += char
183 return res
184}
185
186function progress (curr, max) {
187 var filledLength = Math.floor((curr / 100) * max)
188 var emptyLength = max - filledLength
189 var i = 1 + filledLength
190 var j = i + emptyLength
191
192 var str = StartDelimiter
193 while (str.length < i) str += Filled
194 while (str.length < j) str += Empty
195 str += EndDelimiter
196 return str
197}
198
199function raf (cb) {
200 setTimeout(cb, 50)
201}
202
203function spaceBetween (left, right) {
204 var len = process.stdout.columns - strip(left).length - strip(right).length
205 var space = ''
206 for (var i = 0; i < len; i++) {
207 space += ' '
208 }
209 return left + space + right
210}