Cookbook

基础类型获取和操作

01.获取变量

// 获取所有变量
$(code)
  .find('$_$name')
	.each(item => {
  	console.log(item.match)
	})

// 变量名list
$(code)
  .find('list')

02.修改某个变量值

// 获取fetch变量,将其变量名改为request
const res = $(`const fetch = () => {}; const noChange = 'fetch'`)
  .find('fetch')
  .each((item) => {
    item.replaceBy('request')
  })
  .root()
  .generate()

03.获取字符串

// 获取所有字符串
$(code)
  .find(`'$_$str'`)
	.each(item => {
		console.log(item.match)	
	})

// 获取确定的字符串'My Tree'
$(code)
  .find(`'My Tree'`)
	.each(item => {
		console.log(item.node)	
	})

04.获取赋值语句

// 赋值(Assignment)与定义(Declaration)语句需要区分开 
// 获取所有赋值语句
$(code)                  
  .find(`$_$1 = $_$2`)
	.each(item => {
		console.log(item.match[1])
	  console.log(item.match[2])
	})

// 获取对 this.isOpen 的赋值语句
$(code)
  .find('this.isOpen = $_$2')
  .each(item => {
    console.log(item.match[2])
   })

// 获取对car对象中的color属性的赋值语句
$(code)
  .find('car.color = $_$2')

// 获取对任意对象中的color属性的赋值语句
$(code)
	.find('$_$1.color = $_$2')

// 获取被[1, 2]赋值的变量名
$(code)
  .find('$_$1 = [1, 2]')

05.获取定义语句

// 获取所有定义语句,包括变量定义、函数定义
$(code)
	.find(`var $_$1 = $_$2`)

// 获取对list的定义语句
$(code)
	.find([`var list = $_$1`, `let list = $_$1`, `const list = $_$1`])

06.在某作用域里面获取变量定义

// 找到create函数,在其内部获取type变量定义

$(code)
	.find('function() create() {}')
	.find('let type = $_$1')

// 找到create函数处理之后返回全局获取type变量定义或进行其他操作
$(code)
	.find('function() create() {}')
	.each(item => {})
	.root()								// 回到全局
	.find('let type = $_$1')

07.获取类定义

// 获取所有类定义
$(code)
  .find(`class $_$1 {}`)

// 获取Car类的定义
$(code)
  .find(`class Car {}`)

// 获取Car类且有color\size属性的类定义
$(code)
  .find(`class Car {
    color = $_$c
    size = $_$s
  }`)
	.each(item => {
	  item.match['c']
  	item.match['s']
	})

08.获取ts类型定义

// 获取类型定义为CheckBoxProps的语句
$(code)
  .find('CheckBoxProps')		// 找到的有可能是变量名,也有可能是类型定义
  .each(item => {
      if (item.parent().node.type == 'TSTypeReference') {
          // 判断其父节点是类型定义TSTypeReference,就找到了
      }
  })

// 获取类型为CheckBoxProps的变量定义
$(code)
	.find('let $_$1:CheckBoxProps = $_$2')
	.each(item => {
		item.match[1]			// 变量名
  	item.match[2]			// 变量值
	})

// 获取带有CheckBoxProps类型入参的箭头函数 *
$(code)
	.find('($_$1: CheckBoxProps) => {}')
	.each(item => {
		item.match		// 入参
  	item.node			// 箭头函数完整节点
	})

09.获取条件判断if语句

// 获取所有if条件判断
$(`if (a && 1 || 0) { b } else { c; dosth() }`)
  .find(`if ($_$1) { $$$2 } else { $$$3 }`)
	.each(item => {
		item.node	// 整个if语句对应的ast节点
  	item.match	
  	/** 每个通配符匹配到的内容	
      a && 1 || 0 
      b
      c; dosth()
    **/
	})

// 获取判断isShow的条件判断
$(code)
  .find('if (isShow) {}')
  .each(item => {
    result.push(item.child('consequent.body.0').generate())
  })

函数相关的获取和操作

10.获取函数定义

$(code)
  .find(`function $_$1() {}`)
	.each(item => {
		item.match[1]	// 函数名
  	item.node			// 函数完整节点
	})

// 获取箭头函数
$(code)
  .find(`() => {}`)

// 获取入参包含type的箭头函数
$(code)
  .find(`(type) => {}`)

11.获取函数名确定的函数内容

方法1:利用$$$通配符,AST实例的match属性获取
$(code)
  .find(`function afunc() { $$$1 }`)
  .each(item => {
    item.match[1][0].value		// 函数内容
  	item.match[1][0].node			// 函数内容对应的ast节点
  })

方法2:利用attr()获取函数节点的子节点
$(code)
  .find(`function afunc() {}`)
  .each(item => {
    $(item.attr('body')).generate()			// 函数内容
  	item.attr('body')										// 函数内容对应的ast节点
  })

