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

babel相关资料 #95

Open
Genluo opened this issue Aug 9, 2020 · 5 comments
Open

babel相关资料 #95

Genluo opened this issue Aug 9, 2020 · 5 comments

Comments

@Genluo
Copy link
Owner Author

Genluo commented Aug 28, 2020

语法插件🤔

用babbel创造自定义JS语法
@babel/parser 已经支持了很多 JavaScript 语法特性,Parser也不支持扩展. 因此plugin-syntax-*实际上只是用于开启或者配置Parser的某个功能特性。

@Genluo
Copy link
Owner Author

Genluo commented Aug 30, 2020

辅助插件开发

  1. @babel/template: 某些场景直接操作AST太麻烦,就比如我们直接操作DOM一样,所以Babel实现了这么一个简单的模板引擎,可以将字符串代码转换为AST。比如在生成一些辅助代码(helper)时会用到这个库

  2. @babel/types: AST 节点构造器和断言. 插件开发时使用很频繁

  3. @babel/helper-*: 一些辅助器,用于辅助插件开发,例如简化AST操作

  4. @babel/helper: 辅助代码,单纯的语法转换可能无法让代码运行起来,比如低版本浏览器无法识别class关键字,这时候需要添加辅助代码,对class进行模拟。

@Genluo
Copy link
Owner Author

Genluo commented Aug 30, 2020

NodePath和Node的关联✅

副作用处理

修改了NodePath中对应的Node, 修改完成之后需要手动映射到NodePath变量中,
path.replaceWith(path.node);

我们可以对 AST 进行任意的操作,比如删除父节点的兄弟节点、删除第一个子节点、新增兄弟节点... 当这些操作'污染'了 AST 树后,访问者需要记录这些状态,响应式(Reactive)更新 Path 对象的关联关系, 保证正确的遍历顺序,从而获得正确的转译结果。

作用域处理

如果修改了scope,也需要使用相关的path.scope来进行更新作用域信息

我们可以对 AST 进行任意的操作,比如删除父节点的兄弟节点、删除第一个子节点、新增兄弟节点... 当这些操作'污染'了 AST 树后,访问者需要记录这些状态,响应式(Reactive)更新 Path 对象的关联关系, 保证正确的遍历顺序,从而获得正确的转译结果。

@Genluo
Copy link
Owner Author

Genluo commented Aug 31, 2020

babel-macros

  1. issue
  2. awesome-babel-macros
  3. 开发文档

提升语言的表现能力

示例

import evalm from 'eval.macro'
const x = evalm`
function fib(n) {
  const SQRT_FIVE = Math.sqrt(5);
  return Math.round(1/SQRT_FIVE * (Math.pow(0.5 + SQRT_FIVE/2, n) - Math.pow(0.5 - SQRT_FIVE/2, n)));
}

fib(20)
`

//      ↓ ↓ ↓ ↓ ↓ ↓

const x = 6765

使用Macro相比Plugin带来的好处

  1. 很显然,Macro不需要配置.babelrc(当然babel-plugin-macros这个基座需要装好). 这个对于CRA这种不推荐配置构建脚本的工具来说很有帮助
  2. 由隐式转换为了显式。上一节就说了“显式好于隐式”。你必须在源代码中通过导入语句声明你使用了 Macro; 而基于插件的方式,你可能不知道preval这个标识符哪里来的? 如何被应用?何时被应用?而且通常你还需要和其他工具链的配合,例如ESlint、Typescript声明等等。
  3. Macro相比Plugin 更容易被实现。因为它专注于具体的 AST 节点,见下文
  4. 另外,当配置出错时,Macro可以得到更好的错误提示

@Genluo
Copy link
Owner Author

Genluo commented Aug 31, 2020

节点静态求值

export function getJsxValue(path: NodePath<babelTypes.JSXAttribute>) {
 
  // generate
  // 单纯字符串
  if (babelTypes.isStringLiteral(path.node.value)) {
    return path.node.value.value;
  }
  // {} 这种类型
  if (babelTypes.isJSXExpressionContainer(path.node.value)) {
    const JSXExpressionContainerExpressionNode = path.node.value.expression;
    if (
      babelTypes.isStringLiteral(JSXExpressionContainerExpressionNode) ||
      babelTypes.isBooleanLiteral(JSXExpressionContainerExpressionNode) ||
      babelTypes.isNumericLiteral(JSXExpressionContainerExpressionNode)
    ) {
      return JSXExpressionContainerExpressionNode.value;
    }

    if (babelTypes.isObjectExpression(JSXExpressionContainerExpressionNode)) {
      const result = generate(JSXExpressionContainerExpressionNode);

      return eval(`(${result.code})`);
    }
  }
  if (path.node.value === null) {
    return true;
  }
  throw path.buildCodeFrameError("不能赋值为非基本类型");
}

通过内置api可以进行求值

t.evaluate(parse("5 + 5")) // { confident: true, value: 10 }
t.evaluate(parse("!true")) // { confident: true, value: false }
// ❌两个变量相加无法求值,因为变量值在运行时才存在,这里confident为false:  
t.evaluate(parse("foo + foo")) // { confident: false, value: undefined }

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

No branches or pull requests

1 participant