@ 2fd/ 命令 
在Node.js中构建命令行工具的模块化,面向对象的框架, 包含 TypeScript 定义.
零从属.
@2fd/command没有运行时间依赖: 安装 它在你的包里加了一个node_modules。唯一的依赖关系 是 TypeScript,用于编译源.
安装
npm install --save @2fd/command
支持的 Node.js 版本是 CI 测试的版本: 从 0.12至26 (英语).
支持的TypeScript版本是CI测试的版本: 2.0, 2.4, 2.9, 3.9,
4.9, 5.x, 6.x 和最新发布. 这个 types 任务安装打包
软件包输入 compat/ 并用每个版本检查它。 TS 2.0 电话
改为 5.x 使用 moduleResolution: node,和 TS 5.x 并随后使用 nodenext 与
strict。 。 。 .
目标
-
无依取相. 没有别的东西被拉入你的树里,所以有 没有用于审计、更新或补丁的中转软件包.
-
懒装入. 命令可以注册为指向它的字符串 模块(
'./commands#migrate') (中文(简体) ). 模块只是required 当 命令运行(或当帮助被打印时),那么大的工具快速启动. -
命令名属于工具而非包. 那个工具 注册命令决定如何引用(
migration:init, (中文(简体) ),mgt:i(中文(简体) ). 第三方包导出命令; 您选择需要的, 重命名 或者换成你自己的. -
完全模块化. 命令、参数、旗帜、输入和输出都很小 界面后面的物体,所以任何一块都可以共享,替换或扩展.
-
已打出字来. 装有包件的船舶类型定义
index.d.ts(无)typings编号 :@types需要安装。 用 TypeScript 2.0 向上测试 至最新版本(见 安装) (中文(简体) ).
快开始
// cli.js
const {
Command,
ExecutorCommand,
Param,
BooleanFlag,
ValueFlag,
ArgvInput,
ConsoleOutput,
} = require('@2fd/command');
class Greet extends Command {
constructor() {
super();
this.description = 'Greet someone';
this.params = new Param('name [...others]');
this.flags = [
new BooleanFlag('loud', ['--loud', '-l'], 'Uppercase the greeting'),
new ValueFlag('times', ['--times', '-t'], 'Repeat N times', Number, 1),
];
}
action(input, output) {
const names = [input.params.name, ...(input.params.others || [])];
let msg = 'Hello ' + names.join(', ');
if (input.flags.loud)
msg = msg.toUpperCase();
for (let i = 0; i < input.flags.times; i++)
output.log(msg);
}
}
const tool = new ExecutorCommand();
tool.version = '1.0.0';
tool.description = 'My tool';
tool.addCommand('greet', new Greet());
tool.handle(new ArgvInput(process.argv), new ConsoleOutput());
$ node cli.js greet Ana Bob -l -t 2
HELLO ANA, BOB
HELLO ANA, BOB
图标
命令是执行命令的任何对象 CommandInterface数字 :
interface CommandInterface<F, P> {
description: string;
handle(input: InputInterface<F, P>, output: OutputInterface): void;
}
您通常会延长一个内建课程, 而不是执行 直接说.
Command (类)
扩展 Command,定义 description, (中文(简体) ), params 财务报告和审定财务报表 flags,并执行
action。 。 。 . handle() 解析输入、校验参数和旗帜,然后
电话 action()。 。 。 .
import {
Command,
InputInterface,
OutputInterface,
Param,
BooleanFlag,
} from '@2fd/command';
type Flags = { force: boolean };
type Params = { requireParam: string; optionalParam?: string; optionalParamList?: string[] };
export class MyCommand extends Command<Flags, Params> {
description = 'My Command description';
params = new Param('requireParam [optionalParam] [...optionalParamList]');
flags = [
new BooleanFlag('force', ['--force', '-f'], 'Force flag'),
];
action(input: InputInterface<Flags, Params>, output: OutputInterface): void {
// input.params.requireParam -> string
// input.params.optionalParam -> string | undefined
// input.params.optionalParamList -> string[] | undefined
// input.flags.force -> boolean
}
}
export const myCommand = new MyCommand();
执行规则 Command数字 :
params需要( 使用NoParams或者说IgnoreParams没有命令 立场论据).flags必须是数组(可以是空的).- 两面旗帜不能共用
name或同一选项(例如两个)-f) (中文(简体) ). - 未知选项
Unexpected option: <flag>。 。 。 . - 帮助旗帜( A)
-h, --help自动添加。 当存在时,帮助是 印刷和action()没有叫.
独立命令可以运行, 无需执行器 :
// command.js
const { ArgvInput, ConsoleOutput } = require('@2fd/command');
const { myCommand } = require('./commands');
myCommand.handle(new ArgvInput(process.argv), new ConsoleOutput());
$ node command.js --help
My Command description
Usage: node command.js [OPTIONS] requireParam [optionalParam] [...optionalParamList]
[OPTIONS]:
--force, -f Force flag
-h, --help Print this help
快速命令( 函数)
普通函数 (input, output) => void 可注册为命令.
设置 a description 属性在帮助中显示(默认)
改为 -) (中文(简体) ). 快速命令接收原始输入:没有解析参数/标记,
input.argv 包含其余参数.
function ping(input, output) {
output.log('pong', input.argv);
}
ping.description = 'Reply with pong';
exports.ping = ping;
参数
Param 从定义字符串中解析位置参数。 每个
白空分隔符号定义了一段:
| 脚本 | 含义 | 数值 input.params |
|---|---|---|
name | 所需经费 | string |
[name] | 选项 | string | undefined |
...name | 需要的列表( 至少一个值) | string[] |
[...name] | 可选列表 | string[] | undefined |
new Param('source');
new Param('source [target]');
new Param('...files');
new Param('src1 src2 [optional] [...rest]');
数值按顺序分配。 一个列表参数会消耗所有剩余位置
参数。 丢出额外参数 Unexpected param: <value>; 需要缺失
抛出 Param <name> is required。 。 。 .
其他参数处理器 :
| 类 | 行为 |
|---|---|
NoParams | 任何位置参数 Unexpected param。 。 。 . |
IgnoreParams | 立场论点被默默地忽略了. |
旗帜
每一面旗帜都要 (name, list, description)数字 :
name: 使用的密钥input.flags。 。 。 .list: 激活旗帜的选项, 例如 .['--force', '-f']。 。 。 .description: 帮助中显示的文本。 要求, 不能为空 .
| 类 | 数值 input.flags[name] |
|---|---|
BooleanFlag(name, list, desc) | true 如果有的话,否则 false。 。 。 . |
ValueFlag(name, list, desc, parser?, default?) | 下一个辩论,通过 parser 如果给出. default 缺席,否则 undefined。 。 。 . |
ListValueFlag(name, list, desc, parser?, default?) | 数值矩阵,每起发生一次(--tag a --tag b) (中文(简体) ). default (或 []如缺席. |
RequireFlag(flag) | 如果值为 undefined, (中文(简体) ), null, (中文(简体) ), NaN 或为空. |
HelpFlag() | 布尔 help 打开旗子 -h, --help自动添加. |
NullFlag() | 无上正旗. |
const {
BooleanFlag, ValueFlag, ListValueFlag, RequireFlag,
} = require('@2fd/command');
this.flags = [
new BooleanFlag('dryRun', ['--dry-run', '-n'], 'Do not write anything'),
new ValueFlag('port', ['--port', '-p'], 'Port to listen on', Number, 8080),
new ListValueFlag('env', ['--env', '-e'], 'Environment variable (repeatable)'),
new RequireFlag(
new ValueFlag('config', ['--config', '-c'], 'Config file path')
),
];
解析注释 :
- 任何从
-被当成一面旗帜. - 数值作为单独的参数传递(
--port 80) (中文(简体) ).--port=80财务报告和审定财务报表 分组短旗( u)-abc)不支持. - 无法从
-· ;--port -1掷出Flag port (--port, -p) expect a value。 。 。 .
执行器( 多命令工具)
ExecutorCommand 将第一个位置参数导出到已注册的命令
并转达其余的论据.
const { ExecutorCommand, ArgvInput, ColorConsoleOutput } = require('@2fd/command');
const { myCommand } = require('./commands');
const tool = new ExecutorCommand();
tool.version = '1.0.0';
tool.description = 'My tool';
// Command instance
tool.addCommand('run', myCommand);
// Lazy loaded: "<module path>#<export name>"
tool.addCommand('build', './lib/commands#build');
// Lazy loaded: the module itself is the command (module.exports = ...)
tool.addCommand('clean', './lib/commands/clean');
// Several at once
tool.addCommands({
'start': './lib/commands#start',
'stop': './lib/commands#stop',
});
// Namespaced: registered as "db:migrate" and "db:seed"
tool.addCommadsNS('db', {
'migrate': './lib/commands/db#migrate',
'seed': './lib/commands/db#seed',
});
tool.handle(new ArgvInput(process.argv), new ColorConsoleOutput());
在没有命令(或不明命令)的情况下运行会打印帮助. 图标 首先列出名称空间,然后按字母顺序排列:
$ node cli.js
My tool [v1.0.0]
Usage: node cli.js [OPTIONS] [COMMAND]
[COMMAND]:
build Build the project
clean Remove build output
run My Command description
start Start the server
stop Stop the server
db:migrate Run migrations
db:seed Seed the database
[OPTIONS]:
-h, --help Print this help
懒惰的加载路径
字符串命令有格式 <module>[#<export>]数字 :
-
<module>从开始.解决 反对process.cwd()没有 对登记它的文件。 对于能够运行于任意的工具 目录,使用绝对路径 :const { join } = require('path'); tool.addCommand('build', join(__dirname, 'lib/commands') + '#build'); -
任何其他
<module>以require.resolve(包装名称, 绝对路径 :'some-package/commands#migrate'。 。 。 . -
无
#<export>,则使用模块导出本身. -
导出可以是命令对象或快速命令功能.
懒惰命令在运行或执行器帮助被打印时被加载
(阅读其 description) (中文(简体) ).
执行者可以筑巢
ExecutorCommand 本身是一个命令,所以可以在另一个命令内注册
执行器以建立更深层次的命令树 .
投入和产出
ArgvInput(argv, exec?)
环绕 process.argv。它删除了节点.js可执行和脚本路径
argv 并储存在 exec,用于打印 Usage: 线条.
如果剧本生活在一个 PATH 目录(全球安装) bin),只有
脚本名称显示 .
interface InputInterface<F, P> {
argv: string[]; // arguments not consumed yet
exec: string[]; // how the tool was invoked, e.g. ['node', 'cli.js']
flags: F; // parsed flags
params: P; // parsed params
}
产出
| 类 | 行为 |
|---|---|
ConsoleOutput | 写给 console.log 编号 : console.error。 。 。 . %c 样式被剥去. |
ColorConsoleOutput | 一样,但翻译 %c CSS等风格为ANSI颜色. error() 是红色的. |
双方支持 util.format 占位符 (%s, (中文(简体) ), %d, (中文(简体) ), %j) 加上浏览器
控制台占位符 %i, (中文(简体) ), %f, (中文(简体) ), %o, (中文(简体) ), %O 财务报告和审定财务报表 %c数字 :
output.log('%cdone%c in %dms', 'color:green;font-weight:bold', '', 120);
output.error('Cannot open %s', file);
支助 %c 样式 : color:<name>, (中文(简体) ), background:<name> 编号 :
background-color:<name>, (中文(简体) ), font-weight:bold|normal, (中文(简体) ), font-style:italic, (中文(简体) ),
text-decoration:underline|line-through, (中文(简体) ), visibility:hidden。 。 。 .
颜色名称 : black, (中文(简体) ), red, (中文(简体) ), green, (中文(简体) ), yellow, (中文(简体) ), blue, (中文(简体) ), magenta, (中文(简体) ), cyan, (中文(简体) ),
white, (中文(简体) ), gray编号 :grey (无) gray 页:1.
任何对象 log() 财务报告和审定财务报表 error() 方法可作为输出(用于
例如,在测试中捕获输出).
处理错误
解析和校验错误作为正则 Errors和现在 没有
被图书馆抓住了 环绕 handle() 并设置
退出代码 :
try {
tool.handle(new ArgvInput(process.argv), output);
} catch (err) {
output.error(err.message);
process.exitCode = 1;
}
库出错 :
| 消息 | 原因 |
|---|---|
Unexpected option: <flag> | 未知旗帜. |
Unexpected param: <value> | 太多的立场争论. |
Param <name> is required | 缺少所需的参数. |
Flag <name> (<list>) expect a value | ValueFlag编号 :ListValueFlag 无值. |
Flag <name> (<list>) is required | RequireFlag 未提供. |
Cannot overwrite flag ... | 复制旗名或选项 . |
<export> not found in <path> | Lazy 命令导出不存在 . |
扩展
自定义旗和参数执行三个钩子的小接口,调用
此顺序由 Command.handle()数字 :
before(input, output):一次,在解析前. 在此设定默认值 .parse(arg, input, output): 每个匹配参数。 旗帜可以消耗 其数值与input.argv.shift()。 。 。 .after(input, output): 一次, 解析后( 跳过何时)--help实值 (现) 在这里验证或正常化.
interface FlagInterface<F> {
name: string;
description: string;
list: string[];
before(input, output): void;
parse(flag: string, input, output): void;
after(input, output): void;
}
interface ParamInterface<P> {
definition: string; // shown in the "Usage:" line
before(input, output): void;
parse(param: string, input, output): void;
after(input, output): void;
}
示例: 反向旗 (-v -v -v → 3) (中文(简体) ).
const { FlagConstructor } = require('@2fd/command');
class CountFlag extends FlagConstructor {
before(input) { input.flags[this.name] = 0; }
parse(flag, input) { input.flags[this.name]++; }
after() { }
}
new CountFlag('verbose', ['--verbose', '-v'], 'Increase verbosity');
要自定义帮助, 请覆盖 helpDescription(), (中文(简体) ), helpUsage(executable) 或者说
helpOptions() 在命令中,或用设置替换帮助旗
helpFlag 第一次选举之前 handle() 电话.
API 参考
下面的一切都是从 @2fd/command。 。 。 .
| 导出 | 仁爱 | 说明 |
|---|---|---|
Command | 类 | 使用参数和旗帜命令的下级. |
ExecutorCommand | 类 | 路由子命令;支持懒惰加载. |
Param | 类 | 定义字符串中的位置参数 . |
NoParams | 类 | 拒绝位置参数 . |
IgnoreParams | 类 | 忽略位置参数 . |
FlagConstructor | 类 | 验证的基础类 name, (中文(简体) ), list, (中文(简体) ), description。 。 。 . |
BooleanFlag | 类 | true编号 :false 旗帜. |
ValueFlag | 类 | 有单一值的旗帜 . |
ListValueFlag | 类 | 可重复带有数值列表的旗帜 . |
RequireFlag | 类 | 做一个包裹的旗帜需要. |
HelpFlag | 类 | -h, --help。 。 。 . |
NullFlag | 类 | 无上正旗. |
ArgvInput | 类 | 输入来自 process.argv。 。 。 . |
ConsoleOutput | 类 | 普通控制台输出 . |
ColorConsoleOutput | 类 | 使用 ANSI 颜色的控制台输出 . |
CommandInterface | 接口 | 任何命令的合同. |
InputInterface | 接口 | 输入对象合同. |
OutputInterface | 接口 | 产出对象合同. |
发展
npm install
npm run compile # tsc -p .
npm run declaration # generate .d.ts files
npm test # mocha (run compile first)