diff --git a/src/api/dev.ts b/src/api/dev.ts index c996b06..754a2b2 100644 --- a/src/api/dev.ts +++ b/src/api/dev.ts @@ -200,6 +200,8 @@ class Watcher extends EventEmitter { handle_result: (result: CompileResult) => { deferred.promise.then(() => { const restart = () => { + this.emit('restart'); + log = ''; this.crashed = false; diff --git a/src/cli/dev.ts b/src/cli/dev.ts index 2e990fb..8668f8c 100644 --- a/src/cli/dev.ts +++ b/src/cli/dev.ts @@ -6,15 +6,25 @@ import prettyMs from 'pretty-ms'; import { dev as _dev } from '../api/dev'; import * as events from '../api/interfaces'; -export function dev(opts: { port: number, open: boolean, bundler?: string }) { +function boxed_output() { const screen = blessed.screen({ smartCSR: true }); - const status_box = blessed.box({ + const status_box = blessed.log({ width: '100%', height: '50%', - scrollable: true + scrollable: true, + style: { + scrollbar: { + bg: 'black' + } + }, + scrollbar: {}, + input: true, + mouse: true, + keys: true, + scrollOnInput: false }); let mouse_is_down = false; @@ -39,13 +49,24 @@ export function dev(opts: { port: number, open: boolean, bundler?: string }) { screen.on('mouseup', data => { mouse_is_down = false; + dragging = false; }); - const log_box = blessed.box({ + const log_box = blessed.log({ bottom: '0', width: '100%', height: '50%', - scrollable: true + scrollable: true, + style: { + scrollbar: { + bg: 'black' + } + }, + scrollbar: {}, + input: true, + mouse: true, + keys: true, + scrollOnInput: false }); const divider = blessed.line({ @@ -56,86 +77,137 @@ export function dev(opts: { port: number, open: boolean, bundler?: string }) { screen.append(status_box); screen.append(log_box); screen.append(divider); - screen.render(); screen.key(['escape', 'q', 'C-c'], function(ch, key) { return process.exit(0); }); - const append_log = data => { - log_box.setContent(log_box.getContent() + data); + const append_log = (data: Buffer | string) => { + log_box.setContent(log_box.content + data); screen.render(); }; - const append_status = line => { - const lines = status_box.getLines(); - status_box.insertLine(lines.length, line); + const append_status = (data: Buffer | string) => { + status_box.setContent(status_box.content + data); screen.render(); }; + return { + stdout: append_log, + + stderr: append_log, + + clear_logs: () => { + log_box.setContent(`${colors.inverse(` server log • ${new Date().toISOString()}\n`)} \n`); + screen.render(); + }, + + log: (line: string) => { + append_status(line + '\n'); + }, + + append: append_status, + + clear: () => { + status_box.setContent(`${colors.inverse(` build log • ${new Date().toISOString()}\n`)} \n`); + screen.render(); + } + }; +} + +function streamed_output() { + return { + stdout: process.stdout.write.bind(process.stdout), + + stderr: process.stderr.write.bind(process.stderr), + + clear_logs: () => {}, + + log: (line: string) => { + console.log(line); + }, + + append: (data: Buffer | string) => { + process.stdout.write(data); + }, + + clear: () => {} + }; +} + +export function dev(opts: { port: number, open: boolean, bundler?: string, stream: boolean }) { + const output = opts.stream + ? streamed_output() + : boxed_output(); + + output.clear(); + try { const watcher = _dev(opts); let first = true; watcher.on('ready', (event: events.ReadyEvent) => { + output.log(colors.bold.cyan(`> Listening on http://localhost:${event.port}`)); + if (first) { - append_status(colors.bold.cyan(`> Listening on http://localhost:${event.port}`)); if (opts.open) child_process.exec(`open http://localhost:${event.port}`); first = false; } - - event.process.stdout.on('data', append_log); - event.process.stderr.on('data', append_log); }); + watcher.on('restart', output.clear_logs); + watcher.on('stdout', output.stdout); + watcher.on('stderr', output.stderr); + watcher.on('invalid', (event: events.InvalidEvent) => { const changed = event.changed.map(filename => path.relative(process.cwd(), filename)).join(', '); - status_box.setContent(''); - append_status(`\n${colors.bold.cyan(changed)} changed. rebuilding...`); + + output.clear(); + output.log(`${colors.bold.cyan(changed)} changed. rebuilding...`); }); watcher.on('error', (event: events.ErrorEvent) => { - append_status(colors.red(`✗ ${event.type}`)); - append_status(colors.red(event.message)); + output.log(colors.red(`✗ ${event.type}`)); + output.log(colors.red(event.message)); }); watcher.on('fatal', (event: events.FatalEvent) => { - append_status(colors.bold.red(`> ${event.message}`)); - if (event.log) append_status(event.log); + output.log(colors.bold.red(`> ${event.message}`)); + if (event.log) output.log(event.log); }); watcher.on('build', (event: events.BuildEvent) => { if (event.errors.length) { - append_status(colors.bold.red(`✗ ${event.type}`)); + output.log(colors.bold.red(`✗ ${event.type}`)); event.errors.filter(e => !e.duplicate).forEach(error => { - if (error.file) append_status(colors.bold(error.file)); - append_status(error.message); + if (error.file) output.log(colors.bold(error.file)); + output.log(error.message); }); const hidden = event.errors.filter(e => e.duplicate).length; if (hidden > 0) { - append_status(`${hidden} duplicate ${hidden === 1 ? 'error' : 'errors'} hidden\n`); + output.log(`${hidden} duplicate ${hidden === 1 ? 'error' : 'errors'} hidden\n`); } } else if (event.warnings.length) { - append_status(colors.bold.yellow(`• ${event.type}`)); + output.log(colors.bold.yellow(`• ${event.type}`)); event.warnings.filter(e => !e.duplicate).forEach(warning => { - if (warning.file) append_status(colors.bold(warning.file)); - append_status(warning.message); + if (warning.file) output.log(colors.bold(warning.file)); + output.log(warning.message); }); const hidden = event.warnings.filter(e => e.duplicate).length; if (hidden > 0) { - append_status(`${hidden} duplicate ${hidden === 1 ? 'warning' : 'warnings'} hidden\n`); + output.log(`${hidden} duplicate ${hidden === 1 ? 'warning' : 'warnings'} hidden\n`); } } else { - append_status(`${colors.bold.green(`✔ ${event.type}`)} ${colors.gray(`(${prettyMs(event.duration)})`)}`); + output.log(`${colors.bold.green(`✔ ${event.type}`)} ${colors.gray(`(${prettyMs(event.duration)})`)}`); } }); } catch (err) { - append_status(colors.bold.red(`> ${err.message}`)); + output.log(colors.bold.red(`> ${err.message}`)); process.exit(1); } } \ No newline at end of file