webpack的提示安装cli包是如何实现的?

261 阅读2分钟

使用webpack时,如果没有安装webpack-cli会自动提示你安装,并且给出选项yes/no,输入选择并敲enter键则开始安装,做的人性化的,特意拿来研究一下,分享给掘友。如果你想做Cli工具本文可能对你有帮助。

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情

webpack执行路线分析

我们一般会运行npx webpack XXX,结合package.json的设置:

"bin": {
  "webpack": "bin/webpack.js"
},

可以得知入口文件为/bin/webpack.js

打开代码,扫一眼大概的运行图:

image.png

我们关注的点来到了!cli.installed分支:

if(!cli.installed){
    //提示需要安装 cli
    console.error(
       `We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join(
          " "
       )} ${cli.package}".`
    );
    //征求用户的同意
    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 => {
        //用户输入只要以y(不分大小写)开头即是同意安装
        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."
           );

           return;
        }
        //执行安装
        runCommand(packageManager, installOptions.concat(cli.package))
           .then(() => {
              runCli(cli);
           })
           .catch(error => {
              console.error(error);
              process.exitCode = 1;
           });
    })
}

ok,看完上述简单的流程,我们总结一下:

  • 检测是否安装了某个包:existsSync,isDirectory
  • 没有则,文字提示并开启获取用户输入的程序: readline.createInterface
  • 对用户的输入处理并判断是否开始安装: 子进程child_process.spawn

来一个简单版的自动安装cli

这里,我们会省掉一些不必要的配置,如bin文件指向等,需要提示安装。

我们来实现一个简单版的检测并提示安装lodash的程序。

const path = require("path");
const fs = require('fs')
const readLine = require('readline')
const cp = require("child_process");
//判断是否安装
const isInstalled = packageName => {
    let dir = __dirname;
    const targetPath = path.join(dir, "node_modules", packageName)
    if (
        fs.existsSync(targetPath) && fs.statSync(targetPath).isDirectory()
    ) {
        return true;
    } else {
        return false;
    }
};

//开始主程序
if (!isInstalled('lodash')) {
    const question = '您需要安装lodash,才能继续,您愿意这样做吗?(yes/no)'

    const popupInterface = readLine.createInterface({
        input: process.stdin,
        output: process.stderr
    });
    popupInterface.question(question, answer => {
        //关闭用户交互
        popupInterface.close();
        const yes = answer.toLowerCase().startsWith("y");
        if (yes) {
            const executedCommand = cp.spawn('npm', ['install', 'lodash', '--save'], {
                stdio: "inherit",
                shell: true
            });

            executedCommand.on("error", error => {
               console.error('安装失败,请稍后重试')
            });

            executedCommand.on("exit", code => {
                if (code === 0) {
                    console.log('恭喜你已经成功安装了lodash')
                } else {
                    console.log('安装失败,请稍后重试')
                }
            });
        } else {
            console.log('您依然可以以后安装lodash,再见!')
        }
    })
} else {
    console.log('恭喜你已经安装了lodash')
}

效果如下:

image.png

再次运行则提示:

image.png