babel插件开发

本文介绍了如何使用Babel开发插件来扩展其功能,包括代码解析、转换和生成。通过示例展示了如何检查导入模式并给出提示,以及如何处理类方法的this绑定,确保代码在运行时的正确性。文章详细解释了Babel的工作流程,并提供了编写插件所需的关键库和具体步骤。
部署运行你感兴趣的模型镜像

babel插件开发

简介

babel本身是一个将JavaScriptg高版本的新语法编译成低版本语法以提升兼容性的编译器,babel除了自身的兼容编译以外也提供了接口允许用户编写插件扩展功能,在这个基础上我们可以利用babel提供的api实现代码检查、代码生成、自定义语法等功能。

babel是如何工作的

561794-20171120013201211-2132813361
babel对源代码的处理主要有解析、转换、生成 三个阶段,这里简单聊一下转换功能的使用

编写简单的babel插件

@babel/core中已包含编写插件所需要的基础工具,也可以单独安装一下几个包单独处理

// 处理源代码的解析生成ast,内置了多种解析插件
yarn add -D @babel/parser
// 遍历ast进行自定义处理
yarn add -D @babel/traverse
// 生成代码
yarn add -D @babel/generator
// ast节点类型,节点创建
yarn add -D @babel/types
// 生成指向源代码的错误信息
yarn add -D @babel/code-frame

导入依赖包

import { parse } from '@babel/parser'
import traverse, { NodePath } from '@babel/traverse'
import {isImportDefaultSpecifier, classMethod, blockStatement, identifier, isClassMethod, isClassProperty, Identifier, expressionStatement, ClassMethod} from '@babel/types'
import generate from '@babel/generator'
import codeFrame from '@babel/code-frame'

准备一段代码片段


let codeText = `
import fs from 'fs'

@testDecorators()
export class Index {
  a: number
  private b: number = 1
  constructor (a: number) {
    this.a = a
  }
  private sum (): number {
    return this.a + this.b
  }

  getB (): number {
    return this.b
  }

  getSum (): number {
    return this.sum()
  }
}
`

解析源代码生成ast,这里sourceType设置为了module以支持import,export等语法解析,并添加了内置的typescript、decorators等插件以支持相应的解析

const astTree = parse(codeText, {
  sourceType: 'module',
  plugins: [
    'typescript',
    ['decorators', { decoratorsBeforeExport: true }],
    'classProperties',
    'classPrivateProperties'
  ]
})

示例:分析代码给出提示

遍历ast检查包导入时是否使用了默认导入模式,如果使用了该模式则打印一个带语法高亮的源代码信息标注异常位置

traverse(astTree, {
  ImportDeclaration: (path) => {
    path.node.specifiers.forEach((specifier) => {
      if (isImportDefaultSpecifier(specifier)) {
        console.log('禁止使用默认导入')
        const errorMsg = codeFrame(testText, path.node.loc.start.line, specifier.local.loc.end.column, {
          highlightCode: true
        })
        console.log(errorMsg)
      }
    })
    console.log(path.node.source.value)
  }
)

如上面准备的代码片段则会输出以下提示

禁止使用默认导入
> 1 | import fs from 'fs'
    |         ^
  2 | 
  3 | @testDecorators()
  4 | export class Index {

示例:处理成员方法绑定

javascript的class有别于java等语言,this的指向可以在运行时改变,如果不希望调用方改变this的指向,这需要在构造函数为成员方法绑定this,我们可以使用使用traverse遍历ast,找到构造方法并在内部添加代码处理,或者可以生成构造方法并添加对应代码处理

traverse(ast, {
  ClassBody (path) {
    let isExistConstructor = false
    let propertys = []
    let methods = []
    // 判断是否存在构造方法,并收集成员方法列表
    path.node.body.forEach((node) => {
      if (isClassMethod(node)) {
        if (node.kind === 'constructor') {
          isExistConstructor = true
        } else {
          methods.push((node.key as Identifier).name)
        }
      } else if (isClassProperty(node)) {
        propertys.push((node.key as Identifier).name)
      }
    })
    
    // 在构造方法中添加绑定代码
    path.get('body').forEach((subNode) => {
      if (isClassMethod(subNode)) {
        if ((subNode.node as ClassMethod).kind === 'constructor') {
          isExistConstructor = true
          const currentNode = (subNode as NodePath<ClassMethod>)
          let body = currentNode.get('body')
          body.pushContainer('body', methods.map((name) => expressionStatement(identifier(`this.${name}.bind(this)`))))
          currentNode.set('body', body.node)
        }
      }
    })
    // 添加自定义的成员方法,并添加绑定代码
    path.pushContainer('body', classMethod('method', identifier('getTest'), [], blockStatement(methods.map((name) => expressionStatement(identifier(`this.${name}.bind(this)`))))))
  },
})

编译完成后会得到以下结果

import fs from 'fs'

@testDecorators()
export class Index {
  a: number
  private b: number = 1
  constructor (a: number) {
    this.a = a
    this.sum.bind(this)
    this.getB.bind(this)
    this.getSum.bind(this)
  }
  private sum (): number {
    return this.a + this.b
  }

  getB (): number {
    return this.b
  }

  getSum (): number {
    return this.sum()
  }
  
  getTest () {
    this.sum.bind(this);
    this.getB.bind(this);
    this.getSum.bind(this);
  }
}

您可能感兴趣的与本文相关的镜像

Seed-Coder-8B-Base

Seed-Coder-8B-Base

文本生成
Seed-Coder

Seed-Coder是一个功能强大、透明、参数高效的 8B 级开源代码模型系列,包括基础变体、指导变体和推理变体,由字节团队开源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值