边学边想webpack5源码一起读(2)

引言

本文章基于webpack5源码进行解读,编写本文仅为方便作者学习以及记忆,如果有什么说的不对或者瞎猜的不对的地方,请大家指出,请勿乱喷,大家都是自己人感谢大家~

接上回

我们看到了webpack-cli的实例化的部分,虽然很想快速的读下去,但是我们首先要整理以下我们最之前的流程图,方便我们后续去梳理整个过程中发生的事情,作者在这里用自己的方式画了下来,大家也可以通过自己的方式进行进行记录
webpack流程图成长版本.png
上图基本能代表我们上一篇文章的全过程,感兴趣的可以自己点击查看

放一波冗余代码

// 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的模块放到下一篇中一起开始~

下一篇的跳转地址: 暂时没有~

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享