フロントエンド開発マル秘テクニック

2022-08-01

普段から使用しているフロントエンド開発のマル秘テクニックをダラダラっと書いていこうと思います。


共通編

.editorconfig を作成する

環境ごとのエディターの仕様を合わせるため .editorconfig を作成するようにしましょう。

自分が普段使用している設定は以下のとおりです。

root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_size = 2
indent_style = space
trim_trailing_whitespace = true

.npmrc を作成する

npm を使用する場合、設定ファイルを作成すると開発がはかどります。

自分が普段使用している設定は以下のとおりです。

also=dev
engine-strict=true
progress=false
save-exact=true

renovate.json を作成する

npm パッケージのアップデートはある程度勝手に行ってほしいので renovate.json を作成するようにしましょう。

自分が普段使用している設定は以下のとおりです。

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "assignees": ["piro0919"],
  "dependencyDashboard": true,
  "extends": ["config:base", ":timezone(Asia/Tokyo)"],
  "packageRules": [
    {
      "automerge": true,
      "groupName": "devDependencies",
      "matchDepTypes": ["devDependencies"],
      "matchUpdateTypes": ["minor", "patch", "pin", "digest"]
    },
    {
      "automerge": true,
      "groupName": "dependencies",
      "matchDepTypes": ["dependencies"],
      "matchUpdateTypes": ["minor", "patch", "pin", "digest"]
    }
  ],
  "platformAutomerge": true,
  "reviewers": ["piro0919"],
  "schedule": ["every weekend"]
}

Linter Formatter 編

.eslintrc.json を作成する

ESLint を導入しているプロジェクトは多いと思いますが、自分の設定は結構強めだと思います。

開発がはかどる設定もあるので、参考になりましたら。

設定は Next.js × CSS Modules(Sass)× TypeScript を想定しています。

{
  "devDependencies": {
    "@typescript-eslint/eslint-plugin": "5.31.0",
    "eslint": "8.20.0",
    "eslint-config-google": "0.14.0",
    "eslint-config-next": "12.2.3",
    "eslint-config-prettier": "8.5.0",
    "eslint-plugin-filenames": "1.3.2",
    // eslint-plugin-css-modules から乗り換えました
    "eslint-plugin-postcss-modules": "2.0.0",
    "eslint-plugin-sort-destructure-keys": "1.4.0",
    "eslint-plugin-sort-keys-shorthand": "2.2.0",
    "eslint-plugin-typescript-sort-keys": "2.1.0",
    "eslint-plugin-unused-imports": "2.0.0",
    "prettier": "2.7.1",
    "typescript": "4.7.4"
  }
}
{
  "extends": [
    "eslint:recommended",
    "google",
    "plugin:@typescript-eslint/recommended",
    "plugin:typescript-sort-keys/recommended",
    "next/core-web-vitals",
    "prettier"
  ],
  "ignorePatterns": ["*.d.ts", "*.js"],
  "overrides": [
    {
      "excludedFiles": ["_app.tsx", "_document.tsx", "*.d.ts"],
      "files": "*",
      "rules": {
        "filenames/match-exported": "off",
        "filenames/match-regex": "error"
      }
    }
  ],
  "parserOptions": {
    "warnOnUnsupportedTypeScriptVersion": false
  },
  "plugins": [
    "filenames",
    "postcss-modules",
    "sort-destructure-keys",
    "sort-keys-shorthand",
    "typescript-sort-keys",
    "unused-imports"
  ],
  "root": true,
  "rules": {
    "@typescript-eslint/explicit-function-return-type": "error",
    "@typescript-eslint/no-unused-vars": "off",
    "import/newline-after-import": [
      "error",
      {
        "count": 1
      }
    ],
    "import/order": [
      "error",
      {
        "alphabetize": {
          "caseInsensitive": true,
          "order": "asc"
        },
        "warnOnUnassignedImports": true
      }
    ],
    "import/prefer-default-export": "error",
    "newline-before-return": "error",
    "no-duplicate-imports": "error",
    "no-multiple-empty-lines": [
      "error",
      {
        "max": 1
      }
    ],
    "padding-line-between-statements": [
      "error",
      {
        "blankLine": "always",
        "next": [
          "break",
          "const",
          "do",
          "export",
          "function",
          "let",
          "return",
          "switch",
          "try",
          "while"
        ],
        "prev": "*"
      },
      {
        "blankLine": "always",
        "next": "*",
        "prev": [
          "const",
          "do",
          "export",
          "function",
          "let",
          "return",
          "switch",
          "try",
          "while"
        ]
      },
      {
        "blankLine": "never",
        "next": "import",
        "prev": "*"
      },
      {
        "blankLine": "never",
        "next": "case",
        "prev": "case"
      },
      {
        "blankLine": "never",
        "next": "const",
        "prev": "const"
      },
      {
        "blankLine": "never",
        "next": "let",
        "prev": "let"
      }
    ],
    "postcss-modules/no-undef-class": "error",
    "postcss-modules/no-unused-class": "error",
    "react-hooks/exhaustive-deps": [
      "error",
      {
        // true にすると hooks の dependency を自動的に保管してくれるようになるので推奨
        "enableDangerousAutofixThisMayCauseInfiniteLoops": true
      }
    ],
    "react/jsx-sort-props": "error",
    "require-jsdoc": "off",
    "semi": "error",
    "sort-destructure-keys/sort-destructure-keys": "error",
    "sort-keys": "off",
    "sort-keys-shorthand/sort-keys-shorthand": [
      "error",
      "asc",
      {
        "shorthand": "first"
      }
    ],
    "unused-imports/no-unused-imports": "error",
    "unused-imports/no-unused-vars": [
      "error",
      {
        "args": "after-used",
        "argsIgnorePattern": "^_",
        "vars": "all",
        "varsIgnorePattern": "^_"
      }
    ]
  },
  "settings": {
    "import/resolver": {
      "node": {
        "extensions": [".js", ".ts", ".tsx"],
        "moduleDirectory": ["./src"]
      }
    },
    "postcss-modules": {
      "camelCase": true,
      "include": "**/*.scss"
    }
  }
}

