Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >AST in TypeScript 实践

AST in TypeScript 实践

原创
作者头像
JoviCheng
发布于 2019-06-30 09:19:18
发布于 2019-06-30 09:19:18
5.6K1
举报
文章被收录于专栏:进击的全栈进击的全栈

实践来源

  最近参与了一个 Node 项目脚手架的开发工作,为了提高编码效率,导师提议写一个 VSCode 的插件,功能上大体有点像 snippets 代码段,但比 snippets 优秀的地方是,插件还能实现以下两大功能:

  1. 可遍历目前工程目录下所有的 @provide ,结合 VSCode API 可以实现快速添加 @inject
  2. 可识别相应文件代码段,灵活插入代码段

TypeScript

  该 Node 项目由 TypeScript 编写,虽然 TypeScript 在前期编写时对变量类型的定义约束需要消耗我们额外的一点精力,但不得不说的是,在后期 Coding 阶段,配合宇宙编辑器 VSCode 的代码提示,写代码可以跟开火箭一样,行云流水。

  回到 AST 的话题中,因为 TypeScript 在近几年才算热门,AST 在 TypeScript 的应用上的优秀实践也难得一见,相关的文档及教程也不算太完整,于是开始了 AST in TypeScript 的踩坑之旅。

AST with Babel

  Babel 是一个 JavaScript 编译器,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

  Babel 主要通过三个步骤实现以上的流程:解析(Parse)、转换(transform)、生成(generate),对应的Babel提供了许多库去完成以上的事情。

插件实现的大概思路如下:

1.读取 api/index.ts 中定义的好的接口文件,并组合成一个数组,供开发者选择。

2.然后当开发者在想要插入 API 接口时,插件会调用 VSCode 的 vscode.window.showQuickPick API,弹出 QuickPick 供开发者选择。

3.当开发者选择接口后,为了防止重复引用,插件会去判断当前文件是否已经引用了该接口模块,如果已经引入则报错,如果没有,则会去判断接口应该插入的位置。

4.完成接口模块在当前代码段的插入。

  下面会大概介绍完成以上工作所用到的 Babel 库。

@babel/parser

  想要在 JavaScript 代码的特定位置中插入代码,我们就需要先解析目前的代码段。

代码语言:txt
AI代码解释
复制
// currentFileContent 为当前文件的字符串
require('@babel/parser').parse(currentFileContent, {

    sourceType: 'module',

    plugins: [

      'typescript',

      ['decorators', { decoratorsBeforeExport: true }],

      'classProperties',

     'classPrivateProperties'

    ]

  })

  通过这种方式,我们就可以把当前的代码转换为 AST 了

  感兴趣的同学可以到 AST Explorer 尝试一下,这个工具可以解析你提供的 JavaScript 代码,并且会以一种非常直观的图形化结构语法书呈现。

  我们可以尝试一下解析这三行简单的 JavaScript 代码,

代码语言:txt
AI代码解释
复制
let foo = 'Hello'
function PrintHello(){
  console.log(foo)
}

  在 AST Explorer 中,上面三行代码被解析为这样的结构。

 AST
AST

  在 AST Explorer 中,我们甚至查看生成的 JSON 格式的解析结果。

  在解析后得到 AST 后,下一步我们就需要开始分析它的结构了。

@babel/traverse

  在查看解析得到 AST 的 JSON 解析结果后,我们可以发现即便是几行简单的代码也会解析得到几百行的 JSON 结果,为了让我们可以快速得到想要的节点,我们可以使用 babel/traverse 这个工具,进行对 AST 快速的节点遍历与筛选。

代码语言:txt
AI代码解释
复制
traverse(fileAST, {
        ImportDeclaration: function(path) {
          if (path.node.source.value === 'api/apis') {
            // 判断是否重复引用 API 逻辑
            let currentApi = path.node.specifiers.map(item => {
              return item.local.name
            })
            if (currentApi.indexOf(FirstLetterToUpperCase(name)) === -1) {
              path.pushContainer('specifiers', APINode)
              // TODO 加入判断 inject 是否重复逻辑
              let injectNode = InjectNodeConstructor(name)
              traverse(fileAST, {
                ClassBody: function(path) {
                  path.node.body.splice(
                    path.node.body.length - 1,
                    0,
                    injectNode
                  )
                }
              })
            } else {
              vscode.window.showInformationMessage('该 API 已经引入')
              path.stop()
              return
            }
          }
        }
      })

  如上文所描述,通过 babel/traverse 这个工具,我们可以首先遍历一次 AST 去判断之前是否已经引用过相应的接口模块。

  babel/traverse 非常强大,它支持绝大部分类型节点的筛选,具体的文档,可查看 Babel-Handbook .

