Location>code7788 >text

How does babel-loader work? What is a babel-loader plugin? What can a babel-loader plugin do? How to make a babel-loader plugin?

Popularity:419 ℃/2025-02-20 09:36:59

This article will introduce the basic compilation knowledge and the operating principles of babel-loader

What is babel-loader?

As an old-fashioned packaging tool, babel-loader is probably already very familiar with it. It looks like this.

//
  = {
     // ...Other configurations
     module: {
         rules: [
             {
                 test: /\.js$/, // Match all .js files
                 exclude: /node_modules/, // Exclude node_modules directory
                 use: 'babel-loader' // Use babel-loader to handle
             }
         ]
     }
 };

How babel-loader works

babel-loader actually calls Babel's core library @babel/core to process the received code. Babel first uses a parser (such as @babel/parser) to parse JavaScript code into an abstract syntax tree (AST).

AST will not be discussed in depth here. Simply put, AST is to abstract each syntax element in the code (such as variable declarations, function definitions, expressions, etc.) into a tree-like data structure to facilitate subsequent analysis and conversion of the code.

For example, for codeconst message = 'Hello, World!'; will be parsed into containingVariableDeclarationVariableDeclaratorIdentifierandNumericLiteralAST of the node.

// source code
 const message = 'Hello, World!';
 // Corresponding part AST structure
 {
     "type": "VariableDeclaration",
     "kind": "const",
     "declarations": [
         {
             "type": "VariableDeclarator",
             "id": {
                 "type": "Identifier",
                 "name": "message"
             },
             "init": {
                 "type": "StringLiteral",
                 "value": "Hello, World!"
             }
         }
     ]
 }

AST View Online, You can view the relationship between source code and AST online through this website

Babel adds, deletes and checks AST nodes through plugins and presets. This preset can be understood as a prefabricated plugin for babel, which is essentially the same as the plugin; for example: Use@babel/preset-envPresets can convert ES6+ code into backwards compatible JavaScript code to suit different browsers and environments.

This modification process for AST nodes is also very similar to the modification process for dom tree nodes. The plug-in is also the core of Babel conversion, and all real modification processes occur here.

After the plug-in conversion, Babel uses the code generator (@babel/generator) Regenerate the converted AST to JavaScript code. This newly generated code is the final code after conversion

The basic process is as follows:

  1. -> @babel/core
    Receive processing files
  2. -> @beble/parser
    Convert source files to AST
  3. -> Plugin translation/modify AST
    Add, delete, modify and check AST through plug-in
  4. -> @babel/generator
    Translate the modified AST into source code

You can see that the workflow of babel-loader is actually to help us call the babel core library.
In fact, it is possible to complete the translation task without relying on webpack and babel, but we have to manually adjust the file input and output process, and webpack+babel-loader helps us omit this tedious process.

More specific referenceBabel usage guide

What is the babel-loader plugin? What can the babel-loader plugin do?

To sum up, after understanding the workflow of babel-loader, these two questions will be answered easily.
The babel-loader plug-in is an open entry for AST nodes. You can easily add, delete, modify and check AST nodes here. It allows us to focus on the core work of file compilation and skip many other tedious steps.

Using the babel-loader plug-in well can make it easier for us to complete many file compilation tasks.

How to make a babel-loader plugin?

There are many APIs for babel plug-in, and it will be more abstract at first. However, the most core APIs for babel plug-in are actually only three

vistor accessor

It defines how to access and modify AST nodes. The accessor is a bit similar to the configuration in webpack. For example, if { test: /.ts/} is configured in webpack, then the compiler will only check the .ts file, and the same is true for the vistor accessor. You've configured itVariableDeclarationAccessories, all variable declaration statements will enter here, and are configuredFunctionDeclarationAccessories, all function declarations will enter here

For example:function vistor(){}

Visitor: {
   FunctionDeclaration(path) {
     // The name here is vistor
     const functionName = ;
     ('Access to function declaration:', functionName);
   },
 };

Here are the more common vistors:

Identifier

Function: represents variable name, function name, attribute name and other identifiers. It can be used to rename variables, check specific identifiers, and other operations.

const visitor = {
    Identifier(path) {
        if ( === 'oldVariable') {
             = 'newVariable';
        }
    }
};

