[Note] ESLint & Prettier
簡介
ESLint 是一個用來檢查 JavaScript (也支援 TypeScript、React 等)的靜態程式碼檢查工具 (Linter),幫助開發者維持一致的程式碼風格和品質、協助檢查程式碼的錯誤和減少潛在問題。
主要功能
- 程式碼錯誤偵測(例如忘記加
;、變數未定義) - 程式碼風格檢查(如單引號
'與雙引號"的使用、tab 與 space) - 自動修復常見問題(
eslint --fix) - 支援自訂規則與插件擴充
核心概念
- 規則 Rules:ESLint 的核心,定義了程式碼檢查的標準。每個規則都有一個唯一的名稱和對應的配置選項。
- 插件 Plugins:擴展 ESLint 的工具,可以添加新的規則、解析器、報告器等。
- 共享配置 Shareable Configs:包含一系列預定億的規則和配置,便於共享和重用。
ESLint CLI
# 安裝 ESLint
$ npm init @eslint/config@latest
$ pnpm create @eslint/config@latest
# Run ESLint: npx eslint [options] [file|dir|glob]*
$ npx eslint # 檢查所有檔案,等同於 npx eslint .
$ npx eslint yourfile.js
$ npx eslint file1.js file2.js
$ npx eslint "lib/**"
$ pnpm dlx eslint yourfile.js
# Debug
$ npx eslint --debug file.js
$ npx eslint --print-config file.js # 使用時機:當你不確定為什麼 linting 沒有產生預期的結果
$ npx eslint --inspect-config # 使用時機:當你不確定某個檔案是否有套用到特定的 configuration object
# Fix
$ npx eslint --fix
Configuration 設定檔
提示
ESLint v9 引入 **Flat Config(扁平設定)**成為預設格式,是目前最推薦的設定方式,取代傳統的 .eslintrc 系列設定檔。
執行 npm init @eslint/config 後會自動生成 ESLint 的設定檔。有兩種主要格式:
-
舊格式:Legacy Config(階層式設定)
- 檔案名稱:
.eslintrc.js、.eslintrc.json、.eslintrc.yaml、.eslintrc.yml... - 特色:階層式(hierarchical)設定,設定分散在多個檔案,會從專案目錄往上尋找並合併多層設定
- 問題:設定覆蓋行為複雜,難以追蹤最終規則來源
- 檔案名稱:
-
新格式:Flat Config(扁平式設定)- ESLint v9+
- 檔案名稱:
eslint.config.js、eslint.config.mjs、eslint.config.cjs... - 特色:單一設定檔,扁平化陣列結構,順序決定優先級,更直覺可控
- 優勢:每個元素是一個設定物件,所有設定集中處理、使用 ES modules、支援可編程設定
- 檔案名稱:
Flat Config
Flat Config 是一個由設定物件 (configuration objects) 所組成的陣列,放置於專案根目錄 (root):
eslint.config.js
export default [
// 設定物件 1
{
files: ['**/*.js'],
rules: {
/* 規則 */
},
},
// 設定物件 2
{
files: ['**/*.ts'],
rules: {
/* 規則 */
},
},
// ... 更多設定物件
];
匯出方式
Export 一定要是 ESM 格式 (export default)。Flat Config 支援兩種匯出方式:
-
直接匯出陣列 (
export default [ ... ])eslint.config.jsimport js from '@eslint/js';
export default [
js.configs.recommended,
{
files: ['**/*.js'],
rules: {
'no-console': 'warn',
},
},
]; -
使用
defineConfig()helper(官方建議的最佳實踐)eslint.config.jsimport { defineConfig } from 'eslint/config';
import js from '@eslint/js';
export default defineConfig([
js.configs.recommended,
{
files: ['**/*.js'],
rules: {
'no-console': 'warn',
},
},
]);
Configuration Objects 設定物件
每個設定物件定義了一組規則和其適用範圍,包含以下欄位:
核心欄位
| 欄位 | 類型 | 說明 | 範例 |
|---|---|---|---|
name | string | 設定物件的名稱(用於 debug) | "TypeScript files" |
files | string[] | 適用的檔案匹配模式(glob pattern) | ['**/*.ts', '**/*.tsx'] |
ignores | string[] | 排除的檔案匹配模式(glob pattern) | ['node_modules/**', 'dist/**'] |
語言設定相關
| 欄位 | 類型 | 說明 | 範例 |
|---|---|---|---|
languageOptions | object | 語言解析設定 | { parser, parserOptions, globals } |
languageOptions.parser | object | 程式碼解析器 | @typescript-eslint/parser |
languageOptions.parserOptions | object | 解析器選項 | { ecmaVersion: 'latest' } |
languageOptions.globals | object | 全域變數定義 | globals.browser |
規則相關
| 欄位 | 類型 | 說明 | 範例 |
|---|---|---|---|
extends | object | 允許套用現成的規則集,可以是官方或是第三方的 | { 'react-hooks': reactHooks } |
plugins | object | 引入外部規則插件(如 react, prettier) | { 'react-hooks': reactHooks } |
rules | object | 啟用的規則,可以覆蓋 extends 或 plugins | { 'no-console': 'warn' } |
settings | object | 提供 plugin 共用的設定 | { react: { version: 'detect' } } |
其他設定
| 欄位 | 類型 | 說明 | 範例 |
|---|---|---|---|
processor | string or object | 檔案處理器 | 用於處理非 JS 檔案(如 Markdown) |
設定物件執行順序
在 Flat Config 中,陣列順序 決定設定的優先級
- 後面的設定會覆蓋前面的設定
- 相同檔案匹配時,後面的規則優先
- Prettier 設定通常放在最後,確保格式規則不被覆蓋
export default [
js.configs.recommended, // 1. 基本規則
tsConfig, // 2. TypeScript 規則(可能覆蓋基本規則)
reactConfig, // 3. React 規則(可能覆蓋前面規則)
prettierConfig, // 4. Prettier 規則(最後執行,避免衝突)
];
Rules 規則
規則是 ESLint 的核心,每個規則控制一個特定的程式碼檢查
"規則名稱": "狀態"
- 每個規則有一 個唯一的名稱,例如
"no-alert"、"no-console" - 每個規則有三種狀態:
"off"(0,關閉)、"warn"(1,警告)或"error"(2,錯誤) - 某些規則還支援額外參數,如
"indent": ["error", 2]設定縮排為 2 個空格。
規則狀態
{
rules: {
// 字串格式
'no-console': 'off', // 關閉規則
'no-alert': 'warn', // 警告等級
'no-debugger': 'error', // 錯誤等級
// 數字格式(等價)
'no-console': 0, // 關閉
'no-alert': 1, // 警告
'no-debugger': 2, // 錯誤
// 陣列格式(含參數)
'indent': ['error', 2], // 縮排 2 空格
'quotes': ['error', 'single'], // 使用單引號
'max-len': ['warn', { code: 100 }], // 最大行長度 100
}
}
常用規則範例
{
rules: {
// 程式碼品質
'no-unused-vars': 'error',
'no-undef': 'error',
'no-console': 'warn',
// 程式碼風格
'indent': ['error', 2],
'quotes': ['error', 'single'],
'semi': ['error', 'always'],
// TypeScript 特定規則
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
'@typescript-eslint/consistent-type-imports': 'error',
// React 特定規則
'react/jsx-uses-react': 'off',
'react/react-in-jsx-scope': 'off',
}
}
eslint.config.js 完整設定檔範例
eslint.config.js
import { defineConfig } from 'eslint/config';
import js from '@eslint/js';
import ts from '@typescript-eslint/eslint-plugin';
import parserTs from '@typescript-eslint/parser';
import prettier from 'eslint-plugin-prettier/recommended';
import globals from 'globals';
// 一定要是 ESM 格式 (export default)
export default defineConfig([
// 🟡 全域排除設定(不針對特定檔案)
{
name: 'Global ignores',
ignores: [
'node_modules',
'dist',
'build',
'coverage',
'public/mockServiceWorker.js',
],
},
// 🟢 JavaScript 基本規則(官方 js.configs.recommended)
{
...js.configs.recommended,
name: 'JavaScript base rules',
files: ['**/*.js', '**/*.jsx'],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
...globals.browser,
...globals.node,
},
},
rules: {
...js.configs.recommended.rules,
'no-console': 'warn',
'no-debugger': 'error',
},
},
// 🔵 TypeScript 支援 + ESLint plugin
{
name: 'TypeScript rules',
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
parser: parserTs,
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json',
ecmaFeatures: {
jsx: true,
},
},
globals: globals.browser,
},
plugins: {
'@typescript-eslint': ts,
},
rules: {
...ts.configs.recommended.rules,
'@typescript-eslint/no-unused-vars': ['warn'],
'@typescript-eslint/consistent-type-imports': [
'error',
{ prefer: 'type-imports' },
],
},
},
// ✨ Prettier 整合(格式錯誤會報錯)
{
files: ['**/*.{js,ts,tsx,jsx,json,md}'],
plugins: {
prettier,
},
rules: {
'prettier/prettier': 'error',
},
},
// 🔶 範例:特定檔案(如 test)使用 override 規則
{
name: 'Test files overrides',
files: ['**/*.test.{js,jsx,ts,tsx}', '**/*.spec.{js,jsx,ts,tsx}'],
rules: {
'@typescript-eslint/no-explicit-any': 'off', // 在測試檔案允許 any
'no-console': 'off',
},
},
// 🔸 範例:Markdown 檔案避免不必要的 lint
{
files: ['**/*.md'],
languageOptions: {
parser: undefined,
},
rules: {},
},
]);
忽略規則
忽略所有規則
忽略整個檔案:
// 放在檔案的最上方
/* eslint-disable */
忽略單行:
alert('foo'); // eslint-disable-line no-alert, quotes, semi
// eslint-disable-next-line no-alert
alert('foo');
忽略多行:
/* eslint-disable */
alert('foo');
/* eslint-enable */