事情是这样的:由于我常常会写一些npm包,可是有时候我写完一个包,
npm publish
的时候却被提示说包名字被占用了,要不就更名字,要不就加scope,很无奈。
npm 命令行能够经过npm view
的方式去得知一个包是否存在,可是没法批量得知,因此就想着写一个工具来批量选名:)node本教程的相关代码已经全上传到github: 源代码react
在写工具以前,咱们先看看怎么经过 npm 提供的命令来得知包名是否被占用。ios
npm view
git
经过 npm view -h
咱们能够得知其用法:github
npm view [<@scope>/]<pkg>[@<version>] [<field>[.subfield]...]
aliases: v, info, shownpm
经过以上命令来看看 unused-npm-names
包:json
npm view unused-npm-names # 或者 npm info unused-npm-names
会输出:axios
{ name: 'unused-npm-names', 'dist-tags': { latest: '1.1.1' }, versions: [ '1.0.0', '1.0.1', '1.1.0', '1.1.1' ], time: { created: '2018-09-07T02:53:05.277Z', '1.0.0': '2018-09-07T02:53:05.439Z', modified: '2018-09-07T03:44:06.363Z', '1.0.1': '2018-09-07T03:07:46.542Z', '1.1.0': '2018-09-07T03:35:40.221Z', '1.1.1': '2018-09-07T03:44:03.534Z' }, maintainers: [ 'pjy <731401082@qq.com>' ], description: 'Find unused npm names', homepage: 'https://github.com/PengJiyuan/unused-npm-names#readme', keywords: [ 'npm', 'names', 'unused', 'find' ], repository: { type: 'git', url: 'git+https://github.com/PengJiyuan/unused-npm-names.git' }, author: 'PengJiyuan', bugs: { url: 'https://github.com/PengJiyuan/unused-npm-names/issues' }, license: 'MIT', readmeFilename: 'README.md', version: '1.1.1', main: 'index.js', bin: { unn: 'cli.js' }, scripts: { test: 'echo "Error: no test specified" && exit 1' }, dependencies: { axios: '^0.18.0', chalk: '^2.4.1', commander: '^2.17.1' }, gitHead: '818611db1c2baeb589cb3f639559ab6afc9f8e8f', dist: { integrity: 'sha512-t9bCfY3qbeVY54QC6Cznn3YhM0jq6HX0fE0r5TMAq1IOzu+NQ/caA8tfj62pZtDuZKb9R29ne7UyPB+4zAAplw==', shasum: '0b7c162f7656c0d74868bf567713150488f8c473', tarball: 'https://registry.npmjs.org/unused-npm-names/-/unused-npm-names-1.1.1.tgz', fileCount: 5, unpackedSize: 4544, 'npm-signature': '-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkfQDCRA9TVsSAnZWagAAwS4QAKFC1MnosxmJEws07U4O\ngfUPLP04ZLZqtW6nuB/29A72DE1+bh/TGsir83r/sYf1TAPSLOCRd3Nrky3A\n7+umUUOl5zGU5WyG86Fo2XOl5cYgXXWXU6LcZufG/cwM3Xi9MUfxnT7zCEWt\nQPAE8Oh9UhkWCnvFMBA6M6knqK9K08nQf0Ke55UoiuX+OqF8BUlNw8LqEwrI\nMTW8hpjKqsAdo3JhBu0ZkrfTRMq7cTawfjAg+qDs4SSTuWD9OJ9d/2y4OC/p\nX6+3I+Et+SqFJxjGDBounjF1GYYiH3dQPRN8UWL1p9Ypu6YsiZ7l8dp6RH15\nHFUv6lsCmZvhkKc1zO1pY67xUOA9VbLjhXtObwopFvCIehlv3cCw5FMwoa7x\nz+tou0J4II6n68cG6IfTt+9odi9abj7M2YxStW32Miu3efhpXiw2PpX3HWOW\njkY7IQryyxJbQIdKHJqJ59fADHLxpdmr6WADYWt8mKI+9TK9onpSgFgX4udw\ng7fXN3z/L6i7yY+0fvvX/b0jjVzVFNP5kFnUBSnWk/Hjm+h96QS+0xfRCRNv\n5CmVT2kbxYNAdFsFFoNCqHqE+uQoMrSwBw1SIJdybWjs84QrLOrDFjhKypev\nl6bzrgcyE0VWYY1A+zdyquL1cQ+xEJacsfN5NbicxTZhDU0enAtcxhKSe7bz\nJ9CP\r\n=t8xy\r\n-----END PGP SIGNATURE-----\r\n' }, directories: {} }
这样的输出太长了,咱们能够只看 unused-npm-names
最近的版本号:segmentfault
npm view unused-npm-names version
会输出:api
1.1.1
固然,若是这个包不存在的话,就会报 404
的错误,咱们也就知道这个包名是否被占用了。
上面的方式是能够获得咱们想要的结果,但是若是我想从一批名字中选一个可用的,就没有那么方便了,就要一个一个试了。
若是有一个工具能够像这样使用:
unn react react-router react-dom react-pp react-fdasf
能一步鉴别全部的包,那就太方便了。
因此,咱们一步一步来看一下应该怎么实现这个功能。
咱们经过 npm view
能够查看一个包的信息,那么在走这个命令的时候,npm 确定是发了一个请求去拿到的这个包的数据,那么咱们怎么知道 npm 发的什么请求呢?
# 加 --verbose 后缀来看详细的输出 npm view unused-npm-names --verbose
会输出:
... npm http request GET https://registry.npmjs.org/unused-npm-names ... npm info ok
咱们在其中发现,npm 发了个 GET
请求,请求的url是 https://registry.npmjs.org/unused-npm-names
。
哦,那知道了,咱们能够请求 https://registry.npmjs.org/${packageName}
来获取名为 packageName
的包信息。固然,在npm的官方仓库也能找到相关api的用法:package-metadata。
以前有一篇文章,讲了怎么写一个命令行工具,见这里:手把手教你写命令行工具。这篇文章就不从怎么从零开始构建一个命令行工具开始了,咱们直接来代码:
文件目录大概是这样:
unused-npm-names ├── node_modules ├── package.json ├── cli.js (bin) └── index.js (main)
package.json
:
{ "name": "unused-npm-names", "version": "1.0.0", "description": "Find unused npm names", "main": "index.js", "bin": { "unn": "cli.js" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "license": "MIT", "dependencies": { "axios": "^0.18.0", "chalk": "^2.4.1", "commander": "^2.17.1" } }
经过 package.json
中设置 bin
字段,咱们将命令的名字设置为 unn
,比较简短,方便实用。
咱们把查询的主逻辑放到 index.js
中,把命令行逻辑放到 cli.js
中,这样的话咱们既能够经过 cli 的方式去使用,也能够经过 require
的方式在 nodejs 脚本中使用。
// index.js const axios = require('axios'); // 用于发送 http 请求 const chalk = require('chalk'); // 终端输出带颜色的文本 // search方法的参数是一个数组,存放着须要查询的包的名字 // 好比咱们要查询 react和react-dom,那么search(['react', 'react-dom']) function search(pkgs = []) { if (!Array.isArray(pkgs)) { throw 'Param should be an array.'; } console.log(); pkgs.forEach((pkg) => { axios.get(`https://registry.npmjs.org/${pkg}`) .then((res) => { // 若是请求成功,说明包存在,那么名字被占用。 console.log(`${chalk.cyan(pkg)}: ${chalk.red('Used ❌')}`); }) .catch((err) => { // 若是请求失败,而且是由于404报错,那么证实包不存在,名字可用。 if (err.stack && /Request failed with status code 404/.test(err.stack)) { console.log(`${chalk.cyan(pkg)}: ${chalk.green('Unused ✅')}`); } else { // 处理未知状况 console.log(`${chalk.cyan(pkg)}: ${chalk.gray('Unknown 🤔')}`) } }); }); } module.exports = search;
咱们最终实现的cli要支持两种模式,一种是命令行参数解析,一种是传入js文件。以下:
// cli.js #!/usr/bin/env node 'use strict'; const path = require('path'); const program = require('commander'); // 命令行参数解析 const chalk = require('chalk'); const search = require('.'); const pkg = require('./package.json'); program .version(pkg.version, '-v, --version') .usage('[names]') .option('-c, --config [config]', 'use config files') .on('--help', () => { console.log('\n Examples:\n'); console.log(` ${chalk.green('$')} unn react,react-dom,react-router`); console.log(''); }) .parse(process.argv); // program.args是全部解析直接传入的参数 // 好比 unn react react-dom --hehe // 那么program.args是['react', 'react-dom'] let pkgs = program.args; // 若是指定js文件的话,pkgs从文件中读取 if (program.config && typeof program.config === 'string') { // process.cwd() 是当前程序运行的目录 const files = path.resolve(process.cwd(), program.config); try { pkgs = require(files); search(pkgs); } catch (err) { console.log(err); process.exit(1); } } else { if (pkgs.length > 0) { search(pkgs); } else { program.outputHelp(); } }
这样咱们的工具就建立好了,咱们一块儿来试一下吧。(注意:要先 npm link
或者 全局安装了咱们写的这个包,不明白的能够先看上一篇教程)
cli使用
unn react react-dom unnnn # 输出 unnnn: Unused ✅ react-dom: Used ❌ react: Used ❌
配置文件使用
// names.js module.exports = ['react', 'react-router', 'react-dom', 'hahahahahaha'];
unn -c names.js # 输出 hahahahahaha: Unused ✅ react-dom: Used ❌ react: Used ❌ react-router: Used ❌
NodeJs api使用
// search.js const search = require('unused-npm-names'); search(['react', 'react-dom', 'react-router', 'unused-npm-names']);
node search.js # 输出 unused-npm-names: Used ❌ react-router: Used ❌ react: Used ❌ react-dom: Used ❌
固然,这种方式不是百分百准确的,由于有的时候,就算包名字没被占用,也可能会被提示,跟已经存在的包名字太类似,让你更名字或者加scope,那就无能为力了。。。
相关阅读: 手把手教你写一个命令行工具
本章完