这边遇到一个类似于测试的需求,需要从一个js文件里获取函数名,然后尝试执行这些函数和创建实例。这边首先遇到了一个问题是如何动态获取js里的函数名和类名,我这边暂时没找到特别好的方法,已有的方法都是类似于提取语法树那种提取函数名
最后想的还是通过正则匹配函数名来尝试执行,有参数需求的函数尝试用数字代替作为参数尽量执行,加了个5秒超时
附:安装npm
https://nodejs.org/en
安装minimist(用于调用参数)
npm i minimist
调用
node tester.js --fp sample.js
代码:
const fs = require('fs');
const path = require('path');
console.log("--fp指定路径");
var args = require('minimist')(process.argv.slice(2));
console.log("filepath:",args.fp);
// 目标文件路径
const targetPath = path.join(__dirname, args.fp);
// 读取index.js文件内容
const fileContent = fs.readFileSync(targetPath, 'utf8');
// 正则表达式匹配函数名和类名
const functionRegex = /function\s+(\w+)\s*\(/g;
const classRegex = /class\s+(\w+)/g;
const arrowFunctionRegex = /([a-zA-Z_\$][a-zA-Z_\$0-9]*)\s*=\s*\([^)]*\)\s*=>/g;
// const arrowFunctionRegex = /^([^\(\)]+)\s*=>/;
const functions = [];
const classes = [];
// 匹配函数名
let match;
while ((match = functionRegex.exec(fileContent)) !== null) {
functions.push(match[1]);
}
while ((match = arrowFunctionRegex.exec(fileContent)) !== null) {
functions.push(match[1]);
}
// 匹配类名
while ((match = classRegex.exec(fileContent)) !== null) {
classes.push(match[1]);
}
if (!fileContent.includes('module.exports')) {
fs.writeFileSync(
targetPath,
fileContent +
`\nmodule.exports = {${functions.join(',') + ',' + classes.join(',')}}`
);
}
const newRequire = require(targetPath);
/**
*@name asyncFunction
*@des 异步函数
*@param use: () => void | unknow
*@return {Promise}
*/
const asyncFunction = async(use) => {
return new Promise( (resolve, reject) => {
// 异步操作,例如 Ajax 请求或者定时器
try {
const result = use();
if(result) resolve(result)
} catch (error) {
reject(error)
}
});
}
// -- timeout 机制
function withTimeout(promise, timeout) {
return Promise.race([
promise,
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('操作超时'));
}, timeout);
})
]);
}
// 处理类的方法
const onHandleClassFn = async (ClassName, target, name) => {
const params = [];
for (let i = 0; i < target.length; i++) {
params.push('拟参' + i);
}
if (!/^class\s/.test(Function.prototype.toString.call(target))) {
try {
const result = await withTimeout(
asyncFunction(() => {
return target.length
?
target(...params)
:
target()
}),
5000
);
console.log(`---------类${ClassName}的${name}()的return结果:`, result);
} catch (error) {
console.log(`+++++++++抛出错误类${ClassName}的${name}()函数执行出错,error信息:`, error);
}
}
};
for (const iterator in newRequire) {
const target = newRequire[iterator];
const params = [];
for (let i = 0; i < target.length; i++) {
params.push('拟参' + i);
}
if (!/^class\s/.test(Function.prototype.toString.call(target))) {
try {
target.length
? console.log(
`---------以上是${iterator}()执行的log,return:`,
target(...params),
'---------'
)
: console.log(
`---------以上是${iterator}()执行的log,return:`,
target(),
'---------'
);
continue;
} catch (error) {
console.log(`+++++++++抛出错误函数${iterator}执行出错,error:`, error);
}
}
const Instance = target.length ? new target(...params) : new target();
// 获取构造函数包含的所有方法
const methods = Object.getOwnPropertyNames(Instance.__proto__).filter(
(item) => item !== 'constructor'
);
methods.forEach((item) => {
onHandleClassFn(iterator, Instance[item], item);
});
}
用于测试的脚本
function AFn() {
console.log('Run Function A');
return 'AFn is true'
}
function BFn(param1, param2) {
console.log('Run Function B', param1, param2);
return 'BFn is true'
}
const CFn = (param1, param2) => {
console.log('Run Function C', param1, param2);
return 'CFn is true'
}
class Cat {
constructor(name, age) {
this.name = name;
this.age = age;
}
Say1(param1) {
console.log('Say1方法参数:', param1);
return 'Say1 is ' + +new Date();
}
Say2(param1,param2) {
console.log('say2方法参数:', param1, param2);
return 'Say2 is ' + +new Date();
}
ThrowerrFn(){
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('这是一个异步模拟抛error出错误的函数');
reject('this is a error_throw');
}, 200);
})
}
ThrowSuccessFn(){
return new Promise((resolve) => {
setTimeout(() => {
console.log('这是一个异步模拟success的函数');
resolve('success')
}, 2000);
})
}
ThrowTimeoutFn(){
return new Promise((resolve) => {
setTimeout(() => {
resolve('timeout_success, 此结果会因为超时被阻塞输出')
}, 6000);
})
}
Say3(param1,param2) {
console.log('say3方法参数:', param1, param2);
return 'Say3 is ' + +new Date();
}
}
module.exports = {AFn,BFn,CFn,Cat}