🐕

知識0からESLintのFlat Configを導入してみる

2024/08/14に公開

この記事でできる事

  • ESLintの導入〜Flat Configに対応した設定
  • sampleコードを利用したeslintの実行と実行結果の確認

背景

YouTube 動画で ESLint に関して紹介している動画をいくつか見て、
便利さをなんとなく理解した状態で、早速 ESlint を導入してみようと思ったのですが、
どうやら ESLint の v9 から Flat Config なる物がデフォルトになり、
これまでとお作法が変わったということでした。

本記事では Flat Config を採用して、初心者がなんとか基本的な使い方を理解する所までの道のりを記しています。

ESLint とは

https://eslint.org/

JavaScript のコードを解析し、構文エラーや、指定したルールに違反していないかをチェック/修正をしてくれる静的解析ツールです。

静的解析ツールとは

コードを実行せずに解析し、コードの品質や信頼性を検証する手法です。
つまり、プログラムが動く動かないの前に、

  • 構文エラーのない、ちゃんとしたコードを書いている?
  • プロジェクト固有のコーディング規則(指定したルール)に則って書けてる?

という事を確認してくれる素晴らしいツールです。

環境

eslint : 9.8.0

インストール方法

  1. eslint を導入する任意のフォルダに移動

  2. npm の初期化

    npm init -y
    
  3. 開発環境に eslint をインストール

    npm i -D eslint
    

eslint の初期化処理

npx eslint --init
>? How would you like to use ESLint? …
To check syntax only  -- 構文チェックのみ
To check syntax and find problems  -- 構文チェックと問題の発見

To check syntax and find problems を選択。
構文だけでなく、様々な問題を発見してくれるようです(未定義の変数のチェックなど、本当に様々)

>? What type of modules does your project use? …
JavaScript modules (import/export)
CommonJS (require/exports)
None of these

JavaScript modules (import/export) を選択

>? Which framework does your project use? …
React
Vue.js
None of these

任意の framework 選択

>? Does your project use TypeScript? …
No
Yes

TypeScript を利用していればYesを選択

>? Where does your code run? …  (Press <space> to select, <a> to toggle all, <i> to >invert selection)
✔ Browser
✔ Node

ESLint がどの環境でコードを実行するかを指定します。

  • Browser を選択
    ブラウザに特有グローバル変数(例えば、window や document など)を利用しても、Error になりません
  • Node を選択
    すると、Node.js 特有のグローバル変数(例えば、module や process など)を利用しても、Error になりません

両方選択する場合はaを押すと、両方にチェックが入ります。

>The config that you've selected requires the following dependencies:
eslint@9.x, globals, @eslint/js
? Would you like to install them now? › No / Yes

ESLint の設定に必要な依存関係(eslint@9.x, globals, @eslint/js)をインストールするかどうかを尋ねていますので、基本Yesを選択して、インストールを行います。

>? Which package manager do you want to use? …
npm
yarn
pnpm
bun

任意 package manager を選択します。

eslint の設定 (eslint.config.mjs の設定)

初期処理で作成したeslint.config.mjsに独自のルールを追加します。
様々なルールが存在するので、今回は Chat-GPT で確認したおすすめの設定を追加します。

eslint.config.mjs
import globals from "globals";
import pluginJs from "@eslint/js";

export default [
    {
        // 対象ファイルを指定し、グローバルな言語オプションを適用
        files: ["**/*.js"], // 全ての階層の拡張子がjsファイルにeslintを適用
        languageOptions: {
            globals: globals.browser, // ブラウザ環境のグローバル変数を許可
        },
        ignores: ["before.js"], // 無視するファイルを指定
        // 推奨設定を適用
        ...pluginJs.configs.recommended,
        // カスタムルールを指定
        rules: {
            "no-unused-vars": ["error"], // 未使用の変数をエラーとして検出
            "no-undef": ["error"], // 未定義の変数をエラーとして検出
            eqeqeq: ["error", "always"], // 厳密な等価演算子を強制
            "no-console": ["warn"], // console.log の使用を警告
            indent: ["error", 4], // インデントを4スペースで強制
            quotes: ["error", "single"], // シングルクォートを強制
            semi: ["error", "always"], // セミコロンを必須に
            "brace-style": ["error", "1tbs"], // ブレースのスタイルを "1tbs" に強制
            camelcase: ["error", { properties: "always" }], // キャメルケースを強制
            // "newline-after-var": ["error", "always"], // 変数宣言後の空行を強制(コメントアウト中)
            "no-magic-numbers": ["warn", { ignore: [0, 1] }], // マジックナンバーの使用を警告
            "consistent-return": ["error"], // 一貫した return を強制
            "no-var": ["error"], // var の使用を禁止
            complexity: ["warn", { max: 10 }], // 関数の複雑さを制限
            "prefer-const": ["error"], // 再代入されない変数に const を推奨

            // "import/no-unresolved": ["error"], // モジュールの解決をチェック(コメントアウト中)
        },
    },
];

