[Pkg-javascript-commits] [node-stack-utils] 25/67: Support long stack traces

Bastien Roucariès rouca at moszumanska.debian.org
Thu Sep 7 09:53:03 UTC 2017


This is an automated email from the git hooks/post-receive script.

rouca pushed a commit to branch master
in repository node-stack-utils.

commit ac75d4703abb94b1fbce6ed76c78d6d848397645
Author: James Talmage <james at talmage.io>
Date:   Tue Jan 5 15:26:46 2016 -0500

    Support long stack traces
---
 index.js                                       |  40 +++++--
 package.json                                   |  11 +-
 test/_utils.js                                 |  10 ++
 {fixtures => test/fixtures}/capture-fixture.js |   0
 test/fixtures/internal-error.js                |  18 ++++
 test/fixtures/internal-then.js                 |  17 +++
 test/fixtures/long-stack-traces.js             |  16 +++
 test/fixtures/nested-errors.js                 |  49 +++++++++
 test/fixtures/produce-long-stack-traces.js     |  50 +++++++++
 test/long-stack-traces.js                      | 138 +++++++++++++++++++++++++
 test.js => test/test.js                        |  10 +-
 11 files changed, 338 insertions(+), 21 deletions(-)

diff --git a/index.js b/index.js
index 2caf0d5..f97cd87 100644
--- a/index.js
+++ b/index.js
@@ -34,7 +34,12 @@ StackUtils.prototype.clean = function (stack) {
 		stack = stack.slice(1);
 	}
 
-	stack = stack.map(function (st) {
+	var outdent = false;
+	var lastNonAtLine = null;
+	var result = [];
+
+	stack.forEach(function (st1) {
+		var st = st1;
 		var isInternal = this._internals.some(function (internal) {
 			return internal.test(st);
 		});
@@ -43,13 +48,36 @@ StackUtils.prototype.clean = function (stack) {
 			return null;
 		}
 
-		return st.trim()
-			.replace(/^\s*at /, '')
+		var isAtLine = /^\s*at /.test(st);
+
+		if (outdent) {
+			st = st.replace(/\s+$/, '').replace(/^(\s+)at /, '$1');
+		} else {
+			st = st.trim();
+			if (isAtLine) {
+				st = st.substring(3);
+			}
+		}
+
+		st = st
 			.replace(/\\/g, '/')
 			.replace(this._cwd + '/', '');
-	}, this).filter(function (st) {
-		return st;
-	}).join('\n').trim();
+
+		if (st) {
+			if (isAtLine) {
+				if (lastNonAtLine) {
+					result.push(lastNonAtLine);
+					lastNonAtLine = null;
+				}
+				result.push(st);
+			} else {
+				outdent = true;
+				lastNonAtLine = st;
+			}
+		}
+	}, this);
+
+	stack = result.join('\n').trim();
 
 	if (stack) {
 		return stack + '\n';
diff --git a/package.json b/package.json
index 6be2ac0..801163f 100644
--- a/package.json
+++ b/package.json
@@ -22,19 +22,16 @@
   "keywords": [
     ""
   ],
-  "config": {
-    "nyc": {
-      "exclude": [
-        "fixtures/*"
-      ]
-    }
-  },
   "dependencies": {},
   "devDependencies": {
     "ava": "^0.8.0",
+    "bluebird": "^3.1.1",
     "coveralls": "^2.11.6",
     "flatten": "0.0.1",
+    "nested-error-stacks": "^1.0.2",
     "nyc": "^5.2.0",
+    "pify": "^2.3.0",
+    "q": "^1.4.1",
     "xo": "^0.12.1"
   }
 }
