lmk123 / blog

个人技术博客,博文写在 Issues 里。
https://github.com/lmk123/blog/issues
623 stars 35 forks source link

npm workspaces 踩坑记录 #115

Open lmk123 opened 1 year ago

lmk123 commented 1 year ago

先放结论:不要用 NPM,用 Yarn v1(v2 没试过)。


最近准备使用 Workspaces 的方式组织代码,为此,我打算创建一个新的项目。

在这之前,我的包管理器一直使用的是 yarn v1,但考虑到我不打算切换到 yarn v2,所以我决定重新开始用 npm,没想到之后花了很长时间才发现,还是 yarn 香。

我的项目结构大致是这样:

├─ packages
│  ├─ PackageA    <-- 从划词翻译代码里抽离出来的小模块
│  │   └─ package.json
│  └─ PackageB 
│      └─ package.json
│  └─ hcfy        <-- 划词翻译的代码
│      └─ package.json
├─ node_modules
└─ package.json

然后我发现了一个问题,即 npm outdated 会打印出每个 package 里的信息,即使依赖的包名是一样的。举例来说,假设 PackageA 和 PackageB 都依赖了 typescript v3,但 typescript 最新版是 v4,那么 npm outdated 会分别为这两个包打印出过期信息,会显示两条。

于是,我将所有 packages 里的 devDependencies 剪切然后粘贴到了根目录的 package.json 里。这样一来,npm outdated 就只会将根目录下的 package.json 里的过期信息打印出来。

目前为止没有什么问题,但是当我将划词翻译的代码库迁移进来之后,问题就来了。

划词翻译使用的是 react 18,但划词翻译的 dependencies 里有一个包的 peerDependencies 需要的是 react 17,然后当我用 npm i 安装依赖的时候,npm 报错了,要求我用 --force 或者 --legacy-peer-deps 来运行 npm i

现在回想起来,用 yarn 的时候就没有报这个错,如果我当时意识到这个问题,也就不会浪费后面这么多时间了。

我先是用了 --force,但这导致我的项目里出现了两个 react,项目根目录 node_modules 下是 17,但 packages/hcfy/node_modules 下的是 18,但是同一个项目里当然只能有一个版本的 react,所以这是不行的。

两个版本的 react 会导致很多问题:typescript 报错、测试工具报错、项目的组件运行的不是同一个 react 的 render 所以渲染也可能报错等各种问题。

然后我又用了 npm i --legacy-peer-deps 后安装成功了,然后测试了一遍 testbuild 之类的命令,都能正常运行,但我发现了一些问题:

我以为这是因为我把 devDependencies 都移动到了根目录下的 package.json 里导致的,于是又花时间把根目录下的 devDependencies 全部重新移回了每个 packages 的 package.json 里。现在,不带 --legacy-peer-deps 也可以正常运行 npm i了,但 outdated 没信息的问题仍没解决,而且又出现了不同版本的 react 的问题。

目前为止,所有 packages 里只有划词翻译会依赖 react,但 workspaces 仍然出现了两个版本的 react。我尝试删了 node_modules 和 package-lock.json 重新安装,也尝试用 package.json 的 overrides 配置强制让 npm 统一使用 react 18,但都没用。

直到现在,我才突然想起来,之前使用 yarn v1 的时候就没有出现两个版本的 react 的问题,难道是 npm 有问题?

然后我用 yarn v1 安装了依赖,一切问题都没了:

看来我还是离不开 yarn 了 :joy:

题外话:不能切换到 yarn v2 有点可惜

yarn v2 的改造于我而言实在是太过于大刀阔斧了。贴一下我在 yarn 的一个 issue 下看到的让我感触很深的评论:

https://github.com/yarnpkg/berry/issues/749#issuecomment-1219497778

yarn used to be a drop-in replacement of npm with some nice extra features. Since version 2.0 it is not that thing anymore.