Skip to content

Commit 497a33a

Browse files
handle uncaught exceptions in nodejs
1 parent 5f03ec6 commit 497a33a

File tree

2 files changed

+48
-29
lines changed

2 files changed

+48
-29
lines changed

haxelib.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
"license" : "MIT",
55
"tags" : ["js", "php7", "stack", "callstack", "stacktrace"],
66
"description" : "Friendly stack traces for JS and PHP7 targets. Makes them point to haxe sources.",
7-
"version" : "2.1.0",
8-
"releasenote" : "Support for php7 target",
7+
"version" : "2.1.1",
8+
"releasenote" : "Handle uncaught exceptions in nodejs",
99
"classPath" : "src",
1010
"contributors" : ["RealyUniqueName"],
1111
"dependencies" : {

src/jstack/js/JStack.hx

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,20 @@ package jstack.js;
22

33
import haxe.CallStack;
44
import sourcemap.SourcePos;
5-
import js.Lib;
5+
import js.Error;
66
import haxe.io.Path;
77
import js.Browser;
88
import haxe.Http;
99

10-
using StringTools;
11-
1210
/**
1311
* Handles source map
1412
*/
1513
class JStack {
1614

1715
/** Create instance just to invoke `inject()` */
18-
static private var instance = new JStack();
16+
static var instance = new JStack();
1917
/** User-defined callback which will be invoked when sourceMap is loaded */
20-
static private var onReadyCallback : Void->Void;
18+
static var onReadyCallback : Void->Void;
2119

2220
/** Indicates if source map is loaded */
2321
public var ready (default,null) : Bool = false;
@@ -28,25 +26,35 @@ class JStack {
2826
* A call to this method is automatically injected in `static main()` function of your app.
2927
* You don't need to use this method manually.
3028
*/
31-
static public function onReady (callback:Void->Void) : Void
32-
{
29+
static public function onReady (callback:Void->Void) {
3330
onReadyCallback = callback;
3431
if (instance.ready) callback();
3532
}
3633

34+
/**
35+
* Overwrite this method to handle uncaught exceptions manually.
36+
* Any returned value will be written to stderr.
37+
* @param e - Uncaught exception.
38+
*/
39+
@:access(haxe.CallStack)
40+
static public dynamic function uncaughtExceptionHandler (e:Error) : String {
41+
var stack = CallStack.getStack(e);
42+
var error = e.message + CallStack.toString(stack) + '\n';
43+
return error;
44+
}
3745

38-
private function new ()
39-
{
46+
function new () {
47+
if(isNode()) {
48+
untyped __js__('process.on("uncaughtException", {0})', _uncaughtExceptionHandler);
49+
}
4050
inject();
4151
}
4252

43-
4453
/**
4554
* Loads source map and injects hooks into `haxe.CallStack`.
4655
* It's asynchronous process so haxe-related positions in call stack might be not available right away.
4756
*/
48-
public function inject () : Void
49-
{
57+
public function inject () {
5058
loadSourceMap(function (sourceMapData:String) {
5159
var mapper = new SourceMap(sourceMapData);
5260

@@ -66,9 +74,8 @@ class JStack {
6674
/**
6775
* Load source map and pass it to `callback`
6876
*/
69-
private function loadSourceMap (callback : String->Void) : Void
70-
{
71-
if (untyped __js__("typeof window != 'undefined'")) {
77+
function loadSourceMap (callback : String->Void) {
78+
if (!isNode()) {
7279
loadInBrowser(callback);
7380
} else {
7481
loadInNode(callback);
@@ -79,8 +86,7 @@ class JStack {
7986
/**
8087
* Do the job in browser environment
8188
*/
82-
private function loadInBrowser (callback:String->Void) : Void
83-
{
89+
function loadInBrowser (callback:String->Void) {
8490
var file = getCurrentDirInBrowser() + '/' + Tools.getSourceMapFileName();
8591
var http = new Http(file);
8692

@@ -97,8 +103,7 @@ class JStack {
97103
/**
98104
* Do the job in nodejs environment
99105
*/
100-
public function loadInNode (callback:String->Void) : Void
101-
{
106+
public function loadInNode (callback:String->Void) {
102107
var dir : String = untyped __js__("__dirname");
103108
var fs = untyped __js__("require('fs')");
104109

@@ -115,8 +120,7 @@ class JStack {
115120
/**
116121
* Scans DOM for <script> tags to find current script directory
117122
*/
118-
public function getCurrentDirInBrowser () : String
119-
{
123+
public function getCurrentDirInBrowser () : String {
120124
var file = Tools.getOutputFileName();
121125
var scripts = Browser.document.getElementsByTagName('script');
122126

@@ -132,22 +136,37 @@ class JStack {
132136
return path.dir;
133137
}
134138

139+
/**
140+
* Actual handler used for uncaught exceptions
141+
* @param e -
142+
*/
143+
static function _uncaughtExceptionHandler (e:Error) {
144+
var error = uncaughtExceptionHandler(e);
145+
if(error != null && error.length > 0) {
146+
untyped __js__('console.log({0})', error);
147+
}
148+
}
149+
150+
/**
151+
Check if currently run on nodejs.
152+
**/
153+
static function isNode () : Bool {
154+
return untyped __js__("typeof window == 'undefined'");
155+
}
135156
}
136157

137158

138159
/**
139160
* Represents call stack position
140161
*/
141162
@:keep
142-
private class StackPos
143-
{
163+
class StackPos {
144164
/** JS side */
145-
private var js : Dynamic;
165+
var js : Dynamic;
146166
/** HX side */
147-
private var hx : Dynamic;
167+
var hx : Dynamic;
148168

149-
public function new (js:Dynamic, hx:SourcePos)
150-
{
169+
public function new (js:Dynamic, hx:SourcePos) {
151170
this.js = js;
152171
this.hx = hx;
153172
}

0 commit comments

Comments
 (0)