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:

  1. AST usage scenarios
  2. The definition of the AST
  3. JavaScript Parser
  4. Transform arrow functions using AST
  5. 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.