最近工作遇到个问题,一开始项目是在外网运行的,后来需要导入内网运行,我一开始是把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的离线安装方案?所以这个问题的本质是如何从本地安装包。

所以一顿搜查,发现一般的思路都是:

  1. 先在有网的环境的环境下把包安装成功
  2. 将包打成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

发现这个命令还是挺有用的,例如下面两个场景:

  1. 这种情况离线安装包,
  2. 当你开发一个npm包,需要发布给别人的时候,可以用这个命令打包给对方安装测试用,那就不用发测试包啦~

那npm pack链接可以从哪看呢?其实在package-lock.json里就有:

这个resolved就是下载链接,可以直接拷贝下来直接就把tgz包下载了,所以如果写自动化程序,是可以直接根据这个链接下载的。

可以使用的方案如下:

方案一:npm pack打包所有包的tgz

  1. 在一个有网的机器上安装所有的包,然后会生成pnpm-lock.yaml文件,再根据文件中packages字段下载所有的包的tgz,搜了一下发现已经有现成的开源工具了:
效果
get-npm-tgzpackage-lock下载、或者单独的包,提供了命令行
batch-download-tgzpackage-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
  1. 下载完所有的tgz包之后,可以把包放到要安装的目录下,pnpm install tgz/*, 但是在实际操作中遇到了问题,因为有tgz没有下载成功,所以当这样安装的时候,如果遇到没有的tgz包就会报错,也安装不成功。

方案二:store离线安装

请教了chatgpt后,给了一种方案,看起来还可以,所以尝试了下,最后成功啦!方案步骤:

在联网的机器上
  1. 安装所有依赖:确保所有依赖已经下载并存储在 pnpm 的本地缓存中。

     1pnpm install
    
  2. 找到 pnpm 的缓存目录

     1pnpm store path
    

    例如,输出可能是 /home/user/.pnpm-store.

  3. 压缩缓存目录

    使用 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包的形式了。

  4. 复制 package.jsonpnpm-lock.yaml 文件

    确保将项目根目录下的 package.jsonpnpm-lock.yaml 文件复制到离线机器上。

在离线的机器上
  1. 传输缓存文件和项目文件

    cache.tar.gz 文件、package.jsonpnpm-lock.yaml 文件传输到离线机器。

  2. 解压缓存文件

    在离线机器上解压缓存文件。

     1tar -xzvf cache.tar.gz -C /path/to/extract
    

    确保解压后的 .pnpm-store 目录在指定的路径,例如 /path/to/extract/.pnpm-store.

  3. 配置 pnpm 使用本地缓存

    编辑或创建项目根目录下的 .npmrc 文件,指向本地缓存目录。

     1store-dir=/path/to/extract/.pnpm-store
    
  4. 安装依赖

    运行 pnpm install 命令并指定 --offline 选项。

     1pnpm install --offline
    

    这将根据 package.jsonpnpm-lock.yaml 文件在离线环境中安装所有依赖。

示例过程

假设在联网机器上的项目目录结构如下:

 1my-project/
 2  ├── package.json
 3  ├── pnpm-lock.yaml
 4  └── ...

并且本地缓存目录为 /home/user/.pnpm-store.

在联网机器上:
  1. 安装所有依赖:

     1cd my-project
     2pnpm install
    
  2. 找到 pnpm 缓存目录:

     1pnpm store path
    

    假设输出为 /home/user/.pnpm-store.

  3. 压缩缓存目录:

     1tar -czvf cache.tar.gz -C /home/user .pnpm-store
    
  4. 复制 package.jsonpnpm-lock.yaml 文件到离线机器。

在离线机器上:
  1. 传输 cache.tar.gz 文件、package.jsonpnpm-lock.yaml 文件到离线机器。

  2. 解压缓存文件:

     1tar -xzvf cache.tar.gz -C /path/to/extract
    
  3. 配置 pnpm 使用本地缓存:

    在项目根目录下创建或编辑 .npmrc 文件:

     1store-dir=/path/to/extract/.pnpm-store
    
  4. 安装依赖:

     1cd my-project
     2pnpm install --offline
    

我按照上面过程试了一下,之前试是没有成功的,!!所以pnpm的项目也是可以参考这种方式来进行项目到内网的迁移。

npm的离线安装第一个思路一样:根据package-lock.json来打包所有的npm包,然后通过tgz的包的方式安装。

安装的话,把所有下载的tgz都放到一个文件夹,然后通过npm install ./pack/*.tgz,把文件夹下的内容一起安装,按照这种安装后一般会遇到两个问题:

  1. package.json的连接都被改成file:// 开头了,所以安装之前要把package.json先保存起来,等安装完了再恢复。

  1. 有依赖的tgz包没有,会导致安装失败,要保证tgz的全才能安装成功
个人笔记记录 2021 ~ 2025