引言
本文章基于webpack5源码进行解读,编写本文仅为方便作者学习以及记忆,如果有什么说的不对或者瞎猜的不对的地方,请大家指出,请勿乱喷,大家都是自己人感谢大家~
接上回
我们看到了webpack-cli
的实例化的部分,虽然很想快速的读下去,但是我们首先要整理以下我们最之前的流程图,方便我们后续去梳理整个过程中发生的事情,作者在这里用自己的方式画了下来,大家也可以通过自己的方式进行进行记录
上图基本能代表我们上一篇文章的全过程,感兴趣的可以自己点击查看
放一波冗余代码
// node_modules/webpack-cli/lib/bootstrap.js
const WebpackCLI = require('./webpack-cli');
const utils = require('./utils');
const runCLI = async (args, originalModuleCompile) => {
try {
// Create a new instance of the CLI object
const cli = new WebpackCLI();
cli._originalModuleCompile = originalModuleCompile;
await cli.run(args);
} catch (error) {
utils.logger.error(error);
process.exit(2);
}
};
module.exports = runCLI;
复制代码
我们上一次学习到了这个地方,那么这里我们主要关注的其实就三行内容,实例化=>赋值=>运行run函数,那么首先我们来看一下在构造的过程中发生了一些什么内容~
//node_modules/webpack-cli/lib/webpack-cli.js
const fs = require('fs');
const path = require('path');
const { pathToFileURL } = require('url');
const Module = require('module');
const { program, Option } = require('commander');
const utils = require('./utils');
class WebpackCLI {
constructor() {
// Global
this.webpack = require(process.env.WEBPACK_PACKAGE || 'webpack');
this.logger = utils.logger;
this.utils = utils;
// Initialize program
this.program = program;
this.program.name('webpack');
this.program.configureOutput({
writeErr: this.logger.error,
outputError: (str, write) => write(`Error: ${this.utils.capitalizeFirstLetter(str.replace(/^error:/, '').trim())}`),
});
}
// 暂时省略
...
}
复制代码
我们从constructor(...)
中了解到,其实主要是做了一些基础私有变量的挂载,分别是webpack
,logger
,utils
,program
。回到bootstrap.js
文件,cli._originalModuleCompile = originalModuleCompile;
这一行代码,我们可以在webpack-cli.js 1332:1342行
找到对应的一些内容,我们发现这个还跟v8-compile-cache
的issues有点关系点击这里 感兴趣的同学可以看一下,这一块暂时不是我们主要路径上的内容,我们先打个标签,最后作为能力扩展的部分再回来看到这里。
那么接下来的话,主要就是我们run(...)
函数,为了关注我们主路径,这里我删去了大量的过程代码,大家如果自己感兴趣,可以自己私下慢慢研究~
//node_modules/webpack-cli/lib/webpack-cli.js
class WebpackCLI {
...
async run(args, parseOptions) {
// 由于commander的这个命令行三方库所以我们关注到action的这个hook
this.program.action(async (options, program) => {
...
// 上方省略了大量的参数处理
let commandToRun = "build"
if (isKnownCommand(commandToRun)) {
await loadCommandByName(commandToRun, true);
}
...
}
// 这里请大家忽略一下暂时性死区的问题,调整顺序主要是为了方便阅读
const loadCommandByName() = async (commandName, allowToInstall = false) => {
const isBuildCommandUsed = isCommand(commandName, buildCommandOptions); // true
const isWatchCommandUsed = isCommand(commandName, watchCommandOptions); // false
if (isBuildCommandUsed || isWatchCommandUsed) {
const options = this.getBuiltInOptions();
await this.makeCommand(
isBuildCommandUsed ? buildCommandOptions : watchCommandOptions,
isWatchCommandUsed ? options.filter((option) => option.name !== 'watch') : options,
async (entries, options) => {
if (entries.length > 0) {
options.entry = [...entries, ...(options.entry || [])];
}
// 关键节点进入下方函数,为了方便大家阅读,我就后续单独把buildCommand抽了出来
await this.buildCommand(options, isWatchCommandUsed);
},
);
}
...
}
}
async buildCommand(options, isWatchCommand) {
...
// 前面省略大段进入createCompiler
compiler = await this.createCompiler(options, callback);
if (!compiler) {
return;
}
const isWatch = (compiler) =>
compiler.compilers ? compiler.compilers.some((compiler) => compiler.options.watch) : compiler.options.watch;
if (isWatch(compiler) && this.needWatchStdin(compiler)) {
process.stdin.on('end', () => {
process.exit(0);
});
process.stdin.resume();
}
}
async createCompiler(options, callback) {
this.applyNodeEnv(options);
let config = await this.resolveConfig(options);
config = await this.applyOptions(config, options);
config = await this.applyCLIPlugin(config, options);
let compiler;
try {
// 最终感天动地,我们在这里使用到了我们在实例化中所require进来的webpack进入了webpack的过程
compiler = this.webpack(
config.options,
callback
? (error, stats) => {
if (error && this.isValidationError(error)) {
this.logger.error(error.message);
process.exit(2);
}
callback(error, stats);
}
: callback,
);
} catch (error) {
if (this.isValidationError(error)) {
this.logger.error(error.message);
} else {
this.logger.error(error);
}
process.exit(2);
}
// TODO webpack@4 return Watching and MultiWatching instead Compiler and MultiCompiler, remove this after drop webpack@4
if (compiler && compiler.compiler) {
compiler = compiler.compiler;
}
return compiler;
}
...
}
复制代码
从上述代码总结来看,就是webpack-cli使用commander
做了大量的命令行设置,参数处理(格式化),最终落入webpack
的生命周期,ok 为了文章的完整性,作者会把webpack
的模块放到下一篇中一起开始~
下一篇的跳转地址: 暂时没有~