Rush Stack商店部落格活動
跳至主要內容

新增更多工作

本節繼續從 Hello, world 教學中延伸的專案。

Heft 的 架構 架構圍繞著外掛程式套件而設計。Heft 會附帶一系列 官方外掛程式套件,可用於常見的建置工作。其原始碼可在 rushstack/heft-plugins 中找到,如果您想建立自己的 Heft 外掛程式,這裡會是很好的參考來源。

緊接著我們的教學,讓我們啟用兩個最常見的外掛程式:單元測試的 Jest 和風格檢查的 ESlint

為專案加入單元測試

  1. 首先,我們需要為 Jest 安裝 TypeScript 建構型別。這些步驟會自 Hello, world 教學中的my-app 專案繼續執行。請注意,此專案目前尚未使用 Rush,因此我們會直接呼叫 PNPM,將相關依賴性新增至我們的 package.json 檔案(而非使用 rush add

    cd my-app

    # Because @types packages don't follow SemVer, it's a good idea to use --save-exact
    pnpm install --save-dev --save-exact @types/heft-jest
    pnpm install --save-dev @rushstack/heft-jest-plugin
  2. 新增一個 "test" 區段至您的 Heft 設定檔,產生下列結果

    config/heft.json

    {
    "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json",

    "phasesByName": {
    // Define a phase whose name is "build"
    "build": {
    "phaseDescription": "This phase compiles the project source code.",

    // Before invoking the compiler, delete the "dist" and "lib" folders
    "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }],

    "tasksByName": {
    // Define a task whose name is "typescript"
    "typescript": {
    "taskPlugin": {
    // This task will invoke the TypeScript plugin
    "pluginPackage": "@rushstack/heft-typescript-plugin"
    }
    }
    }
    },

    // Define a phase whose name is "test"
    "test": {
    "phaseDescription": "This phase runs the project's unit tests.",

    // This phase requires the "build" phase to be run first
    "phaseDependencies": ["build"],

    "tasksByName": {
    // Define a task whose name is "jest"
    "jest": {
    "taskPlugin": {
    // This task will invoke the Jest plugin
    "pluginPackage": "@rushstack/heft-jest-plugin"
    }
    }
    }
    }
    }
    }

    如需這些設定的完整說明,請參閱 heft.json 範本。

    如果您執行 heft --help,現在應該會看到 testtest-watch 指令列動作,因為我們的第二階段命名為 "test"

  3. 由於 Jest 的 API 包含全域變數,因此我們需要在全域載入它們(而大多數其他 @types 套件是透過來源程式碼中的 import 陳述式載入的)。將您的 tsconfig.json 檔案更新為 "types": ["heft-jest", "node"],而不要只是 "types": ["node"]。結果應該如下所示:

    my-app/tsconfig.json

    {
    "$schema": "http://json.schemastore.org/tsconfig",

    "compilerOptions": {
    "outDir": "lib",
    "rootDirs": ["src/"],

    "forceConsistentCasingInFileNames": true,
    "declaration": true,
    "sourceMap": true,
    "declarationMap": true,
    "inlineSources": true,
    "experimentalDecorators": true,
    "strict": true,
    "useUnknownInCatchVariables": false,
    "esModuleInterop": true,
    "noEmitOnError": false,
    "allowUnreachableCode": false,

    "types": ["heft-jest", "node"],
    "module": "commonjs",
    "target": "es2017",
    "lib": ["es2017"]
    },
    "include": ["src/**/*.ts"],
    "exclude": ["node_modules", "lib"]
    }
  4. 接著,我們需要新增 jest.config.json 設定檔。這個檔案的存在會導致 Heft 呼叫 Jest 測試執行器。Heft 預期特定檔案路徑為 config/jest.config.json。在大多數情況下,您的 Jest 設定只要延伸 Heft 的標準預設,如下所示即可

    my-app/config/jest.config.json

    {
    "extends": "@rushstack/heft-jest-plugin/includes/jest-shared.config.json",
    "collectCoverage": true,
    "coverageThreshold": {
    "global": {
    "branches": 50,
    "functions": 50,
    "lines": 50,
    "statements": 50
    }
    }
    }

    注意:對於網頁專案,您可能想要使用 @rushstack/heft-jest-plugin/includes/jest-web.config.json 而不是 jest-shared.config.json,以支援 lib-commonjslib 資料夾等雙重輸出。深入了解,請查看 Jest 外掛程式 文件。

  5. 現在我們需要新增單元測試。Jest 支援很多功能,但本教學中我們會建立一個簡單的測試檔。.test.ts 檔案擴充功能讓 Heft 在這個檔案中尋找單元測試

    my-app/src/example.test.ts

    describe('Example Test', () => {
    it('correctly runs a test', () => {
    expect(true).toBeTruthy();
    });
    });
  6. 要執行測試,我們需要使用 heft test 動作,因為 heft build 一般會略過測試,以加快開發進度。

    # View the command line help
    heft test --help

    # Build the project and run tests
    heft test --verbose

    # Run Jest in watch mode
    heft test-watch

    天哪,heft test --help 有很多命令列參數!這些參數是從哪裡來的?它們是由 Jest 外掛程式的 heft外掛程式.json 清單檔案新增的,因為我們在工作階段載入了那個外掛程式。

    (如果兩個不同的外掛程式定義了相同的命令列參數,會發生什麼事?Heft 包含一個洗鍊的消除歧義機制,例如,如果您使用其他外掛程式也定義了 --update-snapshots 參數,您可以使用 --jest:update-snapshots 而不是 --update-snapshots。)

  7. 我們應該更新我們的 package.json 指令碼,如此一來 pnpm run test 便會執行 Jest 測試

    my-app/package.json

    {
    . . .
    "scripts": {
    "build": "heft build --clean",
    "test": "heft test --clean",
    "start": "node lib/start.js"
    },
    . . .
    }

注意:不要直接呼叫 jest 命令列。這樣做會執行它在 lib/**/*.js 中找到的測試,但不會呼叫 Heft 的其他任務來更新那些輸出檔案。

設定 Jest 就完成啦!更多資訊,包括偵錯測試的說明,請參考 Jest 外掛程式 簡介和 heft-node-jest-tutorial 範例專案。

啟用程式碼檢查

  1. 為了確保最佳實務做法並找出常見錯誤,我們還要啟用 @rushstack/eslint-config 標準規則集。首先,我們需要在 package.json 檔案中新增更多 NPM 相依性。

    cd my-app

    # Add the ESLint engine
    pnpm install --save-dev eslint

    # Add Heft's plugin for ESLint
    pnpm install --save-dev @rushstack/heft-lint-plugin

    # Add Rush Stack's all-in-one lint ruleset
    pnpm install --save-dev @rushstack/eslint-config
  2. 更新您的 Heft 設定檔,新增在 heft build 階段載入 @rushstack/heft-lint-plugin 的任務

    config/heft.json

    {
    "$schema": "https://developer.microsoft.com/json-schemas/heft/v0/heft.schema.json",

    "phasesByName": {
    // Define a phase whose name is "build"
    "build": {
    "phaseDescription": "Compiles the project source code",

    // Before invoking the compiler, delete the "dist" and "lib" folders
    "cleanFiles": [{ "sourcePath": "dist" }, { "sourcePath": "lib" }],

    "tasksByName": {
    // Define a task whose name is "typescript"
    "typescript": {
    "taskPlugin": {
    // This task will invoke the TypeScript plugin
    "pluginPackage": "@rushstack/heft-typescript-plugin"
    }
    },

    // Define a task whose name is "lint"
    "lint": {
    // This task should run after "typescript" has completed
    // because Heft optimizes ESLint by reusing the TypeScript
    // compiler's AST analysis
    "taskDependencies": ["typescript"],
    "taskPlugin": {
    // This task will invoke the ESLint plugin
    "pluginPackage": "@rushstack/heft-lint-plugin"
    }
    }
    }
    },
    // Define a phase whose name is "test"
    "test": {
    // This phase requires the "build" phase to be run first
    "phaseDependencies": ["build"],
    "tasksByName": {
    // Define a task whose name is "jest"
    "jest": {
    "taskPlugin": {
    // This task will invoke the Jest plugin
    "pluginPackage": "@rushstack/heft-jest-plugin"
    }
    }
    }
    }
    }
    }

    如需這些設定的完整說明,請參閱 heft.json 範本。

  3. 接著,建立 .eslintrc.js 設定檔。在本教學中,我們只會使用官方 Rush Stack 規則集

    my-app/.eslintrc.js

    // This is a workaround for https://github.com/eslint/eslint/issues/3458
    require('@rushstack/eslint-config/patch/modern-module-resolution');

    module.exports = {
    extends: ['@rushstack/eslint-config/profile/node'],
    parserOptions: { tsconfigRootDir: __dirname }
    };

    請注意:如果您的專案使用 React 架構,您還應該從 "@rushstack/eslint-config/mixins/react" mixin 延伸。請參閱 文件記錄,以取得關於 @rushstack/eslint-config 的「個人資料」與「mixin」的詳細資訊。

  4. 為進行測試,嘗試更新您的 start.ts 來源檔,以加入一個 linter 議題

    my-app/src/start.ts

    console.log('Hello, world!');

    export function f() {
    // <--- oops
    }

    當您執行 pnpm run build 時,您應該會看到類似這樣的記錄訊息

    -------------------- Finished (3.555s) --------------------
    Encountered 1 warning
    [build:lint] src/start.ts:3:8 - (@typescript-eslint/explicit-function-return-type) Missing return type on function.

    為解決此問題,請修正程式碼,加上遺漏的回傳類型,然後應該就能順利建置

    my-app/src/start.ts

    console.log('Hello, world!');

    export function f(): void {
    // <--- okay
    }
  5. @rushstack/eslint-config 規則組設計為與 Prettier 程式碼格式化程式搭配使用。為設定該程式,請參閱在 Rush 網站上的 啟用 Prettier 一文。

關於 ESLint 的部分就到此結束!更多詳細資訊可於 程式碼檢查擴充功能 參考中找到。