Related background:

Medium and large companies often have more than one front-end project, and it is often difficult to maintain a certain style of project team with more front-end developers and many front-end projects. This article is mainly a summary of problem collection, research, development and implementation for the current situation of the group in the company.

1. Current situation of front-end projects

There are many items in the front-end group, but the code quality inspection is not unified. For example, xx systems and mobile projects have simple LintrC configurations, but are duplicated configurations; Projects like node are almost simply configured with a few rules; However, code detection is not configured for all projects of the internal YY system, which leads to the need to repeatedly raise relevant code style issues during CR. In addition, for newly opened projects, most of them manually create new files and copy the configuration, which is redundant and inefficient.

2. Related problems and how to solve them

In view of the above situation, the relevant test configuration of each project is not unified and is not easy to manage. Considering the current situation of front-end code detection tools and projects within the group, it is considered to configure a general rule, which sets different rule sets and introduces different rule set extensions for different projects. We only need to maintain the package project and the rules themselves, each project can be downloaded to use. There are several benefits to this approach:

  • Unify the coding style of team members, so that the code style is unified
  • Improving code readability, such as style issues such as line breaks, can make code harder to read
  • Lint can easily find low-level, obvious errors. Early detection can reduce online problems caused by syntax problems and save time troubleshooting and locating problems

Introduction and significance of lint-related tools

1. JSLint

At the heart of JSLint is the Pratt parser implemented by the Top Down Operator Precedence technique. In the course of this evolution, it became obvious that all rules in JSLint were defined by the developer’s personal style, and that to use them you had to accept all rules defined by them, which led to the creation of new Lint/Linter tools.

2. JSHint

JSHint is based on JSLint, and its core implementation is still based on the Pratt parser, but addresses many of JSLint’s weaknesses, notably the ability to add configurable rules, as described in the reference article: “The emergence of JSHint was almost inevitable, an irreconcilable contradiction between front-end developers’ growing desire for code quality and outdated and paranoid tools.”

3. JSCS and ESLint

ESLint and JSCS were released around the same time, and both use AST processing rules and Esprima to parse code. Since source code needs to be converted to AST, the execution speed is actually lower than JSHint. The reason ESLint really took off was the introduction of ES6, which added a lot of new syntax, but JSHint itself was designed to be incompatible with the new syntax for some reason, and ESLint was extensible enough to extend custom rules and replace the default parser with babel-esLint. The syntax for the new framework was also well compatible, so ESlint eventually became widely used; JSCS and ESLint were merged because the implementation principles of JSCS and ESLint were similar and there was no point in maintaining them.

4. Prettier 

Prettier is also a tool for checking code styles for optimization, but it has few rules that can be configured and cannot be changed to enforce a uniform code style. Prettier unlike ESlint, as shown in the figure below, Prettier applies not only to JS Code but also to other common front-end languages, although its rules focus on Code Formatting, not code-quality. Prettier can be used alone in a project or in conjunction with ESlint.

Project configuration ESLint

1. Install and use ESLint

1.1 The corresponding codes in the file enable/disable the corresponding rules

After installing NPM install eslint –save-dev, you can directly enable the rule for the corresponding code block in the project source code:

/* eslint console: "error" */ 
console.log('66666')
Copy the code

It is also possible to disable related rules directly in a code block:

/* eslint-disable */ alert(' foo '); /* eslint-disable */ alert(' foo '); /* eslint-enable */ 2. Disable eslint checking for a line: alert(' foo '); // eslint-disable-line // eslint-disable-next-line no-alert alert(' foo ');Copy the code

1.2 Enable/Disable rules in the Configuration File

NPM install eslint –D After installing ESLint globally, use the eslint –init command to initialize the ESLint configuration file. You can choose from the following configuration questions:

An initial.eslintrc file will be generated as follows:

module.exports = {
  env: {
    browser: true,
    es6: true,
  },
  extends: [
    'plugin:vue/essential',
    'airbnb-base',
  ],
  globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
  },
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
  },
  plugins: [
    'vue',
  ],
  rules: {},
};
Copy the code

2. Describes configuration parameters for ESLint files

Tips: Suggestions or directly read the English translation, some refer to the Chinese meaning of the translation is wrong

ESLint configuration parameter documentation: eslint.org/docs/user-g…

