记一次完整的npm包开发 — 发布过程

  • Post author:
  • Post category:其他




记一次完整的npm包开发 — 发布过程

前言

本人也是菜鸟一枚,现学现开发的,各个文档也尚未熟练,主要面向百度开发,因此文章中只会列出用到的API,更具体的API说明文档请参考最下边的参考链接部分, 如果有能力, 籍此可以尝试开发

CLI

工具



一、npm包初始化

  1. 创建目录并初始化npm


    mkdir auto_whs && cd auto_whs && npm init



  2. git

    创建仓库, 初始化项目, 代码仓库地址写入

    package.json

    中的

    repository

    下的

    url

    字段值

  3. 创建index.js 在开头写入以下代码, 然后就可以在Git命令行用

    ./index.js

    执行代码了

      #!/usr/bin/env node
      console.log('hello world')
    
  4. 去掉


    ./


    路径, 使用

    bin

    字段下的

    auto_whs

    执行

    1. 在 package.json 添加代码
      "name": "auto_whs",
      "bin": {
        "auto_whs": "index.js"
      }
    
    1. 执行

      npm link

      绑定就可以用

      auto_whs

      (package.json中的name) 直接执行了

    npm link 用来在本地项目和本地npm模块之间建立连接,可以在本地进行模块测试

      npm WARN ka@1.0.0 No description
      npm WARN ka@1.0.0 No repository field.
      up to date in 1.026s
      found 0 vulnerabilities
      D:\nodejs\ka -> D:\nodejs\node_modules\ka\index.js // 建立的命令的位置
      D:\nodejs\node_modules\ka -> C:\Users\Hollysys\Desktop\auto_whs // 建立的模块的位置
    


  5. npm unlink


    解除

    link

    绑定

    • 一般来说使用

      npm link

      后, 在本地就可以测试了, 当将包发布到npm.org后,如果要测试线上下载的包

      就需要进行

      npm unlink

      进行本地连接解除,


    • where <命令>




      where yarn

      , 可以查找 link 后命令的位置(如

      where auto_whs

      ), 如果 unlink 失败, 可以进入路径位置,手动删除,并且删除 node_modules 中的包


  6. npm uninstall <package> -g


    在npm unlink成功后删除包



二、npm包发布更新

接着就是中间开发功能的过程, 过程后续部分来说, 假设已经开发完成,那么就要发布了

  1. commit 所有代码更改到 git

  2. 注册npm用户

    https://www.npmjs.com/


  3. npm login

    输入用户名/密码 登录npm


  4. npm publish

    发布包,成功后可在npm官网搜索查看

  5. 修改代码后,更新包的版本

    npm采用语义化版本,共三位,以

    .

    隔开,从左至右依次代表:主版本(major)、次要版本(minor)、补丁版本(patch)。


    1. npm version <patch|minor|major>

      会自动修改包的版本加1, 就是将package.json中的version版本号加1, 根据更新内容决定更新的版本号

    2. npm publish

  6. npm unpublish 包名@version

    删除指定版本的包


  7. npm unpublish 包名 --force

    删除整个包



三、shell.js

  1. 脚本可以通过 child_process 模块新建子进程, 从而执行unix命令

      // child_process 模块是 Node的模块,如 fs,path 一样可以直接require
      // util 模块是Node的模块, 封装了很多通用方法,其中promisify可以将异步方法包装成promise
      const util = require('util')
      const exec = util.promisify(require('child_process').exec);
      (async () => {
        // const res = await exec('这里是要执行的命令')
        const res = await exec('echo hello world')
        console.log(res) // { stdout: 'hello world\r\n', stderr: '' }
      })()
    

  2. shelljs

    模块重新包装了child_process, 调用命令更加方便


    1. npm i shelljs -S
    2. 引入并使用
      /**
       * 本地模式, 需要从shell调用方法
      */
      const shell = require('shelljs');
      // 调用shell的exec方法执行命令, 其实调用的child_process.exec
      // 默认调用命令会在命令行中输出命令调用的信息, 通过 silent: true 可以不在命令行中输出命令调用的输出
      let message = shell.exec(
        `git log --no-merges --pretty=%s --author="${username}" --after="${startTime}" --before="${endTime}"`
      , {silent: true}).toString().trim(); // commit说明信息
    
      shell.cd(dir) // 切换目录
      shell.echo('echo 输出内容') // echo输出内容
      shell.chmod(755, <file|dir>) // 修改权限
      shell.cat() // 查看
      shell.cp() // 复制
      shell.mkdir() // 创建目录
      shell.pwd() // 查看当前所在目录
      shell.mv() // 重命名或者移动文件、目录
      shell.rm() // 删除
      /**
       * 全局模式, 可以直接在文件中写命令
      */
      require('shelljs/global')
    



