天天看點

lerna 項目中內建 babel lint-staged husky eslintlerna 項目中內建 babel lint-staged husky eslint

lerna 項目中內建 babel lint-staged husky eslint

Monorepo 是針對單倉庫、多 package 的流行解決方案, lerna 是它的一種實作。

說明

重要 package 版本
  • “lint-staged”: “^10.5.3”,
  • “eslint”: “^7.17.0”,
  • “husky”: “^4.3.6”,
  • “lerna”: “^3.22.1”
  • “@babel/core”: “^7.12.10”
因為項目采用 lerna 這種 monorepo 解決方案,實際的工作區在 根目錄下的子級目錄,這樣就會對我們使用這些工具造成不可避免的影響(安裝在頂級目錄還是子級目錄?怎麼協同?多個子項目之間互相影響?),要解決這些影響,需要我們做如下的配置。

eslint 在 lerna 項目中的配置

在 lerna 這種 monorepo 項目中,由于會有多個子項目存在,不同子項目對 eslint 的使用可能大同小異,

.eslintrc.js

這個 eslint 配置檔案會在每個使用 eslint 的子項目中單獨建立,項目頂層不建立 .eslintrc.js.
如何獲得 eslint 錯誤提示?
    1. 開發時我們主要是通過代碼編輯器的回報來獲得 eslint 的提示;
    1. 打包 build 時,我們是通過打包工具(Webpack, Rollup)的 eslint 插件 (eslint-loader, @rollup/plugin-eslint)來連結 eslint ,且在指令行中得到 eslint 的回報。
    1. commit 時,會通過鈎子直接觸發 eslint 檢查,并回報在指令行中
針對後兩種情況, 隻要指令是在子項目根目錄運作的 eslint 就會以子項目根目錄的配置檔案為準,是以不用過多擔心子項目之間的 eslint 互相影響。
針對 第一種情況,我們需要確定代碼編輯器能夠正确的獲得 eslint 配置檔案的作用範圍,經測試 WebStorm 不需要經過配置即可識别目前子項目的 eslint 配置檔案. VSCode 的話需要我們給VSCode 的 WorkSpace 做如下設定
// /.vscode/settings.json
{
    // 那個子項目用到了 eslint 都需要手動添加進來,否則 vscode eslint 提示會失效(通配符不可用)
  "eslint.workingDirectories": [
    "packages/rollup-ui", 
    "packages/mjz-ui",
  ],
  "eslint.packageManager": "yarn",
}
           

babel 在 lerna 項目中的內建

檢視官方文檔 ,我們可以了解到Babel将工作目錄視為其邏輯“根”,如果您想在特定的子程式包中運作Babel工具而不将Babel應用于整個倉庫,則會引起問題,是以 在 monorepo 的項目下我們設定 babel 需要有一定的規範
  1. 在項目頂層目錄下建立 一個 babel.config.json 并給其設定 babelrcRoots 選項,這個選項用來設定那些子 package 會被 babel 視為"根“(不被 babel 視為根的子 程式包中的 babelrc 配置檔案不會生效)
// /babel.config.js
module.exports = function(api) {
  api.cache(true);
  return { 
    babelrcRoots: [
        ".",
        "packages/*" // 将子程式包都作為工作目錄
     ] 
  };
};
           
  1. 在子 package 中設定 .babelrc.js 作為配置檔案而不是 babel.config.js
以上配置好後即可正确的使用 babel 了

lint-staged husky 在 lerna 項目中內建

  • husky: 用來做 git 的鈎子,通常我們在 git commit 之前觸發 pre-commit 鈎子,然後通過在 husky 中配置這個鈎子觸發後的執行指令
  • lint-staged: 用來幫助我們快速的做代碼校驗,例如我們用 eslint 做代碼校驗,隻能校驗全量的代碼(js 或者 ts),而 lint-staged 可以查詢 git 的工作區,篩選出與上一個 commit 相比較有改動的代碼,結合 eslint 後我們就可以僅校驗這部分改動的,進而減少時間。理論上講 lint-staged 隻是幫我們篩選出有過改動的檔案,然後通過它的 配置檔案, 再次出發其他腳本進行對應類型檔案的校驗
