Skip to content

Commit

Permalink
fix: stability and improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
pi0 committed Nov 3, 2018
1 parent e7efd1f commit 7e3e9ad
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 101 deletions.
116 changes: 70 additions & 46 deletions src/plugin.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import webpack from 'webpack';
import env from 'std-env';
import prettyTime from 'pretty-time';

import { LogReporter, BarsReporter, ProfileReporter } from './reporters';
import Profile from './profile';
import { startCase } from './utils';
import { consola } from './utils/cli';
import { parseRequest } from './utils/request';

// Use bars when possible as default
const useBars = !(env.ci || env.test || !env.tty);

// Default plugin options
const DEFAULTS = {
name: 'webpack',
Expand All @@ -15,11 +18,22 @@ const DEFAULTS = {
stream: process.stdout,
reporters: [],
reporter: null,
log: !!env.minimalCLI,
bars: !env.minimalCLI,
log: !useBars,
bars: useBars,
};

// Default state object
const DEFAULT_STATE = {
start: null,
progress: -1,
message: '',
details: [],
request: null,
stats: null,
hasErrors: false,
};

// Mapping from name => { isRunning, details, progress, msg, request }
// Mapping from name => State
const globalStates = {};

export default class WebpackBarPlugin extends webpack.ProgressPlugin {
Expand All @@ -30,14 +44,14 @@ export default class WebpackBarPlugin extends webpack.ProgressPlugin {
this.name = startCase(options.name);

// this.handler will be called by webpack.ProgressPlugin
this.handler = (percent, msg, ...details) =>
this.updateProgress(percent, msg, details);
this.handler = (percent, message, ...details) =>
this.updateProgress(percent, message, details);

// Keep our state in shared ojbect
this.states = globalStates;
if (!this.states[this.name]) {
this.states[this.name] = {
isRunning: false,
...DEFAULT_STATE,
color: this.options.color,
profile: this.options.profile ? new Profile(this.name) : null,
};
Expand Down Expand Up @@ -70,71 +84,81 @@ export default class WebpackBarPlugin extends webpack.ProgressPlugin {
try {
reporter[fn](this, payload);
} catch (e) {
consola.error(e);
process.stdout.write(e.stack + '\n');
}
}
}
}

hasRunning() {
return Object.values(this.states).some((state) => state.isRunning);
get hasRunning() {
return Object.values(this.states).some((state) => state.progress !== 100);
}

hasErrors() {
return Object.values(this.states).some(
(state) => state.stats && state.stats.hasErrors()
);
get hasErrors() {
return Object.values(this.states).some((state) => state.hasErrors);
}

apply(compiler) {
super.apply(compiler);

const hook = (stats) => {
this.state.stats = stats;
try {
if (!this.hasRunning()) {
this.callReporters('done');
}
} catch (e) {
consola.error(e);
// Hook helper for webpack 3 + 4 support
function hook(hookName, fn) {
if (compiler.hooks) {
compiler.hooks[hookName].tap('WebpackBar:' + hookName, fn);
} else {
compiler.plugin(hookName, fn);
}
};

if (compiler.hooks) {
compiler.hooks.done.tap('WebpackBar', hook);
} else {
compiler.plugin('done', hook);
}

// Adds a hook right before compiler.run() is executed
hook('beforeCompile', () => {
Object.assign(this.state, {
...DEFAULT_STATE,
start: process.hrtime(),
_allDoneCalled: false,
});

this.callReporters('beforeRun');
});

// Compilation has completed
hook('done', (stats) => {
const time = prettyTime(process.hrtime(this.state.start), 2);
const hasErrors = stats.hasErrors();
const status = hasErrors ? 'with some errors' : 'succesfuly';

Object.assign(this.state, {
...DEFAULT_STATE,
stats,
progress: 100,
message: `Compiled ${status} in ${time}`,
hasErrors,
});

this.callReporters('progress');
this.callReporters('done');

if (!this.hasRunning) {
this.callReporters('beforeAllDone');
this.callReporters('allDone');
}
});
}

updateProgress(percent, msg, details = []) {
updateProgress(percent = 0, message = '', details = []) {
const progress = Math.floor(percent * 100);
const isRunning = progress < 100;

const wasRunning = this.state.isRunning;

Object.assign(this.state, {
details,
progress,
msg: isRunning && msg ? msg : '',
message: message || '',
details,
request: parseRequest(details[2]),
elapsed: process.hrtime(this.state.start),
isRunning,
});

if (!wasRunning && isRunning) {
// Started
this.state.start = process.hrtime();
this.callReporters('compiling');
} else if (wasRunning && !isRunning) {
// Finished
this.callReporters('compiled');
}

if (this.options.profile) {
this.state.profile.onRequest(this.state.request);
}

this.callReporters('update');
this.callReporters('progress');
}
}
91 changes: 44 additions & 47 deletions src/reporters/bars.js
Original file line number Diff line number Diff line change
@@ -1,44 +1,43 @@
/* eslint-disable no-console */
import Consola from 'consola';
import chalk from 'chalk';
import prettyTime from 'pretty-time';
import consola from 'consola';

import { renderBar, colorize, ellipsisLeft } from '../utils/cli';
import { formatRequest } from '../utils/request';
import { BULLET, TICK } from '../utils/consts';
import { BULLET, TICK, CROSS } from '../utils/consts';
import LogUpdate from '../utils/log-update';

let lastRender = Date.now();

let logUpdate = null;
const logUpdate = new LogUpdate();

export default class BarsReporter {
compiling() {
logUpdate = logUpdate || new LogUpdate();
Consola.pause();
beforeRun() {
consola.pause();
}

done() {
logUpdate.done();
Consola.resume();
done(context) {
this._renderStates(context.states);
}

compiled(context) {
this._renderStates(context);
beforeAllDone() {
logUpdate.done();
consola.resume();
}

update(context) {
if (Date.now() - lastRender > 200) {
this._renderStates(context);
progress(context) {
if (Date.now() - lastRender > 50) {
this._renderStates(context.states);
}
}

_renderStates(context) {
_renderStates(states) {
lastRender = Date.now();

const renderedStates = Object.keys(context.states)
const renderedStates = Object.keys(states)
.sort((n1, n2) => n1.localeCompare(n2))
.map((name) => ({ name, state: context.states[name] }))
.map((name) => ({ name, state: states[name] }))
.filter((c) => c.state.progress !== -1)
.map((c) => this._renderState(c))
.join('\n\n');

Expand All @@ -48,38 +47,36 @@ export default class BarsReporter {
_renderState({ name, state }) {
const color = colorize(state.color);

if (!state.isRunning) {
// Not started yet
if (!state.time) {
const line1 = chalk.grey(`${BULLET} ${name}`);
const line2 = chalk.grey(` Waiting to start...`);
return line1 + '\n' + line2;
}

let line1;
let line2;

if (state.progress >= 0 && state.progress < 100) {
// Running
line1 = [
color(BULLET),
color(name),
renderBar(state.progress, state.color),
state.message,
`(${state.progress || 0}%)`,
chalk.grey(state.details[0] || ''),
chalk.grey(state.details[1] || ''),
].join(' ');

line2 = state.request
? ' ' +
chalk.grey(
ellipsisLeft(formatRequest(state.request), logUpdate.columns)
)
: '';
} else if (state.progress === 100) {
// Finished
const line1 = color(`${TICK} ${name}`);
const time = prettyTime(state.time, 2);
const line2 = chalk.grey(` Compiled succesfuly in ${time}`);
return line1 + '\n' + line2;
line1 = color(`${state.hasErrors ? CROSS : TICK} ${name}`);
line2 = chalk.grey(' ' + state.message);
} else {
// Invalid state
return '';
}

const line1 = [
color(BULLET),
color(name),
renderBar(state.progress, state.color),
state.msg,
`(${state.progress || 0}%)`,
chalk.grey((state.details && state.details[0]) || ''),
chalk.grey((state.details && state.details[1]) || ''),
].join(' ');

const line2 = state.request
? ' ' +
chalk.grey(
ellipsisLeft(formatRequest(state.request), logUpdate.columns)
)
: '';

return line1 + '\n' + line2;
}
}
7 changes: 4 additions & 3 deletions src/utils/consts.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const nodeModules = `${path.delimiter}node_modules${path.delimiter}`;
export const BAR_LENGTH = 25;
export const BLOCK_CHAR = '█';
export const BLOCK_CHAR2 = '█';
export const NEXT = chalk.blue(figures(' › '));
export const BULLET = figures('●');
export const TICK = figures('✔');
export const NEXT = ' ' + chalk.blue(figures.pointerSmall) + ' ';
export const BULLET = figures.bullet;
export const TICK = figures.tick;
export const CROSS = figures.cross;
10 changes: 5 additions & 5 deletions src/utils/log-update.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ export default class LogUpdate {
return (process.stderr.columns || 80) - 2;
}

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

write(data) {
fs.writeSync(2, data);
fs.fsyncSync(2);
}

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

done() {
this.prevLineCount = 0;
}
Expand Down

0 comments on commit 7e3e9ad

Please sign in to comment.