likes
comments
collection
share

配置eslint+prettier+husky+lint-staged(pnpm版)

作者站长头像
站长
· 阅读数 41

背景

本文是以在vue搭建的组件库中使用eslintprettier解决代码格式校验和格式化代码风格的使用指南,但是不限于是组件库项目,只要是vue项目全部通用。你可以按步骤一步步配置,可以说是手把手教你配置eslintprettier了,以及怎么通过husky+lint-staged去帮你做git提交前的代码校验和格式化。

注意⚠️:本文的使用的包管理工具是pnpm,如果你项目中用的是npm,只需要把文中的安装命令pnpm改为npm,并且去掉-w简写符号;如果你使用的是yarn,那么只需要把pnpm i改为 yarn add,并且去掉-w简写符号。

安装eslint

eslint是一个代码检查工具,通过配置一个个规则来限制代码的合法性和风格。在根目录安装:

pnpm i eslint -D -w

eslint 主要解决的是代码质量问题(意味着程序存在潜在bug),prettier解决的是代码风格问题

安装prettier并配置eslint校验

安装eslint-plugin-prettier 插件,把 Prettier 推荐的格式问题的配置以 ESLint rules 的方式写入,这样做可以统一代码问题的来源,报错的来源依旧是 ESLint 。

pnpm i prettier -D -w
pnpm i eslint-config-prettier -D -w //用于解决和 Prettier 冲突的 ESLint 的配置
pnpm i eslint-plugin-prettier -D -w //启用 eslint-plugin-prettier

在根目录增加.eslintrc.js 文件,用于配置校验规则

module.exports = {
  root: true,
  env: {
    es6: true,
    browser: true,
    node: true,
  },
  parser: 'vue-eslint-parser',
  plugins: ['vue', '@typescript-eslint', 'prettier'],
  extends: [
    'eslint:recommended',
    'plugin:import/recommended',
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended',
    'prettier',
  ],
  parserOptions: {
    ecmaVersion: 12,
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
    },
  },
  rules: {
    // js/ts
    camelcase: ['error', { properties: 'never' }],
    'no-console': ['warn', { allow: ['error'] }],
    'no-debugger': 'warn',
    'no-constant-condition': ['error', { checkLoops: false }],
    'no-restricted-syntax': ['error', 'LabeledStatement', 'WithStatement'],
    'no-return-await': 'error',
    'no-var': 'error',
    'no-empty': ['error', { allowEmptyCatch: true }],
    'prefer-const': [
      'warn',
      { destructuring: 'all', ignoreReadBeforeAssign: true },
    ],
    'prefer-arrow-callback': [
      'error',
      { allowNamedFunctions: false, allowUnboundThis: true },
    ],
    'object-shorthand': [
      'error',
      'always',
      { ignoreConstructors: false, avoidQuotes: true },
    ],
    'prefer-rest-params': 'error',
    'prefer-spread': 'error',
    'prefer-template': 'error',

    'no-redeclare': 'off',
    '@typescript-eslint/no-redeclare': 'error',

    // best-practice
    'array-callback-return': 'error',
    'block-scoped-var': 'error',
    'no-alert': 'warn',
    'no-case-declarations': 'error',
    'no-multi-str': 'error',
    'no-with': 'error',
    'no-void': 'error',

    'sort-imports': [
      'warn',
      {
        ignoreCase: false,
        ignoreDeclarationSort: true,
        ignoreMemberSort: false,
        memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
        allowSeparatedGroups: false,
      },
    ],
    // ts
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/no-non-null-assertion': 'off',
    '@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
    '@typescript-eslint/consistent-type-imports': [
      'error',
      { disallowTypeAnnotations: false },
    ],
    '@typescript-eslint/ban-ts-comment': ['off', { 'ts-ignore': false }],

    // vue
    'vue/no-v-html': 'off',
    'vue/require-default-prop': 'off',
    'vue/require-explicit-emits': 'off',
    'vue/multi-word-component-names': 'off',
    'vue/prefer-import-from-vue': 'off',
    'vue/no-v-text-v-html-on-component': 'off',
    'vue/html-self-closing': [
      'error',
      {
        html: {
          void: 'always',
          normal: 'always',
          component: 'always',
        },
        svg: 'always',
        math: 'always',
      },
    ],

    // prettier
    'prettier/prettier': 'error',

    // import
    'import/first': 'error',
    'import/no-duplicates': 'error',
    'import/no-unresolved': 'off',
    'import/namespace': 'off',
    'import/default': 'off',
    'import/no-named-as-default': 'off',
    'import/no-named-as-default-member': 'off',
    'import/named': 'off',
  },
}

