ESLint و Prettier برای React

من در ابتدا نصب و کانفیگ ESLint رو به شما آموزش میدم و همچنین بهتون میگم که چطور میتونید قبل از commit کردن روی گیت قوانین ESLint رو به طور خودکار بررسی کنید و بعد از اون به سراغ اکستنشن Prettier میرم و بهتون میگم که چطوری میتونین اون رو با ESLint پروژتون integrate کنید، که با هر بار ذخیره فایل‌های پروژتون تمامی قوانین به طور خودکار اعمال بشه


نصب و کانفیگ ESLint

ESLint is an open source project originally created by Nicholas C. Zakas in June 2013. Its goal is to provide a pluggable linting utility for JavaScript.

شما با استفاده از ESLint میتونین دولاپرها رو مقید کنید از یک سری اصول و قوانین در هنگام کد زدن پیروی کنن و از مسیر خارج نشن، به این کار اصطلاحاً Linting می‌گویند.

نصب

پلاگین‌های ESLint ای مفیدی که من به پروژم اضاف کردم ایناست:

  • eslint-plugin-babel
  • eslint-plugin-css-modules : برای سی‌اس‌اس ماژول
  • eslint-plugin-filenames : برای ساختار و نام فایل‌ها، بهش الگو میدین
  • eslint-plugin-flowtype
  • eslint-plugin-import
  • eslint-plugin-no-async-without-await : اینم که از اسمش مشخصه
  • eslint-plugin-react : برای خود ری‌اکت
  • eslint-plugin-react-hooks : برای ری‌اکت هوک
  • eslint-plugin-react-redux : برای ریداکس
  • eslint-plugin-redux-saga : برای ریداکس ساگا
  • eslint-plugin-simple-import-sort : برای مرتب سازی ایمپورت‌های خارجی و داخلی پروژتون

اگه توی پروژتون از TypeScript استفاده میکنید، دو تا پلاگین زیر بدردتون میخوره

  • @typescript-eslint/eslint-plugin
  • @typescript-eslint/parser

با استفاده از دستور زیر تمامی پلاگین‌ها و پیش‌نیازهای ESLint رو نصب می‌کنیم.

yarn add -D eslint babel-eslint eslint-config-react-app eslint-loader eslint-plugin-babel eslint-plugin-react eslint-plugin-css-modules eslint-plugin-filenames eslint-plugin-flowtype eslint-plugin-import eslint-plugin-no-async-without-await eslint-plugin-react-hooks eslint-plugin-react-redux eslint-plugin-redux-saga eslint-plugin-simple-import-sort

کانفیگ

حالا یک فایل با نام .eslintrc در روت پروژتون ایجاد کنید و به شکل زیر ESLint رو کانفیگ کنید.

{
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  },
  "plugins": [
    "babel",
    "react",
    "react-hooks",
    "react-redux",
    "redux-saga",
    "no-async-without-await",
    "css-modules",
    "filenames",
    "simple-import-sort"
  ],
  "extends": [
    "react-app",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react-redux/recommended",
    "plugin:redux-saga/recommended",
    "plugin:css-modules/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  },
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    },
    "ecmaVersion": 2018,
    "sourceType": "module"
  },
  "rules": {
    "strict": ["error", "safe"],
    "no-debugger": "error",
    "brace-style": [ "error", "1tbs", { "allowSingleLine": true  } ],
    "no-trailing-spaces": "error",
    "keyword-spacing": "error",
    "space-before-function-paren": ["error", "never"],
    "spaced-comment": ["error", "always"],
    "vars-on-top": "error",
    "no-undef": "error",
    "no-undefined": "warn",
    "comma-dangle": ["error", "never"],
    "quotes": ["error", "single"],
    "semi": ["error", "always"],
    "guard-for-in": "error",
    "no-eval": "error",
    "no-with": "error",
    "valid-typeof": "error",
    "no-unused-vars": "error",
    "no-continue": "warn",
    "no-extra-semi": "warn",
    "no-unreachable": "warn",
    "no-unused-expressions": "warn",
    "max-len": ["warn", 80, 4],
    "react/prefer-es6-class": "warn",
    "react/jsx-boolean-value": "warn",
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "@typescript-eslint/indent": ["error", 2],
    "@typescript-eslint/no-explicit-any": "off",
    "react/prop-types": "off",
    "react-redux/mapDispatchToProps-returns-object": "off",
    "react-redux/prefer-separate-component-file": "off",
    "@typescript-eslint/explicit-function-return-type": [ "warn", { "allowExpressions": true } ],
    "no-async-without-await/no-async-without-await": "warn",
    "css-modules/no-undef-class": "off",
    "filenames/match-regex": [
      "error",
      "^[a-zA-Z]+\\.*\\b(typescript|module|locale|validate|test|action|api|reducer|saga)?\\b$",
      true
    ],
    "filenames/match-exported": "off",
    "filenames/no-index": "error",
    "simple-import-sort/sort": "error"
  }
}

