Here is some sample code from three files:
// foo.js
var myFunc = require("./myFunc");
function foo(){
myFunc("message");
}
// bar.js
var myFunc = require("./myFunc");
function bar(){
myFunc("message");
}
// myFunc.js
module.exports = myFunc;
function myFunc(arg1){
console.log(arg1);
// Here I need the file path of the caller function
// For example, "/path/to/foo.js" and "/path/to/bar.js"
}
I need to get the file path of the caller function dynamically, without any extra argument passing, for myFunc
.
You need to fiddle with the inner workings of v8
. See: the wiki entry about the JavaScript Stack Trace API.
I've based a little test on some code in a proposed commit and it seems to work. You end up with an absolute path.
// omfg.js module.exports = omfg function omfg() { var caller = getCaller() console.log(caller.filename) } // private function getCaller() { var stack = getStack() // Remove superfluous function calls on stack stack.shift() // getCaller --> getStack stack.shift() // omfg --> getCaller // Return caller's caller return stack[1].receiver } function getStack() { // Save original Error.prepareStackTrace var origPrepareStackTrace = Error.prepareStackTrace // Override with function that just returns `stack` Error.prepareStackTrace = function (_, stack) { return stack } // Create a new `Error`, which automatically gets `stack` var err = new Error() // Evaluate `err.stack`, which calls our new `Error.prepareStackTrace` var stack = err.stack // Restore original `Error.prepareStackTrace` Error.prepareStackTrace = origPrepareStackTrace // Remove superfluous function call on stack stack.shift() // getStack --> Error return stack }
And a test that includes omfg
module:
#!/usr/bin/env node // test.js var omfg = require("./omfg") omfg()
And you will get on the console the absolute path of test.js
.
EXPLANATION
This is not so much a "node.js" issue as it is a "v8" issue.
See: Stack Trace Collection for Custom Exceptions
Error.captureStackTrace(error, constructorOpt)
adds to the error
parameter a stack
property, which evaluates by default to a String
(by way of FormatStackTrace
). If Error.prepareStackTrace(error, structuredStackTrace)
is a Function
, then it is called instead of FormatStackTrace
.
So, we can override Error.prepareStackTrace
with our own function that will return whatever we want--in this case, just the structuredStackTrace
parameter.
Then, structuredStackTrace[1].receiver
is an object representing the caller.
Or instead of fiddling with inner workings of the V8 engine, you use module.parent.filename
to get absolute path to the module that required your module. As demonstrated here: https://gist.github.com/capaj/a9ba9d313b79f1dcd9a2
Just keep in mind that modules are cached, so if any other file requires it and calls it, it will always be the path to the first importer.
You can make use of caller-callsite
package:
console.log(callerCallsite().getFileName());
The alternatives are callsites
and stackman
packages. callsites
provides you with all call sites ("stack frames" in v8
terminology). And stackman
gives the call sites decorated with custom functions and behavior. Source context, among other things. Which is lines of code surrounding call site line. Also it makes use of source maps if available.
The issue with stackman
is that it returns call sites asynchronously. Which is not particularly usable when running from debugger.
Here's some code I used which you might find useful:
var callsites = require('callsites');
var util = require('util');
var path = require('path');
function printStackTrace() {
callsites().slice(1).forEach(function(cs) {
printCallSite(cs);
});
}
function printCallSite(cs) {
console.log(util.format('%s:%i',
path.relative(process.cwd(), cs.getFileName()),
cs.getLineNumber()));
console.log(' getTypeName(): ' + cs.getTypeName());
console.log(' getFunctionName(): ' + cs.getFunctionName());
console.log(' getMethodName(): ' + cs.getMethodName());
// console.log(' getEvalOrigin(): ' + cs.getEvalOrigin());
// console.log(' isTopLevel(): ' + (cs.isTopLevel ? cs.isTopLevel() : null));
// console.log(' isEval(): ' + cs.isEval());
// console.log(' isNative(): ' + cs.isNative());
// console.log(' isConstructor(): ' + cs.isConstructor());
}
function getCallSiteIndexes(cond) {
var cond = cond || function() { return true; };
var options = arguments[1] || {};
var css = options['callsites'] || callsites().slice(1);
var r = [];
for (var i = 0; i < css.length; i++) {
var cs = css[i];
if (cond(cs)) {
if (options['first'])
return i;
r.push(i);
}
}
return options['first'] ? null : r;
}
function getFirstCallSiteIndex(cond) {
var css = callsites().slice(1);
return getCallSiteIndexes(cond, {first: true, callsites: css});
}
function getCallSites(cond) {
var options = arguments[1] || {};
var css = options['callsites'] || callsites().slice(1);
var indexes = getCallSiteIndexes(cond,
Object.assign({}, {callsites: css}, options));
if (options['first'])
return css[indexes];
return indexes.map(function(i) {
return css[i];
});
}
function getFirstCallSite(cond) {
var css = callsites().slice(1);
return getCallSites(cond, {first: true, callsites: css});
}
fucntion f() {
var firstCS = callsites()[0];
var runAsChildCSIndex = getFirstCallSiteIndex(function(cs) {
return cs.getFileName() == firstCS.getFileName() && cs.getFunctionName() == 'Compiler.runAsChild';
});
if (runAsChildCSIndex) {
printCallSite(callsites()[runAsChildCSIndex + 1]);
} else {
var compilerRunCS = getFirstCallSite(function(cs) {
return cs.getFileName() == firstCS.getFileName() && cs.getFunctionName() == 'Compiler.run';
});
printCallSite(compilerRunCS);
}
...
the only way to get the path of the caller function in a node is via the stack trace (forget external library):
function getCallerFilePath(path) {
let stack = new Error().stack.split('\n')
return stack[2].slice(
stack[2].lastIndexOf('(')+1,
stack[2].lastIndexOf('.js')+3
)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With