const MS_IN_MINUTE = 60000; const MS_IN_SECOND = 1000; const chalk = require("chalk"); const { fg, bg } = require("./colours"); const { groupBy, getAverages, getTotalActiveTime } = require("./utils"); const humanTime = (ms, options = {}) => { if (options.verbose) { return ms.toLocaleString() + " ms"; } const minutes = Math.floor(ms / MS_IN_MINUTE); const secondsRaw = (ms - minutes * MS_IN_MINUTE) / MS_IN_SECOND; const secondsWhole = Math.floor(secondsRaw); const remainderPrecision = secondsWhole > 0 ? 2 : 3; const secondsRemainder = Math.min(secondsRaw - secondsWhole, 0.99); const seconds = secondsWhole + secondsRemainder .toPrecision(remainderPrecision) .replace(/^0/, "") .replace(/0+$/, "") .replace(/^\.$/, ""); let time = ""; if (minutes > 0) time += minutes + " min" + (minutes > 1 ? "s" : "") + ", "; time += seconds + " secs"; return time; }; const smpTag = () => bg(" SMP ") + " ⏱ "; module.exports.smpTag = smpTag; module.exports.getHumanOutput = (outputObj, options = {}) => { const hT = (x) => humanTime(x, options); let output = "\n\n" + smpTag() + "\n"; if (outputObj.misc) { output += "General output time took " + fg(hT(outputObj.misc.compileTime, options), outputObj.misc.compileTime); output += "\n\n"; } if (outputObj.plugins) { output += smpTag() + "Plugins\n"; Object.keys(outputObj.plugins) .sort( (name1, name2) => outputObj.plugins[name2] - outputObj.plugins[name1] ) .forEach((pluginName) => { output += chalk.bold(pluginName) + " took " + fg(hT(outputObj.plugins[pluginName]), outputObj.plugins[pluginName]); output += "\n"; }); output += "\n"; } if (outputObj.loaders) { output += smpTag() + "Loaders\n"; outputObj.loaders.build .sort((obj1, obj2) => obj2.activeTime - obj1.activeTime) .forEach((loaderObj) => { output += loaderObj.loaders.map(fg).join(", and \n") + " took " + fg(hT(loaderObj.activeTime), loaderObj.activeTime) + "\n"; let xEqualsY = []; if (options.verbose) { xEqualsY.push(["median", hT(loaderObj.averages.median)]); xEqualsY.push(["mean", hT(loaderObj.averages.mean)]); if (typeof loaderObj.averages.variance === "number") xEqualsY.push(["s.d.", hT(Math.sqrt(loaderObj.averages.variance))]); xEqualsY.push([ "range", "(" + hT(loaderObj.averages.range.start) + " --> " + hT(loaderObj.averages.range.end) + ")", ]); } if (loaderObj.loaders.length > 1) { Object.keys(loaderObj.subLoadersTime).forEach((subLoader) => { xEqualsY.push([subLoader, hT(loaderObj.subLoadersTime[subLoader])]); }); } xEqualsY.push(["module count", loaderObj.averages.dataPoints]); if (options.loaderTopFiles) { const loopLen = Math.min( loaderObj.rawStartEnds.length, options.loaderTopFiles ); for (let i = 0; i < loopLen; i++) { const rawItem = loaderObj.rawStartEnds[i]; xEqualsY.push([rawItem.name, hT(rawItem.end - rawItem.start)]); } } const maxXLength = xEqualsY.reduce( (acc, cur) => Math.max(acc, cur[0].length), 0 ); xEqualsY.forEach((xY) => { const padEnd = maxXLength - xY[0].length; output += " " + xY[0] + " ".repeat(padEnd) + " = " + xY[1] + "\n"; }); }); } output += "\n\n"; return output; }; module.exports.getMiscOutput = (data) => ({ compileTime: data.compile[0].end - data.compile[0].start, }); module.exports.getPluginsOutput = (data) => Object.keys(data).reduce((acc, key) => { const inData = data[key]; const startEndsByName = groupBy("name", inData); return startEndsByName.reduce((innerAcc, startEnds) => { innerAcc[startEnds[0].name] = (innerAcc[startEnds[0].name] || 0) + getTotalActiveTime(startEnds); return innerAcc; }, acc); }, {}); module.exports.getLoadersOutput = (data) => { const startEndsByLoader = groupBy("loaders", data.build); const allSubLoaders = data["build-specific"] || []; const buildData = startEndsByLoader.map((startEnds) => { const averages = getAverages(startEnds); const activeTime = getTotalActiveTime(startEnds); const subLoaders = groupBy( "loader", allSubLoaders.filter((l) => startEnds.find((x) => x.name === l.name)) ); const subLoadersActiveTime = subLoaders.reduce((acc, loaders) => { acc[loaders[0].loader] = getTotalActiveTime(loaders); return acc; }, {}); return { averages, activeTime, loaders: startEnds[0].loaders, subLoadersTime: subLoadersActiveTime, rawStartEnds: startEnds.sort( (a, b) => b.end - b.start - (a.end - a.start) ), }; }); return { build: buildData }; };