【備忘録】Node.js×pnpm×TypeScript

# はじめに

Vue CLIやviteを利用すると設定ファイルを一通り用意してくれてしまうので、学習のためにイチから各種ライブラリを導入して作成してみた。
またpnpmも初めて使ってみた。

記事作成時点での最新情報を盛り込んだつもりだがNodeやTSまわりの技術は日進月歩なので古い（または古くなった）情報があったらすみません。

### 開発環境
- macOS Ventura 13.1
- Visual Studio Code 1.74.3
- Node.js 18.13.0

### 事前準備

#### pnpm有効化

https://pnpm.io/ja/installation#corepack%E3%81%AE%E4%BD%BF%E7%94%A8

最近のNode.jsにはpnpmがバンドルされているので有効化だけすればよい。
※ドキュメントにはないが自分の環境では`sudo`がないとPermission Deniedになった

```
sudo corepack enable
```

有効化確認＆バージョン確認

```
pnpm -v
```
>7.25.0

# 作成手順
## プロジェクト定義
### Nodeプロジェクト初期化

```
pnpm init
```

### TypeScript導入

https://www.typescriptlang.org/download

```
pnpm add typescript -D
```

有効化確認＆バージョン確認

```
pnpm tsc -v
```
>Version 4.9.4

### tsconfig.json作成

https://typescriptbook.jp/reference/tsconfig/tsconfig.json-settings

https://www.typescriptlang.org/tsconfig

雛形となるtsconfig.jsonを生成

```
pnpm tsc --init
```

参考サイトを参考に一部修正

```diff_json:tsconfig.json
  {
+   "include": ["src/**/*"],
    "compilerOptions": {
-     "target": "es2016",
+     "target": "es2022",

-     // "rootDir": "./",
+     "rootDir": "./src",

-     // "outDir": "./",
+     "outDir": "./dist",
    }
  }
```

この時点ではソースコードが見つからないためVSCodeがエラーを出してくるが気にしない。

## Hello, world
### Node標準ライブラリ型定義
確認のためNode標準ライブラリをimportしたいので型定義を落としておく。
```
pnpm add @types/node -D
```

### サンプル実装

※後でコード整形する前提でちょっと汚く書いている

```typescript:src/greet.ts
export const getHello = (name:string):string=>{
    return `Hello, ${name}!`
}
```

```typescript:src/index.ts
import { version } from 'node:process' // Node標準ライブラリ
import { getHello } from './greet' // 自分のモジュール

console.log(version)
console.log(getHello('world'))
```

## 実行
### コンパイル
tsconfig.jsonを利用してコンパイルを実行。
```
pnpm tsc --project ./tsconfig.json 
```
tsconfig.jsonに記述した通り、`dist`ディレクトリに出力される。

### Node.js上で実行

```
node dist/index.js 
```
>v18.13.0
Hello, world!

## ts-node

いちいちコンパイル→実行の２段階は面倒なのでts-nodeを導入してtsファイルを直接実行する。

### ts-node導入

https://www.wakuwakubank.com/posts/726-typescript-ts-node/

```
pnpm add ts-node -D
```

導入確認＆バージョン確認

```
pnpm ts-node -v
```
>v10.9.1


### スクリプト化

```diff_json:package.json
  {
    "scripts": {
+     "dev": "ts-node src/index.ts",
    }
  }
```

スクリプトを実行

```
pnpm run dev
```

>\> vsc-pnpm-ts-demo@1.0.0 dev /Users/kurukuruz/work/repo/vsc-pnpm-ts-demo
>\> ts-node src/index.ts
>
>v18.13.0
>Hello, world!

## ESLint & Prettier

https://zenn.dev/kohski/articles/eslint-prettier-integration

### Prettier導入

```
pnpm add prettier -D
```

空の設定ファイルも作っておく。

```
echo {}> .prettierrc.json
```

### ESLint導入

```
pnpm add eslint -D
```

対話型CLIを利用して設定を定義する。

```
pnpm eslint --init
```

>✔ How would you like to use ESLint? · problems
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · none
✔ Does your project use TypeScript? · Yes
✔ Where does your code run? · node
✔ What format do you want your config file to be in? · JSON
The config that you've selected requires the following dependencies:
>
>@typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
✔ Would you like to install them now? · Yes
✔ Which package manager do you want to use? · pnpm

### ESLint-Prettier連携

（記事作成時点の）最新はconfigを使うのが推奨らしい。

```
pnpm add eslint-config-prettier -D
```

`extends`の**末尾**に追加

```diff_json:.eslintrc.json
  {
    "extends": [
-     "plugin:@typescript-eslint/recommended"
+     "plugin:@typescript-eslint/recommended",
+     "prettier"
    ],
  }
```

### スクリプト化

有無を言わさず書き換えを伴うストロングスタイル。

```diff_json:package.json
  {
    "scripts": {
+     "lint": "eslint --fix src/ && prettier --write src/",
    }
  }
```

スクリプトを実行

```
pnpm run lint
```

今回は下記のように修正された。

- 適切なスペース文字挿入
- インデント幅調整
- 末尾にセミコロン
    ```diff_typescript:src/greet.ts
    - export const getHello = (name:string):string=>{
    -     return `Hello, ${name}!`
    - }
    + export const getHello = (name: string): string => {
    +   return `Hello, ${name}!`;
    + };
    ```
- 文字列の囲いをダブルクォーテーション化
    ```diff_typescript:src/index.ts
    - import { version } from 'node:process' // Node標準ライブラリ
    - import { getHello } from './greet' // 自分のモジュール
    + import { version } from "node:process"; // Node標準ライブラリ
    + import { getHello } from "./greet"; // 自分のモジュール

    - console.log(version)
    - console.log(getHello('world'))
    + console.log(version);
    + console.log(getHello("world"));
    ```

宗派は色々あると思うが、ここでは深く考えずデフォルトのままにしておく。

## Jest
### Jest導入

https://jestjs.io/ja/docs/getting-started

https://kulshekhar.github.io/ts-jest/docs/getting-started/installation

Jest本体だけでなく、TypeScriptで使用するためのライブラリも同時に追加

```
pnpm add jest ts-jest @types/jest -D
```

CLIを利用して設定ファイルを生成

```
pnpm ts-jest config:init
```

### テストコードを作成

Jestにデフォルトでテストコードとみなされる`__tests__`ディレクトリ配下に作成する。

```typescript:__tests__/greet.ts
import { getHello } from "../src/greet";

// @types/jestによりJest関連の関数（testなど）はimportなしで利用できる
test("hello hoge", () =>{
  expect(getHello("hoge")).toBe("Hello,hoge!") // ここはわざと失敗するようにしてる
});
```

### スクリプト化

```diff_json:package.json
  {
    "scripts": {
-     "test": "echo \"Error: no test specified\" && exit 1"
+     "test": "jest"
    }
  }
```

スクリプトを実行

```
pnpm run test
```

![スクリーンショット 2023-01-16 18.17.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266076/5f580ec2-9dd8-e7bd-7a10-800319c74573.png)

テストコードが実行され、ご丁寧に失敗箇所・理由を出力してくれることが確認できる。

# 成果物

https://github.com/kurukuruz/vsc-pnpm-ts-demo