如何在node.js中获取调用函数的文件路径?

Dex*_*ter 26 javascript node.js

以下是三个文件的示例代码:

// 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"
}
Run Code Online (Sandbox Code Playgroud)

我需要动态获取调用函数的文件路径,而不需要传递任何额外的参数myFunc.

exe*_*osh 36

你需要摆弄内心的运作v8.请参阅:有关JavaScript Stack Trace API的wiki条目.

我在一个提议的提交中对一些代码进行了一点测试,它似乎有效.你最终得到了一条绝对的道路.

// 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
}
Run Code Online (Sandbox Code Playgroud)

并且包含omfg模块的测试:

#!/usr/bin/env node
// test.js

var omfg = require("./omfg")

omfg()
Run Code Online (Sandbox Code Playgroud)

而你将获得控制台的绝对路径test.js.


说明

这不是一个"node.js"问题,因为它是一个"v8"问题.

请参阅:自定义例外的堆栈跟踪集合

Error.captureStackTrace(error, constructorOpt)error参数添加一个stack属性,默认情况下计算为String(通过FormatStackTrace).如果Error.prepareStackTrace(error, structuredStackTrace)是a Function,则调用它而不是FormatStackTrace.

因此,我们可以Error.prepareStackTrace使用我们自己的函数覆盖,它将返回我们想要的任何东西 - 在这种情况下,只是structuredStackTrace参数.

然后,structuredStackTrace[1].receiver是表示调用者的对象.


Cap*_*paj 33

或者,不是摆弄V8引擎的内部工作,而是使用module.parent.filename获取模块所需模块的绝对路径.如下所示:https://gist.github.com/capaj/a9ba9d313b79f1dcd9a2

请记住,模块是缓存的,因此如果任何其他文件需要它并调用它,它将始终是第一个导入程序的路径.

  • 是的,但如果函数和调用者之间有多个`module.exports`,则它不起作用. (5认同)

小智 7

获取节点中调用者函数的路径的唯一方法是通过堆栈跟踪(忘记外部库):

function getCallerFilePath(path) {
    let stack = new Error().stack.split('\n')
    return stack[2].slice(
        stack[2].lastIndexOf('(')+1, 
        stack[2].lastIndexOf('.js')+3
    )
}
Run Code Online (Sandbox Code Playgroud)


x-y*_*uri 5

您可以使用caller-callsite包:

console.log(callerCallsite().getFileName());
Run Code Online (Sandbox Code Playgroud)

替代方案是callsitesstackman包。callsites为您提供所有调用站点(v8术语中的“堆栈帧” )。并stackman赋予调用站点装饰自定义功能和行为。源上下文等。这是围绕呼叫站点行的代码行。如果可用,它还使用源映射。

问题stackman在于它异步返回调用站点。从调试器运行时,这不是特别有用。

这是我使用的一些代码,您可能会发现它们很有用:

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);
    }
    ...
Run Code Online (Sandbox Code Playgroud)