开发属于自己的脚手架(xx-cli) 第二篇

1,042 阅读4分钟

上一篇讲了一下cli的准备知识后,这一篇就到真正cli脚手架的核心文件

我们来重温一下,对cli需求分解一下,

用户层面:

需求:输入cli 命令 -> 终端选择 -> 输入基本信息 -> 文件生成

用户.png

cli脚手架:

需求:接收命令 -> 进行命令获取和验证 -> 输出用户选择的指令 -> 实现指令功能 -> 下载模板(文件拷贝,生成) -> 安装依赖

cli.png

创建目录文件

image.png

  • template : cli生成的项目模板文件;
  • .gitignore : git屏蔽文件,即不让git文件上传;
  • cli.js : cli脚手架核心代码文件;
  • package : 不解释啦……
  • README : 不解释啦……

cli.js 核心介绍

commander:解析用户输入的命令

引入

//commande.js 可以自动的解析命令和参数,用于处理用户输入的命令。
const program = require('commander');

使用

image.png

 当用户输入 sliao-cli -v  //输出 ======Dark,sliao-cli====== version: 1.1.0
 当用户输入 sliao-cli init test 就会进入action中;

inquirer 命令行交互

引入

    // 设置环境变量的 文件头
    const inquirer = require("inquirer");

使用

image.png 备注:此处demo展示就只列举了单项

当用户输入 sliao-cli init name 后,脚手架收到指令,解析然后提供配置参数输入

image.png

inquirer.then 进行逻辑书写;demo这里以生成html模板为例;

其他介绍

//chalk, 可以给终端的字体加上颜色
const chalk = require('chalk');
//log-symbols, 可以在终端上显示出 √ 或者 × 等的图标。
const symbols = require('log-symbols');

image.png

如 chalk 给终端上增加颜色,更加美观

脚手架代码实现

项目文件目录

image.png

脚手架相关配置在package.json

{
  "name": "sliao-cli",  //脚手架名称
  "version": "1.1.0",   //当前包版本
  "description": "cli test ",  //描述
  "bin": "cli.js",    //入口文件,脚手架核心入口文件
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  ……
}

创建可执行文件

我们的可执行文件cli.js,当前为了方便直接放在更目录 此时我们要配置当前脚手架的执行环境,所以在cli文件的头部添加

#!/usr/bin/env node

该信息必须在头部,不能在其他顶部添加任何信息,否则会导致报错

开发核心文件cli.js

  • inquirer 解析到用户操作后,对应执行脚手架核心文件;
  • 此脚手架以生成html模板为例:
  • 解析用户操作后执行拷贝文件,然后将模板生成对应文件夹,并生成对应模板文件;

直接上代码:

      inquirer
        .prompt([
          {
            type: "input",
            name: "name",
            message: "project name is ",
          },
        ])
        .then((answers) => {
          // 模板目录
          const tmlDir = path.join(__dirname, "templates");
          // 目标目录
          const destDir = process.cwd();
          // 读取模板文件,将模板下的文件全部转换到目标目录
          fs.readdir(tmlDir, (err, files) => {
            if (err) throw err;
            files.forEach((file) => {
              // console.log(file);
              // 通过模板引擎渲染文件
              ejs.renderFile(path.join(tmlDir, file), answers, (error, result) => {
                if (error) throw error;
                // console.log(result)
                // 将结果写入模板
                fs.writeFileSync(path.join(destDir, file), result);
              });
            });
          });
        })

将我们的cli链接到全局,以便全局可以使用

  • 进入项目文件
  • 执行npm link
  • 链接成功后即可使用全局命令

备注:使用命令调试方法 1.使用命令sliao-cli init name 2.使用node自带命令

image.png

到现在基本就完成了我们自定义的一个cli脚手架工程

执行

  • 新建一个文件夹
  • 进入文件夹,执行脚手架
  • 将生成对应html模板 image.png (截图为sliao-cli脚手架现有版本;欢迎使用sliao-cli脚手架;)

cli.js源码

#!/usr/bin/env node

// 设置环境变量的 文件头
const inquirer = require("inquirer");
const path = require("path");
const fs = require("fs");
const ejs = require("ejs");
//commande.js 可以自动的解析命令和参数,用于处理用户输入的命令。
const program = require('commander');
//chalk, 可以给终端的字体加上颜色
const chalk = require('chalk');
//log-symbols, 可以在终端上显示出 √ 或者 × 等的图标。
const symbols = require('log-symbols');



/**
 * @description: program.version 调用该命令时 (如 sliao-cli -v) 会带出版本号:x.x.x
 * @description: program.command 定义初始化命令 (如 sliao-cli init <项目名>)
 * @description:  program.action action 是执行command命令时发生的回调
 * @param:  node index.js    init test == sliao-cli init test
 * @returns:  program.prase(process.argv)解析命令行中的参数,解析出name,并传入action回调
 */
program.version(chalk.green('======Dark,sliao-cli====== \n  version: 1.1.0'), '-v, --version').
  command('init <name>').
  action(name => {
    console.log(name)
    //fs.existsSync 如果路径存在,则返回 true,否则返回 false
    if (!fs.existsSync(name)) {
      console.log(chalk.magentaBright('正在创建项目(Creating project)……'));
      inquirer
        .prompt([
          {
            type: "input",
            name: "name",
            message: "project name is ",
          },
        ])
        .then((answers) => {
          // 模板目录
          const tmlDir = path.join(__dirname, "templates");
          // 目标目录
          const destDir = process.cwd();
          // 读取模板文件,将模板下的文件全部转换到目标目录
          fs.readdir(tmlDir, (err, files) => {
            if (err) throw err;
            files.forEach((file) => {
              // console.log(file);
              // 通过模板引擎渲染文件
              ejs.renderFile(path.join(tmlDir, file), answers, (error, result) => {
                if (error) throw error;
                // console.log(result)
                // 将结果写入模板
                fs.writeFileSync(path.join(destDir, file), result);
              });
            });
          });
        })
        .catch((error) => {
          // Something else went wrong
          throw error;
        });
    } else {
      console.log(symbols.error,chalk.red('项目依旧存在'));
    }
  })
program.parse(process.argv);

github源码欢迎Fork,让我们共同学习; github

如果喜欢这个cli,欢迎打赏一杯咖啡

WeChat