Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ast 入门(从写babel插件来深入了解ast) #26

Open
AnnVoV opened this issue Feb 10, 2019 · 0 comments
Open

ast 入门(从写babel插件来深入了解ast) #26

AnnVoV opened this issue Feb 10, 2019 · 0 comments
Labels

Comments

@AnnVoV
Copy link
Owner

AnnVoV commented Feb 10, 2019

最终效果

先看效果,写了一个小例子,请点击查看
https://astexplorer.net/#/gist/49a1bb9cc81e392220ffd998ff47162c/4728dec0c4645e40c58426886bc2069c959544e4

前言

查找ast相关资料,看到别人引用资源里有一个ppt,《how writing a babel plugin is like... jQuery》看了一遍发现,真香!下面我就按照我的理解总结一下ppt里面的内容。通过了解如何开发一个babel插件,也能让我们更好地理解ast. 后面可以不看,重点推荐下面这两个资料:

ast 基础

在开始往下面看之前,为了对ast节点类型有更全面的认识,推荐阅读下这个同学写的文章:Pines-Cheng/blog#53 Babylon 与 JavaScript 抽象语法树

What is Babel?

Babel is a javascript compiler。

Babel 是一个通用的,多用途的Javascript 编译器

整个ppt最犀利的观点就是:

Babel::javascript jQuery::DOM
Babel turns the code you have into the code you need

dom 之于 jQuery 同 javascript 之于 Babel

Compile Overview

pic1
babel 中的几个核心模块:

  • babylon: 解析器 将code -> ast
  • babel-traverse: 转换模块 允许我们遍历,浏览,修改ast
  • babel-generator: 生成模块 将ast -> 最终需要的code

Write a Babel Plugin

A Plugin is just a function

export default babel => {
  return {
    visitor:{...}
  }
}

AST Visitor Pattern

export default function(babel) {
  const t = babel.types;
  return {
    visitor: {
      BinaryExpression(path) {...},
      Identifier(path) {...},
      FunctionDeclaration(path) {...},
      ClassDeclaration(path) {...},
    }
  }
}

当Babel遍历ast时,会查看每一个节点,当发现visitor对象上有对应的方法时,会调用这个方法,并且把对应的上下文传递进去。

举个例子

var a = 2**3 // 源代码转换为
var a = Math.pow(2, 3); // 目标代码

上面通过babel 插件如何实现呢?

Step1 查看源代码ast 结构

我们利用https://astexplorer.net 这个网址可以查看到源代码对应的ast结构,让我们知道我们需要的各个节点类型。

pic2

Step2 获取源码对应结构

export default function(babel) {
  // 插件大体结构是一样的
  const t = babel.types;
  return {
    visitor: {
      // 2**3 是一个二元表达式
      BinaryExpression(path) {
        // console.log(path); 可以把对应路径打印出来看看
        if (path.node.operator !== '**') {
          return; // 如果节点对应的操作不是** 不做任何处理
        }
        const {left, right} = path.node; // 获取表达式左右两项
      }
    }
  }
}

Step3 对源码结构进行替换

最终我们希望把2**3 替换为一个函数调用Math.pow(2,3), 所以要看下Math.pow(2,3) 的ast 结构,同样利用上面那个网址
pic3

// babel-type 手册https://babeljs.io/docs/en/next/babel-types.html

t.callExpression(
  t.memberExpression( // 创建函数
    t.identifier('Math'),
    t.identifier('pow'),
  ),
  [left, right] // 入参
);

这样就生成了一个Math.pow(2, 3)的函数调用节点

step4 节点替换

export default function(label) {
  const t = babel.types;
  return {
    visitor: {
      BinaryExpression(path) {
        if (path.node.operator !== '**') {
          return;
        }
        path.replaceWith(t.callExpression(
          t.memberExpression(
            t.identifier('Math'),
            t.identifier('pow')
          ),
          [left, right]
        ));
      }
    }
  }
}

拓展

其实写babel插件的重点在于了解ast, 有了ast以后我们可以做很多事情,除了写bable插件,我们也可以去写eslint插件,本质是一样的,只是语法有些略微的区别。

module.exports = {
  meta: {
    docs: {
      description: "Disallow if statement without block",
      category: "Best Practices",
      recommended: true
    }
  },
  create(context) {
    return {
      IfStatement(node) {
        if(isBlock(node.consequent) && isBlock(node.alternate)) {
          return;
        }
        context.report({
          node,
          message: 'yo, y u non block?'
          fix: function(fixer) {
            // fix the code
          }
        })
        function isBlock(n) {
          return !n || n.type === 'BlockStatement';
        }
      }
    };
    
    
   //  更复杂的babel插件参考
   //  https://juejin.im/post/5a17d51851882531b15b2dfc
  }
};

参考资料:
1.how writing a babel plugin is like...(重要 非常棒)
https://hzoo.github.io/babel-plugin-slides/assets/player/KeynoteDHTMLPlayer.html#0
2.Writing custom Babel and ESLint plugins
http://slides.com/kentcdodds/a-beginners-guide-to-asts#/6
3.@babel/types babel的参考手册
4.通过开发 Babel 插件理解抽象语法树(AST)
https://www.zcfy.cc/article/understanding-asts-by-building-your-own-babel-plugin
5.如何写一个eslint 插件(拓展)
https://www.zcfy.cc/article/creating-an-eslint-plugin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant