@arve.knudsen/bankai
Version:
The easiest way to compile JavaScript, HTML and CSS
211 lines (175 loc) • 4.94 kB
JavaScript
var ansi = require('ansi-escape-sequences')
var differ = require('ansi-diff')
var pretty = require('prettier-bytes')
var keypress = require('keypress')
var strip = require('strip-ansi')
var nanoraf = require('nanoraf')
var StartDelimiter = '|'
var EndDelimiter = '|'
var Filled = '█'
var Empty = '░'
var NewlineMatcher = /\n/g
var files = [
'assets',
'documents',
'scripts',
'styles',
'manifest',
'service-worker'
]
module.exports = render
function render (state) {
var diff = differ()
var render = nanoraf(onrender, raf)
process.stdout.on('resize', onresize)
if (process.stdin.isTTY) {
keypress(process.stdin)
process.stdin.setRawMode(true)
process.stdin.resume()
process.stdin.on('keypress', onkeypress)
}
return render
function onrender () {
process.stdout.write(diff.update(view(state)))
}
function onresize () {
diff.resize({ width: process.stdout.columns, height: process.stdout.rows })
diff.update('')
render()
}
function onkeypress (ch, key) {
if (key && key.ctrl && key.name === 'c') {
process.exit()
} else if (ch === '1') {
// TODO: Switch to the main view.
render()
} else if (ch === '2') {
// TODO: Switch to the log view.
render()
} else if (ch === '3') {
// TODO: Switch to the stats view.
render()
}
}
}
function view (state) {
if (state.error) {
return '\x1b[33c' + state.error
}
var str = '\x1b[33c'
str += header(state)
str += '\n\n'
str += files.reduce(function (str, filename) {
var file = state.files[filename]
if (!file) return ''
var status = file.status
var count = status === 'done' ? String(state.count[filename]) : ''
if (status === 'done') status = clr(status, 'green')
// Make it so singular words aren't pluralized.
var name = count === '1'
? file.name.replace(/s$/, '')
: file.name
str += clr(padLeft(count, 3), 'yellow') + ' '
str += padRight(clr(name, 'green'), 14)
var size = pretty(file.size).replace(' ', '')
str += pad(7 - size.length) + clr(size, 'magenta') + ' '
str += clr(file.timestamp, 'cyan') + ' '
str += progress(file.progress, 10) + ' '
str += status
return str + '\n'
}, '') + '\n'
var ssrState = 'Pending'
if (state.ssr) {
ssrState = state.ssr.success
? 'Success'
: `Skipped - ${state.ssr.error.message} ${state.ssr.error.stack.split('\n')[1].trim()}`
}
str += 'Server Side Rendering: ' + ssrState + '\n'
str += footer(state)
// pad string with newlines to ensure old rendered lines are cleared
var padLines = Math.max(process.stdout.rows - str.match(NewlineMatcher).length - 1, 0)
str += '\n'.repeat(padLines)
return str
}
// header
function header (state) {
var SSEStatus = state.sse > 0
? clr('connected', 'green')
: state.port
? 'ready'
: clr('starting', 'yellow')
var httpStatus = state.port
? clr(clr('https://localhost:' + state.port, 'underline'), 'blue')
: clr('starting', 'yellow')
var left = `HTTP: ${httpStatus}`
var right = `Live Reload: ${SSEStatus}`
return spaceBetween(left, right)
}
// footer
function footer (state) {
var size = Object.keys(state.files).reduce(function (num, filename) {
var file = state.files[filename]
return num + file.size
}, 0)
var bottomLeft = tabBar(1, 0)
var totalSize = clr(pretty(size).replace(' ', ''), 'magenta')
var bottomRight = `Total size: ${totalSize}`
return spaceBetween(bottomLeft, bottomRight)
}
function tabBar (count, curr) {
var str = ''
var tmp
for (var i = 0; i < count; i++) {
tmp = String(i + 1)
if (curr === i) {
tmp = `[ ${tmp} ]`
} else {
tmp = clr(tmp, 'gray')
if (i !== 0) tmp = ' ' + tmp
if (i !== count) tmp = tmp + ' '
}
str += tmp
}
return str
}
function clr (text, color) {
return process.stdout.isTTY ? ansi.format(text, color) : text
}
function padLeft (str, num, char) {
str = String(str)
var len = strip(str).length
return pad(num - len, char) + str
}
function padRight (str, num, char) {
str = String(str)
var len = strip(str).length
return str + pad(num - len, char)
}
function pad (len, char) {
char = String(char === undefined ? ' ' : char)
var res = ''
while (res.length < len) res += char
return res
}
function progress (curr, max) {
var filledLength = Math.floor((curr / 100) * max)
var emptyLength = max - filledLength
var i = 1 + filledLength
var j = i + emptyLength
var str = StartDelimiter
while (str.length < i) str += Filled
while (str.length < j) str += Empty
str += EndDelimiter
return str
}
function raf (cb) {
setTimeout(cb, 50)
}
function spaceBetween (left, right) {
var len = process.stdout.columns - strip(left).length - strip(right).length
var space = ''
for (var i = 0; i < len; i++) {
space += ' '
}
return left + space + right
}