前端架构: 脚手架框架之commander从基础到高级应用教程

news/发布时间2024/5/15 17:34:13

commander


1 )概述

  • commander 是一个更为知名的脚手架框架
  • 进入它的npm官网: https://www.npmjs.com/package/commander
  • 目前版本: 12.0.0
  • Weekly Downloads 133,822,797 (动态数据)
  • 最近更新:15 days ago (npm)
  • 说明这是一个更优质的库
  • 同时使用commander的案例也更为知名:vue-cli,webpack-cli,create-react-app
  • 所以commander是我们开发脚手架过程中首选的框架

2 )快速实现一个 commander 脚手架

  • 仍旧在 xyzcli 这个脚手架项目之下,之前是使用 yargs, 这时候把 bin/index.js 修改成 bin/yargs.js (用于备份)
  • 进行安装 $ npm i commander -S
  • 新建 bin/index.js 并编写
    #!/usr/bin/env nodeconst pkg = require('../package.json')
    const commander = require('commander');
    // const { program } = commander; // 脚手架实例 program是一个单例
    const program = new commander.Command(); // 这种手动实例化单例,同上,二取一program.version(pkg.version) // 设定版本.parse(process.argv) // 解析所有参数
    
  • 执行 $ xyzcli --version, 并查看结果
    1.0.0
    
  • 执行 $ xyzcli --help, 并查看结果
    Usage: xyzcli [options]Options:-V, --version  output the version number-h, --help     display help for command
    
  • 这时候,使用 commander 完成了最小粒度的脚手架
  • 这时候,可见,-V, -h 这种别名短称都已经默认给出了

3 )commander 通用全局配置

3.1 配置 usage

const program = new commander.Command(); // 这种手动实例化单例,同上,二取一program.usage('<command> [option]').version(pkg.version) // 设定版本.parse(process.argv) // 解析所有参数
  • 执行 $ xyzcli -h, 查看输出
    Usage: xyzcli <command> [option]Options:-V, --version  output the version number-h, --help     display help for command
    
    • 可以看到 Usage: xyzcli <command> [option]

3.2 配置 name

const program = new commander.Command(); // 这种手动实例化单例,同上,二取一program.name(pkg.name).usage('<command> [option]').version(pkg.version) // 设定版本.parse(process.argv) // 解析所有参数
  • 这里修改下 package.json 中的 name,比如修改成 xyzcli111

  • 执行 $ xyzcli -h, 查看输出

    Usage: xyzcli111 <command> [option]Options:-V, --version  output the version number-h, --help     display help for command
    
    • 注意这里的,Usage: xyzcli111 <command> [option] 变成了 xyzcli111
    • 验证完成,将 package.json 中的 name 从 xyzcli111 再修改回 xyzcli
  • 在实际应用中,我们会取 bin 属性中的第一个key (一般而言,bin里的配置和包名一致, 但是最好取bin的第一个key值)

    const program = new commander.Command();program// .name(pkg.name).name(Object.keys(pkg.bin)[0]) // 注意这里.usage('<command> [option]').version(pkg.version) // 设定版本.parse(process.argv) // 解析所有参数
    
  • 这里不做输出测试

3.3 配置 option

const program = new commander.Command(); // 这种手动实例化单例,同上,二取一program// .name(pkg.name).name(Object.keys(pkg.bin)[0]).usage('<command> [option]').version(pkg.version) // 设定版本.option('-f, --first', '第一个').option('-s, --separator <char>', '使用分隔符分割', ',').option('-d, --debug', '开启调试模式', false).option('-e, --envName <envName>', '获取环境变量名称').parse(process.argv) // 解析所有参数
  • 执行 $ xyzcli -h, 查看输出
    Usage: xyzcli <command> [option]Options:-V, --version            output the version number-f, --first              第一个-s, --separator <char>   使用分隔符分割 (default: ",")-d, --debug              开启调试模式 (default: false)-e, --envName <envName>  获取环境变量名称-h, --help               display help for command
    
  • 对参数进行解析和判断处理
    const program = new commander.Command(); // 这种手动实例化单例,同上,二取一program// .name(pkg.name).name(Object.keys(pkg.bin)[0]).usage('<command> [option]').version(pkg.version) // 设定版本.option('-f, --first', '第一个') // 选择第一个参数.option('-s, --separator <char>', '使用分隔符分割', ',').option('-d, --debug', '开启调试模式', false).option('-e, --envName <envName>', '获取环境变量名称').parse(process.argv) // 解析所有参数const options = program.opts(); // 获取所有可用的 opt 作为参数
    const limit = options.first ? 1 : undefined; // 存在第一个参数, 则选1,不存在则不定义
    console.log(program.args[0].split(options.separator, limit)); // 基于分隔符分割获取参数
    
  • 执行,$ xyzcli -f x,y,z,查看输出结果
    [ 'x' ]
    
  • 执行,$ xyzcli -s / -f o/p/q, 查看输出结果
    [ 'o' ]
    