Literal

Function: Represents various literals, such as strings, numbers, booleans, null, etc. Can be used to modify the value of a literal.

const visitor = {
    Literal(path) {
        if (typeof  === 'string') {
             = ();
        }
    }
};

VariableDeclaration

Function: Represents variable declaration statements, such as var, let and const declarations. Can be used to modify the type of a variable declaration, add or delete variable declarations.
Example:

const visitor = {
    VariableDeclaration(path) {
        if ( === 'var') {
             = 'let';
        }
    }
};

FunctionDeclaration

Function: represents a function declaration statement. It can be used to modify function names, parameters, function bodies, etc.
Example:

const visitor = {
    FunctionDeclaration(path) {
        const newFunctionName = ('newFunction');
         = newFunctionName;
    }
};

BinaryExpression

Function: Represents binary expressions, such as a + b, a * b, etc. Can be used to modify operators or operands.
Example:

const visitor = {
    BinaryExpression(path) {
        if ( === '+') {
             = '-';
        }
    }
};

CallExpression

Function: Represents function call expressions, such as func(), (), etc. It can be used to modify the call function name, parameters, etc.
Example:

const visitor = {
    CallExpression(path) {
        if ( === 'oldFunction') {
             = 'newFunction';
        }
    }
};

IfStatement

Function: represents if statement. Can be used to modify the contents of a conditional expression, if block, or else block.
Example:

const visitor = {
    IfStatement(path) {
        const newTest = (true);
         = newTest;
    }
};

ReturnStatement

Function: represents the return statement. Can be used to modify the return value.
Example:

const visitor = {
    ReturnStatement(path) {
        const newReturnValue = (0);
         = newReturnValue;
    }
};

path

path is a very important API, it is used to check node path information and obtain AST node information, etc., this can be understood as equivalent to a window object in web development

1. Access node information

  • Get the node itself:passYou can directly access the AST node currently traversed to, and then obtain various attributes of the node.
= function(babel) {
     const { types: t } = babel;
     return {
         Visitor: {
             Identifier(path) {
                 const node = ;
                 ('Identifier node name:', );
             }
         }
     };
 };
  • Get the parent node:useThe parent node of the current node can be obtained, which is very useful when you need to process the current node based on the parent node information.
= function(babel) {
     const { types: t } = babel;
     return {
         Visitor: {
             Identifier(path) {
                 const parentNode = ;
                 if ((parentNode)) {
                     ('The current Identifier is part of the variable declaration');
                 }
             }
         }
     };
 };

2. Node operation

  • Replace nodes(newNode)Method is used to replace the current node with a new node.
 = function(babel) {
    const { types: t } = babel;
    return {
        visitor: {
            Identifier(path) {
                if ( === 'oldName') {
                    const newNode = ('newName');
                    (newNode);
                }
            }
        }
    };
};
  • Delete nodes()Methods can be used to remove the current node from the AST.
= function(babel) {
     const { types: t } = babel;
     return {
         Visitor: {
             // Assume that all calls are to be removed
             CallExpression(path) {
                 const callee = ;
                 if ((callee) &&
                     (, { name: 'console' }) &&
                     (, { name: 'log' })
                 ) {
                     ();
                 }
             }
         }
     };
 };

3. Traversal control

  • Continue to traverse the child nodes(visitor)Methods allow continued traversal in the subtree of the current node, using a custom accessor object.
= function(babel) {
     const { types: t } = babel;
     return {
         Visitor: {
             FunctionDeclaration(path) {
                 const customVisitor = {
                     Identifier(subPath) {
                         ('Identifier inside Function:', );
                     }
                 };
                 (customVisitor);
             }
         }
     };
 };
  • Skip child node traversal()The method can skip the child node traversal of the current node and directly enter the next sibling node.
= function(babel) {
     const { types: t } = babel;
     return {
         Visitor: {
             ObjectExpression(path) {
                 // Skip the child node traversal of the ObjectExpression node
                 ();
             }
         }
     };
 };

4. Scope Management

  • Get scopeYou can get the scope where the current node is located. Scope objects provide many methods for managing variables and identifiers.