اجازه بدین قبل از اینکه بریم سراغ مرحله بعد یه توضیح کوتاهی راجب کانفیگ بالا بدم.

قسمت plugins که مشخصه، همون لیستی ایه که اون بالا معرفی کردم، شما از طریق extends میتونید یکسری rule هایی که خود پلاگین‌ها به صورت پیشنهادی نوشتن رو به rule های پروژتون extend کنید.

قسمت parserOptions برای اینکه بهش بگین من از ecmaVersion 2018 استفاده میکنم و قسمت مهم داستان هم rule هاست که از طریق سه نوع type با نام‌های error، warn و off می‌تونید سطح اجباری بودن و نبودن و rule هارو تعیین کنید.

یکی از rule هایی که شاید فقط مختص پروژه خودم باشه و خودمم خیلی دوسش دارم رو اجازه بدین واستون یه توضیح کوتاهی راجبش بدم، رول filenames/match-regex که با استفاده از یک الگوی مشخص بهش میگین که نام فایل‌های پروژم از این ساختار خارج نشه، برای مثال نام چندتا از فایل‌های من به این شکله:

  • Login.tsx
  • Login.module.scss
  • Login.locale.tsx
  • Login.reducer.tsx
  • ...

کانفیگ اسکریپت ESLint در package.json

{
  "scripts": {
    "lint": "./node_modules/.bin/eslint src --ext .js,.jsx,.ts,.tsx",
    "lint:fix": "./node_modules/.bin/eslint src --ext .js,.jsx,.ts,.tsx --fix"
  }
}

طرز استفاده از اسکریپت بالا در خط فرمان به این شکله

yarn lint
yarn lint:fix
// OR
npm run lint
npm run lint:fix

کانفیگ ESLint برای pre-commit

شما با استفاده از pre-commit میتونید به صورت خودکار قوانین ESLint رو قبل از اینکه دولاپری بخواد کامیتش رو انجام بده بررسی کنید و اگه error ای در قوانین باشه جلوی کامیتش رو بگیرید.

"scripts": {
  "precommit-msg": "echo 'Pre-commit checks...' && exit 0"
},
"pre-commit": [
  "precommit-msg",
  "lint"
]

نصب و کانفیگ Prettier

Prettier is an opinionated code formatter. It enforces a consistent style by parsing your code and re-printing it with its own rules that take the maximum line length into account, wrapping code when necessary.

اکستنشن Prettier برای VSCode

https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode

خط فرمان Prettier

yarn add -D prettier-eslint-cli 

کانفیگ اسکریپت Prettier در package.json

{
  "scripts": {
    "prettier": "./node_modules/.bin/prettier-eslint \"src/**/*.tsx\"",
    "prettier:fix": "./node_modules/.bin/prettier-eslint --write \"src/**/*.tsx\" \"src/**/*.ts\"",
  }
}

طرز استفاده از اسکریپت بالا در خط فرمان به این شکله

yarn prettier
yarn prettier:fix
// OR
npm run prettier
npm run prettier:fix

کانفیگ VSCode برای ESLint و Prettier

فایل زیر رو در روت پروژه ایجاد و خطوط زیر رو در اون اضاف کنید.

.vscode/settings.json

{
  "prettier.eslintIntegration": true,
  "editor.formatOnSave": true
}

خط اول برای integration اکستنشن Prettier با ESLint و خط دوم برای اینکه بعد از هر بار فشردن کلیدهای Ctrl + S اکستنشن Prettier به طور خودکار اجرا بشه.

اگه سوالی راجب rule های ESLint من داشتین، میتونید در قسمت کامنت‌ها ازم بپرسین