recast-function
Version:
Use recast to translater function.
178 lines (175 loc) • 7.15 kB
JavaScript
; (function () {
"use strict"
var _global;
var recast = require("recast");
var {
variableDeclaration, variableDeclarator, functionExpression,
identifier: id,
expressionStatement,
memberExpression,
assignmentExpression,
arrowFunctionExpression,
blockStatement,
classExpression,
classStatement,
classBody,
methodDefinition,
classDeclaration,
callExpression,
binaryExpression,
literal,
objectExpression,
doExpression,
exportNamedDeclaration,
functionDeclaration,
} = recast.types.builders;
function fileDownload(content, filename) {
var eleLink = document.createElement('a');
eleLink.download = filename;
eleLink.style.display = 'none';
var blob = new Blob([content]);
eleLink.href = URL.createObjectURL(blob);
document.body.appendChild(eleLink);
eleLink.click();
URL.revokeObjectURL(eleLink.href); // 解除链接
document.body.removeChild(eleLink);
};
function normalToReactClass(code, isDownLoad) {
var ast = '';
try {
ast = recast.parse(code);
} catch (error) {
var index = error.index;
// 标签不能直接return
if (error.description === 'Unexpected token <') {
var afterCode = code.substring(index, code.length);
var matchB = afterCode.match(/<\w+?[ \>]/);
var hStr = matchB[0].substring(1, matchB[0].length - 1);
function findFromLast(str, flag, num) {
num = typeof num == "undefined" ? 0 : num;
var pattern = new RegExp(
flag + "[^" + flag + "]*(?=(" + flag + "[^" + flag + "]*){" + num + "}$)", "g");
var target = str.match(pattern);
return target ? target[0] : target;
}
var afterStr = findFromLast(afterCode, "</" + hStr + ">");
var endIndex = index + afterCode.length - afterStr.length + 3 + hStr.length;
code = code.substring(0, index) + '`r_e_s_p_a_c_e'
+ code.substring(index, endIndex) + 'r_e_s_p_a_c_e`'
+ code.substring(endIndex, code.length);
return normalToReactClass(code, isDownLoad);
} else {
console.log('code', code);
console.log('error code', code.substring(0, error.index));
console.warn(error);
}
}
if (!ast) { return false; }
// 先把所有props转换成this.props
recast.types.visit(ast, {
visitIdentifier(path) {
var node = path.value;
if (node.name === 'props') { node.name = 'this.props'; }
// this.visitor.names.push(node.name);
this.traverse(path);
}
});
// 用来保存遍历到的全部函数名
var funcIds = []
recast.types.visit(ast, {
// 遍历所有的函数定义
visitFunctionDeclaration(path) {
// 获取遍历到的函数名、参数、块级域
var node = path.node
var funcName = node.id
var params = node.params
var body = node.body
// 保存函数名
funcIds.push(funcName.name)
// 这是上一步推导出来的ast结构体
var rep = classDeclaration( // Declaration
id(funcName.name),
classBody([
methodDefinition(
'constructor',
id('constructor'),
functionExpression(
null,
[id('props')],
blockStatement([
expressionStatement(callExpression(id('super'), [id('props')])),
expressionStatement(assignmentExpression('=',
memberExpression(id('this'), id('state'), false),
objectExpression([])))
])
),
false
),
methodDefinition(
'constructor',
id('render'),
functionExpression(
null,
[],
body
),
false
)
]), memberExpression(id('React'), id('Component'), false)
)
// 将原来函数的ast结构体,替换成推导ast结构体
path.replace(rep)
// 停止遍历
return false
}
})
recast.types.visit(ast, {
visitFunction(path) {
var node = path.node;
if (node.type === 'FunctionDeclaration') {
var funcName = node.id;
var params = node.params;
var body = node.body;
var rep = variableDeclaration("const", [
variableDeclarator(funcName, arrowFunctionExpression(params, body))
]);
path.replace(rep)
}
this.traverse(path);
}
});
recast.types.visit(ast, {
// 遍历所有的函数调用
visitCallExpression(path) {
var node = path.node;
// 如果函数调用出现在函数定义中,则修改ast结构
if (funcIds.includes(node.callee.name)) {
node.callee = memberExpression(id('exports'), node.callee)
}
// 停止遍历
return false
}
})
// 打印修改后的ast源码
var codeResult = recast.print(ast).code;
if (/`r_e_s_p_a_c_e/.test(codeResult)) {
codeResult = codeResult.replace(/`r_e_s_p_a_c_e/g, "");
codeResult = codeResult.replace(/r_e_s_p_a_c_e`/g, "");
}
if (isDownLoad) {
fileDownload(codeResult, '函数normalToReactClass_' + (new Date()).getTime() + '.js');
}
return codeResult;
}
var recastFunction = {
normalToReactClass: normalToReactClass,
};
_global = (function () { return this || (0, eval)('this'); }());
if (typeof module !== "undefined" && module.exports) {
module.exports = recastFunction;
} else if (typeof define === "function" && define.amd) {
define(function () { return recastFunction; });
} else {
!('recastFunction' in _global) && (_global.recastFunction = recastFunction);
}
}());