The introduction

Babel has a mountain of presence on the front end, more or less popping up in every project.

Most front-end developers may not know much about Babel, but mastering Babel is a must on the way to becoming a senior front-end engineer, both in business development and for personal advancement.

In this article, we will only talk about “dry goods”, and take you to the beauty of Babel from the perspective of principle and deep practice.

We’ll go from the basics of Babel to the world of Babel plug-in developers, and from there you’ll be comfortable with Babel.

The general contents of the paper are as follows:

  • A guide to everyday use of Babel

    • Starting with the basics, walk you through the common Plugin and Preset.

    • Babel configuration in front-end infrastructure projects.

    • Babel related polyfill content.

  • Babel plug-in development guide

    • Take you into the compilation world of Babel and understand the principles behind Babel.

    • Develop your own Babel plugin.

🚀 Without further ado, let’s begin the real world of Babel.

BabelDaily usage

We’ll start with the basic configuration of Babel and related content.

commonpluginandPreset

First let’s talk about the difference and connection between Plugin and Preset.

The so-called Preset is a set of plugins, and you can think of Preset as a package called a cluster of plugins.

commonPreset

The article lists three of the most common Preset, and more Prest are available here.

babel-preset-env

@babel/preset-env is an intelligent preset that can turn our higher version oF JavaScript code into lower version of JavaScript code according to built-in rules.

Preset -env integrates most of plugin (State > 3) translation plug-ins internally, which will translate the code according to the corresponding parameters.

You can see the specific parameter configuration here.

@babel/preset-env does not contain any JavaScript syntax proposals below Stage 3. If you want to be compatible with grammars that are lower than Stage 3, you need to introduce the corresponding Plugin for compatibility.

An extra note is that babel-preset-env is only for the translation of syntax stages, such as the translation arrow function, const/let syntax. For polyfills of some apis or Es 6 built-in modules, preset-env is untranslatable. We will explain this content in detail in the later Polyfill.

babel-preset-react

When we use JSX in React, we know that JSX will essentially be compiled as the react.createElement () method.

The babel-preset-react preset serves as a translation of JSX.

babel-preset-typescript

For TypeScript code, there are two ways to compile TypeScript code into JavaScript code.

  1. Use the TSC command to compile the TS code in combination with cli parameters or the tsconfig configuration file.

  2. Ts code is compiled using Babel with babel-preset-typescript code.

Common Plugin

The Babel website has an extensive List of plugins.

Most of the common plugins are integrated in babel-preset-env, and if you find that the latest JS syntax is not supported in your project, you can refer to the Babel Plugin List to find the corresponding syntax Plugin to add to the Babel configuration.

There are also less commonly used packages, such as @babel/register, which overwrites the require command and adds a hook to it. Thereafter, whenever files with.js,.jsx,.es, and.es6 suffixes are loaded using require, Babel is transcoded first.

These packages are not commonly used in daily life. If you have relevant compilation requirements, you can go to Babel’s official website. If there is no plugin/package available on the official website, don’t worry! We will also teach you how to develop the Babel plugin later.

The most common @babel/ plugin-transform-Runtime is explained in detail in Polyfill below.

In front-end infrastructureBabelThe configuration,

Next, let’s talk about Babel configuration related to front-end project construction.

In terms of front-end build tools, whether you are using Webapack or Rollup or any build packaging tool, there is no internal Babel configuration.

Here we use the example of WebPack, which is most commonly used in the business. Other build tools use different packages, but the principles of Babel configuration are the same.

The Babel configuration we use daily in WebPack mainly involves the following three related plug-ins:

  • babel-loader

  • babel-core

  • babel-preset-env

You probably see them a lot during project construction, so we’ll step through a snippet of pseudocode to explain the differences and connections between them.

First we need to understand that the loader in WebPack is essentially a function that takes our source code as an input and returns new content.

babel-loader

So babel-Loader is essentially a function that we match to the corresponding JSX, right? /tsx? File to babel-loader:

/ * * * *@param SourceCode sourceCode content@param Options babel-loader parameters *@returns Processed code */
function babelLoader (sourceCode,options) {
  // ..
  return targetCode
}
Copy the code

