少女祈禱中...
Loading...
图片加载过慢?试试 从源站加载资源 X

ccloli

通过 Verdaccio 自建 npm 仓库测试 npm 包

在开发会被其他项目依赖的 npm 包时,有时需要在其他项目下安装测试。为了方便测试,我们通常会将包本地安装或者发布到 npm 仓库下再安装。但是实际上前者会有很多 npm 处理依赖的坑,而后者即便可以用 canary 通道,某些精神洁癖的同学还是会不太乐意。

那么我们可以换种思路,为何我们不在本地自建一个 npm registry?这样可以随意发布测试,不会踩到本地安装 npm 的坑,还不会影响到线上包。Verdaccio 就是一个随开随用甚至无需配置的 npm 仓库,用户不需要自己搭建数据库,只需要一行指令即可在本地启动一个 npm 仓库。

太长不看版

本地安装的坑

或许你会好奇,npm 是支持本地安装包的,原理就是在 node_modules 下添加一个 symlink,指向对应的目录:

同时刷新 package.json 的依赖:

这样安装在一般情况下是没问题的,node 在运行时能找到对应的包。问题在于这样的操作只是添加一个 symlink 而已,而这个包里的依赖不一定会同步复制到当前的 node_modules 下。如果这个包里的依赖只是在这个包用上的话,一般不需要考虑这个问题。但是如果这个包下的依赖同时是项目下的其他依赖需要的依赖,那么就会遇到找不到依赖的问题。

具体到实际的例子,我们正在维护一份包含 ESLint 等 lint 规则的包,这个包会包含 lint 规则所需的所有依赖(如 eslint-plugin-reacteslint-plugin-import 等),这样用户不需要手动安装这些依赖。然而由于本地安装不会处理本地包所使用的依赖,也就是不会复制到 node_modules 目录下,而这些依赖是被项目的 ESLint 等依赖使用的,最终会导致 ESLint 等会找不到对应的插件。

这种情况的解决方法只能是手动将对应的依赖复制到 node_modules 下,但是一个个手动复制未免也过于繁琐了。所幸已经有人开发了 npm-install-offline 这样的包来解决这种问题:

它的原理就是将项目所需要的上游依赖包复制到当前项目的 node_modules 下,这样项目在运行时就能找到这些依赖了。但是这个包在应对 lerna 这类 monorepo 时会有意想不到的 bug,lerna 在 bootstrap 后会将关联的包在 node_modules 下用 symlink 连接起来,而这个包无法处理 symlink,导致复制对应的依赖时会卡住。虽然这种情况下 npm-install-offline 还可以指定 --symlink 参数也使用 symlink 连接:

这种情况下依赖就能顺利链接到 node_modules 下了,一般也能解决不少问题。然而对于之前提到的实际中的例子,包里的依赖其实不仅仅是被项目依赖如 ESLint 调用的,同时自身也需要调用项目中的依赖,例如 eslint-plugin-import 就需要 require('eslint')。然而 symlink 的依赖在寻找上游依赖时可能就犯了难,会抛出 Cannot find module 'eslint' 的错误,盲猜是依赖执行时并不是 symlink 后的项目所在的 node_modules 下寻找,而是在原来的目录下寻找,最终导致找不到依赖。

总之 npm 处理本地包的依赖大概就是一坨💩,如果你的包也是这样复杂,建议尽早放弃本地安装的思路,否则可能还会有更多的坑。

自建 npm 仓库

既然本地安装目录不靠谱,那么 npm pack 呢?不好意思,npm pack 并不会打包 node_modules,安装后依赖还是从远程仓库上拉的,对于 monorepo 并不友好。所以此时更好的方法应该是直接发包并安装测试,可是这样有可能会影响已经发布的包版本。那么我们何不自己搭建一个 npm 仓库?

自建 npm 仓库是可行的,最常见的方案就是 cnpm,而自己搭建 cnpm 则还需要准备数据库等环境依赖。那么有更快更简单的方法吗?当然有,比如这次要用上的 Verdaccio,它是一个开源轻量免配置的 npm 私服,用户不需要准备本地的使用环境,只需要安装即可使用:

启动后 Verdaccio 会默认运行在 127.0.0.1:4873 上,你可以打开浏览器确认:

此时我们只需要将 npm 的 registry 指向本地,就可以开始操作了。

首先我们需要登录本地仓库的 registry,用户名密码其实可以随意:

然后我们就可以将包发布到本地的仓库下了:

如果不出意外,你可以在刚才的 web 页面上看到刚刚发布的包了。

在其他项目下测试时,只需要在安装时指定 registry 即可:

这样你就在本地搭建了一个运行在本地的 npm 仓库,现在你可以随意折腾你的包并随意发布了,本地发布到 Verdaccio 的包是不会发布到上游的。

进阶配置

Verdaccio 的配置文件默认放在用户目录下,一般是取 $XDG_DATA_HOME 环境变量,Windows 是取 %APPDATA% 环境变量。此外在启动 Verdaccio 时也会输出配置文件的位置,个人实践的配置文件位置如下:

开放公网访问

Verdaccio 默认是绑定在 127.0.0.1:4873 下的,如果需要绑定到公网 IP 可以改为监听 0.0.0.0,此外也可以修改端口。

自动代理上游的包

如果你是采用全局配置 npm registry 的话,如果使用了公司内部的包,那么本地安装上游的包时可能会遇到找不到依赖的问题,因为 Verdaccio 虽然默认会代理上游 npmjs 的包,但是私有仓库是不支持的。此时你需要在安装原始项目依赖时用默认的 registry,然后安装测试的依赖时指定 registry。其实我们可以通过配置上行链路让 Verdaccio 自动查找上游的包,这样就只需要将 registry 指向本地即可。

Verdaccio 的默认上行链路配置如下:

我们可以在 uplinks 里指定上行链路,然后在 packages 里指定对应的 scope 走对应的链路。例如我们可以将公司的私有 scope @foo 指向公司的 cnpm,然后默认的依赖走淘宝的中转,配置可以写成这样:

然后我们可以在需要测试的项目下删除原有的 .npmrc,然后安装依赖时直接使用本地的 Verdaccio,或者直接将 .npmrc 指向本地的 Verdaccio,它会帮我们代理好上游的包,不再需要来回切换 registry 了: