ESLint静态代码分析工具

重要通知

如果您使用 ESLint,请安装 eslint-config-prettier 以使 ESLint 和 Prettier 彼此配合得很好。它会关闭所有不必要的或可能与 Prettier 冲突的 ESLint 规则。

基本概况

ESLint 是一种用于识别和报告在 ECMAScript/JavaScript 代码中发现的模式的工具。

ESLint与JSLint、JSHint的差异

  • ESLint 使用Espree进行 JavaScript 解析。
  • ESLint 使用 AST 来评估代码中的模式。
  • ESLint 是完全可插拔的,每一条规则都是一个插件,你可以在运行时添加更多。

安装配置

> npm i -g eslint # 全局安装,注意是否影响其他项目 
> npm init @eslint/config

> npm install eslint --save-dev      # 局部安装
> ./node_modules/.bin/eslint --init  # 初始化配置文件
> ./node_modules/.bin/eslint main.js # 运行相关文件

文件目录与优先级顺序

  • .eslintrc.js
  • .eslintrc.cjs
  • .eslintrc.yaml
  • .eslintrc.yml
  • .eslintrc.json
  • package.json

关闭ESLint校验语句

/* eslint-disable */            // 关闭单个文件校验,放在文件顶部
[code] 

/* eslint-enable */             // 关闭段落校验
[code]

[code]  // eslint-disable-line  // 关闭当前行校验

// eslint-disable-next-line     // 关闭下一行校验
[code]   

命令行工具

> eslint */**/*.js # 检测当前目录所有js文件

注意事项

解决 Prettier 和 ESLint 的冲突

  • eslint-plugin-prettier 将 Prettier 的规则设置到 ESLint 的规则中。
  • eslint-config-prettier 关闭 ESLint 中与 Prettier 中会发生冲突的规则。

解析器

export default {
  parser: "vue-eslint-parser"
}

开启自动检测

npm run lint检测指令

{
  "scripts": {
    "lint": "eslint \"src/**/*.{vue,ts,js}\" --fix",
    "lint:eslint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
  }
}

配置.vscode/settings.json

{
  "editor.formatOnSave": true,

  // 为ESLint启用“保存时自动修复”,并且仍然具有格式和快速修复功能
  "editor.codeActionsOnSave": {
    "source.fixAll": true,
    "source.fixAll.eslint": true
  }
}

.eslintignore

忽略文件目录

dist
node_modules
public

src/assets

.eslintrc.cjs
.prettierrc.cjs
.stylelintrc.cjs

package.json配置规则

{
  "name": "ESLint",
  "version": "1.0.0",

  "eslintConfig": {
    "plugins": ["example"],
    "env": {
      "example/custom": true,
      "browser": true,
      "node": true,
      "es2021": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
      "ecmaVersion": 12,
      "sourceType": "module"
    },
    "rules": {
      "indent": ["error", 2],
      "quotes": ["error", "single"],
      "semi": ["error", "always"],
      "no-console": "warn",
      "no-unused-vars": ["warn", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }]
    }
  }
}

推荐配置

在项目中经常的配置选项。

rules: {
  "semi": ["error", "always"], # 分号
}

.eslintrc.js与package.json

package.json

{
	"name": "erp_ding_fe",
	"version": "0.0.0",
	"private": true,
	"scripts": {
		"dev": "vite --mode serve-dev",
		"build:test": "vite build --mode  build-test",
		"build": "vite build --mode build",
		"preview": "vite preview",
		"lint": "eslint --ext .js,.jsx,.vue,.ts,.tsx src --fix --ignore-path .gitignore",
		"format": "prettier --write src/"
	},
	"dependencies": {
		"axios": "0.21.3",
		"lib-flexible": "^0.3.2",
		"miment": "^0.0.9",
		"nprogress": "0.2.0",
		"path": "0.12.7",
		"pinia": "^2.1.3",
		"qs": "^6.10.3",
		"terser": "^5.19.2",
		"vant": "^4.6.3",
		"vue": "^3.3.4",
		"vue-router": "^4.2.2"
	},
	"devDependencies": {
		"@babel/eslint-parser": "7.16.3",
		"@types/node": "15.0.1",
		"@typescript-eslint/eslint-plugin": "5.5.0",
		"@typescript-eslint/parser": "5.5.0",
		"@vitejs/plugin-legacy": "1.6.4",
		"@vitejs/plugin-vue": "1.10.2",
		"@vitejs/plugin-vue-jsx": "1.3.1",
		"@eslint/js": "^9.2.0",
		"@rushstack/eslint-patch": "^1.2.0",
		"postcss-pxtorem": "^6.0.0",
		"prettier": "^2.8.8",
		"sass": "^1.64.1",
		"sass-loader": "^13.3.2",
		"svg-sprite-loader": "^6.0.11",
		"typescript-eslint": "^7.9.0",
		"unplugin-vue-components": "^0.25.1",
		"vite": "^4.3.9",
		"vite-plugin-html": "^3.2.0",
		"vite-plugin-svg-icons": "^2.0.1",
		"typescript": "~4.5.0",
		"vue-tsc": "^1.0.8",
		"@vue/compiler-sfc": "3.2.26",
		"@vue/eslint-config-prettier": "^7.0.0",
		"@vue/eslint-config-typescript": "^11.0.0",
		"@vue/tsconfig": "^0.1.3",
		"eslint": "7.32.0",
		"eslint-config-prettier": "8.3.0",
		"eslint-define-config": "1.2.0",
		"eslint-plugin-import": "2.25.3",
		"eslint-plugin-prettier": "4.0.0",
		"eslint-plugin-vue": "^9.0.0"
	}
}

