webpack 入口文件

157 阅读3分钟

本章主要介绍了 webapck 中的入口文件其中有创建编译勾子与多个webpack 配置的创建,还有webpack 的入口函数

/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

// 引入 Node.js 内置的 util 模块
const util = require("util");

// 引入 Webpack 选项校验工具
const webpackOptionsSchemaCheck = require("../schemas/WebpackOptions.check.js");
const webpackOptionsSchema = require("../schemas/WebpackOptions.json");

// 引入 Webpack 核心组件
const Compiler = require("./Compiler"); // 单个编译器
const MultiCompiler = require("./MultiCompiler"); // 多编译器(用于多配置)
const WebpackOptionsApply = require("./WebpackOptionsApply"); // 处理 Webpack 选项
const {
	applyWebpackOptionsDefaults,
	applyWebpackOptionsBaseDefaults
} = require("./config/defaults"); // Webpack 默认选项处理
const { getNormalizedWebpackOptions } = require("./config/normalization"); // 选项标准化工具
const NodeEnvironmentPlugin = require("./node/NodeEnvironmentPlugin"); // Webpack 运行环境插件
const memoize = require("./util/memoize"); // 缓存工具函数

// 获取 Webpack 选项校验函数(懒加载)
const getValidateSchema = memoize(() => require("./validateSchema"));

/**
 * @callback Callback
 * @template T
 * @param {Error | null} err - 错误对象(如果有)
 * @param {T=} stats - 编译结果对象(可选)
 */

/**
 * 创建 MultiCompiler(多编译器实例)
 * @param {ReadonlyArray<WebpackOptions>} childOptions - Webpack 配置数组
 * @param {MultiCompilerOptions} options - MultiCompiler 选项
 * @returns {MultiCompiler} - 返回 MultiCompiler 实例
 */
const createMultiCompiler = (childOptions, options) => {
	// 逐个创建 Compiler 实例
	const compilers = childOptions.map((options, index) =>
		createCompiler(options, index)
	);
	// 创建 MultiCompiler 实例
	const compiler = new MultiCompiler(compilers, options);
	// 处理编译器之间的依赖关系
	for (const childCompiler of compilers) {
		if (childCompiler.options.dependencies) {
			compiler.setDependencies(
				childCompiler,
				childCompiler.options.dependencies
			);
		}
	}
	return compiler;
};

/**
 * 创建 Compiler(单个编译器实例)
 * @param {WebpackOptions} rawOptions - Webpack 配置对象
 * @param {number} [compilerIndex] - 编译器索引(多编译器时使用)
 * @returns {Compiler} - 返回 Compiler 实例
 */
const createCompiler = (rawOptions, compilerIndex) => {
	// 标准化 Webpack 配置
	const options = getNormalizedWebpackOptions(rawOptions);
	// 应用基本默认配置
	applyWebpackOptionsBaseDefaults(options);
	// 创建 Compiler 实例
	const compiler = new Compiler(
		/** @type {string} */ (options.context),
		options
	);
	// 启用 Node.js 运行环境插件
	new NodeEnvironmentPlugin({
		infrastructureLogging: options.infrastructureLogging
	}).apply(compiler);
	// 加载 Webpack 插件
	if (Array.isArray(options.plugins)) {
		for (const plugin of options.plugins) {
			if (typeof plugin === "function") {
				/** @type {WebpackPluginFunction} */
				(plugin).call(compiler, compiler);
			} else if (plugin) {
				plugin.apply(compiler);
			}
		}
	}
	// 应用 Webpack 选项的默认值
	const resolvedDefaultOptions = applyWebpackOptionsDefaults(
		options,
		compilerIndex
	);
	if (resolvedDefaultOptions.platform) {
		compiler.platform = resolvedDefaultOptions.platform;
	}
	// 触发 Webpack 生命周期钩子
	compiler.hooks.environment.call();
	compiler.hooks.afterEnvironment.call();
	// 处理 Webpack 选项(插件系统)
	new WebpackOptionsApply().process(options, compiler);
	// 触发初始化钩子
	compiler.hooks.initialize.call();
	return compiler;
};

/**
 * Webpack 入口函数
 * @param {WebpackOptions | (ReadonlyArray<WebpackOptions> & MultiCompilerOptions)} options - Webpack 配置
 * @param {Callback<Stats> & Callback<MultiStats>=} callback - 可选的回调函数
 * @returns {Compiler | MultiCompiler | null} - 返回 Compiler 或 MultiCompiler
 */
const webpack = (options, callback) => {
	const create = () => {
        // 循环验证选项中的每个选项是否符合webpack 的规则
		if (!asArray(options).every(webpackOptionsSchemaCheck)) {
			getValidateSchema()(webpackOptionsSchema, options);
		}
		let compiler;
		let watch = false;
		let watchOptions;
                // 判断 webpack 配置选项是否是数组
		if (Array.isArray(options)) {
                        // 如果是数组就使用多编译器实例
			compiler = createMultiCompiler(options, options);
			watch = options.some(options => options.watch);
			watchOptions = options.map(options => options.watchOptions || {});
                        // 否则使用单个编译器实例
		} else {
			const webpackOptions = options;
                        // 获取编译器使用句柄并传入选项
			compiler = createCompiler(webpackOptions);
                        /// 获取和选项中的监听
			watch = webpackOptions.watch;
                        // 获取选项中的监听选项
			watchOptions = webpackOptions.watchOptions || {};
		}
                // 返回句柄
		return { compiler, watch, watchOptions };
	};
        // 如果回调函数为真
	if (callback) {
		try {
                    // 创建编译勾子并获取句柄
			const { compiler, watch, watchOptions } = create();
			// 如果监听为真
                        if (watch) {
                        // 在编译句柄中调用watch 并传入选项与回调
				compiler.watch(watchOptions, callback);
                                // 否则
			} else {
                        // 使用编译勾子运行
				compiler.run((err, stats) => {
					compiler.close(err2 => {
						callback(err || err2, stats);
					});
				});
			}
			return compiler;
		} catch (err) {
                // 捕获到错误时执行 nextTick 表示在当前线程执行完成之后再执行
			process.nextTick(() => callback(err));
			return null;
		}
	} else {
        // 如果没有回调了籽当我直接获取编译与监听句柄
		const { compiler, watch } = create();
		if (watch) {
                // 如果监听为真,使用工具调用deprecate 方法
			util.deprecate(() => {}, "必须提供 callback 以处理 watch 选项。")();
		}
		return compiler;
	}
};

module.exports = webpack;