zoukankan      html  css  js  c++  java
  • Commander基本使用

    随着NodeJs的不断发展,对于前端来说要做的东西也就更多,Vue脚手架React脚手架等等等一系列的东西都脱颖而出,进入到人们的视野当中,对于这些脚手架工具来讲也只是停留在应用阶段,从来没有想过脚手架是如何实现的?vue init webpack 项目名称是如何通过这样的命令创建了一个项目,其最重要的模块就是今天要说的Commander

    Commander模块又国外TJ大神所编写

    项目地址:Commander

    Commander基本用法

    Commander文档写的很详细,跟着文章详细的学习一下,Commander是一个Nodejs模块,需要在Node环境中运行,在使用前确认一下Node环境是否已安装。

    安装依赖

    npm install commander --save
    

    Options 解析

    Commander模块下存在option方法用来定义commander的选项options,用来作为选项的文档。

    var program = require('commander');
    
    program
      .option('-g, --git [type]', 'Add [marble]', 'Angie')
      .parse(process.argv);
    
    console.log("process.argv",process.argv)
    console.log("program.args",program.args)
    
    console.log('you ordered a pizza with:');
    if (program.git) console.log('  - git');
    console.log('  - %s git', program.git);
    

    上面的示例将解析来自process.argvargsoptions,然后将剩下的参数(未定义的参数)赋值给commander对象的args属性(program.args),program.args是一个数组。

    打印输出一下process.argvprogram.args并查看了一下输出结果如下,使用如下命令运行一下文件:

    node index -g type Aaron
    
    process.argv ['F:\node\installation\node.exe',
                  'C:\Users\wo_99\Desktop\cli-dome\index',   
                  '-g',
                  'type',
                  'Aaron' ]
    program.args [ 'Aaron' ]  
    

    option方法可以接收三个参数:

    1. 自定义标志必须:分为长短标识,中间用逗号、竖线或者空格分割;标志后面可跟必须参数或可选参数,前者用<>包含,后者用[]包含。
    2. 选项描述省略不报错:在使用 --help 命令时显示标志描述
    3. 默认值可省略:当没有传入参数时则会使用默认值

    若我们执行node index -g得到的结果则是Angie git其第三个参数则作为了默认值填写在了对应的位置上。除了上面所说还可以使用如下命令:

    //  执行 -g 参数 a
    //  执行 -b 参数 s
    node index -g a -b s
    //  执行 -g和-b 传入a参数给-g
    //  -b 参数暂时不知道怎么传入
    node index -gb a
    

    版本选项

    调用版本会默认将-V--version选项添加到命令中。当存在这些选项中的任何一个时,该命令将打印版本号并退出。

    var program = require('commander');
     
    program
        .version('0.0.1')
        .parse(process.argv);
    //  执行命令
    //  node index -V
    
    //  输出结果
    //  0.0.1
    

    如果希望程序响应-v选项而不是-V选项,只需使用与option方法相同的语法将自定义标志传递给version方法,版本标志可以被命名为任何值,但是长选项是必需的。

    var program = require('commander');
    program
      .version('0.0.1', '-e, --version');
    

    command 添加命令名称

    该方法允许使用命令行去执行一段命令,也就是一段:

    var program = require('commander');
    
    program
      .version('0.0.1', '-V, --version')
      .command('rm <dir>')
      .action(function (dir, cmd) {
        console.log('remove ' + dir + (cmd.recursive ? ' recursively' : ''))
      });
    
    program.parse(process.argv);
    
    //  执行命令
    //  node index rm /aaa -r
    
    //  输出结果
    //  remove /aaa recursively     即:代码中console内容
    

    command函数接收三个参数:

    1. 命令名称必须:命令后面可跟用<>[]包含的参数;命令的最后一个参数可以是可变的,像实例中那样在数组后面加入...标志;在命令后面传入的参数会被传入到action的回调函数以及program.args数组中。
    2. 命令描述可省略:如果存在,且没有显示调用action(fn),就会启动子命令程序,否则会报错
    3. 配置选项可省略:可配置noHelp、isDefault

    使执行命令时,将验证该命令的options,任何未知的option都将报错。但是,如果基于action的命令如果没有定义action,则不验证options

    var program = require('commander');
    
    program
      .version('0.0.1', '-V, --version')
      .command('rm <dir>')
      .option('-r, --recursive', 'Remove recursively')
      .action(function (dir, cmd) {
        console.log('remove ' + dir + (cmd.recursive ? ' recursively' : ''))
      });
    program.parse(process.argv);
    console.log(program.args)
    
    //  执行命令
    //  node index rm /aaa -r /ddd
    
    //  输出结果
    //  remove /ddd recursively
    //  [ '/aaa' ]
    

    helpOption 帮助

    提供帮助信息

    var program = require('commander');
    program
      .version('0.1.0')
      .helpOption('-h,--HELP')
      .option('-f, --foo', 'enable some foo')
      .option('-b, --bar', 'enable some bar')
      .option('-B, --baz', 'enable some baz');
    program.parse(process.argv);
    
    //  执行命令
    //  node index -h 或 node index --HELP
    /*  输出结果
     *  Options:
     *    -V, --version  output the version number
     *    -f, --foo      enable some foo
     *    -b, --bar      enable some bar
     *    -B, --baz      enable some baz
     *    -h,--HELP      output usage information 
     */
    

    输出帮助信息并立即退出。可选的回调cb允许在显示帮助文本之前对其进行后处理。helpOption也提供长名-h,--HELP前面为短名后面为长名调用时使用两这都是可以的。与version的使用是类似的。

    description 命令描述

    用来描述命令,也就是命令的说明,上面说过command第二个参数也同样是命令的描述,当与description同时存在的话,则会优先于第二个参数的描述,description则会作为全局描述在最顶部显示。该描述只用在使用-HELP的时候才能看见。

    var program = require('commander');
    
    program
      .version('0.0.1', '-V, --version')
      .command('rm <dir>',"arg is description")
      .description("this is description")
      .option('-r, --recursive', 'Remove recursively')
      .action(function (dir, cmd) {
        console.log('remove ' + dir + (cmd.recursive ? ' recursively' : ''))
      });
    
    program.parse(process.argv);
    
    //  执行命令
    //  node index -h
    //  输出结果
    /*
    this is description
    
    Options:
      -V, --version    output the version number
      -r, --recursive  Remove recursively       
      -h, --help       output usage information 
    
    Commands:
      rm <dir>         arg is description       
      help [cmd]       display help for [cmd] 
    */
    

    通过上面的输出结果可以看的出,rm命令最后的描述是arg is description,若删除第二个参数则会输出this is description

    自定义事件侦听器

    用于捕获optioncommand,当其被使用贼会被触发函数。

    var program = require('commander');
    
    program
      .version('0.0.1', '-V, --version')
      .command('rm <dir>',"arg is description")
      .option('-r, --recursive', 'Remove recursively')
      .option('-g, --git [type]', 'Add [marble]', 'Angie')
      .option('-a, --am',"ampm")
      .action(() => {
        console.log(123)
      });
    program.on('option:am', function () {
      console.log("on:am")
    });
    program.on('option:recursive', function () {
      console.log("option:recursive")
    });
    program.on('command:rm', function () {
      console.log("command:rm")
    });
    program.on('option:git', function () {
      console.log("option:git")
    });
    program.on('command:*', function () {
      console.log(987)
      console.error('Invalid command: %s
    See --help for a list of available commands.', program.args.join(' '));
      process.exit(1);
    });
    program.on('--help', function() {
      console.log('****************');
      console.log('Examples:');
      console.log('****************');
      console.log('  $ deploy exec sequential');
      console.log('  $ deploy exec async');
    });
    program.parse(process.argv);
    

    分别执行commandoption,会依次触发对应的函数,但是command:*具体是什么时候触发的?

    1. commandoption已经定义但是没有进行事件捕获时会触发
    2. 规定参数或没有参数时,传入了参数也会触发该函数
    3. 没有该命令

    以上情况就会触发command:*对应的事件,option:紧紧跟随的是option的长名。才会捕获到该事件。

    开发本地模块

    创建项目文件如下:

    ├─bin
    │  └─init-project.js
    ├─lib
    │  └─install.js
    └─templates
        └─dome1
    

    创建好项目目录以后,安装如下依赖包:

    1. chalk
    2. commander
    3. fs-extra
    4. path
    5. through2
    6. vinyl-fs
    7. which

    命令:npm install --save-dev chalk commander fs-extra through2 vinyl-fs which path

    首先在init-project.js中第一行添加#! /usr/bin/env node,这是用来指定脚本的执行程序,这里的Node可以用!/usr/bin/node,若用户将Node安装在非默认路径下会找不到Node。So~最好选择env环境变量查找Node安装目录。

    init-project.js

    #! /usr/bin/env node
    
    // 引入依赖
    var program = require('commander');
    var vfs = require('vinyl-fs');
    var through = require('through2');
    const chalk = require('chalk');
    const fs = require('fs-extra');
    const path = require('path');
    
    // 定义版本号以及命令选项
    program
      .version('1.0.0')
      .option('-i --init [name]', 'init a project', 'myFirstProject')
    
    program.parse(process.argv);
    
    if (program.init) {
      // 获取将要构建的项目根目录
      var projectPath = path.resolve(program.init);
      // 获取将要构建的的项目名称
      var projectName = path.basename(projectPath);
      console.log(`Start to init a project in ${chalk.green(projectPath)}`);
    
      // 根据将要构建的项目名称创建文件夹
      fs.ensureDirSync(projectName);
    
      // 获取本地模块下的demo1目录
      var cwd = path.join(__dirname, '../templates/demo1');
    
      // 从demo1目录中读取除node_modules目录下的所有文件并筛选处理
      vfs.src(['**/*', '!node_modules/**/*'], { cwd: cwd, dot: true }).
      pipe(through.obj(function (file, enc, callback) {
        if (!file.stat.isFile()) {
          return callback();
        }
    
        this.push(file);
        return callback();
      }))
        // 将从demo1目录下读取的文件流写入到之前创建的文件夹中
        .pipe(vfs.dest(projectPath))
        .on('end', function () {
          console.log('Installing packages...')
    
          // 将node工作目录更改成构建的项目根目录下
          process.chdir(projectPath);
    
          // 执行安装命令
          require('../lib/install');
        })
        .resume();
    }
    

    install.js

    // 引入依赖
    var which = require('which');
    const chalk = require('chalk');
    
    var childProcess = require('child_process');
    
    // 开启子进程来执行npm install命令
    function runCmd(cmd, args, fn) {
      args = args || [];
      var runner = childProcess.spawn(cmd, args, {
        stdio: 'inherit'
      });
    
      runner.on('close', function (code) {
        if (fn) {
          fn(code);
        }
      })
    }
    
    // 查找系统中用于安装依赖包的命令
    function findNpm() {
      var npms = ['tnpm', 'cnpm', 'npm'];
      for (var i = 0; i < npms.length; i++) {
        try {
          // 查找环境变量下指定的可执行文件的第一个实例
          which.sync(npms[i]);
          console.log('use npm: ' + npms[i]);
          return npms[i]
        } catch (e) {
        }
      }
      throw new Error(chalk.red('please install npm'));
    }
    
    var npm = findNpm();
    runCmd(which.sync(npm), ['install'], function () {
      console.log(npm + ' install end');
    })
    

    完成如上代码之后,更改package.json添加属性如下:

    {
      "bin": {
        "q-init": "./bin/init-project.js"
      }
    }
    

    注:自定义指令后指定的文件,一定要添加.js后缀文件名,否则会抛出错误。

    接下来剩下的就是测试了,对于测试来说不需要把安装包推到npm中,npm为了方便,提供了npm link命令,可以实现预发布。在项目根目录中使用npm link没有报错的话,就说明推送成功了。现在就可以在全局中使用q-init了。

    在全局中使用initP -h命令,能够输出所编译的help信息就说明可以初始化项目了。

    Usage: init-project [options]
    
    Options:
      -V, --version     output the version number
      -i --init [name]  init a project (default: "myFirstProject")
      -h, --help        output usage information
    

    总结

    commanderVue-cli、creat-app(react)中都起到了很大的作用,这种创建脚手架的方式与vue-cli的方式不同,vue-cli则是使用git远程拉取项目再完成初始化,这样一来要比这种更加的方便灵活,每次模板变更不需要再次上传包,只需要更改git仓库就好了,方便快捷。

  • 相关阅读:
    客户端配置文件tnsname.ora
    MVC3使用Area解耦项目
    系统中同时安装sql2005 和 sql2008 R2 提示要删除SQL Server 2005 Express
    sqlserver执行时间和自行效率
    sqlserver2008链接服务器中执行存储过程配置过程
    sqlserver中top 1 赋值的问题
    js跨域post请求
    uploadify 报http 302错误
    go 函数
    Node.js +Express+MongoDB+mogoose+ejs+bootstrap+jquery
  • 原文地址:https://www.cnblogs.com/aaron---blog/p/11669592.html
Copyright © 2011-2022 走看看