javascriptで現在実行中のファイルパス(__FILE__)及び行番号(__LINE__)を取得する方法


注: arguments.callee はstrict mode では使えず、Error.prototype.stack は標準化から外れています。あくまでデバック目的にだけ利用し、一般公開するソースコードには含めないことを推奨します。また、__FILE__ はURLから生成しており、サーバに存在するファイルパスとは異なることがあります。

Object.defineProperty(window, '__STACK__', {
    get: function(){
        let origin = Error.prepareStackTrace;
        Error.prepareStackTrace = function(_, stack){ return stack; };
        let err = new Error;
        Error.captureStackTrace(err, arguments.callee);
        let stack = err.stack;
        Error.prepareStackTrace = origin;
        return stack;
    }
});
Object.defineProperty(window, '__FILE__', {
    get: function(){
        let filename = __STACK__[1].getFileName().replace(location.origin, "").replace(window.location.search, "");
        if(!filename) filename = "/";
        return filename;
    }
});
Object.defineProperty(window, '__LINE__', {
    get: function(){
        return __STACK__[1].getLineNumber();
    }
});
console.log("__FILE__", __FILE__);
console.log("__LINE__", __LINE__);

jQueryの $.ajax() ( $().load(), $.get(), $.getScript() なども $.ajax()のラッパーなのでこれに含まれる) で外部ファイルを読み込んだ場合、 xmlHttpRequest による読み込みなのでデフォルトではファイル名が取得されない(Chrome のconsole で試すと、VM上に展開されているのがわかる)。従って__FILE__ は使えない(__LINE__ は使える)。その為これらを使いたい場合は、上記とは別に工夫する必要がある。

まず、読み込み元のファイルに以下を記述する。

$.ajaxSetup({
    dataFilter: function(data) {
        let filename = this.url.replace(location.origin, "").replace(/\?.*/, "");
        if(this.dataType === "script") return `__XHR_FILE__ = "${filename}";\n` + data;
        else if(this.dataType === "html") return `<script class="init_script">__XHR_FILE__ = "${filename}";<\/script>` + data;
        else return data;
    },
});

こうすれば、(dataType が "script" と "html" のとき限定だが) __XHR_FILE__ に現在のファイルパスが代入される。あとは読み込み先のファイルで __XHR_FILE__ を参照するだけ。

注意点として、javascript は非同期でファイル読み込みを行っているので、あるファイル内で __XHR_FILE__が代入された後、別のファイルの読み込みが起こり __XHR_FILE__ の値が置き換わる可能性がある。細かくは試してないが、ファイルの最初の行で let xml_file = __XHR_FILE__; のように値をコピーしておけば問題ないだろう。