⚙️

Next.js(13.5)でTailwindCSS・Sassの導入。ESLint・Stylelint・Prettierのセットアップ

2023/10/07に公開

本日のお題

Next.js(AppRouter)・TailwindCSS・Sassの欲張りセットで、 ESLint,StyleLint Prettierを導入

1. Next.jsセットアップ

https://nextjs.org/docs/getting-started/installation
まずはここから

npx create-next-app@latest
✔ What is your project named? … component-library
✔ Would you like to use TypeScript? …Yes 
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … No
Creating a new Next.js app in /Users/fuku079/Product/Blog/component-library.

2. リンター導入:ESLintの設定

(ESLintについて詳しく知りたい方は以下の記事を参照)
https://zenn.dev/crsc1206/articles/d92548257fb445

初期状態では以下の構成になっている

.eslintrc.json
{
  "extends": "next/core-web-vitals"
}

下記のようなものも含まれているらしい

eslint-plugin-react,eslint-plugin-react-hooks,eslint-plugin-next

Core-web-vitals:パフォーマンス測定のための指標

Googleが提唱しているUXに関する指標のようなもの
公式でも解説されている
https://nextjs.org/learn/seo/web-performance

設定ファイルの優先度

eslintの設定ファイルは拡張子によって、優先順位が変わる

1.eslintrc.js
2.eslintrc.cjs
3.eslintrc.yaml
4.eslintrc.yml
5.eslintrc.json
6 package.json

💡数字が小さい方が優先

今回はeslintrc.jsを使用

TypeScriptのESLintの有効化

例:型が使われていないためエラーが出てるコンポーネント

import { FC } from "react";
type SampleProps = {
	  name: string;
};

export const Sample: FC = ({ user }) => { //プロパティ 'user' は型 '{}' に存在しません。
  return <div>{user.name}</div>;
};

このコンポーネントがある状態で、ESLintを走らせてみると…

% npm run lint
> component-library@0.1.0 lint
> next lint

✔ No ESLint warnings or errors
(base) fuku079@fukuuranoMacBook-Air compo

なんと、通っちゃいました…
これではいけないので、TypeScript用のルールを追加します

pluginとparserをインストール

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

.eslintrc.jsに設定を追加

module.exports = {
  root: true,
  extends: ["plugin:@typescript-eslint/recommended", "next/core-web-vitals"],
  plugins: [],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    project: "./tsconfig.json",
  },
  rules: {
    "@typescript-eslint/no-unused-vars": "error", //宣言されてるけど使用されてない変数をエラーに
    "@typescript-eslint/no-explicit-any": "warn", // any型の場合に警告を出す
    "@typescript-eslint/no-unsafe-call": "error", // 型安全性が確保されてない関数を呼び出した場合にエラーに
    "@typescript-eslint/no-unsafe-member-access": "error", //オブジェクトのメンバーへの型安全性が確保されていないアクセスを検出
    "@typescript-eslint/no-unsafe-return": "error", // 型安全性が確保されていない値の返却を検出
  },
};

(tips:下の二つはわかりづらいので、後ほど深掘っていく…)

設定し終えたら、もう一度Linterを走らせてみる

% npm run lint

> component-library@0.1.0 lint
> next lint

./src/app/Sample.tsx
3:6  Error: 'SampleProps' is defined but never used.  @typescript-eslint/no-unused-vars
6:16  Error: Unsafe member access .name on an `any` value.  @typescript-eslint/no-unsafe-member-access

ちゃんとエラーが出た!

修正後のコード

import { FC } from "react";

type SampleProps = {
  user: {
    name: string;
  };
};

export const Sample: FC<SampleProps> = ({ user }) => {
  return <div>{user.name}</div>;
};

tips:オブジェクトのメンバーと戻り値の型安全性を検知

@typescript-eslint/no-unsafe-member-access
型定義で定義してるメンバー(変数)以外のものを使用している場合に、エラーが出る

type User = {
  name: string;
};

