Open11

Next.js + Typescript環境構築

ki504178ki504178

何か作ろうとしたり試したりするたびに毎度1から構築するのが面倒なので、
よく使うパターンでコードベースを作成しておく。

前提

  • PC: M1 Mackbook Air
  • OS: Mounterey 12.4
  • Docker: v20.10.14
  • VS Code + Remote Container
ki504178ki504178

ローカルを汚したくないので先ずはNode用のDockerfileを作成。

FROM node:16

作成したらVS CodeのRemote Containerで作成したDockerfileを読み込む。

ki504178ki504178

公式のコマンドでNext.js + Typescript環境を作成

npx create-next-app --typescript

ki504178ki504178

よく使うライブラリを追加していく。

Jest

yarn add -D jest @testing-library/react @testing-library/jest-dom jest-environment-jsdom

※ Jest 28~jext-environment-jsdomを追加しないとjest実行時に怒られるので入れること

jext.config.js
const nextJest = require("next/jest");

const createJestConfig = nextJest({
  // next.config.jsとテスト環境用の.envファイルが配置されたディレクトリをセット。基本は"./"で良い。
  dir: "./",
});

// Jestのカスタム設定を設置する場所。従来のプロパティはここで定義。
const customJestConfig = {
  // jest.setup.jsを作成する場合のみ定義。
  // setupFilesAfterEnv: ["<rootDir>/jest.setup.js"],
  moduleNameMapper: {
    // aliasを定義 (tsconfig.jsonのcompilerOptions>pathsの定義に合わせる)
    "^@/(.*)$": "<rootDir>/src/$1",
  },
  testEnvironment: "jest-environment-jsdom",
};

// createJestConfigを定義することによって、本ファイルで定義された設定がNext.jsの設定に反映されます
module.exports = createJestConfig(customJestConfig);

configについては以下を参考にさせていただきました。
https://zenn.dev/miruoon_892/articles/e42e64fbb55137

Storybook

  • 初期導入

npx sb init

Need to install the following packages:と聞かれたらyでパッケージをインストールしてあげる。

Prettier

  • インストール

create-next-appでESLintは導入されてるため、Typescript用のESLintと組み合わせる前提として以下でインストール

yarn add -D prettier eslint-config-prettier @typescript-eslint/parser @typescript-eslint/eslint-plugin

  • config
.eslintrc.js
module.exports = {
  env: {
    browser: true,
    es6: true,
  },
  extends: [
    "next/core-web-vitals",
    "eslint:recommended",
    // TypeScriptでチェックされる項目をLintから除外する設定
    "plugin:@typescript-eslint/recommended",
    // prettierのextendsは他のextendsより後に記述する
    "prettier",
  ],
  plugins: ["@typescript-eslint"],
  // ESLintでTypescriptを解析する
  parser: "@typescript-eslint/parser",
  parserOptions: {
    sourceType: "module",
    // TypeScriptのLint時に参照するconfigファイルを指定
    project: "./tsconfig.json",
  },
  // 上位ディレクトリにある親のeslintrcを参照しないようにする
  root: true,
  rules: {},
};
.prettierrc.json
{
  "printWidth": 120,
  "trailingComma": "all",
  "tabWidth": 2,
  "semi": false,
  "singleQuote": true,
  "jsxSingleQuote": true
}
  • VS Codeのソース保存時の自動整形

settings.jsonに以下を追加 ※とりあえず全ソースに適用してるけど、不便な場合は拡張子指定する

settings.json
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,

MSW

  • Storybookでも使えるようにしておく

yarn add -D msw msw-storybook-addon

  • 絶対パスインポートが効くようにwebpack設定
.storybook/main.js
module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  addons: ['@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions'],
  webpackFinal: async (config) => {
    config.resolve.alias = { ...(config.resolve.alias || []), '@': path.resolve(__dirname, '../src') }

    return config
  },
  framework: '@storybook/react',
  core: {
    builder: '@storybook/builder-webpack5',
  },
}
ki504178ki504178

