Closed9

heft コマンド

K.InabaK.Inaba

Heft is a config-driven toolchain that invokes other popular tools such as TypeScript, ESLint, Jest, Webpack, and API Extractor. You can use it to build web applications, Node.js services, command-line tools, libraries, and more. Heft builds all your JavaScript projects the same way: A way that works.

DeepL + 意訳

Heft は、TypeScript、ESLint、Jest、Webpack、API Extractor などの有名なツールを呼び出す設定駆動型のツールチェインです。Web アプリケーション、Node.js サービス、コマンドラインツール、ライブラリなどを構築するために使用できます。Heft は、あなたの JavaScript プロジェクトをすべて同じ方法でビルドします。言い換えれば、一つの動作に集約します。

K.InabaK.Inaba

Rush で管理された複数のプロジェクトをまたがって、重複する package.json"build" スクリプトを管理したくないよね
-> heft を使うと共通化できて嬉しい、とのこと

K.InabaK.Inaba

heft コマンドライン
https://rushstack.io/pages/heft/cli/

  • clean
    プロジェクトのビルド成果を破棄する

  • build
    プロジェクトをビルドする

  • start
    プロジェクトのローカルサーバーを起動する

  • test
    プロジェクトをビルドし、テストをする

K.InabaK.Inaba

heft チュートリアル
https://rushstack.io/pages/heft_tutorials/getting_started/

npm install --global pnpm

Rush Stack 全体的に pnpm 推し。
複数プロジェクトにまたがった共通パッケージをシンボリックリンクで配置して、ストレージ効率が良いとかなんとか。

TypeScript プロジェクトを開始にあたって最低限のファイル配置

my-app/package.json
{
  "name": "my-app",
  "version": "1.0.0",
  "description": "A Heft tutorial project",
  "license": "MIT",
  "main": "lib/start.js",
  "typings": "lib/start.d.ts",
  "scripts": {
    "start": "node lib/start.js"
  }
}
my-app/src/start.ts
console.log("Hello, world!");

依存のインストール

pnpm install --save-dev @rushstack/heft
pnpm install --save-dev typescript
# 型定義のインストールは --save-exact オプションを常に付けるように
pnpm install --save-dev --save-exact @types/node

TypeScript の設定

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,
    "esModuleInterop": true,
    "types": ["node"],

    "module": "commonjs",
    "target": "es2017",
    "lib": ["es2017"]
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "lib"]
}

プロジェクトローカルの node_modules にインストールされた heft バイナリでも動作するけど、グローバルインストールしておくのが吉。
ローカルとグローバルでバージョンが異なっても、適切な方を勝手に選んで実行してくれるらしい。

pnpm install --global @rushstack/heft

heft コマンドでビルド

heft build

走らす

pnpm run start

# rush が導入済みなら
rushx start
# も OK
# だけどこのチュートリアルでは未導入のはずなので覚えておくだけ

NPM スクリプトに "build" を足しておくと、後で rush との連携に効くらしい。

my-app/package.json
{
  . . .
  "scripts": {
    "build": "heft build --clean",
    "start": "node lib/start.js"
  },
  . . .
}

heft clean コマンドが適切にビルド成果を削除できるように、以下の設定を済ます。

my-app/config/heft.json
/**
 * Defines configuration used by core Heft.
 */
{
  "$schema": "https://developer.microsoft.com/json-schemas/heft/heft.schema.json",

  "eventActions": [
    {
      /**
       * The kind of built-in operation that should be performed.
       * The "deleteGlobs" action deletes files or folders that match the
       * specified glob patterns.
       */
      "actionKind": "deleteGlobs",

      /**
       * The stage of the Heft run during which this action should occur. Note that actions specified in heft.json
       * occur at the end of the stage of the Heft run.
       */
      "heftEvent": "clean",

      /**
       * A user-defined tag whose purpose is to allow configs to replace/delete handlers that were added by other
       * configs.
       */
      "actionId": "defaultClean",

      /**
       * Glob patterns to be deleted. The paths are resolved relative to the project folder.
       */
      "globsToDelete": ["dist", "lib", "temp"]
    }
  ],

  /**
   * The list of Heft plugins to be loaded.
   */
  "heftPlugins": [
    // {
    //  /**
    //   * The path to the plugin package.
    //   */
    //  "plugin": "path/to/my-plugin",
    //
    //  /**
    //   * An optional object that provides additional settings that may be defined by the plugin.
    //   */
    //  // "options": { }
    // }
  ]
}
K.InabaK.Inaba

やたらコメント付き JSON が出てくるので、VSCode を使っている場合は .json の関連を変えておいたほうが楽かもしれない

setting.json
{
    . . .
    "files.associations": {
        "*.json": "jsonc"
    },
    . . .
}
K.InabaK.Inaba

Heft から Jest ユニットテスト、ESLint を呼ぶhttps://rushstack.io/pages/heft_tutorials/adding_tasks/

Jest

型定義インストール

# 型定義のときは --save-exact オプション付き
pnpm install --save-dev --save-exact @types/heft-jest

型定義を足したときは tsconfig.json も直す

my-app/tsconfig.json
{
    . . .
    "types": ["heft-jest", "node"],
    . . .
}

Jest 呼び出しの設定
たいていは標準プリセットを呼ぶだけ

my-app/config/jest.config.json
{
  "preset": "./node_modules/@rushstack/heft/includes/jest-shared.config.json"
}

テストを書く

my-app/src/example.test.ts
describe('Example Test', () => {
  it('correctly runs a test', () => {
    expect(true).toBeTruthy();
  });
});

実行

heft test

heft test コマンドは、テスト前にビルドもしてくれるので、package.json"build" コマンドを heft build から差し替えておくと、ビルドのたびにテストを通してくれてステキ。

my-app/package.json
{
  . . .
  "scripts": {
    "build": "heft test --clean", // --clean オプション ビルド成果を破棄して新規ビルド
    "start": "node lib/start.js"
  },
  . . .
}

ESLint

pnpm install --save-dev eslint
pnpm install --save-dev @rushstack/eslint-config
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"extends に含める。@rushstack/eslint-configの "profile "と "mixins "の詳細についてはドキュメントを見る

例えば start.ts に、返り値の型を指定しない空の関数を作ると、

my-app/src/start.ts
console.log('Hello, world!');

export function f() {
  // <--- あらら
}

pnpm run build ( = heft test --clean ) の中で Linting されて警告が吐かれる

. . .
---- Compile started ----
[copy-static-assets] Copied 0 static assets in 0ms
[typescript] Using TypeScript version 3.9.7
[eslint] Using ESLint version 7.5.0
[eslint] Encountered 1 ESLint error:
[eslint]   ERROR: src\start.ts:3:8 - (@typescript-eslint/explicit-function-return-type) Missing return type on function.
. . .

返り値の型を正しく void に指定すると

my-app/src/start.ts
console.log('Hello, world!');

export function f(): void {
  // <--- 勝ったなこれ
}

pnpm run build の警告が消える。

K.InabaK.Inaba

Heft のツールチェーンをカスタマイズして、その上で NPM スクリプト "build"で呼ぶコマンドを heft * に統一すれば、パッケージを跨いだ共通処理にできてラクチンよってことか。

K.InabaK.Inaba

https://rushstack.io/pages/heft/rig_packages/
更にここまでの設定を "rig package" なる NPM パッケージにまとめて、それを各プロジェクトで extends して使うことで、"rig package" 定義の heft の動作をリポジトリ全体に伝播できるし、プロジェクトローカルで設定のオーバーライドもできてオイシイと。

このスクラップは2022/04/27にクローズされました