ESLint9+Prettier3+Vue

37 阅读7分钟

package.json

{
  "devDependencies": {
    "@eslint/js": "9.39.2",
    "@stylistic/eslint-plugin": "5.6.1",
    "eslint": "9.39.2",
    "eslint-config-prettier": "10.1.8",
    "eslint-import-resolver-typescript": "4.4.4",
    "eslint-plugin-import": "2.32.0",
    "eslint-plugin-prettier": "5.5.4",
    "eslint-plugin-unused-imports": "4.3.0",
    "eslint-plugin-vue": "10.6.2",
    "globals": "16.5.0",
    "prettier": "3.7.4",
    "typescript-eslint": "8.50.0"
  }
}

eslint.config.mjs

import eslint from '@eslint/js';
import stylistic from '@stylistic/eslint-plugin';
import importPlugin from 'eslint-plugin-import';
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
import unusedImports from 'eslint-plugin-unused-imports';
import eslintPluginVue from 'eslint-plugin-vue';
import { defineConfig } from 'eslint/config';
import globals from 'globals';
import typescriptEslint from 'typescript-eslint';

export default defineConfig(
  {
    ignores: [
      'node_modules',
      'dist',
      'public',
      'release',
    ],
  },
  /**
   * 配置全局变量
   */
  {
    extends: [
      /** js推荐配置 */
      eslint.configs.recommended,
      /** ts推荐配置 */
      ...typescriptEslint.configs.recommended,
      /** vue推荐配置 */
      ...eslintPluginVue.configs['flat/recommended'],
    ],
    files: ['**/*.{ts,js,vue}'],
    plugins: {
      'import': importPlugin,
      'unused-imports': unusedImports,
    },
    languageOptions: {
      ecmaVersion: 'latest',
      sourceType: 'module',
      globals: {
        ...globals.browser, // window, document, console, etc.
        ...globals.node, // process, require, module, __dirname, etc.
        ...globals.electron, // electron-specific globals (if available)
        electronAPI: true, // 自定义全局变量
        // Electron 特有全局变量(如果 globals.electron 不存在)
        require: 'readonly', // Node.js require
        module: 'readonly', // Node.js module
        __dirname: 'readonly', // Node.js __dirname
        __filename: 'readonly', // Node.js __filename
        process: 'readonly', // Node.js process
        Buffer: 'readonly', // Node.js Buffer
        setTimeout: 'readonly', // Browser/Node.js
        clearTimeout: 'readonly', // Browser/Node.js
        setInterval: 'readonly', // Browser/Node.js
        clearInterval: 'readonly', // Browser/Node.js
      },
      parserOptions: {
        parser: typescriptEslint.parser,
        // 为 import 插件提供解析器
        ecmaFeatures: {
          modules: true,
        },
      },
    },
    settings: {
      // 为 import 插件提供路径解析
      'import/resolver': {
        node: {
          extensions: ['.js', '.ts', '.vue'],
        },
      },
    },
    rules: {
      // ===================== 核心 ESLint 基础规则 =====================
      'no-case-declarations': 'off', // 关闭switch case内变量声明校验
      'space-before-function-paren': 'off', // 关闭函数括号前空格格式校验
      'prefer-const': 'off', // 关闭优先使用const声明变量校验
      'no-undef': 'off', // 关闭未定义变量校验

      // ===================== Import 导入规范规则 =====================
      'import/first': 'error', // 强制导入语句放在文件顶部
      'import/newline-after-import': 'error', // 强制导入语句后空行
      'import/no-duplicates': 'error', // 禁止重复导入同一模块
      // 优化:仅管控导入语句内部成员排序,放弃整体排序(避免与 import/order 冲突)
      'sort-imports': [
        'error', // 启用导入语句排序校验
        {
          ignoreCase: false, // 排序区分大小写
          ignoreDeclarationSort: true, // 关键:关闭整体排序,仅管内部成员
          ignoreMemberSort: false, // 启用导入内部成员排序
          memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'], // 导入语法优先级
          allowSeparatedGroups: false, // 禁止空行分隔导入组
        },
      ],
      // 核心:管控导入语句整体顺序(内置→第三方→本地),与 sort-imports 功能隔离
      'import/order': [
        'error', // 启用导入模块来源排序校验
        {
          'groups': [
            'builtin', // Node.js内置模块
            'external', // 第三方库
            'internal', // 项目内部模块
            'parent', // 上级目录模块组
            'sibling', // 同级目录模块组
            'index', // 索引文件
            'object', // 导入对象
            'type', // 类型导入
          ],
          'pathGroups': [
            {
              pattern: 'vue', // 匹配vue模块
              group: 'external', // 归为第三方库组
              position: 'before', // 排在其他第三方库前
            },
          ],
          'pathGroupsExcludedImportTypes': ['builtin'], // 排除内置模块
          'newlines-between': 'always', // 不同组间强制空行
          'alphabetize': {
            order: 'asc', // 组内按字母升序排列
            caseInsensitive: true, // 排序忽略大小写
          },
        },
      ],
      'unused-imports/no-unused-imports': 'error', // 强制校验未使用的导入语句

      // ===================== TypeScript 专属规则  =====================
      '@typescript-eslint/ban-ts-ignore': 'off', // 关闭禁止使用@ts-ignore注释校验
      '@typescript-eslint/ban-ts-comment': 'off', // 关闭禁止使用ts相关注释校验
      '@typescript-eslint/ban-types': 'off', // 关闭禁止使用特定类型校验
      '@typescript-eslint/explicit-function-return-type': 'off', // 关闭强制函数显式返回类型校验
      '@typescript-eslint/no-explicit-any': 'off', // 关闭禁止使用any类型校验
      '@typescript-eslint/no-var-requires': 'off', // 关闭禁止使用require导入校验
      '@typescript-eslint/no-empty-function': 'off', // 关闭禁止空函数校验
      '@typescript-eslint/no-non-null-assertion': 'off', // 关闭禁止非空断言校验
      '@typescript-eslint/explicit-module-boundary-types': 'off', // 关闭模块边界类型显式声明校验
      '@typescript-eslint/no-empty-interface': 'off', // 关闭禁止空接口校验
      '@typescript-eslint/no-inferrable-types': 'off', // 关闭禁止显式声明可推断类型校验

      // ===================== Vue 专属规则 =====================
      // Vue 核心规范
      'vue/script-setup-uses-vars': 'error', // 强制校验script-setup中使用的变量
      'vue/no-reserved-component-names': 'off', // 关闭禁止使用保留字作为组件名校验
      'vue/custom-event-name-casing': 'off', // 关闭自定义事件名大小写规范校验
      'vue/one-component-per-file': 'off', // 关闭单文件仅包含一个组件校验
      'vue/multi-word-component-names': 'off', // 关闭组件名多单词校验
      'vue/no-v-html': 'off', // 关闭禁止使用v-html指令校验

      // Vue 模板格式/属性规则
      'vue/attributes-order': 'off', // 关闭模板属性排序校验
      'vue/html-closing-bracket-newline': 'off', // 关闭标签闭合括号换行校验
      'vue/max-attributes-per-line': 'off', // 关闭每行最大属性数校验
      'vue/multiline-html-element-content-newline': 'off', // 关闭多行元素内容换行校验
      'vue/singleline-html-element-content-newline': 'off', // 关闭单行元素内容换行校验
      'vue/attribute-hyphenation': 'off', // 关闭属性名连字符校验

      // Vue 组件属性/事件规则
      'vue/require-default-prop': 'off', // 关闭强制props设置默认值校验
      'vue/require-explicit-emits': 'off', // 关闭强制显式声明emits校验

      // Vue 标签自闭合规则(唯一启用的 Vue 格式规则)
      'vue/html-self-closing': [
        'error', // 启用标签自闭合规范校验
        {
          html: {
            void: 'always', // HTML空标签强制自闭合
            normal: 'never', // HTML普通标签禁止自闭合
            component: 'always', // Vue组件强制自闭合
          },
          svg: 'always', // SVG标签强制自闭合
          math: 'always', // Math标签强制自闭合
        },
      ],
    },
  },

  stylistic.configs.customize({
    indent: 2,
    quotes: 'single',
    semi: false,
    jsx: true,
    braceStyle: '1tbs',
    arrowParens: 'always',
  }),

  /**
   * prettier 配置
   * 会合并根目录下的.prettier.config.js 文件
   * @see https://github.com/prettier/eslint-plugin-prettier
   */
  eslintPluginPrettierRecommended,
);

prettier.config.js

/**
 * Prettier 代码格式化配置
 * @type {import('prettier').Config}
 * @see https://www.prettier.cn/docs/options.html Prettier 官方配置文档
 */
export default {
  /**
   * 尾逗号配置:强制在所有多行结构(对象、数组、函数参数等)末尾添加尾逗号
   * @value "all" 所有多行结构都加尾逗号(包括函数参数、对象、数组)
   * @value "es5" 仅在 ES5 兼容场景加(对象、数组),函数参数不加
   * @value "none" 不添加尾逗号
   */
  trailingComma: 'all',

  /**
   * 字符串引号配置:JS/TS 字符串默认使用单引号(JSX 由 jsxSingleQuote 控制)
   * @value true 单引号
   * @value false 双引号
   */
  singleQuote: true,

  /**
   * 语句分号配置:强制在语句末尾添加分号
   * @value true 加尾部分号(如 const a = 1;)
   * @value false 省略尾部分号(如 const a = 1)
   */
  semi: true,

  /**
   * 行宽限制:代码行的最大字符宽度,超过则自动换行
   * @value 80 行业通用值,兼顾可读性和屏幕适配
   */
  printWidth: 80,

  /**
   * 箭头函数括号配置:箭头函数参数强制加括号(即使只有一个参数)
   * @value "always" 始终加括号(如 (x) => x)
   * @value "avoid" 单个参数时省略括号(如 x => x)
   */
  arrowParens: 'always',

  /**
   * 纯文本换行配置:Markdown 等纯文本超出 printWidth 时强制换行
   * @value "always" 始终换行
   * @value "never" 从不换行
   * @value "preserve" 保留原文本换行格式
   */
  proseWrap: 'preserve',

  /**
   * 换行符配置:强制使用 LF(\n)作为换行符(Unix/Linux/macOS 风格)
   * @value "lf" LF 换行符(推荐跨平台统一)
   * @value "crlf" Windows 换行符(\r\n)
   * @value "cr" 老式 Mac 换行符(\r)
   * @value "auto" 跟随系统自动识别
   */
  endOfLine: 'lf',

  /**
   * 实验性三元表达式格式化:禁用实验性规则,使用稳定的传统格式化方式
   * @value true 启用实验性排版
   * @value false 禁用(推荐,避免格式不稳定)
   */
  experimentalTernaries: false,

  /**
   * 缩进空格数:缩进使用 2 个空格
   * @value 2 前端通用缩进值(React/Vue 主流规范)
   */
  tabWidth: 2,

  /**
   * 缩进方式:禁用 Tab 符,统一使用空格缩进
   * @value true 使用 Tab 符缩进
   * @value false 使用空格缩进(推荐,避免 Tab/空格混用)
   */
  useTabs: false,

  /**
   * 对象属性引号配置:保持引号风格一致性
   * @value "consistent" 有一个属性需要引号则全部加,否则都不加
   * @value "as-needed" 仅必要时加引号(如特殊字符属性)
   * @value "preserve" 保留原格式
   */
  quoteProps: 'consistent',

  /**
   * JSX 引号配置:JSX 中使用双引号(与 singleQuote 区分)
   * @value true JSX 使用单引号
   * @value false JSX 使用双引号(推荐,符合 React 官方规范)
   */
  jsxSingleQuote: false,

  /**
   * 对象大括号空格:大括号内侧保留空格
   * @value true 保留空格(如 { a: 1 })
   * @value false 不保留(如 {a: 1})
   */
  bracketSpacing: true,

  /**
   * JSX 闭合括号换行:多行 JSX 闭合括号单独换行
   * @value true 闭合括号与最后一个属性同行
   * @value false 闭合括号单独换行(推荐,提升可读性)
   */
  bracketSameLine: false,

  /**
   * 兼容旧版配置:等价于 bracketSameLine,控制 JSX 闭合括号换行
   * @deprecated 建议使用 bracketSameLine,此配置为兼容低版本 Prettier
   */
  jsxBracketSameLine: false,

  /**
   * Vue 缩进规则:禁用 Vue 文件中 <script>/<style> 标签内的额外缩进
   * @value true 标签内代码额外缩进
   * @value false 保持标签内代码原有缩进(避免与模板缩进冲突)
   */
  vueIndentScriptAndStyle: false,

  /**
   * JSX/Vue 属性换行:禁用「单个属性占一行」,多个属性可同行(超宽时自动换行)
   * @value true 每个属性单独占一行
   * @value false 多个属性同行(推荐,减少冗余换行)
   */
  singleAttributePerLine: false,
};