在根目录增加.prettierrc 文件,用于配置代码风格

{
  "semi": false,
  "singleQuote": true,
  "overrides": [
    {
      "files": ".prettierrc",
      "options": {
        "parser": "json"
      }
    }
  ]
}

安装husky+lint-staged对本地代码进行拦截检查

husky:操作git钩子的工具,

lint-staged:本地暂存代码检查工具

通过这两个工具配合操作,使用husky拦截git的一系列操作(本文只涉及拦截git commit操作),监听到提交命令之后,然后交给lint-staged对本地提交的代码进行格式检查,检查通过则正常提交,不通过则提示错误信息,取消代码暂存。

  1. 安装这两个工具
pnpm i lint-staged -D -w
pnpm i husky -D -w
// 可以使用下面的命令统一安装
pnpm i lint-staged husky -D -w
  1. 初始化husky,同时会在根目录创建.husky文件夹
// 在package.json中添加脚本(该命令需要npm版本为7.24.2以上的版本才支持set-script)
npm set-script prepare "husky install"
// 初始化husky,将 git hooks 钩子交由,husky执行
pnpm run prepare 
// 在.husky文件夹下添加pre-commit文件,命令为:pnpm exec lint-staged
npx husky add .husky/pre-commit "pnpm exec lint-staged"

注意⚠️:千万不用自己手动创建pre-commit文件,这样会不生效,只能通过命令创建

  1. package.json 文件中配置lint-staged,或者在根目录创建 **.lintstagedrc.json**配置检查机制,内容如下:
"lint-staged": {
    "*.{vue,js,ts,jsx,tsx,json}": "eslint --fix",
    "*.md": [
      "prettier --write"
    ]
  },

本地操作提交,验证配置是否生效。

Q&A

  1. 本地执行eslint提交校验的时候,提示:" <script setup> cannot contain ES module exports vue/no-export-in-script-setup"

问题原因: .vue 文件中,使用<script setup>的同时,还出现了export

配置eslint+prettier+husky+lint-staged(pnpm版)

配置eslint+prettier+husky+lint-staged(pnpm版)

由于注册组件这里使用的是 Table.name进行注册,export是为了导出组件名称,看起来又必不可少。

解决方案1:eslint提示了啥,我们就关闭这个提示,即在eslintrc.jsrules添加'vue/no-export-in-script-setup': 'off'这样就关闭了这个校验规则

解决方案2:使用插件unplugin-vue-define-options,通过使用 defineOptions 宏帮你设置name属性,而不需要单独写一个<script></script>来定义名称。参考链接

pnpm i unplugin-vue-define-options -D -w

vuepress2.x版本中配置vite插件需要配置Vite 打包工具(v2.vuepress.vuejs.org/zh/referenc…

pnpm i -D @vuepress/bundler-vite@next -w

docs/.vuepress/config.ts文件中引入

import DefineOptions from 'unplugin-vue-define-options/vite'
import { viteBundler } from '@vuepress/bundler-vite'

export default {
  ....
  bundler: viteBundler({
    viteOptions: {
      plugins: [DefineOptions()],
    },
  }),
  
}

在组件中使用defineOptions

<script setup lang="ts">
defineOptions({
  name: 'BaseTable',
})
</script>

解决方案3:跟方案2类似,使用vite-plugin-vue-setup-extend插件,可以直接将name属性写在script标签上

pnpm i vite-plugin-vue-setup-extend -D -w
// 同样需要安装vite打包工具
pnpm i -D @vuepress/bundler-vite@next -w

docs/.vuepress/config.ts文件中引入

import VueSetupExtend from 'vite-plugin-vue-setup-extend'
import { viteBundler } from '@vuepress/bundler-vite'

export default {
  ....
  bundler: viteBundler({
    viteOptions: {
      plugins: [VueSetupExtend()],
    },
  }),
  
}

在组件中使用defineOptions

<script setup lang="ts" name="BaseTable">
...
</script>