No func assign is implemented by Eslint plug-in and Babel plug-in respectively

Eslint has many built-in rule s, one of which is called no func assign, which means that a function cannot be assigned a value in order to avoid an error in the call caused by the re assignment of the function.

There are two ways to implement this rule. We use Babel plug-in and Eslint plug-in respectively.

Train of thought analysis

The goal is to check the re assignment of the declared function. There are two ideas:

The first idea is to find all function declarations in the scope, analyze the places that reference it, and report an error if it is an assignment statement.

The second idea is to find all assignment statements in reverse. If the declaration of the variable on the left in the scope is a function, an error will be reported.

The source code of this rule of Eslint is implemented with the first idea, and we use the Babel plug-in to implement the second idea.

The Babel plug-in implements lint

To implement according to the second idea, first find all assignment statements:

const noFuncAssignLint = (api, options) => {

    return {
        visitor: {
            AssignmentExpression(path, state) {
            }
        }
    }
};

module.exports = noFuncAssignLint;

Then use the scope related api of path.scope to find the declaration in the scope on the left, that is, binding, and then judge whether the type of the declaration is a function. If so, an error will be reported.

const noFuncAssignLint = (api, options) => {

    return {
        visitor: {
            AssignmentExpression(path, state) {
                const assignTarget = path.get('left').toString();
                const binding = path.scope.getBinding(assignTarget);

                if (binding) {
                    if (binding.path.isFunctionDeclaration() || binding.path.isFunctionExpression()) {
                        // report errors..
                    }
                }
            }
        }
    }
};

module.exports = noFuncAssignLint;

Babel plug-in can declare the visitor of AST type to be processed, which will be called during traversal, in which ast can be analyzed and converted. The api of path is provided for the addition, deletion and modification of AST, and the api of path.scope is used for scope analysis. The api based on path and path.scope can complete various analysis and conversion functions.

The Eslint plug-in implements lint

This rule is originally implemented by eslint, which is based on the first idea. That is, find all the function declarations, analyze the references, and report an error if it is an assignment.

The rule of eslint consists of two parts:

  • The meta part is the original information, including documents, error messages, etc
  • The create part is the implementation of lint function
module.exports = {
    meta: {
        type: "problem",

        docs: {
            description: "disallow reassigning `function` declarations",
            recommended: true,
            url: "https://eslint.org/docs/rules/no-func-assign"
        },

        schema: [],

        messages: {
            isAFunction: "'{{name}}' is a function."
        }
    },

    create(context) {

        function checkForFunction(node) {}

        return {
            FunctionDeclaration: checkForFunction,
            FunctionExpression: checkForFunction
        };
    }
};

We declare the processing of function declaration and function expression, that is, we get the declaration in the scope through the context api, and then judge the reference. If the reference is an assignment statement, an error will be reported.

function checkForFunction(node) {
    context.getDeclapanredVariables(node).forEach(checkVariable);
}

function checkVariable(variable) {
    if (variable.defs[0].type === "FunctionName") {
        checkReference(variable.references);
    }
}

function checkReference(references) {
    // If it is an assignment statement
    astUtils.getModifyingReferences(references).forEach(reference => {
        context.report({
            node: reference.identifier,
            messageId: "isAFunction",
            data: {
                name: reference.identifier.name
            }
        });
    });
}

The Eslint plug-in can declare the ast type listener to be processed, which will be called during traversal. It can analyze the AST and then report an error. Context api is provided to analyze ast, such as scope analysis, and context.report is also provided to report errors.

The difference between Babel plug-in and Eslint plug-in

Babel and Eslint parse the source code into AST, and then traverse the AST for processing.

The AST processing function in Babel is called visitor, which can be used to analyze and modify AST. In Eslint, it is called listener, because only AST can be analyzed and cannot be modified.

Babel plug-in provides the api of path for adding, deleting and modifying AST. The api of path.scope is used to analyze the scope, including declarations and references. The Eslint plug-in provides an api for context, which is used to analyze the scope, etc.

Eslint's AST contains token information, which can be used for format checking, such as spaces and line breaks, but Babel's AST does not, so format checking can only be implemented by eslint.

The Eslint plug-in supports fix to modify the code, but it is not implemented by modifying the AST, but by specifying how to modify a certain range and replacing it with a string.

summary

Around no func assign, the built-in rule of eslin, we analyzed two ideas and implemented them with Babel plug-in and eslin plug-in respectively. In fact, it is mainly scope analysis. This function is supported in Eslint plug-in and Babel plug-in.

The functions of Eslint and Babel plug-ins are based on AST, but Babel does AST analysis and conversion, while Eslint only does AST analysis (including format check).

It should be noted that the fix function of Eslint is not to modify the AST implementation, but a simple string replacement.

Eslint plug-in and Babel plug-in are all implemented based on AST. They have many homogeneous parts that can be compared and studied.

Added by Centrek on Thu, 25 Nov 2021 05:55:59 +0200