.eslintrc.js

// https://blog.csdn.net/Sheng_zhenzhen/article/details/108685176
module.exports = {
  root: true,
  env: {
    browser: true,
    commonjs: true,
    es6: true,
    node: true
  },

  globals: {
    defineEmits: true,
    document: true,
    localStorage: true,
    __APP_VERSION__: true,
    GLOBAL_VAR: true,
    window: true,
    defineProps: true,
    defineExpose: true,
    ElementPlusLocaleZhCn: true,
    pdfjsLib: true
  },
  plugins: ['@typescript-eslint', 'prettier', 'import'],
  extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:vue/vue3-recommended', 'prettier'],
  parserOptions: {
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
      tsx: true
    }
  },
  rules: {
    "semi": ["error", "always"],

    //close lf error
    'import/no-unresolved': [0],
    'vue/multi-word-component-names': 'off',
    'vue/no-deprecated-router-link-tag-prop': 'off',
    'import/extensions': 'off',
    'import/no-absolute-path': 'off',
    'no-async-promise-executor': 'off',
    'import/no-extraneous-dependencies': 'off',

    'vue/no-multiple-template-root': 'off',
    'vue/html-self-closing': 'off',
    'vue/attribute-hyphenation': 'off',
    "vue/v-on-event-hyphenation": 'off',
    "vue/no-v-html": "off",
    "vue/valid-v-slot": "off",
    "vue/valid-v-for": "off",

    'no-console': 'off',
    'no-plusplus': 'off',
    'no-useless-escape': 'off',
    'no-bitwise': 'off',
    '@typescript-eslint/no-explicit-any': ['off'],
    '@typescript-eslint/explicit-module-boundary-types': ['off'],
    '@typescript-eslint/ban-ts-comment': ['off'],
    'vue/no-setup-props-destructure': ['off'],
    '@typescript-eslint/no-empty-function': ['off'],
    'vue/script-setup-uses-vars': ['off'],
    //can config  to 2 if need more then required
    '@typescript-eslint/no-unused-vars': [0],
    'no-param-reassign': ['off']
  }
}

eslint-config-prettier

关闭所有不必要的规则或可能与 Prettier 冲突的规则。

.eslintrc.json

基本规则

  • 0 | "off": 关闭规则
  • 1 | "warn": 将规则视为一个警告(不会影响退出码)
  • 2 | "error": 将规则视为一个错误 (退出码为1)

基本配置

// 所有的规则默认都是禁用的。

