10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TypeScriptのコード品質を高めるnpmパッケージ構成を試した(ESLint+Prettier+CSpell+npm-run-all+VSCode)

Last updated at Posted at 2023-03-03

概要

TypeScriptの開発環境を整備していくうえで、ある程度必要そうなチェックが楽にできる環境が出来上がったので記事にします。

環境

  • VSCode
  • TypeScript
  • Node(npm利用のため)
  • npm(yarnでも可)

利用する拡張機能・パッケージの紹介

名称 説明
ESLint 静的コード分析ツール
Prettier 自動フォーマットツール
CSpell 打ち間違い検出ツール
npm-run-all 複数のnpm scriptをまとめて実行

ESLint

javasciptの開発を行う際に、ソースコードが決められた構文に沿っているかをチェックするためのツールとして提供されています。
(このようなプログラムを静的に解析して問題点を発見するツールをリンター Linterと呼びます)
ECMAScriptというJavaScriptの標準仕様は年1のペースでバージョンが上がっているため最新の仕様に対応できているかを人の目だけで監視するのは困難です。
また、チーム内で定めたコーディング規約についても命名規則が合っているかをコードレビュー内で確認するのは手間がかかります。
ESLintはこれらの悩みを解決します。

Prettier

綺麗にフォーマットされたコードは可読性を高め、扱いやすいソースコードとなります。
また、複数人で開発する際のフォーマットの仕方を統一することでフォーマットずれによる差分の抑制となります。

CSpell

キーボードでの英単語の打ち間違いの事をタイポ(typo)とも呼びます。
ソースコード上の変数名や関数名のタイポはエンドユーザーに見える部分ではありませんが、それでもシステムの保守性を高めるならタイポ無しを目指すべきです。
また、APIの項目名やテーブルの列名などはタイポが原因でエラーが発生する事もあります。
それに対しエラーが発生した際に気づくよりは打ち間違いをしたタイミングで気づけるのが好ましいです。

npm-run-all

npmにはnpmスクリプトという、CLIで実行するようなコマンドを定義できる機能があります。
例えばビルドコマンドやテストの実行などをコマンドで行う場合に、npmスクリプト機能を用いれば毎回同じコマンドを打ち直す必要は無くなります。
npm run [scriptname]
の形式で実行することが可能です。

但しデフォルトのままでは1個のnpm run [scriptname]で実行できるコマンドは1個なのでシェルスクリプトなどを作成しそれを呼び出すといった手間が発生します。
npm-run-allはnpmスクリプトの仕組みの中で複数のコマンドを実行するための仕組みが備わっています。

ゴール

コマンド1つで

  • JavaScriptやTypeScriptの規約チェック
  • プロジェクト内のコーディング規約チェック
  • スペルチェック
  • TypeScriptファイルのコンパイルによるエラーチェック
  • デバック開始

までが行えて、かつ開発時に随時フォーマットと規約チェックが行われる状態を目指します。

最終的なプロジェクトの中身は以下の画像の通りになります。
image.png

構築手順

  1. プロジェクト用のフォルダ作成
  2. package.jsonの生成
  3. npmパッケージのインストール
  4. ESLint設定ファイルの作成
  5. Prettier設定ファイルの作成
  6. CSpell用ファイルの作成
  7. VSCodeの設定ファイルの作成
  8. package.jsonにnpmスクリプトを定義
  9. 動作確認

プロジェクト用のフォルダ作成

まず.vscodeフォルダを直下に作成します。
この中には現在開いているプロジェクト内にのみ影響のある設定を残すことが出来ます。
VSCodeの設定はユーザ単位での設定とプロジェクトごとの設定の2種類があり、同じ設定項目に対しユーザごとの設定とプロジェクトに対する設定の2種類が存在する場合は、プロジェクトの設定が優先されたはずです。
そのためチームで開発する際にVSCode関連の設定の統制を取りたい場合はこのフォルダごとソース管理をする事で可能となります。

次にsrcフォルダを作成します。
ここは名前の通りソースコードを格納するフォルダです。また、__test__というフォルダを作成していますがこちらはJestなどでテストをする際のテストコードを格納するフォルダとなっています。