完全にツボにハマった(泣
一旦保留しよう。。。

ki504178ki504178

お!今日起動しなおしたら効くようになった!これで勝つる!!

ki504178ki504178

ESLint追加設定

  • scripts, VS Code保存時にimportいい感じにしてくれるように以下を追加

yarn add -D eslint-plugin-unused-imports eslint-plugin-import

  • config

とりあえずグループごとにソートされればOKとしておく。

.eslintrc.js
module.exports = {
  env: {
    browser: true,
    es6: true,
  },
  extends: [
    'next/core-web-vitals',
    'eslint:recommended',
    // TypeScriptでチェックされる項目をLintから除外する設定
    'plugin:@typescript-eslint/recommended',
    // prettierのextendsは他のextendsより後に記述する
    'prettier',
  ],
  plugins: ['@typescript-eslint', 'import', 'unused-imports'],
  // ESLintでTypescriptを解析する
  parser: '@typescript-eslint/parser',
  parserOptions: {
    sourceType: 'module',
    // TypeScriptのLint時に参照するconfigファイルを指定
    project: './tsconfig.json',
  },
  // 上位ディレクトリにある親のeslintrcを参照しないようにする
  root: true,
  rules: {
    '@typescript-eslint/no-unused-vars': 'off', // or "no-unused-vars"
    'unused-imports/no-unused-imports': 'error',
    'unused-imports/no-unused-vars': [
      'warn',
      { vars: 'all', varsIgnorePattern: '^_', args: 'after-used', argsIgnorePattern: '^_' },
    ],
    'import/order': [
      'error',
      {
        // グループごとの並び順
        groups: [
          'builtin', // 1. fsや path などの node "builtin" のモジュール
          'external', // 2. npm install したパッケージ
          'internal', // 3. webpack などでパス設定したモジュール
          ['parent', 'sibling'], // 4. 親階層と小階層のファイル
          'object', // object"-imports
          'type', // 型だけをインポートする type imports
          'index', // 同階層のファイル
        ],
      },
    ],
  },
}

参考:https://chaika.hatenablog.com/entry/2022/01/17/083000

ki504178ki504178

pre-commit, pre-push

  • commit, pushの前にLintやFormatなど実行しがちなので以下を追加
yarn add -D husky lint-staged
npx husky-init && yarn
# husky-init無いからインストールするで?と聞かれたらy
  • husky

pre-commitはステージングされたts,tsxだけを対象としてPrettier, ESLintの順にfixで実行

.husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# ステージングされたts,tsxファイルをスペース区切りで取得
files=`git diff --cached --name-only | grep -E "*.ts|tsx$" | tr "\r\n" " " | tr "\n" " "`

if [ -n ${files} && ${#files(@)} > 0 ]; then
  echo "[pre-commit] Find *.tx|tsx, Exec ESLint & Prettier fix."

  yarn format:fix $files
  ret=$?
  if [ $ret != 0 ]; then
    echo "[pre-commit] Prettier fix error."
    exit $ret
  fi

  yarn eslint:fix $files
  ret=$?
  if [ $ret != 0 ]; then
    echo "[pre-commit] ESLint fix error."
    exit $ret
  fi
fi

exit 0

pre-pushは全体に適用したいが、ファイル数が多くなると極端に遅くなるTypecheckを実行

.husky-pre-push
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# commitされたts,tsxファイルを取得
files=`git log HEAD^..HEAD --name-only --oneline | grep -E "*.ts|tsx$"`

if [ -n ${files} && ${#files(@)} > 0 ]; then
  echo "[pre-push] Find *.tx|tsx, Exec Typechecking"

  yarn tsc
  ret=$?
  if [ $ret != 0 ]; then
    echo "[pre-commit] Typechecking error."
    exit $ret
  fi
fi

exit 0
ki504178ki504178

だいぶ仕上がってきた気がするので一旦ここまで。