Notes#
The default development environment for this article is VSCode. It is advisable to unify the development environment within the team to avoid issues related to editor differences and plugin incompatibility.
The core of the tools mentioned below is configuration files, and these configuration files can usually be configured in various formats, such as .js
, .yaml
, .json
, or directly in package.json
. The related settings will not be elaborated further; specific configuration formats can be found in the official documentation, and this article will only analyze some configuration items.
Code Style#
Consistent code style is the cornerstone of project collaboration. Using ESLint and Prettier can help avoid code merge conflicts caused by inconsistent code formatting and improve code readability and maintainability. Although it has been mentioned in Understanding ESLint and Prettier, the following will provide a more complete explanation as an upgraded version.
ESLint#
The first tool introduced in this article is ESLint, which has the following functions:
- Provides code standards, for example: recommending the use of
===
instead of==
, suggesting that unmodified variables useconst
instead oflet
, and other such rules. - Secondly, it formats code, controlling issues like indentation and line breaks.
Installation#
npm install --save-dev eslint
VSCode Plugin#
Installing ESLint in the project indeed provides file validation and organization interfaces, but in practice, formatting code cannot always be done manually by adjusting the API for the current file. At this point, the ESLint plugin is needed. After installing the plugin, you can call the ESLint API to process the current file using a shortcut, and even automatically format the current file upon saving. In the coding window, warnings and errors will be marked with yellow and red lines.
After installing both the ESLint core and the VSCode plugin, you can use some basic features, but to work with files like React, Vue, TS, etc., you need to install the corresponding plugins (note that these are ESLint plugins, unrelated to the previously mentioned VSCode plugins). The related plugin issues will be introduced in detail later.
Configuration#
First, take a look at the ESLint configuration file, which is used to configure and extend ESLint rules. Here is an example:
{
"root": true,
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": { "project": ["./tsconfig.json"] },
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/strict-boolean-expressions": [
2,
{
"allowString": false,
"allowNumber": false
}
]
},
"ignorePatterns": ["src/**/*.test.ts", "src/frontend/generated/*"]
}
Next, let's analyze the three configurations: extends, plugins, and rules in detail.
Extends#
The aforementioned rules consist of one or two hundred items, and manually configuring each one is impractical. We can use Extends.
Extends is a collection of configurations; adding Extends is equivalent to adding a set of configurations. When configuring the value of extends, the package name prefix eslint-config-
can be omitted.
Many large companies have their own set of standards. For example, the well-known Airbnb configuration file in the field of front-end code standards is eslint-config-airbnb. After installation, you only need to write it like this:
{
"extends": "airbnb"
}
However... I personally recommend using eslint:recommended
or standard
instead of airbnb
, because Airbnb has too many rules, such as no-plusplus
and no-underscore-dangle
, which are not problematic in normal use. The difference from the original coding habits is quite large, so I chose not to use it 😂.
Of course, there are other options for configuration collections, such as eslint-config-alloy; you can also publish your own configuration collection, which is actually a recommended practice by the official documentation.
Plugins#
Plugins are more powerful than Extends; they not only supplement configurations but also add custom ESLint rules. When configuring the value of plugins, the package name prefix eslint-plugin-
can be omitted; since plugins can also contain configuration collections, when using the configuration collection in a plugin, the format plugin:package-name/config-name
can be used, such as plugin/essential.
After installing eslint-plugin-vue, you can add the plugin like this, which will add a large number of Vue rules to ESLint (note that this only adds rules and does not configure whether the rules are used):
{
// ...
"extends": ["plugin:vue/essential"],
"plugins": ["vue"]
// ...
}
The detailed configuration for various languages will not be elaborated here; the documentation for eslint-plugin-xxx
generally provides comprehensive help.
Rules#
After adding the rule collections in extends, you may need to fine-tune some rules according to your habits. At this point, you can configure some individual rules in rules
.
There are three levels of rules:
- "off" or 0 - turn the rule off
- "warn" or 1 - turn the rule on as a warning (doesn’t affect exit code)
- "error" or 2 - turn the rule on as an error (exit code is 1 when triggered)
The configuration looks something like this (some special rules may have other configuration items, which can be found on the corresponding rule page):
{
"plugins": ["plugin1"],
"rules": {
"eqeqeq": "off",
"curly": "error",
"quotes": ["error", "double"],
"plugin1/rule1": "error"
}
}
Other Issues#
Configuration not taking effect:
If you update .eslintrc
and find that the new configuration is not taking effect in VSCode, you can quickly restart the ESLint plugin by pressing ctrl shift P and selecting Reload Window
.
VSCode auto-formatting configuration on save, modify settings.json
:
// settings.json
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
// "editor.formatOnSave": true,
}
Prettier#
Prettier is a modern code formatting tool. Compared to ESLint, it focuses on code formatting and can handle multiple languages, including JavaScript, CSS, SCSS, markdown, yaml, etc. Prettier complements the issue that ESLint only handles JS files, but for the intersection of JS that both can handle, some additional compatibility operations are still needed.
Installation#
npm install --save-dev --save-exact prettier
Make sure to add --save-exact
, as different versions of Prettier may handle certain formats differently. To ensure that all team members have the same format, it is essential to unify the Prettier version.
VSCode Plugin#
The principle is the same as ESLint; npm installation only provides the API, and installing the VSCode plugin is necessary for convenient formatting in the editor.
Configuration#
By far the biggest reason for adopting Prettier is to stop all the ongoing debates over styles.
Prettier provides only a few configuration items to reduce formatting disputes, so everyone can argue only over these few items (just kidding). The following are almost all of Prettier's configurations, with the filename being .prettierrc
:
{
"arrowParens": "always",
"bracketSameLine": true,
"bracketSpacing": true,
"embeddedLanguageFormatting": "auto",
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"jsxSingleQuote": false,
"printWidth": 80,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"requirePragma": false,
"semi": true,
"singleAttributePerLine": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"useTabs": false,
"vueIndentScriptAndStyle": false
}
ESLint Compatibility#
Previously, in Using ESLint and Prettier for Better Cross-IDE Collaboration, it was mentioned that ESLint and Prettier can be manually made compatible. However, that was just a method for synchronizing when using different editors. Here, I will introduce the method of using the ESLint plugin for compatibility.
To fully use ESLint to replace Prettier's work, you need eslint-plugin-prettier. Its principle is to use eslint-config-prettier to turn off all Prettier-related rules and then transfer Prettier's rules to ESLint for validation through the plugin. To achieve all of the above operations, you only need the following configuration:
{
"extends": ["plugin:prettier/recommended"]
}
Because configuring "extends": ["plugin:prettier/recommended"]
is equivalent to filling in the following set of configurations:
{
"extends": ["prettier"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error",
"arrow-body-style": "off",
"prefer-arrow-callback": "off"
}
}
Other Issues#
Special rules:
Note that some special rules overridden by Prettier, such as the default disabling of vue/html-self-closing
, should be configured in rules
according to your needs.
Commit Access Control#
The above tools are related to unifying code formats, but they do not guarantee that the code submitted to the code repository has been formatted. Using husky can check the code before submission to ensure that the submitted code complies with team standards.
Husky#
Git hooks made easy 🐶 woof!
husky provides the ability to intervene in Git hooks for npm projects. It supports all Git hooks, but we generally only use pre-commit
and commit-msg
.
pre-commit
is used for checks before submission, and commit-msg
is used for commit message validation.
# Install husky
npm install husky --save-dev
# Enable Git hooks
npx husky install
# Configure npm's prepare command to automatically run specific scripts after dependency installation
# Note: Using pkg requires npm version 7 or higher
npm pkg set scripts.prepare="husky install"
If you cannot use pkg
, you can directly configure it in package.json
:
{
"scripts": {
"prepare": "husky install"
}
}
After that, you can add the commands triggered by the hook using husky add <file> [cmd]
. Next, I will introduce the lint-staged
and commitlint
that should be used in pre-commit
and commit-msg
, respectively.
lint-staged#
Run linters against staged git files and don't let 💩 slip into your code base!
For a newly integrated ESLint codebase, checking all files on every commit and disallowing submission if they do not pass seems a bit excessive, right? Using lint-staged allows you to only check the files being committed, enabling your project to gradually update the code style.
Installation#
npm install --save-dev lint-staged
Configuration#
The configuration file is named .lintstagedrc
, and since lint-staged's configuration is relatively short, it can be directly written in the lint-staged
object in package.json
.
Configuration example:
{
"src/**/*.{ts,js}": ["eslint --cache --fix"],
"src/**/*.{json,less}": ["prettier --write"]
}
Husky Trigger#
# Run this after husky has been installed
npx husky add .husky/pre-commit "npx lint-staged"
commitlint#
commitlint can check whether the commit messages comply with the standards.
In simple terms, the format should be: type(scope?): subject
, and a practical example might be: feat(blog): add comment section
. For detailed specifications, you can refer to Understanding Semantic Commit.
Installation#
npm install --save-dev @commitlint/config-conventional @commitlint/cli
Configuration#
The configuration file is named .commitlintrc
, with the configuration example:
{
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [
2,
'always',
[
'feat', // New feature
'fix', // Bug fix
'docs', // Documentation
'style', // Formatting (changes that do not affect code execution)
'refactor', // Refactoring (changes that are neither new features nor bug fixes)
'test', // Adding tests
'revert', // Reverting
'chore', // Changes to the build process or auxiliary tools
'perf', // Performance optimization
'types' // Changes to TypeScript type definition files
]
],
'type-case': [0],
'type-empty': [0],
'scope-empty': [0],
'scope-case': [0],
'subject-full-stop': [0, 'never'],
'subject-case': [0, 'never'],
'header-max-length': [0, 'always', 72]
}
}
You can view a more complete filling example on GitHub.
Husky Trigger#
# Run this after husky has been installed
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}'
Package Manager#
Use a fixed package manager, generally choosing from the following options:
To quickly understand the differences between these managers, you can refer to “Quick Guide to npm, yarn, pnpm”. I also strongly recommend “JavaScript package managers compared: npm, Yarn, or pnpm?”, which is comprehensive and clear. Below is a performance comparison chart from this article:
You can choose according to your preferences and compatibility, but regardless of which manager you choose, do not delete lock files unless necessary!!! When installing dependencies, the manager needs to calculate dependency versions, which is time-consuming. Having a lock file allows for direct installation according to the lock list.
However, when there is a lock file, the download addresses for dependencies are fixed, so configuring repositories will not take effect, which needs to be noted during continuous integration.
P.S. Different managers may have some differences when configuring husky.
Engine Version#
If you keep the lock file for dependencies, it is also essential to record the current Node version that the application is compatible with. Different Node versions can lead to different downloaded dependencies, or even dependencies that are not compatible with the current Node version. Additionally, there are some old projects that must run with an older Node version, but when taking over, it is often unclear which version is being used, which can be quite frustrating.
To solve this problem, we can specify the Node and npm versions in package.json
:
{
"engines": {
"node": ">=0.10.3 <15",
"npm": "~1.0.20"
}
}
When there are many team members and projects, it is likely that everyone will have different versions. When necessary, you can use a Node version manager. Here are three recommended options:
- nvm for Unix, macOS nvm-windows for Windows
- n for macOS, Linux
- fnm for macOS, Windows, Linux
These can generally install a version with one command and switch versions with another command, which is very convenient.
.npmrc#
The filename containing rc
is already familiar; .npmrc
is used to add project-level npm configurations, such as:
- Changing the repository. Some companies have their own mirrors, which can be configured in
.npmrc
, so you don't have to include a long string of parameters every time you install, and it won't affect global configurations. - You can also add download addresses for some binary packages, such as the troublesome sass.
registry=https://mirrors.huaweicloud.com/repository/npm/
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
Branch Strategy#
Choose a branch strategy. You can select from the following three strategies or adjust one based on these strategies to find the most suitable one for your team:
Summary#
- Use ESLint and Prettier to control code standards and styles.
- Add VSCode plugins for rapid formatting of the current file.
- Implement commit access control to ensure that code uploaded to version control complies with standards.
- Use husky to add commit hooks to control whether submissions are successful.
- lint-staged is used to only check the files being committed, facilitating gradual control of code standards in the project.
- commitlint ensures the readability of commit messages.
- Use a unified package manager within the team and retain dependency lock files.
- Fix engine versions to prevent dependency changes and avoid the dilemma of not knowing which version to run for old projects.
- Use
.npmrc
to fix repository configurations. - Choose a branch strategy to make code history more orderly.
If anyone feels there are omissions or has any questions, I will supplement!