记一次完整的npm包开发 — 发布过程
前言
本人也是菜鸟一枚,现学现开发的,各个文档也尚未熟练,主要面向百度开发,因此文章中只会列出用到的API,更具体的API说明文档请参考最下边的参考链接部分, 如果有能力, 籍此可以尝试开发
CLI
工具
一、npm包初始化
-
创建目录并初始化npm
mkdir auto_whs && cd auto_whs && npm init
-
在
git
创建仓库, 初始化项目, 代码仓库地址写入
package.json
中的
repository
下的
url
字段值 -
创建index.js 在开头写入以下代码, 然后就可以在Git命令行用
./index.js
执行代码了#!/usr/bin/env node console.log('hello world')
-
去掉
./
路径, 使用
bin
字段下的
auto_whs
执行- 在 package.json 添加代码
"name": "auto_whs", "bin": { "auto_whs": "index.js" }
-
执行
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 // 建立的模块的位置
-
npm unlink
解除
link
绑定-
一般来说使用
npm link
后, 在本地就可以测试了, 当将包发布到npm.org后,如果要测试线上下载的包
就需要进行
npm unlink
进行本地连接解除, -
where <命令>
如
where yarn
, 可以查找 link 后命令的位置(如
where auto_whs
), 如果 unlink 失败, 可以进入路径位置,手动删除,并且删除 node_modules 中的包
-
一般来说使用
-
npm uninstall <package> -g
在npm unlink成功后删除包
二、npm包发布更新
接着就是中间开发功能的过程, 过程后续部分来说, 假设已经开发完成,那么就要发布了
-
commit 所有代码更改到 git
-
注册npm用户
https://www.npmjs.com/
-
npm login
输入用户名/密码 登录npm -
npm publish
发布包,成功后可在npm官网搜索查看 -
修改代码后,更新包的版本
npm采用语义化版本,共三位,以
.
隔开,从左至右依次代表:主版本(major)、次要版本(minor)、补丁版本(patch)。-
npm version <patch|minor|major>
会自动修改包的版本加1, 就是将package.json中的version版本号加1, 根据更新内容决定更新的版本号 -
npm publish
-
-
npm unpublish 包名@version
删除指定版本的包 -
npm unpublish 包名 --force
删除整个包
三、shell.js
-
脚本可以通过 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: '' } })()
-
shelljs
模块重新包装了child_process, 调用命令更加方便-
npm i shelljs -S
- 引入并使用
/** * 本地模式, 需要从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
一个用户与命令行交互的工具
-
安装并引入
// npm i inquirer -S const inquirer = require('inquirer');
-
提问问题的方法
/** * 方法: 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命令行(参数)解决方案
-
安装并引入
// npm install commander // 声明 program 变量, 为简化使用,Commander 提供了一个全局对象 const { program } = require('commander');
-
简单使用
-
定义版本号
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 自定义命令关联外部可执行文件
- program.command(‘add’, ’ This is a add command’) 形如这样定义命令,没有action回调函数,有第二个参数的情况,当执行auto_whs add命令的时候会从入口文件所在的同级目录查找可执行文件,查找的可执行文件名为program-command形式,以 – 连接,后边是定义的命令, 前边是入口文件名,若入口文件名有后缀名,会被去掉, 比如 入口文件为index.js 则会查找 index-add 文件(有无后缀名均可)
- program.command(‘add’) 形如这样的,没有action 也没有第二个参数,则什么都不执行, 不会报错
- program.comman(‘add’).action(() => {}) 有action 会执行 action回调, 另外需要注意的是,这样定义命令需要放在最后,否则action的参数并非是当前执行的命令,可能会达不到你预期想要的执行操作
- 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打开浏览器,而默认是不会打开的.
-
安装
// 安装的过程会自动下载最新版的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
-
引入使用
// 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'); // 打开网页
-
设置窗口大小
Page.setViewport()
-
生成PDF
await page.pdf({path: 'hn.pdf', format: 'A4'});
-
上传
const el = await page.$(selector) await el.uploadFile(...filePaths) // 设置输入这些路径的文件的值。如果某些 filePaths 是相对路径,那么它们将被解析为相对于 当前工作目录
-
其他的一些使用到的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>) // 选择器 方法 或超时时间, 就是 等待的意思,或者等待元素出现
-
sleep函数
// 自己写了个简单的 sleep() 等待函数 function sleep (timeStamp) { return new Promise((resolve, reject) => { setTimeout(() => { resolve() }, timeStamp) }) } sleep(3000) // 利用js的单线程, 等待3秒以阻塞代码执行
八、nexe
nexe 可以将node应用打包成
exe
可执行程序
九、收藏夹吃灰记语
虽然只是一个简单的可以与命令行交互的npm包的开发,但是用到的插件也是比较多,重要的不是功能如何, 而且了解一个
CLI
的开发流程,npm包的开发、发布、更新流程,涉及的文档也有一定的量,还需要学习才能熟悉啊。
十、相关参考链接
-
https://www.w3cschool.cn/jhnpsm/mxfyoozt.html
Node.js命令行程序开发教程 -
https://segmentfault.com/a/1190000017461666
npm包发布教程系列 -
http://documentup.com/shelljs/shelljs
shelljs英文文档 -
https://www.npmjs.com/package/inquirer
inquirer.js英文文档 -
https://github.com/tj/commander.js/blob/master/Readme_zh-CN.md
Commander.js中文文档 -
http://bullyork.github.io/2017/09/27/yargs/
yargs中文教程类似于Commander.js -
https://www.npmjs.com/package/colors
colors.js文档 -
https://github.com/nexe/nexe
nexe文档 -
https://zhaoqize.github.io/puppeteer-api-zh_CN/#/
puppeteer中文API文档 -
https://blog.csdn.net/qupan1993/article/details/85371556
网上一篇
puppeteer
学习笔记
十一、附一部分
npm
相关命令
- npm info 包名 // 查看npm远程仓库当前包的最新信息,版本等
- npm root [-g] // 在标准输出上将有效的 node_modules 文件夹打印出来。
- npm ls 包名 // 查看当前有效的指定包的版本
- npm ls -g 包名
- npm list [-g] // 查看打当前安装的包
- npm update 包名 // 更新升级指定包
- npm uninstall 包名 // 从当前node_modules 删除包
- npm uninstall 包名 -S // 从package.json的 dependencies 删除
- npm uninstall 包名 -D // 从package.json的 devDependencies 删除
- npm config set registry http://registry.npmjs.org 临时设置淘宝镜像
- npm install -g cnpm –registry=https://registry.npm.taobao.org
- npm -v 查看npm版本
- npm login 登录npm
- npm publish 发布(将包发布到npm), package.json中的仓库地址一定要存在
- npm version patch 修改包的版本,在原来版本上加1
- npm unpublish 包名@version 删除指定版本的包
- npm unpublish 包名 –force 删除整个包
十二、附
nrm
相关命令
nrm(npm registry manager) 是npm的镜像源管理工具, 可以迅速的在下载源地址之间切换
- 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/
- nrm test [镜像源] 测试相应源的速度,可测所有的速度
- nrm use [镜像源] 切换镜像源名,如hollysys
- nrm add registry 添加镜像源, registry 镜像源名 url 镜像源地址
- nrm delete 删除对应的源