疑问

  • path API的官方文档?
    • 不晓得有没有官方文档,但是后文“Traverse 的 path、scope、visitor”中有一个已经很详尽的版本了,基本够用。
  • 通过@babel/types创建了AST元素后,如何插入AST语法树对应位置?
    • 利用@babel/parser的path API
  • 如何删除/修改指定AST元素?
    • 利用@babel/parser的path API
  • @babel/code-frame工作方式不明?我的理解babel到头来讲只是字符串转换,为什么可以有报错警告?不需要和编辑器结合才能警告么?
  • 有了@babel/core还需要单独使用其他包么?
  • 在babel-plugin-import中,并没有显示地使用任何以下出现的babel包,却处处可见这些babel包api的影子?bebel-plugin-import的实现原理是什么?和这些babel包的关系是什么?
    • babel-plugin-import的结构,和@babel/traverse的转换函数的visitor的结构非常接近,这是因为babel-plugin-import是一个标准的babel plugin结构,后文会提到:
    • image.png
    • 没有显示引用却随处可见影子的原因是因为,babel插件的第一个参数暴露出了这些包的内部API,与这些babel包的关系是封装与原生。
  • 是否可以自定义plugin和preset?babel-plugin-import的实现原理就是自定义的plugin和preset么?

    摘要

  • 版本

    • babel 6:没有按照 scope 来划分,是 babel-parser
    • babel 7: 变成了@babel/parser

      api大览

      babel 的整体功能通过 @babel/core 提供
  • parse 阶段

    • @babel/parser,功能是把源码转成 AST
  • transform 阶段
    • @babel/traverse:调用 visitor 函数遍历AST
    • @babel/types :修改AST
    • @babel/template:批量创建 AST
  • generate 阶段
    • @babel/generate:AST->code,同时生成 sourcemap
  • 其他

  • parse 返回整个 AST

  • parseExpression 返回表达式的 AST
    1. function parse(input: string, options?: ParserOptions): File
    2. function parseExpression(input: string, options?: ParserOptions): Expression
    options是核心,具体api可查看文档。其实主要分为两类(parse的内容是什么、以什么方式parse)
    最常用的 option 就是 plugins、sourceType 这两个,比如要 parse tsx 模块,那么就可以这样来写:
    1. require("@babel/parser").parse("code", {
    2. sourceType: "module",
    3. plugins: [
    4. "jsx",
    5. "typescript"
    6. ]
    7. });

    @babel/traverse

    1. function traverse(parent, opts)
    parent 指定要遍历的 AST 节点,opts 指定 visitor 函数。 ```javascript // 进入 FunctionDeclaration 节点时调用 traverse(ast, { FunctionDeclaration: {
    1. enter(path, state) {}, // 为对象,则可以明确指定 enter 或者 exit 时的处理函数
    2. exit(path, state) {}
    } })

// 默认是进入节点时调用,和上面等价 traverse(ast, { FunctionDeclaration(path, state) {} // 为函数,那么就相当于是 enter 时调用的函数 })

  1. 两个参数:
  2. <a name="YE4sb"></a>
  3. ### (1)path
  4. path是遍历过程中的路径,会保留上下文信息,有很多属性和方法(核心)
  5. - path.node 指向当前 AST 节点
  6. - path.getpath.set 获取和设置当前节点属性的 path
  7. - path.parent 指向父级 AST 节点
  8. - path.getSiblingpath.getNextSiblingpath.getPrevSibling 获取兄弟节点
  9. - path.find 从当前节点向上查找节点
  10. - path.scope 获取当前节点的作用域信息
  11. - path.isXxx 判断当前节点是不是 xx 类型
  12. - path.assertXxx 判断当前节点是不是 xx 类型,不是则抛出异常
  13. - path.insertBeforepath.insertAfter 插入节点
  14. - path.replaceWithpath.replaceWithMultiplereplaceWithSourceString 替换节点
  15. - path.remove 删除节点
  16. - path.skip 跳过当前节点的子节点的遍历
  17. - path.stop 结束后续遍历
  18. <a name="ivoMx"></a>
  19. ### (2)state
  20. 遍历过程中在不同节点之间传递数据的机制<br />有点this的味道,babel-plugin-import里大量使用了这种方式进行通信:[https://github.com/umijs/babel-plugin-import/blob/d21264e4fb9c9f60041ceed748177db16f347ad7/src/Plugin.js#L55](https://github.com/umijs/babel-plugin-import/blob/d21264e4fb9c9f60041ceed748177db16f347ad7/src/Plugin.js#L55)
  21. <a name="BQLbY"></a>
  22. ## @babel/types
  23. 遍历 AST 的过程中需要创建一些 AST 和判断 AST 的类型.<br />isXxx 会返回 boolean 表示结果,而 assertXxx 则会在类型不一致时抛异常。<br />所有的 AST buildassert api 可以在 [babel types 文档](https://link.juejin.cn/?target=https%3A%2F%2Fbabeljs.io%2Fdocs%2Fen%2Fbabel-types%23api)中查,如:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/2841570/1646895660619-f31fe8fc-83cc-4a6a-bdda-7da86c0dc874.png#clientId=u72051acb-12fc-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=402&id=u874ab61a&margin=%5Bobject%20Object%5D&name=image.png&originHeight=448&originWidth=1031&originalType=binary&ratio=1&rotation=0&showTitle=false&size=35756&status=done&style=none&taskId=u5727f58f-3283-41eb-ae32-e3c34f5e56a&title=&width=924.1456385135247)<br />通常要先知道所操作的节点是什么AST类型,再选择对应的判断/创建方法。
  24. <a name="q2EtZ"></a>
  25. ## @babel/templete
  26. [https://babel.docschina.org/docs/en/babel-template/](https://babel.docschina.org/docs/en/babel-template/)<br />看名字就晓得是模板,基础用法:<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/2841570/1646896214066-26de8cf4-8a42-4797-bba2-e0f276b67cbf.png#clientId=u72051acb-12fc-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=824&id=ub0d144dd&margin=%5Bobject%20Object%5D&name=image.png&originHeight=847&originWidth=526&originalType=binary&ratio=1&rotation=0&showTitle=false&size=52887&status=done&style=none&taskId=ud2b09ed6-0307-48b6-9458-d82fb512211&title=&width=511.47900390625)<br />_无论是此处的templete占位符还是@babel/traverse的state用法都让我想起Vue_<br />其他用法:
  27. - 根据模版创建整个 AST
  28. - template.ast
  29. - template.program
  30. - 返回的 AST 的根节点是 Program
  31. - 知道具体创建的 AST 的类型
  32. - template.expressiontemplate.statementtemplate.statements 等方法创建具体的 AST
  33. - 更多详见文档,估计最常用的还是基础用法。
  34. <a name="EPWJX"></a>
  35. ## @babel/generator
  36. [https://babel.docschina.org/docs/en/babel-generator/](https://babel.docschina.org/docs/en/babel-generator/)
  37. ```javascript
  38. import { parse } from "@babel/parser";
  39. import generate from "@babel/generator";
  40. const code = "class Example {}";
  41. const ast = parse(code);
  42. const output = generate(
  43. ast,
  44. {
  45. /* options */
  46. },
  47. code
  48. );
  • 第一个参数是要打印的 AST
  • 第二个参数是 options,指定细节
    • options 中常用的是 sourceMaps,开启了这个选项才会生成 sourcemap
  • 第三个参数当多个文件合并打印的时候需要用到

    @babel/code-frame

    有错误信息要打印的时候,需要打印错误位置的代码时使用。
    ?工作原理、表现形式不明

    @babel/core

    整合以上全部
    options 主要配置 plugins 和 presets,指定具体要做什么转换:

    1. transformSync(code, options); // => { code, map, ast }
    2. transformFileSync(filename, options); // => { code, map, ast }
    3. transformFromAstSync(
    4. parsedAst,
    5. sourceCode,
    6. options
    7. ); // => { code, map, ast }
  • tips

    • transformXxx 的 api,已经被标记为过时了,后续会删掉,不建议用,直接用 transformXxxSync 和 transformXxxAsync
    • 还有一个 createConfigItem 的 api,用于 plugin 和 preset 的封装(后面的章节涉及,感觉这才是重点)