注: 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__;
のように値をコピーしておけば問題ないだろう。