<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Monorepo 的這些坑,我們幫你踩過了!

          共 18691字,需瀏覽 38分鐘

           ·

          2021-06-13 21:37

          前言

          筆者目前所在團隊是使用 Monorepo 的方式管理所有的業(yè)務(wù)項目,而隨著項目的增多,穩(wěn)定性以及開發(fā)體驗受到挑戰(zhàn),諸多問題開始暴露,可以明顯感受到現(xiàn)有的 Monorepo 架構(gòu)已經(jīng)不足以支撐日漸龐大的業(yè)務(wù)項目。

          現(xiàn)有的 Monorepo 是基于 yarn workspace 實現(xiàn),通過 link 倉庫中的各個 package,達(dá)到跨項目復(fù)用的目的。package manager 也理所當(dāng)然的選擇了 yarn,雖然依賴了 Lerna,由于發(fā)包場景較為稀少,基本沒有怎么使用。

          可以總結(jié)為以下三點:

          • 通過 yarn workspace link 倉庫中的 package
          • 使用 yarn 作為 package manager 管理項目中的依賴
          • 通過 lerna 在應(yīng)用 app 構(gòu)建前按照依賴關(guān)系構(gòu)建其依賴的 packages

          存在的問題

          命令不統(tǒng)一

          存在三種命令

          1. yarn
          2. yarn workspace
          3. lerna

          新人上手容易造成誤解,部分命令之間功能存在重疊。

          發(fā)布速度慢

          如果我們需要發(fā)布 app1,則會

          1. 全量安裝依賴,app1、app2、app3 以及 package1 至 package6 的依賴都會被安裝;
          2. package 全部被構(gòu)建,而非僅有 app1 依賴的 package1 與 package2 被構(gòu)建。

          Phantom dependencies

          一個庫使用了不屬于其 dependencies 里的 Package 稱之為 Phantom dependencies(幻影依賴、幽靈依賴、隱式依賴),在現(xiàn)有 Monorepo 架構(gòu)中該問題被放大(依賴提升)。


          由于無法保證幻影依賴的版本正確性,給程序運行帶來了不可控的風(fēng)險。app 依賴了 lib-a,lib-a 依賴了 lib-x,由于依賴提升,我們可以在 app 中直接引用 lib-x,這并不可靠,我們能否引用到 lib-x,以及引用到什么版本的 lib-x 完全取決于 lib-a 的開發(fā)者。

          NPM doppelgnger

          相同版本的 Package 可能安裝多份,打包多份。

          假設(shè)存在以下依賴關(guān)系


          最終依賴安裝可能存在兩種結(jié)果:

          1. lib-x@^1 * 1 份,lib-x@^2 * 2 份
          2. lib-x@^2 * 1 份,lib-x@^1 * 2 份

          最終本地會安裝 3 份 lib-x,打包時也會存在三份實例,如果 lib-x 要求單例,則可能會造成問題。

          Yarn duplicate

          Yarn duplicate 及解決方案[1]

          假設(shè)存在以下依賴關(guān)系

          當(dāng) (p)npm 安裝到相同模塊時,判斷已安裝的模塊版本是否符合新模塊的版本范圍,如果符合則跳過,不符合則在當(dāng)前模塊的 node_modules 下安裝該模塊。即 lib-a 會復(fù)用 app 依賴的 [email protected]。

          然而,使用 Yarn v1 作為包管理器,lib-a 會單獨安裝一份 [email protected]。

          • difference between npm and yarn behavior with nested dependencies #3951[2]
          • Yarn installing multiple versions of the same package[3]
          • yarn-deduplicate-Cleans up yarn.lock by removing duplicates.[4]
          • Yarn v2 supports package deduplication natively[5]

          peerDependencies 風(fēng)險

          Yarn 依賴提升,在 peerDependencies 場景下可能導(dǎo)致 BUG。

          1. app1 依賴 [email protected]
          2. app2 依賴 [email protected]
          3. [email protected][email protected] 作為 peerDependency,故 app2 也應(yīng)該安裝 [email protected]

          若 app2 忘記安裝 [email protected],那么結(jié)構(gòu)如下

          --apps
              --app1
              --app2
          --node_modules
              [email protected]
              [email protected]

          此時 [email protected] 會錯誤引用 [email protected]。

          Package 引用規(guī)范缺失

          目前項目內(nèi)存在三種引用方式:

          1. 源碼引用:使用包名引用。需要配置宿主項目的構(gòu)建腳本,將該 Package 納入構(gòu)建流程。類似于直接發(fā)布一個 TypeScript 源碼包,引用該包的項目需要做一定的適配。
          2. 源碼引用:使用文件路徑引用??梢岳斫狻八拗髟谧陨?src 之外的源文件”,即宿主項目源代碼的一部分,而非 Package。宿主需要提供該所有依賴,在 Yarn 依賴提升的前提下達(dá)到了跨項目復(fù)用,但存在較大風(fēng)險。
          3. 產(chǎn)物引用。打包完成,直接通過包名引用產(chǎn)物。

          Package 引用版本不確定性

          假設(shè)一個 Monorepo 中的 package1 發(fā)布至了 npm 倉庫,那么 Monorepo 中的 app1 應(yīng)當(dāng)如何在 package.json 中編寫引用 package1 的版本號?

          package1/packag.json

          {
            "name""package1",
            "version""1.0.0"
          }

          app1/package.json

          {
            "name""app1",
            "dependencies": {
              "package-1""?" // 這里的版本號應(yīng)該怎么寫?`*` or `1.0.0`
            }
          }

          在處理 Monorepo 中項目的互相引用時,Yarn 會進行以下幾步判斷:

          1. 判斷當(dāng)前 Monorepo 中,是否存在匹配 app1 所需版本的 package1;
          2. 若存在,執(zhí)行 link 操作,app1 直接使用本地 package1;
          3. 若不存在,從遠(yuǎn)端 npm 倉庫拉取符合版本的 package1 供 app1 使用。

          需要特別注意的是:* 無法匹配 prerelease 版本 ?? Workspace package with prerelease version and wildcard dep version #6719[6]。

          假設(shè)存在以下場景:

          1. package1 此前已經(jīng)發(fā)布了 1.0.0 版本,此時遠(yuǎn)端倉庫與本地 Monorepo 中代碼一致;
          2. 產(chǎn)品同學(xué)提了一個只服務(wù)于 Monorepo 內(nèi)部應(yīng)用的需求;
          3. package1 在 1.0.0 版本下迭代,無需變更版本號發(fā)布;
          4. Yarn 判斷 Monorepo 中的 package1 版本滿足了 app1 所需版本(*1.0.0);
          5. app1 順利使用上 package1 的最新特性。

          直到某天,該需求特性需要提供給外部業(yè)務(wù)方使用。

          1. pacakge1 將版本改為 1.0.0-beta.0 并進行發(fā)版;
          2. Yarn 判斷當(dāng)前 Monorepo 中的 package1 版本不滿足 app1 所需版本;
          3. 從遠(yuǎn)端拉取 [email protected] 供 app1 使用;
          4. 遠(yuǎn)端 [email protected] 已經(jīng)落后 app1 先前使用的本地 [email protected] 太多;
          5. 準(zhǔn)備事故通報以及復(fù)盤。

          這種不確定性,導(dǎo)致引用此類 package 時會經(jīng)常犯嘀咕:我到底引用的是本地版本還是遠(yuǎn)端版本?為什么有時候是本地版本,有時候是遠(yuǎn)端版本?我想用上 package1 的最新內(nèi)容還需要時刻保持與 package1 的版本號保持一致 ,那我干嘛用 Monorepo ?

          yarn.lock 沖突

          (p)npm 支持自動化解決 lockfile 沖突,yarn 需要手動處理,在大型 Monorepo 場景下,幾乎每次分支合并都會遇到 yarn.lock 沖突。

          • 不解決沖突無腦 yarnyarn.lock 會直接失效,全部版本更新到 package.json 最新,風(fēng)險太大,失去 lockfile 的意義;
          • 人工解決沖突往往會出現(xiàn) Git conflict with binary files,只能使用 master 的提交再重新 yarn,流程繁瑣。

          Automatically resolve conflicts in lockfile · Issue #2036 · pnpm/pnpm[7]

          可以發(fā)現(xiàn),現(xiàn)有 Monorepo 管理方式缺陷過多,隨著其內(nèi)項目的不斷增加,構(gòu)建速度會越來越慢,同時程序的健壯性無法得到保證。僅憑開發(fā)人員自覺是不可靠的,我們需要一套解決方案。

          推薦閱讀:node_modules 困境[8]

          解決方案

          pnpm[9]

          Fast, disk space efficient package packageManager

          在 npm@3 之前, node_modules 的結(jié)構(gòu)是干凈且可預(yù)測的,因為 node_modules 中的每個依賴項都有其自己的 node_modules 文件夾,其所有依賴項都在 package.json 中指定。

          node_modules
          └─ foo
             ├─ index.js
             ├─ package.json
             └─ node_modules
                └─ bar
                   ├─ index.js
                   └─ package.json

          但是這樣帶來了兩個很嚴(yán)重的問題:

          1. 依賴層級過深在 Windows 下會出現(xiàn)問題;
          2. 同一 Package 作為其他多個不同 Package 的依賴項時,會被拷貝很多次。

          為了解決這兩個問題,npm@3 重新思考了 node_modules 的結(jié)構(gòu),引入了平鋪的方案。于是就出現(xiàn)了下面我們所熟悉的結(jié)構(gòu)。

          node_modules
          ├─ foo
          |  ├─ index.js
          |  └─ package.json
          └─ bar
             ├─ index.js
             └─ package.json

          與 npm@3 不同,pnpm 使用另外一種方式解決了 npm@2 所遇到的問題,而非平鋪 node_modules。

          在由 pnpm 創(chuàng)建的 node_modules 文件夾中,所有 Package 都與自身的依賴項分組在一起(隔離),但是依賴層級卻不會過深(軟鏈接到外面真正的地址)。

          -> - a symlink (or junction on Windows)

          node_modules
          ├─ foo -> .registry.npmjs.org/foo/1.0.0/node_modules/foo
          └─ .registry.npmjs.org
             ├─ foo/1.0.0/node_modules
             |  ├─ bar -> ../../bar/2.0.0/node_modules/bar
             |  └─ foo
             |     ├─ index.js
             |     └─ package.json
             └─ bar/2.0.0/node_modules
                └─ bar
                   ├─ index.js
                   └─ package.json
          1. 基于非扁平化的 node_modules 目錄結(jié)構(gòu),解決 Phantom dependencies。Package 只可觸達(dá)自身依賴。
          2. 通過軟鏈復(fù)用相同版本的 Package,避免重復(fù)打包(相同版本),解決 NPM doppelgnger(順帶解決磁盤占用)。

          可以發(fā)現(xiàn),很多與包管理器相關(guān)的問題就此迎刃而解。

          • Why should we use pnpm?[10]
          • 平鋪 node_modules 不是唯一的路[11]

          Rush[12]

          a scalable monorepo manager for the web

          1. 命令統(tǒng)一。

          rush(x) xxx 一把梭,減少新人上手成本。同時 Rush 除了 rush add 以及 rushx xxx 等命令需要在指定項目下運行,其他命令均為全局命令,可在項目內(nèi)任意目錄執(zhí)行,避免了在終端頻繁切換項目路徑的問題。


          1. 強大的依賴分析能力。

          Rush 中的許多命令支持分析依賴關(guān)系,比如 -t(to) 參數(shù):

          $ rush install -t @monorepo/app1

          該命令只會安裝 app1 的依賴及其 app1 依賴的 package 的依賴,即按需安裝依賴。

          $ rush build -t @monorepo/app1

          該命令會執(zhí)行 app1 以及 app1 依賴的 package 的構(gòu)建腳本。

          類似的,還有 -f(from) 參數(shù),可以使命令只作用于當(dāng)前 package 以及依賴了該 package 的 package。

          1. 保證依賴版本一致性

          Monorepo 中的項目應(yīng)當(dāng)盡量保證依賴版本的一致性,否則很有可能出現(xiàn)重復(fù)打包以及其他的問題。

          Rush 則提供了許多能力來保證這一點,如rush check、rush add -p package-name -m 以及 ensureConsistentVersions

          有興趣的同學(xué)可以自行翻閱 Rush 的官方文檔,十分詳盡,對于一些常見問題也有說明。

          Package 引用規(guī)范


          產(chǎn)物引用

          傳統(tǒng)引用方式,構(gòu)建完成后,app 直接引用 package 的構(gòu)建產(chǎn)物。開發(fā)階段可以通過構(gòu)建工具提供的能力保證實時構(gòu)建(如 tsc --watch)

          • 優(yōu)點:規(guī)范,對 app 友好。
          • 缺點:隨著模塊增多,package 熱更新速度可能變得難以忍受。

          源碼引用

          package.json 中的 main 字段配置為源文件的入口文件,引用該 package 的 app 需要將該 package 納入編譯流程。

          • 優(yōu)點:借助 app 的熱更新能力,自身沒有生成構(gòu)建產(chǎn)物的過程,熱更新速度快
          • 缺點:需要 app 進行適配, alias 適配繁瑣;

          引用規(guī)范

          1. 對于項目內(nèi)部使用的 packages ,稱為 features,不應(yīng)當(dāng)向外發(fā)布,直接將 main 字段設(shè)置為源文件入口并配置 app 項目的 webpack,走后編譯形式。
          2. 對于需要對外發(fā)布的 packages,不應(yīng)該也不允許引用 features,必須要有構(gòu)建過程,如果需要使用源碼開發(fā)增加熱更新速度,可以新增一個自定義的入口字段,app 的 webpack 配置中優(yōu)先識別該入口字段即可。

          補充:rush build 命令是支持構(gòu)建產(chǎn)物緩存的,如果 app 拆分粒度夠小,可復(fù)用的包足夠多,同時打包鏡像支持構(gòu)建產(chǎn)物緩存的 set 與 get,就可以做到增量構(gòu)建 app。

          Workspace protocol (workspace:)

          在 PNPM 和 Yarn 支持 Workspace 能力之前,Rush 就誕生了。Rush 的方法是將所有軟件包集中安裝在 common / temp 文件夾中,然后 Rush 創(chuàng)建從每個項目到 common / temp 的符號鏈接。與 PNPM Workspace 本質(zhì)上是等效的。

          開啟 PNPM workspace[13] 能力從而可以使用 workspace:協(xié)議保證引用版本的確定性,使用了該協(xié)議引用的 package 只會使用 Monorepo 中的內(nèi)容。

          {
            "dependencies": {
              "foo""workspace:*",
              "bar""workspace:~",
              "qar""workspace:^",
              "zoo""workspace:^1.5.0"
            }
          }

          推薦引用 Monorepo 內(nèi)的 package 時統(tǒng)一使用該協(xié)議,引用本地最新版本內(nèi)容,保證更改能夠及時擴散同步至其他項目,這也是 Monorepo 的優(yōu)勢所在。

          若一定要使用遠(yuǎn)端版本,需要在 rush.json 中配置具體 project (增加 cyclicDependencyProjects配置),詳見 rush_json[14]

          很幸運的是 PNPM workspace 中 workspace:* 可以匹配 prerelease 版本 ?? Local prerelease version of packages should be linked only if the range is \*[15]

          問題記錄

          Monorepo Project Dependencies Duplicate

          這個問題類似于前面提到的 Yarn duplicate,但并不是 Yarn 獨有的。

          假設(shè)存在以下依賴關(guān)系(將 Yarn duplicate 的例子進行改造,放在 Monorepo 場景中)

          app1 以及 package1 同屬于 Monorepo 內(nèi)部 project。

          在 Rush(pnpm)/Yarn 項目中,會嚴(yán)格按照 Monorepo 內(nèi) project 的 package.json 所聲明的版本進行安裝,即 app1 安裝 [email protected],package1 安裝 [email protected]。

          此時對 app1 進行打包,則 [email protected][email protected] 都會被打包。

          對這個結(jié)果你也許會有一些意外,但仔細(xì)想想,又很自然。

          換一種方式理解,整個 Monorepo 是一個大的虛擬 project,我們所有的 project 都作為這個虛擬 project 的直接依賴存在。

          {
            "name""fake-project",
            "version""1.0.0",
            "dependencies": {
              "@fake-project/app1""1.0.0",
              "@fake-project/package1""1.0.0"
            }
          }

          安裝依賴時,(p)npm 首先下載直接依賴項,然后再下載間接依賴項,并且在安裝到相同模塊時,判斷已安裝的模塊版本(直接依賴項)是否符合新模塊(間接依賴項)的版本范圍,如果符合則跳過,不符合則在當(dāng)前模塊的 node_modules 下安裝該模塊。

          而 app1 和 package1 的直接依賴關(guān)系 lib-a 是該 fake-project 的間接依賴項,無法滿足上述判斷條件,于是按照對應(yīng) package.json 中描述的版本安裝。

          解決方案:Rush: Preferred versions[16]

          Rush 可以通過手動指定 preferredVersions 的方式,避免兩個可兼容版本的重復(fù)。這里將 Monorepo 中 lib-a 的 preferredVersions 指定為 1.2.0,相當(dāng)于在該虛擬 project 下直接安裝了指定的版本的模塊,作為直接依賴項。

          {
            "name""fake-project",
            "version""1.0.0",
            "dependencies": {
              "@fake-project/app1""1.0.0",
              "@fake-project/package1""1.0.0",
              "lib-a""1.1.0"
            }
          }

          對于 Yarn,由于 Yarn duplicate 的存在,就算在根目錄指定安裝確定版本的 lib-a 也是無效的。但是依舊有兩種方案可以進行處理:

          1. 通過 yarn-deduplicate[17] 針對性的修改 yarn.lock;
          2. 使用resolutions 字段。過于粗暴,不像 preferredVersions 可以允許不兼容版本的存在,不推薦。

          需要謹(jǐn)記:在 Yarn 下消除重復(fù)依賴,也應(yīng)該一個 Package 一個 Package 的去進行處理,小心使得萬年船。

          1. 對于存在副作用的公共庫,版本最好保持統(tǒng)一;
          2. 對于其他的體積?。ɑ蛑С职葱杓虞d)、無副作用的公共庫,重復(fù)打包在一定程度上可以接受的。

          prettier

          由于根目錄不再存在 node_modules,故需要每個項目安裝一個 prettier 作為 devDependency 并編寫 .prettierrc.js 文件。

          本著偷懶的原則,根目錄新建 .prettierrc.js(不依賴任何第三方包),全局安裝 prettier 解決該問題。

          eslint

          先看一個場景,若在項目中使用 eslint-config-react-app,除了需要安裝 eslint-config-react-app,還需要安裝一系列 peerDependencies 插件。

          為什么 eslint-config-react-app 不將這一系列插件作為 dependencies 內(nèi)置,而是作為 peerDependencies?使用者并不需要關(guān)心預(yù)設(shè)配置內(nèi)引用了哪些插件。

          具體討論可以查看該 issue,里面有相關(guān)問題的討論:Support having plugins as dependencies in shareable config #3458[18] 。

          總而言之:和 eslint 插件的具體查找方式有關(guān),如果因為依賴提升失?。ǘ喟姹緵_突),導(dǎo)致需要的插件被裝在了非根目錄 node_modules 下,就可能產(chǎn)生問題,而用戶自行安裝 peerDependencies 可以保證不會出現(xiàn)該問題。

          當(dāng)然,我們也發(fā)現(xiàn)一些開源的 eslint 預(yù)設(shè)配置不需要安裝 peerDependencies,這些預(yù)設(shè)利用了 yarn 和 npm 的扁平 node_modules 結(jié)構(gòu),也就是依賴提升,裝的包都被提升至根目錄 node_modules,故可以正常運作。即便如此,在基于 Yarn 的 Monorepo 中,依賴一旦復(fù)雜起來,就可能出現(xiàn)插件無法被查找到的情況,能夠正常運轉(zhuǎn)就像一個有趣的巧合。

          在 Rush 中,不存在依賴提升(提升也不一定靠譜),裝一系列的插件又過于繁瑣,則可以通過打補丁[19]的方式繞過。

          git hooks

          通常會在項目中使用 husky 注冊 pre-commitcommit-msg 鉤子,用于校驗代碼風(fēng)格以及 commit 信息。

          很明顯,在 Rush 項目的結(jié)構(gòu)下,根目錄是沒有 node_modules 的,無法直接使用 husky

          我們可以借助 rush init-autoinstaller[20] 的能力來達(dá)到一樣的效果,本節(jié)主要參考官方文檔 Installing Git hooks[21] 以及 Enabling Prettier[22] 的內(nèi)容。

          # 初始化一個名為 rush-lint 的 autoinstaller

          $ rush init-autoinstaller --name rush-lint

          cd common/autoinstallers/rush-lint

          # 安裝 lint 所需依賴

          $ pnpm i @commitlint/cli @commitlint/config-conventional @microsoft/rush-lib eslint execa prettier lint-staged

          # 更新 rush-lint 的 pnpm-lock.yaml

          $ rush update-autoinstaller --name rush-lint

          rush-lint 目錄下新增 commit-lint.js 以及 commitlint.config.js,內(nèi)容如下

          commit-lint.js

          const path = require('path');
          const fs = require('fs');
          const execa = require('execa');

          const gitPath = path.resolve(__dirname, '../../../.git');
          const configPath = path.resolve(__dirname, './commitlint.config.js');
          const commitlintBinPath = path.resolve(__dirname, './node_modules/.bin/commitlint');

          if (!fs.existsSync(gitPath)) {
              console.error('no valid .git path');
              process.exit(1);
          }

          main();

          async function main({
              try {
                  await execa('bash', [commitlintBinPath, '--config', configPath, '--cwd', path.dirname(gitPath), '--edit'], {
                      stdio'inherit',
                  });
              } catch (\_e) {
                  process.exit(1);
              }
          }

          commitlint.config.js

          const rushLib = require("@microsoft/rush-lib");

          const rushConfiguration = rushLib.RushConfiguration.loadFromDefaultLocation();

          const packageNames = [];
          const packageDirNames = [];

          rushConfiguration.projects.forEach((project) => {
            packageNames.push(project.packageName);
            const temp = project.projectFolder.split("/");
            const dirName = temp[temp.length - 1];
            packageDirNames.push(dirName);
          });
          // 保證 scope 只能為 all/package name/package dir name
          const allScope = ["all", ...packageDirNames, ...packageNames];

          module.exports = {
            extends: ["@commitlint/config-conventional"],
            rules: {
              "scope-enum": [2"always", allScope],
            },
          };

          注意:此處不需要新增 prettierrc.js(根目錄已存在) 以及 eslintrc.js(各項目已存在)。

          根目錄新增 .lintstagedrc 文件

          .lintstagedrc

          {
            "{apps,packages,features}/**/*.{js,jsx,ts,tsx}": [
              "eslint --fix --color",
              "prettier --write"
            ],
            "{apps,packages,features}/**/*.{css,less,md}": ["prettier --write"]
          }

          完成了相關(guān)依賴的安裝以及配置的編寫,我們接下來將相關(guān)命令執(zhí)行注冊在 rush 中。

          修改 common/config/rush/command-line.json 文件中的 commands 字段。

          {
            "commands": [
              {
                "name""commitlint",
                "commandKind""global",
                "summary""Used by the commit-msg Git hook. This command invokes commitlint to lint commit message.",
                "autoinstallerName""rush-lint",
                "shellCommand""node common/autoinstallers/rush-lint/commit-lint.js"
              },
              {
                "name""lint",
                "commandKind""global",
                "summary""Used by the pre-commit Git hook. This command invokes eslint to lint staged changes.",
                "autoinstallerName""rush-lint",
                "shellCommand""lint-staged"
              }
            ]
          }

          最后,將 rush commitlint 以及 rush lint 兩個命令分別與 commit-msg 以及 pre-commit鉤子進行綁定。common/git-hooks 目錄下增加 commit-msg 以及 pre-commit 腳本。

          commit-msg

          #!/bin/sh

          node common/scripts/install-run-rush.js commitlint || exit $? #++

          pre-commit

          #!/bin/sh

          node common/scripts/install-run-rush.js lint || exit $? #++

          如此,便完成了需求。

          避免全局安裝 eslint 以及 prettier

          經(jīng)過上一節(jié)的處理,在 rush-lint 目錄下安裝了 eslint 以及 prettier 后,我們便無需全局安裝了,只需要配置一下 VSCode 即可。

          {
            // ...
            "npm.packageManager""pnpm",
            "eslint.packageManager""pnpm",
            "eslint.nodePath""common/autoinstallers/rush-lint/node_modules/eslint",
            "prettier.prettierPath""common/autoinstallers/rush-lint/node_modules/prettier"
            // ...
          }

          附錄

          常用命令

          yarnrush(x)detail
          yarn installrush install安裝依賴
          yarn upgraderush updaterush update 安裝依賴,基于 lock 文件
          rush update --full 全量更新到符合 package.json 的最新版本
          yarn add package-namerush add -p package-nameyarn add 默認(rèn)安裝版本號為 ^ 開頭,可接受小版本更新
          rush add 默認(rèn)安裝版本號為 ~ 開頭,僅接受補丁更新
          rush add 可通過增加 --caret 參數(shù)達(dá)到與 yarn add 效果一致
          rush add 不可一次性安裝多個 package
          yarn add package-name --devrush add -p package-name --dev-
          yarn remove package-name-rush 不提供 remove 命令
          -rush build執(zhí)行文件存在變更(基于 git)的項目的 build 腳本
          rush build -t @monorepo/app1 表示只構(gòu)建 @monorepo/app1 及其依賴的 package
          rush build -T @monorepo/app1 表示只構(gòu)建 @monorepo/app1 依賴的 package,不包含其本身
          -rush rebuild默認(rèn)執(zhí)行所有項目的 build 腳本
          yarn xxx(自定義腳本)rushx xxx(自定義腳本)yarn xxx 執(zhí)行當(dāng)前目錄下 package.json 中的 xxx 腳本(npm scripts)
          rushx xxx 同理。可以直接執(zhí)行 rushx 查看當(dāng)前項目所支持的腳本命令。

          工作流

          # 從 git 拉取最新變更
          $ git pull

          # 更新 NPM 依賴
          $ rush update

          # 重新打包 @monorepo/app1 依賴的項目(不含包其本身)
          $ rush rebuild -T @monorepo/app1

          # 進入指定項目目錄
          cd ./apps/app1

          # 啟動項目 
          $ rushx start # or rushx dev

          參考文章

          • Rush.js[23]
          • node_modules 困境[24]
          • Why should we use pnpm?[25]
          • 平鋪 node_modules 不是唯一的路[26]

          參考資料

          [1]

          Yarn duplicate 及解決方案: https://github.com/worldzhao/blog/issues/10

          [2]

          difference between npm and yarn behavior with nested dependencies #3951: https://github.com/yarnpkg/yarn/issues/3951

          [3]

          Yarn installing multiple versions of the same package: https://stackoverflow.com/questions/49072905/yarn-installing-multiple-versions-of-the-same-package

          [4]

          yarn-deduplicate-Cleans up yarn.lock by removing duplicates.: https://github.com/atlassian/yarn-deduplicate

          [5]

          natively: https://github.com/yarnpkg/berry/pull/1558

          [6]

          Workspace package with prerelease version and wildcard dep version #6719: https://github.com/yarnpkg/yarn/issues/6719

          [7]

          Automatically resolve conflicts in lockfile · Issue #2036 · pnpm/pnpm: https://github.com/pnpm/pnpm/issues/2036

          [8]

          node_modules 困境: https://zhuanlan.zhihu.com/p/137535779

          [9]

          pnpm: https://pnpm.io/

          [10]

          Why should we use pnpm?: https://www.kochan.io/nodejs/why-should-we-use-pnpm.html

          [11]

          平鋪 node_modules 不是唯一的路: https://pnpm.io/zh/blog/2020/05/27/flat-node-modules-is-not-the-only-way

          [12]

          Rush: https://rushjs.io/

          [13]

          PNPM workspace: https://pnpm.io/zh/workspaces

          [14]

          rush_json: https://rushjs.io/pages/configs/rush_json/

          [15]

          Local prerelease version of packages should be linked only if the range is *: https://github.com/pnpm/pnpm/pull/2259

          [16]

          Rush: Preferred versions: https://rushjs.io/pages/advanced/preferred_versions/

          [17]

          yarn-deduplicate: https://github.com/atlassian/yarn-deduplicate

          [18]

          Support having plugins as dependencies in shareable config #3458: https://github.com/eslint/eslint/issues/3458

          [19]

          補丁: https://www.npmjs.com/package/@rushstack/eslint-patch

          [20]

          rush init-autoinstaller: https://rushjs.io/pages/commands/rush_init-autoinstaller/

          [21]

          Installing Git hooks: https://rushjs.io/pages/maintainer/git_hooks/

          [22]

          Enabling Prettier: https://rushjs.io/pages/maintainer/enabling_prettier/

          [23]

          Rush.js: https://rushjs.io/

          [24]

          node_modules 困境: https://zhuanlan.zhihu.com/p/137535779

          [25]

          Why should we use pnpm?: https://www.kochan.io/nodejs/why-should-we-use-pnpm.html

          [26]

          平鋪 node_modules 不是唯一的路: https://pnpm.io/zh/blog/2020/05/27/flat-node-modules-is-not-the-only-way


          瀏覽 117
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产精品激情在线 | 国产肏屄高视频大香蕉 | 日本免费成人黄色网址 | 日韩 欧美中文字幕第一页在线 | 成年人黄色免费网站 |