anh lam web image

Setup Eslint và Prettier cho dự án React theo chuẩn coding style guide của Airbnb

Ai cũng từng phải trải qua giai đoạn code bị đồng nghiệp chửi, sếp chửi và mọi người chửi, sau khi push code lên cho mọi người review thì nhận được rất nhiều comments là “em phải sửa lại dòng này, dòng này phải viết như thế này mới đúng”, “Sao chỗ này em có dấu chấm phẩy mà ở chỗ này em không có dấu chấm phẩy”, “em có thể sắp xếp lại thứ tự import thư viện cho nó đẹp mắt hơn được không”, “Sao lúc em dùng tab và lúc em dùng space vậy?”, “Sao em đặt tên hàm có lúc là camelCase có lúc là PascalCase vậy?”  v.v. Vậy lý do là gì để mọi người chửi mình như thế? Tại sao những người dẫn dắt team (Leader) không đặt ra chuẩn chung cho team để team follow theo. Khi không có chuẩn chung thì mọi người vừa mất thời gian review và trong dự án của mình nếu có 10 người thì không thể nào style code của 10 người đều giống nhau hết, có người code kiểu A, có người code kiểu B, có người code kiểu XYZ. Đó là vấn đề cần phải khắc phục ngay lúc này. 

Chúng ta cần có một chuẩn chung trong dự án, và nếu thành viên nào code không đúng chuẩn chung đó, thì IDE của họ sẽ tự nhắc nhở họ. Có rất nhiều coding style guide khác nhau như: Airbnb, Idiomatic.js, Google JavaScript Style Guide, JavaScript Standard Style v.v. Trong bài viết này mình sẽ tập trung vào Airbnb style guide, đây là chuẩn mà mình thấy nhiều pro đề xuất sử dụng và mình cũng đang áp dụng chuẩn này vào các dự án của mình. Các bạn có thể truy cập vào trang của Airbnb https://airbnb.io/javascript/ ngoài Javascript style guide, Airbnb còn có Ruby style guide, CSS & Sass, CSS in Javascript v.v. Ở bài viết này mình sẽ chỉ nói về cách setup coding style guide cho dự án React, các bạn có thể vào đường dẫn này để tham khảo về các chuẩn mà Airbnb đề cập https://airbnb.io/javascript/react/, Ở đây Airbnb có đưa ra các tình huống thế nào là bad code và thế nào là good code.

Eslint là gì?

Theo trang chính của Eslint thì Eslint là một open source project nó giúp chúng ta phân tích, tìm kiếm và fix lỗi tự động trong khi code. Plugin của Eslint hỗ trợ hầu hết trên các Code Editor. Ngoài ra chúng ta còn có thể viết ra những custom rule và tích hợp vào Eslint nếu chúng ta muốn. Đây là trang chính của Eslint: https://eslint.org/

Prettier là gì?

Theo trang chính của Prettier thi Prettier là một code formatter, Nó sẽ ghi lại code của chúng ta dựa trên độ dài tối đa của mỗi dòng code mà chúng ta cấu hình, nó giúp code chúng ta trở nên đẹp hơn, sạch sẽ và nhất quán hơn, mà không làm ảnh hưởng đến logic code. Ví dụ:

Đoạn code này nó vừa trong một dòng nên Prettier sẽ giữ nguyên và ko format code.

foo(arg1, arg2, arg3, arg4);

Đoạn code này nó quá dài để hiển thị trên cùng 1 dòng nó sẽ phải format lại.

foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());

Đây là đoạn code mà Prettier đã giúp chúng ta format lại. 

foo(

  reallyLongArg(),

  omgSoManyParameters(),

  IShouldRefactorThis(),

  isThereSeriouslyAnotherOne()

);

Các bạn có thể truy cập để đọc thêm tại: https://prettier.io/docs/en/

Tại sao phải setup Eslint và Prettier cho dự án?