四、inquirer.js

一个用户与命令行交互的工具

  1. 安装并引入

      // npm i inquirer -S
      const inquirer = require('inquirer'); 
    
  2. 提问问题的方法

      /**
       * 方法: inquirer.prompt(questions) -> promise   questions 数组类型
       *        
       * type: 表示提问的类型
       *    -- input 输入框
       *    -- password 密码输入框 密码不显示
       *    -- confirm  询问框 y/n
       *    -- list 选择列表  choices可用
       *    -- rawlist 带编号的选择列表  choices可用
       *    -- expand 带缩写选择列表  choices可用
       *    -- checkbox 多选  choices可用
       *    -- editor
       *  message   问题的描述
       *  name      存储当前问题回答的变量,即字段名
       *  validate  对用户输入的值或者做的选择做校验
       *  default   问题的默认结果值 
       *  choices   列表选项,在某些type下可用,并且包含一个分隔符(separator)
       *  filter    对用户的回答进行过滤处理,返回处理后的值
       *  transformer 对用户回答的显示效果进行处理(如:修改回答的字体或背景颜色),但不会影响最终的答案的内容
       *  when      根据前面问题的回答,判断当前问题是否需要被回答
       *  pageSize  修改某些type类型下的渲染行数
       *  prefix    修改message默认前缀
       *  suffix    修改message默认后缀。
      */
      
      // 说明: 字符串后跟 .red/green 等, 是用了 colors 插件 对命令行输出文字改变颜色
      const initQuestions = [{
          type: 'input',
          message: 'whs用户名:'.green,
          name: 'whsUser',
          validate: (value) => {
            if (!value) {
              const {
                whsUser = ''
              } = readConfig();
              if (whsUser) return true
              return 'whs用户名不能为空'.red
            }
            return true
          }
        },
        {
          type: 'password',
          message: 'whs密码:'.green,
          name: 'whsPwd',
          default: '',
          validate: (value) => {
            if (value) {
              return true;
            } else {
              return '密码不能为空'.red
            }
          }
        },
        {
          type: 'confirm',
          message: '是否保存信息?'.magenta,
          name: 'isSaveConfig',
          default: true
        },
        {
          type: 'list',
          message: '请选择一种水果:',
          name: 'fruit',
          choices: [
              "Apple",
              "Pear",
              "Banana"
          ],
          filter: function (val) { // 使用filter将回答变为小写
              return val.toLowerCase();
          }
        },
        {
          type: 'rawlist',
          message: '请选择一种水果:',
          name: 'fruit',
          choices: [
            "Apple",
            "Pear",
            "Banana"
          ]
        },
        {
          type: "expand",
          message: "请选择一种水果:",
          name: "fruit",
          choices: [
              {
                  key: "a",
                  name: "Apple",
                  value: "apple"
              },
              {
                  key: "O",
                  name: "Orange",
                  value: "orange"
              },
              {
                  key: "p",
                  name: "Pear",
                  value: "pear"
              }
          ]
        },
        {
          type: "checkbox",
          message: "选择颜色:",
          name: "color",
          choices: [
              {
                  name: "red"
              },
              new inquirer.Separator(), // 添加分隔符
              {
                  name: "blur",
                  checked: true // 默认选中
              },
              {
                  name: "green"
              },
              new inquirer.Separator("--- 分隔符 ---"), // 自定义分隔符
              {
                  name: "yellow"
              }
          ]
        },
        {
          type: "editor",
          message: "请输入备注:",
          name: "editor"
        }
      ]
      const answers = await inquirer.prompt(initQuestions) // await  需要在async关键字下使用,此处未做
      console.log(answers) // 交互完毕得到的结果,可以用来后续操作
    



