关于node.js:Node交互式命令行工具开发自动化文档工具

3次阅读

共计 4559 个字符,预计需要花费 12 分钟才能阅读完成。

nodejs开发命令行工具,流程绝对简略,但一套残缺的命令行程序开发流程下来,还是须要下点功夫,网上材料大多零散,这篇教程意在整合一下残缺的开发流程。
npm 上命令行开发相干包很多,例如 minimistoptimistnoptcommander.jsyargs 等等,应用办法和成果相似。其中用得比拟多的是 TJ 大神的 commander 和 yargs,本文以 commander 为根底讲述,能够参考这篇教程,yargs 教程能够参考阮大神的或者这一篇。
另外,一个残缺的命令行工具开发,还须要理解 processshelljspathlinebyline 等模块,这些都是 node 根底模块或一些简略模块,非常简单,就不多说了,另外如果你不想用回调函数解决异步还须要理解一下 PromiseGenerator 函数。这是教程:i5ting大神的《深入浅出 js(Node.js)异步流程管制》和阮大神的异步编程教程以及 promise 小人书,另外想尝试 ES7 stage3 阶段的 async/await 异步解决方案,可参考这篇教程,async/await解决方案须要 babel 转码,这是教程。自己喜爱 async/await(哪个node 开发者不喜爱呢?)但不喜爱倒腾,况且 async/await 自身就是 Promise 的语法糖,所以没抉择应用,据江湖音讯,nodejs将在往年晚些时候(10 月份?)反对async/await,很是期待。
以下是文章开端实例用到的一些依赖。

"dependencies": {
    "bluebird": "^3.4.1",
    "co": "^4.6.0",
    "colors": "^1.1.2",
    "commander": "^2.9.0",
    "dox": "^0.9.0",
    "handlebars": "^4.0.5",
    "linebyline": "^1.3.0",
    "mkdirp": "^0.5.1"
  }

其中 bluebird 用于 Promise 化,TJ 大神的 co 用于执行 Generator 函数,handlebars是一种模板,linebyline用于分行读取文件,colors用于丑化输入,mkdirp用于创立目录,另外教程中的示例是一款工具,能够自动化生成数据库和 API 接口的 markdown 文档,并通过批改 git hooks,使我的项目的每次commit 都会自动更新文档,借助了 TJ 大神的 dox 模块。
<span style=”color:rgb(0, 136, 204)”> 所有举荐教程 / 教材,仅供参考,自行甄选浏览。</span>

装置 Node

各操作系统下装置见 Nodejs 官网,装置实现之后用 node -v 或者 which node 等命令测试装置是否胜利。which在命令行开发中是一个十分有用的命令,应用 which 命令确保你的零碎中不存在名字雷同的命令行工具,例如 which commandName,例如which testdev 命令返回空白那么阐明 testdev 命令名称还没有被应用。

初始化

  1. 新建一个 .js 文件,即是你的命令要执行的主程序入口文件,例如 testdev.js。在文件第一行退出#!/usr/bin/env node 指明零碎在运行这个文件的时候应用 node 作为解释器,等价于 node testdev.js 命令。
  2. 初始化 package.json 文件,应用 npm init 命令依据提示信息创立,也能够是应用 npm init -y 应用默认设置创立。创立实现之后须要批改 package.json 文件内容退出 "bin": {"testdev": "./testdev.js"} 这条信息用于通知 npm 你的命令 (testdev) 要执行的脚本文件的门路和名字,这里咱们指定 testdev 命令的执行文件为当前目录下的 testdev.js 文件。
  3. 为了不便测试在 testdev.js 文件中退出代码console.log('hello world');,这里只是用于测试环境是否搭建胜利,更加简单的程序逻辑和过程须要依照理论状况进行编写

测试

应用 npm link 命令,能够在本地装置刚刚创立的包,而后就能够用 testdev 来运行命令了,如果失常的话在控制台会打印出hello world

commander

TJ 的 commander 十分简洁,README.md曾经把应用办法写的十分清晰。上面是例子中的代码:

const program = require('commander'),
  co = require('co');

const appInfo = require('./../package.json'),
  asyncFunc = require('./../common/asyncfunc.js');

program.allowUnknownOption();
program.version(appInfo.version);

program
  .command('init')
  .description('初始化当前目录 doc.json 文件')
  .action(() => co(asyncFunc.initAction));

