从一次 yarn 命令死循环说起

从一次 yarn 命令死循环说起

💡 原文中文,约5000字,阅读约需12分钟。
📝

内容提要

在使用 Yarn workspace 管理的 monorepo 项目中,子包执行依赖安装时,根目录的 preinstall 钩子未被触发。分析发现,使用 `yarn --cwd ../ preinstall` 命令因路径问题导致死循环。最终,通过将相对路径改为绝对路径,并使用 `git rev-parse --show-toplevel` 获取项目根目录,解决了该问题。

🔎

延伸解读

问题根源:相对路径与 yarn 的 cwd 解析

文章指出,yarn v1 的 --cwd 参数并非简单等同于 cd。其内部通过 findProjectRoot 从指定目录向上查找 package.json,若未找到则用 path.dirname 处理路径。当传入相对路径如 ../ 时,path.dirname 可能返回当前目录,导致 --cwd ../ 失效。在共享包被拷贝到子包 node_modules 后,相对路径的基准目录改变,从而引发死循环。

解决方案:使用绝对路径与 git 命令

作者通过将相对路径改为绝对路径解决了问题。具体做法是使用 git rev-parse --show-toplevel 获取项目根目录的绝对路径,再传递给 --cwd。这样无论当前工作目录如何变化,yarn 都能正确找到项目根目录,避免因相对路径解析错误导致的死循环。该方法简单有效,适用于使用 git 管理的 monorepo 项目。

版本差异与最佳实践

文章提到,yarn v2 新增了 --top-level 属性,专门用于解决类似问题,说明官方已意识到该痛点。对于仍在使用 yarn v1 的项目,建议避免在共享包中定义 preinstall 钩子,因为共享包被安装到子包时,其钩子执行环境可能不符合预期。更好的做法是在使用共享包的子包中统一处理初始化逻辑,或使用绝对路径确保命令正确执行。

❓

Q&A

在 Yarn workspace 中,为什么子包的 preinstall 钩子没有被触发?

因为子包执行依赖安装时,根目录的 preinstall 钩子未被触发,导致需要在每个子包中单独添加该钩子。

使用 'yarn --cwd ../ preinstall' 命令时出现死循环的原因是什么?

因为该命令在执行时不断触发自己,导致进程数量激增,最终造成 CPU 占用率达到 100%。

如何解决 Yarn workspace 中的 preinstall 钩子问题?

通过将相对路径改为绝对路径,并使用 'git rev-parse --show-toplevel' 获取项目根目录来解决该问题。

Yarn v2 中新增的 --top-level 属性有什么作用?

该属性专门用于解决类似于 preinstall 钩子未触发的问题,简化了路径管理。

在 Yarn workspace 中,如何确保共享包的 preinstall 钩子正常执行?

只需确保在使用共享包的地方执行 preinstall 钩子,而不必在共享包自身中添加该钩子。

Yarn 的 cwd 参数是如何工作的?

cwd 参数指定当前工作目录,Yarn 会从该目录向上查找 package.json 文件,以确定项目根目录。

🏷️

标签

➡️

继续阅读