= function(babel) {
     const { types: t } = babel;
     return {
         Visitor: {
             Identifier(path) {
                 const scope = ;
                 const binding = ();
                 if (binding) {
                     ('Variable binding information:', binding);
                 }
             }
         }
     };
 };
  • Create a new identifier(name)Methods are used to generate a unique identifier to avoid naming conflicts.
 = function(babel) {
    const { types: t } = babel;
    return {
        visitor: {
            FunctionDeclaration(path) {
                const newId = ('newFunction');
                 = newId;
            }
        }
    };
};

types

It is mainly used to create, verify and operate abstract syntax tree (AST) nodes, and is also a very frequently used API, equivalent to a document object in web development.

Create an AST node

  • Create an identifier node:use(name)An identifier node can be created to represent variable names, function names, etc.
const babel = require('@babel/core');
 const t = ;

 // Create an identifier node named 'message'
 const identifier = ('message');
  • Create a function declaration node(id, params, body)Can be used to create function declaration nodes, whereidIt's the function name,paramsis an array of parameters,bodyIt is a function body.
const id = ('add');
const param1 = ('a');
const param2 = ('b');
const params = [param1, param2];
const body = ([
    (
        ('+', param1, param2)
    )
]);
const functionDeclaration = (id, params, body);

Verify the AST node type

  • Determine whether the node is an identifier(node)Used to determine whether a node is an identifier node.
const babel = require('@babel/core');
 const t = ;

 const node = ('test');
 if ((node)) {
     ('This is an identifier node');
 }
  • Determine whether the node is a function declaration(node)It can determine whether a node is a function declaration node.
const id = ('multiply');
 const param1 = ('x');
 const param2 = ('y');
 const params = [param1, param2];
 const body = ([
     (
         ('*', param1, param2)
     )
 ]);
 const functionNode = (id, params, body);
 if ((functionNode)) {
     ('This is a function declaration node');
 }

Operation AST node

  • Modify the name of the identifier node: The identifier node can be modified directlynameproperty.
const babel = require('@babel/core');
const t = ;

const identifier = ('oldName');
 = 'newName';
  • Modify the parameters of the function declaration node: Can modify the function declaration nodeparamsproperty.
const id = ('subtract');
 const param1 = ('m');
 const param2 = ('n');
 const params = [param1, param2];
 const body = ([
     (
         ('-', param1, param2)
     )
 ]);
 const functionNode = (id, params, body);

 // Add a new parameter
 const newParam = ('c');
 (newParam);

Assist in generating complex code structures

  • Generate conditional statements: Can be used(test, consequent, alternate)generateifStatement.
const test = ('>', ('a'), ('b'));
 const consequence = ([
     (
         (
             (''),
             [('a greater than b')]
         )
     )
 ]);
 const alternate = ([
     (
         (
             (''),
             [('a is less than or equal to b')]
         )
     )
 ]);
 const ifStatement = (test, consequent, alter);

If you need more API references or accessor features,Click on the detailed official document

Here is a simplest plugin demonstration:

//
  = function (babel) {
     // Deconstruct the type module from the babel object to operate the AST node
     const { types: t } = babel;

     return {
         // The visitor object defines how to access and modify AST nodes
         Visitor: {
             // Take the Identifier node as an example here. This function will be executed when traversing to the Identifier AST node
             Identifier(path) {
                 // path represents the path of the current node, containing the context information of the node
                 if ( === 'oldIdentifier') {
                     // If the name of the node is 'oldIdentifier', modify it to 'newIdentifier'
                      = 'newIdentifier';
                 }
             }
         }
     };
 };

Introduced in webpack

const path = require('path');
 // Introduce the plugin we wrote
 const myBabelPlugin = require('../my-babel-plugin/myBabelPlugin');

  = {
     // Entry file
     entry: './src/',
     // Output configuration
     output: {
         path: (__dirname, 'dist'),
         filename: ''
     },
     module: {
         rules: [
             {
                 // Match .js files
                 test: /\.js$/,
                 // Exclude node_modules directory
                 exclude: /node_modules/,
                 use: {
                     loader: 'babel-loader',
                     options: {
                         // Use the plugin we wrote
                         plugins: [myBabelPlugin]
                     }
                 }
             }
         ]
     }
 };