First published at: github.com/USTB-musion…
Writing in the front
Babel,Webpack, VUe-CLI, esLint, and many other tools and libraries rely on the Abstract Syntax Tree to examine and analyze code. In the front end, AST is widely used. For example, in vue.js, the first step in the process of converting the template written in the code into the render function is to parse the template string to generate AST. In order to give developers a better programming experience, JS syntax is not suitable for the understanding of the program. Therefore, the source code needs to be converted into an AST to be more suitable for program analysis, and the browser compiler usually converts the source code into an AST for further analysis and other operations. By understanding the concept of AST, it is helpful to gain insight into some of the front-end frameworks and tools.
This paper will summarize from the following parts:
- AST usage scenarios
- The definition of the AST
- JavaScript Parser
- Transform arrow functions using AST
- Use AST to realize the Babel plug-in
AST usage scenarios
- Code syntax checks, code style checks, code formatting, code highlighting, code error prompts, code auto-completion, and so on
- JSLint, JSHint checks for code errors or styles to find potential errors
- IDE error prompts, formatting, highlighting, autocomplete, and more
- Code obfuscation compression
- UglifyJS2 etc.
- Optimization changes the code, changes the code structure to achieve the desired structure
- Code packaging tools webpack, rollup, and more
- CommonJS, AMD, CMD, UMD, etc
- CoffeeScript, TypeScript, JSX, etc. Convert to native Javascript
The definition of the AST
- The official definition of AST:
In computer science, an abstract syntax tree (AST for short), or syntax tree, is a tree-like representation of the abstract syntactic structure of source code, specifically the source code of a programming language.
Here is the AST converter online:AST converter. After the code is converted into the AST, the format is roughly as follows:
The JSON format after AST conversion is roughly as follows:
{
"type": "Program",
"start": 0,
"end": 16,
"body": [
{
"type": "FunctionDeclaration",
"start": 0,
"end": 16,
"id": {
"type": "Identifier",
"start": 9,
"end": 12,
"name": "ast"
},
"expression": false,
"generator": false,
"params": [],
"body": {
"type": "BlockStatement",
"start": 14,
"end": 16,
"body": []
}
}
],
"sourceType": "module"
}
Copy the code
The type field, as a string, indicates the type of the node. Such as “BlockStatement”, “Identifier”, “BinaryExpression”, etc. Each type of node defines attributes that describe the node type. These nodes can then be used to analyze other operations.
JavaScript Parser
-
JavaScript Parser is a Parser that converts JavaScript source code into abstract syntax trees.
-
The browser converts the JS source code through the parser into an abstract syntax tree, which is further converted into bytecode or directly generated into machine code.
-
Generally speaking, each JS engine has its own abstract syntax tree format. Chrome V8 engine, Firefox SpiderMonkey engine, etc. MDN provides a detailed description of the SpiderMonkey AST format, which is the industry standard.
JS Parser’s three-plate axe
1. Use esprima to convert the source code to AST
let esprima = require('esprima');
let code = 'function ast(){}';
let ast = esprima.parse(code);
console.log(ast);
Copy the code
After installing via NPM I esprima -s, run the above code and output:
Script {
type: 'Program',
body:
[ FunctionDeclaration {
type: 'FunctionDeclaration',
id: [Identifier],
params: [],
body: [BlockStatement],
generator: false,
expression: false,
async: false } ],
sourceType: 'script' }
Copy the code
2. Averse traverses and updates the AST through the estraverse
let esprima = require('esprima');
let estraverse = require('estraverse');
let code = 'function ast(){}';
let ast = esprima.parse(code);
estraverse.traverse(ast, {
enter(node) {
console.log('enter', node.type)
if (node.type == 'Indentifier') {
node.name += 'enter';
}
},
leave(node) {
console.log('leave', node.type)
if (node.type == 'Indentifier') {
node.name += 'leave';
}
}
})
console.log(ast);
Copy the code
Once installed via NPM I estraverse -s, running the above code will output:
Script {
type: 'Program',
body:
[ FunctionDeclaration {
type: 'FunctionDeclaration',
id: [Identifier],
params: [],
body: [BlockStatement],
generator: false,
expression: false,
async: false } ],
sourceType: 'script' }
Copy the code
3. Use EsCodeGen to regenerate AST source code
t esprima = require('esprima'); let estraverse = require('estraverse'); let escodegen = require('escodegen'); let code = 'function ast(){}'; let ast = esprima.parse(code); estraverse.traverse(ast, { enter(node) { console.log('enter', node.type) if (node.type == 'Identifier') { node.name += '_enter'; } }, leave(node) { console.log('leave', node.type) if (node.type == 'Identifier') { node.name += '_leave'; }}}); let result = escodegen.generate(ast) console.log(result);Copy the code
After installing the NPM I escodeGen -s, execute the above code and print:
function ast_enter_leave() {
}
Copy the code
In this case, let’s
function ast() {
}
Copy the code
Modified to:
function ast_enter_leave() {
}
Copy the code
Transform arrow function
Using babel-core(Babel core library, which implements the core conversion engine) and babel-types(which can implement type judgment, generate AST nodes, etc.) and AST
let sum = (a, b) => a + b
Copy the code
To be:
let sum = function(a, b) {
return a + b
}
Copy the code
The implementation code is as follows:
// let Babel = require('babel-core'); // let types = require('babel-types'); let code = `let sum = (a, b) => a + b`; // let sum = function(a, B) {// return a + b //} // Let visitor = {ArrowFunctionExpression(path) {console.log(path.type); let node = path.node; let expression = node.body; let params = node.params; let returnStatement = types.returnStatement(expression); let block = types.blockStatement([ returnStatement ]); let func = types.functionExpression(null,params, block,false, false); path.replaceWith(func); }} let arrayPlugin = {visitor} // Let result = babel.transform(code, {plugins: plugins) [ arrayPlugin ] }) console.log(result.code);Copy the code
Use AST to realize the Babel plug-in
The implementation code is as follows:
Let code = 'const result = 1000 * 60 * 60'; let babel = require('babel-core'); let types= require('babel-types'); let visitor = { BinaryExpression(path) { let node = path.node; if (! isNaN(node.left.value) && ! isNaN(node.right.value)) { let result = eval(node.left.value + node.operator + node.right.value); result = types.numericLiteral(result); path.replaceWith(result); let parentPath = path.parentPath; // If the parent of this expression is also an expression, Need the recursive calculation if (path. ParentPath. Node. Type = = 'BinaryExpression') {visitor. BinaryExpression. Call (null, path.parentPath) } } } } let cal = babel.transform(code, { plugins: [ {visitor} ] });Copy the code
Refer to the article
AST Abstract syntax tree
JS abstract syntax tree at a glance
Go deep into Babel. This one will do
Everest Front-end Architect Training course
You can pay attention to my public account “Muchen classmates”, goose factory code farmers, usually record some trivial bits, technology, life, perception, grow together.