About the options, Babel – loader support directly by the parameters of the loader form injection, but also in internal loader function by reading. Babelrc/Babel config. Js/Babel. Config. Json ` ` etc files into the configuration.

You can read about how Babel initializes various infrastructure projects here.

babel-core

Babel-loader is just a function that recognizes a matching file and accepts the corresponding parameters. So the core library of Babel is @babel/core.

Babel-core is a compilation library at the core of Babel, which can generate AST abstract syntax trees by lexical-syntactic analysis – semantic analysis process of our code, so that the operation of “this tree” is called new code through compilation.

Babel-core is a combination of @babel/parse and @babel/ Generator. For those of you who are familiar with JS compilation, you may know esprima and EscodeGen. You can refer to babel-core as a combination of these two libraries.

Babel-core compiles our code using the transform method.

There are a variety of compilation methods in Babel-core, such as the transform method, which directly takes the form of a string, or the transformFile method, which takes the path of a JS file to compile the file as a whole.

It also supports both synchronous and asynchronous methods, which you can see here.

The rules for compiling and using babel-core will be covered in more detail in the plug-in section.

Next, let’s refine the corresponding babel-loader function:

const core = require('@babel/core')

/ * * * *@param SourceCode sourceCode content@param Options babel-loader parameters *@returns Processed code */
function babelLoader (sourceCode,options) {
  // Compile the source code passed in through the transform method
  core.transform(sourceCode)
  return targetCode
}
Copy the code

Here we call the babel-core library in the babel-loader to compile the code.

babel-preset-env

Babel-loader is essentially a function that internally translates JavaScript code through the Babel /core package.

But for code translation, we need to tell Babel what rules to follow. For example, I need to tell Babel, “Hey, Babel. Convert my code to EcmaScript 5!” .

This is what babel-preset-env serves as here: it tells Babel what rule I need to code-shift for.

const core = require('@babel/core');

/ * * * *@param SourceCode sourceCode content@param Options babel-loader parameters *@returns Processed code */
function babelLoader(sourceCode, options) {
  // Compile the source code passed in through the transform method
  core.transform(sourceCode, {
    presets: ['babel-preset-env'].plugins: [...]. });return targetCode;
}
Copy the code

Plugin and prest are the same thing here, so I put plugin directly in the code. Similarly, some other preset or plugin serve this purpose.

As for the infrastructure configuration of Babel, I’m sure you’ve already understood their responsibilities and fundamentals. If you have any other configuration questions, check out the Babel documentation or check out my article on how to build engineered multi-page applications with React- Webpack5-typescript.

Babelrelatedpolyfillcontent

What is meant bypolyfill

As for polyfill, let’s first explain what polyfill is.

First, let’s clarify these three concepts:

  • The latestESSyntax, such as arrow functions,let/const.
  • The latestES Api, such asPromise
  • The latestESInstance/static method, for exampleString.prototype.include

Babel-prest-env only converts the latest ES syntax, not the corresponding Api and instance methods, such as the array. from static method in ES 6. Babel doesn’t translate this method, so if we want to recognize and run the array. from method in older browsers as we expect, we need to add polyfill to implement this method on Array.

In fact, a brief summary can be made, and the syntactic conversion preset-env is perfectly suitable. However, some built-in method modules cannot recognize preset transformation only through the syntactic transformation of preset-env, so a series of “ship-like” tools are needed to supplement the low-level code implementation of this part. That’s what polyfill does,

For the content of the Polyfill method, Babel addresses two aspects:

  • @babel/polyfill

  • @babel/runtime

  • @babel/plugin-transform-runtime

Now that we know what a polyfill is and what it does and what it means, let’s break down the usage and differences between the two Babel packages one by one.

@babel/polyfill

Let’s look at the first way to implement polyfill:

@babel/polyfillintroduce

BabelPolyfill is implemented by adding methods to the global object by adding attributes and directly modifying the Prototype of the built-in object.

For example, we need to support String.prototype.include. After introducing babelPolyfill, it adds include methods to the global String prototype object to support our Js Api.