eslint の実行

1. sample の準備

eslint.config.mjs に記述したルールから逸脱した内容のコードを、sample.js にまとめて記述して、実際に実行した時にどの様な結果になるかを確認していきます。

sample.js
// 1. no-unused-vars ルール
function calculateSum(a, b) {
    const unusedVar = 10; // no-unused-vars: 使われていない変数
    const sum = a + b;
    return sum;
}
calculateSum(1,2);

// 2. no-undef ルール
function printMessage() {
    console.log(message); // no-undef: 'message' が未定義
}
printMessage();

// 3. eqeqeq ルール
function isEqual(x, y) {
    if (x == y) {  // eqeqeq: == を使用
        return true;
    }
    return false;
}
isEqual(1,1);

// 4. no-console ルール
function logMessage(message) {
    console.log(message); // no-console: console.log を使用
}
logMessage('message');

// 5. indent ルール
function greet(name) {
return 'Hello, ' + name; // indent: インデントが不正
}
greet('Hello World')

// 6. quotes ルール
function sayHello() {
    const greeting = "Hello, World!";  // quotes: ダブルクォート使用
    return greeting;
}
sayHello();

// 7. semi ルール
function getGreeting(name) {
    return 'Hello, ' + name // semi: セミコロンがない
}
getGreeting('World');

// 8. brace-style ルール
function showMessage(condition) {
    if (condition) 
    { // brace-style: ブレースのスタイルが不正
        console.log('Condition is true');
    }
}
showMessage(true);

// 9. camelcase ルール
function process_data() { // camelcase: スネークケースが使用されている
    const user_name = 'John Doe'; // camelcase: スネークケースが使用されている
    return user_name;
}
process_data();

// 10. no-magic-numbers ルール
function calculateDiscount(price) {
    return price * 0.8; // no-magic-numbers: マジックナンバー 0.8 が使用されている
}
calculateDiscount(100);

// 11. consistent-return ルール
function checkStatus(status) {
    if (status === 'success') {
        return true;
    } else if (status === 'failure') {
        return false;
    } // consistent-return: 一貫性のない return
        // else {    'success' or 'failure' に該当しない場合にundefinedが返されてしまう
    // return null 
    // }
}
checkStatus('success');

// 12. no-var ルール
function displayMessage() {
    var message = 'Hello!';  // `var` が使用されている
    console.log(message);
}
displayMessage();

// 13. complexity ルール
// ネストが深くかつ、複雑な構成
function calculate(value) {
    if (value > 10) {
        if (value < 20) {
            if (value % 2 === 0) {
                if (value > 15) {
                    return 'Value is between 16 and 20 and even';
                } else {
                    return 'Value is between 11 and 15 and even';
                }
            } else {
                return 'Value is between 11 and 20 and odd';
            }
        } else {
            if (value < 30) {
                if (value % 2 === 0) {
                    return 'Value is between 21 and 30 and even';
                } else {
                    return 'Value is between 21 and 30 and odd';
                }
            } else {
                return 'Value is greater than 30';
            }
        }
    } else {
        return 'Value is 10 or less';
    }
}
calculate(10);

// 14. prefer-const ルール
function getMessage(name) {
    const message = 'Hello, ' + name; // prefer-const: この変数は再代入されないため、constを使用すべき
    return message;
}
getMessage('hoge');

2. npx eslint の実行 (--fix オプションをつけずに実行)

npx eslint

上記コマンドを実行すると、eslint.config.mjsに記述した内容に従って、チェックした結果が出力されます。
今回の sanple.js では 28 箇所で問題が確認されています。
オプションの --fixをつけると、動作に影響の無い部分で自動的に 7 箇所修正してくれます。

✖ 28 problems (14 errors, 14 warnings)
7 errors and 0 warnings potentially fixable with the --fix option.

ESlintTutorial/sample.js
    3:11  error    'unusedVar' is assigned a value but never used                                 no-unused-vars
    7:16  warning  No magic number: 2                                                             no-magic-numbers
   11:5   warning  Unexpected console statement                                                   no-console
   11:17  error    'message' is not defined                                                       no-undef
   17:11  error    Expected '===' and instead saw '=='                                            eqeqeq
   26:5   warning  Unexpected console statement                                                   no-console
   32:1   error    Expected indentation of 4 spaces but found 0                                   indent
   34:21  error    Missing semicolon                                                              semi
   38:22  error    Strings must use singlequote                                                   quotes
   45:28  error    Missing semicolon                                                              semi
   52:5   error    Opening curly brace does not appear on the same line as controlling statement  brace-style
   53:9   warning  Unexpected console statement                                                   no-console
   59:10  error    Identifier 'process_data' is not in camel case                                 camelcase
   60:11  error    Identifier 'user_name' is not in camel case                                    camelcase
   61:12  error    Identifier 'user_name' is not in camel case                                    camelcase
   67:20  warning  No magic number: 0.8                                                           no-magic-numbers
   69:19  warning  No magic number: 100                                                           no-magic-numbers
   72:10  error    Expected to return a value at the end of function 'checkStatus'                consistent-return
   78:1   error    Expected indentation of 4 spaces but found 8                                   indent
   86:5   error    Unexpected var, use let or const instead                                       no-var
   87:5   warning  Unexpected console statement                                                   no-console
   94:17  warning  No magic number: 10                                                            no-magic-numbers
   95:21  warning  No magic number: 20                                                            no-magic-numbers
   96:25  warning  No magic number: 2                                                             no-magic-numbers
   97:29  warning  No magic number: 15                                                            no-magic-numbers
  106:25  warning  No magic number: 30                                                            no-magic-numbers
  107:29  warning  No magic number: 2                                                             no-magic-numbers
  120:11  warning  No magic number: 10                                                            no-magic-numbers

