我们的 idea 是在不编辑现有函数的情况下实现这一点,也不必向代码库中的所有函数添加包装器.
理想情况下,console.log应该只应用于我的代码库中的函数,而不是 node 模块函数.
我想在控制台中记录函数名和调用函数所用的参数,并在末尾记录函数名和返回值.
有没有人知道满足这个要求的解决方案?也许和巴别塔一起?
我们的 idea 是在不编辑现有函数的情况下实现这一点,也不必向代码库中的所有函数添加包装器.
理想情况下,console.log应该只应用于我的代码库中的函数,而不是 node 模块函数.
我想在控制台中记录函数名和调用函数所用的参数,并在末尾记录函数名和返回值.
有没有人知道满足这个要求的解决方案?也许和巴别塔一起?
这里有一个巴别塔插件,应该可以做你想做的事情.请注意,对于函数defined,它将匿名地记录"<;匿名&>"作为函数名,而不管它们稍后是否被赋值给变量,这可能是您想要的,也可能不是.
我试图使代码尽可能简单,这样您应该能够根据自己的喜好进行调整.
babel-plugin-autologcalls.mjs
个
function insertLogBeforeFunctionBody(
t,
node,
// default to using the arguments keyword
args = [t.identifier("arguments")]
) {
// node.body is a BlockStatement
// we want to append a console.log("CALL [<function name>]", arguments) to the body of that block statement
node.body.body.unshift(
t.expressionStatement(
t.callExpression(t.identifier("console.log"), [
t.stringLiteral(`CALL [${node.id?.name || "<anonymous>"}]`),
...args,
])
)
);
}
// recursively extract the identifiers from any kind of parameter
function getParameterIdentifiers(param) {
switch (param.type) {
case "Identifier":
return [param];
case "ArrayPattern":
return param.elements.flatMap(getParameterIdentifiers);
case "ObjectPattern":
return param.properties.flatMap((prop) =>
getParameterIdentifiers(prop.value)
);
case "RestElement":
return [param.argument];
}
}
function insertLogBeforeArrowFunctionBody(t, node, scope) {
// rewrite arrow function of form () => ... to () => { return ... } so we can insert a console.log
if (node.body.type !== "BlockStatement") {
node.body = t.blockStatement([
// will later be replaced by the ReturnStatement visitor
t.returnStatement(node.body),
]);
}
insertLogBeforeFunctionBody(
t,
node,
// arrow functions don't support the arguments keyword, so we extract the arguments from the parameters
node.params.flatMap(getParameterIdentifiers)
);
}
// recursively find the function the return statement was used in
function getFunctionFromReturnStatement(path) {
return path.type.match(/^(Arrow)?Function(Declaration|Expression)$/)
? path.node
: getFunctionFromReturnStatement(path.parentPath);
}
function insertLogBeforeReturnStatement(t, path) {
const retId = path.scope.generateUidIdentifier("ret");
path.replaceWithMultiple([
// const <retId> = <returned value>
// console.log("RETURN [<function name>]", <retId>);
// return <retId>;
t.variableDeclaration("const", [
t.variableDeclarator(retId, path.node.argument),
]),
t.expressionStatement(
t.callExpression(t.identifier("console.log"), [
t.stringLiteral(
`RETURN [${
getFunctionFromReturnStatement(path).id?.name || "<anonymous>"
}]`
),
retId,
])
),
// add extra __isLogged property to prevent an infinite loop where the replacement return statement gets visited
{ ...t.returnStatement(retId), __isLogged: true },
]);
}
export default function ({ types: t }) {
return {
visitor: {
// function <name>() { ... } (statement)
FunctionDeclaration({ node }) {
insertLogBeforeFunctionBody(t, node);
},
// function <name?>() { ... } (expression)
FunctionExpression({ node }) {
insertLogBeforeFunctionBody(t, node);
},
// () => ... or () => { ... }
ArrowFunctionExpression({ node, scope }) {
insertLogBeforeArrowFunctionBody(t, node, scope);
},
// return ...
ReturnStatement(path) {
if (!path.node.__isLogged) {
insertLogBeforeReturnStatement(t, path);
}
},
},
};
}
要使用它,请将其注册到babel.config.json
或.babelrc
文件中,如下所示:
{
"plugins": ["./babel-plugin-autologcalls.mjs"]
}
然后运行npm i -D @babel/core @babel/cli
,在项目中安装了Babel之后,使用命令npx babel <source.js> --out-dir <output directory>
编译代码.
(请注意,使用巴别塔有不同的方法,请参阅its documentation了解更多选项.)
使用此插件,以下代码...
function emptyfn() {}
function normalfn(s) {
if (s.length > 1) {
return s;
}
return arguments;
}
const anonymousfn = function () {
return arguments;
};
const arrowfn = (
_,
n,
[expanded],
{
_: {
nested: [fn],
},
d,
}
) => {
return fn(n + expanded - d);
};
const lambda = (...args) => args;
const exampleargs = [
"a",
10,
[5, 6, 7],
{ _: { nested: [(a) => `(${a})`] }, d: -11 },
];
normalfn(...exampleargs);
anonymousfn(...exampleargs);
arrowfn(...exampleargs);
lambda(...exampleargs);
...将编译为:
function emptyfn() {
console.log("CALL [emptyfn]", arguments);
}
function normalfn(s) {
console.log("CALL [normalfn]", arguments);
if (s.length > 1) {
const _ret = s;
console.log("RETURN [normalfn]", _ret);
return _ret;
}
const _ret2 = arguments;
console.log("RETURN [normalfn]", _ret2);
return _ret2;
}
const anonymousfn = function () {
console.log("CALL [<anonymous>]", arguments);
const _ret3 = arguments;
console.log("RETURN [<anonymous>]", _ret3);
return _ret3;
};
const arrowfn = (_, n, [expanded], {
_: {
nested: [fn]
},
d
}) => {
console.log("CALL [<anonymous>]", _, n, expanded, fn, d);
const _ret4 = fn(n + expanded - d);
console.log("RETURN [<anonymous>]", _ret4);
return _ret4;
};
const lambda = (...args) => {
console.log("CALL [<anonymous>]", args);
const _ret5 = args;
console.log("RETURN [<anonymous>]", _ret5);
return _ret5;
};
const exampleargs = ["a", 10, [5, 6, 7], {
_: {
nested: [a => {
console.log("CALL [<anonymous>]", a);
const _ret6 = `(${a})`;
console.log("RETURN [<anonymous>]", _ret6);
return _ret6;
}]
},
d: -11
}];
normalfn(...exampleargs);
anonymousfn(...exampleargs);
arrowfn(...exampleargs);
lambda(...exampleargs);
希望这个能帮上忙!