ESLint

安裝 ESLint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
import js from '@eslint/js';
import vue from 'eslint-plugin-vue';
import globals from 'globals';
// 使用 auto-import 時需設定
// import autoImportGlobals from './.eslint-auto-import.js';

export default [
js.configs.recommended,
...vue.configs['flat/recommended'],
{
languageOptions: {
ecmaVersion: 2021,
sourceType: 'module',
globals: {
...globals.browser,
...globals.es2021,
...globals.node,
// 使用 auto-import 時需設定
// ...autoImportGlobals.globals
}
},
rules: {
// ========== 基本規則 ==========
// 禁用 var,必須使用 const 或 let
'no-var': 'error',
'prefer-const': 'error',

// 分號使用規則
semi: ['error', 'always'],
'semi-spacing': ['error', { before: false, after: true }],

// 引號使用規則 - 統一使用單引號
quotes: ['error', 'single', {
avoidEscape: true,
allowTemplateLiterals: true
}],

// ========== 格式化 ==========
// 縮排規則 - 使用 2 個空格
indent: ['error', 2, {
SwitchCase: 1,
VariableDeclarator: 1,
outerIIFEBody: 1,
MemberExpression: 1,
FunctionDeclaration: { parameters: 1, body: 1 },
FunctionExpression: { parameters: 1, body: 1 },
CallExpression: { arguments: 1 },
ArrayExpression: 1,
ObjectExpression: 1,
ImportDeclaration: 1,
flatTernaryExpressions: false,
ignoreComments: false
}],

// 換行和空行規則
'max-len': ['warn', {
code: 100,
ignoreUrls: true,
ignoreStrings: true,
ignoreTemplateLiterals: true
}],
'eol-last': ['error', 'always'],
'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 1 }],

// 逗號規則
'comma-dangle': ['error', 'never'],
'comma-spacing': ['error', { before: false, after: true }],
'comma-style': ['error', 'last'],

// 括號規則
'brace-style': ['error', '1tbs', { allowSingleLine: true }],
curly: ['error', 'all'],

// ========== 變數命名規則 ==========
// 駝峰命名法
camelcase: ['error', {
properties: 'always',
ignoreDestructuring: false,
ignoreImports: false,
ignoreGlobals: false
}],

// 禁止未使用的變數
'no-unused-vars': ['error', {
vars: 'all',
args: 'after-used',
ignoreRestSiblings: true
}],

// 變數必須先宣告後使用
'no-undef': 'error',

// ========== 函數規則 ==========
// 函數名稱規則
'func-names': ['warn', 'as-needed'],
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],

// 箭頭函數規則
'arrow-spacing': ['error', { before: true, after: true }],
'arrow-parens': ['error', 'as-needed'],
'prefer-arrow-callback': ['error', { allowNamedFunctions: false }],

// ========== 物件和陣列規則 ==========
// 物件簡寫語法
'object-shorthand': ['error', 'always'],
'quote-props': ['error', 'as-needed'],

// 解構賦值
'prefer-destructuring': ['warn', {
array: true,
object: true
}, {
enforceForRenamedProperties: false
}],

// ========== 比較運算符規則 ==========
// 使用嚴格等號
eqeqeq: ['error', 'always', { null: 'ignore' }],
'no-eq-null': 'off',

// ========== 錯誤處理規則 ==========
// 禁止空的 catch 區塊
'no-empty': ['error', { allowEmptyCatch: false }],

// Promise 錯誤處理
'prefer-promise-reject-errors': 'error',

// ========== 安全性規則 ==========
// 禁止使用 eval
'no-eval': 'error',
'no-implied-eval': 'error',

// 禁止使用全域變數
'no-implicit-globals': 'error',

// 禁止修改參數
'no-param-reassign': ['error', { props: false }],

// ========== 程式碼品質規則 ==========
// 禁止 console.log (除了開發環境)
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'warn',

// 禁止未使用的表達式
'no-unused-expressions': ['error', {
allowShortCircuit: true,
allowTernary: true
}],

// ========== 註解規則 ==========
// JSDoc 相關
'spaced-comment': ['error', 'always', {
line: { markers: ['/'] },
block: { markers: ['*'], balanced: true }
}],

// ========== 模組化規則 ==========
// ES6 模組
'prefer-template': 'error',
'template-curly-spacing': ['error', 'never']
}
},
// Vue
{
files: ['**/*.vue'],
rules: {
// Vue 檔案命名
'vue/match-component-file-name': ['error', {
extensions: ['vue'],
shouldMatchCase: true
}],
'vue/multi-word-component-names': 'off',
'vue/component-api-style': ['error', ['script-setup', 'composition']],
'vue/prop-name-casing' : ['off', 'camelCase'],
'vue/attribute-hyphenation': 'off',
'eslintvue/v-on-event-hyphenation':'off',
'vue/component-definition-name-casing': ['off', 'PascalCase'],
'vue/component-name-in-template-casing': ['off', 'kebab-case']
}
},
{
ignores: ['dist/', 'node_modules/', 'public/', 'vite.config.js']
}
];