对js编码实现函数打桩实现国际化
对
js
编码实现函数打桩,实现自动国际化;
js
const a = '哈哈';
目标转成:
js
const a = window.$tx('哈哈');
第一步初始化
准备基础代码、以及依赖如下:
js
import * as parser from '@babel/parser';
import traverse from '@babel/traverse';
import generate from '@babel/generator';
import template from '@babel/template';
import * as types from '@babel/types';
const code = `
const a = window.$tx('国际化');
const obj = {
a: 'test',
b: '哈哈',
d: 'a哈',
}
const a1 = a ? '哈哈1' : a;
const k = '我' + '是babel';
function fn(params) {
let results = ''
switch (params) {
case '国际化':
results = '国际化'
break;
case 'aa':
results = 'aa'
break;
default:
break;
}
return results;
}
`
const ast = parser.parse(code, {
sourceType: 'unambiguous',
});
console.log(generate(ast).code);
第二步 利用traverse
对 ast
进行改造
traverse
ast=>修改过的ast
可以使用template
帮咱们快速生成window.$tx 的 ast
如下:
js
import template from '@babel/template';
const txTemp = template(`window.$tx(NODE_TEMPLATE)`);
也可以使用types
创建window.$tx ast
如下:
js
import * as types from '@babel/types';
const txCallExpression = types.callExpression(
types.identifier('window.$tx'), // 被调用的函数名
[types.stringLiteral('哈哈')] // 入参数 这个是需要打桩的语言文本
)
编写转化部分程序:
使用template
js
export function isCN(value) { // 判断是不是中文
return /[\u4E00-\u9FFF]+/g.test(value);
}
traverse(ast, {
StringLiteral(path) { // 使用字符串字面量
if (isCN(path.node.value)) { // path.node.value 就是文本,判断是中文就做处理
const tem = txTemp({
NODE_TEMPLATE: path.node,
});
path.replaceWith(tem); // 使用 path.replaceWith 做程序转换
path.skip(); // 转换完成跳过本次循环
}
},
});
使用types
:
js
function isCN(value) { // 判断是不是中文
return /[\u4E00-\u9FFF]+/g.test(value);
}
traverse(ast, {
StringLiteral(path) { // 使用字符串字面量
if (isCN(path.node.value)) { // path.node.value 就是文本,判断是中文就做处理
const txCallExpression = types.callExpression(
types.identifier('window.$tx'), // 被调用的函数名
[types.stringLiteral(path.node.value)] // 入参数 这个是需要打桩的语言文本
)
path.replaceWith(txCallExpression);
path.skip();
}
},
});
这个时候运行会发现
js
const a = window.$tx('国际化');
被转成了:
js
const a = window.$tx(window.$tx('国际化'));
这明显不是咱们想要的,添加判断如下:
js
function isCN(value) { // 判断是不是中文
return /[\u4E00-\u9FFF]+/g.test(value);
}
traverse(ast, {
CallExpression(path) {
const calleeCode = generate(path.node.callee).code;
if (calleeCode.includes('$tx')) {
path.skip();
}
},
StringLiteral(path) { // 使用字符串字面量
if (isCN(path.node.value)) { // path.node.value 就是文本,判断是中文就做处理
const txCallExpression = types.callExpression(
types.identifier('window.$tx'), // 被调用的函数名
[types.stringLiteral(path.node.value)] // 入参数 这个是需要打桩的语言文本
)
path.replaceWith(txCallExpression);
path.skip();
}
},
});
完整代码
js
import * as parser from '@babel/parser';
import traverse from '@babel/traverse';
import generate from '@babel/generator';
import template from '@babel/template';
import * as types from '@babel/types';
const code = `
const a = window.$tx('国际化');
const obj = {
a: 'test',
b: '哈哈',
d: 'a哈',
}
const a1 = a ? '哈哈1' : a;
const k = '我' + '是babel';
function fn(params) {
let results = ''
switch (params) {
case '国际化':
results = '国际化'
break;
case 'aa':
results = 'aa'
break;
default:
break;
}
return results;
}
`
const ast = parser.parse(code, {
sourceType: 'unambiguous',
});
function isCN(value) { // 判断是不是中文
return /[\u4E00-\u9FFF]+/g.test(value);
}
const txTemp = template(`window.$tx(NODE_TEMPLATE)`);
traverse(ast, {
CallExpression(path) {
const calleeCode = generate(path.node.callee).code;
if (calleeCode.includes('$tx')) {
path.skip();
}
},
StringLiteral(path) { // 使用字符串字面量
if (isCN(path.node.value)) { // path.node.value 就是文本,判断是中文就做处理
const txCallExpression = types.callExpression(
types.identifier('window.$tx'), // 被调用的函数名
[types.stringLiteral(path.node.value)] // 入参数 这个是需要打桩的语言文本
)
path.replaceWith(txCallExpression);
path.skip();
// const tem = txTemp({
// NODE_TEMPLATE: path.node,
// });
// path.replaceWith(tem); // 使用 path.replaceWith 做程序转换
// path.skip(); // 转换完成跳过本次循环
}
},
});
console.log(generate(ast, {
jsescOption: {
minimal: true, // issues https://github.com/babel/babel/issues/4909#issuecomment-397715926
}
}).code);
输出:
bash
$ node ./dist/js.js
const a = window.$tx('国际化');
const obj = {
a: 'test',
b: window.$tx("哈哈"),
d: window.$tx("a哈")
};
const a1 = a ? window.$tx("哈哈1") : a;
const k = window.$tx("我") + window.$tx("是babel");
function fn(params) {
let results = '';
switch (params) {
case window.$tx("国际化"):
results = window.$tx("国际化");
break;
case 'aa':
results = 'aa';
break;
default:
break;
}
return results;
}
总结
注意
generate
的jsescOption.minimal
设置为true
, 不然就会乱码了 issues地址。 经过咱们上面的编码就完成了自动化函数打桩的过程。