怎麼樣寫一個能告訴你npm包名字是否被佔用的工具

事情是這樣的:

由於我常常會寫一些npm包,可是有時候我寫完一個包,npm publish 的時候卻被提示說包名字被佔用了,要不就更名字,要不就加scope,很無奈。
npm 命令行能夠經過 npm view 的方式去得知一個包是否存在,可是沒法批量得知,因此就想着寫一個工具來批量選名:)node

本教程的相關代碼已經全上傳到github: 源代碼react

NPM方式

在寫工具以前,咱們先看看怎麼經過 npm 提供的命令來得知包名是否被佔用。ios

npm viewgit

經過 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

能一步鑑別全部的包,那就太方便了。

因此,咱們一步一步來看一下應該怎麼實現這個功能。

1、看npm如何作的

咱們經過 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

2、開始寫工具

以前有一篇文章,講了怎麼寫一個命令行工具,見這裏:手把手教你寫命令行工具。這篇文章就不從怎麼從零開始構建一個命令行工具開始了,咱們直接來代碼:

文件目錄大概是這樣:

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,那就無能爲力了。。。

相關閱讀: 手把手教你寫一個命令行工具

源代碼:https://github.com/PengJiyuan...

本章完

相關文章
相關標籤/搜索