The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

設定 Angular 專案使用 husky 簡化 Git hooks 設定並用 Prettier 統一風格

我們在團隊中開發 Angular 應用程式,經常需要同步每個成員的程式碼格式,與其訂定 Coding Style (代碼風格),倒不如直接用工具強制所有成員用一致的風格進行程式碼排版。本篇文章我將示範用 husky 搭配 prettier 來設定 Git 的 pre-commit hook,讓每個人在 git commit 之前就自動將變更的程式碼進行排版。

介紹 npm set-script 命令

我在安裝 husky 的時候,看到一個很方便的 npm set-script 命令,他可以透過 npm 命令,使用命令列執行的方式幫你自動設定 package.jsonscripts 區段內容,自動設定執行腳本內容。

不過我卻發現在我的電腦不能執行 npm set-script 命令,查詢 npm-set-script 官網文件後發現這是 npm 7.x 才有的全新功能。由於我的電腦安裝的是 Node.js 14.17.5 LTS 版本,內建的 npm 是 6.14.14 版,所以不支援這個好用命令。

在 Windows 要升級 npm 必須以管理者身份執行以下 Windows PowerShell 命令才能順利升級到最新版:

Set-ExecutionPolicy Unrestricted -Scope CurrentUser -Force
npm install -g npm-windows-upgrade
npm-windows-upgrade -p -v latest
C:\>npm --version
7.21.0

如果是 Linux 或 macOS 的話,很簡單的透過以下命令就可以升級完畢:

npm install –g npm@latest

注意: 只有在第一次設定 husky 的時候才有可能需要升級 npm 版本,就算你不升級版本,手動調整 package.json 也是可以的,不一定要用 npm set-script 來調整 package.json 的內容。對於團隊成員來說,其實 npm 用什麼版本都沒差!

這個段落純粹想記錄一下在 Windows 環境如何升級 npm 版本,但 Angular CLI v12 目前還不支援 npm v7 的版本,所以要降版請用以下命令:

npm-windows-upgrade -p -v 6.14.15

為團隊設定 Coding Style (代碼風格) 自動化設定

