Skip to content

Commit

Permalink
feat(log-update): support concurrent writes to stdout/stderr on render
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Nov 9, 2018
1 parent 2e67e13 commit d51c508
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 21 deletions.
17 changes: 1 addition & 16 deletions src/reporters/fancy.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* eslint-disable no-console */
import chalk from 'chalk';
import consola from 'consola';

import { renderBar, colorize, ellipsisLeft } from '../utils/cli';
import { formatRequest } from '../utils/webpack';
Expand All @@ -12,29 +11,15 @@ const logUpdate = new LogUpdate();
let lastRender = Date.now();

export default class FancyReporter {
start() {
// TODO: remove in next major release.
// Some projects still depend on legacy versions of consola
if (typeof consola.pause === 'function') {
consola.pause();
}
}

allDone() {
logUpdate.done();

if (typeof consola.resume === 'function') {
consola.resume();
}
}

done(context) {
this._renderStates(context.statesArray);

if (context.hasErrors) {
if (typeof consola.resume === 'function') {
consola.resume();
}
logUpdate.done();
}
}

Expand Down
67 changes: 62 additions & 5 deletions src/utils/log-update.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,93 @@ import wrapAnsi from 'wrap-ansi';
export default class LogUpdate {
constructor() {
this.prevLineCount = 0;
this.listening = false;
this.extraLines = '';
this._onData = this._onData.bind(this);
this._streams = [process.stdout, process.stderr];
}

render(lines) {
this.listen();

const wrappedLines = wrapAnsi(lines, this.columns, {
trim: false,
hard: true,
wordWrap: false,
});

const earaseChars = ansiEscapes.eraseLines(this.prevLineCount);
const data =
ansiEscapes.eraseLines(this.prevLineCount) +
wrappedLines +
'\n' +
this.extraLines;

this.write(earaseChars + wrappedLines + '\n');
this.write(data);

this.prevLineCount = wrappedLines.split('\n').length + 1;
this.prevLineCount = data.split('\n').length;
}

get columns() {
return (process.stderr.columns || 80) - 2;
}

write(data) {
process.stderr.write(data, 'utf-8');
if (process.stderr.__write) {
process.stderr.__write(data, 'utf-8');
} else {
process.stderr.write(data, 'utf-8');
}
}

clear() {
this.done();
this.write(ansiEscapes.eraseLines(this.prevLineCount));
this.prevLineCount = 0;
}

done() {
this.stopListen();

this.prevLineCount = 0;
this.extraLines = '';
}

_onData(data) {
const str = String(data);
const lines = str.split('\n').length - 1;
if (lines > 0) {
this.prevLineCount += lines;
this.extraLines += data;
}
}

listen() {
if (this.listening) {
return;
}

const t = this;

for (const stream of this._streams) {
if (!stream.__write) {
stream.__write = stream.write;
stream.write = function write(data, ...args) {

This comment has been minimized.

Copy link
@phorsuedzie

phorsuedzie Jan 10, 2019

Contributor

Note: karma captures this intermediate process.stdout.write here: https://github.com/karma-runner/karma/blob/376142e282d09ae827038969414498f586ab00cd/lib/reporters/base.js#L9

Unfortunately, karma uses the replaced write after stopListen directly, which then misses __write. See: https://cmty.app/nuxt/webpackbar/issues/c31.

This comment has been minimized.

Copy link
@phorsuedzie

phorsuedzie Jan 10, 2019

Contributor

This keeps karma happy:

       if (!stream.__write) {
-        stream.__write = stream.write;
+        const original = stream.write;
+        stream.__write = original;
         stream.write = function write(data, ...args) {
-          t._onData(data);
-          this.__write(data, ...args);
+          if (stream.__write) {
+            t._onData(data);
+            this.__write(data, ...args);
+          } else {
+            original.call(this, data, ...args);
+          }

I would additionally suggest to not backup the original write to stream.__write (consider some other library doing the same), but as a property webpackbar of function write(...). The checks will then look for stream.write.webpackbar. And this.__write(...) will have to be changed.

t._onData(data);
this.__write(data, ...args);
};
}
}

this.listening = true;
}

stopListen() {
for (const stream of this._streams) {
if (stream.__write) {
stream.write = stream.__write;
delete stream.__write;
}
}

this.listening = false;
}
}

0 comments on commit d51c508

Please sign in to comment.