preface
EditorConfig, Prettier, and ESLint are often used on projects with only a vague understanding of why they are needed, what they do, and how they work. In order to answer my doubts, I began to explore and finally put together this article. With this article, you can:
- Clarify why the three standardization tools were created, how they work, how they are configured and integrated, and why they need to be used together
- Understand the effects, principles, and use of Git Hooks, Husky, lint-passage
EditorConfig: Editor Coding Style
EditorConfig addresses the problem
The code editor maintains a configuration of its own, which can often be modified through the Preferences Settings. The configuration includes encoding style Settings such as “Use Tab or Space for indentation”, “How many columns per Tab”, “whether to display blank lines at the end of the file”, and so on.
The code editor configuration information is maintained independently and is not shared. If you use different editors to open the same file, if the editor configuration is not uniform, it is likely that the display and input will be inconsistent.
For example, if a Tab takes up two columns of Sublime and a Tab takes up four columns of VSCode, opening the same file will look different
For example, a Sublime indented Tab and VSCode indented 2 Spaces, edit the same file and enter different content
There are only pitfalls for a single editor to operate independently, but in the case of multiple editors or across editors, the pitfalls can easily escalate into problems, resulting in a messy code state.
How does EditorConfig work
We share the.editorConfig file in the project, which can be parsed by EditorConfig, which tells the editor to override the default configuration.
Some editors have built-in support for EditorConfig, such as WebStorm and Github; Some editors, such as VSCode and Sublime, require the EditorConfig plug-in to be installed.
EditorConfig has become a widely used solution with high coverage support for mainstream code editors.
How do I use EditorConfig
1. Ensure that EditorConfig is supported in the editor
For details about how to support each editor, see “No Plugin Necessary” and “Download a Plugin” in the official documentation
2. Create and configure.editorConfig
Create a.editorConfig file in the project directory. Editorconfig can configure coding style entries including:
Indent_style sets the indent to TAB or space tab_width sets the number of columns TAB occupies. The default is indent_size. Indent_size sets the number of columns to indent. If indent_style is TAB, then tab_width is the indent width. The value is set to the encoding of LF, CR and CRLF charset. The value is latin1, UTF-8, UTF-8-BOM, UTF-16BE and UTF-16LE. Utf-8 is not recommended. -bom trim_trailing_whitespace true means that the whitespace characters at the end of the line will be removed. Insert_final_newline True means that the file will end with a blank line root Is the highest-level configuration file. When set to true, the upward search stopsCopy the code
EditorConfig acts as an editor, so configuration can be applied to all types of editable files. EditorConfig also supports differentiation for specific files or specific types of files.
For Details, see the File Format Details section in the official document.
# https://editorconfig.org
# is already a top-level profile, so you don't need to search upwards
root = true
[*]
# Coded character set
charset = utf-8
The # indent style is space
indent_style = space
One indent takes two Spaces, because tab_with is not set, and one Tab takes two columns
indent_size = 2
# Lf
end_of_line = lf
The # file ends with a blank line
insert_final_newline = true
# Removes any whitespace characters at the beginning of a line
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
Copy the code
conclusion
EditorConfig solves the problem of coding style consistency at the editor configuration level.
However, the section on code style is not covered, such as whether “semicolons should be added at the end of statements”, “strings should be enclosed in single or double quotes”, “writing specifications for multi-line objects”, and so on.
Prettier: Unified Code style (Code Formatter)
Problem solved by Prettier
Every developer’s code writing style is more or less different. For example, some authors prefer to add semicolons, while others don’t think it is necessary to write semicolons at all based on the recognition ability of the JS engine. Before Prettier, the debate over code style seemed never-ending. A uniform specification would end the debate. People would no longer have to worry about how to style the code. Code changes would focus on logic rather than code style.
How does Prettier work
Prettier parses code into an AST tree through syntax analysis, and uses the code style specifications on the AST tree to regenerate code. (You can see the conversion process on Playground.)
Prettier automatically deduces the parser based on the file path (.js files use the Babel parser, and.css files use the CSS parser) and parses code. Recognizing and parsing different types of files is limited by the internal support of Prettier, so Prettier does not cover all types of files. As shown in the following figure, Prettier supports mainstream front-end development languages, enough for daily development.
Configuration is Prettier
Prettier, an Prettier tool, is a strict code style tool that minimizes configuration and strictly regulates the way code is organized.
13, The reason Prettier provides only a few configuration options is pretty straightforward: Why do you need more of them so people can’t decide how to configure them, if you want to stop arguing about Prettier?
Prettier can handle the following:
- String quote style
- Empty line processing
- Multi-line object format
- The semicolon processing
- Print width: controls line feed
- Control the scope of comment influence through newlines
Configuration items
Prettier provides the following configuration items, which can be queried and configured as needed. Generally, only a small number of Prettier configurations are required.
{
printWidth: 80.// Print width, which defaults to 80 columns
tabWidth: 2.// The number of columns to indent. Default is 2 columns
useTabs: false.// If the indentation style is Tab, the default is false, using Spaces to indent
semi: true.// Add a semicolon to the end of the statement. The default is true
singleQuote: false.// Use single quotes. The default is false
quoteProps: "as-needed".// Attributes in an object are quoted,
// "as-needed" only quotes the attributes needed,
Keep attribute quotes consistent in the same object, share happiness, share difficulties
// "preserve" forces quotation marks.
// Default is as-needed
jsxSingleQuotes: false.// Use single quotes in JSX. The default is false
trailingComma: "es5".// Whether to add a comma at the end of multiple lines
// "es5" adds commas to comma-enabled containers such as Objects/Arrays
// "all" adds as many commas as possible
// "none" does not allow adding a tease
// The default value is "es5"
bracketSpacing: true.// Whether to preserve Spaces on both sides of the object, such as the difference between {foo:bar} and {foo:bar}
jsxBracketSameLine: false.// Whether multi-line JSX elements can be matched with attributes. Default is false
arrowParens: "always".// The arrow function arguments are wrapped in parentheses, such as (x) => x and x => x
// "always"
// "avoid packages when possible.
rangeStart: 0.// Format only a portion of the file. The range starts on a few lines
rangeEnd: Infinity,
// Format only a part of the file, where the range ends on a line
parser: "none"
// Specify the parser, Prettier deduces the parser from the file path
// For example,.js files are parsed with Babel, and.scss files are parsed with post-SCSS
filepath: "none"
// Specifies the file name to be used to infer which parser to use
requirePragma: false
// Only format files that have a formatting identifier at the top of the file
// For large unformatted projects, specify a small number of files to format first
insertPragma: false
proseWrap: "preserve"
htmlWhitespaceSensitivity: "css"
// The whitespace sensitivity of HTML files
// "CSS" is consistent with the display property of the CSS
// "strict" Spaces are sensitive
// "ignore" Spaces are not sensitive
vueIndentScriptAndStyle: false
// Whether to apply indentation to code inside the
endOfLine: "lf"
/ / a newline character
embeddedLanguageFormatting: "auto"
// Whether to format the embedded reference code, such as the embedded code block in the MarkDown file
// "auto" Prettier Automatically identifies and formats the Prettier
// "off" turns off automatic formatting
}
Copy the code
The configuration file
Prettier uses Cosmiconfig to support configuration files. Cosmiconfig is a common tool for reading configuration files. Search the file tree for the configuration file in the following order:
- The prettier field in package.json
- . Prettierrc file
- . Prettierrc. Json file
- . Prettierrc. Js file
- . Prettierrc. Toml file
Select either of the preceding methods to customize Prettier. If no configuration file exists, Prettier uses the default value.
Shared configuration
module.exports = { ... require("@company/prettier-config"),
semi: false};Copy the code
Sometimes the configuration file needs to be shared among multiple projects. In this case, the configuration file can be published as a separate module and shared by introducing a module.
If you need to share configuration files in Monorepo, you can place them directly in the root directory. If you need to customize your subproject, you can import the configuration files from the root directory and then extend them.
Install the dependency on Prettier
# Install fixed versions to ensure consistent performance across different environments (dev and CI)
yarn add prettier --dev --exact
Copy the code
After the dependency is installed, the prettier –write [file/dir/glob] command is used to format the specified file, directory, or match.
But often we don’t choose to call them manually, and instead integrate them into the IDE and Git Hooks to make them part of the automated process.
IDE integration
For details about how to install each IDE, see Editor Support on the Prettier home page.
The Prettier formatting tool has the Prettier kernel built-in and an independent configuration. However, when an Prettier dependency is installed on a project, it uses project dependencies > Global Dependencies > Built-in Dependencies. The Prettier configuration file in the Prettier project is used first.
After installing the tool, you need to set the IDE to become a sharp tool. Take VSCode as an example:
- Set to default Format tool: “Format Document With…” -> “Configure Default Formatter…” , call “Format Document” to manually Format the current file to detect the effect of the setting;
- Set automatic formatting when saving files: Select “Format On Save” in Preference Settings.
The above Settings ensure that locally modified files are properly formatted, but not externally imported files or edited by other members of the team. So you need to set up a more secure barrier: go through automatic formatting before you commit your code.
Git Hooks integration
To automatically format code before committing, you need to use Git Hooks. First, check out Git Hooks, Husky, and Lint-passage
Git Hooks
Git has hooks that allow users to hook custom scripts to automate the execution of specific tasks at the right time. For example, the prettier –write command is used to automatically format the code on commit.
All of Git’s hooks are covered in detail at git-git Hooks (git-scm.com).
We only use the “submit workflow” hook here, and the commit workflow contains four hooks:
pre-commit
Before submitting informationEdit theRun, plug in at this stageCode reviewProcess, check failed to return a non-zero value can stop the submission process;prepare-commit-msg
Run after the default message has been created, which isBefore starting the editorCan be loaded at this stagecommitizen
Such auxiliary filling tools;commit-msg
在 When you’re done editingRun, can be used at this stagecommitlint
Conduct normative inspection of submitted information;post-commit
在 After submissionRun, usually do some notification at this stage.
The most intuitive way to use Git hooks is to use the.git/hooks sample file. Remove the.sample suffix from the corresponding hook file to enable it. However, there are drawbacks to this mode of operation:
- You need to work with a.git directory outside the scope of your project
- Unable to synchronize.git/hooks to the remote repository
Husky
Two disadvantages of using.git/hooks can be avoided perfectly by specifying the hooks directory for git, as shown in the following command
Git hooksPath =.githooks
git config core.hooksPath .githooks
Copy the code
Husky builds on this solution by assigning the hooks directory to the.husky directory under your project, and provides an easy to use command-line tool to manage the hooks files in the directory. Why not follow the manual installation of Husky to see what it does
# installation husky
npm install husky --save-dev
This step creates the.husky directory and sets Git hooksPath
npx husky install
# Attach automatically activated Git hooks to the NPM PREPARE lifecycle
# prepare trigger: NPM publish/ NPM pack when NPM install is executed with no parameter
npm set-script prepare "husky install"
Copy the code
lint-staged
If you format all the files in your project at once, you are likely to make extensive changes that are out of your control.
In this case, you can use Lint-Passage to limit processing to files in Git staging area (staged).
Lint-passage also supports configuration with Cosmiconfig, which is usually set directly in package.json because it requires less configuration content.
Lint-passage matches specified tasks by file, such as:
{
"lint-staged": {
"*.{ts,js,json,yml,md}": ["prettier --write"]}}Copy the code
In this way, the tasks to be performed can be specified directly from Lint-Staged. When used with Git Hooks, simply mount the NPX Lint-Staged driver task to the Hooks.
Git Hooks integrate Prettier
The code is formatted before commit, but the pre-commit phase executes prettier –write, with lint-Passage limiting the scope of operations. One command to do it:
# MRM is a configuration file generation tool
# MRM Lint-Passage will do the following:
# 1. Automatically install Lint-Staged and Husky dependencies
# 2. Generate Lint-Staged configurations from existing configuration files in your project
# 3. Attach NPX Lint-passage (PASSAGE) to the pre-commit hooks via husky
npx mrm lint-staged
Copy the code
Use with EditorConfig
Before summarizing, we need to think about a question: Do EditorConfig still need after Prettier? If they intersect and do not contain, is there a conflict?
“EditorConfig” works on preview and input, “Prettier” reorganizes the code in save and commit, “Prettier” being the final decision on how the code looks.
Prettier actually uses the. Editorconfig file, as described in the API · Prettier, where Prettier uses the. For an intersection property, the priority is as follows: Prettier > EditorConfig > Prettier Default value.
Considering the EditorConfig covers all types of documents, my plan is EditorConfig intersection property management, other properties are controlled by Prettier.
conclusion
“Prettier” is used to style code but does not involve syntax checking, leading to the next topic, ESLint
ESLint: JavaScript Code checker (Code Linter)
Prettier focuses on uniform code styles, while ESLint focuses on finding problems with code and avoiding errors.
JavaScript itself is a dynamically weakly typed language. In its original state, there was no syntax checking tool support, so developers had to “try and error” as they ran code to find errors. ESLint fills this gap, alerting developers to problems as they write code, and even fixing them if they can.
A brief overview of what Lint provides:
1. Avoid low-level bugs and find possible grammar errors
Using undeclared variables, modifying const variables…
2. Prompt to delete redundant code
Declared but unused variables, repeated cases…
3. Make sure your code follows best practices
Refer to Airbnb Style and javascript Standard
4. Unify the team’s code style
With or without a semicolon? Use tabs or Spaces?
Reference from Deep Understanding ESLint-Zhihu.com
How ESLint works
The underlying elements of ESLint are the AST and Rules. The internal working steps of ESLint can be summarized as:
- ESLint parses source code into an AST using a parser
- Traversing the AST, firing specific hooks as it traverses nodes and paths
- Rule mounts the detection logic on the hook. If the current syntax is found to be nonconforming when executing the detection logic, the error is reported directly to ESLint.
Rule
The Rule ontology is a module that contains the attributes meta and CREATE.
meta
The metadata that defines the rulecreate
Returns an object that defines the detection logic on the appropriate hook
Take ESLint’s built-in no-void rule for example: the no-void rule recommends that void be disabled
As shown in AST Explorer, the void operator is resolved in AST as a node of type UnaryExpression.
At least estree knowable UnaryExpression covers “-” | “+” | “!” Typeof “~” | | “” |” void “|” delete “operator, and through the field operator to distinguish. So UnaryExpression[operator=”void”] matches void, and the AST iterates over the UnaryExpression[operator=”void”] node to perform all tests mounted on the hook.
The code for the no-void module is as follows
/ * * *@fileoverview Rule to disallow use of void operator.
* @author Mike Sidorov
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
// Rule type: "suggestion" indicates that no error will be caused, but change is recommended
type: "suggestion".// Document description: The description displayed on the rule list page
docs: {
description: "disallow `void` operators".category: "Best Practices".recommended: false.url: "https://eslint.org/docs/rules/no-void",},// The set of strings available in the reported information
messages: {
noVoid: "Expected 'undefined' and instead saw 'void'.",},// Specify valid rule options
schema: [{type: "object".properties: {
allowAsStatement: {
type: "boolean".default: false,}},additionalProperties: false,}]},create(context) {
// Valid options specified in meta can be read in the runtime context
const allowAsStatement =
context.options[0] && context.options[0].allowAsStatement;
//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------
return {
'UnaryExpression[operator="void"]'(node) {
// Execute the detection logic when traversing the void node
if (
allowAsStatement &&
node.parent &&
node.parent.type === "ExpressionStatement"
) {
return;
}
// An error message is reported
context.report({
node,
// Look for strings from the set of strings set in meta
messageId: "noVoid"}); }}; }};Copy the code
Fix the implementation of the
As you’ve all heard, ESLint can fix fixable problems in addition to checking for problems. How can you determine if a problem is fixable?
Again, in the Rule definition, fixable problems are reported with a fixer that tells ESLint how to fix them.
context.report({
node: node,
message: "Missing semicolon".
fix: function(fixer) {
return fixer.insertTextAfter(node, ";"); }});Copy the code
Configuration ESLint
In ESLint’s architecture, you need parsers, you need rules; With this architecture, ESLint is configurable and extensible, and pluginization is the highlight of ESLint. Finally, developers are presented with a rich set of configurations, followed by ESLint configuration items.
parser
ESLint uses Espree as its parser by default. Espree currently fully supports ECMAScript 2020 (2021.04.17) and partially supports ECMAScript 2021 (the latest proposals can be found on their proposals).
Thanks to Espree keeping up with language updates, developers can use ESLint directly in most environments. However, when ESLint is used in conjunction with other tools, you need to configure proper parsers.
@babel/eslint-parser
Client-oriented projects often need to compile higher-level languages to ES5 and require a language Compiler such as Babel, which makes sure that the Compiler is consistent with the AST parsed by Linter.
@babel/ eslint-Parser for scenarios where Babel and ESLint are used together, generates and converts the Babel parser into an ESTree that ESLint understands;
@typescript-eslint/parser
Espree cannot parse TS syntax, and @typescript-eslint/ Parser parses TS source code to ESTree.
parserOptions
Espree supports ES2020 and also supports JSX. You can specify which language you want to support in the parser options
{
"parserOptions": {
// ECMAScript version 6-10 2015-2020 is used for development. The default is ES5
"ecmaVersion": 6.// Code type: script, module
"sourceType": "module".// Additional language features
"ecmaFeatures": {
/ / enable JSX
"jsx": true.// Allow return statements in global scope
"globalReturn": true.// Enable global Strict mode
"impliedStrict": true}}}Copy the code
Global variables and environments
ESLint’s no-undef rule detects undefined variables in code. Some variables are declared by manually imported third-party libraries and need to be declared in the ESLint configuration file before they can be used
{
"globals": {
// Declare jQuery objects as global variables
"$": false True indicates that the variable is writable, false indicates readonly, and "off" indicates that the variable is disabled}}Copy the code
An environment defines a predefined set of global variables that are set according to the code execution environment
{
"env": {
"browser": true.// The project has code running in the browser environment
"node": true.// The project has code running in node.js environment
"es6": true // Start ES6 global variables and types, and automatically set the parser to ES6}}Copy the code
rules
{
"rules": {
"eqeqeq": "off"."curly": "error"."quotes": ["error"."double"]}}Copy the code
ESLint maintains rule definitions internally, and in the configuration file, you can turn rules on or off using comments or configuration files.
Rules have three states:
"off"
或0
– Turn off rules"warn"
或1
– To enable the rule, use the warning level error:warn
(Will not cause the program to exit)"error"
或2
– To enable the rule, use the error level error:error
(When triggered, the program exits)
The meta-schema in the rule definition limits the valid parameters that can be accepted. The parameters are passed after the rule state during configuration
// Turn on the quotes rule and pass the argument "double"
/* eslint quotes: ["error", "double"] */
// Turn off rule eqeqeq
/* eslint eqeqeq: "off" */
Copy the code
plugins
ESLint’s built-in core rules only consider ECMAScript. They don’t handle things that are not ECMAScript scope, such as React, Vue, TypeScript. This is where you need to customize some rules through plug-ins.
An ESLint configuration is an object module, which I’ll call a configuration object for short. Each configuration object can have properties like Parser, rules, plugins, extends, etc.
Take eslint-plugin-prettier as an example:
module.exports = {
// Provides a set of configuration objects named RECOMMENDED that can be applied directly to a configuration through extends
configs: {
recommended: {
extends: ["prettier"].plugins: ["prettier"].rules: {
"prettier/prettier": "error"."arrow-body-style": "off"."prefer-arrow-callback": "off",}}},// Customize the rule, [plug-in name]/[Rule name], the rule is prettier/prettier
rules: {
prettier: {
meta: {
docs: {
url: "https://github.com/prettier/eslint-plugin-prettier#options",},type: "layout".fixable: "code".schema: [
// Prettier options:].messages: {
// Prettier Messages:}},create(context) {
return {
Program(){}}; }},}};Copy the code
extends
{
"extends": "eslint:recommended"
}
Copy the code
An ESLint configuration is an object module that applies a set of existing configuration objects through extends.
Inheritance is the merging of configuration objects on top of the current configuration object. There are several types of inherited objects:
eslint:
The beginning of the string, provided by the officialeslint:recommended
Enable rules that ESLint recommends to enableeslint:all
Turn on all core rules in ESLint
eslint-config-
The following characters are shared by third-party libraries:- Use code best practices directly, such as
eslint-config-airbnb
,eslint-config-standard
- Use custom common configurations to
eslint-config-xxx
Send packages to NPM and share them in multiple projects
- Use code best practices directly, such as
plugin:
The first string, using the configuration in Plugin- The calling mode is
Plugin: plug-in name/configuration name
, such asplugin:react/recommended
It means to useeslint-plugin-react
In therecommended
configuration
- The calling mode is
- Relative path, using the local configuration file
The configuration file
ESLint does not use Cosmiconfig to maintain configuration files, but takes matters into its own and prioritizes them in the following order. After the configuration file is found, the search continues up until the root: true file is found, and then the configuration object of all files is merged. The advantage of this pattern is that it automatically implements inheritance at the directory level.
.eslintrc.js
.eslintrc.cjs
.eslintrc.yaml
.eslintrc.yml
.eslintrc.json
package.json
Install dependencies
yarn add eslint -D
Copy the code
Once the dependencies are installed, you can use ESLint manually from the command line
# eslint CLI bootstrates creating configuration files
yarn eslint --init
# Try lint to specify a file
yarn eslint xxx.js
# lint all files
yarn eslint .
Lint specifies the type, specifies the directory
yarn eslint --ext .jsx,.js lib/
# With cache, only check files that have changed
yarn eslint --cache
Try to fix the problem
yarn eslint --fix
Copy the code
Next, add Lint to the automated workflow
IDE integration
To install plug-ins for each IDE, see Integrations – ESLint Chinese
Using VSCode as an example, install the ESLint-Visual Studio Marketplace plug-in, which requires ESLint dependencies to be installed in the project or global environment.
The plug-in provides intelligent hints. This section describes several common configuration items:
{
// Set the execution time of Lint when onType is entered and when onSave is saved
"eslint.run": "onSave".// Set automatic fix when saving
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true}}Copy the code
Git Hooks integration
Git Hooks highlighted in “Prettier”, Lint is integrated into the commit process, also configured from lint-Passage
{
"lint-staged": {
"src/**/*.{js}": ["eslint --fix"]}}Copy the code
Use this parameter with Prettier
ESLint has two types of rules:
- Formatting rules: Max-len, no-mixed-spaces-and-tabs, key-spacing, comma-style… Formatting rules: Max-len, no-mixed-spaces-and-tabs, key-spacing, comma-style…
- Code quality rules: no-unused-vars, no-extra-bind, no-implicit-globals, prefer-promise-reject-errors…
Can ESLint replace Prettier?
Code style checking seems to overlap with Prettier. Can ESLint completely replace Prettier? No.
First, not all ESLint rules provide fixers. The examples listed above only comma-style fixes.
Second, ESLint’s emphasis on JS/TS doesn’t take into account CSS, Markdown styles;
Prettier is an expert, and the code style standardization must be given to Prettier.
How to resolve conflicts?
How to resolve conflicts where ESLint and Prettier have overlapping responsibilities?
1, [eslint-config-prettier](prettier/eslint-config-prettier: Turns off all rules that are unnecessary or might conflict with Prettier. (github.com Rules, ESLint for code quality, prettier for formatter;
Solution 2, eslint-plugin-prettier This plug-in adds the prettier/prettier rule, which executes prettier and reports errors to ESLint. In short, the prettier merged into the field of eslint, bears the function of checking code style, at the same time need to match the eslint – the closing of the config – prettier eslint code style check related rules.
Install config to disable ESLint rules
yarn add eslint-config-prettier -D
Copy the code
//.eslintrc.json Uses the configuration provided by the prettier plug-in
// Add the full group configuration: config/plugin/rules
{
"extends": ["plugin:prettier/recommended"]}Copy the code
/ / vscode Settings
{
"editor.formatOnSave": true."editor.defaultFormatter": "esbenp.prettier-vscode"."editor.codeActionsOnSave": {
"source.fixAll.eslint": true}}Copy the code
conclusion
EditorConfig, Prettier, and ESLint each have their own expertise in code normalization, but they can be used together to achieve full coverage.
Prettier takes EditorConfig into account, ESLint combines Prettier with plugins, and individual configurations are recommended from the bottom up
- EditorConfig focuses on uniform editor encoding style configuration
- Prettier focuses on checking and automatically correcting code styles
- ESLint focuses on JavaScript code quality checking
The final configuration list is available in code-canonicalization (github.com)
The resources
Prettier just read this one – Zhihu (zhihu.com)
Understanding ESLint – Zhihu.com
How ESLint works – Zhihu.com
EditorConfig
Official document for Prettier
Configuring ESLint – ESLint Chinese
It-git Hooks (git-git scm.com)
husky/index.ts
Okonet/Lint-Passage: 🚫💩 — Run linters on git Passage files (github.com)