五、commander.js

完整的Node.js命令行(参数)解决方案

  1. 安装并引入

      // npm install commander
      // 声明 program 变量, 为简化使用,Commander 提供了一个全局对象
      const { program } = require('commander');
    
  2. 简单使用

  • 定义版本号

      program.version('0.0.1') // version 定义版本
    
  • 选项


    program.option()


    • .option()

      定义选项,同时可以附加选项简介
    • 每个选项可以定义一个段选项(-后面跟单个字符)和一个长选项(–后面接一个或多个单词), 使用逗号、空格、或者 | 分隔
      // <dir> 表示 -a或者 --auto 后边的参数传值必填
      // [dir] 表示 -e或者 --exec 后边的参数传值可选
      // 通过program.parse(arguments)方法处理参数,没有被使用的选项会存放在program.args数组中
      // .option(arg1, arg2, [arg3]) arg1 定义选项、arg2定义说明, arg3定义默认值, 
      program
          .option('-a, --auto', 'auto job_booking')
          .option('-p, --project-path <dir>', 'project fulPath') // 项目目录路径
          .option('-e, --exec [dir]', 'config and exec')
          .parse(process.argv) 
    
      const program_opts = program.opts() // 获取命令行参数选项
      const program_args = program.args // 获取命令行参数
      console.log(program_opts) // 如 auto_whs -a   结果: { auto: true, projectPath: undefined, exec: undefined }
      console.log(program_args) // 如 auto_whs config -a  可以解析到参数结果: ['config']
    
  • 通过.command()或.addCommand()可以配置命令,有两种实现方式:为命令绑定处理函数,或者将命令单独写成一个可执行文件

  • .command()的第一个参数可以配置命令名称及参数,参数支持必选(尖括号表示)、可选(方括号表示)及变长参数(点号表示,如果使用,只能是最后一个参数)。

      // 通过绑定处理函数实现命令(这里的指令描述为放在`.command`中)
      // 返回新生成的命令(即该子命令)以供继续配置
      program
        .command('clone <source> [destination]')
        .description('clone a repository into a newly created directory')
        .action((source, destination) => {
          console.log('clone command called');
        });
    
      // 通过独立的的可执行文件实现命令 (注意这里指令描述是作为`.command`的第二个参数)
      // 返回最顶层的命令以供继续添加子命令
      program
        .command('start <service>', 'start named service')
        .command('stop [service]', 'stop named service, or all if no name supplied');
    
      // 分别装配命令
      // 返回最顶层的命令以供继续添加子命令
      program
        .addCommand(build.makeBuildCommand());  
    
  • command 自定义命令关联外部可执行文件

  1. program.command(‘add’, ’ This is a add command’) 形如这样定义命令,没有action回调函数,有第二个参数的情况,当执行auto_whs add命令的时候会从入口文件所在的同级目录查找可执行文件,查找的可执行文件名为program-command形式,以 – 连接,后边是定义的命令, 前边是入口文件名,若入口文件名有后缀名,会被去掉, 比如 入口文件为index.js 则会查找 index-add 文件(有无后缀名均可)
  2. program.command(‘add’) 形如这样的,没有action 也没有第二个参数,则什么都不执行, 不会报错
  3. program.comman(‘add’).action(() => {}) 有action 会执行 action回调, 另外需要注意的是,这样定义命令需要放在最后,否则action的参数并非是当前执行的命令,可能会达不到你预期想要的执行操作
  4. program.comman(‘add’, ‘这是第二个参数对命令的说明’).action(() => {}) 有第二个参数也有action, 会执行action, 不会查找外部可执行文件



