The problem

// It is used for testing
let url = 'ceshi/dasdadaadsa'

// TODO incomplete logic, subsequent optimization
for(a of b) {
    ...
}
Copy the code

When we submit code, sometimes we just want to do temporary submission, or modify some fields for testing, or even we have added remarks to remind ourselves that we need to modify later, but there are still some cases that we forget

So, can we use ESLint to customize rules to validate code when we submit it

plan

Scenario 1: Find out if ESLint has a ready-made API

'no-warning-comments': [{terms: ['todo'.'Joe'].location: 'anywhere'}] // There can be no warning remarks
Copy the code

Warning if the remarks contain certain strings

attribute value meaning
terms [“todo”, “fixme”, “xxx”] Fields responsible for verification (case insensitive)
location start/anywhere Check position

The current sample

'no-warning-comments': [{terms: ['todo'.'ceshi'].location: 'start'}] // There can be no warning remarks
Copy the code

Incorrect code example

/*eslint no-warning-comments: "error"*/

function callback(err, results) {
  if (err) {
    console.error(err);
    return;
  }
  // TODO
  / / zhang SAN
}
Copy the code

Error reported because todo and three fields were present

/*eslint no-warning-comments: "error"*/

function callback(err, results) {
  if (err) {
    console.error(err);
    return;
  }
  // xyb TODO
}
Copy the code

The no-warning-Comments check will split up multiple words, so toDO will still hit

Correct code examples

/*eslint no-warning-comments: "error"*/

function callback(err, results) {
  if (err) {
    console.error(err);
    return;
  }
  // mnbdjklmbasdbjkTODO
}
Copy the code

In this case, the value of location is start, which means that the validation starts at the beginning of the string, while the todo field appears at the end, so it is correct to pass. If you want to change the value to error, you need to change localtion to anywhere

Pass: verifies only remarks, which is different from actual requirements

Scenario 2: Customize esLint plugins

Eslint custom rules can be introduced via NPM or customized by creating a new directory rule in a project and adding its own validation logic inside ybxu.js

Again inpackage.jsonChanges in thelint

Add –rulesdir./rules SRC Specifies the official way to write custom methods, pointing to your own folder

Again in.eslintrc.jsIn the filerulesAdd an attribute to theybxu: 1, pay attention to what you addjsThe same name

Pass: Yarn Lint needs to be executed when it is executed. It is too different from the current script (check.js), and the above node node is the AST tree. There is no complete code directly corresponding to the node node used in the above code, so it is too troublesome to reverse build the code.

Plan 3: Quit and give up

As long as I give up fast enough that failure will never catch up with me

Option 4: Based on existingcheck.js

Now the check.js logic

Package. The json file

"husky": {
    "hooks": {
        "pre-commit": "lint-staged"."pre-push": "npm run push-lint"."commit-msg": "commitlint -E HUSKY_GIT_PARAMS"}},"lint-staged": {
    "*.{js,css,less,sass,scss,json,md,vue}": [
        "prettier --write"."node check.js"."git add"]}Copy the code

You can see that our Git commit actually executes node check.js

Check the js file

// check.js
const exec = require('child_process').exec;
const CLIEngine = require('eslint').CLIEngine;
const cli = new CLIEngine({});
function getErrorLevel(number) {
    switch (number) {
        case 2:
            return 'error';
        case 1:
            return 'warn';
        default:}return 'undefined';
}
let pass = 0;

// Check only js and vue code in SRC directory
exec('git diff --cached --name-only src| grep -E ".js$|.vue$"'.(error, stdout) = > {
    if (stdout.length) {
        const array = stdout.split('\n');
        array.pop();
        let index = array.indexOf('.eslintrc.js');

        const results = cli.executeOnFiles(array).results;
        let errorCount = 0;
        let warningCount = 0;
        results.forEach(result= > {
            errorCount += result.errorCount;
            warningCount += result.warningCount;
            if (result.messages.length > 0) {
                console.log('\n');
                console.log(result.filePath);
                result.messages.forEach(obj= > {
                    const level = getErrorLevel(obj.severity);
                    console.log(
                        `   ${obj.line}:${obj.column}  ${level}  ${obj.message}  ${obj.ruleId}`
                    );
                    pass = 1; }); }});if (warningCount > 0 || errorCount > 0) {
            console.log(
                `\n   ${errorCount + warningCount} problems (${errorCount} The ${'errors'} ${warningCount} warnings)`
            );
        }
        process.exit(pass);
    }
    if(error ! = =null) {
        console.log(`exec error: ${error}`); }});Copy the code

