current position:Home>I the first page of the scaffold - AST abstract syntax tree

I the first page of the scaffold - AST abstract syntax tree

2022-08-06 18:17:06Lin Yuchen

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情

事件篇

For a while I was writing avue项目,Each new page contains some fixed components(Such as the navigation bar),Set some fixed property methods(e.g. from the route objectmetatake page title)and referencing some global objects, etc.,Copy and paste every time,It's not complicated, but it's complicated,So I'm going to write a new page directly program when have to deal with them together,程序也比较简单,As long as write a template to usenodejs的fsThe module does file processing just fine:

const PAGE_TEMPLATE = ` <template> <div id="page"> <ss-head-navbar :title="title"></ss-head-navbar> </div> </template> <script> import app from "_u/app.js"; import NavBar from "_c/head-navbar/head-navbar"; export default { name:"user-info", components: { "ss-head-navbar": NavBar }, beforeMount() { this.title = this.$route.meta.title; }, data() { return { title: "" } } } </script>`

var fs = require("fs");
const readline = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
})

readline.question(`Please enter the page file path(如:userInfo/userInfo.vue):`, path => {
  var pathArray = path.split("/");
  // 获取文件名和目录名
  var fileName = pathArray[pathArray.length - 1];
  var directoryName = path.replace(fileName, "");
  // 获取扩展名
  var extension = fileName.match(/\..*$/)?.[0];
  if (!extension) {
    // If there is no suffix and suffix
    path += ".vue"
  }
  else if (extension != ".vue") {
    // If the suffix is ​​notvue
    console.warn("后缀名不是.vue,替换成.vue")
    path.replace(/\..*$/, ".vue")
  }
  fs.writeFile(`src/pages/${path}`, PAGE_TEMPLATE, (err) => {
    if (err) {
      fs.mkdir(`src/pages/${directoryName}`, (err) => {
        if (err) throw err
        fs.writeFile(`src/pages/${path}`, PAGE_TEMPLATE, (err) => {
          if (err) throw err;
        })
      })
    };
    console.log('文件已创建');
  });
  readline.close()
})
复制代码

Since do this step,So can we go further,Write the new page to the route?

疑惑篇

这个项目用的是vue-router,This means routing is introduced into a similar object:

let routes = [
  {
    path: '/promotion/goods/list',
    name: 'promotion_goods',
    component: () => import('@/pages/promotion/goods/list.vue'),
    meta: {
      title: "Consumer list page"
    }
  }
]
复制代码

也就是说,The newly generated page needs to add a new routing object here,Fill in the file path of the new pageroutes数组中.

这时候,就需要AST(抽象语法树)来帮忙了:

计算机科学中,抽象语法树Abstract Syntax Tree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示.它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构.之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节.比如,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现;而类似于 if-condition-then 这样的条件跳转语句,可以使用带有三个分支的节点来表示.

换句话说,An abstract syntax tree is code of code,describe code in code,从而做到“write code”的功能.

那么,这么一个JS对象,What is its syntax tree like??Use this site to view below:astexplorer.net/

A simple exploration of the NuggetsAST相关的文章,The solution came.

解决篇

也就是说,we can read the currentJS文件的AST树,Connect the newly added node to the tree after parsing,Then you can generate a newJS代码了.

Four packages need to be introduced here:

  • @babel/parse for parsing the original codeAST树
  • @babel/traverse 用于遍历AST树
  • @babel/type Used to add nodes of the specified type
  • @babel/generator 用于根据ASTtree generates new code
const { parse } = require("@babel/parser");
const generate = require("@babel/generator");
const traverse = require("@babel/traverse");
const { objectExpression, objectProperty, stringLiteral,arrowFunctionExpression } = require("@babel/types");

const code = ` let routes = [ { path: '/promotion/goods/list', name: 'promotion_goods', component: () => import('@/pages/promotion/goods/list.vue'), meta: { title: "consumption page" } } ] `

const ast = parse(code);

// traverse the current codeAST树
traverse.default(ast, {
  // Iterate only through the array in it
  ArrayExpression(path) {
    // Add objects in array based on structure
    path.node.elements.push(objectExpression([
      objectProperty(stringLiteral("path"), stringLiteral("/promotion/goods/detail")),
      objectProperty(stringLiteral("name"), stringLiteral("promotion_detail")),
      objectProperty(stringLiteral("component"), arrowFunctionExpression([],stringLiteral('@/pages/promotion/goods/detail.vue'))),
      objectProperty(stringLiteral("name"), objectExpression([
        objectProperty(stringLiteral("title"), stringLiteral(escape("Consumption details page"))),
      ])),
    ]));
  },
})

const output = generate.default(ast);
console.log(unescape(output.code));
复制代码

The final result is this:

let routes = [{
  path: '/promotion/goods/list',
  name: 'promotion_goods',
  component: () => import('@/pages/promotion/goods/list.vue'),
  meta: {
    title: "consumption page"
  }
}, {
  "path": "/promotion/goods/detail",
  "name": "promotion_detail",
  "component": () => "@/pages/promotion/goods/detail.vue",
  "name": {
    "title": "Consumption details page"
  }
}];
复制代码

The more difficult part is generating new nodes:

objectExpression([
  objectProperty(stringLiteral("path"), stringLiteral("/promotion/goods/detail")),
  objectProperty(stringLiteral("name"), stringLiteral("promotion_detail")),
  objectProperty(stringLiteral("component"), arrowFunctionExpression([], stringLiteral('@/pages/promotion/goods/detail.vue'))),
  objectProperty(stringLiteral("name"), objectExpression([
    objectProperty(stringLiteral("title"), stringLiteral(escape("Consumption details page"))),
  ])),
])
复制代码

Here we need to compare the previous page withAST树,Find the constructor of the corresponding node,and then nested through layers,生成新的节点:

  • Object Expression CorrespondenceobjectExpression
  • A key-value pair in the object corresponds toobjectProperty
  • 字符串对应stringLiteral
  • fat arrow function correspondencearrowFunctionExpression,Note that the first parameter of the constructor is an array,代表参数,The latter is the function body

Finally use it in the same way as beforenodejs的fs对象对JS文件进行改写,A simple page scaffolding is done!

copyright notice
author[Lin Yuchen],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/218/202208061755239414.html

Random recommended