We said that this approach is essentially to mount properties on global/built-in objects, so this approach inevitably causes global contamination.

application@babel/polyfill

There is a useBuiltIns parameter in babel-preset-env that determines how @babel/polyfill is used in preset-env.

{
    "presets": [["@babel/preset-env", {
            "useBuiltIns": false}}]]Copy the code
  • useBuiltIns"usage"| "entry"| false
false

When the useBuiltIns parameter is passed in using preset-env, the default is false. It means that only the latest ES syntax will be converted, and no apis or methods will be converted.

entry

When an entry is passed in, we need to manually introduce core-JS once in the project entry file, which will introduce incompatible polyfills in full according to our configured browserList.

Tips: in Babel7.4. After 0, @babel/polyfill was discarded and became an integration of the other two packages. core-js/stable”; “regenerator-runtime/runtime”; . You can see the changes here, but they are used in the same way, except for the different packages introduced in the entry file.

You can see the browser Compatibility list configuration here.

// Additional polyfill is required in the project entry file
// Core-JS 2.0 uses "@babel/polyfill" in core-JS 3.0 to become the top two packages
import "@babel/polyfill"

// babel
{
    "presets": [["@babel/preset-env", {
            "useBuiltIns": "entry"}}]]Copy the code

Also note that when we use useBuiltIns: Entry/Usage, we need to specify core-js as an additional parameter. The default is to use Core-JS 2.0, which is the implementation of the “shim” mentioned above. It implements a set of built-in methods or apis like Promise.

Core-js 2.0 is installed along with PRESET -env and doesn’t need to be installed separately

usage

When configured as Entry, perset-env fully introduces polyfill based on our browser compatibility list. Full introduction for example, we only use the array. from method in our code. But polyfill doesn’t just introduce array. from; it also introduces Promise, array.prototype. include, and other methods that aren’t used. This results in too much volume being introduced into the package.

This brings in our useBuintIns: Usage configuration.

When we configure useBuintIns: Usage, polyfill is added on demand based on the browser compatibility configured and the Api used in the code.

When using Usage, we do not need to introduce polyfill in the project entry, it will be introduced on demand based on what is used in our project.

{
    "presets": [["@babel/preset-env", {
            "useBuiltIns": "usage"."core-js": 3}}]]Copy the code
aboutusageandentryThere is a fundamental difference to note.

Let’s take the example of introducing promises into a project.

When we configure useBuintInts: Entry, only one polyfill is introduced in the full entry file. You can understand it this way:

// When configured with entry.// A list of ways to implement polyfill
global.Promise = promise

// Other files when used
const a = new Promise(a)Copy the code

Whereas when useBuintIns: Usage is used, preset-env can only be introduced based on the polyfills used by each module.

Preset -env helps us to be introduced intelligently where needed, such as:

/ / a. js
import "core-js/modules/es.promise"; .Copy the code
/ / b.j s

import "core-js/modules/es.promise"; .Copy the code
  • In the Usage case, if we had many modules, there would undoubtedly be a lot of redundant code (import syntax).

  • Similarly, when using Usage, polyfill is introduced locally inside the module, so global variables are not polluted, while entry is mounted in the global variables, so global variables are polluted.

Different parameters of usageBuintIns have different scenarios of fitness, and the specific use scenarios of parameters need to be combined with the actual situation of their own projects to find the best way.

@babel/runtime

We mentioned that @babel/ Polyfill is a side effect of contaminating global variables, but Babel also provides another way to implement polyfill, which is @babel/ Runtime.

In a nutshell, @babel/ Runtime is more of an on-demand solution, like where you need to use Promise, @babel/runtime will add import Promise from ‘babel-Runtime /core-js/ Promise’ at the top of his file.

Meanwhile, for the useBuintIns configuration item of preset-env, our polyfill is preset-env to help us intelligentially introduce.

Babel-runtime, on the other hand, will leave the intelligence to us to import whatever we need.

NPM install — save@babel/Runtime and introduce the polyfill where it is needed. Such as:

We need to manually introduce the corresponding runtime polyfill
import Promise from 'babel-runtime/core-js/promise'

const promsies = new Promise(a)Copy the code

All in all, Babel/Runtime is what you might call a run-time “where you need to go” library.

For Babel/Runtime in most cases, we will cooperate with @babel/plugin-transfrom- Runtime to introduce polyfill to achieve intelligent runtime.

@babel/plugin-transform-runtime

babel-runtimeExisting problems

Babel-runtime when we manually introduce some polyfills, it injects utility functions like _extend(), classCallCheck() into our code, which are included in every compiled file, such as:

class Circle {}
// to compile the Class, use the _classCallCheck utility function
function _classCallCheck(instance, Constructor) { / /... }
var Circle = function Circle() { _classCallCheck(this, Circle); };
Copy the code

If we had more than one file in our project using class, it would be a disaster to inject such a redundant and repeated utility function into each file.

So for the two questions mentioned above:

  • babel-runtimeUnable to achieve intelligent analysis, we need to manually introduce.
  • babel-runtimeRedundant code is generated repeatedly during compilation.

We are going to introduce our hero @babel/ plugin-transform-Runtime.

@babel/plugin-transform-runtimerole

The @babel/ plugin-transform-Runtime plugin is designed to solve the run-time problem mentioned above.

  • babel-runtimeUnable to achieve intelligent analysis, we need to manually introduce.

The @babel/ plugin-transform-Runtime plugin will intelligently analyze the JS code that needs to be translated in our project and modularize the implementation of polyfill from babel-Runtime.

  • babel-runtimeRedundant code is generated repeatedly during compilation.

The @babel/ plugin-transform-Runtime plugin supports helpers. You can see all its configuration parameters here.

This helpers parameter allows you to convert utility functions such as classCallCheck and extends into require statements that repeat the compile phase. In this case, these utility functions are not repeated in the module in use. Like this:

// @babel/plugin-transform-runtime converts utility functions into require statements
// Instead of injecting tool module code directly into modules like runtime
var _classCallCheck = require("@babel/runtime/helpers/classCallCheck"); 
var Circle = function Circle() { _classCallCheck(this, Circle); };
Copy the code
configuration@babel/plugin-transform-runtime

In fact, the usage principle has been thoroughly analyzed above. If you have any questions about the configuration, please leave me a comment in the comments section or go to Babel website to check it out.

Here is a list of its current default configurations:

{
  "plugins": [["@babel/plugin-transform-runtime",
      {
        "absoluteRuntime": false."corejs": false."helpers": true."regenerator": true."version": "7.0.0 - beta. 0"}}]]Copy the code

conclusionpolyfill

As you can see for Polyfill I actually spent a lot of time trying to make the differences and connections between them, so let’s summarize a little bit.

There are two main ways to implement polyfill in Babel:

  • One is to use @babel/polyfill with preset-env, which may pollute the global scope.

  • One is to use @babel/ Runtime in conjunction with @babel/ plugin-transform-Runtime without contaminating the scope.

  • Global import contaminates the global scope, but relative to local import. It adds a lot of extra import statements and increases the package size.

UseBuintIns :usage is similar to @babel/plugin-transform-runtime

My personal choice is to use @babel/ plugin-transform-Runtime first and @babel/polyfill for business development.

Babel-runtime is designed to reduce repetitive code. The code generated by Babel may use utility functions such as _extend() and classCallCheck(), which are included in the compiled file by default. If there are multiple files, each file may contain a duplicate copy of the code. Babel - the runtime plug-ins can transform these tools function code into the require statement, pointing to the Babel - the runtime of the references, such as the require (' Babel - the runtime/helpers/classCallCheck '). This way, the classCallCheck code does not need to exist in every file.Copy the code

BabelPlug-in development

Now that we’ve talked about how Babel is used in everyday business, let’s talk about the development of the Babel plug-in.

You may be wondering what the Babel plugin can do. In short, the Babel plugin takes you deeper into the principles of front-end compilation.

Of course, if it’s not just a personal enhancement, if you’re developing your own component library and you want to implement on-demand import like in Element-Plus, or maybe you have your own special rules for Lint. Or for some special js writing support.