.stylelintrc.json を作成する

CSS Modules(Sass)を想定しています。

{
  "devDependencies": {
    "prettier": "2.7.1",
    "stylelint": "14.9.1",
    "stylelint-config-css-modules": "4.1.0",
    "stylelint-config-prettier": "9.0.3",
    "stylelint-config-recommended-scss": "7.0.0",
    "stylelint-config-sass-guidelines": "9.0.1",
    "stylelint-config-standard": "26.0.0",
    "stylelint-config-standard-scss": "5.0.0",
    "stylelint-order": "5.0.0",
    "stylelint-scss": "4.3.0"
  }
}
{
  "extends": [
    "stylelint-config-standard-scss",
    "stylelint-config-standard",
    "stylelint-config-css-modules",
    "stylelint-config-recommended-scss",
    "stylelint-config-sass-guidelines",
    "stylelint-config-prettier"
  ],
  "plugins": ["stylelint-order", "stylelint-scss"],
  "rules": {
    "at-rule-no-unknown": null,
    "color-named": [
      "never",
      {
        "ignore": ["inside-function"]
      }
    ],
    "order/properties-alphabetical-order": true,
    "scss/at-rule-no-unknown": [
      true,
      {
        "ignoreAtRules": ["value"]
      }
    ],
    "selector-class-pattern": null,
    "selector-pseudo-class-no-unknown": [
      true,
      {
        "ignorePseudoClasses": ["export", "global"]
      }
    ]
  }
}

commitlint  を導入する

コミットメッセージに統一を持たせたいので commitlint は必ず導入するようにしましょう。

{
  "devDependencies": {
    "@commitlint/cli": "17.0.3",
    "@commitlint/config-conventional": "17.0.3",
    "husky": "8.0.1",
    "lint-staged": "13.0.3"
  }
}

Next.js 編

src フォルダーを作成する

公式サイト に書かれているとおりです。

TypeScript の場合は、tsconfig.jsonbaseUrlsrc を設定すると絶対パスによるインポートが可能となります。

またあわせて styles フォルダーに対してパスを貼ってやると、スタイルシートの絶対パスによるインポートも可能となります。

{
  "compilerOptions": {
    "baseUrl": "src",
    "paths": {
      "styles/*": ["styles/*"]
    }
  }
}

画像の最適化をオフにする

Next.js が提供している Image コンポーネントのデフォルトの設定では、画像の最適化がオンになっています。

画像の最適化自体は便利な機能なのですが、実際には Vercel のサーバーで最適化処理が走っており、最適化を行った量によって課金が発生する恐れもあります。

そのため、個人的には最適化はオフにしておいたほうが無難だと思っています。

Next.js のバージョン 12.2 よりグローバルに設定が可能となったので、設定を入れるのもアリかもしれません。

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    images: {
      unoptimized: true,
    },
  },
};

module.exports = nextConfig;

そんな感じです、意外と少なかったですね。

参考になれば幸いです。