欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

yarn?命令死循環(huán)問題分析解決

 更新時間:2023年01月13日 08:32:20   作者:4Ark  
這篇文章主要為大家介紹了yarn?命令死循環(huán)問題分析解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

前言

最近有個想法,希望在一個 yarn workspace 項目中實現(xiàn)任意一個子包中安裝依賴時,都執(zhí)行一些類似于初始化、同步配置的動作。

然而在操作過程中遇到了一個關(guān)于 yarn --cwd 有趣的問題,特地記錄下來,希望能對后來者有所幫助。

遇到什么問題呢

先交代一下我們項目的基本情況,它是一個通過 yarn workspace 管理的 monorepo 項目,使用的是 yarn v1.22.11 版本,目錄結(jié)構(gòu)大致如下:

monorepo
├── package.json
├── app-a
│   └── package.json
├── app-b
│   └── package.json
└── config
    └── package.json

其中 app-aapp-b 都使用了 config 這個共享包:

"dependencies": {
  "@monorepo/config": "../config",
}

我們需要在根目錄的 package.json 中的 preinstall 鉤子做一些初始化操作:

"scripts": {
  "preinstall": "./bin/init.sh",
}

此時我們在根目錄執(zhí)行 yarn 或者 yarn add <pkg-name>,都會觸發(fā) preinstall 這個鉤子,但在 app-a 中執(zhí)行 yarn是不會觸發(fā)根目錄的 preinstall 鉤子的。

因此,我們需要分別在每個子包上都加上這行,也即在每個子包安裝依賴時都執(zhí)行一下根目錄的 preinstall 命令:

"scripts": {
  "preinstall": "yarn --cwd ../ preinstall",
}

于是,奇怪的事情就發(fā)生了,當(dāng)我在 app-a 中執(zhí)行 yarn 的時候,它停留在安裝 @monorepo/config 的階段,同時我的電腦明顯變得卡頓,于是打開 htop 一看,好家伙,滿屏都是:

4ark   40987  26.3  0.5 409250368  78624   ??  R  8:36下午   0:00.09 /usr/local/bin/node /usr/local/bin/yarn --cwd ../ preinstall

CPU 占用率直接達到 100%,嚇得我趕緊 kill 掉這些進程:

ps aux | grep preinstall | awk '{print $2}' | xargs kill -9

分析原因

驚嚇過后,來分析一下原因,很顯然這段命令陷入了死循環(huán),導(dǎo)致越來越多進程,于是嘗試在每個子包中都手動執(zhí)行一遍 yarn --cwd ../ preinstall 后,發(fā)現(xiàn)一切正常,那問題出在哪呢?

于是我再執(zhí)行了一遍 yarn,并且用以下命令將進程信息復(fù)制出來,以便分析:

ps -ef | pbcopy

隨后驗證我剛剛的猜測,的確是這個命令在不斷觸發(fā)自己,導(dǎo)致死循環(huán):

UID   PID  PPID   C STIME   TTY     TIME CMD
501 50399 50379   0  8:50下午 ??   0:00.10 /usr/local/bin/node /usr/local/bin/yarn --cwd ../ preinstall
501 50400 50399   0  8:50下午 ??   0:00.11 /usr/local/bin/node /usr/local/bin/yarn --cwd ../ preinstall
501 50401 50400   0  8:50下午 ??   0:00.11 /usr/local/bin/node /usr/local/bin/yarn --cwd ../ preinstall
501 50402 50401   0  8:50下午 ??   0:00.12 /usr/local/bin/node /usr/local/bin/yarn --cwd ../ preinstall

由于三個分包執(zhí)行的命令都一樣,不清楚是不是由于某個分包引起,于是修改一下命令以便區(qū)分:

"scripts": {
  "preinstall": "echo app-a && yarn --cwd ../ preinstall",
}

隨后發(fā)現(xiàn)問題是出現(xiàn)在 config 這個子包,于是我把這個子包的 preinstall 命令去掉,果然沒有這個問題了,非常奇怪。

難道是 --cwd ../ 這個路徑有問題?驗證一下,把命令改成這樣:

"scripts": {
  "preinstall": "pwd && yarn --cwd ../ preinstall",
}

發(fā)現(xiàn) pwd 輸出是這樣子的:

/4ark/projects/monorepo/app-a/node_modules/@monorepo/config

從這里的輸出我們發(fā)現(xiàn)了兩個問題,第一個問題是:

  • yarn workspace 共享包的 preinstall 被執(zhí)行的時候,其實已經(jīng)被拷貝到 app-anode_modules 中,而不是在當(dāng)前目錄,因此 --cwd ../ 并不指向項目根目錄。