All in all, knowing how to compile is really about doing anything!

Take you into thebabelCompiled world of

The focus of this article is not on compilation. But rest assured, I will introduce you to the development of the Babel plug-in in the most popular way.

At the heart of many tools and libraries, including WebPack, Lint, Babel, and others, is the concept of Abstract Syntax Tree (AST) for handling code.

AST

The abstract syntax tree is converted from JavaScript Parser to an abstract syntax tree that defines the structure of the code. And then through the manipulation of the tree to achieve the analysis of the code, change, optimization.

For converting code to a different AST you can use astExplorer’s current mainstream AST conversion for any parser here.

Here we first list some reference sites involved to you:

  • Astexplorer: This is an online code translator that can convert any code into an AST in the way that the industry currently does.

  • Babel – Handbook: Babel plug-in development Chinese manual document.

  • The -super-tiny-Compiler-CN: a small open source listp-style converted JS compiler on Github, which is highly recommended for those interested in compiling principles.

babelBasic guide to plug-in development

When we need to develop our own Babel plug-in, we usually use some Babel libraries to carry out the code parser, Transform AST, generator Code, and do not need to carry out manual lexical/grammar analysis of the code.

Plug-in development typically involves these libraries:

  • @babel/core: As mentioned above, Babel /core is the core library of Babel. The core API is here. For example, the transform, parse method we talked about above.

  • @babel/ Parser: Babel parser.

  • @babel/types: This module contains methods for manually building the AST and checking the AST node types (such as generating the corresponding node from the corresponding API).

  • Babel /traverse: This module is used to traverse the Ast. It maintains the state of the entire tree (note that traverse is a deep traverse for the Ast).

  • Babel/Generator: This module is used for code generation, which is returned via the AST to generate new code.

babelWorkflow of

In everyday front-end projects, most of the time we use Babel to convert JS code.

Its workflow can be summarized as the following three aspects:

  • Parse stage: In this stage, our JS code (string) is lexically analyzed to generate a series of tokens. Then, the tokens are syntactic and called an AST abstract syntax tree. (Like babel-parser, which does this)

  • Transform phase: In this phase, Babel transforms the new JS syntax nodes into browser-compatible syntax nodes by traversing the tree and adding, deleting, modifying and checking the old AST. (This is where Babel /traverse traverses the tree)

  • Generator phase: This phase Babel generates new code by deep traversing the new AST transformation as well. (@babel/generator)

Let’s use a picture to illustrate this process:

babelIn theASTThe traversal process of

  • AST is the so-called depth first traversal. Students who do not know depth first can refer to relevant information by themselves ~

  • The AST node traversal in Babel is based on a Visitor pattern, where different visitors perform different operations to get different results.

  • A method named for each node is mounted on the visitor, and when an AST traversal is performed the matching method name is triggered to perform the corresponding method operation.

Hand in hand with your developmentbabelThe plug-in

Here we start with a simple arrow function from ES6 to ES5 to get you started on real Babel plug-in development.

I’m sure some students may wonder why we need to implement @babel/plugin-transform-arrow-functions to convert arrow functions.

Yes, the plugin already exists in Babel and it’s perfect. Here I want to emphasize that the reason why I chose this example is to introduce you to the development process of the Babel plug-in. A simple example can give you a better understanding of the idea behind the development of the plug-in, so that you can draw inferior-related examples.

Let’s get started

First let’s look at the results we need to achieve:

The target

// input
const arrowFunc = () = > {
	console.log(this)}// output
var _this = this
funciton arrowFunc() {
    console.log(_this)
}
Copy the code

babelOriginal conversion mode

/** ** Babel plugin * mainly still@babel/ Core's Transform, parse's handling of AST, and various conversion rules in Babel /types * * AST is a depth-first traversal * * Internal use of visitor mode * * Babel also mainly does AST conversion * * 1. Tokens = 1 ["var","a","=","1"] * 2 Parse tokens to generate AST syntax trees according to fixed rules * 3. Syntax tree conversion on the basis of the old syntax tree to add, delete, change, search to generate a new grammar * 4. Generate code according to the new Tree generate new code */

