这一节中主要介绍了webpack 中的统计信息管理,日志获取以及模块构的添加以及缓存获取,之类的方法。
js
/**
* 获取当前编译的统计信息对象。
* @returns {Stats} 统计信息对象。
*/
getStats() {
return new Stats(this);
}
/**
* 创建并规范化统计选项。
* @param {string | boolean | StatsOptions | undefined} optionsOrPreset 统计选项或预设值。
* - 如果是 `boolean`:
* - `false` 变为 `{ preset: "none" }`
* - `true` 变为 `{ preset: "normal" }`
* - 如果是 `string`,转换为 `{ preset: optionsOrPreset }`
* @param {CreateStatsOptionsContext=} context 额外的上下文信息(可选)。
* @returns {NormalizedStatsOptions} 规范化的统计选项。
*/
createStatsOptions(optionsOrPreset, context = {}) {
if (typeof optionsOrPreset === "boolean") {
// 如果是布尔值,则转换为对应的预设值
optionsOrPreset = {
preset: optionsOrPreset === false ? "none" : "normal"
};
} else if (typeof optionsOrPreset === "string") {
// 如果是字符串,转换为预设值对象
optionsOrPreset = { preset: optionsOrPreset };
}
if (typeof optionsOrPreset === "object" && optionsOrPreset !== null) {
// 创建一个浅拷贝对象,以确保包含原型链上的属性
/** @type {Partial<NormalizedStatsOptions>} */
const options = {};
// 遍历所有属性进行拷贝
// eslint-disable-next-line guard-for-in
for (const key in optionsOrPreset) {
options[key] = optionsOrPreset[/** @type {keyof StatsOptions} */ (key)];
}
// 触发 `statsPreset` 钩子,根据预设修改选项
if (options.preset !== undefined) {
this.hooks.statsPreset.for(options.preset).call(options, context);
}
// 触发 `statsNormalize` 钩子,对选项进行最终规范化处理
this.hooks.statsNormalize.call(options, context);
return /** @type {NormalizedStatsOptions} */ (options);
}
/** @type {Partial<NormalizedStatsOptions>} */
const options = {};
// 触发 `statsNormalize` 钩子,确保选项符合规范
this.hooks.statsNormalize.call(options, context);
return /** @type {NormalizedStatsOptions} */ (options);
}
/**
* 创建一个统计数据工厂对象。
* @param {NormalizedStatsOptions} options 统计选项。
* @returns {StatsFactory} 统计数据工厂对象,用于生成统计数据。
*/
createStatsFactory(options) {
const statsFactory = new StatsFactory();
// 触发 `statsFactory` 钩子,允许外部修改 `statsFactory`
this.hooks.statsFactory.call(statsFactory, options);
return statsFactory;
}
/**
* 创建一个统计数据打印对象。
* @param {NormalizedStatsOptions} options 统计选项。
* @returns {StatsPrinter} 统计数据打印对象,用于格式化和输出统计信息。
*/
createStatsPrinter(options) {
const statsPrinter = new StatsPrinter();
// 触发 `statsPrinter` 钩子,允许外部修改 `statsPrinter`
this.hooks.statsPrinter.call(statsPrinter, options);
return statsPrinter;
}
/**
* 获取缓存实例
* @param {string} name 缓存的名称
* @returns {CacheFacade} 返回缓存外观对象
*/
getCache(name) {
return this.compiler.getCache(name);
}
/**
* 获取一个日志记录器
* @param {string | (function(): string)} name 日志记录器的名称,或返回名称的函数
* @returns {Logger} 具有指定名称的日志记录器
*/
getLogger(name) {
if (!name) {
throw new TypeError("Compilation.getLogger(name) called without a name");
}
/** @type {LogEntry[] | undefined} */
let logEntries;
return new Logger(
(type, args) => {
// 如果 `name` 是一个函数,则调用它获取日志名称
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compilation.getLogger(name) called with a function not returning a name"
);
}
}
let trace;
switch (type) {
case LogType.warn:
case LogType.error:
case LogType.trace:
// 生成错误堆栈跟踪信息,剔除加载器相关调用
trace = ErrorHelpers.cutOffLoaderExecution(
/** @type {string} */(new Error("Trace").stack)
).split("\n").slice(3);
break;
}
/** @type {LogEntry} */
const logEntry = {
time: Date.now(),
type,
args,
trace
};
// 触发 `log` 钩子,若未被拦截,则存储日志记录
if (this.hooks.log.call(name, logEntry) === undefined) {
if (
logEntry.type === LogType.profileEnd &&
typeof console.profileEnd === "function"
) {
console.profileEnd(
`[${name}] ${/** @type {NonNullable<LogEntry["args"]>} */ (logEntry.args)[0]}`
);
}
if (logEntries === undefined) {
logEntries = this.logging.get(name);
if (logEntries === undefined) {
logEntries = [];
this.logging.set(name, logEntries);
}
}
logEntries.push(logEntry);
if (
logEntry.type === LogType.profile &&
typeof console.profile === "function"
) {
console.profile(
`[${name}] ${/** @type {NonNullable<LogEntry["args"]>} */ (logEntry.args)[0]}`
);
}
}
},
// 获取子日志记录器
childName => {
if (typeof name === "function") {
if (typeof childName === "function") {
return this.getLogger(() => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compilation.getLogger(name) called with a function not returning a name"
);
}
}
if (typeof childName === "function") {
childName = childName();
if (!childName) {
throw new TypeError(
"Logger.getChildLogger(name) called with a function not returning a name"
);
}
}
return `${name}/${childName}`;
});
}
return this.getLogger(() => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compilation.getLogger(name) called with a function not returning a name"
);
}
}
return `${name}/${childName}`;
});
}
if (typeof childName === "function") {
return this.getLogger(() => {
if (typeof childName === "function") {
childName = childName();
if (!childName) {
throw new TypeError(
"Logger.getChildLogger(name) called with a function not returning a name"
);
}
}
return `${name}/${childName}`;
});
}
return this.getLogger(`${name}/${childName}`);
}
);
}
/**
* 添加模块到编译中
* @param {Module} module 要添加的模块
* @param {ModuleCallback} callback 回调函数,返回编译中的模块(新模块或已存在模块)
* @returns {void}
*/
addModule(module, callback) {
this.addModuleQueue.add(module, callback);
}
/**
* 内部方法:添加模块
* @param {Module} module 要添加的模块
* @param {ModuleCallback} callback 回调函数
* @returns {void}
*/
_addModule(module, callback) {
const identifier = module.identifier();
const alreadyAddedModule = this._modules.get(identifier);
if (alreadyAddedModule) {
return callback(null, alreadyAddedModule);
}
const currentProfile = this.profile
? this.moduleGraph.getProfile(module)
: undefined;
if (currentProfile !== undefined) {
currentProfile.markRestoringStart();
}
this._modulesCache.get(identifier, null, (err, cacheModule) => {
if (err) return callback(new ModuleRestoreError(module, err));
if (currentProfile !== undefined) {
currentProfile.markRestoringEnd();
currentProfile.markIntegrationStart();
}
if (cacheModule) {
cacheModule.updateCacheModule(module);
module = cacheModule;
}
this._modules.set(identifier, module);
this.modules.add(module);
if (this._backCompat)
ModuleGraph.setModuleGraphForModule(module, this.moduleGraph);
if (currentProfile !== undefined) {
currentProfile.markIntegrationEnd();
}
callback(null, module);
});
}
/**
* 根据模块对象获取已存在的模块
* @param {Module} module 提供的模块
* @returns {Module} 该模块
*/
getModule(module) {
const identifier = module.identifier();
return /** @type {Module} */ (this._modules.get(identifier));
}
/**
* 根据标识符查找模块
* @param {string} identifier 模块的标识符
* @returns {Module|undefined} 返回模块,若不存在则返回 `undefined`
*/
findModule(identifier) {
return this._modules.get(identifier);
}
/**
* 安排模块的构建
* @param {Module} module 需要构建的模块
* @param {ModuleCallback} callback 回调函数
* @returns {void}
*/
buildModule(module, callback) {
this.buildQueue.add(module, callback);
}
/**
* 内部方法:构建模块
* @param {Module} module 需要构建的模块
* @param {ModuleCallback} callback 回调函数
* @returns {void}
*/
_buildModule(module, callback) {
const currentProfile = this.profile
? this.moduleGraph.getProfile(module)
: undefined;
if (currentProfile !== undefined) {
currentProfile.markBuildingStart();
}
module.needBuild(
{
compilation: this,
fileSystemInfo: this.fileSystemInfo,
valueCacheVersions: this.valueCacheVersions
},
(err, needBuild) => {
if (err) return callback(err);
if (!needBuild) {
if (currentProfile !== undefined) {
currentProfile.markBuildingEnd();
}
this.hooks.stillValidModule.call(module);
return callback();
}
this.hooks.buildModule.call(module);
this.builtModules.add(module);
module.build(
this.options,
this,
this.resolverFactory.get("normal", module.resolveOptions),
this.inputFileSystem,
err => {
if (currentProfile !== undefined) {
currentProfile.markBuildingEnd();
}
if (err) {
this.hooks.failedModule.call(module, err);
return callback(err);
}
this.hooks.succeedModule.call(module);
return callback();
}
);
}
);
}