export const Sample2 = () => {
  const user: User = {
    name: "John",
    age: 25, //型 '{ name: string; age: number; }' を型 'User' に割り当てることはできません。オブジェクト リテラルは既知のプロパティのみ指定できます。'age' は型 'User' に存在しません。ts(2322)
  };
  return <div>{user.age}</div>;
};

@typescript-eslint/no-unsafe-return
型定義で定義してるメンバー(変数)以外のものを使用している場合に、エラーが出る

type User = {
  name: string;
};

export function getUser(): User {
  const user: any = {
    name: "John",
    age: 25,
  };
  return user; // User型にはageプロパティが存在しない
}

export const Sample3 = () => {
  const user = getUser();
  return <div>{user.age}</div>;
};

さっきのルール追加によって、これらの型安全性に関する問題をLinterでも検知できるようにする

% npm run lint

> component-library@0.1.0 lint
> next lint

./src/app/Sample.tsx
30:3  Error: Unsafe return of an `any` typed value.  @typescript-eslint/no-unsafe-return

検知!!

import周りの設定 (✨神✨)

eslint-plugin-unused-imports:未使用のimport文を削除するplugin

開発中に、「このコンポーネントやライブラリ使おう…….やっぱこっち使おう」という風にどんどんいろんなものをimportすることがあります。
けど、結局使わなくなったimport文を後から剥がしていくのって結構大変...(面倒)

そんな時に役立つpluguin

$ npm i -D eslint-plugin-unused-imports

eslintrc.jsに追加

plugins: ["unused-imports"],

import/order:import文の順番を自動で並び替えしてくれるruleを追加

先ほど同様、import文がごちゃごちゃになってきたら整理するのが面倒
だけどこれがあれば、自動的に並び替えてくれてスッキリ!

eslintrc.jsのrulesに追加

rules: {
  "import/order": [
    "error",
    {
      // インポートを以下のカテゴリに分類
      groups: [
        "builtin", // Node.jsの組み込みモジュール
        "external", // node_modulesからのインポート
        "internal", // 同じパッケージ内の他のモジュールからのインポート
        "parent", // 現在のディレクトリの親ディレクトリからのインポート
        "sibling", // 同じディレクトリ内の他のモジュールからのインポート
        "index", // 現在のディレクトリのインデックスファイルからのインポート
        "object", // オブジェクトのインポート
        "type", // TypeScriptの型のインポート
      ],
      // 特定のインポートパターンをカスタムグループに分類
      pathGroups: [
        {
          pattern: "{react,react-dom/**,react-router-dom}", // このパターンに一致するインポート
          group: "builtin", // 上記のインポートをbuiltinグループとして扱う。
          position: "before", // そのグループの前に配置
        },
      ],
      // pathGroupsの設定から特定のインポートタイプを除外します。
      pathGroupsExcludedImportTypes: ["builtin"], // builtinタイプのインポートを除外。
      // インポート文のアルファベット順にソート
      alphabetize: {
        order: "asc", //
      },
    },
  ],
}

並び替えに関しては、自分の好みでカスタマイズしていこう
(以下の記事を参考に)
https://zenn.dev/knowledgework/articles/0994f518015c04?redirected=1#importの自動整列(import/order)

.eslintignoreで管理対象から省くファイルを定義

node_modules
.next
out
public
.prettierrc.js
.eslintrc.js
tailwind.config.js
next.config.js
postcss.config.js

3. フォーマッター:Prettierの導入

インデントやセミコロンなどのルールを指定できる
開発チームによって変わってくるので、どういうルールなのかを理解できるようになっておきます

まずはインストール

$ npm i -D prettier eslint-config-prettier

.prettierrcというファイルを作成し、この中にルールを記述する

{
  "printWidth": 120, // 120文字以上で改行
  "jsxBracketSameLine": false, // jsxの閉じタグを新しい行に
  "tabWidth": 2, // インデントのスペース
  "trailingComma": "none", // 配列やオブジェクトの最後の要素にカンマはつけない
  "semi": false, // 分の終わりにセミコロンを追加しない
  "singleQuote": true // 文字列はシングルクォートで囲む
}