@babel/types

  这个插件的核心功能,就是将开发者选择的接口模块,变成代码插入到当前代码段中,那么在构造新的代码段这个过程中,babel/types 就派上用场了。

  通过 babel/types 的 API ,我们可以很方便的构造出对应的 AST 语法块,而后加入到 AST 中。

代码语言:txt
AI代码解释
复制
// API 节点构造器
function APINodeConstructor(apiName: string): t.ImportSpecifier {
  return t.importSpecifier(
    t.identifier(FirstLetterToUpperCase(apiName + 'API')),
    t.identifier(FirstLetterToUpperCase(apiName + 'API'))
  )
}

// Service 节点构造器
function ServiceNodeConstructor(
  serviceName: string,
  servicePath: string
): t.ImportDeclaration {
  return t.importDeclaration(
    [
      t.importSpecifier(
        t.identifier(FirstLetterToUpperCase(serviceName)),
        t.identifier(FirstLetterToUpperCase(serviceName))
      )
    ],
    t.stringLiteral(servicePath)
  )
}

  如上,通过 babel/types 的 t. 封装好了相应的节点构造器,只需传入对应的 Name 参数即可返回相应的 AST 节点。

  同样,babel/types 也支持多种节点类型的构造,具体文档可参考Babel - types .

@babel/generator

  最后,我们已经完成对 AST 的查找,更改,插入操作了,下一步就是把 AST 转换成 JavaScript 代码了,这时候我们就会用到 babel/generator .

  同样,babel/generator 的用法非常简单:

代码语言:txt
AI代码解释
复制
import {parse} from '@babel/parser';
import generate from '@babel/generator';

...

const generateCode = generate(outCode, {
      retainLines: true,
      sourceMaps: false,
      decoratorsBeforeExport: true
  })

  generate 第一个参数为需要编译成代码的 AST ,第二个参数为 options,具体可参考 Babel - generator.

  至此,整个插件的核心流程就完成了。

总结

  上次了解到 AST 还是在分析 Vue.js 是如何编译 Template 的,但没有深入去细究(虽然这次也不算太深入),这次的实践过程大概了解了 Babel 对于代码处理的过程以及所使用到的一些库。

  篇幅有限,只是简单描述了一些 Babel 工具库的大概用法,也只是简单描述了插件实现的大概思路。

  下一步计划是,等到把这个插件真正完善后,再详细写一篇关于该插件具体思路及改进的问题。

  初次尝试,如有错误内容,敬请原谅,烦请多多指教!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
