webpack命令-从零开始webpack源码思考1

    技术2024-04-17  91

    1、下载需要分析的项目

    分别是核心、cli、本地开发服务器

    2、看webpack-4.43.0

    package.json

    "main": "lib/webpack.js", "web": "lib/webpack.web.js", "bin": "./bin/webpack.js",

     

    命令行调用,执行bin对应js文件;nodejs代码调用执行main对应文件。

    3、./bin/webpack.js

    #!/usr/bin/env node // @ts-ignore process.exitCode = 0; /** * @param {string} command process to run * @param {string[]} args commandline arguments * @returns {Promise<void>} promise */ const runCommand = (command, args) => { const cp = require("child_process"); return new Promise((resolve, reject) => { const executedCommand = cp.spawn(command, args, { stdio: "inherit", shell: true }); executedCommand.on("error", error => { reject(error); }); executedCommand.on("exit", code => { if (code === 0) { resolve(); } else { reject(); } }); }); };

    process.exitCode,设置退出状态,0成功,1错误。

    runCommand,使用child_process开启子进程,执行命令,返回promise。inherit:继承父进程相关的stdio。

     

    /** * @param {string} packageName name of the package * @returns {boolean} is the package installed? */ const isInstalled = packageName => { try { require.resolve(packageName); return true; } catch (err) { return false; } };

    require.resolve执行成功,说明包存在,出现错误,则捕获说明不存在。

     

    /** * @typedef {Object} CliOption * @property {string} name display name * @property {string} package npm package name * @property {string} binName name of the executable file * @property {string} alias shortcut for choice * @property {boolean} installed currently installed? * @property {boolean} recommended is recommended * @property {string} url homepage * @property {string} description description */ /** @type {CliOption[]} */ const CLIs = [ { name: "webpack-cli", package: "webpack-cli", binName: "webpack-cli", alias: "cli", installed: isInstalled("webpack-cli"), recommended: true, url: "https://github.com/webpack/webpack-cli", description: "The original webpack full-featured CLI." }, { name: "webpack-command", package: "webpack-command", binName: "webpack-command", alias: "command", installed: isInstalled("webpack-command"), recommended: false, url: "https://github.com/webpack-contrib/webpack-command", description: "A lightweight, opinionated webpack CLI." } ];

    两个包的信息,webpack-cli和webpack-command,下面会用到。

    const installedClis = CLIs.filter(cli => cli.installed);

    过滤这两包,获取安装过的包组成的数组。

    if (installedClis.length === 0) { const path = require("path"); const fs = require("fs"); const readLine = require("readline"); let notify = "One CLI for webpack must be installed. These are recommended choices, delivered as separate packages:"; for (const item of CLIs) { if (item.recommended) { notify += `\n - ${item.name} (${item.url})\n ${item.description}`; } } console.error(notify); const isYarn = fs.existsSync(path.resolve(process.cwd(), "yarn.lock")); const packageManager = isYarn ? "yarn" : "npm"; const installOptions = [isYarn ? "add" : "install", "-D"]; console.error( `We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join( " " )}".` ); const question = `Do you want to install 'webpack-cli' (yes/no): `; const questionInterface = readLine.createInterface({ input: process.stdin, output: process.stderr }); questionInterface.question(question, answer => { questionInterface.close(); const normalizedAnswer = answer.toLowerCase().startsWith("y"); if (!normalizedAnswer) { console.error( "You need to install 'webpack-cli' to use webpack via CLI.\n" + "You can also install the CLI manually." ); process.exitCode = 1; return; } const packageName = "webpack-cli"; console.log( `Installing '${packageName}' (running '${packageManager} ${installOptions.join( " " )} ${packageName}')...` ); runCommand(packageManager, installOptions.concat(packageName)) .then(() => { require(packageName); //eslint-disable-line }) .catch(error => { console.error(error); process.exitCode = 1; }); }); }

    如果两个包,一个也没安装,就提示必须安装,这是推荐的包。

    使用 fs.existsSync,在当前工作目录,判断是否存在yarn.lock文件。

    存在,设置yarn add;否则npm install -D 。

    提示询问是否同意安装webpack-cli? yes/no

    使用readLine.createInterface,监控用户输入。

    questionInterface.question监控到输入,就关闭监控输入,转小写判断输入是否y开头,不是,就提示你得安装,你可以人工手动安装。

    并设置错误process.exitCode = 1;返回。

    如果输入的开头是y,就提示安装webpack-cli,并且执行安装命令,注意runCommand参数,第二个参数是数组,表示命令行命令的参数,child_process子进程的.spawn函数,也支持这个数组形式参数。

    前面runCommand返回promise,子进程执行退出时,如果code为0,表示成功执行命令,也就是成功安装,执行resolve。

    然后就到了then,为了确保成功安装了webpack-cli,就require(packageName)下试下。 如果异常,也是catch,提示错误,设置process.exitCode = 1。 子进程如果失败,非0,会走reject();这时也会到catch逻辑。 promise的错误传递体现了。

    else if (installedClis.length === 1) { const path = require("path"); const pkgPath = require.resolve(`${installedClis[0].package}/package.json`); // eslint-disable-next-line node/no-missing-require const pkg = require(pkgPath); // eslint-disable-next-line node/no-missing-require require(path.resolve( path.dirname(pkgPath), pkg.bin[installedClis[0].binName] )); }

    俩个如果有一个安装了,我们这时并不知道安装了哪儿个,不过无所谓,我们就取这个安装过的包的package.json文件路径,然后引用它。

    path.dirname(pkgPath),找到文件路径的目录路径。

    找到package.json内bin对象,进而根据安装过的cli的binName,找到对应bin对应的js路径。

    然后引用它。

    这里引用它,其实就是执行它,相当于执行命令行命令。

     

    else { console.warn( `You have installed ${installedClis .map(item => item.name) .join( " and " )} together. To work with the "webpack" command you need only one CLI package, please remove one of them or use them directly via their binary.` ); // @ts-ignore process.exitCode = 1; }

    如果两个都安装了,提示只需要一个,卸载一个吧,设置退出码=1。

     

     

     

     

    Processed: 0.010, SQL: 9