kz-source

1.0.2 • Public • Published

node 实现切换源

1)设置源的命名为:

npm config set registry  源地址

比如我们想设置淘宝镜像,使用命令:npm config set registry https://registry.npm.taobao.org 如果我们想设置 npm 的镜像的话,我们使用命令:npm config set registry https://registry.npmjs.org

2)查看 npm 的 registry 配置项命令如下

npm config get registry

nodejs 编写 cli 命令行工具 可以看之前的文章

因此会使用到如下几个工具:

{
  "dependencies": {
    "chalk": "^4.0.0",
    "commander": "^11.1.0",
    "inquirer": "^8.0.0",
    "node-http-ping": "^0.3.1"
  }
}

node-http-ping 是 ping 网址的一个库。

最后我的 package.json 配置项如下:

{
  "name": "kz-source",
  "version": "1.0.0",
  "description": "npm 切换源",
  "main": "index.js",
  "bin": {
    "kz-source": "./src/index.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "npm",
    "cnpm",
    "yarn"
  ],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "chalk": "^4.0.0",
    "commander": "^11.1.0",
    "inquirer": "^8.0.0",
    "node-http-ping": "^0.3.1"
  }
}

目录结构如下:

|--- src
| |--- index.js
|--- package.json
|--- registries.json

registries.json 是一个 json 数据,默认有哪些源,代码如下:

{
  "npm": {
    "home": "https://www.npmjs.org",
    "registry": "https://registry.npmjs.org/",
    "ping": "https://registry.npmjs.org"
  },
  "cnpm": {
    "home": "https://cnpmjs.org",
    "registry": "https://r.cnpmjs.org/",
    "ping": "https://r.cnpmjs.org"
  },
  "taobao": {
    "home": "https://registry.npm.taobao.org",
    "registry": "https://registry.npm.taobao.org/",
    "ping": "https://registry.npm.taobao.org"
  }
}

src/index.js 就是我们编写的命令行工具代码。编写完成后,我们执行 npm link, 将当前的代码 软链接到 npm 全局目录下, npm 检测到 package.json 里面存在一个 bin 字段, 它会在全局 npm 包下生存一个可执行文件.

1)kz-source 命令 查看所有可用的命令

我们在命令中执行 kz-source 命令,可以看到有如下命令:


使用 kz-source -V 可以获取到当前的版本号。

2)kz-source ls 查看所有的源

我们可以使用 kz-source ls 查看所有的源,如下:


默认就是我们的 registries.json 默认的配置三个源地址,前面的带 星号 说明是当前的源。

3)kz-source current 命令查看当前的源


4)kz-source use 切换当前的源


当我们选择淘宝源的时候,我们再查看当前的源,是淘宝源,然后查看所有的源,星号 在淘宝上,说明淘宝是当前的源。


5)kz-source ping 测试速度


6)kz-source add 增加一个自定义源


7) kz-source rename 重命名自定义名称


8)kz-source edit 编辑自定义源地址


9)kz-source delete 删除自定义源


src/index.js 所有代码如下(也是参考网上资料的):

#!/usr/bin/env node

const { program } = require('commander');

// 引入package.json
const PKG = require('../package.json');

// 引入初始源
const registries = require('../registries.json');

const inquirer = require('inquirer');

// 子线程用于执行shell命令
const { exec, execSync } = require('child_process');

// ping 网址的一个库
const ping = require('node-http-ping');

const fs = require('fs');
const chalk = require('chalk'); // console 变颜色
const path = require('path');
program.version(PKG.version); // 设置版本默认命令 -V --version

const whiteList = ['npm', 'cnpm', 'taobao']; // 白名单

// 获取当前源地址 比如 https://registry.npmjs.org/
const getOrigin = async () => {
  return await execSync('npm get registry', { encoding: "utf-8" })
};

// 列出所有的源,如果当前有在使用前面加上 *
program.command('ls').description('查看所有的源').action(async () => {
  const rets = await getOrigin();
  const keys = Object.keys(registries);
  const message = [];

  /**
   * 获取源的key的最大长度 + 3 的含义,
   * 比如淘宝源,最后变成 *taobao-- https://registry.npm.taobao.org/
   * 如果是 npm 源的话,最后拼接成 * npm---- https://registry.npmjs.org/
   */
  const max = Math.max(...keys.map(v => v.length)) + 3;

  keys.forEach(k => {
    // 如果是当前的源的话,前面加一个星号,代表是当前的源
    const newK = registries[k].registry == rets.trim() ? ('* ' + k) : (' ' + k);
    const arrs = new Array(...newK);
    arrs.length = max;
    const prefix = Array.from(arrs).map(v => v ? v : '-').join('');

    message.push(prefix + ' ' + registries[k].registry);
  })
  console.log(message.join('\n'));
});

// 切换源
program.command('use').description('请切换源').action(() => {
  inquirer.prompt([
    {
      type: 'list',
      name: 'selectSource',
      message: "请选择源",
      choices: Object.keys(registries)
    }
  ]).then(result => {
    const reg = registries[result.selectSource].registry;
    exec(`npm config set registry ${reg}`, null, (err, stdout, stderr) => {
      if (err) {
        console.error('切换错误', err);
      } else {
        console.log('切换成功');
      }
    })
  })
});

// 查看当前的源
program.command('current').description('查看当前源').action(async () => {
  const reg = await getOrigin();
  const v = Object.keys(registries).find(k => {
    if (registries[k].registry === reg.trim()) {
      return k;
    }
  })
  if (v) {
    console.log(chalk.blue('当前的源:', v));
  } else {
    console.log(chalk.green('当前的源:', reg));
  }
});

