前言
不知道大家有没有经历过这么一个情景:升级某个依赖,然后项目死活都运行不起来,控制台的信息也找不到问题所在。最终在github经过一番交流,才发现是另外一个依赖被弃用了,而导致新版本不兼容了。那么问题来了,怎么可以知道依赖弃用的相关信息呢?
依赖弃用只有在 npm install 的时候提示,而在项目正常运行的时候是没有任何相关信息的(ps:使用 pnpm 的项目,在 pnpm-lock.yaml 中会有 deprecated 的提示)。那么就有可能会出现这样的情况:安装完依赖后,在未来的某个时间节点,作者将其状态改为弃用,不再维护。当然,项目还是可以正常运行的。但是,当你升级依赖的时候,就有可能出现以上提及的情景。在 npm 的issues中也可以找到大家对于 deprecated 提示的抱怨。
所以,可不可以开发一个包来随时检查依赖的弃用情况。
正文
npm-deprecated-check: 检查当前项目、全局或者指定依赖是否已弃用。
npm install -g npm-deprecated-check
首先,全局安装 npm-deprecated-check。ndc 是它的缩写命令。
ndc [args]
command:
ndc current Check the packages of the current project
ndc global Check global packages
ndc package <packageName> Check for specified package
ndc version Show version
ndc * Check the packages of the current project
option:
-a, --all
-d, --deep
-h, --help
从上述命令列表可以看出,npm-deprecated-check 的主要功能是检查当前项目、全局或者指定依赖。all 会打印所有依赖弃用情况,deep 会根据依赖树深度检查。其中,ndc global 支持 manager 选项,可以指定包管理器,默认为 npm,还可选 yarn 和 pnpm。ndc package 支持 range 选项,可以指定版本进行查询。
主要逻辑是获取当前的 registry,然后拼接包名请求获取并缓存依赖信息,接着根据 semver.maxSatisfying 得到依赖的安装版本,这样就知悉该版本依赖的弃用情况。
而对于当前项目,首先会查找 lockfile 获取 dependencies 和 devDependencies 的安装版本,不需要通过 semver.maxSatisfying 比较获得。如果存在多个 lockfile,则按照修改时间进行排序。如果项目中没有 lockfile,就直接拿 package.json 的 dependencies 和 devDependencies。
const npmLockPath = resolve('./package-lock.json');
const yarnLockPath = resolve('./yarn.lock');
const pnpmLockPath = resolve('./pnpm-lock.yaml');
function getDependenciesOfLockfile(packages: { [k: string]: VersionOrRange }) {
const npmLock = {
path: npmLockPath,
read: function() {
const { dependencies } = fs.readJsonSync(this.path);
const result: Record<string, VersionOrRange> = {};
for(const packageName in packages) {
dependencies[packageName] && (result[packageName] = { version: dependencies[packageName].version });
}
return result;
}
}, yarnLock = {
path: yarnLockPath,
read: function() {
const content = fs.readFileSync(this.path).toString('utf-8');
const json = lockfile.parse(content);
const result: Record<string, VersionOrRange> = {};
for(const packageName in packages) {
json.object[`${packageName}@${packages[packageName].range}`] && (result[packageName] = { version: json.object[`${packageName}@${packages[packageName].range}`].version });
}
return result;
}
}, pnpmLock = {
path: pnpmLockPath,
read: async function() {
const content = await readWantedLockfile(resolve(this.path, '..'), { ignoreIncompatible: false });
if(content && content.packages) {
const packageNames = Object.keys(packages);
const result: Record<string, VersionOrRange> = {};
for(const depPath in content.packages) {
const info = dp.parse(depPath);
packageNames.includes(info.name as string) && (result[info.name as string] = { version: info.version });
}
return result;
} else {
return {};
}
}
}
const result = [npmLock, yarnLock, pnpmLock]
.filter(ele => fs.existsSync(ele.path))
.sort((a, b) => fs.lstatSync(a.path).mtimeMs - fs.lstatSync(b.path).mtimeMs)
.reduce(async (total, current) => Object.assign(await total, await current.read()), {});
return Promise.resolve(result);
}
结语
目前,npm-deprecated-check已基本满足我的使用要求,可能存在部分还需要优化的地方,比如 deep 深度检查的时候,打印依赖树路径,这样显示依赖弃用情况更加清楚;还有对其他 url 的弃用查询,比如github url。
如果有同学对这个项目感兴趣,或者有更好的特性需求,欢迎提出或一起维护。
ps: 查询文档的时候,看到说 pnpm outdated 会列出 deprecated 的信息。但我尝试了一下并没有得到 deprecated 信息,如果有同学了解相关内容,麻烦评论区告知一声,感激不尽。