這一點比較好理解,畢竟 config 作為一個依賴包,確實應(yīng)該被拷貝到應(yīng)用的 node_modules 。

而第二個問題就不太理解了,為什么明明設(shè)置了 --cwd ../,卻依然在當(dāng)前目錄執(zhí)行呢?按照預(yù)期 cwd 的指向應(yīng)該是:

/4ark/projects/monorepo/app-a/node_modules/@monorepo

難道是我對 cwd 參數(shù)的理解有偏差?看一下 yarn 的文檔中對 cwd 描述:

Specifies a current working directory, instead of the default ./. Use this flag to perform an operation in a working directory that is not the current one.

This can make scripts nicer by avoiding the need to cd into a folder and then cd back out.

從文檔的描述來看,cwd 的作用不就是代替 cd 嗎,但現(xiàn)在的結(jié)果看來 yarn --cwd ../ preinstall 并不等價于 cd ../ && yarn preinstall 。

這就不得不讓人疑惑 cwd 的定位方式了,在網(wǎng)上搜尋一番沒找到相關(guān)的討論,那只能自己動手豐衣足食,直接從 yarn 源碼中尋找答案。

分析源碼

前面我們說到,我們使用的是 yarn v1.22.11,在 yarn 的 GitHub 倉庫中發(fā)現(xiàn) v1 版本的最新版本停留在 v1.23.0-0,那我們就從這個版本的源碼來進行分析,首先克隆代碼到本地:

git clone --depth=1 https://github.com/yarnpkg/yarn

然后安裝依賴并運行起來:

yarn && yarn watch

這時候它就會自動監(jiān)聽代碼修改然后重新編譯,我們查看 package.json 發(fā)現(xiàn) yarn 的 bin 主要是調(diào)用 ./bin/yarn.js:

"bin": {
  "yarn": "./bin/yarn.js",
  "yarnpkg": "./bin/yarn.js"
},

也就是我們直接執(zhí)行 bin/yarn.js 的效果就如同執(zhí)行 yarn,試一下查看版本:

> /Users/4ark/projects/yarn/bin/yarn -v
1.23.0-0

PS:當(dāng)然你也可以在項目目錄下使用 npm link 把它掛載到本地中。

接下就是一番調(diào)試,終于定位到可以回答我們疑問的代碼,在這里

function findProjectRoot(base: string): string {
  let prev = null;
  let dir = base;
  do {
    if (fs.existsSync(path.join(dir, constants.NODE_PACKAGE_JSON))) {
      return dir;
    }
    prev = dir;
    dir = path.dirname(dir);
  } while (dir !== prev);
  return base;
}
const cwd = command.shouldRunInCurrentCwd ? commander.cwd : findProjectRoot(commander.cwd);

可以看到 cwd 的定位方式是從當(dāng)前目錄尋找是否存在 package.json,若存在,則返回此目錄,否則將目錄經(jīng)過 path.dirname 處理一遍,繼續(xù)尋找,直到尋找到最外層。

那么這里最關(guān)鍵的是 path.dirname 的返回值,我們先看一下文檔對于它的描述:

The path.dirname() method returns the directory name of a path, similar to the Unix dirname command. Trailing directory separators are ignored,

就是返回一個路徑中的目錄部分,作用與 unix 下的 dirname 命令一致,通常是這么使用的:

> dirname /4ark/app/index.js
/4ark/app
> dirname /4ark/app/packages/index.js
/4ark/app/packages

是不是會膚淺地認(rèn)為它的作用就是返回一個路徑的上一級目錄?如果傳入的是一個絕對路徑,確實可以這么膚淺地認(rèn)為,然而當(dāng)傳入的是一個相對路徑時,情況就不一樣了:

> dirname ../app/index.js
../app
> dirname ../../
../
> dirname ../

問: 會返回什么呢?

答案是:.,也就是當(dāng)前目錄。

那這里就能回答我們之前的問題,為什么在 node_module/@monorepo/config 中使用 yarn --cwd ../ preinstall 卻在當(dāng)前目錄執(zhí)行,因為它的上一級 node_modules/@monorepo 不存在 package.json,所以經(jīng)過 dirname ../ 處理后 cwd 的指向就是當(dāng)前目錄。

如果對 node.js 中 path.dirname 的實現(xiàn)方式感興趣,可以看這里 path.js#L538-L554

解決方案

摸清楚原因后,那解決這個問題也不是難事,只要我們把相對路徑改成絕對路徑,是不是就能解決這個問題了?