4 ) 配置 command 命令

  • 大类上还分两种小类型
    • 调用 command api 注册命令
    • 调用 addCommand api 注册命令

4.1 command

const program = new commander.Command(); // 这种手动实例化单例,同上,二取一program// .name(pkg.name).name(Object.keys(pkg.bin)[0]).usage('<command> [option]').version(pkg.version) // 设定版本.option('-f, --first', '第一个') // 选择第一个参数.option('-s, --separator <char>', '使用分隔符分割', ',').option('-d, --debug', '开启调试模式', false).option('-e, --envName <envName>', '获取环境变量名称')// 定义一个克隆的命令
const clone = program.command('clone');
clone.description('克隆仓库').action(() => {console.log('do clone');});program.parse(process.argv) // 解析所有参数, 注意这里的调用位置
  • 直接执行 $ xyzcli, 并输出
    Usage: xyzcli <command> [option]Options:-V, --version            output the version number-f, --first              第一个-s, --separator <char>   使用分隔符分割 (default: ",")-d, --debug              开启调试模式 (default: false)-e, --envName <envName>  获取环境变量名称-h, --help               display help for commandCommands:clone                    克隆仓库help [command]           display help for command
    
    • 这里可以看到存在两个 command: clone 和 help
  • 执行 $ xyzcli clone -h, 并输出
    Usage: xyzcli clone [options]克隆仓库Options:-h, --help  display help for command
    
  • 执行 $ xyzcli clone, 并输出
    do clone
    
  • 对 clone 命令进行扩展
    // 定义一个克隆的命令
    const clone = program.command('clone <source> [destination]');
    clone.description('克隆仓库').action((source, destination) => {console.log('do clone: ', source, destination);});program.parse(process.argv) // 解析所有参数, 注意这里的调用位置
    
  • 执行 $ xyzcli clone, 并输出
    error: missing required argument 'source'
    
    • 可见source是必传项
    • 而 destination 是可选项
  • 执行 $ xyzcli clone ss dd, 并输出
    do clone:  ss dd
    
  • 在clone命令中添加针对该命令的option
    // 定义一个克隆的命令
    const clone = program.command('clone <source> [destination]');
    clone.description('克隆仓库').option('--fc, --force', '是否强制克隆', false).action((source, destination, cmdObj) => {console.log('do clone: ', source, destination, cmdObj.force);});program.parse(process.argv) // 解析所有参数, 注意这里的调用位置
    
  • 执行 $ xyzcli clone ss dd, 并输出
    do clone:  ss dd false
    
  • 执行 $ xyzcli clone ss dd --fc, 并输出
    do clone:  ss dd true
    
    • 注意,这里简称不能与前面定义的冲突,如果是多个字符,用 --, 不能用一个 -
    • 这里 --fc--force
  • 执行 $ xyzcli clone --force ss dd, 并输出
    do clone:  ss dd true
    
    • 注意这里更换了option的位置,只要在 clone 后面,就不会受到影响

