Speaking of AST grammar, many front-end friends are not unfamiliar, but the real use is very few, this article simply summarizes my own learning and using AST process.

So what is an AST in the first place?

JavaScript is an interpreted type language, which goes through three stages of lexing, parsing (AST), and code generation before execution

  • Lexical analysis: JavaScript analyzes code strings into lexical units

    Var answer = 6; Can be decomposed into var, answer, =, 6,; [ { "type": "Keyword", "value": "var" }, { "type": "Identifier", "value": "answer" }, { "type": "Punctuator", "value": "=" }, { "type": "Numeric", "value": "6" }, { "type": "Punctuator", "value": ";" } ]Copy the code

    For specific type values, see @babel/types

  • Syntax analysis:

    Converting lexical units into syntax structure trees composed of nested elements is called abstract syntax code (AST).

    {
      "type": "File",
      "start": 0,
      "end": 16,
      "loc": {
        "start": {
          "line": 1,
          "column": 0
        },
        "end": {
          "line": 2,
          "column": 0
        }
      },
      "errors": [],
      "program": {
        "type": "Program",
        "start": 0,
        "end": 16,
        "loc": {
          "start": {
            "line": 1,
            "column": 0
          },
          "end": {
            "line": 2,
            "column": 0
          }
        },
        "sourceType": "module",
        "interpreter": null,
        "body": [
          {
            "type": "VariableDeclaration",
            "start": 0,
            "end": 15,
            "loc": {
              "start": {
                "line": 1,
                "column": 0
              },
              "end": {
                "line": 1,
                "column": 15
              }
            },
            "declarations": [
              {
                "type": "VariableDeclarator",
                "start": 4,
                "end": 14,
                "loc": {
                  "start": {
                    "line": 1,
                    "column": 4
                  },
                  "end": {
                    "line": 1,
                    "column": 14
                  }
                },
                "id": {
                  "type": "Identifier",
                  "start": 4,
                  "end": 10,
                  "loc": {
                    "start": {
                      "line": 1,
                      "column": 4
                    },
                    "end": {
                      "line": 1,
                      "column": 10
                    },
                    "identifierName": "answer"
                  },
                  "name": "answer"
                },
                "init": {
                  "type": "NumericLiteral",
                  "start": 13,
                  "end": 14,
                  "loc": {
                    "start": {
                      "line": 1,
                      "column": 13
                    },
                    "end": {
                      "line": 1,
                      "column": 14
                    }
                  },
                  "extra": {
                    "rawValue": 6,
                    "raw": "6"
                  },
                  "value": 6
                }
              }
            ],
            "kind": "var"
          }
        ],
        "directives": []
      },
      "comments": []
    }
    Copy the code

    In fact, we can just look at the body, which contains our syntax tree structure, in fact, some fields start, end, loc values are basically useless to us

    We can add, update, and remove nodes from the source code in this step,

    1. Using @babel/ Parser to turn strings into AST syntax trees,
    2. Use the @babel/traverse transform code
    const parser = require('@babel/parser'); const traverse = require('@babel/traverse'); var answer = 6; const ast = parser.parse(code); traverse.default(ast, { enter(path) { if(path.isIdentifier({name: "answer"})) { path.node.name = 'question'; } if(path.isLiteral({value: 6})) { path.node.value = 666; }}}); const newCode = generator.default(ast, {}, code).code; console.log('newCode: ', newCode) // newCode: var question = 666;Copy the code

    We can also use the Node type in the syntax tree to make code changes

    traverse.default(ast, {
      VariableDeclarator(path) {
        if(path.node.id.name ==='answer'){
          path.node.init.value ='666'
        }
      }
    });
    Copy the code
  • Code generation:

  const generator = require('@babel/generator');
  const newCode = generator.default(ast, {}, code).code;

  console.log('newCode: ', newCode) // newCode:  var question = 666;
Copy the code

In the process of front-end development, it is inevitable to develop some code tools, through which we can modify our source code, so as to improve the development efficiency. In this case, we need to analyze the source code, modify the source code, and produce new code. This is where AST parsing is used