這邊我直接以 Angular 12 專案為範例進行實作,講述完整的安裝與設定過程。

  1. 先透過 Angular CLI 12.2.2 建立一個全新專案:

    npm i -g @angular/cli
    
    ng new demo1 --routing --style=css
    cd demo1
    
  2. 安裝 huskypretty-quick 套件

    npm install --save-dev husky prettier pretty-quick
    

    注意: 雖然 pretty-quick 套件本身就包含了 prettier 套件在內,但是 prettier 被註冊在 peerDependencies 裡面,使用 npm 會全自動一併安裝起來,但使用 yarn 的話會跳出警告訊息,必須手動額外安裝。所以我上述命令才連同 prettier 一併安裝!

    [3/4] Linking dependencies...
    warning " > pretty-quick@3.1.1" has unmet peer dependency "prettier@>=2.0.0".
    
  3. 設定 npm 的 prepare 生命週期腳本並執行 husky 安裝

    你可以透過 npm pkg set scripts.prepare 命令方便的將 husky install 自動註冊進 npm 的 prepare 生命週期階段(Life Cycle Scripts)。然後 npm run prepare 可以直接執行 husky install 命令,這個動作會自動幫你建立一個 .husky 資料夾:

    npm pkg set scripts.prepare="husky install"
    npm run prepare
    

    預設 husky install 產生的所有檔案都不會加入 Git 版控,你額外新增的 Hooks 檔案才需要加入版控。

    特別值得一提的地方,是這個 prepare 會在執行 npm install 之後自動執行 (必須在沒有附帶任何其他參數下執行 npm install 才行)。如果你寫的專案是會發布到 npm registry 的話,prepare 會在 npm publishnpm pack 之前自動執行。詳細的執行生命週期請見 Life Cycle Operation Order 文件。

    請不要將 husky install 註冊在 postinstall 生命週期階段!(Typicode's blog - Why husky doesn't autoinstall anymore)

  4. 設定 husky 的 pre-commit Hook

    這裡我們設定 pre-commit Hook,讓團隊成員在執行 git commit 之前,就會自動透過 pretty-quickpretty-quick --staged 命令對 Git 暫存的檔案 (Staged files) 進行程式碼編排,而且是參照 .prettierrc.json, .prettierignore.editorconfig 的設定進行,確保團隊的程式撰寫風格一致。以下命令會產生一個 .husky/pre-commit 檔案,這個檔案需要加入到 Git 版控之中。

    npx husky set .husky/pre-commit "npx pretty-quick --staged"
    

    這個 npx pretty-quick --staged 命令,主要做兩件事:

    1. 找出目前 Git 版控中,已經被加入到 Staged 狀態的檔案清單
    2. 將這些工作目錄中的檔案透過 Prettier 排版後,直接加入到 Git 的 Staged 狀態中

    簡單來說,他會在無形之中,默默的將你的程式碼排版成 Prettier 想要排版的樣子。你想要用自己的方式排版怎麼辦呢?沒錯!Prettier 就是這麼霸道,也沒什麼好商量的,整個團隊都用同一套規則集排版程式碼就對了,如此一來,你就不要再花時間想程式碼風格這件事情了!

  5. 加入 .prettierignore 檔案 (不要透過 Prettier 排版的檔案清單)

    我基本上習慣將專案根目錄下的 package.jsonpackage-lock.json 加入到 .prettierignore 檔案中,畢竟這些檔案都是透過 npm 工具在更新的居多,沒必要特別排版過。除此之外,我也會把 Visual Studio Code 的設定檔 (.vscode/*) 也都自動排除在 Prettier 掃描的清單中。最後,Angular 建置後的檔案通常也不用自動格式化。

    # IDE/Editor
    .vscode
    .idea
    
    # Package Manager
    package.json
    package-lock.json
    yarn.lock
    
    # TypeScript-related
    tsconfig.json
    tsconfig.app.json
    tsconfig.spec.json
    
    # Angular-related
    **/assets/**/*
    
    # Compiled output
    /dist
    /tmp
    /out-tsc
    /bazel-out
    
    # Miscellaneous
    /.angular/cache
    .sass-cache/
    /connect.lock
    /coverage
    /libpeerconnection.log
    testem.log
    /typings
    
  6. 加入 .prettierrc.json 檔案

    其實 Prettier 可以設定的選項並不多,但有些地雷。其中初學者最容易遇到的問題,就是 .prettierrc.json.editorconfig 設定不一致的情況。在編輯器中有兩種不同的風格,絕對會遇到許多問題,所以請務必設定正確!

    你要是從網路上找的許多的 .prettierrc 範例,都會叫你設定 tabWidthuseTabs 這兩項。但是這兩個設定會跟 .editorconfig 中的 indent_sizeindent_style 完全重複,如果你剛好兩邊的設定不相同,就可能會出現非常詭異的情況,有時候甚至覺得會不知所措,也就是俗稱「鬼打牆」的情況。所以我一般都會特別看過兩邊的設定是否一致,這點真的非常重要!

    {
      "tabWidth": 2,
      "useTabs": false,
      "printWidth": 100,
      "bracketSpacing": true,
      "singleQuote": true,
      "trailingComma": "es5",
      "semi": true,
      "overrides": [
        {
          "files": [
            "*.json",
            ".babelrc"
          ],
          "options": {
            "parser": "json-stringify"
          }
        },
        {
          "files": [
            "*.jsonc",
            "tsconfig*.json"
          ],
          "options": {
            "parser": "json"
          }
        },
        {
          "files": [
            "*.js",
            "*.cjs",
            "*.mjs"
          ],
          "options": {
            "parser": "babel"
          }
        },
        {
          "files": [
            "*.ts"
          ],
          "options": {
            "parser": "typescript"
          }
        }
      ]
    }
    

    Angular CLI 透過 ng new 產生的專案範本,其 .editorconfig 檔案中的 indent_size 設定值預設為 2

    請注意: 修改 .editorconfig 檔案設定之後,編輯器不一定會立刻生效 (通常會),當你遇到靈異事件的時候,請記得「重開治百病」這句至理名言,將編輯器重開即可!

  7. 調整 Visual Studio Code 的工作區設定

    因為 Visual Studio Code 除了會參考 .editorconfig 編輯器設定外,還有不同程式語言就會有不同的 Formatter 擴充套件,而且不同 Formatter 的格式化差異可能很大,VSCode 本來就內建許多語言的 Formatter,但使用者還可以自行加裝不同的 Formatter 擴充套件,所以開發者的 Formatter 可能會非常混亂!

    為了要讓團隊的開發風格趨於一致,也希望團隊成員不要花任何心思在排版上,所以 Visual Studio Code 的設定其實是非常重要的。

    我們可以在專案目錄下建立 .vscode/settings.json 檔案,讓所有人在「儲存」的時候就自動用我們指定的 Prettier Formatter 排版好。不過,就算使用者沒有設定,由於我們也設定了 pre-commit hook,所以最終在 git commit 的時候,程式碼還是會自動格式化的!但我們真正希望的是,不要有太多的意外,不要有太多的靈異事件,最好我們在編輯器中看到的程式碼,就是最終 Prettier 幫我格式化過後的格式!

    請設定以下 .vscode/settings.json 檔案內容:

    {
      "[typescript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      },
      "[javascript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      },
      "[html]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      },
      "[json]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      },
      "[jsonc]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
      },
      "editor.formatOnSave": true
    }
    

    再建立一個 .vscode/extensions.json 檔案,建議成員一定要安裝 EditorConfigPrettier - Code formatter 擴充套件:

    {
        "recommendations": [
            "angular.ng-template",
            "EditorConfig.EditorConfig",
            "esbenp.prettier-vscode"
        ]
    }
    
  8. 將修改加入 Git 版控

    走到這一步已經全部設定完成,所以從現在開始 git commit 就會開始套用 Prettier 的設定,並自動將已加入 Git Staged 的原始碼進行格式化並加入版控。

    git commit
    

    不過,若你想讓特定版本跳過 Git 的 pre-commit hook 的話,可以考慮用以下命令直接將現有版本加入版控:

    git commit --no-verify
    

