zoukankan      html  css  js  c++  java
  • babel插件开发

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

    babel是如何工作的

    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
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    导入依赖包

    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'
    1
    2
    3
    4
    5
    准备一段代码片段


    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()
    }
    }
    `
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    解析源代码生成ast,这里sourceType设置为了module以支持import,export等语法解析,并添加了内置的typescript、decorators等插件以支持相应的解析

    const astTree = parse(codeText, {
    sourceType: 'module',
    plugins: [
    'typescript',
    ['decorators', { decoratorsBeforeExport: true }],
    'classProperties',
    'classPrivateProperties'
    ]
    })
    1
    2
    3
    4
    5
    6
    7
    8
    9
    示例:分析代码给出提示
    遍历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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    如上面准备的代码片段则会输出以下提示

    禁止使用默认导入
    > 1 | import fs from 'fs'
    | ^
    2 |
    3 | @testDecorators()
    4 | export class Index {
    1
    2
    3
    4
    5
    6
    示例:处理成员方法绑定
    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)`))))))
    },
    })
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    编译完成后会得到以下结果

    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);
    }
    }
    ————————————————

    博主教你手撸JVM 开源项目
    https://github.com/anons-org/nada
    https://gitee.com/grateful/nada

    博主长期对外收徒,欢迎咨询。
    《编程语言设计和实现》《MUD游戏开发》《软件破解和加密》《游戏辅助外挂》《JAVA开发》 以上课程非诚勿扰!



    =================================
    QQ:184377367
    GOLang Q群:6848027
    电子电路入门群 436173132
    C/C++/QT群 1414577
    单片机嵌入式群 306312845
    MUD/LIB/巫师交流群 391486684
    java/springboot/hadoop/ 群 4915800
    WEB前端开发交流群 214737701
    操作系统研发群:15375777
    Linux公社Q群:812742841
    汇编/辅助/破解新手群:755783453
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    es6 --- var const let
    HTTP -- 请求/响应 结构
    点击下载文件
    计算机当前时间
    js -- img 随着鼠标滚轮的变化变化
    vue --- 全局守卫
    vue -- 路由懒加载
    vuex -- 状态管理
    js对数组进行操作
    高性能网站建设
  • 原文地址:https://www.cnblogs.com/cfas/p/15059905.html
Copyright © 2011-2022 走看看