Nodejs how-to-write-a-node-cli-tools-with-commander

Nodejs how-to-write-a-node-cli-tools-with-commander

how-to-write-a-node-cli-tools

什么是cli tools?

CLI(command-line interface,命令行界面)是指可在用户提示符下键入可执行指令的界面。

早启的unix/linux和DOS都是没有可视化界面的,那内存靠计算着用的时代都只能玩玩命令。

node cli

好处

  • 现在火,前端以及服务器端必备技能
  • js语法,非常好写
  • 对版本要求不像ruby那样苛刻

缺点,暂未知,看以后node的路吧

做法

创建git repo

git clone到本地

git clone git@github.com:i5ting/node-cli-demo.git

切换到项目目录

cd node-cli-demo

初始化npm

执行

npm init

一直回车,除非你真的有东西想改动,具体如下

➜  node-cli-demo git:(master) npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.

See npm help json for definitive documentation on these fields and exactly what they do.

Use npm install <pkg> --save afterwards to install a package and save it as a dependency in the package.json file.

Press ^C at any time to quit. name: (node-cli-demo) version: (1.0.0) description: entry point: (index.js) test command: git repository: (https://github.com/i5ting/node-cli-demo.git) keywords: author: license: (ISC) About to write to /Users/sang/workspace/github/node-cli-demo/package.json:

{ “name”: “node-cli-demo”, “version”: “1.0.0”, “description”: “node-cli-demo =============”, “main”: “index.js”, “scripts”: { “test”: “echo “Error: no test specified” && exit 1” }, “repository”: { “type”: “git”, “url”: “https://github.com/i5ting/node-cli-demo.git” }, “author”: “”, “license”: “ISC”, “bugs”: { “url”: “https://github.com/i5ting/node-cli-demo/issues” }, “homepage”: “https://github.com/i5ting/node-cli-demo” }

Is this ok? (yes) ➜ node-cli-demo git:(master) ✗

创建文件

mkdir bin
mkdir test

touch bin/node-cli-demo.js
touch test/node-cli-demo.js

touch index.js
touch gulpfile.js

修改package.json

命令配置(至关重要)

  "preferGlobal": "true",
  "bin": {
    "badge": "bin/badge.js"
  },

此处是关键

preferGlobal确定你的这个命令是不是全局的,一定要设置为true,不然不放到path里,不能全局用的。

bin是配置你的cli名称和具体哪个文件来执行这个的

依赖

  "devDependencies": {
    "chai": "^1.9.2",
    "gulp": "^3.8.10",
    "gulp-istanbul": "^0.3.1",
    "gulp-mocha": "^1.1.1",
    "istanbul": "^0.3.2",
    "mocha": "^2.0.1",
    "sinon": "^1.11.1",
    "supertest": "^0.14.0",
    "supervisor": "^0.6.0",
    "zombie": "^2.1.1"
  },
  "dependencies": {
    "commander": "^2.5.0",
		"config": "^1.7.0",
    "handlebars": "^2.0.0",
    "request": "^2.47.0"
  },

当前npm依赖dependencies

  • commander (命令辅助lib)
  • config (配置项)
  • handlebars (模板操作)
  • request (http请求库)

如果这些不够的话

npm install --save xxx

开发阶段的依赖devDependencies

  • mocha(测试相关)
  • gulp (项目构建相关的)

如果这些不够的话

npm install --save-dev xxx

scripts

  "scripts": {
    "start": "npm publish .",
    "test": " node bin/badge.js -t js -n q "
  },

这里定义了2个命令

  • npm start

这里我用它发布当前npm到npmjs.org上

  • npm test

这里我用它作为测试代码,避免每次都重复输入

修改 bin/node-cli-demo.js

	#!/usr/bin/env node
	/**
	 * Module dependencies.
	 */
	function isDefined(x) { return x !== null && x !== undefined; } 
	Array.prototype.contain = function(obj) {
	  return this.indexOf(obj) !== -1;
	}
var program = require('commander');
var version = require("../package.json").version;

program
  .version(version)
	.usage(" badge -n badge-cli -f [md] -t [npm] ")
	.option('-n, --name [name]', 'npm name,for example: q')
  .option('-f, --format [format]', '可选值:url, markdown(默认值), html, textile, rdoc, asciidoc, rst')
  .option('-t, --type [type]', '可选值:npm(默认值), ruby    , python    , bower    , github    , nuget    , php    , cocoapods    , perl  ')
	.option('-v, --verbose', '打印详细日志')
  .parse(process.argv);

var module_name = '';
if(isDefined(program.name) == true && typeof program.name == 'string' ){
	module_name = program.name;
}else{
	console.log('没有知道模块名称,请重新输入,比如\n\t badge -n badge-cli -f [md] -t [npm] ');
	return;
}

var format = "markdown";
var type = "js";

if (program.format) {
	format = program.format;
}

if (program.type) {
	type = program.type;
}

var verbose = false;
if (program.verbose) {
	verbose = program.verbose;
}

var FORMATS = ['url', 'markdown', 'html', 'textile', 'rdoc', 'asciidoc', 'rst']

if (FORMATS.contain(format) == false) {
	console.log('-f 可选值:url, markdown(默认值), html, textile, rdoc, asciidoc, rst');
	return;
}

var _verbose = verbose;
function log(str){
	if(_verbose == true){
		console.log(str);
	}
}

log('format = ' + format);
log('type = ' + type);
log('name = ' + module_name);
log('verbose = ' + verbose);

// main 
// require('../index')(module_name, type, format, verbose);

注意

  • shabang用法
	#!/usr/bin/env node
  • commander用法
	var program = require('commander');
	var version = require("../package.json").version;
program
  .version(version)
	.usage(" badge -n badge-cli -f [md] -t [npm] ")
	.option('-n, --name [name]', 'npm name,for example: q')
  .option('-f, --format [format]', '可选值:url, markdown(默认值), html, textile, rdoc, asciidoc, rst')
  .option('-t, --type [type]', '可选值:npm(默认值), ruby    , python    , bower    , github    , nuget    , php    , cocoapods    , perl  ')
	.option('-v, --verbose', '打印详细日志')
  .parse(process.argv);

还是比较简单的

  • 参数的默认值
	var module_name = '';
	if(isDefined(program.name) == true && typeof program.name == 'string' ){
		module_name = program.name;
	}else{
		console.log('没有知道模块名称,请重新输入,比如\n\t badge -n badge-cli -f [md] -t [npm] ');
		return;
	}
  • 主文件
	// main 
  require('../index')(module_name, type, format, verbose);

注意主文件参数按需求定义

编写index.js

module.exports = function (module_name, type, format,verbose) {
	console.log('i am main file for cli');
}

编写test/node-cli-demo.js

var assert = require('chai').assert;
var expect = require('chai').expect;
require('chai').should();

describe(‘Node-cli-demo’, function(){ before(function() { // runs before all tests in this block }) after(function(){ // runs after all tests in this block }) beforeEach(function(){ // runs before each test in this block }) afterEach(function(){ // runs after each test in this block })

describe(’#save()’, function(){ it(‘should return test2 when user save’, function(){ assert.equal(1, 1); }) }) })

增加自动测试和代码覆盖率

修改gulpfile.js

	var gulp = require('gulp');
	var istanbul = require('gulp-istanbul');
	var mocha = require('gulp-mocha'); 
gulp.task('test', function (cb) {
  gulp.src(['db/**/*.js'])
    .pipe(istanbul()) // Covering files
    .on('finish', function () {
      gulp.src(['test/*.js'])
        .pipe(mocha({
					ui : 'bdd',
					reporter: 'spec'
				}))
        .pipe(istanbul.writeReports()) // Creating the reports after tests runned
        .on('end', cb);
    });
});

gulp.task('default',['test'], function() {
  gulp.watch(['./db/**/*','./test/**/*'], ['test']);
});

gulp.task('watch',['test'], function() {
  gulp.watch(['./db/**/*','./test/**/*'], ['test']);
});

注意自己修改目录

  • src目录
  • 测试目录

测试

node_modules/.bin/gulp 

这时,你试试修改测试或源文件试试,看看会不会自动触发测试

当然,如果你喜欢只是测试一次,可以这样做

node_modules/.bin/gulp test 

如果你不熟悉gulp,可以再这里https://github.com/i5ting/js-tools-best-practice/blob/master/doc/Gulp.md学习

发布

npm start

技术

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

推荐

版本历史

  • v0.1.0 初始化版本

欢迎fork和反馈

如有建议或意见,请在issue提问或邮件

License

this repo is released under the MIT License.

欢迎关注我的公众号【node全栈】 node全栈.png


6 回复

如何使用Node.js和Commander编写CLI工具

什么是CLI工具?

CLI(Command-Line Interface,命令行界面)是指可以在用户提示符下键入可执行指令的界面。早期的Unix/Linux和DOS系统都没有图形界面,所有操作都需要通过命令行完成。

Node.js CLI工具的好处

  • 当前热门:前端和后端开发中都必不可少。
  • 易学性:使用熟悉的JavaScript语法。
  • 低版本要求:不像Ruby那样对版本有严格的要求。

编写步骤

创建Git仓库
git clone git@github.com:i5ting/node-cli-demo.git
切换到项目目录
cd node-cli-demo
初始化npm
npm init

默认情况下,你可以直接回车,直到生成package.json文件。

创建必要的文件和目录
mkdir bin
mkdir test

touch bin/node-cli-demo.js
touch test/node-cli-demo.js

touch index.js
touch gulpfile.js
修改package.json
命令配置(至关重要)
{
  "preferGlobal": "true",
  "bin": {
    "node-cli-demo": "bin/node-cli-demo.js"
  }
}
  • preferGlobal: 确定你的命令是否全局可用。设置为true表示全局安装。
  • bin: 配置CLI工具名称及其对应的执行文件。
依赖
{
  "devDependencies": {
    "chai": "^1.9.2",
    "gulp": "^3.8.10",
    "gulp-istanbul": "^0.3.1",
    "gulp-mocha": "^1.1.1",
    "istanbul": "^0.3.2",
    "mocha": "^2.0.1",
    "sinon": "^1.11.1",
    "supertest": "^0.14.0",
    "supervisor": "^0.6.0",
    "zombie": "^2.1.1"
  },
  "dependencies": {
    "commander": "^2.5.0",
    "config": "^1.7.0",
    "handlebars": "^2.0.0",
    "request": "^2.47.0"
  }
}
脚本
{
  "scripts": {
    "start": "npm publish .",
    "test": "node bin/node-cli-demo.js -t js -n q"
  }
}
修改bin/node-cli-demo.js
#!/usr/bin/env node
/**
 * Module dependencies.
 */
function isDefined(x) { return x !== null && x !== undefined; }

Array.prototype.contain = function(obj) {
  return this.indexOf(obj) !== -1;
}

var program = require('commander');
var version = require("../package.json").version;

program
  .version(version)
  .usage("node-cli-demo -n badge-cli -f [md] -t [npm]")
  .option('-n, --name [name]', 'npm name, for example: q')
  .option('-f, --format [format]', '可选值:url, markdown(默认值), html, textile, rdoc, asciidoc, rst')
  .option('-t, --type [type]', '可选值:npm(默认值), ruby, python, bower, github, nuget, php, cocoapods, perl')
  .option('-v, --verbose', '打印详细日志')
  .parse(process.argv);

var module_name = '';
if(isDefined(program.name) && typeof program.name === 'string') {
  module_name = program.name;
} else {
  console.log('没有知道模块名称,请重新输入, 比如\n\t node-cli-demo -n badge-cli -f [md] -t [npm]');
  return;
}

var format = "markdown";
var type = "js";

if (program.format) {
  format = program.format;
}

if (program.type) {
  type = program.type;
}

var verbose = false;
if (program.verbose) {
  verbose = program.verbose;
}

var FORMATS = ['url', 'markdown', 'html', 'textile', 'rdoc', 'asciidoc', 'rst'];

if (!FORMATS.contain(format)) {
  console.log('-f 可选值:url, markdown(默认值), html, textile, rdoc, asciidoc, rst');
  return;
}

var _verbose = verbose;
function log(str) {
  if (_verbose) {
    console.log(str);
  }
}

log('format = ' + format);
log('type = ' + type);
log('name = ' + module_name);
log('verbose = ' + verbose);

// 主函数
require('../index')(module_name, type, format, verbose);
编写index.js
module.exports = function (module_name, type, format, verbose) {
  console.log('i am main file for cli');
};
编写测试文件test/node-cli-demo.js
var assert = require('chai').assert;
var expect = require('chai').expect;
require('chai').should();

describe('Node-cli-demo', function() {
  before(function() {
    // 运行前的操作
  });

  after(function() {
    // 运行后的操作
  });

  beforeEach(function() {
    // 每个测试前的操作
  });

  afterEach(function() {
    // 每个测试后的操作
  });

  describe('#save()', function() {
    it('should return test2 when user save', function() {
      assert.equal(1, 1);
    });
  });
});
增加自动测试和代码覆盖率
var gulp = require('gulp');
var istanbul = require('gulp-istanbul');
var mocha = require('gulp-mocha');

gulp.task('test', function(cb) {
  gulp.src(['db/**/*.js'])
    .pipe(istanbul()) // 覆盖文件
    .on('finish', function() {
      gulp.src(['test/*.js'])
        .pipe(mocha({
          ui: 'bdd',
          reporter: 'spec'
        }))
        .pipe(istanbul.writeReports()) // 创建报告
        .on('end', cb);
    });
});

gulp.task('default', ['test'], function() {
  gulp.watch(['./db/**/*', './test/**/*'], ['test']);
});

gulp.task('watch', ['test'], function() {
  gulp.watch(['./db/**/*', './test/**/*'], ['test']);
});

测试

node_modules/.bin/gulp

或者只运行一次测试:

node_modules/.bin/gulp test

发布

npm start

技术

贡献

  • Fork仓库
  • 创建新分支
  • 提交更改
  • 推送到分支
  • 创建Pull Request

版本历史

  • v0.1.0:初始化版本

许可证

本项目采用MIT许可证。


demo 位置https://github.com/i5ting/node-cli-demo

推荐更好的写法

https://github.com/montagejs/minit/blob/master/cli.js

var fs = require("fs");
var path = require("path");

var Command = require(“commander”).Command;

var config = require("./package.json"); var create = require("./lib/create"); var serve = require("./lib/serve");

var cli = new Command(); //extras cli.minitHome = __dirname + “/”; // cli.version(config.version); create.addCommandsTo(cli); var serveCommand = cli.command(‘serve’) .description(‘serve current directory with minit server.’) .action(function(env){ serve.serve(env); }); serve.addOptions(serveCommand);

exports.command = cli;

视频http://haoqicat.com/shiren1118/nodejs/1

针对“Nodejs how-to-write-a-node-cli-tools-with-commander”这个主题,下面将详细介绍如何使用Node.js结合commander库来编写一个命令行工具。

步骤概述

  1. 初始化项目:使用npm init生成package.json
  2. 安装依赖:安装必要的依赖包如commander
  3. 创建入口文件:在项目中创建入口文件,用于定义CLI的行为。
  4. 配置bin路径:在package.json中配置bin路径,以便命令行工具可以通过命令直接调用。
  5. 实现逻辑:在入口文件中实现命令行工具的功能。
  6. 测试与发布:编写测试用例,并通过npm start发布工具。

示例代码

1. 初始化项目

mkdir node-cli-demo
cd node-cli-demo
npm init -y

2. 安装依赖

npm install commander --save

3. 创建入口文件

bin目录下创建node-cli-demo.js

#!/usr/bin/env node
const program = require('commander');

program
  .version('1.0.0')
  .description('A simple CLI tool')
  .option('-m, --message [value]', 'Your custom message')
  .parse(process.argv);

console.log(`Your message: ${program.message || 'No message provided.'}`);

4. 配置package.json

{
  "name": "node-cli-demo",
  "version": "1.0.0",
  "description": "A simple CLI tool",
  "main": "index.js",
  "bin": {
    "node-cli-demo": "bin/node-cli-demo.js"
  },
  "scripts": {
    "start": "npm publish .",
    "test": "node bin/node-cli-demo.js -m 'Test message'"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "chai": "^1.9.2",
    "mocha": "^2.0.1"
  },
  "dependencies": {
    "commander": "^2.5.0"
  }
}

5. 实现逻辑

入口文件bin/node-cli-demo.js已包含基本的CLI逻辑。

6. 测试与发布

npm test
npm start

总结

以上步骤展示了如何使用Node.js和commander库创建一个简单的命令行工具。此工具接受一个可选的--message参数并输出消息。你可以根据需要扩展更多功能和参数。

回到顶部