image.png

VSCode拡張機能のインストール

設定は後ほど行いますので、まずは以下3つの拡張機能をインストールまででお願いいたします。

ESLint

Prettier

Code Spell Checker

package.jsonの作成

npmは導入完了しているものとして進めます。

npm initコマンドを実行したら後はひたすらエンターを押して、最後の確認でyesと打てばpackage.jsonが出来上がります。

$ npm init
...
package name: (typescript_project) 
version: (1.0.0)                                                                                                                                                                                      
description:                                                                                                                                                                                          
entry point: (index.js)                                                                                                                                                                               
test command:                                                                                                                                                                                         
git repository:                                                                                                                                                                                       
keywords:                                                                                                                                                                                             
author:                                                                                                                                                                                               
license: (ISC)                                                                                                                                                                                        
About to write to C:...\typescript_project\package.json:

{
  "name": "typescript_project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes) yes

image.png

mainの項目がindex.jsになっているなど直すところはありますが、一旦先に進みます。

npmパッケージのインストール

コマンド一覧

TypeScript本体

npm install typescript

ESLint本体+TypeScript検証用パッケージ

npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

Prettier本体+ESLintとPrettierを連携させるためのパッケージ

npm install -D prettier eslint-config-prettier

CSpell本体

npm install -D cspell

npm-run-all本体

npm install -D npm-run-all

加えてお好みでJestを入れたりTypeScriptで利用するフレームワークを入れたりしてください。

ESLint設定ファイルの作成

直下に.eslintrc.jsonファイルを作成します。
今回は

  • ES2022の規約に基づいたチェック
  • 変数名などの命名規則のチェック

をESLintで行います。
全ての設定の説明は省略します。

eslintrc.json
{
  "root": true,
  "env": {
    "browser": true,
    "es2022": true
  },
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 2022
  },
  "plugins": ["@typescript-eslint"],
  "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
  "rules": {
    "@typescript-eslint/naming-convention": [
      "error",
      {
        "selector": "variable",
        "modifiers": ["const"],
        "format": ["strictCamelCase", "UPPER_CASE"]
      },
      {
        "selector": "variable",
        "format": ["strictCamelCase"]
      },
      {
        "selector": "function",
        "format": ["strictCamelCase"],
        "leadingUnderscore": "allow"
      },
      {
        "selector": "parameter",
        "format": ["camelCase"]
      },
      {
        "selector": "class",
        "format": ["StrictPascalCase"]
      },
      {
        "selector": "accessor",
        "format": ["strictCamelCase"]
      },
      {
        "selector": "typeAlias",
        "format": ["StrictPascalCase", "UPPER_CASE"]
      },
      {
        "selector": "method",
        "format": ["strictCamelCase"]
      },
      {
        "selector": "interface",
        "format": ["StrictPascalCase"]
      }
    ]
  }
}

extendsに設定している"eslint:recommended", "plugin:@typescript-eslint/recommended"
ESLintが推奨している規約チェックを行うといった設定内容になっています。
また、rulesには個別のルールが設定できるので、その中の一つである命名規則関連のルールを細かく定義しています。
詳しくは公式ドキュメントなどをご参照ください。

Prettier設定ファイルの作成

直下に.prettierrc.jsonファイルを作成します。
作成が出来たら以下公式ドキュメントなどを参考に適用したいフォーマットの設定を記述します。
色々調べて考えた結果

  • printWidthは120程度、但しPrettierの仕様的に改行がいまいちと感じる場合はもっと長めに設定する
  • インデントはデフォルトと同じスペース2文字
  • デフォルトはダブルクォーテーションだがシングルクォーテーションの方が派閥が多い
  • 改行コードはLinuxかWIndowsかで違うところで、かつデフォルトはLFなのでWindowsユーザはCRLFにする
  • それ以外はデフォルトでも良さそう

という結論に至りました。

prettierrc.json
{
  "printWidth": 120,
  "singleQuote": true,
  "endOfLine": "crlf",
  "overrides": [
    {
      "files": "*.vue",
      "options": {
        "printWidth": 240
      }
    }
  ]
}