1 条评论
热度
最新
牛逼
牛逼
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
手把手带你走进Babel的编译世界
谈及 Babel,必然离不开 AST。有关 AST 这个知识点其实是很重要的,但由于涉及到代码编译阶段,大多情况都是由各个框架内置相关处理,所以作为开发(使用)者本身,往往会忽视这个过程。希望通过这篇文章,带各位同学走进 AST,借助 AST 发挥更多的想象力。
PHP开发工程师
2022/03/24
5720
手把手带你走进Babel的编译世界
一文助你搞懂 AST
抽象语法树(Abstract Syntax Tree)简称 AST,是源代码的抽象语法结构的树状表现形式。webpack、eslint 等很多工具库的核心都是通过抽象语法书这个概念来实现对代码的检查、分析等操作。今天我为大家分享一下 JavaScript 这类解释型语言的抽象语法树的概念
coder_koala
2020/06/22
2.6K1
掌握 AST,轻松落地关键业务「技术创作101训练营」
如果你查看目前任何主流的项目中的 devDependencies,我们不会在生产环境用到,但是它们在开发过程中充当着重要的角色。归纳一下有:javascript转译、代码压缩、css预处理器、elint、pretiier,postcss等。所有的上述工具,不管怎样,都建立在了AST这个巨人的肩膀上,都是 AST 的运用:
奋飛
2020/09/20
1.1K0
掌握 AST,轻松落地关键业务「技术创作101训练营」
深入浅出 Babel 上篇:架构和原理 + 实战
这个文章系列将带大家深入浅出 Babel, 这个系列将分为上下两篇:上篇主要介绍 Babel 的架构和原理,顺便实践一下插件开发的;下篇会介绍 babel-plugin-macros , 利用它来写属于 Javascript 的宏,
Nealyang
2019/10/14
8900
深入浅出 Babel 上篇:架构和原理 + 实战
Babel的另类实践 - 重构古董代码
在最近的工作中,接手了一个古老的项目,其中的 JS 代码是一整坨的面条代码,约 3000 行的代码全写在一个文件里,维护起来着实让人头疼。 想不通为啥之前维护项目的同学能够忍受这么难以维护的代码……既然现在这个锅被我拿下了,怎么着也不能容忍如此丑陋的代码继续存在着,必须把它优化一下。 横竖看了半天,由于逻辑都揉在了一个文件里,看都看得眼花缭乱,当务之急便是把它进行模块化拆分,把这一大坨面条状代码拆分成一个个模块并抽离成文件,这样才方便后续的持续优化。 一、结构分析 说干就干,既然要拆分成模块,首先就要
腾讯VTeam技术团队
2020/10/14
9080
编译原理工程实践—05使用babel操作AST实现代码转换
babel 是一个 JavaScript 编译器,使用 babel 可以随心所欲地转化和操作 AST,实现对代码的分析、优化、变更等。可以在 https://esprima.org/demo/parse.html 体验转换查看 js 代码的词法、语法和AST。
CS逍遥剑仙
2025/05/12
220
前端工程师需要了解的 Babel 知识
在前端圈子里,对于 Babel,大家肯定都比较熟悉了。如果哪天少了它,对于前端工程师来说肯定是个噩梦。Babel 的工作原理是怎样的可能了解的人就不太多了。
桃翁
2019/09/24
4650
前端工程师需要了解的 Babel 知识
AST 实战
最近突然对 AST 产生了兴趣,深入了解后发现它的使用场景还真的不少,很多我们日常开发使用的工具都跟它息息相关,如 Babel、ESLint 和 Prettier 等。本文除了介绍 AST 的一些基本概念外,更偏重实战,讲解如何利用它来对代码进行修改。
Dickensl
2022/06/14
7970
AST 实战
【Web技术】780- AST 实现函数错误自动上报
之前有身边有人问我在错误监控中,如何能实现自动为函数自动添加错误捕获。今天我们来聊一聊技术如何实现。先讲原理:在代码编译时,利用 babel 的 loader,劫持所有函数表达。然后利用 AST(抽象语法树) 修改函数节点,在函数外层包裹 try/catch。然后在 catch 中使用 sdk 将错误信息在运行时捕获上报。如果你对编译打包感兴趣,那么本文就是为你准备的。
pingan8787
2020/11/19
9780
【Web技术】780- AST 实现函数错误自动上报
JS代码之混淆
抽象语法树(Abstract Syntax Tree),简称 AST,初识 AST 是在一门网页逆向的课程,该课程讲述了 js 代码中混淆与还原的对抗,而所使用的技术便是 AST,通过 AST 能很轻松的将 js 源代码混淆成难以辨别的代码。同样的,也可以通过 AST 将其混淆的代码 还原成执行逻辑相对正常的代码。
愧怍
2022/12/27
22.4K0
JS代码之混淆
不一样的JavaScript
导读:本文以JavaScript计算机编程语言为载体,从执行过程去解析它的运行原理,从编译的角度去解析它的结构,最后以AST和产生式作为切入点进行案例分析,目的是为了让读者从更底层去了解计算机编程语言。
@超人
2021/07/05
4910
不一样的JavaScript
借助AST ,手写一个解决运行环境差异的loader
最近遇到了一个很特殊的需求,业务代码打包后需要运行在两个不同的环境中,而两个环境中的属性有非常多的差异,我想在打包阶段来处理这些差异,所以就需要自定义一个loader来处理设计到的相关文件
Jou
2023/03/23
4900
借助AST ,手写一个解决运行环境差异的loader
Webpack入门到精通(AST、Babel、依赖)
从名字上就能看出他们使用的环境了,需要注意的是env,他的作用是将最新js转换为es6代码。预设是babel插件的组合,我们可以看下package.json(截取一部分):
落落落洛克
2021/09/17
6010
Webpack入门到精通(AST、Babel、依赖)
前端AST详解,手写babel插件
抽象语法树(Abstract Syntax Tree,AST),是源代码(不仅限于JavaScript,同时还应用于其他语言,例如: Python,Rust等)语法结构的⼀种抽象表示。它以树状的形式表现编程语⾔的语法结构,树上的每个节点都表示源代码中的⼀种结构。 AST 运⽤⼴泛,⽐如:
can4hou6joeng4
2023/11/29
3370
逆向进阶,利用 AST 技术还原 JavaScript 混淆代码
AST(Abstract Syntax Tree),中文抽象语法树,简称语法树(Syntax Tree),是源代码的抽象语法结构的树状表现形式,树上的每个节点都表示源代码中的一种结构。语法树不是某一种编程语言独有的,JavaScript、Python、Java、Golang 等几乎所有编程语言都有语法树。
K哥爬虫
2022/04/28
6.1K3
逆向进阶,利用 AST 技术还原 JavaScript 混淆代码
手写一个webpack,看看AST怎么用|牛气冲天新年征文
@charset "UTF-8";.markdown-body{word-break:break-word;line-height:1.75;font-weight:400;font-size:14px;overflow-x:hidden;color:#353535}.markdown-body h1{padding-bottom:4px;font-size:30px}.markdown-body h1,.markdown-body h2{margin-top:36px;margin-bottom:10px;line-height:1.5;color:#005bb7}.markdown-body h2{position:relative;padding-left:16px;padding-right:10px;padding-bottom:10px;font-size:24px;border-bottom:1px solid #ececec}.markdown-body h2:before{content:"「";position:absolute;top:-6px;left:-10px}.markdown-body h2:after{content:"」";position:absolute;top:6px;right:auto}.markdown-body h3{position:relative;padding-bottom:0;margin-top:30px;margin-bottom:10px;font-size:20px;line-height:1.5;color:#005bb7;padding-left:6px}.markdown-body h3:before{content:"»";padding-right:6px;color:#2196f3}.markdown-body h4{margin-top:24px;font-size:16px}.markdown-body h4,.markdown-body h5{padding-bottom:0;margin-bottom:10px;line-height:1.5;color:#005bb7;padding-left:6px}.markdown-body h5{margin-top:18px;font-size:14px}.markdown-body h6{padding-bottom:0;margin-top:12px;margin-bottom:10px;font-size:12px;line-height:1.5;color:#005bb7;padding-left:6px}.markdown-body p{line-height:inherit;margin-top:16px;margin-bottom:16px}.markdown-body img{max-width:100%}.markdown-body hr{position:relative;width:98%;height:1px;margin-top:32px;margin-bottom:32px;background-image:linear-gradient(90deg,#007fff,rgba(255,0,0,.3),hsla(0,0%,100%,.1),rgba(255,0,0,.3),#007fff);border-width:0;overflow:visible}.markdown-body hr:after{content:"";position:absolute;margin:auto;left:0;right:0;bottom:0;top:0;display:inline-block;width:60px;height:20px;background:#fff;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAgCAYAAABgrToAAAADoklEQVRYR82XTYgcRRTHf2933Q1RjAa9eFO8JHoJ8RQVBQ2iBwXBET0YEUTXNVmNQtTpmeqaWV0XNRq/o4KoECSCEPSg4CF+BYUkIIiCoCJCPIhC/Ihh2Z0nVV27VnZnenumW9i6ddV7//frV69fVQurfMgq56NawFTPAU6QyomqXrw6wIZeyhCPebA5buNR+akKyGoAjd6BshthnYdSjqNcRVuOlIUsD2j0SuA94IwuMHdh5ZUykOUBXfSGbmKI54EtAeYIHSZoy5dl4JxvNYBOKdW1KE8BQ8AkVk6WhasWsAiN0TX9gveXQaPP+Ayt
蒋鹏飞
2021/02/26
7800
手写一个webpack,看看AST怎么用|牛气冲天新年征文
代码自动化重构利器——jscodeshift 初探
背景 开发维护规模较大的前端项目,难免时不时需要进行一些代码重构工作。举一个简单的 ?: 有一个 npm 包 an-npm-package-containing-constants,用于维护项目埋点时
zz_jesse
2021/04/21
1.5K0
代码自动化重构利器——jscodeshift 初探
写一个同事见了会打你的 Prettier 插件
前端的编译工具都是从源码到源码的转换,所以都是 parse、transform、generate 这三步:
神说要有光zxg
2023/02/01
1.1K0
【Babel 小玩具】如何用 Babel 为代码自动引入依赖
最近在尝试玩一玩已经被大家玩腻的 Babel,今天给大家分享「如何用 Babel 为代码自动引入依赖」,通过一个简单的例子入门 Babel 插件开发。
lucifer210
2020/06/10
9420
【Babel 小玩具】如何用 Babel 为代码自动引入依赖
Babel原理
Babel是什么?我们为什么要了解它? 1. 什么是babel ? Babel 是一个 JavaScript 编译器。他把最新版的javascript编译成当下可以执行的版本,简言之,利用babel就
null仔
2020/02/28
1.2K0
Babel原理
相关推荐
手把手带你走进Babel的编译世界
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档