webpack 核心编译器 第三节

这一节中主要介绍了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();
					}
				);
			}
		);
	}
相关推荐
子春一214 分钟前
Flutter 2025 可访问性(Accessibility)工程体系:从合规达标到包容设计,打造人人可用的数字产品
前端·javascript·flutter
白兰地空瓶20 分钟前
别再只会调 API 了!LangChain.js 才是前端 AI 工程化的真正起点
前端·langchain
jlspcsdn1 小时前
20251222项目练习
前端·javascript·html
行走的陀螺仪2 小时前
Sass 详细指南
前端·css·rust·sass
爱吃土豆的马铃薯ㅤㅤㅤㅤㅤㅤㅤㅤㅤ2 小时前
React 怎么区分导入的是组件还是函数,或者是对象
前端·react.js·前端框架
LYFlied2 小时前
【每日算法】LeetCode 136. 只出现一次的数字
前端·算法·leetcode·面试·职场和发展
子春一22 小时前
Flutter 2025 国际化与本地化工程体系:从多语言支持到文化适配,打造真正全球化的应用
前端·flutter
QT 小鲜肉2 小时前
【Linux命令大全】001.文件管理之file命令(实操篇)
linux·运维·前端·网络·chrome·笔记
羽沢313 小时前
ECharts 学习
前端·学习·echarts