// The Babel core transformation library contains the transformation implementation of core - AST - code
/* Babel /core is the equivalent of esprima+Estraverse+Escodegen, which converts the original sourceCode into an AST syntax tree that runs through old syntax trees The plugin passed in/or the 'visitor' passed in the third parameter modiates the matching node to generate a new syntax tree and generates a new code address */
const babel = require('@babel/core');

// Babel /types tool Library This module contains methods for manually building TS and checking AST node types. (Transformation based on different node types)
const babelTypes = require('@babel/types');

// A plug-in to convert arrow functions
const arrowFunction = require('@babel/plugin-transform-arrow-functions');


const sourceCode = `const arrowFunc = () => { console.log(this) }`;

const targetCode = babel.transform(sourceCode, {
  plugins: [arrowFunction],
});

console.log(targetCode.code)
Copy the code

Here we use Babel /core, whose transform method converts our code into an AST and then into the plugins’ processing into a new AST, which generates the corresponding code.

For those of you who don’t know how plug-ins work, you can write your own code according to the code comments, which are only a dozen lines long.

Their implementation@babel/plugin-transform-arrow-functionsThe plug-in

Here we try to implement one ourselves.

First, let’s write the basic structure:

const babel = require('@babel/core');

// Babel /types tool Library This module contains methods for manually building TS and checking AST node types. (Transformation based on different node types)
const babelTypes = require('@babel/types');

// We implemented our own conversion plug-in
const { arrowFunctionPlugin } = require('./plugin-transform-arrow-functions');


const sourceCode = `const arrowFunc = () => { console.log(this) }`;

const targetCode = babel.transform(sourceCode, {
  plugins: [arrowFunctionPlugin],
});
// Prints compiled code
console.log(targetCode.code)
Copy the code
// plugin-transform-arrow-functions.js
const arrowFunctionPlugin = () = > {
    // ...
}
module.exports = {
    arrowFunctionPlugin
}
Copy the code

Here we create a plugin-transform-arrow-functions file to implement our own plugin:

We’ve seen that the Babel plug-in is essentially an object that has a property visitor inside it. The visitor object will have a number of methods, each named based on the name of the node.

When the transform method in the Babel /core traverses the AST, it enters the visitor object for matching, and if the type of the node matches the property on the visitor, the corresponding method is executed.

For example, this code:

