/**
* @file plugins/analyze/hooks.js
* @copyright @spmhome @_2025
* @author Scott Meesseman @spmeesseman
*//** */
const WpwAnalyzePlugin = require("./base");
const { withColor } = require("@spmhome/log-utils");
const { hookRunsWithGlobalError } = require("../../utils/constants");
const { isFunction, epochToMSMS } = require("@spmhome/type-utils");
/**
* @augments WpwAnalyzePlugin
*/
class WpwHooksAnalyzePlugin extends WpwAnalyzePlugin
{
/**
* @private
* @type {string[]}
*/
added;
/**
* @private
* @type {number}
*/
last;
/**
* @private
* @type {number}
*/
start;
/**
* @param {WpwPluginOptions} options Plugin options to be applied
*/
constructor(options)
{
super(options);
this.added = [];
this.start = 0;
this.buildOptions = /** @type {WpwHooksAnalyzePluginOptions} */(this.buildOptions);
}
/**
* @override
*/
static create = WpwHooksAnalyzePlugin.wrap.bind(this);
/**
* @override
*/
onApply() { this.hookSteps(); }
/**
* @private
* @param {WebpackCompilationHookName} hook
* @param {Lowercase<WebpackCompilationHookStage>} [processAssetStage]
* @param {function(any): any} [cb]
*/
addCompilationHook(hook, processAssetStage, cb)
{
const compilationHook = this.build.compilation.hooks[hook];
if (hook === "processAssets" && processAssetStage)
{
const stageEnum = `PROCESS_ASSETS_STAGE_${processAssetStage.toUpperCase()}`,
stage = this.build.wp.Compilation[stageEnum],
hookName = "compilation_processAssets_" + stageEnum;
if (!this.added.includes(hookName))
{
/** @type {WebpackHook<any, any>} */(
compilationHook).tap(
{ stage, name: `${this.name}_${hook}_${processAssetStage}` },
(/** @type {any} */_arg) => this.writeBuildTag(`${hook}::${processAssetStage}`, true)
);
this.added.push(hookName);
}
}
else if (isFunction(/** @type {WebpackHook<any, any>} */(compilationHook).tap))
{
const hookName = "compilation_" + hook;
if (!this.added.includes(hookName))
{
/** @type {WebpackHook<any, any>} */(compilationHook).tap(
`${this.name}_${hook}`, (/** @type {any} */_) =>
{
this.writeBuildTag(hook.toString(), true);
return cb?.(_);
});
this.added.push(hookName);
}
}
};
/**
* @private
* @param {WebpackCompilerHookName} hook
* @param {function(any): any} [cb]
*/
addCompilerHook(hook, cb)
{
const hookName = "compiler_" + hook;
if (!this.added.includes(hookName))
{
this.compiler.hooks[hook].tap(`${this.name}_${hook}`, () =>
{
this.writeBuildTag(hook.toString(), false);
return cb?.();
});
this.added.push(hookName);
}
};
// /**
// * @private
// * @param {WebpackCompilerAsyncHookName} hook
// */
// addCompilerHookPromise(hook)
// {
// this.compiler.hooks[hook].tapPromise(`${hook}LogHookPromisePlugin`, async () => this.writeBuildTag(hook, false));
// };
/**
* @private
*/
hookSteps()
{
this.addCompilerHook("environment", () => { if (!this.start) { this.start = Date.now(); }});
this.addCompilerHook("beforeRun", () => { if (!this.start) { this.start = Date.now(); }});
this.addCompilerHook("done",
() => { if (!this.build.elapsed) { this.build.elapsed = Date.now() - this.start; }}
);
this.addCompilerHook("failed",
() => { if (!this.build.elapsed) { this.build.elapsed = Date.now() - this.start; }}
);
this.addCompilerHook("shutdown",
() => { if (!this.build.elapsed) { this.build.elapsed = Date.now() - this.start; }}
);
if (this.buildOptions.compiler !== false)
{
this.addCompilerHook("infrastructureLog");
this.addCompilerHook("afterEnvironment");
this.addCompilerHook("entryOption");
this.addCompilerHook("afterPlugins");
this.addCompilerHook("afterResolvers");
this.addCompilerHook("initialize");
this.addCompilerHook("run");
this.addCompilerHook("normalModuleFactory");
this.addCompilerHook("contextModuleFactory");
this.addCompilerHook("beforeCompile");
this.addCompilerHook("compile");
}
if (this.buildOptions.compilation)
{
const _getCompilationHooks = () => (/** @type {WebpackCompilation} */compilation) =>
{
// this.compilation = compilation;
const options = this.buildOptions.compilation;
if (options.all || options.default || options.processAssets)
{
this.addCompilationHook("additionalAssets");
this.addCompilationHook("processAssets", "additional");
this.addCompilationHook("processAssets", "pre_process");
this.addCompilationHook("processAssets", "derived");
this.addCompilationHook("processAssets", "additions");
this.addCompilationHook("processAssets", "optimize");
this.addCompilationHook("processAssets", "optimize_count");
this.addCompilationHook("processAssets", "optimize_compatibility");
this.addCompilationHook("processAssets", "optimize_size");
this.addCompilationHook("processAssets", "dev_tooling");
this.addCompilationHook("processAssets", "optimize_inline");
this.addCompilationHook("processAssets", "summarize");
this.addCompilationHook("processAssets", "optimize_hash");
this.addCompilationHook("processAssets", "optimize_transfer");
this.addCompilationHook("processAssets", "analyse");
this.addCompilationHook("processAssets", "report");
this.addCompilationHook("afterProcessAssets");
}
if (options.all || options.default)
{
this.addCompilationHook("beforeCodeGeneration");
this.addCompilationHook("afterCodeGeneration");
this.addCompilationHook("beforeRuntimeRequirements");
this.addCompilationHook("afterRuntimeRequirements");
this.addCompilationHook("record");
this.addCompilationHook("seal");
this.addCompilationHook("afterSeal");
this.addCompilationHook("needAdditionalSeal");
this.addCompilationHook("renderManifest");
this.addCompilationHook("beforeModuleAssets");
this.addCompilationHook("moduleAsset");
this.addCompilationHook("assetPath");
this.addCompilationHook("chunkAsset");
this.addCompilationHook("beforeChunkAssets");
this.addCompilationHook("shouldGenerateChunkAssets");
this.addCompilationHook("needAdditionalPass");
this.addCompilationHook("childCompiler");
this.addCompilationHook("log");
this.addCompilationHook("processWarnings");
this.addCompilationHook("processErrors");
this.addCompilationHook("statsNormalize");
this.addCompilationHook("statsFactory");
this.addCompilationHook("statsPrinter");
}
if (options.all || options.hash)
{
this.addCompilationHook("beforeHash");
this.addCompilationHook("afterHash");
this.addCompilationHook("beforeModuleHash");
this.addCompilationHook("afterModuleHash");
this.addCompilationHook("contentHash");
this.addCompilationHook("chunkHash");
this.addCompilationHook("fullHash");
this.addCompilationHook("recordHash");
}
if (options.all || options.optimization)
{
this.addCompilationHook("optimizeAssets");
this.addCompilationHook("afterOptimizeAssets");
}
// if (options.deprecated)
// {
// this.addCompilationHook("statsPreset");
// this.addCompilationHook("additionalChunkAssets");
// this.addCompilationHook("optimizeChunkAssets");
// this.addCompilationHook("afterOptimizeChunkAssets");
// this.addCompilationHook("processAdditionalAssets"); // not deprecated but ~ as processAssets|ADDITIONAL
// }
};
this.addCompilerHook("compilation", _getCompilationHooks());
this.addCompilerHook("thisCompilation");
}
else {
this.addCompilerHook("compilation");
this.addCompilerHook("thisCompilation");
}
if (this.buildOptions.compiler !== false)
{
this.addCompilerHook("make");
this.addCompilerHook("finishMake");
this.addCompilerHook("afterCompile");
// /** @param {WebpackCompilation} compilation */(compilation) =>
// {
// // const stats = compilation.getStats();
// // stats.toJson().
// if (l.level >= 4)
// {
// const assets = compilation.getAssets();
// l.write("compilation step completed, list all assets", 4, "", null, l.colors.white);
// for (const asset of assets)
// {
// l.writeMsgTag(asset.name, "ASSET", null, 4, " ", null, l.colors.grey);
// l.value(" asset info", JSON.stringify(asset.info), 5);
// }
// }
// });
this.addCompilerHook("shouldEmit");
this.addCompilerHook("emit");
this.addCompilerHook("assetEmitted");
this.addCompilerHook("afterEmit");
this.addCompilerHook("afterDone");
this.addCompilerHook("additionalPass");
this.addCompilerHook("invalid");
this.addCompilerHook("watchRun");
this.addCompilerHook("watchClose");
}
}
/**
* @private
* @param {string} hook
* @param {boolean} isCompilation
*/
writeBuildTag(hook, isCompilation)
{
const key = hook + this.build.wpc.name;
if ((!this.store.data[key] || this.build.log.level >= 4) &&
(!this.build.hasGlobalError || hookRunsWithGlobalError.includes(hook)))
{
const now = Date.now(),
l = this.build.logger,
activePad = l.staticPad,
tagTm1 = `hk::${epochToMSMS(now - this.last, true)}`,
tagTm2 = `rt::${epochToMSMS(now - this.start, true)}`,
tagHk = (!isCompilation? "compiler" : "compilation") + " hooks";
// tagHk = l.tag(`${!isCompilation? "compiler" : "compilation"} hook`);
l.staticPad = "";
this.store.data[key] = true;
// l.valuestar(`${tagHk} ${hook}`, hook, 1, "", 1, null, [ tagTm1, tagTm2 ]);
// l.valuestar(tagHk, hook, 1, "", 1, null, [ tagTm1, tagTm2 ]);
const icon = withColor(l.icons.star, l.color);
l.write(
`${icon} execute ${tagHk}: ${hook.toLowerCase()} ${icon}`,
1, "", null, null, false, null, [ tagTm1, tagTm2 ]
);
l.staticPad = activePad;
this.last = Date.now();
}
};
}
module.exports = WpwHooksAnalyzePlugin.create;