41

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.

execjosh
  • 714
  • 7
  • 9
Dexter
  • 12,172
  • 9
  • 27
  • 30
  • 2
    http://stackoverflow.com/questions/16697791/nodejs-get-filename-of-caller-function – zloctb Aug 27 '14 at 12:43
  • Possible duplicate of [Nodejs: get filename of caller function](http://stackoverflow.com/questions/16697791/nodejs-get-filename-of-caller-function) – ripper234 Mar 18 '17 at 17:00
  • `console.log((new Error()).stack.split("\n")[1].split("/").slice(-1)[0].split(":")[0]) // "caller_file_name.js"` – marcelosalloum Aug 23 '21 at 14:25

6 Answers6

48

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.

execjosh
  • 714
  • 7
  • 9
42

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.

Rubens Mariuzzo
  • 28,358
  • 27
  • 121
  • 148
Capaj
  • 4,024
  • 2
  • 43
  • 56
  • 8
    Yes but if you have multiple `module.exports` between the function and the caller, it doesn't work. – Sylvain B Dec 24 '14 at 10:01
  • @Sylvain in that case, you should be able to traverse parents all the way. That might be problematic, if you are loading modules dynamically, but in most cases, you wouldn't do that. – Capaj Apr 14 '15 at 12:24
  • 1
    @Capaj I was just going to call out Sylvain for the same thing, but that was because of the solution **I** was looking for, not the OP. The OP asked for the filename of the calling `function`, not calling `module`. Your answer fits my question perfectly, but doesn't match fully with the OP's question, so I'd have to agree with him. However, you saved me a lot of continued searching :D – Swivel Aug 14 '15 at 01:45
  • Is there a way to get the caller's name every time your module is required instead of just the first caller (via `parent`)? – odigity May 25 '17 at 02:57
  • Ended up using https://github.com/sindresorhus/callsites/, which at least works in Node, which is what I need. – odigity May 25 '17 at 03:24
6

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);
    }
    ...
x-yuri
  • 16,722
  • 15
  • 114
  • 161
6

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
    )
}
Mehdi Belbal
  • 523
  • 3
  • 7
3

My 2 cents:

Let's say you have a log object which adds as extra information to the console the filename of the caller, for example log responds to log.info(msg) and will do something similar to:

// my_module.js
log.info('hello')
$> [[my_module.js]] hello

info would be:

info: function(msg) {
  let caller = path.basename(module.parent.filename);
  console.log(`[[${caller}]] ${msg}`);
}

Problem: as said before, parent.filename will return you who did require the module in first place, not the caller itself.

Alternative: stack-trace is a module which will do the trick:

const stackTrace = require('stack-trace');
...
info: function(msg) {
  let caller = path.basename(stackTrace.get()[0].getFilename());
  console.log(`[[${caller}]] ${msg}`);
}

The point: stackTrace.get()[0] returns the last Caller which responds to (just some of them)

  • getFileName()
  • getColumnNumber()
  • getFunctionName()
  • getLineNumber()
  • getMethodName()
Manu Artero
  • 9,238
  • 6
  • 58
  • 73
0

If you don't want use a third-party library you can do like this:

    function getFileCallerURL(): string {
        const error: Error = new Error();

        const stack: string[] = error.stack?.split('\n') as string[];

        const data: string = stack[3];

        const filePathPattern: RegExp = new RegExp(`(file:[/]{2}.+[^:0-9]):{1}[0-9]+:{1}[0-9]+`);

        const result: RegExpExecArray = filePathPattern.exec(data) as RegExpExecArray;

        let filePath: string = '';

        if (result && (result.length > 1)) {
            filePath = result[1];
        }

        return filePath;
    }
rplaurindo
  • 1,277
  • 14
  • 23