From d918a9ac582ca1611e1ca1215e49d77adc418623 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Sat, 3 Nov 2018 18:16:02 +0330 Subject: [PATCH] feat: rework bars reporter using a rewrite of log-update --- .eslintrc.js | 3 +- kitchen-sync/webpack.config.js | 10 ++-- package.json | 7 +-- src/plugin.js | 9 ++-- src/reporters/bars.js | 96 +++++++++++++++------------------- src/reporters/log.js | 4 +- src/utils/index.js | 19 ------- src/utils/log-update.js | 44 ++++++++++++++++ yarn.lock | 24 +++++---- 9 files changed, 119 insertions(+), 97 deletions(-) create mode 100644 src/utils/log-update.js diff --git a/.eslintrc.js b/.eslintrc.js index 2951eb8..07d88e8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,6 +9,7 @@ module.exports = { ], 'class-methods-use-this': 0, 'prefer-template': 0, - 'no-underscore-dangle': 0 + 'no-underscore-dangle': 0, + 'arrow-body-style': 0 }, }; diff --git a/kitchen-sync/webpack.config.js b/kitchen-sync/webpack.config.js index 3703639..cc0d307 100644 --- a/kitchen-sync/webpack.config.js +++ b/kitchen-sync/webpack.config.js @@ -10,7 +10,7 @@ consola.wrapConsole(); let lastProgress; -module.exports = { +const config = (name, color) => ({ mode: 'production', context: __dirname, devtool: false, @@ -26,8 +26,8 @@ module.exports = { }, plugins: [ new Self({ - color: 'orange', - name: 'kitchen-sync', + color, + name, reporter: { update({ state }) { if (lastProgress !== state.progress && state.progress % 25 === 0) { @@ -38,4 +38,6 @@ module.exports = { }, }), ], -}; +}); + +module.exports = [config('CyanBar', 'cyan'), config('BlueBar', 'blue')]; diff --git a/package.json b/package.json index e645952..38fc932 100644 --- a/package.json +++ b/package.json @@ -42,13 +42,14 @@ "webpack": "^3.0.0 || ^4.0.0" }, "dependencies": { + "ansi-escapes": "^3.1.0", "chalk": "^2.4.1", - "consola": "^2.0.7", - "draftlog": "^1.0.12", + "consola": "^2.1.0", "figures": "^2.0.0", "pretty-time": "^1.1.0", "std-env": "^2.1.0", - "text-table": "^0.2.0" + "text-table": "^0.2.0", + "wrap-ansi": "^4.0.0" }, "devDependencies": { "@commitlint/cli": "^7.2.1", diff --git a/src/plugin.js b/src/plugin.js index c6b59f3..a3cc056 100644 --- a/src/plugin.js +++ b/src/plugin.js @@ -27,6 +27,7 @@ export default class WebpackBarPlugin extends webpack.ProgressPlugin { super(); this.options = Object.assign({}, DEFAULTS, options); + this.name = options.name; // this.handler will be called by webpack.ProgressPlugin this.handler = (percent, msg, ...details) => @@ -34,14 +35,14 @@ export default class WebpackBarPlugin extends webpack.ProgressPlugin { // Keep our state in shared ojbect this.states = globalStates; - if (!this.states[this.options.name]) { - this.states[this.options.name] = { + if (!this.states[this.name]) { + this.states[this.name] = { isRunning: false, color: this.options.color, - profile: this.options.profile ? new Profile(this.options.name) : null, + profile: this.options.profile ? new Profile(this.name) : null, }; } - this.state = this.states[this.options.name]; + this.state = this.states[this.name]; // Reporters this.reporters = Array.from(this.options.reporters || []); diff --git a/src/reporters/bars.js b/src/reporters/bars.js index eefa8be..73a4b65 100644 --- a/src/reporters/bars.js +++ b/src/reporters/bars.js @@ -1,46 +1,26 @@ +/* eslint-disable no-console */ import Consola from 'consola'; -import draftLog from 'draftlog'; import chalk from 'chalk'; import prettyTime from 'pretty-time'; import { renderBar, colorize, ellipsisLeft } from '../utils/cli'; -import { throttle } from '../utils'; import { formatRequest } from '../utils/request'; import { BULLET, TICK } from '../utils/consts'; +import LogUpdate from '../utils/log-update'; -// Convention: Original values are kept with two underscores +let lastRender = Date.now(); -const globalConsole = { - log: console.__log || console.log, // eslint-disable-line no-console - _stdout: console._stdout, // eslint-disable-line no-console -}; - -const renderT = throttle((fn, ...args) => fn(...args), 1, 50); +let logUpdate = null; export default class BarsReporter { - constructor() { - this._render = this._render.bind(this); - this.drafts = null; - } - compiling() { - if (!globalConsole.draft) { - draftLog(globalConsole); - console.__stdout = console._stdout; // eslint-disable-line no-console - console._stdout = globalConsole._stdout; // eslint-disable-line no-console - } - + logUpdate = logUpdate || new LogUpdate(); Consola.pause(); - globalConsole.log(); - this.drafts = [globalConsole.draft(), globalConsole.draft()]; } done() { - // eslint-disable-next-line no-console - // if (console.__stdout) { - // console._stdout = console.__stdout; // eslint-disable-line no-console - // delete console.__stdout; // eslint-disable-line no-console - // } + lastRender = Date.now(); + logUpdate.done(); Consola.resume(); } @@ -49,45 +29,53 @@ export default class BarsReporter { } update(context) { - renderT(this._render, context); + if (Date.now() - lastRender > 200) { + this._render(context); + } } _render(context) { - const { - state, - options: { stream, name }, - } = context; + lastRender = Date.now(); + + const renderedStates = Object.keys(context.states) + .sort((n1, n2) => n1.localeCompare(n2)) + .map((name) => ({ name, state: context.states[name] })) + .map((c) => this._renderState(c)) + .join('\n\n'); - const columns = stream.columns || 80; + logUpdate.render('\n' + renderedStates + '\n'); + } + + _renderState({ name, state }) { const color = colorize(state.color); if (!state.isRunning) { const color2 = state.progress === 100 ? color : chalk.grey; const line1 = color2(`${TICK} ${name}`); - const time = prettyTime(state.elapsed, 2); + const time = prettyTime(state.elapsed || 0, 2); const line2 = chalk.grey(` Compiled succesfuly in ${time}`); - this.drafts[0](line1); - this.drafts[1](line2); - } else { - 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), columns - 2)) - : ''; - - this.drafts[0](line1); - this.drafts[1](line2); + return line1 + '\n' + line2; } + + 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; } } diff --git a/src/reporters/log.js b/src/reporters/log.js index ceee527..4777407 100644 --- a/src/reporters/log.js +++ b/src/reporters/log.js @@ -4,11 +4,11 @@ import { consola } from '../utils/cli'; export default class LogReporter { compiling(context) { - consola.info(`Compiling ${context.options.name}`); + consola.info(`Compiling ${context.name}`); } compiled(context) { const time = prettyTime(context.state.elapsed, 2); - consola.success(`Compiled ${context.options.name} in ${time}`); + consola.success(`Compiled ${context.name} in ${time}`); } } diff --git a/src/utils/index.js b/src/utils/index.js index fa9fad8..7d6efb6 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -34,22 +34,3 @@ export const range = (len) => { } return arr; }; - -export function throttle(callback, limit, time) { - let calledCount = 0; - let timeout = null; - - return function throttledFn(...args) { - if (limit > calledCount) { - calledCount += 1; - callback(...args); - } - - if (!timeout) { - timeout = setTimeout(() => { - calledCount = 0; - timeout = null; - }, time); - } - }; -} diff --git a/src/utils/log-update.js b/src/utils/log-update.js new file mode 100644 index 0000000..110b9bc --- /dev/null +++ b/src/utils/log-update.js @@ -0,0 +1,44 @@ +import fs from 'fs'; + +import ansiEscapes from 'ansi-escapes'; +import wrapAnsi from 'wrap-ansi'; + +// Based on https://github.com/sindresorhus/log-update/blob/master/index.js + +export default class LogUpdate { + constructor() { + this.prevLineCount = 0; + } + + render(lines) { + const wrappedLines = wrapAnsi(lines, this.columns, { + trim: false, + hard: true, + wordWrap: false, + }); + + const earaseChars = ansiEscapes.eraseLines(this.prevLineCount); + + this.write(earaseChars + wrappedLines + '\n'); + + this.prevLineCount = wrappedLines.split('\n').length + 1; + } + + get columns() { + 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); + } + + done() { + this.prevLineCount = 0; + } +} diff --git a/yarn.lock b/yarn.lock index 937e9ba..9090f27 100644 --- a/yarn.lock +++ b/yarn.lock @@ -445,7 +445,7 @@ ansi-align@^2.0.0: dependencies: string-width "^2.0.0" -ansi-escapes@^3.0.0: +ansi-escapes@^3.0.0, ansi-escapes@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" integrity sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw== @@ -1888,10 +1888,10 @@ configstore@^3.0.0: write-file-atomic "^2.0.0" xdg-basedir "^3.0.0" -consola@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/consola/-/consola-2.0.7.tgz#658707b6ddc96b8e60e012430c2bafaa9d8dd8d4" - integrity sha512-wugpvF6aWVQtC9aUH3q476rdTjW3+VmXQedYr7FX50jxkzNjCt2qRybvZXiP8d5FEAHxCDhqjzKzb9mdQPwfKQ== +consola@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/consola/-/consola-2.1.0.tgz#cf2b762ce1c982e61c3f9da3b9e7f0bccb0c1fc0" + integrity sha512-x+VdZSXUCVdnYLNqZV7fAtPB88rPGNNbAtf00aCD/l75uvMcp7Qh0UaSkkCQ33n/+nkoLS+V2DPT4F+GssUKDw== dependencies: chalk "^2.4.1" dayjs "^1.7.7" @@ -2670,11 +2670,6 @@ dotgitignore@^1.0.3: find-up "^2.1.0" minimatch "^3.0.4" -draftlog@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/draftlog/-/draftlog-1.0.12.tgz#7db6a3c5b62106bb32dd4a35d67bcccb6c7d9da0" - integrity sha1-fbajxbYhBrsy3Uo11nvMy2x9naA= - duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -8153,6 +8148,15 @@ wrap-ansi@^3.0.1: string-width "^2.1.1" strip-ansi "^4.0.0" +wrap-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-4.0.0.tgz#b3570d7c70156159a2d42be5cc942e957f7b1131" + integrity sha512-uMTsj9rDb0/7kk1PbcbCcwvHUxp60fGDB/NNXpVa0Q+ic/e7y5+BwTxKfQ33VYgDppSwi/FBzpetYzo8s6tfbg== + dependencies: + ansi-styles "^3.2.0" + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"