Both Babel and ESLint are AST based, one for code conversion and the other for error checking and fixing. Both Babel and ESLint can analyze and transform code, so what’s the difference?

In this article we explore the differences between the Babel plugin and the ESLint plugin.

Babel plug-in

The Babel compilation process is divided into three steps: Parse, Transform, and Generate. Plug-ins can be specified, and visitor calls are incorporated as the AST is traversed.

For example, we write a plugin that inserts the filename + column number in the console.xx argument:

The callee attribute of the function call node (CallExpression) is checked and, if it is the API of console.xx, a StringLiteral StringLiteral node is inserted into arguments with a filename + column number.

const targetCalleeName = ['log'.'info'.'error'.'debug'].map(item= > `console.${item}`);

const parametersInsertPlugin = ({ types }, options) = > {
    return {
        visitor: {
            CallExpression(path, state) {
                const calleeName = path.get('callee').toString()
                if (targetCalleeName.includes(calleeName)) {
                   const { line, column } = path.node.loc.start;
                   path.node.arguments.unshift(types.stringLiteral(`${options.file.filename}: (${line}.${column}) `)}}}}}module.exports = parametersInsertPlugin;
Copy the code

Then compile the code using Babel Core’s API and call the plug-in:

const { transformFileSync } = require('@babel/core');
const insertParametersPlugin = require('./plugin/parameters-insert-plugin');
const path = require('path');

const inputFilePath = path.join(__dirname, './sourceCode.js');

const { code } = transformFileSync(inputFilePath, {
    plugins: [insertParametersPlugin],
    parserOpts: {
        sourceType: 'unambiguous'.plugins: ['jsx']}});console.log(code);
Copy the code

When the source code is as follows:

console.log(1);

function func() {
    console.info(2);
}

export default class Clazz {
    say() {
        console.debug(3);
    }
    render() {
        return <div>{console.error(4)}</div>}}Copy the code

The target code is:

As you can see, the file name and column number parameters are inserted in the API call to console.xx.

This is an example of the Babel plug-in doing code conversion.

We can summarize the features of the Babel plug-in:

  • The plug-in takes the form of a function that returns an object whose visitor property declares what to do on what node
  • Visitor functions can add, delete, or modify the AST through the PATH API
  • The modified AST is printed as object code

Eslint plug-in

The ESLint plug-in will also parse the code, find the AST to check, and then check and report errors, but not necessarily fix the code, only if a fix is specified.

Let’s write an ESLint plugin that checks the format of an object.

Check and fix the following code formats when required:

const  obj = {
    a: 1.b: 2.c: 3
}
Copy the code

Become this:

const  obj = {
    a: 1.b: 2.c: 3
}
Copy the code

Eslint can look for tokens associated with an AST, that is, we can get tokens that start and end each property of an object and the line and column number,

We verify that the line number of the token ending with the previous attribute is equal to the line number of the token starting with the next attribute.

So here it is:

ObjectExpression specifies that ObjectExpression is checked for the start and end token line numbers of each attribute of the {} expression. If the next attribute is not the +1 line of the previous attribute, an error is reported.

In addition, you can specify how to fix the error. In our case, the error was fixed by replacing the part between the two tokens with a newline character (os.eol) + TAB.

const os = require('os');

module.exports = {
     meta: {
         fixable: true
     },
     create(context) {
         const sourceCode = context.getSourceCode();
         return {
            ObjectExpression(node) {
                for (let i = 1; i < node.properties.length; i ++) {
                     const firstToken = sourceCode.getTokenAfter(node.properties[i - 1]);
                     const secondToken = sourceCode.getFirstToken(node.properties[i]);
                     if(firstToken.loc.start.line ! == secondToken.loc.start.line -1) {
                        context.report({
                            node,
                            message: 'No empty lines between object properties'.loc: firstToken.loc,
                            *fix(fixer) {
                                yield fixer.replaceTextRange([firstToken.range[1],secondToken.range[0]], os.EOL + '\t'); }}); }}}}; }};Copy the code

This completes the inspection and automatic repair of the object format.

The plugin is named object-property-format, and then we call it using API:

First, introduce the ESLint module and create an ESLint object:

const { ESLint } = require("eslint");

const engine = new ESLint({
    fix: false.overrideConfig: {
        parser: '@babel/eslint-parser'.parserOptions: {
            sourceType: "unambiguous".requireConfigFile: false,},rules: {
            "object-property-format": "error"}},rulePaths: ['/'].useEslintrc: false
});
Copy the code

Here, turn off the configuration file (useEslintrc: false) and use only the configuration here (overrideConfig).

We specify Babel’s parser (@babel/eslint-parser) and don’t need a Babel configuration file. Then we introduce the rule that we wrote earlier, object-property-format, and set the error level to error.

You also need to specify rulePaths, which tells ESLint where to look for rules.

After that, we call lintText’s API for Lint:

(async function main() {
  const results = await engine.lintText(` const obj = { a: 1,b: 2, c: 3 } `);

  console.log(results[0].output);

  const formatter = await engine.loadFormatter("stylish");
  const resultText = formatter.format(results);
  console.log(resultText); }) ()Copy the code

For the results, we formatted them using the built-in formater.

Using Node, the result is as follows:

As you can see, ESLint checks for two errors in the object format.

Why wasn’t it fixed? Eslint needs to have fix enabled to fix the code because fix is not enabled.

Change Eslint’s fix option to true and try again:

As you can see, no errors have been reported and the code has been fixed.

This is an example of an ESLint plugin that does code formatting checking and fixing.

Here we can summarize the features of the ESLint plugin rule:

  • Rule takes the form of an object, and the create property is a function that returns an object specifying what AST to check and fix
  • The AST handler can use the context API to retrieve tokens from different parts of the source code for formatting checks
  • The fix function gets access to the Fixer API to add, subtract, and change characters to code at a particular location
  • By default, the code is not fixed. You need to specify fix to fix it

Similarities and differences between esLint and Babel

Let’s compare the features of the Babel plugin and esLint plugin. (The ESLint plugin here strictly refers to esLint’s rules, and esLint plugins can contain multiple rules.)

Babel plug-in:

  • The plug-in takes the form of a function that returns an object whose visitor property declares what to do on what node
  • Visitor functions can add, delete, or modify the AST through the PATH API
  • The modified AST is printed as object code

Eslint plug-in:

  • Rule takes the form of an object, and the create property is a function that returns an object specifying what AST to check and fix
  • The AST handler can use the context API to retrieve tokens from different parts of the source code for formatting checks
  • The fix function gets access to the Fixer API to add, subtract, and change characters to code at a particular location
  • By default, the code is not fixed. You need to specify fix to fix it

Let’s compare the similarities and differences:

  • Formally, esLint’s rule is object-function-object, while the Babel plugin is function-object, and much of it is esLint’s meta-information, or meta attributes. That’s the design difference between the two.

  • Both the Babel plugin and esLint rule can iterate over nodes and specify which nodes to process, but the Babel plugin can add, delete, or alter the AST via the PATH API. Eslint gets the sourceCode via context.getSourcecode (), checks the format through the sourceCode API, and fixes through the Fixer API.

  • Changes to the Babel plugin take effect by default, passing in options at most, while esLint’s fix function takes effect only if it is enabled.

Eslint’s AST records range information in the source code. You can search for tokens based on range information, but Babel Parser can also specify ranges and tokens

That is, it is theoretically possible to implement esLint on Babel, but the API design is different, resulting in different scenarios for both.

  • Babel is the path API for AST additions, deletions and changes, suitable for code analysis and transformation.
  • Eslint checks and fixes code formats via the sourceCode and Fixer apis for error checking and fixing.

However, in essence, the compilation process is not that different.

conclusion

We wrote a Babel plug-in that inserts arguments into the console.xx API and an ESLint plug-in that checks and fixes object formats, analyzed the features of the two and compared them.

The plug-ins are different in form and have different apis:

Babel adds, removes, and modifys the AST using the PATH API, while ESLint checks the format of the code using the sourceCode API and fixes it using fixer’s API. This makes the Babel plugin better suited for code conversion and the ESLint plugin better suited for checksum fixing of code formats. But Babel can actually do the same thing as ESLint, and the compilation process is essentially the same.

This article compares the Babel plugin and esLint plugin together, explaining the essential similarities and API differences between the two plugins, hoping to help you better understand Babel and ESLint plugin.