事情是這樣的:由於我常常會寫一些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,那就無能爲力了。。。
相關閱讀: 手把手教你寫一個命令行工具
本章完