六、colors.js

一个用于 node.js 终端 console.log 的颜色库,可以给命令行中输出不同颜色的文字

  // npm install colors
  var colors = require('colors');
  console.log('hello'.green); // outputs green text
  console.log('i like cake and pies'.underline.red) // outputs red underlined text
  console.log('inverse the color'.inverse); // inverses the color
  console.log('OMG Rainbows!'.rainbow); // rainbow
  console.log('Run the trap'.trap); // Drops the bass
  console.log(colors.green('这是绿色'))


colors 支持的样式

colors 不仅仅支持输出的文字颜色,还支持输出的背景颜色、字体样式等

  • 支持的字体颜色

    • black
    • red
    • green
    • yellow
    • blue
    • magenta

      洋红色
    • cyan

      青色
    • white
    • gray
    • grey

      灰色



七、puppeteer

Puppeteer 是一个 Node 库,它提供了一个高级 API 来通过 DevTools 协议控制 Chromium 或 Chrome。Puppeteer 默认以 headless 模式运行,但是可以通过修改配置文件运行“有头”模式。 由头模式 就是 { headless:false } 会启动chromium打开浏览器,而默认是不会打开的.

  1. 安装

      // 安装的过程会自动下载最新版的Chromium以保证API可以使用
      // Chrominum 是从国外下载的, 所以一般会失败报错
      // 可以通过环境变量类配置不下载chrominum,具体请百度或查看文档PUPPETEER_SKIP_CHROMIUM_DOWNLOAD
      npm install puppeteer -S
    
      // 另一种方式是 替换puppeteer插件,使用 puppeteer-chromium-resolver
      // 这个插件会在下载的时候 自动切换下载源,比如 用cnpm下载过程中,走到下载chrominum时会自动切换到npm源
      // 同样的,引入方式也会发生改变,具体请看下文
      npm install puppeteer-chromium-resolver -S
    
  2. 引入使用

    //  puppeteer 插件时候引用方式
    (async () => {
      const browser = await puppeteer.launch(); // 创建浏览器实例,会唤起chrominum 浏览器窗口
      const page = await browser.newPage(); // 创建一个新tab页
      await page.goto('https://www.baidu.com'); // 打开百度首页
      await page.screenshot({path: 'example.png'}); // 截屏
    
      await browser.close(); // 关闭浏览器实例
    })();
    
    // puppeteer-chrominum-resolver 插件时候引用方式
    const PCR = require("puppeteer-chromium-resolver");
    const stats = await PCR();
    const browser = await stats.puppeteer.launch({headless: false,
      args: ["--no-sandbox"],
      executablePath: stats.executablePath,
      defaultViewport: {
        width: 1550, // 设置浏览器窗口大小
        height: 722,
        slowMo: 20 // 设置peppeteer操作的运行速度, 可以更清晰的看清运行过程
      }
    })
    const page = await browser.newPage();
    await page.goto('https://www.baidu.com'); // 打开网页
    
  3. 设置窗口大小

      Page.setViewport()
    
  4. 生成PDF

      await page.pdf({path: 'hn.pdf', format: 'A4'});
    
  5. 上传

    const el = await page.$(selector)
    await el.uploadFile(...filePaths) // 设置输入这些路径的文件的值。如果某些 filePaths 是相对路径,那么它们将被解析为相对于 当前工作目录
    
  6. 其他的一些使用到的API

      page.on('console', msg => console.log('PAGE LOG:', msg.text())); // 捕获console输出
      const el = await page.$(selector) // 获取一个元素
      el.click() // 点击元素
      page.$$(selector) // 获取一类元素
      page.$eval(selector, pageFunction[...args]) // 获取元素后执行pageFunction,
      page.waitFor(string|number|function>) // 选择器 方法 或超时时间, 就是 等待的意思,或者等待元素出现
    
  7. sleep函数

      // 自己写了个简单的 sleep() 等待函数
        function sleep (timeStamp) {
          return new Promise((resolve, reject) => {
            setTimeout(() => {
              resolve()
            }, timeStamp)
          })
        }
        sleep(3000) // 利用js的单线程, 等待3秒以阻塞代码执行
    