.eslintrc.jsにprettierを追加

extends: ['plugin:@typescript-eslint/recommended', 'next/core-web-vitals', 'prettier'],

.prettierignoreで管理対象から省くファイルを定義

node_modules
.next
dist
out
public/
*.md

4. Sass + Stylelintの導入

Q:TailwindCSSを導入したのになんでSassを入れるんだ?
A:個人開発でTailwindCSS使ってたけど、css.moduleも導入したいと思ったため

あまりCSSを学んでない状態でTailwindCSSを始めちゃったので、素のCSSの知識が乏しいです。
Sassの勉強もしつつ、自分の開発中のプロジェクトでも少しずつ取り入れていきたいという考えです

Sassのインストール

https://nextjs.org/docs/app/building-your-application/styling/sass

npm i --save-dev sass

Next.jsのSassの設定

Next.jsの場合は、バンドルの方で設定する必要がなく、next.config.jsに以下の設定を追加するだけで設定できる

/** @type {import('next').NextConfig} */
const path = require('path')

module.exports = {
  sassOptions: {
    includePaths: [path.join(__dirname, 'styles')]
  }
}

めっちゃ簡単

Stylelintの導入

https://stylelint.io/user-guide/get-started
https://lab.astamuse.co.jp/entry/stylelint

npm i -D stylelint stylelint-config-standard-scss stylelint-config-recess-order stylelint-config-recommended-scss

.stylelintrc.jsを作成し、設定を追加

module.exports = {
  extends: ['stylelint-config-recess-order', 'stylelint-config-recommended-scss'],
  rules: {
    // ::before, ::afterのコロンを2つにする
    'selector-pseudo-element-colon-notation': 'double',
    // クラス名でアンパサンド(&)は禁止(&:hoverなどはOK)
    'scss/selector-no-union-class-name': true,
    // シングルクォーテーションに統一
    'string-quotes': 'single'
  },
  ignoreFiles: ['**/node_modules/**']
}

これによって、cssプロパティの並び順を自動で整理してくれたり、コードを整形してくれる

CSSプロパティの書き順

アルファベット順:名前の通り
視覚順:人銀が見たものを認識する順番に沿って記載
(以下の記事でわかりやすくまとめてくださっています)
https://zenn.dev/web_tips/articles/f1167f4314dcb3

pacage.jsonにコマンドを追加

pacage.jsonのscriptに以下の内容を雨域

"scripts: {
	//..省略
  // Next.jsのESLintを使って、'src'ディレクトリ内のファイルをリントする
  "lint": "next lint --dir src",

  // Next.jsのESLintを使って、プロジェクト内のファイルのリントエラーを自動修正する
  "lint:fix": "next lint --fix",

  // Prettierを使って、指定されたファイルタイプのファイルを自動フォーマットする
  // .gitignoreにリストされているファイルは無視される
  "format": "prettier --write './**/*.{js,jsx,ts,tsx,json}'",

  // Stylelintを使って、指定されたファイルタイプのスタイルファイルをリントする
  "lint:style": "stylelint '**/*.{css,scss,sass}'",

  // Stylelintを使って、指定されたファイルタイプのスタイルファイルのリントエラーを自動修正する
  "lint:style:fix": "stylelint --fix '**/*.{css,scss,sass}'"
}

適当に書いたSCSSファイルをformatしてみる

.text {
  color: blue;
  font-size: 1em;
  text-align: center;
  flex: 1;
  display: flex;
  align-items: center;
}

上記のようななSCSSファイルにStylelintを走らせてみる

% npm run lint:style

> component-library@0.1.0 lint:style
> stylelint '**/*.{css,scss,sass}'

Deprecation warnings:
 - The "string-quotes" rule is deprecated.

