TechBlog masa

  • Top
  • Posts
  • Profile
2025-01-22
SWE
【2025年】NextJS(AppRouter), TS, SCSS環境にprettier, eslint, stylelintを導入する【2025年】NextJS(AppRouter), TS, SCSS環境にprettier, eslint, stylelintを導入する
NextJSTS / NodeJSFormat

likes: 0

Unsupported Block: table_of_contents

Nextjs App Router, TS, SCSS環境のもとで、prettier, eslint, stylelintで保存時に自動でコード整形してくれる。importも自動で挿入・削除・並び替えしてくれる。

npm run checkでprettier, eslint, sytlelintを対象ファイル(ts,tsx / css,scss など)に実行して自動で修正してくれる。

フォーマットやリントから外したいディレクトリ・ファイルを指定できる。

nextjs app router, ts環境でのフォーマット・リントの最小構成。

これにお好みのプラグインやルールを加えていくイメージ。huskyなども。

Unsupported Block: link_preview

VScodeでももちろん可能と思われる。

  • Machine: arm64, Apple M2, v14.6
  • IDE: cursor, v0.44.11

 

  • next: v15
  • react: v19
  • typescript: v5.7
  • prettier: v3.4
  • eslint: v9.17
  • stylelint: v16.12

 

 

Unsupported Block: heading_2
npx create-next-app@latest nextjs-ts-prettier-eslint-stylelint(<-project-name)

✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … No
✔ Would you like your code inside a `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to use Turbopack for `next dev`? … Yes
✔ Would you like to customize the import alias (`@/*` by default)? … No

Creating a new Next.js app in /Users/miwa/Desktop/forCursor/notion-tech-blog/nextjs-ts-prettier-eslint-stylelint.

下記のpackage.jsonを使ってnpm install

{
  "name": "your-project-name",
  "version": "0.1.0",
  "private": true, // publicにするとnpmに登録される
  "scripts": {
    "dev": "echo 'this is test repo, using port 3001' && next dev --turbopack -p 3001",
    "build": "prettier --check . && stylelint --allow-empty-input \"**/*.{css,scss}\" && next build",
    "start": "next start",
    "format": "prettier --write . && echo '\nPrettier Finished'",
    "lint:es": "next lint --fix && echo '\nESLint Finished'",
    "lint:style": "stylelint --fix --allow-empty-input \"**/*.{css,scss}\" && echo 'StyleLint Finished'",
    "check": "npm run format && npm run lint:es && npm run lint:style",
    "build:local": "prettier --write . && stylelint --allow-empty-input \"**/*.{css,scss}\" && next build"
  },
  "dependencies": {
    "@emotion/react": "^11.14.0",
    "@emotion/styled": "^11.14.0",
    "@mui/material": "^6.3.0",
    "next": "15.1.3",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "@typescript-eslint/eslint-plugin": "^8.18.2",
    "@typescript-eslint/parser": "^8.18.2",
    "eslint": "^9",
    "eslint-config-next": "15.1.3",
    "eslint-config-prettier": "^9.1.0",
    "eslint-define-config": "^2.1.0",
    "eslint-plugin-next": "^0.0.0",
    "prettier": "^3.4.2",
    "prettier-plugin-organize-imports": "^4.1.0",
    "sass": "^1.83.0",
    "stylelint": "^16.12.0",
    "stylelint-config-prettier-scss": "^1.0.0",
    "stylelint-config-standard-scss": "^14.0.0",
    "typescript": "^5.7.2"
  }
}

 

Unsupported Block: heading_2
Unsupported Block: heading_3

.prettierrc.json

{
  "tabWidth": 2,
  "useTabs": false,
  "printWidth": 90,

  "trailingComma": "all",
  "semi": false,
  "singleQuote": false,
  "jsxSingleQuote": false,

  "plugins": ["prettier-plugin-organize-imports"]
}

.prettierignore

node_modules
.next
.husky
coverage
.prettierignore
.stylelintignore
.eslintignore
stories
storybook-static
*.log
playwright-report
.nyc_output
test-results
junit.xml
docs

 

Unsupported Block: heading_3

.eslintrc.js

/* eslint-env node */
module.exports = {
  root: true,
  extends: ["next/core-web-vitals", "prettier"],
  plugins: ["@typescript-eslint"],
  parser: "@typescript-eslint/parser",
  overrides: [
    {
      files: ["*.ts", "*.tsx"],
      parserOptions: {
        project: ["./tsconfig.json"],
        projectService: true,
        tsconfigRootDir: __dirname,
      },
      extends: [
        "next/core-web-vitals",
        "plugin:@typescript-eslint/recommended",
        "prettier",
      ],
      // 追加したいルールがあればここに追加
      // rules: {
      //   "no-undef": "off",
      //   "no-unused-vars": "off",
      //   "@typescript-eslint/no-unused-vars": "error",
      // },
    },
  ],
}

 

Unsupported Block: heading_3

.stylelintrc.json

{
  "extends": ["stylelint-config-standard-scss", "stylelint-config-prettier-scss"],
  "rules": {
    "selector-class-pattern": null,
    "no-empty-source": null,
    "color-hex-length": null
  }
}

.stylelintignore

styles/globals.css
styles/Home.module.css
coverage

 

Unsupported Block: heading_3
  • .vscode/settings.json
{
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[scss]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[css]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },

  // stylelint で css, scss ファイルを検証するための設定
  "stylelint.validate": ["css", "scss"],
  "css.validate": false,
  "less.validate": false,
  "scss.validate": false,

  "editor.formatOnSave": true,
  "editor.formatOnPaste": true,
  // 保存時に自動でフォーマット、リントを実行
  "editor.codeActionsOnSave": {
    "source.fixAll.stylelint": "explicit",
    "source.fixAll.eslint": "explicit",
    "source.removeUnusedImports": "explicit",
    "source.addMissingImports": "explicit",
    "source.organizeImports": "explicit"
  },
  // tsconfig.json に構成されている paths に基づいて非相対インポート(@/components/Header みたいな形式)を優先する
  "typescript.preferences.importModuleSpecifier": "non-relative"
}

 

Unsupported Block: heading_3

.gitattributes

改行コードをLFに揃える。Windowsの人はgitで設定してないとRFになりうるのでこれで対策。

* text=auto eol=lf

 

Unsupported Block: heading_2
  • save時にフォーマットされるか
  • save時にimportが挿入・削除・並び替えされるか
  • npm run check実行でpretiter,eslint,stylelintが実行されるか

 

GithubでIssueを立ててもらえると解決できる…かも。

Unsupported Block: link_preview
breadcrumb予定地
profileCard予定地

SideBarPage

共有ボタン予定地
他ボタン予定地