{
  // 配置环境
  "env": {
    "browser": true,
    "commonjs": true,
    "es6": true, // 自动启用es6语法,包括自动启用es6全局变量
    "node": true,
    "jest": true,
    "worker": true,
    "serviceworker": true
  },

  // 配置全局变量,ESLint规则将忽略此变量提示
  "globals": {
    "globalVAR1": "writable", // 允许重写变量
    "globalVAR2": "readonly" // 不允许重写变量
  },

  // 解析器
  "parser": "@typescript-eslint/parser",
  "parser": "vue-eslint-parser", // 解决 出现 error Parsing error: '>' expected 的解析错误
  "parser": "babel-eslint",

  // 设置解析器选项, 所有语言选项默认都是 false, ESLint 默认使用Espree作为其解析器
  "parserOptions": {
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true
    }
  },

  "plugins": [
    "vue",
    "react", 
    "@typescript-eslint", 
    "prettier", 
    "react-hooks"
  ],

  // 默认所有在 规则页面 被标记为"勾"的规则将会默认开启。
  "extends": "eslint:recommended",
  "extends": [
    // 规则列表: https://eslint.bootcss.com/docs/rules/
    "eslint:recommended",  // 启用推荐规则,所有在[规则页面]被标记为""的规则将会默认开启,报告一些常见的问题,在下文中这些推荐的规则都带有一个标记。
    "eslint-config-airbnb",
    "plugin:@typescript-eslint/recommended",
    "prettier",
    "plugin:prettier/recommended",
    "plugin:vue/vue3-recommended"
  ],

  "overrides": [],

  // 匹配规则列表
  "rules": {
    "no-console": process.env.NODE_ENV === "production" ? "warn" : "off",
    "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off",

    "no-useless-constructor": "warn",
    "eqeqeq": "warn",
    "react/no-did-update-set-state": "warn",
    "no-loop-func": "off",
    "no-void": "off",
    "camelcase": "off", // 驼峰命名
    "array-callback-return": "off",
    "import/order": "off",
    "no-return-assign": "off",
    "prefer-spread": "off",
    "import/no-relative-packages": "off",
    "max-classes-per-file": "off",
    "no-empty": "off",
    "no-prototype-builtins": "off",
    "prefer-destructuring": "off",
    "consistent-return": "off",
    "no-return-await": "off",
    "no-await-in-loop": "off",
    "class-methods-use-this": "off",
    "no-param-reassign": "off",
    "radix": "off",
    "no-bitwise": "off",
    "no-lonely-if": "off",
    "no-plusplus": "off",
    "prettier/prettier": "error",
    "indent": "off",
    "guard-for-in": "off",
    "global-require": "off",
    "default-param-last": "off",

    "semi": ["error", "always"], // 分号
    "quotes": ["error", "double"], // 逗号
    
    "no-undef": 2,
    "no-unused-expressions": [
      "error",
      {
        "allowTernary": true,
        "allowShortCircuit": true,
        "allowTaggedTemplates": true
      }
    ],
    "spaced-comment": ["error", "always", { "markers": ["/"] }],
    "import/extensions": "off",
    "import/no-unresolved": "off",
    "import/no-dynamic-require": "off",
    "import/prefer-default-export": "off",
    "import/no-extraneous-dependencies": "off",

    "no-shadow": "off",
    "no-console": "off",
    "no-continue": "off",
    "no-nested-ternary": "off",
    "no-underscore-dangle": "off",
    "no-restricted-syntax": "off",
    "no-use-before-define": "off",
    "no-restricted-exports": "off",
    "linebreak-style": ["error", "unix"],

    "vue/multi-word-component-names": "off",
    "vue/max-attributes-per-line": "off",

    "jsx-a11y/alt-text": "off",
    "jsx-a11y/click-events-have-key-events": "off",
    "jsx-a11y/no-static-element-interactions": "off",
    "jsx-a11y/no-noninteractive-element-interactions": "off",
    "jsx-a11y/anchor-is-valid": "off",
    "jsx-a11y/iframe-has-title": "off",
    "jsx-a11y/label-has-associated-control": "off",
    "jsx-a11y/media-has-caption": "off",

    "react/jsx-indent": "off",
    "react/prop-types": "off",
    "react/jsx-wrap-multilines": "off",
    "react/jsx-filename-extension": "off",
    "react/jsx-props-no-spreading": "off",
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "react/no-unused-prop-types": "off",
    "react/require-default-props": "off",
    "react/destructuring-assignment": "off",
    "react/jsx-one-expression-per-line": "off",
    "react/jsx-boolean-value": ["error", "always"],
    "react/function-component-definition": "off",
    "react/no-arrow-function-lifecycle": "warn",
    "react/sort-comp": "off",
    "react/button-has-type": "off",
    "react/static-property-placement": "off",
    "react/no-unused-class-component-methods": "off",
    "react/no-access-state-in-setstate": "off",
    "react/no-find-dom-node": "off",
    "react/state-in-constructor": "off",
    "react/no-unused-state": "off",
    "react/jsx-no-bind": "off",
    "react/no-array-index-key": "off",
    "react/no-unescaped-entities": "off",
    "react/no-unstable-nested-components": "off",
    "react/jsx-no-useless-fragment": "off",

    "@typescript-eslint/camelcase": "off",
    "@typescript-eslint/no-explicit-any": "off",
    "@typescript-eslint/no-var-requires": "off",
    "@typescript-eslint/no-empty-function": "off",
    "@typescript-eslint/no-empty-interface": "off",
    "@typescript-eslint/no-non-null-assertion": "off",
    "@typescript-eslint/interface-name-prefix": "off",
    "@typescript-eslint/triple-slash-reference": "off",
    "@typescript-eslint/no-triple-slash-reference": "off",
    "@typescript-eslint/explicit-function-return-type": "off",
    "@typescript-eslint/explicit-member-accessibility": "off",
    "@typescript-eslint/explicit-module-boundary-types": "off",
    "@typescript-eslint/ban-types": "off",
    "@typescript-eslint/ban-ts-comment": "off",
    "@typescript-eslint/no-use-before-define": [
      "error",
      {
        "variables": false,
        "functions": false
      }
    ],
    "@typescript-eslint/no-unused-vars": [
      "error",
      {
        "vars": "all",
        "args": "after-used",
        "ignoreRestSiblings": true
      }
    ]
  }
}

eslint-plugin-vue

Vue.js 的官方 ESLint 插件。

Last Updated:
Contributors: 709992523, Eshen