1 What is ESLint
ESLint is a plug-in JavaScript/JSX code inspection tool for detecting and fixing problems in JavaScript code, with the goal of making the code more consistent and avoiding errors.
2 Introducing ESLint into the Vue project
The Vue2 project built with Vue CLI already comes with ESLint, so I won't go into details. Let's see how to introduce ESLint into the Vue3 project built by Vite.
Build a Vue3 project with the following command:
npm create vite@latest vue3-project
After creation, start up:
npm i
npm run dev
The effect is as follows:
2.1 Introducing ESLint
Execute the following command:
npm init @eslint/config
Enter the interactive interface, which can be selected by the up and down arrow keys, and confirmed by pressing the Enter key.
The first question is:
- What do you want to use ESLint for?
- We choose the most comprehensive one: check syntax, spot problems, and enforce uniform code style
$ npm init @eslint/config
? How would you like to use ESLint? …
To check syntax only
To check syntax and find problems
❯ To check syntax, find problems, and enforce code style
The second question is:
- What module system is your project using?
- Because it is running on the browser side, select
ESModule
? What type of modules does your project use? …
❯ JavaScript modules (import/export)
CommonJS (require/exports)
None of these
The third question is:
- What framework are you using? (There is no Angular)
- Select
Vue
? Which framework does your project use? …
React
❯ Vue.js
None of these
The fourth question is:
- Are you using TypeScript?
- Select
Yes
? Does your project use TypeScript? › No / Yes
The fifth question is:
- What environment is your code running in? (This can be multiple choice)
- Select
Browser
Browser Environment
? Where does your code run? … (Press <space> to select, <a> to toggle all, <i> to invert selection)
✔ Browser
✔ Node
The sixth question is:
- What code style do you want to define?
- Choose to use a popular coding style
? How would you like to define a style for your project? …
❯ Use a popular style guide
Answer questions about your style
The seventh question is:
- Which style do you want to use?
-
Airbnb
are many people who use it, so choose this one
? Which style guide do you want to follow? …
❯ Airbnb: https://github.com/airbnb/javascript
Standard: https://github.com/standard/standard
Google: https://github.com/google/eslint-config-google
XO: https://github.com/xojs/eslint-config-xo
The eighth question is:
- What format is the configuration file in?
- Just choose JavaScript (Generate
eslintrc.js
file)
? What format do you want your config file to be in? …
❯ JavaScript
YAML
JSON
Finish! Isn't it super easy!
Take a look at what configurations we have chosen:
✔ How would you like to use ESLint? · style
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · vue
✔ Does your project use TypeScript? · Yes
✔ Where does your code run? · browser
✔ How would you like to define a style for your project? · guide
✔ Which style guide do you want to follow? · airbnb
✔ What format do you want your config file to be in? · JavaScript
It mainly installs the following dependencies for us:
-
eslint-config-airbnb-base@15.0.0
-
eslint-plugin-import@2.26.0
-
eslint-plugin-vue@9.2.0
-
eslint@8.20.0
-
@typescript-eslint/parser@5.30.6
-
@typescript-eslint/eslint-plugin@5.30.6
and generated a eslintrc.cjs
configuration file:
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'plugin:vue/vue3-essential',
'airbnb-base',
],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: [
'vue',
'@typescript-eslint',
],
// 自定义 rules 规则
rules: {
},
};
2.2 ESLint configuration
- parser parser
- extends configure extension
- plugins
- rules custom rules https://eslint.org/docs/latest/rules/
- eslint-disable-next-line Disable ESLint
2.3 Perform ESLint code inspection
Configure the lint script command in scripts in the package.json file:
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
// 配置 lint 脚本命令
"lint": "eslint --ext .vue,.ts src/"
},
Execute the lint script command:
npm run lint
There are a bunch of errors:
/vue3-project/src/App.vue
4:53 error Missing semicolon semi
/vue3-project/src/components/HelloWorld.vue
2:26 error Missing semicolon semi
4:31 error Missing semicolon semi
6:21 error Missing semicolon semi
/vue3-project/src/main.ts
1:32 error Missing semicolon semi
2:21 error Missing semicolon semi
3:28 error Missing semicolon semi
5:29 error Missing semicolon semi
/vue3-project/src/vite-env.d.ts
4:3 error Expected 1 empty line after import statement not followed by another import import/newline-after-import
4:45 error Missing semicolon semi
5:48 error Missing semicolon semi
6:27 error Missing semicolon semi
✖ 12 problems (12 errors, 0 warnings)
12 errors and 0 warnings potentially fixable with the `--fix` option.
Most of them say that there is no semicolon at the end of the sentence, because we chose the Airbnb code specification, so there will be this error message, different code specifications, the built-in inspection rules are not necessarily the same.
2.4 Automatically fix ESLint issues
Add script commands to automatically fix ESLint problems in scripts:
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"lint": "eslint --ext .vue,.ts src/",
// 自动修复 ESLint 问题脚本命令
"lint:fix": "eslint --ext .vue,.ts src/ --fix"
},
implement:
npm run lint:fix
After executing the autofix command, all semicolons are added and unused variables are automatically removed.
Execute again:
npm run lint
No more errors.
3 Configure husky and PR access control
3.1 Configure husky access control
To ensure that the code passes ESLint checks before each commit (git commit), we add a pre-commit gate.
- Step 1: Install husky and lint-staged
npm i lint-staged husky -D
- Step 2: Add prepare script command to scripts in package.json
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"lint": "eslint --ext .vue,.ts src/",
"lint:fix": "eslint --ext .vue,.ts src/ --fix",
// 在 npm install 之后自动执行,生成`.husky`目录。
"prepare": "husky install"
},
- Step 3: Execute the prepare script
npm run prepare
After the command is executed, the .husky
directory will be automatically generated in the project root directory.
- Step 4: Add a pre-commit hook
Execute the following command, the .husky
directory will automatically generate the pre-commit
file hook.
npx husky add .husky/pre-commit "npx lint-staged"
- Step 5: Add lint-staged configuration
"lint-staged": {
"src/**/*.{vue,ts}": "eslint --fix"
},
Through the above five steps, every time you use the git commit
command to submit the submitted code, it will:
- Intercepted by pre-commit hook
- Execute the npx lint-staged command
- Then execute the eslint --fix command to check the files involved in the submitted modified code, and automatically repair the errors that can be repaired. The errors that cannot be repaired will be prompted. Only when all ESLint errors are repaired can the submission be successful.
3.2 Configure PR access control
If you are working on your own open source project, and you are very lucky, there are a group of like-minded partners who are willing to contribute together. At this time, in order to unify everyone's code style, let the contributors focus on feature development, without worrying about the code format specification, and With the ESLint tool prompting contributors, which code may pose potential risks, it is necessary for you to add ESLint gate to the submitted PR.
We've added native ESLint commands:
"scripts": {
"lint": "eslint --ext .vue,.ts src/",
},
We need to create a .github/workflows/pull-request.yml
file in this directory, and write the following in the file:
name: Pull Request
on:
push:
branches: [ dev, main ]
pull_request:
branches: [ dev, main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
name: "ESLint"
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install pnpm
uses: npm/action-setup@v2
with:
version: 6
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- name: Install deps
run: npm i
- name: ESLint
run: npm run lint
In this way, as long as the PR is merged into the dev or main branch, the Github Actions workflow task will be run again. If the ESLint check fails, the PR's checks will be reported as red, and the merge of the PR will be blocked.
The submitter of the PR sees the red report, and can also click on the task to see where the error is reported. If these ESLint problems are corrected, the PR will turn green, and the project administrator can successfully merge the PR into the target branch 🎉
4 Common ESLint Problems and Repair Cases
Next, I will share with you the typical problems encountered during the repair process of ESLint, the open source Vue3 component library of Vue DevUI .
4.1 Case 1: warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any
The frequency of this problem is relatively high, because some types are written any
, which requires a clear type.
For example, in the unit test file of the Pagination component pagination.spec.ts
:
const wrapper = mount({
components: {
DPagination
},
template: `<d-pagination ... />`
}, globalOption);
const btns = wrapper.findAll('a.devui-pagination-link');
expect(btns.map((ele: any) => ele.text()).join()).toEqual('<,1,...,4,5,6,...,16,>');
Among them ele: any
belong to this kind of problem.
The solution is to add a clear type to ele
, and see that the logic is <button>
element, because it is a package element of @vue/test-utils
library, so it needs to wrap a layer DOMWrapper
:
import { DOMWrapper } from '@vue/test-utils';
expect(btns.map((ele: DOMWrapper<Element>) => ele.text()).join()).toEqual('<,1,...,4,5,6,...,16,>');
4.2 Case 2: 'xxx' was used before it was defined no-use-before-define
This is also a relatively common problem. If you use a variable or method before the declaration, the solution is also very simple. You only need to adjust the order of the code, and put the declaration of the variable or method before the calling statement.
For example, in the pagination.tsx
of the Pagination component:
// 极简模式下,可选的下拉选择页码
const litePageOptions = computed(() => liteSelectOptions(totalPages.value));
// 当前页码
const cursor = computed({
get() {
// 是否需要修正错误的pageIndex
if (!props.showTruePageIndex && props.pageIndex > totalPages.value) {
emit('update:pageIndex', totalPages.value || 1);
return totalPages.value || 1;
}
return props.pageIndex || 1;
},
set(val: number) {
emit('update:pageIndex', val);
}
});
// 总页数
const totalPages = computed(() => Math.ceil(props.total / props.pageSize));
Among them, the declaration of totalPages
is in a later position, but before the declaration, it is used in the totalPages
litePageOptions
and cursor
variables totalPages
, so prompt ESLint problem.
The solution is to put the declaration of totalPages
7ef2e70325d54a9331c0ffd80309b5ac--- before litePageOptions
and cursor
.
// 总页数
const totalPages = computed(() => Math.ceil(props.total / props.pageSize));
// 极简模式下,可选的下拉选择页码
const litePageOptions = computed(() => liteSelectOptions(totalPages.value));
// 当前页码
const cursor = computed({ ... });
4.3 Case 3: warning Missing return type on function @typescript-eslint/explicit-module-boundary-types
The problem is because the function lacks a return type, such as in the Fullscreen component utils.ts
document launchImmersiveFullScreen
method:
export const launchImmersiveFullScreen = async (docElement: any) => {
let fullscreenLaunch = null;
if (docElement.requestFullscreen) {
fullscreenLaunch = docElement.requestFullscreen();
} else if (docElement.mozRequestFullScreen) {
fullscreenLaunch = docElement.mozRequestFullScreen();
} else if (docElement.webkitRequestFullScreen) {
fullscreenLaunch = Promise.resolve(docElement.webkitRequestFullScreen());
} else if (docElement.msRequestFullscreen) {
fullscreenLaunch = Promise.resolve(docElement.msRequestFullscreen());
}
return await fullscreenLaunch.then(() => !!document.fullscreenElement);
};
先看下launchImmersiveFullScreen
方法的参数问题, docElement
用any
,也缺失了返回类型, docElement
其实就是document
对象, HTMLElement
类型, launchImmersiveFullScreen
是用来启动沉浸式全屏的,为了实现浏览器兼容, docElement.mozRequestFullScreen
with Firefox, and these methods are not available in HTMLElement, and will report a TS type error, so some modifications are required.
interface CompatibleHTMLElement extends HTMLElement {
mozRequestFullScreen?: () => void;
webkitRequestFullScreen?: () => void;
msRequestFullscreen?: () => void;
}
A type of CompatibleHTMLElement
is defined here, which inherits HTMLElement
and adds some custom methods.
export const launchImmersiveFullScreen = async (docElement: CompatibleHTMLElement) => {
...
}
Let's take a look at the return type of the method launchImmersiveFullScreen
.
return await fullscreenLaunch.then(() => !!document.fullscreenElement);
This method returns a Promise
object, its type is a generic type, we need to pass in a specific type:
export const launchImmersiveFullScreen = async (docElement: CompatibleHTMLElement): Promise<boolean> => {
...
return await fullscreenLaunch.then(() => !!document.fullscreenElement);
};
4.4 Case 4: 'xxx' is already declared in the upper scope @typescript-eslint/no-shadow
This problem is due to the same variable name defined in nested scopes, such as in the use-checked.ts
file of the Tree component:
export default function useChecked(...) {
const onNodeClick = (item: TreeItem) => {
// 这里定义了 id 变量
const { id } = item;
...
filter 里面又定义了一个 id 参数
const currentSelectedItem = flatData.filter(({ id }) => currentSelected[id] && currentSelected[id] !== 'none');
...
}
}
The modification method is to change the name of one of the ids, for example, change the id inside to itemId:
const currentSelectedItem = flatData.filter(({ id: itemId }) => currentSelected[itemId] && currentSelected[itemId] !== 'none');
Welcome to share your problems in the project ESLint
problems in the comment area👏👏
5 thanks
On the road to clearing the ESLint problem in the Vue DevUI component library, a friend I have to mention is @linxiang07 , who has repaired more than 100 components of more than 40 components for more than 4 months from March to July 2022. This is an ESLint issue. Until July 5th, ESLint will be cleared and ESLint access control will be added to the PR. If the subsequent PR does not pass the ESLint check, it will not be able to be merged.
Thank you linxiang
for your contributions!
The following is part of the PR submitted by the classmates linxiang
:
linxiang
The classmate also became the contributor of Vue DevUI
component library TOP3, and became our Committer and administrator🎉🎉
It is worth mentioning that linxiang
the classmate is still our VirtualList
virtual list component and Tree
component and other components of the owner and contributor, and has improved a lot Unit testing of each component, very capable, and a very active and active contributor.
Welcome to join us and create high-quality open source component library projects with excellent developers! If you are interested, you can add assistant wx: devui-official
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。