✖ 28 problems (14 errors, 14 warnings)
  7 errors and 0 warnings potentially fixable with the `--fix` option.

2. --fix オプションをつけて実行

npx eslint --fix

--fixをつけると、eslint.config.mjsに記述したルールに従って、自動的にコードを修正して保存をしてくれます。
今回の sample.js の場合、以下 4 つの項目を自動修正してくれました。

  • インデントの修正
  • ダブルクォートをシングルクォートに修正
  • セミコロンの修正
  • ブレースのスタイルの修正
diff --git a/sample.js b/sample.js
index fad3f9a..8b36e06 100644
--- a/sample.js
+++ b/sample.js
@@ -29,27 +29,26 @@ logMessage('message');

 // 5. indent ルール
 function greet(name) {
-return 'Hello, ' + name; // indent: インデントが不正
+    return 'Hello, ' + name; // indent: インデントが不正
 }
-greet('Hello World')
+greet('Hello World');

 // 6. quotes ルール
 function sayHello() {
-    const greeting = "Hello, World!";  // quotes: ダブルクォート使用
+    const greeting = 'Hello, World!';  // quotes: ダブルクォート使用
     return greeting;
 }
 sayHello();

 // 7. semi ルール
 function getGreeting(name) {
-    return 'Hello, ' + name // semi: セミコロンがない
+    return 'Hello, ' + name; // semi: セミコロンがない
 }
 getGreeting('World');

 // 8. brace-style ルール
 function showMessage(condition) {
-    if (condition)
-    { // brace-style: ブレースのスタイルが不正
+    if (condition) { // brace-style: ブレースのスタイルが不正
         console.log('Condition is true');
     }
 }
@@ -75,7 +74,7 @@ function checkStatus(status) {
     } else if (status === 'failure') {
         return false;
     } // consistent-return: 一貫性のない return
-        // else {    'success' or 'failure' に該当しない場合にundefinedが返されてしまう
+    // else {    'success' or 'failure' に該当しない場合にundefinedが返されてしまう
     // return null
     // }
 }
@@ -83,7 +82,7 @@ checkStatus('success');

 // 12. no-var ルール
 function displayMessage() {
-    var message = 'Hello!';  // `var` が使用されている
+    const message = 'Hello!';  // `var` が使用されている
     console.log(message);
 }
 displayMessage();
(END)

eslint の除外

以下のようにコメントを追加する事で、eslintの解析を除外する事ができます。

eslint-disable-line <ルール名>
eslint-disable <ルール名>

例の中で、変数 example_value がスネークケースでの記述になっており、camelcaseのルールが適用される事でエラーになりますが、コメントを追加することでeslintの解析から除外されます。

特定行の除外

特定の行だけ ESLint のルールを無視したい場合、次のように記述します。

const example_value = 'some-value'; // eslint-disable-line camelcase

特定ブロックの除外

複数行にわたってルールを無視したい場合、disableenavleで囲って記述することで、ブロック内で指定したルールが無視されます。

/* eslint-disable camelcase */
const example_value = 'some-value';
const another_value = 'another-value';
/* eslint-enable camelcase */

特定ファイルの除外

ファイル全体で特定のルールを無効にしたい場合、次のようにファイルの冒頭にコメントを追加します。

/* eslint-disable camelcase */
const example_value = 'some-value';
const another_value = 'another-value';

最後に

eslint に関してはほぼ知識 0 からスタートしましたが、こうして試しながらまとめていくと基本的な利用方法は理解できたように思います。
そして、単純なミスでコードが動かないっという事が、圧倒的に少なくできると思うので、eslint を使わないという選択肢はなくなりました。
早速、実際野プロジェクトにも早速導入してみようと思います。

また、Prettierと強豪する部分もありますが、役割を分担して導入することでより一貫性を保ったスタイルになりそうなので、
今後Prettierについても学んで行こうと思います。

参考

https://qiita.com/standard-software/items/2ac49a409688733c90e7
https://mo-gu-mo-gu.com/about-dependencies-devdependencies/
https://www.youtube.com/watch?v=TTutJJUGMbY&t=469s
https://www.youtube.com/watch?v=2h94FItnqvI&t=229s

GitHubで編集を提案

Discussion