src/app/Sample.module.scss
 3:3Expected "font-size" to come before "color"  order/properties-order
 5:3Expected "flex" to come before "text-align"  order/properties-order
 6:3Expected "display" to come before "flex"     order/properties-order

src/app/globals.css //tailwindcssの設定も引っかかってる
 1:1Unexpected unknown at-rule "@tailwind"  scss/at-rule-no-unknown
 2:1Unexpected unknown at-rule "@tailwind"  scss/at-rule-no-unknown
 3:1Unexpected unknown at-rule "@tailwind"  scss/at-rule-no-unknown

6 problems (6 errors, 0 warnings)

ちゃんとエラーになってる

fixで自動修正

.text {
  display: flex;
  flex: 1;
  align-items: center;
  font-size: 1.5em;
  color: palevioletred;
  text-align: center;
}

整形された!!

VSCodeで保存時に自動整形するようにする

ルートディレクトリに.vscodeディレクトリを作成し、settings.jsonを追加

mkdir .vscode

.vscodeディレクトリ内にsettings.jsonファイルを追加

settings.json
{
  // ESLintの機能を有効にする
  "eslint.enable": true,

  // Stylelintの機能を有効にする
  "stylelint.enable": true,

  // ファイルを保存する際に、自動的にフォーマットを行う
  "editor.formatOnSave": true,

  // 保存時に実行されるコードアクションを指定
  "editor.codeActionsOnSave": {
    // 保存時にESLintによる自動修正を行う
    "source.fixAll.eslint": true,

    // 保存時に不足しているインポートを自動的に追加する
    "source.addMissingImports": true
  },

  // SCSSのバリデーションを無効にする(VSCodeのデフォルトのSCSSバリデーションを避けるため)
  "scss.validate": false,

  // SCSSファイルに特有の設定
  "[scss]": {
    // SCSSファイルを保存する際に自動フォーマットを行わない
    "editor.formatOnSave": false,

    // SCSSファイルを保存する際にStylelintによる自動修正を行う
    "editor.codeActionsOnSave": {
      "source.fixAll.stylelint": true
    }
  },

  // Stylelintでバリデーションを行うファイルタイプを指定(この場合、SCSSファイルのみ)
  "stylelint.validate": ["scss"]
}

これで保存時に自動整形ができるようになる

TailwindCSSのためのESLint設定

TailwindCSSを使ってる時に、classNameを結構適当に書いて、ぐちゃぐちゃになるケースがある
CSSやSass同様に、自動で整形してくれたり、間違ったclassを定義しているのを検知してくれるpluginが存在する

$ npm i -D eslint-plugin-tailwindcss

.eslintrc.jsのextendsに以下を追記

extends: [
	"plugin:tailwindcss/recommended",
]

試しにめちゃくちゃなスタイルを当ててみる

export const Sample: FC<SampleProps> = ({ user }) => {
  return (
    <div className="justify-center flex text-lg p-8 ">
      <div>{user.age}</div>
    </div>
  )
}

これを保存すると…

export const Sample: FC<SampleProps> = ({ user }) => {
  return (
    <div className="flex justify-center p-8 text-lg">
      <div>{user.age}</div>
    </div>
  )
}

順番を並び替えてくれる!!

おわりに

今回できるようになったことのまとめです

ESLint

  • TypeScriptの型チェック
  • import文の自動削除や並べ替え

どこのファイルの何行目なのかを教えてくれたり、自動整形してくれるので、快適になる

Prettier

  • コードの自動整形

Stylelint

  • cssの自動整形
    自動整形によって、チームで統一したコードを保つことができるし、Styleに関してもプロパティの並び順を規則に則り並び替えてくれるので、自分流というのがなくせてすごいいいものだと感じました。

StorybookやJestなどのツールの導入もこれからやっていきたいと思います

参考

https://zenn.dev/necscat/articles/435db2f1dbbb29

https://zenn.dev/arsaga/articles/0fdee431a8374a

https://zenn.dev/brachio_takumi/articles/a8fecd8b1b2742

GitHubで編集を提案

Discussion