Skip to content

简易的 npm 包依赖查看器

Published: at 09:05 PMSuggest Changes

简易的 npm 包依赖查看器

Github: simple-npm-dependency-viewer

代码如下

const registryUrl = 'https://registry.npmjs.org';
const axios = require('axios');
const yargs = require('yargs');
const async = require('async');
const treeify = require('treeify');
const npa = require('npm-package-arg');
const argv = yargs.argv;
const name = argv.name || argv._[0] || '';

let packageList = {};
let packageInfo = {};

let getPackageQueue = async.queue(function (task, callback) {
  getPackage(task, callback);
});

getPackageQueue.drain(function () {
  let data = formatPackageData(packageList, packageList);
  let output = { [name]: data[name] };
  let treeString = treeify.asTree(output, true);
  console.log(`${name}的依赖包如下`);
  console.log(treeString);
});

function getPackageJson(params) {
  let { packageName } = params;
  let url = registryUrl.replace(/\/$/, '') + '/' + packageName;
  return axios.get(url);
}

function getPackagegeDependencies(packageJson) {
  let packageInfo = {};
  let { dependencies, devDependencies } = packageJson;
  if (dependencies) {
    packageInfo = { ...packageInfo, ...dependencies };
  }
  return packageInfo;
}

async function getPackageInfo(name, callback) {
  let packageName = name && npa(name).escapedName;
  const result = await getPackageJson({ packageName }).then((e) => e.data);
  const packageVersion = result['dist-tags'] && result['dist-tags'].latest;
  const packageJson = result.versions[packageVersion];
  const packageDependencies = getPackagegeDependencies(packageJson);
  packageName = npa(name).name;
  const returnData = { packageName, packageVersion, packageDependencies };
  callback && callback(returnData);
  return returnData;
}

function mapPackage(params, callback) {
  let { packageName, packageDependencies } = params;
  if (packageName in packageList) {
    callback && callback();
    return;
  }
  packageList[packageName] = params;

  for (const key in packageDependencies) {
    if (Object.hasOwnProperty.call(packageDependencies, key)) {
      if (!(key in packageList)) {
        getPackageQueue.push({ name: key }, () => {});
      }
    }
  }
  callback && callback();
}

async function getPackage(params, callback) {
  let { name } = params;
  packageInfo = await getPackageInfo(name);
  mapPackage(packageInfo, callback);
}

if (name) {
  getPackageQueue.push({ name }, () => {});
} else {
  console.log('node inxex.js <package name>');
}

function formatPackageData(data, list) {
  const res = {};
  let params = {};
  if ('packageDependencies' in data) {
    params = data.packageDependencies;
  } else {
    params = data;
  }
  for (const key in params) {
    if (Object.prototype.hasOwnProperty.call(params, key)) {
      if (typeof params[key] == 'object') {
        res[key] = formatPackageData(list[key].packageDependencies, list);
      } else {
        res[key + '@' + params[key]] = formatPackageData(
          list[key].packageDependencies,
          list
        );
      }
    }
  }
  return res;
}

module.exports = {
  getPackage,
};

思路

  1. yargs 获取命令行参数
  2. 递归获取每个依赖包信息,放到类数组对象中。
  3. 每个包的信息是从 https://registry.npmjs.org/react 中拿到的,是个 Json,其中包含了详细的包信息,包的版本号这些。
  4. 开始是一个一个去获取,但是发现这样用时很长,效率很低,调用了 async 组件中的一个队列库,并发的获取包信息。
  5. 用简单的算法处理树结构。
  6. 最后用 treeify 将 json 输出成 tree 结构。

后记

代码其实可以再次优化,但是时间不多也没有注释,暂时这样写。


Previous Post
Jenkins 自动化脚本问题及解决方法
Next Post
Linux 查看 PATH 环境变量