1 | const MS_IN_MINUTE = 60000;
|
2 | const MS_IN_SECOND = 1000;
|
3 |
|
4 | const chalk = require("chalk");
|
5 | const { fg, bg } = require("./colours");
|
6 | const { groupBy, getAverages, getTotalActiveTime } = require("./utils");
|
7 |
|
8 | const humanTime = (ms, options = {}) => {
|
9 | if (options.verbose) {
|
10 | return ms.toLocaleString() + " ms";
|
11 | }
|
12 |
|
13 | const minutes = Math.floor(ms / MS_IN_MINUTE);
|
14 | const secondsRaw = (ms - minutes * MS_IN_MINUTE) / MS_IN_SECOND;
|
15 | const secondsWhole = Math.floor(secondsRaw);
|
16 | const remainderPrecision = secondsWhole > 0 ? 2 : 3;
|
17 | const secondsRemainder = Math.min(secondsRaw - secondsWhole, 0.99);
|
18 | const seconds =
|
19 | secondsWhole +
|
20 | secondsRemainder
|
21 | .toPrecision(remainderPrecision)
|
22 | .replace(/^0/, "")
|
23 | .replace(/0+$/, "")
|
24 | .replace(/^\.$/, "");
|
25 |
|
26 | let time = "";
|
27 |
|
28 | if (minutes > 0) time += minutes + " min" + (minutes > 1 ? "s" : "") + ", ";
|
29 | time += seconds + " secs";
|
30 |
|
31 | return time;
|
32 | };
|
33 |
|
34 | const smpTag = () => bg(" SMP ") + " ⏱ ";
|
35 | module.exports.smpTag = smpTag;
|
36 |
|
37 | module.exports.getHumanOutput = (outputObj, options = {}) => {
|
38 | const hT = x => humanTime(x, options);
|
39 | let output = "\n\n" + smpTag() + "\n";
|
40 |
|
41 | if (outputObj.misc) {
|
42 | output +=
|
43 | "General output time took " +
|
44 | fg(hT(outputObj.misc.compileTime, options), outputObj.misc.compileTime);
|
45 | output += "\n\n";
|
46 | }
|
47 | if (outputObj.plugins) {
|
48 | output += smpTag() + "Plugins\n";
|
49 | Object.keys(outputObj.plugins)
|
50 | .sort(
|
51 | (name1, name2) => outputObj.plugins[name2] - outputObj.plugins[name1]
|
52 | )
|
53 | .forEach(pluginName => {
|
54 | output +=
|
55 | chalk.bold(pluginName) +
|
56 | " took " +
|
57 | fg(hT(outputObj.plugins[pluginName]), outputObj.plugins[pluginName]);
|
58 | output += "\n";
|
59 | });
|
60 | output += "\n";
|
61 | }
|
62 | if (outputObj.loaders) {
|
63 | output += smpTag() + "Loaders\n";
|
64 | outputObj.loaders.build
|
65 | .sort((obj1, obj2) => obj2.activeTime - obj1.activeTime)
|
66 | .forEach(loaderObj => {
|
67 | output +=
|
68 | loaderObj.loaders.map(fg).join(", and \n") +
|
69 | " took " +
|
70 | fg(hT(loaderObj.activeTime), loaderObj.activeTime) +
|
71 | "\n";
|
72 |
|
73 | let xEqualsY = [];
|
74 | if (options.verbose) {
|
75 | xEqualsY.push(["median", hT(loaderObj.averages.median)]);
|
76 | xEqualsY.push(["mean", hT(loaderObj.averages.mean)]);
|
77 | if (typeof loaderObj.averages.variance === "number")
|
78 | xEqualsY.push(["s.d.", hT(Math.sqrt(loaderObj.averages.variance))]);
|
79 | xEqualsY.push([
|
80 | "range",
|
81 | "(" +
|
82 | hT(loaderObj.averages.range.start) +
|
83 | " --> " +
|
84 | hT(loaderObj.averages.range.end) +
|
85 | ")",
|
86 | ]);
|
87 | }
|
88 |
|
89 | if (loaderObj.loaders.length > 1) {
|
90 | Object.keys(loaderObj.subLoadersTime).forEach(subLoader => {
|
91 | xEqualsY.push([subLoader, hT(loaderObj.subLoadersTime[subLoader])]);
|
92 | });
|
93 | }
|
94 |
|
95 | xEqualsY.push(["module count", loaderObj.averages.dataPoints]);
|
96 |
|
97 | const maxXLength = xEqualsY.reduce(
|
98 | (acc, cur) => Math.max(acc, cur[0].length),
|
99 | 0
|
100 | );
|
101 | xEqualsY.forEach(xY => {
|
102 | const padEnd = maxXLength - xY[0].length;
|
103 | output += " " + xY[0] + " ".repeat(padEnd) + " = " + xY[1] + "\n";
|
104 | });
|
105 | });
|
106 | }
|
107 |
|
108 | output += "\n\n";
|
109 |
|
110 | return output;
|
111 | };
|
112 |
|
113 | module.exports.getMiscOutput = data => ({
|
114 | compileTime: data.compile[0].end - data.compile[0].start,
|
115 | });
|
116 |
|
117 | module.exports.getPluginsOutput = data =>
|
118 | Object.keys(data).reduce((acc, key) => {
|
119 | const inData = data[key];
|
120 |
|
121 | const startEndsByName = groupBy("name", inData);
|
122 |
|
123 | return startEndsByName.reduce((innerAcc, startEnds) => {
|
124 | innerAcc[startEnds[0].name] =
|
125 | (innerAcc[startEnds[0].name] || 0) + getTotalActiveTime(startEnds);
|
126 | return innerAcc;
|
127 | }, acc);
|
128 | }, {});
|
129 |
|
130 | module.exports.getLoadersOutput = data => {
|
131 | const startEndsByLoader = groupBy("loaders", data.build);
|
132 | const allSubLoaders = data["build-specific"] || [];
|
133 |
|
134 | const buildData = startEndsByLoader.map(startEnds => {
|
135 | const averages = getAverages(startEnds);
|
136 | const activeTime = getTotalActiveTime(startEnds);
|
137 | const subLoaders = groupBy(
|
138 | "loader",
|
139 | allSubLoaders.filter(l => startEnds.find(x => x.name === l.name))
|
140 | );
|
141 | const subLoadersActiveTime = subLoaders.reduce((acc, loaders) => {
|
142 | acc[loaders[0].loader] = getTotalActiveTime(loaders);
|
143 | return acc;
|
144 | }, {});
|
145 |
|
146 | return {
|
147 | averages,
|
148 | activeTime,
|
149 | loaders: startEnds[0].loaders,
|
150 | subLoadersTime: subLoadersActiveTime,
|
151 | };
|
152 | });
|
153 |
|
154 | return { build: buildData };
|
155 | };
|