【开源项目】npm-deprecated-check: 检查当前项目、全局或者指定依赖是否已弃用

911 阅读3分钟

前言

不知道大家有没有经历过这么一个情景:升级某个依赖,然后项目死活都运行不起来,控制台的信息也找不到问题所在。最终在github经过一番交流,才发现是另外一个依赖被弃用了,而导致新版本不兼容了。那么问题来了,怎么可以知道依赖弃用的相关信息呢?

依赖弃用只有在 npm install 的时候提示,而在项目正常运行的时候是没有任何相关信息的(ps:使用 pnpm 的项目,在 pnpm-lock.yaml 中会有 deprecated 的提示)。那么就有可能会出现这样的情况:安装完依赖后,在未来的某个时间节点,作者将其状态改为弃用,不再维护。当然,项目还是可以正常运行的。但是,当你升级依赖的时候,就有可能出现以上提及的情景。在 npm 的issues中也可以找到大家对于 deprecated 提示的抱怨。

所以,可不可以开发一个包来随时检查依赖的弃用情况。

正文

npm-deprecated-check: 检查当前项目、全局或者指定依赖是否已弃用。

npm install -g npm-deprecated-check

首先,全局安装 npm-deprecated-checkndc 是它的缩写命令。

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,还可选 yarnpnpmndc package 支持 range 选项,可以指定版本进行查询。

主要逻辑是获取当前的 registry,然后拼接包名请求获取并缓存依赖信息,接着根据 semver.maxSatisfying 得到依赖的安装版本,这样就知悉该版本依赖的弃用情况。

而对于当前项目,首先会查找 lockfile 获取 dependenciesdevDependencies 的安装版本,不需要通过 semver.maxSatisfying 比较获得。如果存在多个 lockfile,则按照修改时间进行排序。如果项目中没有 lockfile,就直接拿 package.jsondependenciesdevDependencies

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 信息,如果有同学了解相关内容,麻烦评论区告知一声,感激不尽。