There are a lot of configuration parameters in the eslintrc file, most of which are described in the official documents. Here are some of the most important parameters in this use:

  • Root: After setting it to true, ESLint will no longer look up for file detections (the next two rc tests have results)
  • impliedStrict:ecmaFeaturesIn theimpliedStrictWhen set to true, theEnable strict mode throughout the project
  • Env: Specifies the environment to enable, usually browser and Node
  • Plugins: The name of the related plug-in to be imported. The eslint-plugin- prefix can be omitted
  • Extends: Extends includes shareable configuration packages. The package name prefix eslint-config- can also be omitted. It also includes absolute or relative paths to base configuration files
  • rulesThe rules parameter allows you to set many of esLint’s built-in rules, and the official documentation has a fix icon in front of the rules“Indicates that the rule can be automatically repaired during detection. Otherwise, error will be reported and manual repair is required. Rules in the rules property each accept a single parameter with the following value:
"Off" or 0: close the rule "WARN" or 1: start the rule, yellow warning (will not cause the program to exit) "error" or 2: start the rule, red error (program execution result 0 indicates that the test passes; If the execution result is 1, an error needs to be rectified.)Copy the code

Some rules can be configured with additional parameters after being enabled. You can add additional parameters after enabling rules according to different conditions.

3. Command line parameters

ESLint command-line argument documentation: eslint.cn/docs/user-g…

Eslint-related command line Settings can also be directly referenced in the official documentation, typically in package.json files for esLint specific command line Settings to execute the corresponding commands to render different effects. The global syntax is mainly used in the command line, and only a few parameters are introduced here:

  • –ignore-path: follows the name of the file, which will be ignored when esLint detects the file
  • –fix: After this parameter is set, problems that can be automatically fixed during execution are directly fixed. If you do not want to automatically fix problems during execution, do not set this parameter
  • –rulesdir: Reference local development rule path

Custom rule sets

Once we know how to simply configure the relevant detection rules in the project, we will encounter the problems mentioned in the beginning. Repetitive configuration and repeated copy and paste will lead to inefficiency and code redundancy is not easy to manage uniformly. The extensibility of ESLint allows us to do a lot of things easily: for example, to consolidate generic configurations into a shareable configuration package, we only need to sort out generic rules and publish them as an NPM package, other projects just need to download and configure the rules to take effect.

1. Share the overall structure of configuration package eslint-config-zly

The shared configuration package mainly exports some configurations and users can import them directly. Some well-known ESlint-config like Airbnb and Alloy are referred to here.

1.1 refer to reality

Airbnb source: Search Github to find the source code

Airbnb mainly builds two configurations in the package file. Eslint-config-airbnb mainly contains some react rule configurations. Eslint-config-airbnb-base is mainly a basic rule configuration classification.

Eslint did not provide templates for config packages, so after referring to the Airbnb directory structure, it started manually creating files of different categories, dividing some built-in rules into different functional blocks described in the rules module of ESLint. It is then introduced via the extends relative path in the.eslintrc.js file. However, since Airbnb and its project itself use a relatively simple technology stack, there is no problem with releasing two packages in this way. If multiple projects and multiple technology stacks distribute packages in this way, multiple packages need to be managed and released.

1.2 reference alloyTeam

Alloy source: github.com/AlloyTeam/e…

Since airbnb’s separate distribution structure is not suitable for our design, I took a look at the source code of Alloy. Alloy gives Prettier management some stylesheet rules. Inside, the React/Vue/TypeScript plugin rules are listed in base.js, ue. Js, React. Documents, examples, tests, websites, etc., make it easy for you to refer to alloy rules and make your own customization based on them. In addition, Travis – CI is highly automated internally, handing over all processes that can be automatically managed to scripts. This is split and governed by automated scripts into several ESLint configuration files, with each rule managed in a separate directory. After referring to the directory structure of Alloy, we adjusted the eslint-config-zly project structure, mainly putting the base and vue rules in the same set directory:

2. Contract design

Because the final release is to be released to NPM, so combined with some previous reference project source structure, how to release package management for part of the discussion, mainly for the final package release and project use how to introduce, including the main package design is as follows:

2.1 Release multiple packages and use them in combination

The extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends extends This is mainly the package management is too much, combined use is not direct and convenient, so abandoned.

extends: [airbnb/base, airbnb/base]
Copy the code

2.2 Releasing Multiple Packages without Combination:

Similar to Airbnb, rules for different functions are released as a separate NPM package, which can be downloaded and introduced separately when needed. This satisfies the expectation of letting the business side introduce any package they want with any type of rule, but managing the package release requires maintenance one by one, which is cumbersome and does not meet our expectations.

Reference: https://github.com/airbnb/javascript/blob/master/packages/eslint-config-airbnb-base/README.md extends: airbnb/base extends: Airbnb/Vue releases different packages according to different categories in a project, such as eslint-config-Airbnb-base, which needs the base to import the package separatelyCopy the code

2.3 Release only one Package and use it in combination