八、nexe

nexe 可以将node应用打包成

exe

可执行程序



九、收藏夹吃灰记语

虽然只是一个简单的可以与命令行交互的npm包的开发,但是用到的插件也是比较多,重要的不是功能如何, 而且了解一个

CLI

的开发流程,npm包的开发、发布、更新流程,涉及的文档也有一定的量,还需要学习才能熟悉啊。



十、相关参考链接


  1. https://www.w3cschool.cn/jhnpsm/mxfyoozt.html

    Node.js命令行程序开发教程

  2. https://segmentfault.com/a/1190000017461666

    npm包发布教程系列

  3. http://documentup.com/shelljs/shelljs

    shelljs英文文档

  4. https://www.npmjs.com/package/inquirer

    inquirer.js英文文档

  5. https://github.com/tj/commander.js/blob/master/Readme_zh-CN.md

    Commander.js中文文档

  6. http://bullyork.github.io/2017/09/27/yargs/

    yargs中文教程类似于Commander.js

  7. https://www.npmjs.com/package/colors

    colors.js文档

  8. https://github.com/nexe/nexe

    nexe文档

  9. https://zhaoqize.github.io/puppeteer-api-zh_CN/#/

    puppeteer中文API文档

  10. https://blog.csdn.net/qupan1993/article/details/85371556

    网上一篇

    puppeteer

    学习笔记



十一、附一部分


npm


相关命令

  1. npm info 包名 // 查看npm远程仓库当前包的最新信息,版本等
  2. npm root [-g] // 在标准输出上将有效的 node_modules 文件夹打印出来。
  3. npm ls 包名 // 查看当前有效的指定包的版本
  4. npm ls -g 包名
  5. npm list [-g] // 查看打当前安装的包
  6. npm update 包名 // 更新升级指定包
  7. npm uninstall 包名 // 从当前node_modules 删除包
  8. npm uninstall 包名 -S // 从package.json的 dependencies 删除
  9. npm uninstall 包名 -D // 从package.json的 devDependencies 删除
  10. npm config set registry http://registry.npmjs.org 临时设置淘宝镜像
  11. npm install -g cnpm –registry=https://registry.npm.taobao.org
  12. npm -v 查看npm版本
  13. npm login 登录npm
  14. npm publish 发布(将包发布到npm), package.json中的仓库地址一定要存在
  15. npm version patch 修改包的版本,在原来版本上加1
  16. npm unpublish 包名@version 删除指定版本的包
  17. npm unpublish 包名 –force 删除整个包



十二、附


nrm


相关命令

nrm(npm registry manager) 是npm的镜像源管理工具, 可以迅速的在下载源地址之间切换

  1. nrm ls 查看当前可选的源地 (带*的是当前使用的源)
    npm -------- https://registry.npmjs.org/
    yarn ------- https://registry.yarnpkg.com/
    cnpm ------- http://r.cnpmjs.org/
    taobao ----- https://registry.npm.taobao.org/
    nj --------- https://registry.nodejitsu.com/
    npmMirror -- https://skimdb.npmjs.com/registry/
    edunpm ----- http://registry.enpmjs.org/
  * hollysys --- http://192.168.88.50:8081/repository/group-npm/

  1. nrm test [镜像源] 测试相应源的速度,可测所有的速度
  2. nrm use [镜像源] 切换镜像源名,如hollysys
  3. nrm add registry 添加镜像源, registry 镜像源名 url 镜像源地址
  4. nrm delete 删除对应的源



版权声明:本文为c_Janz原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。