overridesを用いるとファイルの形式ごとに設定を分けることが出来ます。
この書き方の場合vueコンポーネントではprintWidthが240となります。
Vueの場合template部分にHTMLとかを書く関係で120でも短いと感じたため、値を倍にしています。

CSpell用ファイルの作成

.vscodeフォルダ配下にcspell.jsonファイルを作成します。
例えばEnshu(遠州)のような固有名詞が出た場合、もちろんこれは英単語ではないのでスペルチェックに引っかかります。
しかしこれはスペルミスではない為辞書に登録することでエラーを回避します。
cspell.jsonファイルはこの辞書を管理する役目を果たします。

例えばこのような変数があったとして、青線の部分がタイポではないかと指摘されています。
image.png

青線にカーソルを当てるとクイックフィックスが選択可能なので、その中から「Enshuをcspell.jsonに追加」を選びます。
image.png

エラーが消えます。
image.png

ユーザー側の辞書ではなくプロジェクトごとのフォルダにワードを登録するメリットとしては、これもまたチーム内で同じ辞書を使うことによる統一ができることとなります。
反面、複数のプロジェクトがある場合に何度も同じワードを辞書に登録する必要がありますが、cspell.jsonの中身をコピーすればその手間は省けます。
image.png

TypeScriptコンパイル用のファイル作成

npx tsc --initコマンドでtsconfig.jsonファイルを作成します。

$ npx tsc --init

Created a new tsconfig.json with:                                                                                       
                                                                                                                     TS 
  target: es2016
  module: commonjs
  strict: true
  esModuleInterop: true
  skipLibCheck: true
  forceConsistentCasingInFileNames: true


You can learn more at https://aka.ms/tsconfig

完了するとやけにコメントアウトがたくさんな設定ファイルが直下に出来上がります。
本来であれば作成するプロジェクトごとに必要な項目を精査して設定ファイルを作りますが今回は割愛します。
strictを指定して厳格なチェックを行うのが一般的で、targetやmoduleの指定をどうするかをちゃんと考えるのが大事です。
※targetはコンパイル成功時にどのバージョンのjavascriptで出力するかを示す設定値となります。

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,

    "strict": true ,
    "skipLibCheck": true ,

    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["src/*.ts", "src/**/*.ts", "src/**/*.d.ts"]
}

VSCodeの設定ファイルの作成

.vscodeフォルダ配下にsetting.jsonファイルを作成します。
ここには主にESLintやPrettierをファイル保存時に実行できるような設定を記述します。
下の方にあるインデントをスペース2個に強制する設定は恐らく書かなくてもPrettierで自動フォーマットしてくれるので問題ないです。
editor.formatOnSaveがセーブ時にフォーマットを自動実行
editor.formatOnPasteがテキストの貼り付け時にフォーマットを自動実行
editor.formatOnTypeが文字入力を行った行のフォーマットを自動実行
の設定項目なので全てTrueにし、都度Prettierによるフォーマットが行われるようにしています。
また、editor.codeActionsOnSaveではESLintによるチェックをセーブのたびに行えるように設定しています。

setting.json
{

  "editor.formatOnSave": true, 
  "editor.formatOnPaste": true, 
  "editor.formatOnType": true, 
  "editor.defaultFormatter": "esbenp.prettier-vscode", 
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true 
  },
  "editor.insertSpaces": true,
  "editor.indentSize": 2
}

package.jsonにnpmスクリプトを定義

現在はpackage.jsonにscriptsという項目に

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  }

このような内容が入っていると思います。
これを

"scripts": {
    "pipeline:debug": "run-s cspell lint:report compile debug",
    "pipeline:build": "run-s cspell lint:report compile build",
    "compile": "tsc",
    "cspell": "cspell src/**/*",
    "lint": "eslint src",
    "lint:report": "eslint src --format=html > eslint_result.html",
    "debug": "echo debug start!!",
    "build": "echo build success!!"
  }

に書き換えてください。
pipeline:debugはデバッグ実行する際に実行するコマンド
pipeline:buildはビルドする際に実行するコマンドを想定しています。
デバッグ実行やビルドを行う前に

  1. スペルチェック
  2. ESLintによるチェック
  3. コンパイルによるエラーチェック

