Skip to content
This repository has been archived by the owner on Jan 31, 2025. It is now read-only.

Commit

Permalink
Merge pull request #10 from sumeetkakkar/fix/wrapped-error
Browse files Browse the repository at this point in the history
fix: handle case where Error class is wrapped
  • Loading branch information
sumeetkakkar authored Mar 9, 2022
2 parents 96474d0 + fe2e070 commit a97a40e
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.idea
node_modules
*.log
package-lock.json
.vscode
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Unreleased

- fix: handle case where Error class is wrapped #10
10 changes: 8 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
'use strict';
var assert = require('assert');


/**
Expand All @@ -8,7 +9,7 @@
* @see https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
*/
module.exports = function (depth) {
var pst, stack, file, frame;
var pst, stack, file, frame, startIdx;

pst = Error.prepareStackTrace;
Error.prepareStackTrace = function (_, stack) {
Expand All @@ -17,8 +18,13 @@ module.exports = function (depth) {
};

stack = (new Error()).stack;
// Handle case where error object is wrapped by say babel. Try to find current file's index first.
startIdx = 0;
while(startIdx < stack.length && stack[startIdx].getFileName() !== __filename) startIdx++;
assert(startIdx < stack.length, 'Unexpected: unable to find caller/index.js in the stack');

depth = !depth || isNaN(depth) ? 1 : (depth > stack.length - 2 ? stack.length - 2 : depth);
stack = stack.slice(depth + 1);
stack = stack.slice(startIdx + depth + 1);

do {
frame = stack.shift();
Expand Down
15 changes: 15 additions & 0 deletions test/caller.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ test('caller', function (t) {
t.end();
});

t.test('determine caller when Error is wrapped', function (t) {
var restoreError, actual, expected;

restoreError = require('./fixtures/wrapped-error')();
try {
actual = caller();
} finally {
restoreError();
}
expected = require.resolve('tape/lib/test');

t.equal(actual, expected);
t.end();
});

t.test('determine caller with depth cap', function (t) {
var callee, actual, expected;

Expand Down
57 changes: 57 additions & 0 deletions test/fixtures/wrapped-error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
function copyConstructorProperties(target, source) {
const keys = Object.getOwnPropertyNames(source);
for (let i = 0; i < keys.length; i++) {
var key = keys[i];
if (!target.hasOwnProperty(key)) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
}
}
}
// inspired from https://github.com/zloirock/core-js/blob/master/packages/core-js/internals/wrap-error-constructor-with-cause.js
function wrapErrorConstructor(ERROR_NAME, wrapper) {
const clearErrorStack = (function () {
const TEST = (function (arg) { return String(Error(arg).stack); })('zxcasd');
const V8_OR_CHAKRA_STACK_ENTRY = /\n\s*at [^:]*:[^\n]*/;
const IS_V8_OR_CHAKRA_STACK = V8_OR_CHAKRA_STACK_ENTRY.test(TEST);
return function clearErrorStackInner(stack, dropEntries) {
if (IS_V8_OR_CHAKRA_STACK && typeof stack == 'string') {
while (dropEntries--) stack = stack.replace(V8_OR_CHAKRA_STACK_ENTRY, '');
} return stack;
};
})();

const OriginalError = globalThis[ERROR_NAME];

const OriginalErrorPrototype = OriginalError.prototype;

const WrappedError = wrapper(function (a) {
const message = String(a);
const result = new OriginalError();
if (message !== undefined) Object.defineProperty(result, 'message', { value: message, enumerable: false, configurable: true, writable: true });
Object.defineProperty(result, 'stack', { value: clearErrorStack(result.stack, 2), enumerable: false, configurable: true, writable: true });
// if (this && OriginalErrorPrototype.isPrototypeOf(this)) {
// // inheritIfRequired(result, this, WrappedError);
// Object.setPrototypeOf(result, this.constructor); //??
// }
return result;
});

WrappedError.prototype = OriginalErrorPrototype;

// Copy ownKeys from OriginalError to WrappedError
copyConstructorProperties(WrappedError, OriginalError);

globalThis[ERROR_NAME] = WrappedError;
};

module.exports = function wrapError() {
const ErrorClassName = 'Error';
const OriginalError = globalThis[ErrorClassName];
wrapErrorConstructor(ErrorClassName, function (init) {
return function Error(message) { return init.apply(this, arguments); };
});

return function restore() {
globalThis[ErrorClassName] = OriginalError;
};
};

0 comments on commit a97a40e

Please sign in to comment.