4.2 addCommand

  • addCommand 的特殊之处是可以注册子命令
    // 定义service脚手架
    const service = new commander.Command('service');
    service.command('start [port]').description('start service at some port').action((port) => {console.log('service port @', port)})program.addCommand(service);
    program.parse(process.argv) // 解析所有参数, 注意这里的调用位置
    
  • 执行 $ xyzcli -h, 查看输出
    Usage: xyzcli <command> [option]Options:-V, --version                           output the version number-f, --first                             第一个-s, --separator <char>                  使用分隔符分割 (default: ",")-d, --debug                             开启调试模式 (default: false)-e, --envName <envName>                 获取环境变量名称-h, --help                              display help for commandCommands:clone [options] <source> [destination]  克隆仓库servicehelp [command]                          display help for command
    
  • 执行 $ xyzcli service -h, 查看输出
    Usage: xyzcli service [options] [command]Options:-h, --help      display help for commandCommands:start [port]    start service at some porthelp [command]  display help for command
    
    • 这里看到 service 命令下存在一个子命令 start
  • 可以看到,这种方式可以对命令进行分组,比如在 service 下 有start, 还可以有stop
    // 定义service脚手架
    const service = new commander.Command('service');
    service.command('start [port]').description('start service at some port').action((port) => {console.log('service port @', port)})service.command('stop').description('stop service').action((port) => {console.log('stop service')})program.addCommand(service);
    program.parse(process.argv) // 解析所有参数, 注意这里的调用位置
    
  • 执行 $ xyzcli service -h, 查看输出
    Usage: xyzcli service [options] [command]Options:-h, --help      display help for commandCommands:start [port]    start service at some portstop            stop servicehelp [command]  display help for command
    
  • 执行 $ xyzcli service stop, 查看输出
    stop service
    
  • 通过子命令功能可大大扩展脚手架功能
  • 注意,分组下的 子命令必须如上分开写,不能连写