12.获取包含某个语句的函数

// 方法一:选择器获取包含this.requester的函数
$(code)
  .find(`function $_$1() {
    this.requester()
  }`)


// 方法二:获取所有函数之后判断内部是否包含this.requester
$(code)
  .find(`function $_$1() {  }`)
  .each(item => {
    if (item.has('this.requester')) {
      // 判断节点内部是否包含this.requester
    }
  })

13.获取函数调用

// 获取所有函数调用
$(code)
	.find('$_$1()')
	.each(item => {
		item.match		// 函数名
  	item.node			// 函数对应的ast节点
  	item.attr('arguments')		// 调用函数的入参
	})

// 获取通过this调用的函数
$(code)
	.find('this.$_$1()')

// 获取对create函数的调用
$(code)
	.find('create()')

14.修改函数入参

alert({
	type: 'error',
  content: '请填写必填项',
  done: () => {}
})
// 将alert函数调用的type、content入参铺平

$(code)
	.replace(`alert({ type: $_$1, done: $_$3, content: $_$2})`, 
           `alert( $_$1, $_$2, $_$3 )`)


// 在alert函数调用的入参最前面插入this
$(code).replace(`alert($$$1)`, `alert(this, $$$1)`)

15.根据原节点结构构造新节点

// 仍然是将alert函数调用的type、content入参铺平这个例子:

alert({
	type: 'error',
  content: '请填写必填项',
  done: () => {}
})

$(code)
	.find(`alert({ type: $_$1, content: $_$2, done: $_$3 })`)
	.each(item => {
		const typeValue = item.match[1][0].value,
          contentValue = item.match[2][0].raw || item.match[2][0].value,,
          doneValue = item.match[3][0].value
    item.replaceBy($(`
			alert( ${typeValue}, ${contentValue}, ${doneValue} )
		`))
	})

对象及属性相关获取和操作

16.获取对象属性

// 获取对象内部名为greet的函数
$(code)
  .find(`greet() {}`)
	.each(item => {
		item.node			// greet方法
  	item.parent(1).node		// greet方法外层对象
	})

// 获取对象内部名为bye的属性
$(code)
  .find(`bye: $_$1`)

17.获取对象

// 获取包含color属性的对象
$(code)
  .find(`{ color: $_$1 }`)
	.each(item => {
		item.node			// 包含color属性的对象
  	item.match		// color的值
	})

// 获取包含color,且color为green的对象
$(code)
  .find(`{ color: 'green' }`)

// 获取包含init() {}成员函数的对象
$(code)
  .find(`{ init() {} }`)

// 获取名为car1的对象
$(code)
  .find(`const car1 = $_$1`)
  .each(item => {
	  // item.match是被通配符匹配到的节点
  	if (item.match[1][0].node.type == 'ObjectExpression') {
    	// 找到car被赋值的节点,判断是不是对象类型
    }
  })

18.修改对象中的属性

const code = Page({
  onShow() { },
  data: { }
})
// 将在Page第一个入参中的onShow函数名修改为render

    // 方法1: 其中$$$1、$$$2代表的是rest含义,捕获剩余部分并且不会改变
    $(code)
      .replace(`Page({
        onShow() {
          $$$1	
        },
        $$$2
      })`, `Page({
        render() {
          $$$1
        },
        $$$2
      })`)

    // 方法2:使用match改变子节点属性
    $(code).find(`Page({ $_$1() { }	})`)
      .each(item => {
        if (item.match[1][0].value == 'onShow') {
          item.match[1][0].node.name = 'render'
        }
      })

19.有条件的修改对象某个属性值

const map = { input: 'textarea' }		// 在map中有映射的才进行修改

const res = $(`
    const componentList = [{
    index: 1,
    component: 'input'
  }, {
    index: 2,
    component: 'radio'
  }, {
    index: 3,
    component: 'checkbox'
  }]`)
.replace('component: $_$1', (match => {
  if (map[match[1][0].value]) {
    return `component: ${map[match[1][0].value]}`
  } else {
    return 'component: $_$1'
  }
}))
.generate()

20.对象中插入一个新属性

Page({
  onShow() { },
  data: { }
})

// 在Page第一个入参对象中插入init() { this.data = {} }

方法一:
    $(code)
      .replace(`Page({
        $$$2
      })`, `Page({
        init() { 
					this.show = false
				},
        $$$2
      })`)

// 方法2:使用append
$(code).find(`Page({})`)
  .each(item => {
		$(item.attr('arguments.0')).append('properties', `init() {}`)
  	// page的arguments[0]是第一个入参对象,通过attr获取到这个节点之后用$()转为AST实例,
  	// 就可以链式调用进行后续操作,append第一个参数是第二个参数指定插入的位置
  })
	.root()
	.generate()