Check in js

const CLIEngine = require('eslint').CLIEngine;
const cli = new CLIEngine({});
Copy the code

Use it directly through the Node API

Finally, throw in the directory data of the modified file below to obtain the verification result results

// array is the address array for modifying files
const results = cli.executeOnFiles(array).results;
Copy the code

Can we plug our custom method into the new CLIEngine({}) procedure?

I didn’t understand the official document and gave up

On second thought, in the Node environment, we also know the path to modify the code, so we can pull out the code and do the verification ourselves

Paste code directly

// check.js
const exec = require('child_process').exec;
const rf = require('fs');
const path = require('path');
const CLIEngine = require('eslint').CLIEngine;
const cli = new CLIEngine({});
function getErrorLevel(number) {
    switch (number) {
        case 2:
            return 'error';
        case 1:
            return 'warn';
        default:}return 'undefined';
}
let pass = 0;
// Encapsulate the method to get file contents
function read(dir) {
    return new Promise((resolve, reject) = > {
        rf.readFile(dir, 'utf-8'.(err, data) = > {
            if (err) {
                reject(err);
            } else{ resolve(data); }}); }); }// Check only js and vue code in SRC directory
exec('git diff --cached --name-only src| grep -E ".js$|.vue$"'.async (error, stdout) => {
    let errorCount = 0;
    let warningCount = 0;

    // Need to prompt warning word
    const warnWordArray = ['todo'];

    if (stdout.length) {
        const array = stdout.split('\n');
        array.pop();
        const publicPath = path.resolve(__dirname); // Get the public path
        let eslintData = [];
        for (let srcItem of array) {
            // Use path.join to join paths to solve Windows and ios compatibility problems
            let eslintPath = path.join(publicPath, srcItem);
            // This is done asynchronously, in case the file does not reach the bottom of the process.exit(pass); Just quit
            await read(eslintPath).then(data= > {
                // Concatenate the content and path into eslintData
                eslintData.push({
                    code: String(data).split('\n'),  // Each value is the number of lines in the code, subscript +1 is the number of lines
                    path: eslintPath
                });
            });
        }

        for (let codeData of eslintData) {
            console.log('\n');
            // Ensure that a module displays path only once
            let isShowPath = false;
            // Ensure that a module displays warn only once
            let isWarn = false;
            let beforeItem = ' '
            codeData.code.forEach((item, line) = > {
                // If the special field is found in the corresponding row
                for (let warnItem of warnWordArray) {
                    // column is the position matching the string in question
                    let column = item.toLowerCase().indexOf(warnItem.toLowerCase());
                    // Whether to skip
                    let isPass = beforeItem.includes('// pass warnWord');
                    // If the field is hit and there is no need to skip, an error message is printed
                    if (column > -1 && !isPass) {
                        pass = 1;
                        isWarn = true;
                        if(! isShowPath) {console.log(codeData.path);
                            isShowPath = true;
                        }
                        // Print the error message, including the number of lines of code, the location of the code, and the specific field
                        console.log(`   ${line + 1}:${column + 1}  warn ${warnItem}Field not processed ');
                        warningCount += 1;
                    }
                }
                beforeItem = item
            });
            if(isShowPath && isWarn) {
                console.log(👿 If this module code needs to be uploaded, add // pass warnWord 'at the top of the corresponding verification line); }}// let index = array.indexOf('.eslintrc.js');

        const results = cli.executeOnFiles(array).results;
        results.forEach(result= > {
            errorCount += result.errorCount;
            warningCount += result.warningCount;
            if (result.messages.length > 0) {
                console.log('\n');
                console.log(result.filePath);
                result.messages.forEach(obj= > {
                    const level = getErrorLevel(obj.severity);
                    console.log(
                        `   ${obj.line}:${obj.column}  ${level}  ${obj.message}  ${obj.ruleId}`
                    );
                    pass = 1; }); }});if (warningCount > 0 || errorCount > 0) {
            console.log(
                `\n   ${errorCount + warningCount} problems (${errorCount} The ${'errors'} ${warningCount} warnings)`
            );
        }
        process.exit(pass);
    }
    if(error ! = =null) {
        console.log(`exec error: ${error}`); }});Copy the code

So let’s just do itnode check.jsIt says gogit commitIt will eventually come to executionnode check.jsC)

Add a comment to skip verification

perfect