create-lbgcli1
Version:
前端脚手架模板
598 lines (579 loc) • 16.6 kB
JavaScript
'use strict';
const webpack = require('webpack');
const env = require('std-env');
const prettyTime = require('pretty-time');
const path2 = require('path');
const chalk2 = require('chalk');
const Consola = require('consola');
const textTable = require('text-table');
const figures = require('figures');
const ansiEscapes = require('ansi-escapes');
const wrapAnsi = require('wrap-ansi');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
const env__default = /*#__PURE__*/_interopDefaultLegacy(env);
const prettyTime__default = /*#__PURE__*/_interopDefaultLegacy(prettyTime);
const path2__default = /*#__PURE__*/_interopDefaultLegacy(path2);
const chalk2__default = /*#__PURE__*/_interopDefaultLegacy(chalk2);
const Consola__default = /*#__PURE__*/_interopDefaultLegacy(Consola);
const textTable__default = /*#__PURE__*/_interopDefaultLegacy(textTable);
const ansiEscapes__default = /*#__PURE__*/_interopDefaultLegacy(ansiEscapes);
const wrapAnsi__default = /*#__PURE__*/_interopDefaultLegacy(wrapAnsi);
function first(arr) {
return arr[0];
}
function last(arr) {
return arr.length ? arr[arr.length - 1] : null;
}
function startCase(str) {
return str[0].toUpperCase() + str.substr(1);
}
function firstMatch(regex, str) {
const m = regex.exec(str);
return m ? m[0] : null;
}
function hasValue(s) {
return s && s.length;
}
function removeAfter(delimiter, str) {
return first(str.split(delimiter)) || "";
}
function removeBefore(delimiter, str) {
return last(str.split(delimiter)) || "";
}
function range(len) {
const arr = [];
for (let i = 0; i < len; i++) {
arr.push(i);
}
return arr;
}
function shortenPath(path2$1 = "") {
const cwd = process.cwd() + path2.sep;
return String(path2$1).replace(cwd, "");
}
function objectValues(obj) {
return Object.keys(obj).map((key) => obj[key]);
}
const nodeModules = `${path2.delimiter}node_modules${path2.delimiter}`;
const BAR_LENGTH = 25;
const BLOCK_CHAR = "\u2588";
const BLOCK_CHAR2 = "\u2588";
const NEXT = " " + chalk2__default['default'].blue(figures.pointerSmall) + " ";
const BULLET = figures.bullet;
const TICK = figures.tick;
const CROSS = figures.cross;
const CIRCLE_OPEN = figures.radioOff;
const consola = Consola__default['default'].withTag("webpackbar");
const colorize = (color) => {
if (color[0] === "#") {
return chalk2__default['default'].hex(color);
}
return chalk2__default['default'][color] || chalk2__default['default'].keyword(color);
};
const renderBar = (progress, color) => {
const w = progress * (BAR_LENGTH / 100);
const bg = chalk2__default['default'].white(BLOCK_CHAR);
const fg = colorize(color)(BLOCK_CHAR2);
return range(BAR_LENGTH).map((i) => i < w ? fg : bg).join("");
};
function createTable(data) {
return textTable__default['default'](data, {
align: data[0].map(() => "l")
}).replace(/\n/g, "\n\n");
}
function ellipsisLeft(str, n) {
if (str.length <= n - 3) {
return str;
}
return `...${str.substr(str.length - n - 1)}`;
}
const parseRequest = (requestStr) => {
const parts = (requestStr || "").split("!");
const file = path2__default['default'].relative(process.cwd(), removeAfter("?", removeBefore(nodeModules, parts.pop())));
const loaders = parts.map((part) => firstMatch(/[a-z0-9-@]+-loader/, part)).filter(hasValue);
return {
file: hasValue(file) ? file : null,
loaders
};
};
const formatRequest = (request) => {
const loaders = request.loaders.join(NEXT);
if (!loaders.length) {
return request.file || "";
}
return `${loaders}${NEXT}${request.file}`;
};
function hook(compiler, hookName, fn) {
if (compiler.hooks) {
compiler.hooks[hookName].tap("WebpackBar:" + hookName, fn);
} else {
compiler.plugin(hookName, fn);
}
}
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__default['default'](lines, this.columns, {
trim: false,
hard: true,
wordWrap: false
});
const data = ansiEscapes__default['default'].eraseLines(this.prevLineCount) + wrappedLines + "\n" + this.extraLines;
this.write(data);
this.prevLineCount = data.split("\n").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__default['default'].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}%)`,
chalk2__default['default'].grey(state.details[0] || ""),
chalk2__default['default'].grey(state.details[1] || "")
].join(" ");
line2 = state.request ? " " + chalk2__default['default'].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 = chalk2__default['default'].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 ${chalk2__default['default'].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 description2 = 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__default['default'](stat.time),
prettyTime__default['default'](avgTime),
description2
]);
});
data.push(["Total", totalRequests, prettyTime__default['default'](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 && path2__default['default'].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 ${chalk2__default['default'].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 = /*#__PURE__*/Object.freeze({
__proto__: null,
fancy: FancyReporter,
basic: SimpleReporter,
profile: ProfileReporter,
stats: StatsReporter
});
const DEFAULTS = {
name: "webpack",
color: "green",
reporters: env__default['default'].minimalCLI ? ["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;
}
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__default['default'](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");
}
}
module.exports = WebpackBarPlugin;