最近工作遇到个问题,一开始项目是在外网运行的,后来需要导入内网运行,我一开始是把node_modules整个目录拷贝过去了,结果运行不起来,可能因为项目是pnpm的,直接从mac拷贝到windows似乎是运行不起来。然后尝试自己下载,缺了好多包,也启动不了运行不起来,最后只好开始了我的导包之旅。
这是一个悲伤的故事,前前后后和对应的人员来回导包4、5次,历时几周才搞完,因为项目不着急,所以就没有急着去搞,真是尴尬。下面就是我的导包之旅:
第一次:我是直接npm install
来安装项目,遇到不能安装的话,就把这个包记录下来,而由于项目是pnpm的,并且有pnpm-lock.yaml文件,这样包的版本都是严格模式,即使内网有某个包的版本,但是只要不是同一版本就不算数。
最开始写了n个包,给了负责包的人一份,好,开始等待,一两天后,对方说上传上去了,好,我去install,尼玛,还是有404的包。
第二次:开始把所有的package.json给他,让他去安装,但由于项目是menorepo的项目,所以给了至少8个package.json。给了之后,继续等待,这次等待有点长。大概几天后,我问他才给我说好了。
然而,我去install后还是有没装上的,不知道是不是给的太多了?这次我把pnpm-lock.yaml删除掉了,不然总是锁固定的版本,如果没有这个文件的话,^x.x.x的版本号还是会根据仓库有的版本去下载的,不会下不到准确的版本就直接报错。后来我在想为什么还是会有缺的?是我给错了还是他们的程序有bug?
第三次,对方开始有点不耐烦了,说让我自己用npm pack
打包自己缺少的包给他,他直接上传,这样才知道原来他们是这样打包的。然后我就把缺少的包,全部用npm pack命令打包了下,给他,这次传的挺快,结果。。。还是有缺的,原来是因为这个命令打的包是不包含所打包的依赖的,所以是包的依赖又缺少了。
所以我问他要脚本,没给,说是让自己写一个,行吧,那就自己写吧。
核心逻辑是:安装一个包的时候用npm view ${packageName}@${version} dependencies --json
命令获取它的依赖,然后递归安装,但是发现出现了死循环,又fix了一下。
后来想了下是不是直接用pnpm-lock.yaml
就好了,里面有packages字段,就包含了所有的依赖以及版本号了,所以可以根据这个值把所有的包用npm pack进行打包,这样就可以了。
第四次:把缺少的文件的tgz都打包出来,给了对方,这下好了,终于好了。
整个故事,很纠结,持续时间也很长,因为不是特别着急,所以就慢慢等着,也没仔细去想,但后来到之后的时候我就发现,这其实是不是pnpm的离线安装方案?所以这个问题的本质是如何从本地安装包。
所以一顿搜查,发现一般的思路都是:
- 先在有网的环境的环境下把包安装成功
- 将包打成tgz格式(npm pack),导入到内网,
npm install xxx.tgz
包安装,但是这样安装,其实也没那么简单,因为有n多个包, 如果包太多,它有一些依赖的包没有对应的tgz包,它会去请求网络安装,但是因为离线所以也安装不成功,所以这样装要保证所有的tgz都在。
如果是npm的项目,可以尝试直接node_modules打zip包,有时候是可以的,而且一般同样的系统的话问题大不,但可能就是存在linux->windows,mac->windows这样,这种可能就出问题。
如果是pnpm项目,大概率是不能直接打zip包启动的,还是得用tgz的方式,因为这种方式是跟系统无关的。
npm pack
npm pack
的使用可以参考:npm pack命令,就是把包打成对应的tgz包。使用场景:
- 打包某个单独的包的版本:
npm pack xxx@1.2.2
。这样就可以把对应的包的tgz版本下载下来。 - 如果你是开发某个npm包,那在当前开发包的目录下执行
npm pack
会把整个包打包下来,也会包含当前目录的node_modules
发现这个命令还是挺有用的,例如下面两个场景:
- 这种情况离线安装包,
- 当你开发一个npm包,需要发布给别人的时候,可以用这个命令打包给对方安装测试用,那就不用发测试包啦~
那npm pack链接可以从哪看呢?其实在package-lock.json里就有:
这个resolved就是下载链接,可以直接拷贝下来直接就把tgz包下载了,所以如果写自动化程序,是可以直接根据这个链接下载的。
可以使用的方案如下:
方案一:npm pack打包所有包的tgz
- 在一个有网的机器上安装所有的包,然后会生成pnpm-lock.yaml文件,再根据文件中packages字段下载所有的包的tgz,搜了一下发现已经有现成的开源工具了:
包 | 效果 |
---|---|
get-npm-tgz | package-lock下载、或者单独的包,提供了命令行 |
batch-download-tgz | package-lock下载、或者单独的包,这个没有package-lock的命令行 |
node-tgz-downloader | 这个执行失败 |
这几个都可以实现这个功能。实现从外网往内网导包的工具,还是很方便的。试了一下第一、第二个都可以成功,包含直接给一个package-lock来下载,或者给一个单独的包下载,都可以成功。
第一个还提供了yarn.lock和pnpm-lock.yaml的方式,把这个格式转换成package-lock的格式,就可以使用这个工具了,而转换当然也是使用工具:
- synp:将
yarn.lock
转化为package-lock.json
- pnpm-lock-to-npm-lock:将
pnpm-lock.yaml
转化为package-lock.json
- 下载完所有的tgz包之后,可以把包放到要安装的目录下,
pnpm install tgz/*
, 但是在实际操作中遇到了问题,因为有tgz没有下载成功,所以当这样安装的时候,如果遇到没有的tgz包就会报错,也安装不成功。
方案二:store离线安装
请教了chatgpt后,给了一种方案,看起来还可以,所以尝试了下,最后成功啦!方案步骤:
在联网的机器上
-
安装所有依赖:确保所有依赖已经下载并存储在
pnpm
的本地缓存中。1pnpm install
-
找到 pnpm 的缓存目录:
1pnpm store path
例如,输出可能是
/home/user/.pnpm-store
. -
压缩缓存目录:
使用
tar
命令压缩缓存目录。1tar -czvf cache.tar.gz -C /home/user .pnpm-store
这将创建一个名为
cache.tar.gz
的压缩文件,包含.pnpm-store
目录。这里它还给了另外一种生成tar文件的方式:
pnpm store export --file /path/to/cache.tar.gz
, 但是这种方式我并没有成功,报file参数错误。。所以就只能用本地打tar包的形式了。 -
复制
package.json
和pnpm-lock.yaml
文件:确保将项目根目录下的
package.json
和pnpm-lock.yaml
文件复制到离线机器上。
在离线的机器上
-
传输缓存文件和项目文件:
将
cache.tar.gz
文件、package.json
和pnpm-lock.yaml
文件传输到离线机器。 -
解压缓存文件:
在离线机器上解压缓存文件。
1tar -xzvf cache.tar.gz -C /path/to/extract
确保解压后的
.pnpm-store
目录在指定的路径,例如/path/to/extract/.pnpm-store
. -
配置 pnpm 使用本地缓存:
编辑或创建项目根目录下的
.npmrc
文件,指向本地缓存目录。1store-dir=/path/to/extract/.pnpm-store
-
安装依赖:
运行
pnpm install
命令并指定--offline
选项。1pnpm install --offline
这将根据
package.json
和pnpm-lock.yaml
文件在离线环境中安装所有依赖。
示例过程
假设在联网机器上的项目目录结构如下:
1my-project/
2 ├── package.json
3 ├── pnpm-lock.yaml
4 └── ...
并且本地缓存目录为 /home/user/.pnpm-store
.
在联网机器上:
-
安装所有依赖:
1cd my-project 2pnpm install
-
找到 pnpm 缓存目录:
1pnpm store path
假设输出为
/home/user/.pnpm-store
. -
压缩缓存目录:
1tar -czvf cache.tar.gz -C /home/user .pnpm-store
-
复制
package.json
和pnpm-lock.yaml
文件到离线机器。
在离线机器上:
-
传输
cache.tar.gz
文件、package.json
和pnpm-lock.yaml
文件到离线机器。 -
解压缓存文件:
1tar -xzvf cache.tar.gz -C /path/to/extract
-
配置 pnpm 使用本地缓存:
在项目根目录下创建或编辑
.npmrc
文件:1store-dir=/path/to/extract/.pnpm-store
-
安装依赖:
1cd my-project 2pnpm install --offline
我按照上面过程试了一下,之前试是没有成功的,!!所以pnpm的项目也是可以参考这种方式来进行项目到内网的迁移。
npm的离线安装第一个思路一样:根据package-lock.json来打包所有的npm包,然后通过tgz的包的方式安装。
安装的话,把所有下载的tgz都放到一个文件夹,然后通过npm install ./pack/*.tgz
,把文件夹下的内容一起安装,按照这种安装后一般会遇到两个问题:
- package.json的连接都被改成file:// 开头了,所以安装之前要把package.json先保存起来,等安装完了再恢复。
- 有依赖的tgz包没有,会导致安装失败,要保证tgz的全才能安装成功