packager: minimize terminal.log() work

Reviewed By: davidaurelio

Differential Revision: D4650441

fbshipit-source-id: 2de2c8e5bea29179fd04ef8db67ac385b3f0a06b
This commit is contained in:
Jean Lauliac 2017-03-03 11:04:55 -08:00 committed by Facebook Github Bot
parent e596217d99
commit 3e9dedf1ac
3 changed files with 57 additions and 24 deletions

View File

@ -14,7 +14,8 @@ jest
.dontMock('json-stable-stringify') .dontMock('json-stable-stringify')
.dontMock('../TransformCache') .dontMock('../TransformCache')
.dontMock('../toFixedHex') .dontMock('../toFixedHex')
.dontMock('left-pad'); .dontMock('left-pad')
.dontMock('lodash/throttle');
const imurmurhash = require('imurmurhash'); const imurmurhash = require('imurmurhash');

View File

@ -9,7 +9,7 @@
'use strict'; 'use strict';
jest.dontMock('../terminal'); jest.dontMock('../terminal').dontMock('lodash/throttle');
jest.mock('readline', () => ({ jest.mock('readline', () => ({
moveCursor: (stream, dx, dy) => { moveCursor: (stream, dx, dy) => {
@ -64,8 +64,16 @@ describe('terminal', () => {
terminal.log('foo %s', 'smth'); terminal.log('foo %s', 'smth');
terminal.status('status'); terminal.status('status');
terminal.log('bar'); terminal.log('bar');
expect(stream.buffer.join('').trim()) jest.runAllTimers();
.toEqual('foo smth bar'); expect(stream.buffer.join('').trim()).toEqual('foo smth bar');
});
it('print status', () => {
const {stream, terminal} = prepare(true);
terminal.log('foo');
terminal.status('status');
jest.runAllTimers();
expect(stream.buffer.join('').trim()).toEqual('foo status');
}); });
it('updates status when logging, single line', () => { it('updates status when logging, single line', () => {
@ -74,8 +82,11 @@ describe('terminal', () => {
terminal.status('status'); terminal.status('status');
terminal.status('status2'); terminal.status('status2');
terminal.log('bar'); terminal.log('bar');
expect(stream.buffer.join('').trim()) jest.runAllTimers();
.toEqual('foo bar status2'); expect(stream.buffer.join('').trim()).toEqual('foo bar status2');
terminal.log('beep');
jest.runAllTimers();
expect(stream.buffer.join('').trim()).toEqual('foo bar beep status2');
}); });
it('updates status when logging, multi-line', () => { it('updates status when logging, multi-line', () => {
@ -83,6 +94,7 @@ describe('terminal', () => {
terminal.log('foo'); terminal.log('foo');
terminal.status('status\nanother'); terminal.status('status\nanother');
terminal.log('bar'); terminal.log('bar');
jest.runAllTimers();
expect(stream.buffer.join('').trim()) expect(stream.buffer.join('').trim())
.toEqual('foo bar status another'); .toEqual('foo bar status another');
}); });
@ -93,8 +105,8 @@ describe('terminal', () => {
terminal.status('status'); terminal.status('status');
terminal.persistStatus(); terminal.persistStatus();
terminal.log('bar'); terminal.log('bar');
expect(stream.buffer.join('').trim()) jest.runAllTimers();
.toEqual('foo status bar'); expect(stream.buffer.join('').trim()).toEqual('foo status bar');
}); });
}); });

View File

@ -12,6 +12,7 @@
'use strict'; 'use strict';
const readline = require('readline'); const readline = require('readline');
const throttle = require('lodash/throttle');
const tty = require('tty'); const tty = require('tty');
const util = require('util'); const util = require('util');
@ -70,28 +71,44 @@ function chunkString(str: string, size: number): Array<string> {
*/ */
class Terminal { class Terminal {
_logLines: Array<string>;
_nextStatusStr: string;
_scheduleUpdate: () => void;
_statusStr: string; _statusStr: string;
_stream: net$Socket; _stream: net$Socket;
constructor(stream: net$Socket) { constructor(stream: net$Socket) {
this._stream = stream; this._logLines = [];
this._nextStatusStr = '';
this._scheduleUpdate = throttle(this._update, 0);
this._statusStr = ''; this._statusStr = '';
this._stream = stream;
} }
/** /**
* Same as status() without the formatting capabilities. We just clear and * Clear and write the new status, logging in bulk in-between. Doing this in a
* rewrite with the new status. If the stream is non-interactive we still * throttled way (in a different tick than the calls to `log()` and
* keep track of the string so that `persistStatus` works. * `status()`) prevents us from repeatedly rewriting the status in case
* `terminal.log()` is called several times.
*/ */
_setStatus(str: string): string { _update(): void {
const {_statusStr, _stream} = this; const {_statusStr, _stream} = this;
if (_statusStr !== str && _stream instanceof tty.WriteStream) { if (_statusStr === this._nextStatusStr && this._logLines.length === 0) {
clearStringBackwards(_stream, _statusStr); return;
str = chunkString(str, _stream.columns).join('\n');
_stream.write(str);
} }
this._statusStr = str; if (_stream instanceof tty.WriteStream) {
return _statusStr; clearStringBackwards(_stream, _statusStr);
}
this._logLines.forEach(line => {
_stream.write(line);
_stream.write('\n');
});
this._logLines = [];
if (_stream instanceof tty.WriteStream) {
this._nextStatusStr = chunkString(this._nextStatusStr, _stream.columns).join('\n');
_stream.write(this._nextStatusStr);
}
this._statusStr = this._nextStatusStr;
} }
/** /**
@ -102,7 +119,10 @@ class Terminal {
* file, then we don't care too much about having a progress bar. * file, then we don't care too much about having a progress bar.
*/ */
status(format: string, ...args: Array<mixed>): string { status(format: string, ...args: Array<mixed>): string {
return this._setStatus(util.format(format, ...args)); const {_nextStatusStr} = this;
this._nextStatusStr = util.format(format, ...args);
this._scheduleUpdate();
return _nextStatusStr;
} }
/** /**
@ -111,9 +131,8 @@ class Terminal {
* `console.log`. * `console.log`.
*/ */
log(format: string, ...args: Array<mixed>): void { log(format: string, ...args: Array<mixed>): void {
const oldStatus = this._setStatus(''); this._logLines.push(util.format(format, ...args));
this._stream.write(util.format(format, ...args) + '\n'); this._scheduleUpdate();
this._setStatus(oldStatus);
} }
/** /**
@ -121,7 +140,8 @@ class Terminal {
* status was the last one of a series of updates. * status was the last one of a series of updates.
*/ */
persistStatus(): void { persistStatus(): void {
return this.log(this.status('')); this.log(this._nextStatusStr);
this._nextStatusStr = '';
} }
} }