import\export相关获取和操作

21.获取import\export语句

// 获取所有import语句 
//		包括import x from 'xx' ;  import { x } from 'xx' ; import 'xx'
$(code)
	.find(`import $_$1 from '$_$2'`)
	// 第二个通配符需要使用引号包裹,因为import语句要求source一定是字符串


// 匹配import异步,如import('@source/package/index').then()
$(code)
	.find(`import($_$1)`)

// 匹配 ExportNameDeclaration 语句
$(code)
	.find(`export $_$1 from '@path/sth'`)


// 匹配 ExportAllDeclaration 语句
$(code)
	.find(`export * from '@path/sth'`)

22.获取import语句并修改source

// 获取模块路径为'bb/bb-plugin'的import语句 并改为'gogocode'

$(code)
	.replace(`import $_$1 from 'bb/bb-plugin'`, `import $_$1 from 'gogocode'`)

// 获取模块路径包含'bb/...'的import语句 并改为bb/gogocode/...
$(code)
	.find(`import { $$$1 } from '$_$2'`)
	.each(item => {
		const source = item.match[2][0].value;
		item.match[2][0].node.value = source.replace('bb/', 'bb/gogocode/');
	})


// 源代码:
// import { useContext, userLogger } from '@as/mdw-hk'
// 将代码中由@as/mdw-hk引入的useContext模块名改为useFContext
$(code).replace(`import { useContext, $$$1 } from '@as/mdw-hk'`, `import { useFContext, $$$1 } from '@as/mdw-hk'`)

jsx标签相关操作

23.修改jsx标签名

// jsx内容:
<View>
  <View name="1" class="active" />
  <View></View>
</View>

// 将View标签名改为div

$(code)
	.replace(`<View $$$1>$$$2</View>`,`<div $$$1>$$$2</div>`)

24.修改jsx属性

// jsx内容:
<View>
  <View name="1" class="active" />
  <View name="1" id="key">text</View>
</View>

// 将View标签的name="1"属性修改为type="input"

$(code)
	.replace(`<View name="1" $$$1>$$$2</View>`,`<View type="input" $$$1>$$$2</View>`)

几个完整的case

25.批量转换一个文件夹下所有.js文件

const glob = require('glob');
const $ = require('gogocode');

glob('./code/**/*.js', function (err, files) {
    files.forEach(function (file) {
        rewrite(file);
    })
})
function rewrite(filePath) {
    const newCode = $.loadFile(filePath)
    	.replace(`let $_$1 = console.log()`, `let $_$1 = void 0`)
    	.generate()
 		$.writeFile(newCode, filePath);
}

26.某行代码后面插入空行

// 在所有的function定义之后插入一个空行
// 空行在ast中没有对应的节点,可以插入一个带时间戳的字符串,ast输出为字符串之后再全局将时间戳字符串替换为空

const placeholder = `placeholder${+new Date()}`;
$(code)
	.find('function $_$1() {}')
	.after(placeholder)
  .root()
  .generate()
  .replace(new RegExp(placeholder, 'g'), '');

27.删除一个节点

// 找到console.log()节点并删除
方法1:
$(code)
	.find('console.log()')
	.remove()

方法2:
$(code).replace('console.log()', '')

28.解构赋值转成 es5 写法

// 找到const {a,b = {b1},c = 3} = d; 转为const a = d.a, b = d.b || {b1}, c = d.c || 3;

const res = $(`const {a,b = {b1},c = 3} = d`)
  .find('const { $_$1 = $_$2 } = $_$3')
  .each(item => {
    const keyList = item.match[1].filter((item, i) => i%2 == 0)
    const obj = item.match[3][0].value
    const newkeyList = keyList.map((key, i) => {
      let dec = `${key.value} = ${obj}.${key.value}`
      if (item.match[2][i].value != key.value) {
        dec += ('||' + item.match[2][i].value)
      }
      return dec
    })
    item.replaceBy(`const ${newkeyList.join(', ')}`)
  })
	.root()
  .generate()

29.插入多段代码

$(code)
	.find(`function create() {}`)
	.each(item => {
			$(item.attr('body')).append('body', `
					let type = 'success'
					console.log('success')
			`)
  .root()
  .generate()
})

30.获取表达式中的变量

$(`(a.b.c && b) || (c && d)`)
.find('$_$1')
.each(item => {
  if (item.parent().node.type == 'MemberExpression' && item.parent(1).node.type != 'MemberExpression') {
    // 输出a.b.c整体 而不是a \ b \ c
    console.log(item.parent().generate())
  } else if (item.parent().node.type != 'MemberExpression') {
    // 输出独立的变量
    console.log(item.generate())
  }
})
// 输出a.b.c \ b \ c \ d
如果对于以上case有疑问,请在github提交issue,我们会很快回复~