0%

简易的npm包依赖查看器

简易的 npm 包依赖查看器

Github: simple-npm-dependency-viewer

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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 结构。

后记

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