Similar to alloy above, functions will be divided clearly through different folders. For example, base only holds basic rules and VUE only holds VUE rules. If both rules are needed, they can be used in combination. So this plan is a little bit off our expectations.

Reference: https://github.com/AlloyTeam/eslint-config-alloy/blob/HEAD/README.zh-CN.md extends: alloy/base, extends: Extends: [alloy/base, alloy/vue] Extends: [alloy/base, alloy/vue]Copy the code

2.4 Send only one package, no combination

Considering the advantages and disadvantages of the previous two packet sending designs and combining with our requirements, we choose the third scheme. All rules are managed in a package, base set is introduced in every other rule set as the basic rule set, and other similar VUE sets are superimposed with VUE rules after introducing base set. Both basic rules and VUE rules take effect when the business side directly introduces the VUE set.

extends: zly/vue extends: zly/react extends: zly/tina extends: Zly /node vue projects are introduced into the Zly package vue set separately, while node projects are introduced into the Zly package node set separatelyCopy the code

3. The set of rules

Once the general project architecture is set up, the next step is content filling. The main purpose is to use different rule sets to detect different frame grammars. The following rule sets are formulated in combination with the company’s existing projects:

- 'best-practices.js'
- 'custom.js'
- 'errors.js'
- 'es6.js'
- 'import.js'
- 'tyle.js'
- 'variables.js'
Copy the code

Finally, under the _base file index.js introduces the above file as extends along with the Airbnb-base package:

Tips: In ecmaFeatures, impliedStrict is set to true, meaning that all projects are in strict mode. ‘use strict’ is not required. Initially, I just wanted to write this configuration in the Node set, but finally I decided to put it in the base set, making sure that all the projects used were in strict mode.

module.exports = {
  parserOptions: {
    ecmaFeatures: {
      impliedStrict: true,
    }
  },
  extends: [
    'airbnb-base',
    join(__dirname, './rules/best-practices'),
    join(__dirname, './rules/errors'),
    join(__dirname, './rules/es6'),
    join(__dirname, './rules/import'),
    join(__dirname, './rules/style'),
    join(__dirname, './rules/variables'),
    join(__dirname, './rules/custom'),
  ],
}
Copy the code

3.2 the vue set

The vUE rule set mainly introduces the vue3-recommended, _base set of eslint-plugin-vue and configures it for a specific rule:

module.exports = {
  env: {
    browser: true,
  },
  extends: [
    'plugin:vue/vue3-recommended',
    join(__dirname, '../_base/index'),
    join(__dirname, './rules/vue'),
  ],
  parserOptions: {
    parser: 'babel-eslint',
  },
  plugins: ['vue'],
}
Copy the code

3.3 Tina set

Tina rules are mainly for small program projects, using the same VUE syntax. There was some discussion on whether to introduce the vue rule set directly or to directly introduce eslint-plugin-Vue. In the end, it was decided to directly introduce the Vue rule set to manually disable unnecessary rules in the Tina set:

module.exports = {
  globals: {
    App: true,
    Page: true,
    wx: true,
    getApp: true,
    getPage: true,
    getCurrentPages: true,
  },
  extends: [
    join(__dirname, '../vue/index'),
    join(__dirname, './rules/tina'),
  ],
  parserOptions: {
    parser: 'babel-eslint',
  },
}
Copy the code

3.4 the node set

Eslint-config-node, ESlint-config-egg, ESlint-plugin-eggache and ESlint-plugin-node After comparison, it is found that ESlint-plugin-Node plug-in has the most and purest node rules, and eslint-plugin-Node is widely used in the community and provides many solutions. Therefore, after multiple investigations, eslint-plugin-Node is finally decided. Nodefocuses on using eslint-plugin-Node recommended, introduces a _base set, and finally makes special processing in rules/node/index.js for a specific node rule.

module.exports = {
  env: {
    node: true,
  },
  extends: [
    'plugin:node/recommended',
    join(__dirname, '../_base/index'),
    join(__dirname, './rules/node')
  ],
  plugins: ['node'],
}
Copy the code

For the rule set above except _base, the order of extends is to introduce the third-party plug-in rules, then the _base rules, and then finally the files that you have modified for the rules.

4. Custom plug-ins

ELSint custom rules and source code analysis juejin.cn/editor/draf…

Eslint can’t integrate custom rules into the esLint configuration package. Only plugins can integrate custom rules. The overall project then transitioned from Config to Plugin.

1. Use the template to initialize the configuration

Yeoman generator – eslint: Yeoman. IO/authoring

The Yeoman template generator-ESLint can be used to quickly create an ESLint Plugin based on the template provided by ESLint. You can initialize the ESLint Plugin using the following steps:

NPM i-g yo NPM i-g generator-eslint // Create a plugin yo eslint:plugin // Create a rule yo eslint:ruleCopy the code

The initialized project directory structure is as follows:

  • The rules folder stores each rule file
  • The Tests folder holds the unit test files
  • Package. json is the description file of the ESLint plugin NPM package, where the name attribute is the name of the ESLint plugin, named eslint-plugin-*

2. Integrate custom rules

The config folder in the lib folder contains the previously defined custom rule set classification, and the rules folder contains the relevant custom rule classification. _.get() disallows the third argument in lodash-get.js in rules and in index.js:

3. Local test

If your NPM package has not been released and you need to debug locally, use NPM Link to debug locally. To use the eslint-plugin command globally, run the NPM link eslint-plugin command in your eslint-plugin project, and then run the NPM link eslint-plugin in your test project to import tests. Executing the NPM link command in the NPM package folder creates a symbolic link between the global {prefix}/lib/node_modules/ and the package folder where you performed the NPM link.

Note: package-name is the name in package.json, not the folder name.

Five. Code submission detection optimization

1. git hooks

Git hooks are hooks that trigger custom scripts during git execution. Every.git file that contains a repository contains a. Git file. In this folder, you can create hooks from the hooks names that will be used when Git does something.

Below is the case script of each hook. You can remove sample and directly write shell script to execute it:

The usual hooks we would use are:

  • Pre-commit: called when a Git commit is executed. It is executed before a commit is committed to check the code that is about to commit. If the result is not zero, the commit will be interrupted.
  • Commit-msg: when git merge is called by git commit or git merge and exits with a non-zero status code, this operation is interrupted to check the submitted code comment format.

Q: Can the corresponding hook file configuration under.git file be directly uploaded to the remote repository for others to pull to achieve the effect of configuration sharing?

A: After checking relevant data, the hook file configuration under.git file cannot be submitted to the remote repository, but it can be copied to other colleagues, but this is not the result we want.

2. husky

Husky is an essential tool for front-end engineering. Since it is not convenient to directly modify.git/hooks files, using Husky allows you to easily add Git hooks to your project.

2.1 the pre – commit library

NPM install pre-commit –save-dev pre-commit — dev pre-commit — dev pre-commit — dev pre-commit — dev

{ 
  "scripts": { 
    "lint": "eslint . --fix", 
  }, 
  "pre-commit" : ["lint"], 
}
Copy the code

2.2 Husky + pre-commit hook

NPM install husky –save-dev: husky –save — dev: husky –save — dev: husky –save — dev: husky –save — dev: husky –save — dev: husky –save — dev You can pair this with a pre-commit hook so that when committing a COMMIT the script NPM run Lint will be executed for full project file detection. If only the current configuration.

Note: Node_modules (husky + pre-commit) node_modules (husky + pre-commit) Because the local node_modules pre-commit library is still there, the two will collide and the husky execution will fail.