// ping 源
program.command('ping').description('测试源地址的速度').action(() => {
  inquirer.prompt([
    {
      type: 'list',
      name: 'selectSource',
      message: '请选择源',
      choices: Object.keys(registries)
    }
  ]).then(result => {
    const url = registries[result.selectSource].ping.trim();
    ping(url).then(time => console.log(chalk.blue(`响应时长: ${time}ms`))).catch(() => console.log(chalk.red('--时间超时---')))
  });
})

// 添加源 读写 registries.json 文件实现
program.command('add').description('添加源').action(() => {
  inquirer.prompt([
    {
      type: 'input',
      name: 'name',
      message: '请输入源名称',
      validate(answer) {
        const keys = Object.keys(registries);
        if (keys.includes(answer)) {
          return `不能起名${answer}跟保留字冲突`;
        }
        if (!answer) {
          return '名称不能为空';
        }
        return true;
      }
    },
    {
      type: 'input',
      name: 'url',
      message: '请输入源地址',
      validate(answer) {
        if (!answer) {
          return '源地址不能为空';
        }
        return true;
      }
    }
  ]).then(result => {
    const del = (url) => {
      const arr = url.split('');
      return arr[arr.length - 1] === '/' ? arr.pop() && arr.join('') : arr.join('');
    }
    registries[result.name] = {
      home: result.url.trim(),
      registry: result.url.trim(),
      ping: del(result.url.trim()), // 去掉末尾的 /
    }
    try {
      fs.writeFileSync(path.join(__dirname, '../registries.json'), JSON.stringify(registries, null, 4));
      console.log(chalk.blue('添加完成'));
    } catch (e) {
      console.log(chalk.red(e));
    }
  })
});

// 删除自定义的源
program.command('delete').description('删除自定义的源').action(() => {
  const keys = Object.keys(registries);
  if (keys.length === whiteList.length) {
    return console.log(chalk.red('当前无自定义源可以删除'));
  } else {
    // 删除 白名单中不存在的源,也就是我们自定义的源
    const diff = keys.filter((key) => !whiteList.includes(key));
    inquirer.prompt([
      {
        type: "list",
        name: "sel",
        message: '请选择要删除的源',
        choices: diff
      }
    ]).then(async result => {
      const current = await getOrigin();
      const setOrigin = registries[result.sel];
      if (current.trim() == setOrigin.registry.trim()) {
        console.log(chalk.red(`当前还在使用该源${registries[result.sel].registry}, 请切换其他源,然后进行删除`));
      } else {
        try {
          delete registries[result.sel];
          // 重新写入文件里面
          fs.writeFileSync(path.join(__dirname, '../registries.json'), JSON.stringify(registries, null, 4));
          console.log(chalk.green('SUCCESS 操作完成'));
        } catch (e) {
          console.log(chalk.red(e));
        }
      }
    })
  }
})

// 重命名自定义源名称
program.command('rename').description('重命名自定义源名称').action(() => {
  const keys = Object.keys(registries);
  if (keys.length === whiteList.length) {
    return console.log(chalk.red('当前无自定义的源可以重命名'));
  } else {
    const diff = keys.filter((key) => !whiteList.includes(key));
    inquirer.prompt([
      {
        type: "list",
        name: 'sel',
        message: '请选择源名称',
        choices: diff
      },
      {
        type: "input",
        name: "rename",
        message: '请输入新的源名称',
        validate(answer) {
          const keys = Object.keys(registries);
          if (keys.includes(answer)) {
            return console.log(chalk.red(`不能起名${answer}, 已存在该源名称`));
          }
          if (!answer.trim()) {
            return console.log(chalk.red('源名不能为空'));
          }
          return true;
        }
      }
    ]).then(async result => {
      registries[result.rename] = Object.assign({}, registries[result.sel]);
      delete registries[result.sel];

      try {
        fs.writeFileSync(path.join(__dirname, '../registries.json'), JSON.stringify(registries, null, 4));
        console.log(chalk.greenBright(`SUCCESS 重命名完成 ${result.rename}`));
      } catch (e) {
        console.log(chalk.red(e));
      }
    })
  }
})

// 编辑自定义源地址
program.command('edit').description('编辑自定义源地址').action(async () => {
  const keys = Object.keys(registries);
  if (keys.length === whiteList.length) {
    return console.log(chalk.red('当前无自定义源可以编辑'));
  }
  const diff = keys.filter((key) => !whiteList.includes(key));
  const { sel } = await inquirer.prompt([
    {
      type: "list",
      name: "sel",
      message: "请选择需要编辑的源",
      choices: diff
    }
  ]);
  const { registerUrl } = await inquirer.prompt([{
    type: "input",
    name: "registerUrl",
    message: "输入修改后的源地址",
    default: () => registries[sel].registry,
    validate(registerUrl) {
      if (!registerUrl.trim()) {
        return '源地址不能为空';
      }
      return true;
    }
  }])
  const del = (url) => {
    const arr = url.split('');
    return arr[arr.length - 1] === '/' ? arr.pop() && arr.join('') : arr.join('');
  }
  registries[sel] = {
    home: registerUrl.trim(),
    registry: registerUrl.trim(),
    ping: del(registerUrl.trim())
  }
  try {
    fs.writeFileSync(path.join(__dirname, '../registries.json'), JSON.stringify(registries, null, 4));
    console.log(chalk.blue('修改完成'));
  } catch (e) {
    console.log(chalk.red(e));
  }
})

program.parse(process.argv);

Readme

Keywords

Package Sidebar

Install

npm i kz-source

Weekly Downloads

0

Version

1.0.2

License

ISC

Unpacked Size

21.8 kB

Total Files

4

Last publish

Collaborators

  • kongzhi0707