const arrowFunctionPlugin = {
    visitor: {
      ArrowFunctionExpression(nodePath) {
          // do something}}},Copy the code

When the AST traversal is performed, if the ArrowFunctionExpression is encountered, the ArrowFunctionExpression method in the visitor object is entered to perform the corresponding logic to operate the current tree.

There are two tips that need to be explained a little bit.

  • How do I know the type of each node? Such asArrowFunctionExpressionThat’s the type of arrow function.

First, all node types are covered in Babel /types. We can look up the corresponding node type by looking at Babel /types.

Of course, there is another, more convenient way, we mentioned above astExplorer, where you can look up the AST generated by the corresponding code to get the corresponding node.

  • What is thenodePathParameter, what does it do?

Each of these methods has a nodePath parameter, which you can think of as a nodePath. It contains all the information about the fork of this node in the tree and the corresponding API. Note that the path can be emphasized here, and you can look up what it means and all the corresponding apis.

Now that we have written the basic structure, let’s start implementing the internal logic of the plug-in.

We know that if we want to compile our code, we will need to modify the AST node. Essentially we modify the AST through operations on the AST node to produce the code result we want.

  • First, we can use AstExplorer to input our source code and the expected compiled code to get the corresponding AST structure.

  • After that, we compared the structure of the two trees and modified the original AST to get our final AST.

  • And then, there shouldn’t be any more steps left. The Babel Transform method generates the corresponding source code based on our modified AST.

Students are strongly encouraged to go into AstExplorer and enter the code to be translated and compare it with the translated code.

Screenshot of arrow function nodes to be compiled:

Partial node screenshots of compiled code:

Here, we find that comparing input and output:

  • outputPlace the arrow function nodeArrowFunctionExpressionReplaced byFunctionDeclaration.
  • outputFor the arrow functionbodyTo invoke the expression declarationExpressionStatementWhen, incomingargumentsfromThisExpressionReplacement of aIdentifier.
  • At the same timeoutputAdded an additional variable declaration in the arrow function scope,const _this = this.

We just need to implement these three functions in our arrowFunctionPlugin. Let’s give it a try.

const babelTypes = require('@babel/types');

function ArrowFunctionExpression(path) {
  const node = path.node;
  hoistFunctionEnvironment(path);
  node.type = 'FunctionDeclaration';
}

/ * * * * *@param {*} NodePath specifies the current nodePath */
function hoistFunctionEnvironment(nodePath) {
  // Look up until you find the nearest top non-arrow function this p.isfunction () &&! p.isArrowFunctionExpression()
  P.isprogram ()
  const thisEnvFn = nodePath.findParent((p) = > {
    return(p.isFunction() && ! p.isArrowFunctionExpression()) || p.isProgram(); });// Next look for node paths where this is used in the current scope
  const thisPaths = getScopeInfoInformation(thisEnvFn);
  const thisBindingsName = generateBindName(thisEnvFn);
  // thisEnvFn add a variable named thisBindingsName with a value of this
  // const _this = this
  thisEnvFn.scope.push({
    // Call babelTypes to generate corresponding nodes
    / / you can access to https://babeljs.io/docs/en/babel-types here in detail
    id: babelTypes.Identifier(thisBindingsName),
    init: babelTypes.thisExpression(),
  });
  thisPaths.forEach((thisPath) = > {
    // Replace this with _this
    const replaceNode = babelTypes.Identifier(thisBindingsName);
    thisPath.replaceWith(replaceNode);
  });
}

/** ** finds where this is used * in the current scope@param {*} NodePath specifies the nodePath */
function getScopeInfoInformation(nodePath) {
  const thisPaths = [];
  // Call the traverse method on nodePath for convenience
  / / here you can access to https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md
  nodePath.traverse({
    // Deeply traverse the node path to find the internal this statement
    ThisExpression(thisPath){ thisPaths.push(thisPath); }});return thisPaths;
}

/** * returns a fixed value *@param {*} Path Node path *@returns* /
function generateBindName(path, name = '_this', n = ' ') {
  if (path.scope.hasBinding(name)) {
    generateBindName(path, '_this' + n, parseInt(n) + 1);
  }
  return name;
}

module.exports = {
  hoistFunctionEnvironment,
  arrowFunctionPlugin: {
    visitor: {
      ArrowFunctionExpression,
    },
  },
};

Copy the code

Let’s run the plugin in our code.

Summarize the plug-in development process

The above is a simple plugin Demo, but it has all the elements in it. A complete Babel plug-in process looks like this, so let’s summarize the Babel plug-in development process a little bit.

  • Compare AST nodes through source code and translated code to find out the corresponding difference nodes, and reuse the previous nodes as much as possible.

  • If a node is modified, added, or deleted, the Api in nodePath calls the corresponding method to process the AST.

At a macro level, the plug-in development process is divided into these two steps, and the rest is your “business logic” for the transformation part of the AST.

The Babel plug-in development section may cover some of the apis that you haven’t touched on before, but I chose to go straight to code instead of going into the API. If you don’t quite understand some parts, please leave a message to me in the comment section. I personally suggest that you check the corresponding API in the Babel-Handbook plug-in development manual, and you will have a deeper understanding here.

The plug-in in this article is only a small Demo level, intended to bring you to the front door of Babel plug-in development. You can view the code in the article here. The REPo includes not only the demo in this article, but also some more difficult plug-in learning mock-ups, as well as the on-demand plug-in that implements the component library mentioned at the beginning of this article (I’m still working on the on-demand plug-in, forgive my laziness…). .

Written in the end

So, thank you to everyone who sees this.

This article is just the tip of the iceberg, and hopefully it will be a starting point for you to explore Babel.