option to not install uncaught exception handler, fix for issue #3
This commit is contained in:
parent
9484e8ec05
commit
18992a7be6
12
README.md
12
README.md
@ -14,7 +14,7 @@ The following terminal commands show a stack trace in node with CoffeeScript fil
|
|||||||
|
|
||||||
$ cat > demo.coffee
|
$ cat > demo.coffee
|
||||||
|
|
||||||
require 'source-map-support'
|
require('source-map-support').install()
|
||||||
foo = ->
|
foo = ->
|
||||||
bar = -> throw new Error 'this is a demo'
|
bar = -> throw new Error 'this is a demo'
|
||||||
bar()
|
bar()
|
||||||
@ -52,7 +52,7 @@ The following terminal commands show a stack trace in node with TypeScript filen
|
|||||||
$ cat > demo.ts
|
$ cat > demo.ts
|
||||||
|
|
||||||
declare function require(name: string);
|
declare function require(name: string);
|
||||||
require('source-map-support');
|
require('source-map-support').install();
|
||||||
class Foo {
|
class Foo {
|
||||||
constructor() { this.bar(); }
|
constructor() { this.bar(); }
|
||||||
bar() { throw new Error('this is a demo'); }
|
bar() { throw new Error('this is a demo'); }
|
||||||
@ -77,6 +77,14 @@ The following terminal commands show a stack trace in node with TypeScript filen
|
|||||||
at Module.runMain (module.js:492:10)
|
at Module.runMain (module.js:492:10)
|
||||||
at process.startup.processNextTick.process._tickCallback (node.js:244:9)
|
at process.startup.processNextTick.process._tickCallback (node.js:244:9)
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
This module installs two things: a change to the `stack` property on `Error` objects and a handler for uncaught exceptions that mimics node's default exception handler (the handler can be seen in the demos above). You may want to disable the handler if you have your own uncaught exception handler. This can be done by passing an argument to the installer:
|
||||||
|
|
||||||
|
require('source-map-support').install({
|
||||||
|
handleUncaughtExceptions: false
|
||||||
|
});
|
||||||
|
|
||||||
### License
|
### License
|
||||||
|
|
||||||
This code is available under the [MIT license](http://opensource.org/licenses/MIT).
|
This code is available under the [MIT license](http://opensource.org/licenses/MIT).
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "source-map-support",
|
"name": "source-map-support",
|
||||||
"description": "Fixes stack traces for files with source maps",
|
"description": "Fixes stack traces for files with source maps",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"main": "./source-map-support.js",
|
"main": "./source-map-support.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node_modules/mocha/bin/mocha"
|
"test": "node_modules/mocha/bin/mocha"
|
||||||
|
@ -89,7 +89,7 @@ function wrapCallSite(cache, frame) {
|
|||||||
|
|
||||||
// This function is part of the V8 stack trace API, for more info see:
|
// This function is part of the V8 stack trace API, for more info see:
|
||||||
// http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
|
// http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
|
||||||
Error.prepareStackTrace = function(error, stack) {
|
function prepareStackTrace(error, stack) {
|
||||||
// Store source maps in a cache so we don't load them more than once when
|
// Store source maps in a cache so we don't load them more than once when
|
||||||
// formatting a single stack trace (don't cache them forever though in case
|
// formatting a single stack trace (don't cache them forever though in case
|
||||||
// the files change on disk and the user wants to see the updated mapping)
|
// the files change on disk and the user wants to see the updated mapping)
|
||||||
@ -97,10 +97,10 @@ Error.prepareStackTrace = function(error, stack) {
|
|||||||
return error + stack.map(function(frame) {
|
return error + stack.map(function(frame) {
|
||||||
return '\n at ' + wrapCallSite(cache, frame);
|
return '\n at ' + wrapCallSite(cache, frame);
|
||||||
}).join('');
|
}).join('');
|
||||||
};
|
}
|
||||||
|
|
||||||
// Mimic node's stack trace printing when an exception escapes the process
|
// Mimic node's stack trace printing when an exception escapes the process
|
||||||
process.on('uncaughtException', function(error) {
|
function handleUncaughtExceptions(error) {
|
||||||
if (!error || !error.stack) {
|
if (!error || !error.stack) {
|
||||||
console.log('Uncaught exception:', error);
|
console.log('Uncaught exception:', error);
|
||||||
process.exit();
|
process.exit();
|
||||||
@ -125,4 +125,24 @@ process.on('uncaughtException', function(error) {
|
|||||||
}
|
}
|
||||||
console.log(error.stack);
|
console.log(error.stack);
|
||||||
process.exit();
|
process.exit();
|
||||||
});
|
}
|
||||||
|
|
||||||
|
exports.install = function(options) {
|
||||||
|
Error.prepareStackTrace = prepareStackTrace;
|
||||||
|
|
||||||
|
// Configure options
|
||||||
|
options = options || {};
|
||||||
|
var installHandler = 'handleUncaughtExceptions' in options ?
|
||||||
|
options.handleUncaughtExceptions : true;
|
||||||
|
|
||||||
|
// Provide the option to not install the uncaught exception handler. This is
|
||||||
|
// to support other uncaught exception handlers (in test frameworks, for
|
||||||
|
// example). If this handler is not installed and there are no other uncaught
|
||||||
|
// exception handlers, uncaught exceptions will be caught by node's built-in
|
||||||
|
// exception handler and the process will still be terminated. However, the
|
||||||
|
// generated JavaScript code will be shown above the stack trace instead of
|
||||||
|
// the original source code.
|
||||||
|
if (installHandler) {
|
||||||
|
process.on('uncaughtException', handleUncaughtExceptions);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
104
test.js
104
test.js
@ -1,23 +1,22 @@
|
|||||||
require('./source-map-support');
|
require('./source-map-support').install();
|
||||||
|
|
||||||
var SourceMapGenerator = require('source-map').SourceMapGenerator;
|
var SourceMapGenerator = require('source-map').SourceMapGenerator;
|
||||||
|
var child_process = require('child_process');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
|
|
||||||
// Create a source map
|
|
||||||
var sourceMap = new SourceMapGenerator({
|
|
||||||
file: '.generated.js',
|
|
||||||
sourceRoot: '.'
|
|
||||||
});
|
|
||||||
for (var i = 1; i <= 100; i++) {
|
|
||||||
sourceMap.addMapping({
|
|
||||||
generated: { line: i, column: 1 },
|
|
||||||
original: { line: 1000 + i, column: 100 + i },
|
|
||||||
source: 'line' + i + '.js'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function compareStackTrace(source, expected) {
|
function compareStackTrace(source, expected) {
|
||||||
|
var sourceMap = new SourceMapGenerator({
|
||||||
|
file: '.generated.js',
|
||||||
|
sourceRoot: '.'
|
||||||
|
});
|
||||||
|
for (var i = 1; i <= 100; i++) {
|
||||||
|
sourceMap.addMapping({
|
||||||
|
generated: { line: i, column: 1 },
|
||||||
|
original: { line: 1000 + i, column: 100 + i },
|
||||||
|
source: 'line' + i + '.js'
|
||||||
|
});
|
||||||
|
}
|
||||||
fs.writeFileSync('.generated.js.map', sourceMap);
|
fs.writeFileSync('.generated.js.map', sourceMap);
|
||||||
fs.writeFileSync('.generated.js', 'exports.test = function() {' +
|
fs.writeFileSync('.generated.js', 'exports.test = function() {' +
|
||||||
source.join('\n') + '};//@ sourceMappingURL=.generated.js.map');
|
source.join('\n') + '};//@ sourceMappingURL=.generated.js.map');
|
||||||
@ -32,6 +31,34 @@ function compareStackTrace(source, expected) {
|
|||||||
fs.unlinkSync('.generated.js.map');
|
fs.unlinkSync('.generated.js.map');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function compareStdout(done, source, expected) {
|
||||||
|
var sourceMap = new SourceMapGenerator({
|
||||||
|
file: '.generated.js',
|
||||||
|
sourceRoot: '.'
|
||||||
|
});
|
||||||
|
sourceMap.addMapping({
|
||||||
|
generated: { line: 1, column: 1 },
|
||||||
|
original: { line: 1, column: 1 },
|
||||||
|
source: '.original.js'
|
||||||
|
});
|
||||||
|
fs.writeFileSync('.original.js', 'this is the original code');
|
||||||
|
fs.writeFileSync('.generated.js.map', sourceMap);
|
||||||
|
fs.writeFileSync('.generated.js', source.join('\n') +
|
||||||
|
'//@ sourceMappingURL=.generated.js.map');
|
||||||
|
child_process.exec('node ./.generated', function(error, stdout, stderr) {
|
||||||
|
expected = expected.join('\n');
|
||||||
|
try {
|
||||||
|
assert.equal((stdout + stderr).slice(0, expected.length), expected);
|
||||||
|
} catch (e) {
|
||||||
|
return done(e);
|
||||||
|
}
|
||||||
|
fs.unlinkSync('.generated.js');
|
||||||
|
fs.unlinkSync('.generated.js.map');
|
||||||
|
fs.unlinkSync('.original.js');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
it('normal throw', function() {
|
it('normal throw', function() {
|
||||||
compareStackTrace([
|
compareStackTrace([
|
||||||
'throw new Error("test");'
|
'throw new Error("test");'
|
||||||
@ -126,3 +153,52 @@ it('eval with sourceURL inside eval', function() {
|
|||||||
' at Object.exports.test (./line1.js:1001:101)'
|
' at Object.exports.test (./line1.js:1001:101)'
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('default options', function(done) {
|
||||||
|
compareStdout(done, [
|
||||||
|
'',
|
||||||
|
'function foo() { throw new Error("this is the error"); }',
|
||||||
|
'require("./source-map-support").install();',
|
||||||
|
'process.nextTick(foo);',
|
||||||
|
'process.nextTick(function() { process.exit(1); });'
|
||||||
|
], [
|
||||||
|
'',
|
||||||
|
'./.original.js:1',
|
||||||
|
'this is the original code',
|
||||||
|
'^',
|
||||||
|
'Error: this is the error',
|
||||||
|
' at foo (./.original.js:1:1)'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handleUncaughtExceptions is true', function(done) {
|
||||||
|
compareStdout(done, [
|
||||||
|
'',
|
||||||
|
'function foo() { throw new Error("this is the error"); }',
|
||||||
|
'require("./source-map-support").install({ handleUncaughtExceptions: true });',
|
||||||
|
'process.nextTick(foo);'
|
||||||
|
], [
|
||||||
|
'',
|
||||||
|
'./.original.js:1',
|
||||||
|
'this is the original code',
|
||||||
|
'^',
|
||||||
|
'Error: this is the error',
|
||||||
|
' at foo (./.original.js:1:1)'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handleUncaughtExceptions is false', function(done) {
|
||||||
|
compareStdout(done, [
|
||||||
|
'',
|
||||||
|
'function foo() { throw new Error("this is the error"); }',
|
||||||
|
'require("./source-map-support").install({ handleUncaughtExceptions: false });',
|
||||||
|
'process.nextTick(foo);'
|
||||||
|
], [
|
||||||
|
'',
|
||||||
|
__dirname + '/.generated.js:2',
|
||||||
|
'function foo() { throw new Error("this is the error"); }',
|
||||||
|
' ^',
|
||||||
|
'Error: this is the error',
|
||||||
|
' at foo (./.original.js:1:1)'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user