版博士V2.0程序
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

README.md 8.3 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. # GOGOCODE
  2. 全网最简单易上手,可读性最强的 AST 处理工具!
  3. 官网:https://gogocode.io
  4. 简介:[阿里妈妈出的新工具,给批量修改项目代码减轻了痛苦](https://juejin.cn/post/6938601548192677918)
  5. # Install
  6. ```
  7. npm install gogocode
  8. ```
  9. # 快速开始
  10. 对于下面的代码
  11. ```javascript
  12. const code = `
  13. const moment = require('moment');
  14. var a = 1;
  15. const b = 2;
  16. function log (x, y = 'World') {
  17. console.log('a')
  18. console.log(a, x, y);
  19. }
  20. `;
  21. ```
  22. 创建一个 AST 实例
  23. ```javascript
  24. const $ = require('gogocode');
  25. const AST = $(code);
  26. ```
  27. -----
  28. - 小明想将 所有的 `a` 变量名替换为 `c`,只需要
  29. ```javascript
  30. $(code).replace('a', 'c')
  31. ```
  32. -----
  33. - 小明改主意了,只想把 `var a = 1` 里的变量名改为 `c`,需要两步:
  34. - 取变量 a 的定义赋值,
  35. ```javascript
  36. $(code).find('var a = 1');
  37. ```
  38. - 将 `a` 变量名替换为 `c`,并输出整体代码
  39. ```javascript
  40. $(code)
  41. .find('var a = 1')
  42. .attr('declarations.0.id.name', 'c')
  43. .root()
  44. .generate();
  45. ```
  46. <br>
  47. 这是直接操作AST的方式,有没有更简单的方法呢?有!
  48. ```javascript
  49. $(code).replace(`var a = 1`, `var c = 1`)
  50. ```
  51. <br>
  52. replace确实用起来爽,但当你在分析转换代码时遇到replace覆盖不到的场景时,请用GoGoCode提供的其他api来精准操作AST吧!
  53. -----
  54. - 小明又改主意了,想把所有定义语句的 `a` 都改成 `c`,只需要将目标语句改一下写成:
  55. ```javascript
  56. $(code).replace(`var a = $_$`, `var c = $_$`)
  57. ```
  58. > 看到这里,你应该已经理解 `find`和`replace` 的第一参有点类似‘jquery 选择器’,而这里的选择器是你需要查找的代码片段,无论想要匹配多么复杂的代码都可以匹配到,其中 `$_$` 通配符可以匹配任意确定代码,代码选择器及通配符详细介绍 <a href="/zh/docs/specification/selector">看这里</a>
  59. -----
  60. - 小明想试试将代码里的 `var` 改为 `let`,`require` 改为 `import`,他发现用 GoGoCode 真的可以像字符串的 replace 一样简单!
  61. ```javascript
  62. $(code)
  63. .replace('var $_$1 = $_$2', 'let $_$1 = $_$2');
  64. .replace('const $_$1 = require($_$2)', 'import $_$1 from $_$2')
  65. ```
  66. ------
  67. 关于如何书写选择器,以及replace详解,请见[GoGoCode详细文档](https://gogocode.io/zh/docs/specification/replace)
  68. ------
  69. # API
  70. ## 获取节点
  71. 所有的节点获取操作都会返回一个新的AST实例,实例中可能包含多个AST节点路径,如`find()`、`siblings()`等,某些api返回的实例只会存在一个AST节点路径,如`next()`
  72. ### AST.find(selector, options)
  73. | 入参 | | 说明 | 类型 | 默认值 |
  74. | --- | --- | --- | --- | --- |
  75. | `selector` | | 代码选择器,可以是代码也可以将代码中的部分内容挖空替换为通配符 | string | 无 |
  76. | `options` | `ignoreSequence` | 匹配时是否忽略顺序<br>忽略顺序的情况:`{a:$_$}`匹配`{b:1, a:2}`<br>需要严格按照顺序匹配的情况:`function($_$, b){}` 匹配`function(a, b){}` | boolean | false |
  77. | | `parseOptions` | 同构造函数的`parseOptions` | | |
  78. 当selector中存在 `$_$` 通配符时,返回的AST实例中存在 `match` 属性,也就是被 `$_$` 匹配到的AST节点,按照$_$紧接着的key做聚合
  79. 如:`$('const a = { key: 1, value: "gogo" }').find('const $_$1 = $_$2')`
  80. <br>
  81. 下图是选择器通过find匹配到的整句代码对应的AST节点:
  82. <img src="http://alp.alicdn.com/1615836728401-2250-646.png" />
  83. <br>
  84. 下图是是 `$_$1` 和 `$_$2` 分别匹配到的节点以及对应的输出
  85. <img src="http://alp.alicdn.com/1615836725013-1244-520.png"/>
  86. ### .parent(level)
  87. 获取某个父节点
  88. | 入参 | 说明 | 类型 | 默认值 |
  89. | --- | --- | --- | --- |
  90. | `level` | 自内向外第n层父元素 | number | 0 |
  91. ### .parents()
  92. 获取所有父节
  93. ### .root()
  94. 获取根节点,对于js来说是`type`为'File'的节点,对于html来说是`nodeType`为'document'的节点
  95. 通常对AST进行操作之后需要获取root元素之后再输出
  96. ### .siblings()
  97. 获取所有兄弟节点
  98. ### .prev()
  99. 获取前一个节点
  100. ### .prevAll()
  101. 获取当前节点之前的同级节点
  102. ### .next()
  103. 获取后一个节点
  104. ### .nextAll()
  105. 获取当前节点之后的同级节点
  106. ### .each(callback)
  107. 以每一个匹配的元素作为上下文来执行一个函数。
  108. | 入参 | 说明 | 类型 | 默认值 |
  109. | --- | --- | --- | --- |
  110. | `callback` | 对于每个匹配的元素所要执行的函数<br>执行函数时,会给函数传递当前节点`node`和`index` | function | 无 |
  111. ### .eq(index)
  112. 获取当前链式操作中第N个AST对象
  113. | 入参 | 说明 | 类型 | 默认值 |
  114. | --- | --- | --- | --- |
  115. | `index` | 需要获取的AST对象的位置 | number | 0 |
  116. ## 操作节点
  117. ### .attr()
  118. 获取或修改AST节点的属性,入参可分三种情况:
  119. 1. 返回属性名称对应的节点或属性值
  120. | 入参 | 说明 | 类型 | 默认值 | 举例 |
  121. | --- | --- | --- | --- | --- |
  122. | `attrName` | ast节点的属性名称,支持多层属性,通过.连接 | string | 无 | declarations<br>declarations.0.id.name |
  123. 2. 修改属性名称对应的节点或属性值
  124. | 入参 | 说明 | 类型 | 举例 |
  125. | --- | --- | --- | --- |
  126. | `attrName` | ast节点的属性名称,支持多层属性,通过.连接 | string | declarations <br>declarations.0.id.name |
  127. | `attrValue` | 将第一个入参获取到的节点或属性修改为该入参 <br>注意:字符串不会被解析为ast节点而是直接替换原有属性 | node string | |
  128. 3. 修改多个属性名称对应的节点或属性值
  129. | 入参 | | 类型 | 默认值 | 举例 |
  130. | --- | --- | --- | --- | --- |
  131. | `attrMap` | `attrName` | string | 无 | declarations <br>declarations.0.id.name |
  132. | | `attrValue` | node | string | 无 | |
  133. ```typescript
  134. AST.attr('init', initNode)
  135. AST.attr({
  136. init: initNode,
  137. 'program.body.0.params.0.name': 'a'
  138. })
  139. AST.attr('program.body.0.params.0.name')
  140. ```
  141. ### .has(selector, options)
  142. 判断是否有某个子节点,返回值为boolean类型
  143. 入参同`.find()`
  144. ### .clone()
  145. 返回由当前节点深度复制的新节点
  146. ### .replace(selector, replacer)
  147. 在当前节点内部用`replacer`替换`selector`匹配到的代码,返回当前节点
  148. | 入参 | | 解释 | 类型 | 例 |
  149. | --- | --- | --- | --- | --- |
  150. | `selector` | | 代码选择器,可以是代码也可以将代码中的部分内容挖空替换为通配符 | `string` | `var $_$1 = $_$2` |
  151. | `replacer` | | 替换代码,同代码选择器通配符顺序与selector的保持一致<br>也可以是确定的ast节点 | `string` | `node` | `let $_$1 = $_$2` |
  152. | `options` | `ignoreSequence` | 匹配时是否忽略顺序 | object | 无 |
  153. | | `parseOptions` | 解析入参 | object | 无 |
  154. ###
  155. ```typescript
  156. AST.replace(`Component`, `module.exports = Magix.View.extend`);
  157. AST.replace(
  158. `export default function calculateData($_$1){$_$2}`,
  159. `function calculateData($_$1){$_$2}`
  160. )
  161. AST.replace(
  162. `navigateToOutside({url: $_$})`,
  163. `jm.attachUrlParams($_$)`,
  164. options: { ignoreSequence: true }
  165. )
  166. ```
  167. ### .replaceBy(replacerAST)
  168. 用replacerAST替换当前节点,返回新节点
  169. | 入参 | 类型 |
  170. | --- | --- |
  171. | `replacerAST` | AST <br>node <br> string |
  172. ### .after(ast)
  173. 在当前节点后面插入一个同级别的节点,返回当前节点
  174. | 入参 | 类型 |
  175. | --- | --- |
  176. | `ast` | AST <br> node <br> string |
  177. ### .before()
  178. 在当前节点前面插入一个同级别的节点,返回当前节点
  179. | 入参 | 类型 |
  180. | --- | --- |
  181. | `ast` | AST <br> node <br> string |
  182. ### .append(attr, ast)
  183. 在当前节点内部某个数组属性的末尾插入一个子节点,返回当前节点
  184. | 入参 | 类型 |
  185. | --- | --- |
  186. | `attr` | 当前节点的数组属性名称 |
  187. | `ast` | AST <br> node <br> string |
  188. - 为什么需要传入`attr`?
  189. 因为某些节点中多个属性都为数组,如函数,存在入参params和函数体body两个数组子节点,必须通过attr来判断插入节点的位置
  190. ```typescript
  191. AST
  192. .find('function $_$() {}')
  193. .append('params', 'b')
  194. .prepend('body', 'b = b || 1;')
  195. ```
  196. ### .prepend()
  197. 在当前节点内部某个数组属性的首位插入一个子节点,返回当前节点
  198. ### .empty()
  199. 清空当前节点所有子节点,返回当前节点
  200. ### .remove()
  201. 移除当前节点,返回根节点
  202. ### .generate()
  203. 将AST对象输出为代码