檢查所有原始碼的排版後風格

在正式執行 git commit 自動排版之前,我們可以透過以下命令,先手動執行看看感覺,驗證一下排版過的格式是否滿意。

  1. 使用 Prettier 對整個專案所有原始碼進行檢查

    只要有出現 [warn] 字樣,就代表該檔案會被格式化,但不會真的寫入:

    npx prettier --check .
    
  2. 使用 Prettier 對整個專案所有原始碼進行格式化

    底下命令則會將原始碼格式化之後寫入檔案,你就可以看出這些檔案的排版風格:

    npx prettier --write .
    

    我會建議啟用 husky + pretty-quick 之後的第一個 Git 版本,可以用 npx prettier --write . 先進行一次格式排版,讓團隊都基於相同的基礎下進行版控,如此一來可以減少日後版本追蹤上的困擾。

    但是如果你的程式碼很多,團隊也不小,專案也尚未進入穩定發展的狀態,這代表團隊成員還在積極開發中,這樣我就會建議可以跳過這個步驟,否則突然的變動可能會造成數千支檔案同時調整過,這樣不但會增加衝突發生的機率,同時也難以透過 git diff 比對排版之前排版之後的版本變更。

  3. 使用 pretty-quick 對已加入 Git Staged 的原始碼進行檢查

    如果透過以下命令檢查出有檔案尚未符合格式要求,該命令的 Exit Code 為 1,這很適合在 CI 階段的時候檢查格式是否符合標準,也可以查出有成員沒有正確設定 husky + pretty-quick 等工具。

    npx pretty-quick --check
    
  4. 使用 pretty-quick 對已加入 Git Staged 的原始碼進行格式化

    以下命令則會直接對檔案進行格式化,並自動寫入格式化後的版本,但不會自動幫你 git add 加入暫存的變更 (Staged)

    npx pretty-quick --bail
    

後記

要搞清楚 Visual Studio Code 的 Formatter, EditorConfig, Prettier, pretty-quick, husky, Git Hooks 彼此之間的連動關係,除了要經年累月的背景知識加持,還要對每套工具的正確理解,才有辦法真正釐清整個來龍去脈。另一方面,這些不同工具之間都有各種不同的使用情境,如果再加上工具新舊版本的差異,可以想見網路上可以找到的文章非常的混亂,可能以前適用的設定,到今日就不適合了。所以看別人的部落格文章時,絕對不能照抄設定,一定要理解消化之後才能正確使用。

我之前有很長一段時間覺得 Prettier 很難用,而且跟 Visual Studio Code 內建的格式化風格差別很大,常常在 VSCode 格式化之後,透過 Prettier 又格式化成另一種格式,覺得非常麻煩。今天整理這篇文章,算是完整解決了我多年來的困擾,非常開心啊! 😄

現在我們公司成長到快 50 人了,團隊的開發效率變成一件非常重要的事情,你很難想像我花多少時間整理這些資訊,但也唯有這樣的研究,才能讓團隊成員更輕鬆無腦的參與團隊協作,所以花這些時間我覺得蠻值得的!🙂

相關連結

留言評論