美文网首页
自定义NPM命令行

自定义NPM命令行

作者: 说叁两事 | 来源:发表于2021-10-15 18:31 被阅读0次

    入口文件

    • 自定义依赖模块:
      • 模块是在 package.json 里通过 main 字段定义这个包对外暴露的入口;
        • 模块起源于node,语法默认支持commonjs规范
        • 模块若使用ES Module语法书写,通过 module 字段定义入口(需要打包工具配合使用)
    • 自定义命令行:
      • 如果是提供命令行工具,则需要通过 pkg#bin 字段来定义暴露的命令名称与实际执行的文件

    这篇文章讲述自定义命令行的声明,NPM依赖包请查看链接:自定义NPM包

    开发环境

    • 自动日志
    • 版本更新

    使用的是husky6+,配置与旧版本不同,跟随文章操作时,请注意版本

    1. husky
    • 安装
     npm install husky --save-dev
     npx husky install
    
    • 配置run-script:安装依赖后自动启动Git hooks
    ```
    "prepare": "husky install"
    ```
    
    • 追加测试钩子
      # Unix系统可用
      npx husky add .husky/pre-commit "npm run test"
      # Windows通过以下命令创建文件(引号在windows命令行中不是规范的语法)
      npx husky add .husky/pre-commit
      # Windows环境下找到新建的文件,编辑文件,指定命令
      #!/bin/sh
      . "$(dirname "$0")/_/husky.sh"
    
      npm run test
    
    1. commitlint
      • commitlint提交信息校验工具
        • 需要和校验规范配合使用,官网默认规范@commitlint/config-conventional —— 校验规范可自定义。
         npm i -D commitlint @commitlint/config-conventional
        
      • commitlint绑定@commitlint/config-conventional
      # Unix
      echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
      # Windows
      echo module.exports = {extends: ['@commitlint/config-conventional']} > commitlint.config.js
      
      • 配置Git hook:在提交commit msg进行参数校验 —— 写在run-script中无效
         # Unix系统可用
         npx husky add .husky/commit-msg "npx --no-install commitlint --edit $1"
         # Windows通过以下命令创建文件(引号在windows环境中不是规范的语法)
         npx husky add .husky/commit-msg
         # Windows环境下找到新建的文件,编辑文件,指定命令
         #!/bin/sh
         . "$(dirname "$0")/_/husky.sh"
      
         npx --no-install commitlint --edit $1
      
    2. standard-version
    • 安装依赖
     npm i --save-dev standard-version
    
    • 配置run-script:发布前自动升级版本号 + 生成日志 + 提交Git
    "prepublishOnly": "standard-version && git push --follow-tags origin master"
    

    Note: 这里不要使用prepublish钩子,该钩子在npm i时运行,而不是npm publish时运行。

    调试

    1. 进入本地NPM
    • npm link创建软链接到全局node环境中
    1. 进入需引入依赖包的项目A中
    • npm link <packageName>建立软链接依赖
    1. 在命令行切换到项目A目录结构下
    # 调用
    npx <packageName> -h
    

    开发

    NPM包是commonJS语法,使用require(),而非import...from...引入依赖。

    而,若NPM包语法使用ES6+语法书写,必须使用ES Moduleimportexport default导出方式,不要混用

    推荐几个常用的命令行辅助工具:

    • shelljs
      • 执行脚本程序
    • inquirer
      • 命令行用户交互界面
    • chalk
      • 命令行日志样式
      • 可嵌套、可链式
    • commander
      • 自定义命令行参数

    组织结构

    |- .husky
    |- bin
      |- actv2-use.js // 单文件命令
      |- actv2.js  // 入口文件,仅做中转作用
    |- lib
      |- actv2.js  // 主文件
    |- .gitignore
    |- .npmrc
    |- package.json
    |- README.md
    

    package.json配置

    {
      "name": "actv2",
      "version": "1.0.0",
      "description": "",
      "bin": "bin/actv2.js",
      "main": "lib/actv2.js",
      "scripts": {
        "prepare": "npx husky install",
        "prepublishOnly": "standard-version && git push --follow-tags origin master",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "@commitlint/config-conventional": "^13.2.0",
        "commitlint": "^13.2.1",
        "husky": "^7.0.2",
        "standard-version": "^9.3.1"
      },
      "dependencies": {
        "chalk": "^4.1.2",
        "commander": "^8.2.0",
        "fs-extra": "^10.0.0",
        "inquirer": "^8.2.0",
        "shelljs": "^0.8.4"
      }
    }
    

    入口文件

    入口文件示例:

    #!/usr/bin/env node
    var { program } = require('commander')
    
    program
    .command('add')
    .argument('<module>', '新增项目模块')
    .option('-y, --yes', '将该模块设置为部署模块')
    .action((moduleName, options) => {
      require('..')().add(moduleName, options)
    })
    
    program
    .command('use <module>', '指定部署模块')
    .alias('u')
    
    program
      .option('-V, --version', '查看版本')
      .helpOption('-h, --help', '查看使用帮助');
    
    
    program.parse(process.argv)
    
    var opts = program.opts()
    
    if (opts.version) {
      process.stdout.write(
        'actv2 ' + require('../package.json').version + '\n')
    } else {
      process.stdout.write(
        'actv2\n' +
        '\n' +
        'Options:\n' +
        '  --version Show version number\n' +
        '  --help    Show help\n' +
        '\n' +
        'Usage:\n' +
        '  actv2 --help\n'
      )
    }
    

    其中,#!/usr/bin/env node是必填行,在安装命令行后,依据该行指定node环境执行该命令行。

    #!/bin/sh
    basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
    
    case `uname` in
        *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
    esac
    
    if [ -x "$basedir/node" ]; then
      "$basedir/node"  "$basedir/../actv2/bin/actv2.js" "$@"
      ret=$?
    else
      node  "$basedir/../actv2/bin/actv2.js" "$@"
      ret=$?
    fi
    exit $ret
    

    vs. 缺失#!/usr/bin/env node

    #!/bin/sh
    basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
    
    case `uname` in
        *CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
    esac
    
    "$basedir/../actv2/lib/run.js"   "$@"
    exit $?
    

    参数解析

    借助commander第三方工具包

    1. 声明
        program.option('') // 可选参数
        program.requiredOption() // 必填参数
        program.requiredOption('-c, --cheese <type>', 'pizza must have cheese'); // <type>值必填,[type]值可选
      
    2. 解析
       program.parse(process.argv)
      
    3. 取值
       program.opts().name
       program.getOptionValue(<name>)
      

    子命令

    借助commander第三方工具包

    声明方式

    • 绑定actioncommand声明
      • command声明只要一个参数
    // 示例
    program
      .command('clone <source> [destination]') // 首行,仅有一个参数
      .description('clone a repository into a newly created directory')
      .option('-y, --yes', 'options.yes')
      .action((source, destination, options, command) => {
        console.log('clone command called');
      });
    
    • 单独的可执行文件
      • command声明添加描述参数,即表明是可执行文件的声明方式
    # 输出以下代码的文件为入口文件
    program
      .version('0.1.0')
      .command('install [name]', 'install one or more packages') // 在入口文件所在的目录下,检索`<入口文件名>-install`文件
      .command('search [query]', 'search with optional query').alise('s') // 在入口文件所在的目录下,检索`<入口文件名>-search`文件
      .command('update', 'update installed packages', { executableFile: 'myUpdateSubCommand' })
      .command('list', 'list packages installed', { isDefault: true });
    

    独立执行文件完整示例:

    // 声明
    program
    .command('use <module>', 'assign module')
    .alias('u')
    

    use命令定义

    #!/usr/bin/env node
    var chalk = require('chalk');
    var { program } = require('commander');
    var Message = require('../config/messages')
    var createdFile = require('../utils/createFile')
    
    program.option('-v, --version [version]', '指定版本号', '0.0.3') // npx <commander name> use <moduleName> -v 0.0.1
    program.parse(process.argv)
    
    var moduleName = program.args  // 获取<moduleName...>列表
    var { version } = program.opts()
    
    if (moduleName.length !== 1) {
      process.stdout.write(chalk.redBright(Message.deployOnly) + '\n')
      process.exitCode = 0
    } else {
      //
    }
    

    执行

    > npx actv2 -h
    Usage: actv2 [options] [command]
    
    Options:
      -V, --version           查看版本
      -h, --help              查看使用帮助
    
    Commands:
      add [options] <module>
      use|u <module>          指定部署模块
      help [command]          display help for command
    
    > npx actv2 add -h
    Usage: actv2 add [options] <module>
    
    Arguments:
      module      新增项目模块
    
    Options:
      -y, --yes   将该模块设置为部署模块
      -h, --help  display help for command
    

    相关文章

      网友评论

          本文标题:自定义NPM命令行

          本文链接:https://www.haomeiwen.com/subject/yewmoltx.html