思考一下,其實 yarn --cwd ../ preinstall,把 ../ 改成絕對路徑行不行呢?比如在本文的場景,../ 其實就是項目的根目錄,那我們完全可以通過別的方式獲取到項目的根目錄,比如 在 git 中:

git rev-parse --show-toplevel

所以,我們把命令改成這樣,問題就迎刃而解了:

- yarn --cwd ../ preinstall
+ yarn --cwd $(git rev-parse --show-toplevel) preinstall

那就不得不提一下,其實在 yarn v2 中新增了一個 --top-level 屬性,它的作用剛好就是為了解決這個問題。

結(jié)語

其實我們再回過頭來想,在本文的例子中,根本不需要在 config 目錄中添加 preinstall 這個鉤子,因為它作為共享包,每次修改都必然要在其它使用這個包的地方,重新安裝一次,所以只要確保這些地方會執(zhí)行 preinstall 就可以了,那也就意味著不會出現(xiàn)本文遇到的問題。

不過,多踩坑也不是壞事,只要搞清楚背后的原因,問題也就不是問題。

以上就是yarn 命令死循環(huán)問題分析解決的詳細(xì)內(nèi)容,更多關(guān)于yarn 命令死循環(huán)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 切換Node.js鏡像源的方法詳解

    切換Node.js鏡像源的方法詳解

    Node.js 是一個非常流行的 JavaScript 運行環(huán)境,擁有許多強大的功能和庫,使得它在 Web 開發(fā)、服務(wù)器端編程等領(lǐng)域中廣泛使用,在本文中,我們將介紹如何切換 Node.js 的鏡像源,并提供一些思考和難點,幫助讀者更好地理解這個問題,需要的朋友可以參考下
    2024-01-01
  • node.js結(jié)合webSocket實現(xiàn)聊天室

    node.js結(jié)合webSocket實現(xiàn)聊天室

    于Node.js和WebSocket的聊天室,主要包括前端頁面,主要是用戶操作的頁面,還包括后臺數(shù)據(jù)通信以及邏輯處理,具有一定的參考價值,感興趣的可以了解一下
    2023-08-08
  • nodejs實現(xiàn)黑名單中間件設(shè)計

    nodejs實現(xiàn)黑名單中間件設(shè)計

    最近寫的項目中涉及到防止灌水的功能,于是設(shè)計了黑名單中間件,跟大家分享一下,同時也希望大家有好的建議能夠拍磚.
    2014-06-06
  • 使用 Node.js 模擬滑動拼圖驗證碼操作的示例代碼

    使用 Node.js 模擬滑動拼圖驗證碼操作的示例代碼

    本篇文章主要介紹了使用 Node.js 模擬滑動驗證碼操作的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • npm、pnpm、yarn之間的區(qū)別小結(jié)

    npm、pnpm、yarn之間的區(qū)別小結(jié)

    平時在項目開發(fā)中,經(jīng)常用到npm、pnpm、yarn這些來安裝包,本文主要介紹了npm、pnpm、yarn之間的區(qū)別小結(jié),具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • Nodejs libuv運行原理詳解

    Nodejs libuv運行原理詳解

    在本篇文章里小編給大家整理的是關(guān)于Nodejs libuv運行原理以及相關(guān)知識點,有需要的朋友們可以學(xué)習(xí)下。
    2019-08-08
  • Node.js zlib模塊之屬性、方法及使用示例詳解

    Node.js zlib模塊之屬性、方法及使用示例詳解

    zlib模塊是Node.js中用于數(shù)據(jù)壓縮和解壓縮的強大工具,它提供了多種壓縮算法和靈活的使用方式,包括Deflate、Gzip等,廣泛用于網(wǎng)絡(luò)傳輸和文件存儲中以減少數(shù)據(jù)大小,提高性能,本文給大家介紹Node.js zlib模塊屬性、方法及使用示例,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • 詳解如何使用Node.js編寫命令工具——以vue-cli為例

    詳解如何使用Node.js編寫命令工具——以vue-cli為例

    本篇文章主要介紹了如何使用Node.js編寫命令工具——以vue-cli為例,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • 詳解Nodejs 通過 fs.createWriteStream 保存文件

    詳解Nodejs 通過 fs.createWriteStream 保存文件

    本篇文章主要介紹了Nodejs 通過 fs.createWriteStream 保存文件,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-10-10
  • Node.js基礎(chǔ)模塊babel使用詳解

    Node.js基礎(chǔ)模塊babel使用詳解

    這篇文章主要介紹了Node.js基礎(chǔ)模塊babel使用詳解,babel是由一系列的組件構(gòu)成,所以我們在開發(fā)過程中,要從自行配置組件來開始使用babel的功能
    2022-06-06

最新評論