を行い、途中でエラーが発生したら終了する仕組みにしています。
pipeline:○○のコマンドの中にrun-sとありますが、これがnpm-run-allの機能の1つです。
run-sの後ろに指定したnpmスクリプトを順番に実行してくれます。
こうすることで開発者はnpm run pipeline:debugnpm run pipeline:buildを使うことさえ意識すれば開発時にチェックしてもらいたい諸々をツールたちが自動で確認して問題ないと判断したうえでデバッグ実行やビルドを行うフローが出来上がります。
なお、ESLintはWARNINGレベル以下のみの場合はエラーとして途中終了しない為、WARNINGレベルで必ず対応してほしいチェック項目が存在する場合はエラーレベルまで引き上げるように.eslintrc.jsonのrulesに記載する必要があります。

設定完了後の再起動

ここまで終わったらVSCodeを再起動してください。

動作確認

①打ち間違いが存在する場合
拡張機能によりPROBLEMSにお知らせしてくれます。
image.png

対応せずにデバッグ実行を実施した場合はエラーとして弾かれて途中で終了します。

$ npm run pipeline:debug

> typescript_project@1.0.0 pipeline:debug
> run-s cspell lint:report test compile debug


> typescript_project@1.0.0 cspell
> cspell src/**/*

1/2 .\src\__test__\hogehoge.spec.ts 655.99ms
2/2 .\src\hogehoge.ts 33.58ms X
C:...\typescript_project\typescript_project\src\hogehoge.ts:1:11 - Unknown word (Hoge)
CSpell: Files checked: 2, Issues found: 1 in 1 files
ERROR: "cspell" exited with 1.

②ES2022の規約に沿っていなかったり、命名規則が間違っている場合
試しにvarを使用したり、本来キャメルケースまたはアッパーケースの変数名をパスカルケースで記述すると問題の部分にエラーとして表示されます。
警告の方は、用意した変数が他の処理で全く使われていないので警告として出ています。

image.png

ESLintの機能だと思われますが、保存するときにvarはconstに変更されました。

image.png

エラーをすべて対応せずにデバッグ実行を実施した場合はエラーとして弾かれて途中で終了します。

$ npm run pipeline:debug

> typescript_project@1.0.0 pipeline:debug
> run-s cspell lint:report test compile debug


> typescript_project@1.0.0 cspell
> cspell src/**/*

1/2 .\src\__test__\hogehoge.spec.ts 571.36ms
2/2 .\src\hogehoge.ts 18.22ms
CSpell: Files checked: 2, Issues found: 0 in 0 files

> typescript_project@1.0.0 lint:report
> eslint src --format=html > eslint_result.html

ERROR: "lint:report" exited with 1.

ESLintの結果はコンソールに大量に出ると見づらいためHTMLに出力しています。

image.png

③コンパイルエラーが発生する場合

存在しない変数の値を代入しようとしてみました。

image.png

$ npm run pipeline:debug

> typescript_project@1.0.0 pipeline:debug
> run-s cspell lint:report compile debug


> typescript_project@1.0.0 cspell
> cspell src/**/*

1/1 .\src\hogehoge.ts 622.14ms
CSpell: Files checked: 1, Issues found: 0 in 0 files

> typescript_project@1.0.0 lint:report
> eslint src --format=html > eslint_result.html


> typescript_project@1.0.0 compile
> tsc

src/hogehoge.ts:3:19 - error TS2552: Cannot find name 'fileName3'. Did you mean 'fileName'?

3 const fileName2 = fileName3;
                    ~~~~~~~~~

  src/hogehoge.ts:1:7
    1 const fileName = 'test.txt';
            ~~~~~~~~
    'fileName' is declared here.


Found 1 error in src/hogehoge.ts:3

ERROR: "compile" exited with 2.

まとめ

これらのパッケージと拡張機能によりコーディング規約の統一、ミスの事前発見が可能となりました。
また、開発環境のインストール以外はリポジトリを落としてもらえれば簡単に導入可能となっています。
今回はVueやJestなどを使う際の考慮は除いていますが、環境作成のサポートになれば幸いです。

10
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?