time-analytics-webpack-plugin
Version:
analytize the time of loaders and plugins
143 lines • 6.99 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.analyzer = exports.WebpackMetaEventType = exports.PluginEventType = exports.TapType = exports.LoaderEventType = exports.LoaderType = exports.AnalyzeInfoKind = void 0;
const ramda_1 = require("ramda");
const utils_1 = require("./utils");
const writer_1 = require("./writer");
var AnalyzeInfoKind;
(function (AnalyzeInfoKind) {
AnalyzeInfoKind[AnalyzeInfoKind["loader"] = 0] = "loader";
AnalyzeInfoKind[AnalyzeInfoKind["plugin"] = 1] = "plugin";
AnalyzeInfoKind[AnalyzeInfoKind["webpackMeta"] = 2] = "webpackMeta";
})(AnalyzeInfoKind = exports.AnalyzeInfoKind || (exports.AnalyzeInfoKind = {}));
var LoaderType;
(function (LoaderType) {
LoaderType[LoaderType["pitch"] = 0] = "pitch";
LoaderType[LoaderType["normal"] = 1] = "normal";
})(LoaderType = exports.LoaderType || (exports.LoaderType = {}));
var LoaderEventType;
(function (LoaderEventType) {
LoaderEventType[LoaderEventType["start"] = 0] = "start";
LoaderEventType[LoaderEventType["end"] = 1] = "end";
})(LoaderEventType = exports.LoaderEventType || (exports.LoaderEventType = {}));
var TapType;
(function (TapType) {
TapType[TapType["normal"] = 0] = "normal";
TapType[TapType["async"] = 1] = "async";
TapType[TapType["promise"] = 2] = "promise";
})(TapType = exports.TapType || (exports.TapType = {}));
var PluginEventType;
(function (PluginEventType) {
PluginEventType[PluginEventType["start"] = 0] = "start";
PluginEventType[PluginEventType["end"] = 1] = "end";
})(PluginEventType = exports.PluginEventType || (exports.PluginEventType = {}));
/**
* The exact one hook of webpack
*
* Name format is `${hooks container name}_${hook name}`
*/
var WebpackMetaEventType;
(function (WebpackMetaEventType) {
WebpackMetaEventType[WebpackMetaEventType["Compiler_compile"] = 0] = "Compiler_compile";
WebpackMetaEventType[WebpackMetaEventType["Compiler_done"] = 1] = "Compiler_done";
})(WebpackMetaEventType = exports.WebpackMetaEventType || (exports.WebpackMetaEventType = {}));
class WebpackTimeAnalyzer {
constructor() {
this._isInitilized = false;
this.loaderData = [];
this.pluginData = [];
this.metaData = [];
}
initilize() {
(0, utils_1.assert)(this._isInitilized === false, '${PACKAGE_NAME} is initialized twice. This should be a bug if it is not intentional. Please submit an issue.');
this._isInitilized = true;
}
clear() {
(0, utils_1.assert)(this._isInitilized === true, 'Time Analyzer must be initialized when clearing.');
this._isInitilized = false;
this.loaderData = [];
this.pluginData = [];
this.metaData = [];
}
collectLoaderInfo(loaderInfo) {
this.loaderData.push(loaderInfo);
}
collectPluginInfo(pluginInfo) {
this.pluginData.push(pluginInfo);
}
collectWebpackInfo(metaInfo) {
this.metaData.push(metaInfo);
}
output(option) {
(0, utils_1.assert)(this._isInitilized === true, 'Time Analyzer must be initialized when outputing.');
const tmp1 = analyticsOutputMetaInfo(this.metaData);
const tmp2 = analyticsPluginInfos(this.pluginData);
const tmp3 = analyticsOutputLoaderInfos(this.loaderData, { ignoredLoaders: option.ignoredLoaders });
writer_1.Writer.writeResult(tmp1, tmp2, tmp3, option);
this.clear();
}
}
function isArraySortBy(paths, arr) {
let prevValue = 0;
for (const item of arr) {
const curValue = (0, ramda_1.path)(paths, item);
(0, utils_1.assert)(typeof curValue === 'number');
if (curValue < prevValue) {
return false;
}
prevValue = curValue;
}
return true;
}
function analyticsOutputMetaInfo(data) {
// validate
(0, utils_1.assert)(isArraySortBy(['time'], data), 'webpack meta event info should be sorted by time.');
const compilerCompileEvents = data.filter(info => info.hookType === WebpackMetaEventType.Compiler_compile);
(0, utils_1.assert)(compilerCompileEvents.length === 1, 'webpack must start only once');
const compilerDoneEvents = data.filter(info => info.hookType === WebpackMetaEventType.Compiler_done);
(0, utils_1.assert)(compilerDoneEvents.length === 1, 'webpack must done only once');
const compileTotalTime = compilerDoneEvents[0].time - compilerCompileEvents[0].time;
return {
totalTime: compileTotalTime,
};
}
function analyticsPluginInfos(data) {
(0, utils_1.assert)(isArraySortBy(['time'], data), 'plugin event info should be sorted by time.');
const res = { pluginsInfo: [] };
const nameGrouppedPlugin = (0, ramda_1.groupBy)((0, ramda_1.prop)('pluginName'), data);
Object.entries(nameGrouppedPlugin).forEach(([pluginName, dataA]) => {
let currentPluginTotalTime = 0;
const idGroupedPlugin = (0, ramda_1.groupBy)((0, ramda_1.prop)('tapCallId'), dataA);
Object.entries(idGroupedPlugin).forEach(([tapCallId, dataB]) => {
(0, utils_1.assert)(dataB.length === 2
&& dataB[0].eventType === PluginEventType.start
&& dataB[1].eventType === PluginEventType.end, 'each tap execution should be collected info for start and end, once and only once.');
const tapTime = dataB[1].time - dataB[0].time;
currentPluginTotalTime += tapTime;
});
res.pluginsInfo.push({ name: pluginName, time: currentPluginTotalTime });
});
return res;
}
function analyticsOutputLoaderInfos(data, options) {
(0, utils_1.assert)(isArraySortBy(['time'], data), 'loader event info should be sorted by time.');
const res = { loadersInfo: [] };
const nameGrouppedLoader = (0, ramda_1.groupBy)((0, ramda_1.prop)('loaderPath'), data);
Object.entries(nameGrouppedLoader).forEach(([loaderPath, dataA]) => {
let currentLoaderTotalTime = 0;
if (!options.ignoredLoaders.some(ignoredLoader => loaderPath.includes(ignoredLoader))) {
const idGroupedPlugin = (0, ramda_1.groupBy)((0, ramda_1.prop)('callId'), dataA);
Object.entries(idGroupedPlugin).forEach(([callId, dataB]) => {
(0, utils_1.assert)(dataB.length === 2
&& dataB[0].eventType === LoaderEventType.start
&& dataB[1].eventType === LoaderEventType.end, `each laoder execution should be collected info for start and end, once and only once. But for ${loaderPath}, there is an error, why?`);
const tapTime = dataB[1].time - dataB[0].time;
currentLoaderTotalTime += tapTime;
});
}
res.loadersInfo.push({ path: loaderPath, time: currentLoaderTotalTime });
});
return res;
}
exports.analyzer = new WebpackTimeAnalyzer();
//# sourceMappingURL=analyzer.js.map