為什麼 husky 放棄了傳統的 JS 配置

前端森林發表於2021-09-25

前言

husky想必大家都不陌生。作為前端工程化中一個不可或缺的的工具,它可以向我們的專案中新增git hooks。同時配合lint-staged可以方便的在程式碼提交前進行lint

最近要對一個老專案新增commit-msg校驗,同時要在commit前進行eslint校驗。之前我也寫過一篇類似的文章你可能已經忽略的 git commit 規範,就直接上手了。

大致流程就是先安裝依賴:

npm i husky -D

然後在package.json配置:

{
  "husky": {
    "hooks": {
      "pre-commit": "npm run test", // 在commit之前先執行npm run test命令
      "commit-msg": "commitlint -e $HUSKY_GIT_PARAMS" // 校驗commit時新增的備註資訊是否符合我們要求的規範
    }
  }
}

然後測試了一下commit操作,好傢伙,直接commit成功了。根本沒有對commit-msg做校驗。

我就納悶了,之前不都是這樣搞的嗎 ?

沒辦法,去查一下文件吧。

然後就看到了這個:

原來在 husky(6.0.0)版本做了Breaking change。再看下專案中安裝的版本號:"husky": "^7.0.1"。難怪不生效了,,

之前寫你可能已經忽略的 git commit 規範文章時,用的還是1.0.1的版本。?

既然這樣,我們先來看下作者為什麼要做這樣的改動吧:

這是作者寫的一篇Why husky has dropped conventional JS config,也就是為什麼 husky 放棄了傳統的 JS 配置。下面簡單概括一下。

為什麼 husky 放棄了傳統的 JS 配置

v4 版本之前 husky的工作方式是這樣的:為了能夠讓使用者設定任何型別的git hookshusky不得不建立所有型別的git hooks

這樣做的好處就是無論使用者設定什麼型別的git hookhusky都能確保其正常執行。但是缺點也是顯而易見的,即使使用者沒有設定任何git hookhusky也向git中新增了所有型別的git hook

在當時 husky 有過這樣的設想:有沒有可能讓husky只新增我們需要的git hook呢?作者嘗試過解決這個問題,但是失敗了。

因為husky需要在兩個地方進行配置才能完成一個完整的git hook功能。一個是在package.json中配置git hook所要執行的真正命令,一個是在.git/hooks/中配置相對應的git hook。也就是說無論是新增還是刪除git hook就要保證在這兩個地方同步執行對應的操作。作者無法找到一個可靠的方法來同步這兩個地方的配置,因此失敗了。

新版 husky 的工作原理又是什麼呢?

直到 2016 年,Git 2.9引進了core.hooksPath,可以設定Git hooks指令碼的目錄,這個引進也就是新版husky改進的基礎:

  • 可以使用husky installgit hooks的目錄指定為.husky/
  • 使用husky add命令向.husky/中新增hook

通過這種方式我們就可以只新增我們需要的git hook,而且所有的指令碼都儲存在了一個地方(.husky/目錄下)因此也就不存在同步檔案的問題了。

ok,瞭解了這麼多,我想你也大概理解作者為什麼要做這種破壞性更新的原因了吧。那麼我們接著上面的按照新版husky的配置規則對我們的專案進行配置。

新版 husky 實踐

我們可以直接按照官方文件的指引來進行。

安裝

Install husky

npm install husky --save-dev

Enable Git hooks

npx husky install

如果想安裝後自動啟用hooks,可以執行:

npm set-script prepare "husky install"

這樣就會在package.json裡面新增一條指令碼:

// package.json
{
  "scripts": {
    "prepare": "husky install"
  }
}
prepareNPM 操作生命週期中的一環,在執行 install 的時候會按生命週期順序執行相應鉤子:
NPM7: preinstall -> install -> postinstall -> prepublish -> preprepare -> prepare -> postprepare

這樣就會在程式碼根目錄生成如下所示的結構:

新增 hook

我們可以使用husky add <file> [cmd]指令來新增一條hook

commit-msg

在專案中我們會使用commit-msg這個git hook來校驗我們commit時新增的備註資訊是否符合規範。在以前我們通常是這樣配置的:

{
  "husky": {
    "hooks": {
      "commit-msg": "commitlint -e $HUSKY_GIT_PARAMS" // 校驗commit時新增的備註資訊是否符合我們要求的規範
    }
  }
}

在新版husky$HUSKY_GIT_PARAMS這個變數不再使用了,取而代之的是$1。所以我們要做如下操作:

執行npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'會在.husky下生成一個commit-msgshell檔案:

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
echo "========= 執行commit-msg校驗 ======="
npx --no-install commitlint --edit $1

此時如果執行git commit操作,會有如下報錯:

提示我們缺少commitlint.config.js檔案,這裡先安裝依賴:

npm install --save-dev @commitlint/cli @commitlint/config-conventional

然後在根目錄新建一個commitlint.config.js檔案並加入如下內容:

module.exports = {
  extends: ["@commitlint/config-conventional"]
};

這時再執行commit就會發現已經生效了:

pre-commit

commit前,我們可以執行測試用例、eslint 校驗等,只有這些通過了,才允許提交。這也就是在pre-commit這個鉤子裡需要做的事情。

執行npx husky add .husky/pre-commit "npm run test:unit"就會在.husky下生成一個pre-commit的 shell 檔案:

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

echo "========= 執行pre-commit操作(如執行測試用例、eslint校驗等,可自行新增) ======="
npm run test:unit

讓我們再做一次commit操作:

至此,我們就基於新版husky,完成了專案中commit-msgpre-commit兩個鉤子的新增。

相關文章