program
  .command('show')
  .description('显示配置文件状态')
  .action(() => co(asyncFunc.showAction));

program
  .command('run')
  .description('启动程序')
  .action(() => co(asyncFunc.runAction));

program
  .command('modifyhook')
  .description('批改我的项目下的 hook 文件')
  .action(() => co(asyncFunc.modifyhookAction));

program
  .command('*')
  .action((env) => {console.error('不存在命令"%s"', env);
  });

program.on('--help', () => {console.log('Examples:');
  console.log('');
  console.log('$ createDOC --help');
  console.log('$ createDOC -h');
  console.log('$ createDOC show');
  console.log('');
});

program.parse(process.argv);

定义了四个命令和个性化帮忙阐明。

交互式命令行 process

commander只是实现了命令行参数与回复一对一的固定性能,也就是一个命令必然对应一个回复,那如何实现人机交互式的命令行呢,相似 npm init 或者 eslint --init 这样的与用户交互,交互之后依据用户的不同需要反馈不同的后果呢。这里就须要 node 内置的 process 模块。
这是我实现的一个 init 命令性能代码:

exports.initAction = function* () {
  try {var docPath = yield exists(process.cwd() + '/doc.json');
    if (docPath) {
      func.initRepl(config.coverInit, arr => {co(newDoc(arr));
      })
    } else {
      func.initRepl(config.newInit, arr => {co(newDoc(arr));
      })
    }
  } catch (err) {console.warn(err);
  }

首先查看 doc.json 文件是否存在,如果存在执行笼罩交互,如果不存在执行生成交互,try...catch捕捉谬误。
交互内容配置如下:

    newInit:
    [
        {
            title:'initConfirm',
            description:'初始化 createDOC, 生成 doc.json. 确认?(y/n)',
            defaults: 'y'
        },
        {
            title:'defaultConfirm',
            description:'是否应用默认配置.(y/n)',
            defaults: 'y'
        },
        {
            title:'showConfig',
            description:'是否显示 doc.json 以后配置?(y/n)',
            defaults: 'y'
        }
    ],
    coverInit:[
        {
            title:'modifyConfirm',
            description:'doc.json 已存在,初始化将覆盖文件. 确认?(y/n)',
            defaults: 'y'
        },
        {
            title:'defaultConfirm',
            description:'是否应用默认配置.(y/n)',
            defaults: 'y'
        },
        {
            title:'showConfig',
            description:'是否显示 doc.json 以后配置?(y/n)',
            defaults: 'y'
        }
    ],

人机交互局部代码也就是 initRepl 函数内容如下:

// 初始化命令,人机交互管制
exports.initRepl = function (init, func) {
  var i = 1;
  var inputArr = [];
  var len = init.length;
  process.stdout.write(init[0].description);
  process.stdin.resume();
  process.stdin.setEncoding('utf-8');
  process.stdin.on('data', (chunk) => {chunk = chunk.replace(/[\s\n]/, '');
    if (chunk !== 'y' && chunk !== 'Y' && chunk !== 'n' && chunk !== 'N') {console.log(config.colors.red('您输出的命令是:' + chunk));
      console.warn(config.colors.red('请输出正确指令:y/n'));
      process.exit();}
    if ((init[i - 1].title === 'modifyConfirm' || init[i - 1].title === 'initConfirm') &&
      (chunk === 'n' || chunk === 'N')
    ) {process.exit();
    }
    var inputJson = {title: init[i - 1].title,
      value: chunk,
    };
    inputArr.push(inputJson);
    if ((len--) > 1) {process.stdout.write(init[i++].description)
    } else {process.stdin.pause();
      func(inputArr);
    }
  });
}

人机交互才用向用户发问依据用户不同输出产生不同后果的模式进行,程序读取发问列表并记录用户输出后果,如果用户输出 n/N 则终止交互,用户输出非法字符(除 y/Y/n/N 以外)提醒输出命令谬误。

文档自动化

文档自动化,其中数据库文档自动化,才用依赖 sequelize 的办法手写(依据需要不同自行编写逻辑),API文档才用 TJ 的 dox 也很简略。因为此处代码与命令行性能相关度不大,请读者自行去示例地址查看代码。

示例地址

github 地址
npm 地址

正文完
 0