You can trace function calls with help of putout code transformer. Plugin
will look this way:
const {template, types, operator} = require('putout');
const {replaceWith} = operator;
const {BlockStatement} = types;
// create nodes
const buildLog = template(`console.log('TYPE' + ' ' + 'NAME')`);
const buildLogEnter = template(`console.log('enter' + ' ' + 'NAME' + '(' + JSON.stringify(Array.from(arguments)) + ')')`);
const buildLogException = template(`console.log('TYPE' + ' ' + 'NAME' + ': ' + trace$error.message); throw trace$error`);
const buildTryCatch = template(`try {
BLOCK;
} catch(trace$error) {
CATCH;
} finally {
FINALLY;
}
`);
const JSON = 'JSON';
// nodes we are searching for
module.exports.include = () => [
'Function',
];
module.exports.fix = (path) => {
const name = getName(path);
// create 3 types of events
const enterLog = buildLogEnter({
NAME: name,
JSON,
});
const exitLog = buildLogEvent(name, 'exit');
const errorLog = buildLogExceptionEvent(name);
// move function body into try-catch
const bodyPath = path.get('body');
replaceWith(bodyPath, BlockStatement([buildTryCatch({
BLOCK: path.node.body.body,
CATCH: errorLog,
FINALLY: exitLog,
})]));
// add into the beginning of function "console.log" with "enter" event
bodyPath.node.body.unshift(enterLog);
};
// get name of a function
function getName(path) {
if (path.isClassMethod())
return path.node.key.name;
if (path.isFunctionDeclaration())
return path.node.id.name;
const {line} = path.node.loc.start;
return `<anonymous:${line}>`;
}
// build logger
function buildLogEvent(name, type) {
return buildLog({
NAME: name,
TYPE: type,
});
}
// build logger that throws
function buildLogExceptionEvent(name) {
return buildLogException({
NAME: name,
TYPE: 'error',
});
}
Let's suppose that this is the code you want to trace:
const processFile = (a) => a;
process([]);
function process(runners) {
const files = getFiles(runners);
const linted = lintFiles(files);
return linted;
}
function getFiles(runners) {
const files = [];
for (const run of runners) {
files.push(...run());
}
return files;
}
function lintFiles(files) {
const linted = [];
for (const file of files) {
linted.push(processFile(file));
}
return linted;
}
Here is a full picture:

If you save processed source as trace.js
and run it with node, you will have:
> node trace.js
enter process([[]])
enter getFiles([[]])
exit getFiles
enter lintFiles([[]])
exit lintFiles
exit process
There is putout issue related to tracing functions.