本章主要介绍了 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;