What is babel?
You can test it yourself on the babel website.
Uses of babel
- Translate esnext, typescript, flow, etc. to js supported by the target environment.
- Some use-specific code conversions. babel exposes a number of api’s that can be used to parse code to AST, convert AST, and generate target code. Developers can use it for specific-purpose conversions such as function stubbing (automatic insertion of code into functions, e.g. buried code), automatic internationalization, default import to named import, etc.
- Static analysis of code. After parse, the code can be transformed because the structure of the AST allows the code to be understood. After understanding the code, it can also be used to analyze the information of the code and perform some checks, in addition to converting and generating the target code.
- The linter tool is to analyze the structure of the AST and to check the code specification.
- api Documentation automatic generation tool that extracts comments from the source code and then generates documentation.
- The type checker checks the AST for type consistency based on the type information extracted or derived from the AST, thus reducing type-related errors at runtime.
- Compression and obfuscation tool, this also analyzes the code structure and performs various compilation optimizations such as removing dead code, variable name obfuscation, constant folding, etc. to generate smaller and better performing code.
The babel compilation process
The overall babel compilation process is divided into three steps.
- parse: convert the source code into an abstract syntax tree (AST) by parser
- transform: traverse the AST and call various transform plugins to add, delete and change the AST
- generate: print the transformed AST to the target code and generate sourcemap in three steps.
The purpose of the parse phase is to convert the source code string into a machine-understandable AST, which is divided into lexical analysis and syntax analysis.
let name = 'guang'; such a piece of source code, we have to first divide it into words (token) that cannot be subdivided, that is
let, name, =, 'guang', this process is lexical analysis, splitting the string into words according to the rules of word formation.
The transform phase is the processing of the AST generated by parse, and it will traverse the AST, and the corresponding visitor function will be called when different AST nodes are processed in the traversal process.
The generate phase prints the AST to the target code string and generates the sourcemap.
Literals, identifiers, expressions, statements, module syntax, and class syntax all have their own ASTs.
For example, literalLiteral.
‘a’ is StringLiteral, 123 is NumberLiteral, variable names, attribute names, and other identifiers are Identifier.
We can go to the AST visualization tool to understand each node.
You can also go to @babel/types to see all AST types.
Public Properties of AST
- type: the type of the AST node.
- start, end, loc: start and end represent the start and end subscripts of the source string corresponding to the node, and do not distinguish between rows and columns. The loc attribute is an object with line and column attributes that record the start and end row numbers, respectively.
- leadingComments, innerComments, trailingComments: indicates the starting comments, middle comments, and ending comments, because there may be comments in each AST node, and they may be in the starting, middle, and ending positions.
- extra: Record some extra information for handling some special cases.
It provides two api’s: parse and parseExpression, both of which convert the source code to AST, but parse returns an AST with File as the root node and parseExpression returns an AST with Expression as the root node. You can specify what to parse and how to parse it. The two most common options are plugins and sourceType.
plugins: Specifies plugins such as jsx, typescript, flow, etc. to parse the corresponding syntax.
sourceType: specifies whether to support parsing module syntax, and has three values: module parses the es module syntax, script does not parse the es module syntax and is executed as a script, and unambiguous is based on whether the content has import and unambiguous is based on whether the content has import and export to determine whether to parse the es module syntax.
The parse AST is traversed and modified by @babel/traverse. The babel traverse package provides the traverse method: function traverse(parent, opts)
parent specifies the AST node to be traversed and opts specifies the visitor function. babel calls the corresponding visitor function when traversing the AST corresponding to parent. enter is called before traversing the children of the current node and exit is called after traversing the children of the current node.
The path parameter is the path during traversal, which preserves contextual information. The path provides access to node information.
The parameter state allows passing data between different nodes.
Creates an AST and determines the type of the AST. To create an IfStatement you call t.ifStatement(test, consequent, alternate);
To determine if a node is an IfStatement, call isIfStatement or assertIfStatement t.isIfStatement(node, opts); t.assertIfStatement(node, opts);
Batch node creation
Print to target code string
To complete the babel compilation process based on the packages above, you can start with the source strings, source files, and ASTs.
- package.json config
- The babel-loader needs to be configured in webpack
We need to tell babel how to compile and what to compile. There are several ways to configure a file.
- Set the babel field in package.json.
.babelrc file or .babelrc.js
Same as .babelrc.js, but babel.config.js is for the whole project.
The configuration file tells babel what to compile and then introduces the corresponding plugins, for example, the @babel/plugin-transform-arrow-functions plugin for the arrow function transformation.
Now the arrow function in our code will be compiled as a normal function.
When there are a lot of plugins or a lot of options for a plugin, it becomes more expensive to use. A preset is a layer of wrapping around babel configuration.
Just install the preset and it will automatically convert the new features in the code into code supported by the target browser, depending on the target browser you set.
plugin-transform-runtime and runtime plugin
When compiling a class with babel, you need some tool functions to help with the implementation.
Each class generates _classCallCheck, which ends up generating a lot of duplicate code. plugin-transform-runtime is designed to solve this problem.
babel can translate some new features, but for new built-in functions (Promise, Set, Map), static methods (Array.from, Object.assign), instance methods (Array.prototype.includes) that need babel-polyfill to be addressed. babel-polyfill will fully simulate an ES2015+ environment.
Because @babel/polyfill is relatively large, introducing it as a whole both increases the size of the project and pollutes it with too many variables, so it is more recommended to use preset-env to introduce polyfill on demand.
A simple case
You want to use babel to automatically insert arguments for filename and row number in api such as console.log, so that you can easily locate the code, which does not affect other logic.
Idea: When traversing the AST to console.log and other api automatically insert some arguments, that is, to do some processing of the function call expression CallExpression specified by the visitor. callExrpession node has two properties, callee and arguments, respectively, corresponding to the name of the function called and So we need to determine when callee is console.xx and insert an AST node in the array of arguments, creating an AST node requires the @babel/types package.