使用npm-link命令帮助开发npm包
修改于: 2023-08-27 16:32
此文章久未修订,请自行甄别内容准确性。
npm link 命令核心原理即使用 symlink 能力建立本地npm包目录的软链接, 本文将基于官网v8.x文档归纳其一些特性和用法.
本文内容基于 node@v16.x, npm@v8.x
npm link 做了什么
在本地npm包仓库下运行命令 npm link 后, 应该会看到这样的输出:
added 1 package in 495ms
或者这样的:
up to date in 744ms
内部其实是 建立了当前npm包目录到npm全局的symlink, 也就是 ln -s 命令做的事情.
那么npm全局目录在哪呢, 可以运行命令 ls -l $(npm root -g), 如果刚才npm link成功了, 应该能看到link的包被列出, 以及你此前全局安装过的其他npm包也在这里.
比如使用了nvm的情况下像这样:

npm ls -g也能看到link到全局的包
如果link的包名是基于某个命名空间的, 比如 @hello/world, 那列出的就是目录 @hello, 再进去就是 world 目录咯.
如果link到全局的包还包含了bin配置, 像这样:

则也能被link到全局bin下:

npm link pkg-utils 做了什么
在本地另一工程下(以pkg-host为例)执行 npm link pkg-utils, 会看到类似 changed [n] packages in [m]s 的输出, 就好像在host工程内安装了pkg-utils这个npm包一样.
但本地link默认不会改变package.json文件内容, 即如果package.json里之前有该包且版本号为^1.0.0, link本地包时并不会被修改为 file://../path/to/pkg-utils.
如果link了原本未安装的新包, 则
package-lock.json会被改变, 如果原来已经安装了, 则也不会改变
最终效果也就是pkg-utils包所在的本地目录被软链到了pkg-host工程的 node_modules中, 然后可以像使用常规npm i安装的包一样来使用:

使用 npm link 进行 “真包调试”
npm link比较适合的用途之一就是帮助开发npm包项目, 可以不实际发布包版本就做到”真包调试”, 但也有一些区别和要注意的点:
会link整个目录: 常规安装npm包时, 实际上安装到node_modules内的是npm pack的产物, 会应用.npmignore规则只安装真正被发布的子文件或目录, 而npm link终究只是个symlink, 实际上会link整个目录, 包括所有子文件和目录.
妥善取消link: 当认为开发完成, 并想要取消link时, 可以这么做:
- 在
pkg-host里重新运行npm i或npm i pkg-utils, 也就是重新装一次这个npm包, 这样做会把link的包挤掉, 安装回package.json内声明好的版本 - (不推荐) 在
pkg-host里npm unlink pkg-utils, 实测这同时会移除 package.json 里的包依赖(如果此前安装了)
以上两步只是从pkg-host工程里移除了link过来的pkg-utils, 实际上pkg-utils还被link在全局呢, 想要移除全局的link, 可以使用npm uninstall/rm -g命令, 就像正常移除一个全局安装的包一样:

使用ts开发的工程, link后使用的应该(should)是编译后的js模块, 而不是原始的ts模块, 按照一般的tsconfig配置, 会忽略node_modules内的ts模块, 而使用编译后的commonjs模块, 这就需要npm包工程在link后, 继续改动ts模块时, 还要记得重新进行tsc编译, 生成最新的js模块供宿主工程使用.
或者也可以专门配置宿主工程的tsconfig, 来包含node_modules中这个npm包的ts模块, 但更建议的方式是写一个脚本监听相关文件改动, 然后自动编译ts到js.
还要注意哪些?
- npm link 支持多个包: 在包1和包2各自
npm link后, 在包3里npm link 包1 包2即可同时 link 两个包 - 工程化项目如何判断某个包是link状态:
fs.lstatSync('包所在的node_modules下的目录').isSymbolicLink() - link 某个包后 publish: 这样做是可以的, 但需要把link的包列在
bundleDependencies里, 如果此前没安装这个包, 还需要执行npm install <dep> --package-lock-only, 然后在pkg-host里运行npm publish会将目前link的pkg-utils内容一起打包进npm pack产物里. 但个人不建议这么做, link应该只用在本地调试 - …
拓展阅读
什么是synlink? 一图流解释:

yarn和pnpm 都有自己的link实现方式, 要避免混用
