\\d+)m|\\${ANSI_ESCAPE_LINK}(?.*)${ANSI_ESCAPE_BELL})`).exec(pre.slice(index).join('')) || {groups: {}}; if (groups.code !== undefined) { const code = Number.parseFloat(groups.code); escapeCode = code === END_CODE ? undefined : code; } else if (groups.uri !== undefined) { escapeUrl = groups.uri.length === 0 ? undefined : groups.uri; } } const code = ansiStyles.codes.get(Number(escapeCode)); if (pre[index + 1] === '\n') { if (escapeUrl) { returnValue += wrapAnsiHyperlink(''); } if (escapeCode && code) { returnValue += wrapAnsiCode(code); } } else if (character === '\n') { if (escapeCode && code) { returnValue += wrapAnsiCode(escapeCode); } if (escapeUrl) { returnValue += wrapAnsiHyperlink(escapeUrl); } } } return returnValue; }; // For each newline, invoke the method separately function wrapAnsi(string, columns, options) { return String(string) .normalize() .replace(/\r\n/g, '\n') .split('\n') .map(line => exec(line, columns, options)) .join('\n'); } const originalWrite = Symbol("webpackbarWrite"); 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 data = ansiEscapes.eraseLines(this.prevLineCount) + wrappedLines + "\n" + this.extraLines; this.write(data); const _lines = data.split("\n"); this.prevLineCount = _lines.length; } get columns() { return (process.stderr.columns || 80) - 2; } write(data) { const stream = process.stderr; if (stream.write[originalWrite]) { stream.write[originalWrite].call(stream, data, "utf-8"); } else { stream.write(data, "utf-8"); } } clear() { this.done(); this.write(ansiEscapes.eraseLines(this.prevLineCount)); } 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; } for (const stream of this._streams) { if (stream.write[originalWrite]) { continue; } const write = (data, ...args) => { if (!stream.write[originalWrite]) { return stream.write(data, ...args); } this._onData(data); return stream.write[originalWrite].call(stream, data, ...args); }; write[originalWrite] = stream.write; stream.write = write; } this.listening = true; } stopListen() { for (const stream of this._streams) { if (stream.write[originalWrite]) { stream.write = stream.write[originalWrite]; } } this.listening = false; } } const logUpdate = new LogUpdate(); let lastRender = Date.now(); class FancyReporter { allDone() { logUpdate.done(); } done(context) { this._renderStates(context.statesArray); if (context.hasErrors) { logUpdate.done(); } } progress(context) { if (Date.now() - lastRender > 50) { this._renderStates(context.statesArray); } } _renderStates(statesArray) { lastRender = Date.now(); const renderedStates = statesArray.map((c) => this._renderState(c)).join("\n\n"); logUpdate.render("\n" + renderedStates + "\n"); } _renderState(state) { const color = colorize(state.color); let line1; let line2; if (state.progress >= 0 && state.progress < 100) { line1 = [ color(BULLET), color(state.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 { let icon = " "; if (state.hasErrors) { icon = CROSS; } else if (state.progress === 100) { icon = TICK; } else if (state.progress === -1) { icon = CIRCLE_OPEN; } line1 = color(`${icon} ${state.name}`); line2 = chalk.grey(" " + state.message); } return line1 + "\n" + line2; } } class SimpleReporter { start(context) { consola.info(`Compiling ${context.state.name}`); } change(context, { shortPath }) { consola.debug(`${shortPath} changed.`, `Rebuilding ${context.state.name}`); } done(context) { const { hasError, message, name } = context.state; consola[hasError ? "error" : "success"](`${name}: ${message}`); } } const DB = { loader: { get: (loader) => startCase(loader) }, ext: { get: (ext) => `${ext} files`, vue: "Vue Single File components", js: "JavaScript files", sass: "SASS files", scss: "SASS files", unknown: "Unknown files" } }; function getDescription(category, keyword) { if (!DB[category]) { return startCase(keyword); } if (DB[category][keyword]) { return DB[category][keyword]; } if (DB[category].get) { return DB[category].get(keyword); } return "-"; } function formatStats(allStats) { const lines = []; Object.keys(allStats).forEach((category) => { const stats = allStats[category]; lines.push(`> Stats by ${chalk.bold(startCase(category))}`); let totalRequests = 0; const totalTime = [0, 0]; const data = [ [startCase(category), "Requests", "Time", "Time/Request", "Description"] ]; Object.keys(stats).forEach((item) => { const stat = stats[item]; totalRequests += stat.count || 0; const description = getDescription(category, item); totalTime[0] += stat.time[0]; totalTime[1] += stat.time[1]; const avgTime = [stat.time[0] / stat.count, stat.time[1] / stat.count]; data.push([ item, stat.count || "-", prettyTime(stat.time), prettyTime(avgTime), description ]); }); data.push(["Total", totalRequests, prettyTime(totalTime), "", ""]); lines.push(createTable(data)); }); return `${lines.join("\n\n")} `; } class Profiler { constructor() { this.requests = []; } onRequest(request) { if (!request) { return; } if (this.requests.length) { const lastReq = this.requests[this.requests.length - 1]; if (lastReq.start) { lastReq.time = process.hrtime(lastReq.start); delete lastReq.start; } } if (!request.file || !request.loaders.length) { return; } this.requests.push({ request, start: process.hrtime() }); } getStats() { const loaderStats = {}; const extStats = {}; const getStat = (stats, name) => { if (!stats[name]) { stats[name] = { count: 0, time: [0, 0] }; } return stats[name]; }; const addToStat = (stats, name, count, time) => { const stat = getStat(stats, name); stat.count += count; stat.time[0] += time[0]; stat.time[1] += time[1]; }; this.requests.forEach(({ request, time = [0, 0] }) => { request.loaders.forEach((loader) => { addToStat(loaderStats, loader, 1, time); }); const ext = request.file && path.extname(request.file).substr(1); addToStat(extStats, ext && ext.length ? ext : "unknown", 1, time); }); return { ext: extStats, loader: loaderStats }; } getFormattedStats() { return formatStats(this.getStats()); } } class ProfileReporter { progress(context) { if (!context.state.profiler) { context.state.profiler = new Profiler(); } context.state.profiler.onRequest(context.state.request); } done(context) { if (context.state.profiler) { context.state.profile = context.state.profiler.getFormattedStats(); delete context.state.profiler; } } allDone(context) { let str = ""; for (const state of context.statesArray) { const color = colorize(state.color); if (state.profile) { str += color(` Profile results for ${chalk.bold(state.name)} `) + ` ${state.profile} `; delete state.profile; } } process.stderr.write(str); } } class StatsReporter { constructor(options) { this.options = Object.assign({ chunks: false, children: false, modules: false, colors: true, warnings: true, errors: true }, options); } done(context, { stats }) { const str = stats.toString(this.options); if (context.hasErrors) { process.stderr.write("\n" + str + "\n"); } else { context.state.statsString = str; } } allDone(context) { let str = ""; for (const state of context.statesArray) { if (state.statsString) { str += "\n" + state.statsString + "\n"; delete state.statsString; } } process.stderr.write(str); } } const reporters = { __proto__: null, fancy: FancyReporter, basic: SimpleReporter, profile: ProfileReporter, stats: StatsReporter }; const DEFAULTS = { name: "webpack", color: "green", reporters: isMinimal ? ["basic"] : ["fancy"], reporter: null }; const DEFAULT_STATE = { start: null, progress: -1, done: false, message: "", details: [], request: null, hasErrors: false }; const globalStates = {}; class WebpackBarPlugin extends Webpack.ProgressPlugin { constructor(options) { super({ activeModules: true }); this.options = Object.assign({}, DEFAULTS, options); this.handler = (percent, message, ...details) => { this.updateProgress(percent, message, details); }; const _reporters = Array.from(this.options.reporters || []).concat(this.options.reporter).filter(Boolean).map((reporter) => { if (Array.isArray(reporter)) { return { reporter: reporter[0], options: reporter[1] }; } if (typeof reporter === "string") { return { reporter }; } return { reporter }; }); this.reporters = _reporters.map(({ reporter, options: options2 = {} }) => { if (typeof reporter === "string") { if (this.options[reporter] === false) { return void 0; } options2 = { ...this.options[reporter], ...options2 }; reporter = reporters[reporter] || require(reporter); } if (typeof reporter === "function") { try { reporter = new reporter(options2); } catch (err) { reporter = reporter(options2); } } return reporter; }).filter(Boolean); } callReporters(fn, payload = {}) { for (const reporter of this.reporters) { if (typeof reporter[fn] === "function") { try { reporter[fn](this, payload); } catch (e) { process.stdout.write(e.stack + "\n"); } } } } get hasRunning() { return objectValues(this.states).some((state) => !state.done); } get hasErrors() { return objectValues(this.states).some((state) => state.hasErrors); } get statesArray() { return objectValues(this.states).sort((s1, s2) => s1.name.localeCompare(s2.name)); } get states() { return globalStates; } get state() { return globalStates[this.options.name]; } _ensureState() { if (!this.states[this.options.name]) { this.states[this.options.name] = { ...DEFAULT_STATE, color: this.options.color, name: startCase(this.options.name) }; } } apply(compiler) { if (compiler.webpackbar) { return; } compiler.webpackbar = this; super.apply(compiler); hook(compiler, "afterPlugins", () => { this._ensureState(); }); hook(compiler, "compile", () => { this._ensureState(); Object.assign(this.state, { ...DEFAULT_STATE, start: process.hrtime() }); this.callReporters("start"); }); hook(compiler, "invalid", (fileName, changeTime) => { this._ensureState(); this.callReporters("change", { path: fileName, shortPath: shortenPath(fileName), time: changeTime }); }); hook(compiler, "done", (stats) => { this._ensureState(); if (this.state.done) { return; } const hasErrors = stats.hasErrors(); const status = hasErrors ? "with some errors" : "successfully"; const time = this.state.start ? " in " + prettyTime(process.hrtime(this.state.start), 2) : ""; Object.assign(this.state, { ...DEFAULT_STATE, progress: 100, done: true, message: `Compiled ${status}${time}`, hasErrors }); this.callReporters("progress"); this.callReporters("done", { stats }); if (!this.hasRunning) { this.callReporters("beforeAllDone"); this.callReporters("allDone"); this.callReporters("afterAllDone"); } }); } updateProgress(percent = 0, message = "", details = []) { const progress = Math.floor(percent * 100); const activeModule = details.pop(); Object.assign(this.state, { progress, message: message || "", details, request: parseRequest(activeModule) }); this.callReporters("progress"); } } export { WebpackBarPlugin as default };