Như mình cũng đã nói ở đầu bài, chúng ta cần đưa ra chuẩn chung khi code cho dự án, nó sẽ tiết kiệm được thời gian làm code review và giúp dự án trông có vẻ chuyên nghiệp và nhất quán hơn. Chúng ta không thể nào tự khẳng định rằng chúng ta code nhất quán từ đầu đến cuối mà không xảy ra bất kỳ sai sót nào, vậy nên Eslint và Prettier sẽ là công cụ đắc lực giúp code chúng ta và các thành viên trong team đẹp hơn, nhất quán hơn.

Khởi tạo dự án React với Eslint?

Khởi tạo dự án React bằng dòng lệnh sau:

npx create-react-app react-eslint-and-prettier-example-typescript --template typescript

Install eslint và các thư viện cần thiết

npm i -–save-dev eslint eslint-config-airbnb @typescript-eslint/eslint-plugin @typescript-eslint/parser

Tạo file .eslintrc.eslintignore ngang hàng với file package.json

Nội dung của file  .eslintignore

build/*

.eslintignore sẽ giúp eslint hiểu là những folder nào và file nào mà eslint không nên scan qua. 

Nội dung của file .eslintrc

{
  "env": {
    "browser": true,
    "jest": true
  },
  "plugins": [
    "@typescript-eslint",
    "react"
  ],
  "settings": {
    "react": {
      "pragma": "React",
      "version": "18.0"
    },
    "import/resolver": {
      "node": {
        "extensions": [
          ".js",
          ".jsx",
          ".ts",
          ".tsx"
        ],
        "moduleDirectory": [
          "node_modules",
          "./"
        ]
      }
    }
  },
  "extends": [
    "airbnb",
    "airbnb/hooks"
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "rules": {
    "no-console": "warn",
    "no-eval": "error",
    "react/react-in-jsx-scope": "off",
    "react/jsx-filename-extension": [
      2,
      {
        "extensions": [
          ".js",
          ".jsx",
          ".ts",
          ".tsx"
        ]
      }
    ],
    "import/extensions": [
      "error",
      "ignorePackages",
      {
        "js": "never",
        "jsx": "never",
        "ts": "never",
        "tsx": "never"
      }
    ]
  }
}

Giải thích về một số cấu hình trong file .eslintrc

env” nơi chứa những cấu hình cho environment, ở đây trong dự án nếu chúng ta có viết unit test bằng jest thì ta thêm “jest”: true vào. Còn “browser”: true khi trong dự án chúng ta có sử dụng các thành phần của browser như window object hoặc localStorage v.v. thì ta thêm “browser”: true vào để eslint không báo lỗi những dòng code này.

"plugins": Nơi đây chứa những plugin nào cho eslint config.

"settings": Nơi chứa những cài đặt, ở đây mình có cài đặt để nói cho eslint hiểu là mình đang dùng React version 18. “import/resolver” nơi nói cho Eslint hiểu root directory của dự án nằm ở đâu và các file extensions có trong dự án. Ở đây mình có khai báo “./” trong moduleDirectory, tương tự như tsconfig.json mình có baseUrl=”.” thì khi mình import ví dụ: import App from ‘src/App’ thì eslint sẽ hiểu src là folder nào.

extends”:  Cấu hình cho Eslint biết sẽ sử dụng những rule được kế thừa từ thư viện nào ở đây mình sử dụng những rule của airbnb và airbnb/hooks.

parserOptions”: nói cho Eslint hiểu mình sẽ sử dụng parser nào.

rules”: Cuối cùng nhưng không kém phần quan trọng đó là rules, mình có thể thêm các rule và cấu hình các rule tùy thích, có thể thiết lập cho các rule chỉ là warning hay sẽ thông báo error luôn.

Trong file package.json phần scripts chúng ta thêm 2 dòng này vào nhé:

"lint": "eslint . --ext .js,.jsx,.ts,.tsx"
"lint:fix": "eslint --fix .  --ext .js,.jsx,.ts,.tsx"

Nào hãy chạy thử bằng lệnh

npm run lint

Sau đó chạy lệnh sau để fix tự động

npm run lint:fix

Nếu Eslint không thể fix cho chúng ta lỗi được thông báo hãy đọc thông báo lỗi và tự fix bằng tay nhé.

Reorder import trong Eslint

Đây là rule mà mình thấy rất hữu ích, đôi khi chúng ta viết code chúng ta sẽ không để ý rằng, mọi người import thư viện và các file không theo một nguyên tắc nào cả nhìn vào rất khó chịu và xấu. Thế nên hãy sử dụng plugins simple-import-sort nó sẽ giúp chúng ta sắp xếp lại các import theo một quy tắc nhất định.

Để cài đặt được plugin simple-import-sort hãy chạy dòng lệnh này:

npm i --save-dev eslint-plugin-simple-import-sort

Ở phần plugins ta thêm vào simple-import-sort:

"plugins": ["@typescript-eslint", "react", "simple-import-sort"],

Ở phần rules ta thêm rule này vào:

"simple-import-sort/imports": [
  "error",
  {
    "groups": [
      // Packages `react` related packages come first.
      ["^react", "^@?\\w"],
      // Internal packages.
      ["^(@|src)(/.*|$)"],
      // Side effect imports.
      ["^\\u0000"],
      // Parent imports. Put `..` last.
      ["^\\.\\.(?!/?$)", "^\\.\\./?$"],
      // Other relative imports. Put same-folder imports and `.` last.
      ["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
      // Style imports.
      ["^.+\\.?(css)$"]
    ]
  }
]

Theo cấu hình này thứ tự ưu tiên sẽ là:

  1. React package được đưa lên đầu, ví dụ: import React from ‘react’;
  2. Sau đó là các thư viện từ node_modules, ví dụ: import { Button } from ‘antd’;
  3. Sau đó đến internal package ví dụ: như import App from ‘src/App’;
  4. Sau đó là side effect import ví dụ: import  ‘any-package’;
  5. Theo sau là import với relative path ví dụ: import Result from ‘../components/Result’;
  6. Tiếp theo là same-folder import ví dụ: import CustomModal from ‘./CustomModal’;
  7. Và cuối cùng là style import ví dụ: import ‘./styles.scss’;

Sau khi cấu hình xong hãy thử sắp xếp lại import một cách lung tung và để cho eslint reorder lại giúp bạn. Tính năng này thật cool phải không.

Full cấu hình tại thời điểm này:

{
  "env": {
    "browser": true,
    "jest": true
  },
  "plugins": ["@typescript-eslint", "react", "simple-import-sort"],
  "settings": {
    "react": {
      "pragma": "React",
      "version": "18.0"
    },
    "import/resolver": {
      "node": {
        "extensions": [".js", ".jsx", ".ts", ".tsx"],
        "moduleDirectory": ["node_modules", "./"]
      }
    }
  },
  "extends": ["airbnb", "airbnb/hooks"],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "rules": {
    "no-console": "warn",
    "no-eval": "error",
    "react/react-in-jsx-scope": "off",
    "react/jsx-filename-extension": [2, { "extensions": [".js", ".jsx", ".ts", ".tsx"] }],
    "import/extensions": [
      "error",
      "ignorePackages",
      {
        "js": "never",
        "jsx": "never",
        "ts": "never",
        "tsx": "never"
      }
    ],
    "simple-import-sort/imports": [
      "error",
      {
        "groups": [
          // Packages `react` related packages come first.
          ["^react", "^@?\\w"],
          // Internal packages.
          ["^(@|src)(/.*|$)"],
          // Side effect imports.
          ["^\\u0000"],
          // Parent imports. Put `..` last.
          ["^\\.\\.(?!/?$)", "^\\.\\./?$"],
          // Other relative imports. Put same-folder imports and `.` last.
          ["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
          // Style imports.
          ["^.+\\.?(css)$"]
        ]
      }
    ]
  }
}

Cài đặt Prettier với Eslint?

Install prettier bằng dòng lệnh sau:

npm i --save-dev prettier eslint-plugin-prettier eslint-config-prettier

Bạn có thể cấu hình Prettier trong file .prettierrc nhưng ở đây mình sẽ cấu hình trực tiếp trong file .eslintrc luôn.

Phần “pluggins” bạn thêm prettier vào:

"plugins": ["@typescript-eslint", "react", "prettier", "simple-import-sort"],

Phần “extends” cũng thêm prettier vào:

"extends": ["airbnb", "airbnb/hooks", "prettier"],

Và phần “rules” thêm rule cấu hình prettier vào:

"prettier/prettier": [
  "error",
  {
      "printWidth": 80,
      "trailingComma": "es5",
      "semi": true,
      "no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
      "no-unused-vars": ["error"]
  }
]

Cấu hình .eslintrc đầy đủ:

{
    "env": {
        "browser": true,
        "jest": true
    },
    "plugins": [
        "@typescript-eslint",
        "react",
        "prettier",
        "simple-import-sort"
    ],
    "settings": {
        "react": {
            "pragma": "React",
            "version": "18.0"
        },
        "import/resolver": {
            "node": {
                "extensions": [
                    ".js",
                    ".jsx",
                    ".ts",
                    ".tsx"
                ],
                "moduleDirectory": [
                    "node_modules",
                    "./"
                ]
            }
        }
    },
    "extends": [
        "airbnb",
        "airbnb/hooks",
        "prettier"
    ],
    "parser": "@typescript-eslint/parser",
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        }
    },
    "rules": {
        "no-console": "warn",
        "no-eval": "error",
        "react/react-in-jsx-scope": "off",
        "react/jsx-filename-extension": [
            2,
            {
                "extensions": [
                    ".js",
                    ".jsx",
                    ".ts",
                    ".tsx"
                ]
            }
        ],
        "simple-import-sort/imports": [
            "error",
            {
                "groups": [
                    // Packages `react` related packages come first.
                    [
                        "^react",
                        "^@?\\w"
                    ],
                    // Internal packages.
                    [
                        "^(@|src)(/.*|$)"
                    ],
                    // Side effect imports.
                    [
                        "^\\u0000"
                    ],
                    // Parent imports. Put `..` last.
                    [
                        "^\\.\\.(?!/?$)",
                        "^\\.\\./?$"
                    ],
                    // Other relative imports. Put same-folder imports and `.` last.
                    [
                        "^\\./(?=.*/)(?!/?$)",
                        "^\\.(?!/?$)",
                        "^\\./?$"
                    ],
                    // Style imports.
                    [
                        "^.+\\.?(css)$"
                    ]
                ]
            }
        ],
        "import/extensions": [
            "error",
            "ignorePackages",
            {
                "js": "never",
                "jsx": "never",
                "ts": "never",
                "tsx": "never"
            }
        ],
        "prettier/prettier": [
            "error",
            {
                "printWidth": 80,
                "trailingComma": "es5",
                "semi": true,
                "no-mixed-spaces-and-tabs": [
                    "error",
                    "smart-tabs"
                ],
                "no-unused-vars": [
                    "error"
                ]
            }
        ]
    }
}

Thế là đã xong, bạn hãy thử chỉnh sửa code, thụt dòng không đúng, xóa dấu chấm phẩy hoặc để một dòng code quá dài, Prettier sẽ giúp bạn format lại code khiến nó đẹp hơn. Bạn hãy thử chạy lệnh và chờ đợi kết quả:

npm run lint:fix

Tổng Kết

Tóm lại Eslint và Prettier là 2 trợ thủ đắc lực trong việc giúp cho developer điều chỉnh lại code của mình cho đồng nhất với team, và nhìn vào dự án nếu có eslint thì trông có vẻ rất chuyên nghiệp hơn so với dự án mỗi người code một kiểu.

Đây là link github các bạn có thể checkout full source code tại đây: https://github.com/PhongVX/react-eslint-and-prettier-example-typescript