// lint-staged.config.js 針對不同類型的改動檔案執行對應的腳本進行校驗
module.exports = {
  'src/**/*.{less,css,md,html}': ['prettier --write'],
  'src/**/*.{js,jsx}': ['prettier --write', 'yarn lint:js'],
  'src/**/*.{ts,tsx}': ['prettier --write', 'yarn lint:ts'],
};
           
在 lerna 項目中,由于所有子項目公用一個 repo 源代碼倉庫,是以它的 husky 鈎子隻能建立在最頂層目錄;
每次 commit 都很有可能是多個子項目都有改動,這個時候使用 lint-staged 時,就不但要區分檔案類型,還要區分改動檔案所在的子項目(因為不同的子項目可能會有不同的校驗處理),lerna 項目中實作這個能力有如下三種方法
  1. 将 lint-staged 安裝到最頂層,這樣在每次 pre-commit 時都借助 husky 觸發 lint-staged 執行,然後在lint-staged 中通過比對路徑來決定哪些子項目的 校驗執行
.
├── lerna.json
├── package.json
├── .huskyrc.js
├── lint-staged.config.js
├── packages
│   ├── mjz-ui
│   │   ├── package.json
│   │   ├── rollup.config.js
│   │   ├── src
│   │   └── tsconfig.json
│   └── rollup-ui
│       ├── package.json
│       ├── rollup.config.js
│       ├── src
│       ├── tsconfig.json
└── yarn.lock
           
// lint-staged.config.js 根據路徑比對子項目
module.exports = {
    'packages/mjz-ui/src/**/*.{js,jsx}': 'cd packages/mjz-ui && yarn lint:js',
    'packages/rollup-ui/src/**/*.{ts,tsx}': 'cd packages/rollup-ui && yarn lint:ts' ,
};
           
  1. 将 lint-staged 安裝到每一個子項目,然後在 husky 觸發鈎子後執行所有 子項目的 lint-staged
// /package.json
{
  "scripts": {
    "precommit:prj-1": "cd prj-1 && npm run lint-staged",
    "precommit:prj-2": "cd prj-2 && npm run lint-staged",
    "precommit": "npm-run-all precommit:*" // husky 觸發這個指令,然後這個指令觸發所有子項目的 lint-staged
  },
}
           
  1. 以上兩個方案可以用作非 lerna 的 monorepo 項目中,針對 lerna 項目我們可以使用 lerna 指令來實作對“哪個子項目有修改”的判斷;同樣 lint-staged 需要安裝到任意一個需要做校驗的子項目中
.
├── lerna.json
├── package.json
├── .huskyrc.js
├── lint-staged.config.js
├── packages
│   ├── mjz-ui
│   │   ├── package.json
│   │   ├── lint-staged.config.js // 這裡隻要關系目前子項目的改動即可
│   │   ├── rollup.config.js
│   │   ├── src
│   │   └── tsconfig.json
│   └── rollup-ui
│       ├── package.json
│       ├── lint-staged.config.js
│       ├── rollup.config.js
│       ├── src
│       ├── tsconfig.json
└── yarn.lock
           
// /.huskyrc.js
module.exports = {
  hooks: {
    'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS',
    'pre-commit': 'lerna run --concurrency 1 --stream lint-staged --since HEAD --exclude-dependents' // 通過 lerna 指令查到哪個子項目有改動,并除服這個子項目對應的 lint-staged
  }
}
           
綜上,針對 lerna 項目,我會選擇第三種方案實作, 因為每一個子項目的 lint-staged 應該隻關心目前子項目的(即lint-staged 配置檔案放在子項目下),頂層目錄不應該包含“關于怎麼校驗某個子項目代碼”的邏輯,是以排除方案一;使用第三種方案同時還能兼顧“僅對有改動的檔案做校驗”,而第二種方案不能做到這一點。

繼續閱讀