Skip to content

Instantly share code, notes, and snippets.

@languanghao
Created December 20, 2024 10:15
Show Gist options
  • Save languanghao/a983fad14e2754a82be7508afeccc6ba to your computer and use it in GitHub Desktop.
Save languanghao/a983fad14e2754a82be7508afeccc6ba to your computer and use it in GitHub Desktop.
vue3+eslint8 eslint config. Support mix lang="js" and lang="ts"
const { resolve } = require('node:path')
const fs = require('node:fs')
/**
* 不是ts写的vue文件
* @type {string[]}
*/
const vueJsFiles = []
/**
* 用ts写的vue文件
* @type {string[]}
*/
const vueTsFiles = []
// 遍历所有vue文件,将ts写的vue文件放到vueTsFiles,js写的vue文件放到vueJsFiles
require('fast-glob')
.sync(['**/*.vue', '!**/node_modules/**'], { cwd: resolve() })
.forEach((filePath) => {
const content = fs.readFileSync(filePath, 'utf8')
if (/<script[^>]*\blang\s*=\s*"ts"[^>]*>/i.test(content))
vueTsFiles.push(filePath)
else
vueJsFiles.push(filePath)
})
const espree = require('eslint')
const tsParser = require('@typescript-eslint/parser')
/**
* vue文件的规则
*/
const sfcFilesRule = {
'vue/html-indent': [
'error',
2,
{
attribute: 1,
baseIndent: 1,
closeBracket: 0,
alignAttributesVertically: true,
ignores: [],
},
],
'vue/script-indent': ['error', 2, {
baseIndent: 0,
switchCase: 1,
}],
'vue/max-attributes-per-line': [
'error',
{
singleline: 4,
multiline: 1,
},
],
'vue/html-closing-bracket-newline': ['error', {
singleline: 'never',
multiline: 'always',
}],
'vue/first-attribute-linebreak': ['error', {
singleline: 'beside',
multiline: 'beside',
}],
'vue/valid-define-props': 'off',
'vue/v-on-event-hyphenation': ['error', 'always', {
autofix: false,
ignore: ['update:modelValue'],
}],
'vue/v-slot-style': ['error', {
atComponent: 'shorthand',
default: 'shorthand',
named: 'shorthand',
}],
'vue/singleline-html-element-content-newline': 'off',
'vue/html-closing-bracket-spacing': ['error', {
startTag: 'never',
endTag: 'never',
selfClosingTag: 'always',
}],
'vue/multi-word-component-names': 'off',
}
/**
* ts文件的规则
*/
const tsFileRules = {
'@stylistic/indent': ['error', 2], // 缩进长度
'@stylistic/quotes': ['error', 'single'], // 使用单引号
'@stylistic/semi': ['error', 'never'], // 不允许结尾分号
'@stylistic/comma-dangle': ['error', {
arrays: 'always-multiline',
objects: 'always-multiline',
imports: 'always-multiline',
exports: 'always-multiline',
functions: 'ignore',
}], // 结尾的逗号样式
'@stylistic/member-delimiter-style': ['error', {
multiline: { delimiter: 'none' },
singleline: { delimiter: 'comma' },
}], // 接口的成员不允许分号结尾
'@stylistic/no-trailing-spaces': ['error', {
skipBlankLines: true,
ignoreComments: true,
}], // 行尾空白
'@stylistic/brace-style': ['error', '1tbs', { allowSingleLine: true }], // 大括号样式
'no-tabs': ['error', { allowIndentationTabs: true }], // 不允许使用 tab
'object-curly-spacing': ['error', 'always'], // 确保括号内的间距一致
'no-mixed-spaces-and-tabs': 'error', // 不允许混合空格和 tab
'space-before-function-paren': ['error', {
anonymous: 'always',
named: 'never',
asyncArrow: 'always',
}], // 函数括号前后空格
'object-property-newline': ['error', { allowAllPropertiesOnSameLine: true }], // 对象属性换行
'newline-per-chained-call': ['error', { ignoreChainWithDepth: 3 }], // 链式调用换行
'no-unused-vars': 'off',
'@typescript-eslint/no-non-null-assertion': 'off', // 允许使用!! 非空判断
'@typescript-eslint/explicit-module-boundary-types': 'off', // 每个方法都要明确返回值
'@typescript-eslint/array-type': 'warn', // 使用 T[] 替代 Array<T>
'@typescript-eslint/no-require-imports': 'off', // 允许使用 require 导入
// 下面这个规则有bug,使用ide的配置强制实现,分别对应在 .vscode/settings.json 和 .editorconfig
// '@typescript-eslint/consistent-type-imports': ['error', {
// prefer: 'type-imports',
// disallowTypeAnnotations: true,
// fixStyle: 'separate-type-imports',
// }], // 使用 import type 替代 import
// 允许特殊字符
'no-irregular-whitespace': ['error', {
skipStrings: true,
skipComments: true,
skipRegExps: true,
skipTemplates: true,
}],
}
/**
* js文件规则
*/
const jsFileRules = {
// Common
'semi': ['error', 'never'],
'curly': ['error', 'multi-or-nest', 'consistent'],
'quotes': ['error', 'single'],
'quote-props': ['error', 'consistent-as-needed'],
'no-param-reassign': 'off',
'array-bracket-spacing': ['error', 'never'],
'brace-style': ['error', 'stroustrup', { allowSingleLine: true }],
'block-spacing': ['error', 'always'],
'camelcase': 'off',
'comma-spacing': ['error', {
before: false,
after: true,
}],
'comma-style': ['error', 'last'],
'comma-dangle': ['error', 'always-multiline'],
'no-constant-condition': 'warn',
// 'no-debugger': 'error',
// 'no-console': ['error', { allow: ['warn', 'error'] }],
'no-cond-assign': ['error', 'always'],
'func-call-spacing': ['off', 'never'],
'key-spacing': ['error', {
beforeColon: false,
afterColon: true,
}],
'indent': ['error', 2, {
SwitchCase: 1,
VariableDeclarator: 1,
outerIIFEBody: 1,
}],
'no-restricted-syntax': [
'error',
// 'DebuggerStatement',
'LabeledStatement',
'WithStatement',
],
'object-curly-spacing': ['error', 'always'],
'no-return-await': 'off',
'space-before-function-paren': [
'error',
{
anonymous: 'always',
named: 'never',
asyncArrow: 'always',
},
],
// es6
'no-var': 'error',
'prefer-const': [
'error',
{
destructuring: 'all',
ignoreReadBeforeAssign: true,
},
],
'prefer-arrow-callback': [
'error',
{
allowNamedFunctions: false,
allowUnboundThis: true,
},
],
'object-shorthand': [
'error',
'always',
{
ignoreConstructors: false,
avoidQuotes: true,
},
],
'prefer-exponentiation-operator': 'error',
'prefer-rest-params': 'error',
'prefer-spread': 'error',
'prefer-template': 'error',
'template-curly-spacing': 'error',
'arrow-parens': ['error', 'as-needed', { requireForBlockBody: true }],
'generator-star-spacing': 'off',
'spaced-comment': ['error', 'always', {
line: {
markers: ['/'],
exceptions: ['/', '#'],
},
block: {
markers: ['!'],
exceptions: ['*'],
balanced: true,
},
}],
// best-practice
'array-callback-return': 'error',
'block-scoped-var': 'error',
'consistent-return': 'off',
'complexity': ['off', 11],
'eqeqeq': ['error', 'smart'],
'no-alert': 'warn',
'no-case-declarations': 'error',
'no-multi-spaces': 'error',
'no-multi-str': 'error',
'no-with': 'error',
'no-void': 'error',
'no-useless-escape': 'off',
'vars-on-top': 'error',
'require-await': 'off',
'no-return-assign': 'off',
'operator-linebreak': ['error', 'before'],
'max-statements-per-line': ['error', { max: 1 }],
}
module.exports = {
env: {
node: true,
},
globals: {
process: true,
},
plugins: ['vue', '@stylistic', '@typescript-eslint'],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
},
overrides: [
{
files: ['**/*.js', '**/*.jsx', '**/*.cjs', '**/*.mjs', ...vueJsFiles],
extends: [
'plugin:vue/vue3-recommended',
'eslint:recommended',
],
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 2022,
ecmaFeatures: {
jsx: true,
},
parser: espree,
},
rules: {
...jsFileRules,
...sfcFilesRule,
'vue/no-v-html': 'off',
},
},
{
files: ['**/*.ts', '**/*.tsx', '**/*.cts', '**/*.mts', ...vueTsFiles],
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
],
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 2022,
ecmaFeatures: {
jsx: true,
},
parser: tsParser,
},
rules: {
...tsFileRules,
...sfcFilesRule,
},
},
],
}
@languanghao
Copy link
Author

If you migate a vue project from js to ts, eslint can't apply vue sfc file to different rules by <script lang="ts"> or <script lang="js">.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment