1.0.1 • Published 3 years ago

lwb_cli v1.0.1

Weekly downloads
-
License
ISC
Repository
-
Last release
3 years ago

CLI 脚手架工具

定义

CLI(command-line interface)是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。

必备模块

常用的脚手架工具,比如 create-react-app、vue-cli、angular-cli 等等,灰常方便,他们也使用了一堆 npm 包,或者功能类似的 npm 包。

包名功能
Commander用户入参解析,如 -help
inquirer常见的交互式命令行用户界面的集合
download-git-repo在 git 中下载模板
chalk改变控制台的字体颜色
metalsmith读取所有文件,实现模板渲染
consolidate统一模板引擎

实现功能,输入命令行,脚手架名 指令 入参: lwb-cli create myReact

准备工作

创建项目

mkdir lwb-cli

配置

npm init

链接全局包

    "bin": {
        "lwb-cli": "./bin/www"
    }

www 文件中,选择 node 环境,选择执行入口文件

    #! /usr/bin/env node
    require('../src/main.js');

开发期间链接到全局下使用

npm link

commander

命令行解析

   program
    .command("create")
    .alias("c")
    .description("新建一个项目")
    .action(() => {
        console.log("create");
    });

    // 根据不同的动作,动态引入对应模块的文件
    // path.resolve() 方法将路径或路径片段的序列解析为绝对路径。
    // process.argv 属性返回一个数组,就是用户在命令行中传入的参数。

    require(path.resolve(__dirname, action))(...process.argv.slice(3));

输出 version

version 一般存在于我们的 package 包里,包括项目名,全局变量单独存起来,新建一个 utils 文件夹,新建 constants 文件,导出 version、name

    program.version(version).parse(process.argv);

输出 help 信息

    program.on('--help', () => {
      console.log('Examples');
        Reflect.ownKeys(actionsMap).forEach((action) => {
            (actionsMap[action].examples || []).forEach((example) => {
                console.log(`  ${example}`);
            });
        });
    });

创建 create 命令

create 命令的主要作用就是去 git 仓库中拉取模板并下载对应的版本到本地,如果有模板则根据用户填写的信息渲染好模板,生成到当前运行命令的目录下~

    module.exports = async (projectName) => {
        console.log(projectName);
    };

拉项目

1、axios 2、github 的 API,获取当前组织中的所有仓库信息,https://api.github.com/orgs/项目名/repos

inquirer & ora

1、inquirer 询问式 2、ora loading 效果

    const spinner = ora('fetching repo list');
    spinner.start()
    spinner.succeed()

    const { repo } = await Inquirer.prompt({
        name: 'repo',
        type: 'list',
        message: '请选择一个项目来创建模板', choices: repos, // 选择模式
    });

下载模板 download-git-repo

这个方法不是 promise 方法、node 已经提供了现成方法

    const { promisify } = require('util');
    const downLoadGit = require('download-git-repo');
    downLoadGit = promisify(downLoadGit);

下载下来的文件,需要放到当前目录

    // 存储模板的位置
    const downloadDirectory =
        process.env[process.platform === "darwin" ? "HOME" : "USERPROFILE"]

ncp 实现文件的拷贝功能

    let ncp = require('ncp');
    ncp = promisify(ncp);
    // 将下载的文件拷贝到当前执行命令的目录下
    await ncp(target, path.join(path.resolve(), projectName));

模板编译

刚才说的是简单文件,那当然直接拷贝就好了,但是有的时候用户可以定制下载模板中的内容,拿 package.json 文件为例,用户可以根据提示给项目命名、设置描述等,在项目文件夹里增加 ask.js

    module.exports = [
    {
        type: 'confirm',
        name: 'private',
        message: 'ths resgistery is private?',
    }
]

根据对应的询问生成最终的 package.json,下载的模板中使用了 ejs 模板,核心原理就是将下载的模板文件,依次遍历根据用户填写的信息渲染模板,将渲染好的结果拷贝到执行命令的目录下

    "autor":"<%=author%>",
    "description": "<%=description%>",
    "license": "<%=license%>"

安装需要用到的模块 metalsmithn ejs consolidate

    // 下载下来的文件,如果有ask文件: 就是个复杂的模板,需要用户选择 ,然后编译模板
  if (!fs.existsSync(path.join(resultDirectory, "ask.js"))) {
    // 拿到下载到的目录之后,直接copy到当前目录下即可 ncp
    await ncp(resultDirectory, path.resolve(projectName));
    chalkSuccess(`创建项目成功 请执行\n cd ${projectName}`);
    process.exit();
  } else {
    // 1、让用户填信息
    await new Promise((resolve, reject) => {
      metalsmith(__dirname) //传入默认路径,默认遍历当前路径下的src文件夹
        .source(resultDirectory)
        //拷贝到用户指定目录下
        .destination(path.resolve(projectName))
        .use(async (files, metal, done) => {
          const asks = require(path.join(resultDirectory, "ask.js"));
          const userSelected = await Inquirer.prompt(asks);
          const meta = metal.metadata();
          Object.assign(meta, userSelected);
          delete files["ask.js"];
          done();
        })
        .use((files, metal, done) => {
          const metaData = metal.metadata();
          Reflect.ownKeys(files).forEach(async fileName => {
            //处理带 <%的命令
            if (fileName.includes("js") || fileName.includes("json")) {
              let content = files[fileName].contents.toString();
              // console.log("content: ", content)
              if (content.includes("<%")) {
                //渲染content
                content = await render(content, metaData);
                files[fileName].contents = Buffer.from(content);
              }
            }
          });
          done();
        })
        .build(err => {
          if (err) {
            chalkError(err);
            reject();
          } else {
            chalkSuccess(`创建项目成功 请执行 cd ${projectName}`);
            process.exit();
            resolve();
          }
        });
    }).catch({});
  }

项目发布

    //查看npm路径、一般都是淘宝,修改为正式的
    npm config get registry   //查看
    npm config set registry=http://registry.npm.org/ //设置

    //第一次发布需要登录

    npm login

    //发布
    npm publish