4.3 )对命令注册进行自动匹配

  • 可以通过 program.arguments 来监听所有命令输入
  • 除了上面注册的命令,其他命令都会命中这里的 arguments 中
    // 命令匹配
    program.arguments('<cmd> [options]').description('test command').action((cmd, options)=> {console.log(cmd, options);})program.parse(process.argv) // 解析所有参数, 注意这里的调用位置
    
  • 执行 $ xyzcli test, 查看输出
    test undefined
    
  • 执行 $ xyzcli, 查看输出
    error: missing required argument 'cmd'
    
    • 可以看到,这里 cmd 是要求强制输入的
  • 执行 $ xyzcli tt ss, 查看输出
    tt ss
    
  • 执行 $ xyzcli -h, 查看输出
    Usage: xyzcli <command> [option]test commandOptions:-V, --version                           output the version number-f, --first                             第一个-s, --separator <char>                  使用分隔符分割 (default: ",")-d, --debug                             开启调试模式 (default: false)-e, --envName <envName>                 获取环境变量名称-h, --help                              display help for commandCommands:clone [options] <source> [destination]  克隆仓库service
    
    • 可以看到这里没有什么信息,只有 一个 test command 的描述
  • 执行 $ xyzcli test -h, 同样也看不到其他实用的信息
  • 现在,对其再次进行修改,测试
    // 命令匹配
    program.arguments('<cmd> [options]').description('test command', {cmd: 'command to run',options: 'options for command'}).action((cmd, env)=> {console.log(cmd, env);})program.parse(process.argv) // 解析所有参数, 注意这里的调用位置
    
  • 执行 $ xyzcli test -h, 查看输出结果
    Usage: xyzcli <command> [option]test commandArguments:cmd                                     command to runoptions                                 options for commandOptions:-V, --version                           output the version number-f, --first                             第一个-s, --separator <char>                  使用分隔符分割 (default: ",")-d, --debug                             开启调试模式 (default: false)-e, --envName <envName>                 获取环境变量名称-h, --help                              display help for commandCommands:clone [options] <source> [destination]  克隆仓库service
    
  • 可以看到,这里多了 Arguments 这一项,里面的描述配置都有说明
  • 它的强大之处在于,可以匹配到所有输入的命令, 同时强制必须传 cmd 命令
  • 这个功能和在yargs框架中的 demandCommand 功能类似
  • 还有一种方式,也同样强大,如下
    program.command('install [name]', 'install package')program.parse(process.argv) // 解析所有参数, 注意这里的调用位置
    
  • 执行 $ xyzcli -h, 查看输出结果
    Usage: xyzcli <command> [option]test commandArguments:cmd                                     command to runoptions                                 options for commandOptions:-V, --version                           output the version number-f, --first                             第一个-s, --separator <char>                  使用分隔符分割 (default: ",")-d, --debug                             开启调试模式 (default: false)-e, --envName <envName>                 获取环境变量名称-h, --help                              display help for commandCommands:clone [options] <source> [destination]  克隆仓库serviceinstall [name]                          install package
    
    • 可以看到,最后的 install
  • 如果执行 $ xyzcli install -h, 查看发现报错
    • 报错信息为:Error: 'xyzcli-install' does not exist
    • 它会把当前脚手架 xyzcli 加上 -install 组成一个新的命令 xyzcli-install
    • 而这个命令在我们电脑上是没有的
    • 这个命令有些像 $ npm init install 当输入这个方法的时候也会报错
    • 它会告诉你,'create-install@latest' is not in the npm registry.
    • 这是因为,npm init 命令后面加上参数时,比如 加上 abc, 就是 npm init abc
    • 默认会在前面加上 create, 然后找这个包,也就是 create-abc 这个包
    • 如果这个包存在,则动态下载并执行这个包, 这块了解即可
  • 还可以加上别名
    program.command('install [name]', 'install package').alias('i')program.parse(process.argv) // 解析所有参数, 注意这里的调用位置
    
  • 执行 $ xyzcli -h, 查看输出结果
    Usage: xyzcli <command> [option]test commandArguments:cmd                                     command to runoptions                                 options for commandOptions:-V, --version                           output the version number-f, --first                             第一个-s, --separator <char>                  使用分隔符分割 (default: ",")-d, --debug                             开启调试模式 (default: false)-e, --envName <envName>                 获取环境变量名称-h, --help                              display help for commandCommands:clone [options] <source> [destination]  克隆仓库serviceinstall|i [name]                        install package
    
    • 可以看到上述别名起作用了
  • 同时,还可以添加第三个options参数
    program.command('install [name]', 'install package', {executableFile: 'npm', // 手动修改可执行文件}).alias('i')program.parse(process.argv) // 解析所有参数, 注意这里的调用位置
    
  • 这时候我们执行 $ xyzcli i i vue -S
    • 这里的 xyzcli i 或 xyzcli install 等价于 npm
    • 所以,这时候,xyzcli i i vue -S 等价于 npm i vue -S
    • 执行后,可见 package.json 中存在了 vue
    • 这里很奇怪的是: 两个 i, 但是这个问题不大
  • 还有一些其他的配置,比如:
    program.command('install [name]', 'install package', {executableFile: 'npm', // 手动修改可执行文件isDefault: true, // 默认会匹配这个// hidden: true, // 不再帮助信息中显示}).alias('i')program.parse(process.argv) // 解析所有参数, 注意这里的调用位置
    
    • 这里 isDefault 设置为 true时,当执行 $ xyzcli 就直接会匹配到 npm 命令的帮助信息
    • 这里 hidden 设置为 true时,在帮助信息中就看不到 install 命令了

全部example参考

#!/usr/bin/env nodeconst pkg = require('../package.json')
const commander = require('commander');
// const { program } = commander; // 脚手架实例 program是一个单例
const program = new commander.Command(); // 这种手动实例化单例,同上,二取一program// .name(pkg.name).name(Object.keys(pkg.bin)[0]).usage('<command> [option]').version(pkg.version) // 设定版本.option('-f, --first', '第一个') // 选择第一个参数.option('-s, --separator <char>', '使用分隔符分割', ',').option('-d, --debug', '开启调试模式', false).option('-e, --envName <envName>', '获取环境变量名称')// 定义一个克隆的命令
const clone = program.command('clone <source> [destination]');
clone.description('克隆仓库').option('--fc, --force', '是否强制克隆', false).action((source, destination, cmdObj) => {console.log('do clone: ', source, destination, cmdObj.force);});// 定义service脚手架
const service = new commander.Command('service');
service.command('start [port]').description('start service at some port').action((port) => {console.log('service port @', port)})service.command('stop').description('stop service').action((port) => {console.log('stop service')})program.addCommand(service);// 设定 install 命令
program.command('install [name]', 'install package', {executableFile: 'npm', // 手动修改可执行文件// isDefault: true,// hidden: true,}).alias('i')// 命令匹配,用于兜底
program.arguments('<cmd> [options]').description('test command', {cmd: 'command to run',options: 'options for command'}).action((cmd, env)=> {console.log(cmd, env);	})program.parse(process.argv) // 解析所有参数, 注意这里的调用位置

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.bcls.cn/qDvp/3212.shtml

如若内容造成侵权/违法违规/事实不符,请联系编程老四网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

普中51单片机学习(十四)

中断系统 中断的概念 CPU在处理某一事件A时&#xff0c;发生了另一事件B请求CPU迅速去处理&#xff08;中断发生&#xff09;,CPU暂时中断当前的工作&#xff0c;转去处理事件B&#xff08;中断响应和中断服务)&#xff0c;待CPU将事件B处理完毕后&#xff0c;再回到原来事件…

10.vue学习笔记(组件数据传递-props回调函数子传父+透传Attributes+插槽slot)

文章目录 1.组件数据传递2.透传Attributes&#xff08;了解&#xff09;禁用Attributes继承 3.插槽slot 1.组件数据传递 我们之前讲解过了组件之间的数据传递&#xff0c;props 和 自定义事件 两种方式 props&#xff1a;父传子 自定义事件&#xff1a;子传父 props通过额外方…

nginx服务

“欢唱吧&#xff0c;呼唤它&#xff0c;回来啊~” Web服务器简介 Web服务器&#xff0c;一般是指“网站服务器”&#xff0c;其本质就是驻留于互联网中&#xff0c;某一台机器(计算机)上的进程(程序)。Web服务器通常就是为用户提供信息浏览服务&#xff0c;更可以放置数据文件…

强化学习入门(Matlab2021b)-创建环境【2】

目录 1 前言2 利用step和reset函数创建自定义环境2.1 对象描述2.2 reset函数2.3 step函数2.3 构建自定义环境3 使用匿名函数传递额外的参数4 可视化检查自定义函数的输出参考链接1 前言 本文介绍如何基于MATLAB编写step、reset函数,创建自己的强化学习环境(Environment)。 使…

Java面试题:volatile专题

王有志,一个分享硬核Java技术的互金摸鱼侠 加入Java人的提桶跑路群:共同富裕的Java人 今天是《面霸的自我修养》第4篇文章,我们一起来看看面试中会问到哪些关于volatile的问题吧。数据来源: 大部分来自于各机构(Java之父,Java继父,某灵,某泡,某客)以及各博主整理文档…

【C语言经典100题#4】判断三角形

题目名称&#xff1a; 输入三个整数a,b,c&#xff0c;判断由a,b,c作为三条边组成的三角形&#xff0c;如果不能组成三角形则输出&#xff1a;非三角形&#xff1b;如果是三角形&#xff0c;再继续判断&#xff0c;如果是等边三角形&#xff0c;则输出&#xff1a;等边三角形&a…

NLP_GPT生成式自回归模型

文章目录 介绍完整代码小结 介绍 自回归(Autoregressive)是自然语言处理模型的一种训练方法&#xff0c;其核心思想是基于已有的序列(词或字符)来预测下一个元素。在GPT中&#xff0c;这意味着模型会根据给定的上文来生成下一个词&#xff0c;如图所示。 在GPT模型的训练和推…

网络原理-TCP/IP(7)

目录 网络层 路由选择 数据链路层 认识以太网 以太网帧格式 认识MAC地址 对比理解MAC地址和IP地址 认识MTU ARP协议 ARP协议的作用 ARP协议工作流程 重要应用层协议DNS(Domain Name System) DNS背景 NAT技术 NAT IP转换过程 NAPT NAT技术的优缺点 网络层 路由…

【MySQL】数据库概述

目录 一、为什么使用数据库&#xff1f; 二、数据库与数据库管理系统 2.1 相关概念 2.2 两者关系 三、 MySQL介绍 四、 RDBMS和非RDBMS 4.1 关系型数据库&#xff08;RDBMS&#xff09; 4.2 非关系型数据库&#xff08;非RDBMS&#xff09; 五、关系型数据库设计规则 …

在Mac上搭建MongoDB环境

最近工作中需要装MongoDB环境&#xff0c;搭建过程中遇到了一些问题&#xff0c;在这里记录一下安装MongoDB环境的方法以及问题的解决方法。有两种安装MongoDB的方法&#xff1a;brew安装和手动安装。 目录 使用Homebrew安装MongoDB 手动安装MongoDB&#xff08;不使用Homebr…

Neo4j导入数据之JAVA JDBC

目录结构 前言设置neo4j外部访问代码整理maven 依赖java 代码 参考链接 前言 公司需要获取neo4j数据库内容进行数据筛查&#xff0c;neo4j数据库咱也是头一次基础&#xff0c;辛辛苦苦安装好整理了安装neo4j的步骤&#xff0c;如今又遇到数据不知道怎么创建&#xff0c;关关难…

【QT-lineEidte动画效果

QT-lineEidte动画效果 一、演示效果二、核心代码三、下载链接 一、演示效果 二、核心代码 #ifndef DynamicUnderlineLineEdit_H #define DynamicUnderlineLineEdit_H#include <QWidget> #include <QLineEdit> #include <QPainter> #include <QPaintEvent…

构造器详解

定义: 是一种特殊类型的方法&#xff0c;用于创建对象时初始化对象的状态。 使用new关键字创建对象 构造器特点: 1.和类名相同 2.没有返回值 public class Person {String name;public Person() {this.name"John";}}public class Test {public static void main…

解决docker中运行的jar包连不上前端程序

目录 检查端口映射 查看容器的 IP 地址 检查容器网络设置 防火墙和网络策略 前端程序配置 跨域资源共享 (CORS) 日志查看 连接问题通常涉及到网络配置和端口映射。确保你在 Docker 中运行的 JAR 包可以被前端程序访问&#xff0c;可以采取以下步骤来解决问题&#xff1a…

美团面试:说说Java OOM的三大场景和解决方案?

美团面试&#xff1a;说说Java OOM的场景和解决方案&#xff1f; 尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的面试题&…

C语言---二维数组指针

1.int a[3][4] {0}; ---------------------------------------- printf("%d\n",sizeof(a)); 12元素&#xff0c;12*448字节&#xff1b; ------------------------------------- printf("%d\n",sizeof(a[0][0])); 一个元素&#xff0c;4字节&#xff1…

【springblade】springblade(bladeX) 数据权限失效原因分析

文章目录 数据权限接口权限 前言&#xff1a;最近博主在按照bladeX官方文档 配置数据权限 结果发现失效了&#xff0c;网上搜了一下没找到合适的答案&#xff0c;本着求人不如求己的精神&#xff0c;自己调试了一下发现了问题所在&#xff0c;也大致看了一下bladeX的权限逻辑。…

【EI会议征稿通知】第十届能源材料与环境工程国际学术会议(ICEMEE 2024)

第十届能源材料与环境工程国际学术会议&#xff08;ICEMEE 2024&#xff09; 2024 10th International Conference on Energy Materials and Environment Engineering 随着前9年的成功&#xff0c;ICEMEE在2024年迎来了第10届。很荣幸地宣布&#xff0c;第十届能源材料与环境…

算法刷题:找到字符串中所有的字母异位词

找到字符串中所有的字母异位词 .题目链接题目详情题目解析算法原理滑动窗口流程图定义指针及变量进窗口判断出窗口更新结果 我的答案 . 题目链接 找到字符串中所有的字母异位词 题目详情 题目解析 所谓的异位词,就是一个单词中的字母,打乱顺序,重新排列得到的单词 如:abc-&g…

小清新卡通人物404错误页面源码

小清新卡通人物404错误页面源码由HTMLCSSJS组成,记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c;重定向这个界面 蓝奏云&#xff1a;https://wfr.lanzout.com/i6XbU1olftde
推荐文章