美文网首页
Node.js 开发 cli 工具

Node.js 开发 cli 工具

作者: zdxhxh | 来源:发表于2020-12-25 13:41 被阅读0次

Node.js 开发 cli 工具

一、为什么需要 cli 工具

我们见识过了很多的cli工具,例如 vue-cli、create-react-app、angular/cli等。它让开发者摆脱了wepack复杂繁琐的配置,快速、个性化地搭建项目,从而更好地专注业务层面的开发。

二、起步

cli的开发思路是复制拷贝,其实与拷贝git仓库无异。

创建一个文件夹,运行

npm init -y
cnpm i arg chalk esm inquirer listr lodash ncp pkg-install --save

上面是我们所需要的依赖,以下是各个安装包的说明

arg : 解析cli 命令行的参数

chalk : 让控制台打印颜色

esm : 这个包可以通过装饰 require,从而可以在模块中使用最新的ECMAScript语法

inquirer : 控制台交互式命令集合

lodash : JavaScript 实用工具库

ncp : 拷贝文件使用库

pkg-install : 用于在生成的项目中安装依赖

编辑 package.json

{
  "name": "zdxhxh-cli",
  "version": "0.0.1",
  "description": "cli 工具",
  "main": "src/main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "license": "ISC",
  "bin": {
    "xh-cli": "bin/create-project.js"
  },
  "author": {
    "name": "xh"
  },
  "keywords": [
    "cli",
    "create-project"
  ],
  "files": [
    "bin/",
    "src/",
    "templates/"
  ]
}

说明 :

  • bin : 用来指定内部命令对应可执行文件的位置,当全局安装时这个包时,由于node_modules/.bin/目录会在运行时,加入系统的PATH环境变量,所以在命令行行中直接通过命令来调用这些脚本
  • files : 用来指定那些目录会在发布时,上传到NPM服务器上。这里我们需要bin、src、template这几个目录

按照以下目录创建文件

├─bin
   └─ create-project.js
├─src
|  └─cli.js
|  └─main.js
│  └─utils
|      └─parseArgumentsIntoOptions.js
|      └─promptForMissingOptions.js
└─templates

三、编码

bin/create-project.js中 ,当我们运行 xh-cli时,就会执行该文件

#!/usr/bin/env node
// 这样包装后,引入的模块文件可以使用最新的 ES语法
require = require("esm")(module /*, options*/);
// 属性会返回一个数组,其中包含当 Node.js 进程被启动时传入的命令行参数
require("../src/cli").cli(process.argv);

接下来的目标是 : 解析命令行参数。

src/utils/parseArgumentsIntoOptions.js导出方法,代码如下

import arg from "arg";
import get from "lodash/get";
// 解析命令行参数 rawArgs即 process.argv
export default function (rawArgs) {
  const args = arg(
    {
      "--git": Boolean,
      "--yes": Boolean,
      "--install": Boolean,
      "--version": Boolean,
      "-y": "--yes",
      "-i": "--install",
      "-v": "--version",
    },
    {
      // process.argv除了前两个,其余的元素才是额外的命令行参数
      argv: rawArgs.slice(2), 
    }
  );
  return {
    skipPrompts: get(args, "--yes", false), // 快速跳过配置 
    version : get(args, "--version", false),
    template: args._[0],
    runInstall: get(args, "--install", false),  // 拷贝后自动安装依赖 
  };
}

接下来的目标是 : 在没有传入默认的语言参数下,让用户在命令行中选择参数.

src/utils/promptForMissingOptions.js导出方法,代码如下

import inquirer from "inquirer";
import get from "lodash/get";
export default async function promptForMissingOptions(options) {
  if (options.skipPrompts) {
    return {
      ...options,
      template: get(options, "template", "javascript")
    };
  }

  const questions = [];
  // 如果没有指定
  if (!options.template) {
    questions.push({
      type: "list",
      name: "template",
      message: "请选择壳工程语言",
      choices: ["javascript", "typescript"],
      default: "javascript"
    });
  }

  const answers = await inquirer.prompt(questions);
  return {
    ...options,
    template: get(options, "template", answers.template)
  };
}

src/cli.js中导出 cli 方法

import parseArgumentsIntoOptions from "./utils/parseArgumentsIntoOptions";
import promptForMissingOptions from "./utils/promptForMissingOptions";
import get from "lodash/get";
import pjson from "../package.json";
import chalk from "chalk";

export async function cli(args) {
  let options = parseArgumentsIntoOptions(args);
  // 打印版本信息
  if (get(options, "version")) {
    return console.log(chalk.green(`当前cli版本为 : ${pjson.version}`));
  }
  options = await promptForMissingOptions(options);
  console.log(options);
}

在命令行执行以下命令

npm link 
xh-cli

则会出现命令行交互

image-20201225105156614.png

接下来的目标 : 执行上述命令交互后,根据所选项将项目复制到当前执行命令的路径上。

我们在src/main.js中,增加以下代码

import chalk from "chalk";
import fs from "fs";
// 用于递归复制文件
import ncp from "ncp";
import path from "path";
import { promisify } from "util";
import Listr from "listr";
import { projectInstall } from "pkg-install";
import get from "lodash/get";

// 递归拷贝文件
const copy = promisify(ncp);

async function copyTemplateFiles(options) {
  return copy(options.templateDirectory, options.targetDirectory, {
    // 是否覆盖原有内容
    clobber: false
  });
}

export async function createProject(options) {
  options = {
    ...options,
    targetDirectory: get(
      options,
      "targetDirectory",
      path.resolve(process.cwd(), get(options, "template"))
    )
  };

  const currentFileUrl = import.meta.url;

  const templateDir = path.resolve(
    new URL(currentFileUrl).pathname.replace(/^\//, ""),
    "../../templates",
    options.template.toLowerCase()
  );

  options.templateDirectory = templateDir;

  const tasks = new Listr([
    {
      title: "拷贝中",
      task: () => copyTemplateFiles(options)
    },
    {
      title: "安装依赖",
      task: () =>
        projectInstall({
          cwd: options.targetDirectory
        }),
      skip: () =>
        !options.runInstall
          ? "Pass --install to automatically install dependencies"
          : undefined
    }
  ]);

  await tasks.run();
  console.log("%s Project ready", chalk.green.bold("DONE"));
  return true;
}

src/cli.js中增加以下代码

import { createProject } from "./main";
export async function cli(args) {
  // ...
  await createProject(options);
}

这样,cli工具的雏形已经完成了。

四、运行

template创建typescriptjavascript两个文件夹,并各自创建README.md文件

├─javascript
└─typescript

在任意目录下执行

xh-cli

选择任意一个,你看是不是复制成功了呢 ?

image-20201225110252405.png

当然,你通过以下命令在拷贝项目成功后添加依赖

xh-cli --install

最终,发布到npm 中

npm publish 

相关文章

网友评论

      本文标题:Node.js 开发 cli 工具

      本文链接:https://www.haomeiwen.com/subject/jagbnktx.html