diff --git a/test/_utils.js b/test/_utils.js
new file mode 100644
index 0000000..e9e3ff7
--- /dev/null
+++ b/test/_utils.js
@@ -0,0 +1,10 @@
+var flatten = require('flatten');
+var path = require('path');
+
+module.exports.join = join;
+module.exports.fixtureDir = path.join(__dirname, 'fixtures');
+
+function join() {
+	var args = Array.prototype.slice.call(arguments);
+	return flatten(args).join('\n') + '\n';
+}
diff --git a/fixtures/capture-fixture.js b/test/fixtures/capture-fixture.js
similarity index 100%
rename from fixtures/capture-fixture.js
rename to test/fixtures/capture-fixture.js
diff --git a/test/fixtures/internal-error.js b/test/fixtures/internal-error.js
new file mode 100644
index 0000000..d524c2f
--- /dev/null
+++ b/test/fixtures/internal-error.js
@@ -0,0 +1,18 @@
+'use strict';
+var NestedError = require('nested-error-stacks');
+var util = require('util');
+
+function InternalError(message, nested) {
+	NestedError.call(this, message, nested);
+}
+
+util.inherits(InternalError, NestedError);
+InternalError.prototype.name = 'InternalError';
+
+module.exports = function (cb, err) {
+	setTimeout(bound.bind(null, cb, err), 0);
+};
+
+function bound(cb, err) {
+	cb(new InternalError('internal' + (err ? ': ' + err.message : ''), err));
+}
diff --git a/test/fixtures/internal-then.js b/test/fixtures/internal-then.js
new file mode 100644
index 0000000..6b20a49
--- /dev/null
+++ b/test/fixtures/internal-then.js
@@ -0,0 +1,17 @@
+
+//var p = global.InternalPromise.resolve().then(function () {});
+
+module.exports = function internalLibraryOuterFn(then) {
+	return global.InternalPromise.resolve().then(function internalLibraryInnerFn() {
+		return global.InternalPromise.resolve().then(then);
+	});
+};
+
+module.exports.reject = function internalLibraryOuterReject() {
+	return global.InternalPromise.resolve().then(function internalLibraryInnerReject() {
+		return global.InternalPromise.reject(new Error('inner')).catch(function (e) {
+			return e.stack;
+		});
+	});
+};
+
diff --git a/test/fixtures/long-stack-traces.js b/test/fixtures/long-stack-traces.js
new file mode 100644
index 0000000..a79cbbc
--- /dev/null
+++ b/test/fixtures/long-stack-traces.js
@@ -0,0 +1,16 @@
+'use strict';
+
+var Q = require('q');
+Q.longStackSupport = true;
+global.InternalPromise = Q;
+module.exports.q = require('./produce-long-stack-traces');
+
+var longStackTracePath = require.resolve('./produce-long-stack-traces');
+var internalThen = require.resolve('./internal-then');
+delete require.cache[longStackTracePath];
+delete require.cache[internalThen];
+
+var bluebird = require('bluebird');
+bluebird.config({longStackTraces: true});
+global.InternalPromise = bluebird;
+module.exports.bluebird = require('./produce-long-stack-traces');
diff --git a/test/fixtures/nested-errors.js b/test/fixtures/nested-errors.js
new file mode 100644
index 0000000..b43a37e
--- /dev/null
+++ b/test/fixtures/nested-errors.js
@@ -0,0 +1,49 @@
+'use strict';
+
+var NestedError = require('nested-error-stacks');
+var util = require('util');
+var internal = require('./internal-error');
+
+function foo(cb) {
+	bar(function nested(err) {
+		cb(new FooError('foo' + err.message, err));
+	});
+}
+
+function bar(cb) {
+	internal(function moreNested(err) {
+		cb(new BarError('bar: ' + err.message, err));
+	});
+}
+
+function FooError(message, nested) {
+	NestedError.call(this, message, nested);
+}
+
+util.inherits(FooError, NestedError);
+FooError.prototype.name = 'FooError';
+
+function BarError(message, nested) {
+	NestedError.call(this, message, nested);
+}
+
+util.inherits(BarError, NestedError);
+BarError.prototype.name = 'BarError';
+
+module.exports.top = function(cb) {
+	internal(function (err) {
+		cb(err.stack);
+	}, new Error('baz'));
+};
+
+module.exports.middle = function (cb) {
+	internal(function (err) {
+		cb(new FooError('foo', err).stack);
+	}, new Error('bar'));
+};
+
+module.exports.bottom = function (cb) {
+	foo(function (err){
+		cb(err.stack);
+	});
+};
diff --git a/test/fixtures/produce-long-stack-traces.js b/test/fixtures/produce-long-stack-traces.js
new file mode 100644
index 0000000..32ae07e
--- /dev/null
+++ b/test/fixtures/produce-long-stack-traces.js
@@ -0,0 +1,50 @@
+'use strict';
+
+var Promise = global.InternalPromise;
+var internalThen = require('./internal-then');
+
+module.exports = Promise.resolve().then(function outer() {
+	return Promise.resolve().then(function inner() {
+		return Promise.resolve().then(function evenMoreInner() {
+			return Promise.resolve().then(function mostInner() {
+				a.b.c.d()
+			}).catch(function catcher(e) {
+				return e.stack;
+			});
+		});
+	});
+});
+
+module.exports.middle = Promise.resolve().then(function outer() {
+	return Promise.resolve().then(function inner() {
+		return internalThen(function evenMoreInner() {
+			return Promise.resolve().then(function mostInner() {
+				a.b.c.d()
+			}).catch(function catcher(e) {
+				return e.stack;
+			});
+		});
+	});
+});
+
+module.exports.top = Promise.resolve().then(function outer() {
+	return Promise.resolve().then(function inner() {
+		return Promise.resolve().then(function evenMoreInner() {
+			return Promise.resolve().then(internalThen.reject);
+		});
+	});
+});
+
+module.exports.bottom = new Promise(function (resolve) {
+	setTimeout(internalThen.bind(null, function outer() {
+		return Promise.resolve().then(function inner() {
+			return Promise.resolve().then(function evenMoreInner() {
+				return Promise.resolve().then(function mostInner() {
+					a.b.c.d()
+				}).catch(function catcher(e) {
+					resolve(e.stack);
+				});
+			});
+		});
+	}),0);
+});
diff --git a/test/long-stack-traces.js b/test/long-stack-traces.js
new file mode 100644
index 0000000..5b62d11
--- /dev/null
+++ b/test/long-stack-traces.js
@@ -0,0 +1,138 @@
+import test from 'ava';
+import StackUtils from '../';
+import longStackTraces from './fixtures/long-stack-traces';
+import pify from 'pify';
+const nestedErrors = pify(require('./fixtures/nested-errors'), Promise);
+
+import {join, fixtureDir} from './_utils';
+
+function internals() {
+	return StackUtils.nodeInternals().concat([
+		/[\\\/]long-stack-traces\.js:[0-9]+:[0-9]+\)?$/,
+		/[\\\/]internal-error\.js:[0-9]+:[0-9]+\)?$/,
+		/[\\\/]internal-then\.js:[0-9]+:[0-9]+\)?$/,
+		/[\\\/]node_modules[\\\/]/,
+		// TODO: Should any of these be default internals?
+		/[\\\/]\.node-spawn-wrap-\w+-\w+[\\\/]node:[0-9]+:[0-9]+\)?$/,
+		/internal[\\\/]module\.js:[0-9]+:[0-9]+\)?$/,
+		/node\.js:[0-9]+:[0-9]+\)?$/
+	]);
+}
+
+const stackUtils = new StackUtils({internals: internals(), cwd: fixtureDir});
+
+test('indents lines after first "From previous event:"', async t => {
+	const cleanedStack = stackUtils.clean(await longStackTraces.bluebird);
+	const expected = join([
+		'mostInner (produce-long-stack-traces.js:10:5)',
+		'From previous event:',
+		'    evenMoreInner (produce-long-stack-traces.js:9:29)',
+		'From previous event:',
+		'    inner (produce-long-stack-traces.js:8:28)',
+		'From previous event:',
+		'    outer (produce-long-stack-traces.js:7:27)',
+		'From previous event:',
+		'    Object.<anonymous> (produce-long-stack-traces.js:6:36)'
+	]);
+
+	t.is(cleanedStack, expected);
+});
+
+test('removes empty "From previous event:" sections from the bottom', async t => {
+	const stack = await longStackTraces.bluebird.bottom;
+	const cleanedStack = stackUtils.clean(stack);
+
+	const expected = join([
+		'mostInner (produce-long-stack-traces.js:43:6)',
+		'From previous event:',
+		'    evenMoreInner (produce-long-stack-traces.js:42:30)',
+		'From previous event:',
+		'    inner (produce-long-stack-traces.js:41:29)',
+		'From previous event:',
+		'    outer (produce-long-stack-traces.js:40:28)'
+	]);
+
+	t.is(cleanedStack, expected);
+});
+
+test('removes empty "From previous event:" sections from the top', async t => {
+	const stack = await longStackTraces.bluebird.top;
+	const cleanedStack = stackUtils.clean(stack);
+
+	const expected = join([
+		'From previous event:',
+		'    evenMoreInner (produce-long-stack-traces.js:33:29)',
+		'From previous event:',
+		'    inner (produce-long-stack-traces.js:32:28)',
+		'From previous event:',
+		'    outer (produce-long-stack-traces.js:31:27)',
+		'From previous event:',
+		'    Object.<anonymous> (produce-long-stack-traces.js:30:40)'
+	]);
+
+	t.is(cleanedStack, expected);
+});
+
+test('removes empty "From previous event:" sections from the middle', async t => {
+	const stack = await longStackTraces.bluebird.middle;
+	const cleanedStack = stackUtils.clean(stack);
+
+	const expected = join([
+		'mostInner (produce-long-stack-traces.js:22:5)',
+		'From previous event:',
+		'    evenMoreInner (produce-long-stack-traces.js:21:29)',
+		'From previous event:',
+		'    inner (produce-long-stack-traces.js:20:10)',
+		'From previous event:',
+		'    outer (produce-long-stack-traces.js:19:27)',
+		'From previous event:',
+		'    Object.<anonymous> (produce-long-stack-traces.js:18:43)'
+	]);
+
+	t.is(cleanedStack, expected);
+});
+
+test.cb('removes empty "Caused by:" sections from the top', t => {
+	nestedErrors.top(stack => {
+		const cleanedStack = stackUtils.clean(stack);
+
+		const expected = join([
+			'Caused By: Error: baz',
+			'    Object.module.exports.top (nested-errors.js:36:5)'
+		]);
+
+		t.is(cleanedStack, expected);
+		t.end();
+	});
+});
+
+test.cb('removes empty "Caused by:" sections from the bottom', t => {
+	nestedErrors.bottom(stack => {
+		const cleanedStack = stackUtils.clean(stack);
+
+		const expected = join([
+			'nested (nested-errors.js:9:6)',
+			'moreNested (nested-errors.js:15:3)',
+			'Caused By: BarError: bar: internal',
+			'    moreNested (nested-errors.js:15:6)'
+		]);
+
+		t.is(cleanedStack, expected);
+		t.end();
+	});
+});
+
+test.cb('removes empty "Caused by:" sections from the middle', t => {
+	nestedErrors.middle(stack => {
+		const cleanedStack = stackUtils.clean(stack);
+
+		const expected = join([
+			'nested-errors.js:41:6',
+			'Caused By: Error: bar',
+			'    Object.module.exports.middle (nested-errors.js:42:5)'
+		]);
+
+		t.is(cleanedStack, expected);
+		t.end();
+	});
+});
diff --git a/test.js b/test/test.js
similarity index 97%
rename from test.js
rename to test/test.js
index 555b095..9a764e4 100644
--- a/test.js
+++ b/test/test.js
@@ -1,12 +1,11 @@
 import path from 'path';
 import test from 'ava';
-import flatten from 'flatten';
-import StackUtils from './';
+import StackUtils from '../';
 import CaptureFixture from './fixtures/capture-fixture';
+import {join, fixtureDir} from './_utils';
 
 const LinuxStack1 = join(linuxStack1(), internalStack());
 const WindowsStack1 = join(windowsStack1(), internalStack());
-const fixtureDir = path.join(__dirname, 'fixtures');
 
 const version = process.version.slice(1).split('.').map(function (val) {
 	return parseInt(val, 10);
@@ -294,11 +293,6 @@ test('parseLine: handles native errors', t => {
 	});
 });
 
-function join() {
-	var args = Array.prototype.slice.call(arguments);
-	return flatten(args).join('\n') + '\n';
-}
-
 function linuxStack1() {
 	return [
 		'Error: foo',

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-stack-utils.git



More information about the Pkg-javascript-commits mailing list