{"husky": {"hooks": {"pre-commit": "NPM run lint", // Run the NPM run lint command before committing}} "scripts": {"lint": "eslint . --ext .js,.vue", } }Copy the code

3. lint-staged

The Husky configuration already makes it easy to implement pre-commit code detection, but using detection directly has some drawbacks, such as checking all the project files for each commit. Lint-staged tests can be used to detect only committed code. Lint-staged reads only files from staging areas and runs configured scripts, avoiding code that has not been committed to staging. Lint-staged filtering files use the Glob syntax, which can be used in conjunction with HusKY, which triggers Lint-staged filtering files, which execute scripts, This configuration can reduce both the amount of time it takes for ESLint to fully detect and the amount of time it takes to fix problems!

/ / package. The json file "husky" : {" hooks ": {" pre - commit" : "lint - staged,"}}, "lint - staged" : {" *. Vue ": [ "eslint --cache --fix" ], "*.js": [ "eslint --cache --fix" ] },Copy the code

3.1 Two copies of.eslintrc.js configuration

Some projects are not separate vue projects or Node projects, similar to xx system, node is used as the middle layer, at this time there will be two environments in a project, in order to check with different rule sets under the folder of different environments, we are going to configure two.eslintrc files, the main configuration is as follows:

// package.json file "scripts": {"lint": "NPM run lint-web && NPM run lint-node", "lint-web": "eslint . -c ./.eslintrc-web.js --ext .js,.vue --ignore-pattern server/ --cache --fix", "lint-node": "eslint server/ -c ./.eslintrc-node.js --ext .js --cache --fix", }Copy the code
Module. exports = {root: true, plugins: ['@zly'], extends: ['plugin:@zly/vue',],}Copy the code
Module. exports = {root: true, plugins: ['@zly'], extends: ['plugin:@zly/node',],}Copy the code

After the configuration, the system can normally throw errors, but the automatic autofix function is completely disabled. The subsequent discovery may have been caused by the fact that we changed.eslintrc.js to.eslintrc-node.js,.eslintrc-web.js, and the IDE cannot dynamically recognize the configuration of ESLint after the change. You can do it manually in the editor, but it’s really cumbersome for everyone’s compiler to do it manually, which brings us to root.

3.2 root test

When I first introduced the root parameter, I looked at the relevant translations in official documents and on the Internet. I always felt that different translations were different, and I was also ambiguous about this concept alone, so I did a test experiment of root. Eslint-root-test creates a.eslintrc.js file in the root directory and a.eslintrc.js file in the server folder in the root directory. The test results are as follows:

According to the above test results, root can exactly solve our need for two copies of RC, so we set.eslintrc.js file in the following directory and.eslintrc.js file in the server file for intelligent system. Set root to true for both rc files.

// package.json file "husky": {"hooks": {"pre-commit": "lint-passage ", "commit-msg": "commitlint -e $HUSKY_GIT_PARAMS" } }, "lint-staged": { "*.vue": [ "stylelint --config /.stylelintrc.short.js", "stylelint --config /.stylelintrc.js --fix", "eslint --cache --fix" ], "*.css": [ "stylelint --config /.stylelintrc.short.js", "stylelint --config /.stylelintrc.js --fix" ], "server/**/*.js": [ "eslint -c ./server/.eslintrc.js --cache --fix" ], "! (server)/**/*.js": [ "eslint --cache --fix" ] }, "scripts": { "lint": "npm run lint-web && npm run lint-node", "lint-web": "eslint . --ext .js,.vue --ignore-pattern server/ --cache --fix", "lint-node": "eslint server/ -c ./server/.eslintrc.js --ext .js --cache --fix", },Copy the code

NPM run Lint-web NPM run lint-node NPM run lint-node Lint-staged configurations also perform staging file detection for server and non-server files.

4. Package. json is not processed at the same time as dependent versions in node_modules

Check – library dependencies: www.npmjs.com/package/che…

We often run into this problem when the latest pull code has a dependency upgrade, but the node_modules locally installed dependency is still old. It’s fine to run but it’s best to keep both dependencies in sync.

Problem a:

Q: If we need to update our own private NPM dependencies in package.json every time, but the local version is still the old version, how can we give a hint about the inconsistency between the remote and local versions of the private NPM?

Solution a:

A: I looked around and couldn’t find A particularly suitable solution.

Solution 2:

A: Package. json < span style = “box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; font-size: 14px! Important; word-break: break-word! Important;” Any time there is an out of sync it will give you a hint.

4.1 NPM Script Hooks

NPM scripts are custom scripts recorded in the scripts field in package.json. With custom scripts, users can record common command lines in some projects in package.json without having to type them every time. NPM script provides pre and POST hooks. Custom script commands can also add pre and POST hooks. For example, the hooks of the dev script are predev and postdev. When NPM run dev is executed, the hooks are automatically executed in the following order:

npm run predev && npm run dev && npm run postdev
Copy the code

4.2 check – dependencies

Nodecheck. Js < nodecheck. Js > nodeCheck. Js < nodecheck. It works when both sides are in line:

// package.json 文件

"scripts": {
  "predev": "node check.js"
}
Copy the code
const output = require('check-dependencies') .sync({ verbose: True,}) if (output.status === 1) {throw new Error(' local dependencies are not in sync with package.json ')}Copy the code

Check to see if you need to add another js file to your project. Check to see if you need to add another js file to your project. Check to see if you need to add another js to your project.

// package.json 文件

"scripts": {
  "predev": "check-dependencies"
}
Copy the code

Vi. Follow-up development

1. Routine maintenance

At present, most of the projects have been replaced and the test is normal. To access the testing tool for the new project, you only need to install the NPM package and configure it in.eslintrc.js to take effect, which is simple and convenient. In the future, you only need to unify some rules.

2. Items to be solved

In fact, I found a lot of problems in the testing process. For example, I was used to webstrom at the beginning, but there were too many problems in the testing process. Following the principle of single variable, I used VScode for testing.

  • For the same configuration, vscode automatically fixes normally, but eslint automatically fixes in webstrom
  • Eslint is bypassed when submitting code in Sourcetree for unknown reasons

3. Reference

Debugging command line tools: segmentfault.com/a/119000001…

ESLint application practice in large team: tech.meituan.com/2019/08/01/…