diff --git a/index.js b/index.js index ec5dd13..0c311db 100644 --- a/index.js +++ b/index.js @@ -103,11 +103,19 @@ const _stackWithCauses = (err, seen) => { const cause = getErrorCause(err); - // TODO: Follow up in https://github.com/nodejs/node/issues/38725#issuecomment-920309092 on how to log stuff - if (cause) { seen.add(err); return (stack + '\ncaused by: ' + _stackWithCauses(cause, seen)); + } else if (Object.prototype.hasOwnProperty.call(err, 'cause')) { + /** @type {string} */ + let stringified; + try { + // @ts-ignore + stringified = JSON.stringify(err.cause); + } catch { + stringified = ''; + } + return (stack + '\ncaused by: ' + stringified); } else { return stack; } diff --git a/test/stack.spec.js b/test/stack.spec.js index acbf6b3..8613a81 100644 --- a/test/stack.spec.js +++ b/test/stack.spec.js @@ -92,4 +92,54 @@ describe('stackWithCauses()', () => { const result = stackWithCauses(err); result.should.equal('xyz789\ncaused by: abc123\ncaused by: xyz789\ncauses have become circular...'); }); + + describe('should append non-Error causes to the end of the cause trail', () => { + it('for string causes', () => { + const err = new ErrorWithCause('foo', { cause: 'string cause' }); + err.stack = 'xyz789'; + stackWithCauses(err).should.equal('xyz789\ncaused by: "string cause"'); + }); + + it('for number causes', () => { + const err = new ErrorWithCause('foo', { cause: 123 }); + err.stack = 'xyz789'; + stackWithCauses(err).should.equal('xyz789\ncaused by: 123'); + }); + + it('for non-Error object causes', () => { + const err = new ErrorWithCause('foo', { cause: { name: 'TypeError', message: 'foo' } }); + err.stack = 'xyz789'; + stackWithCauses(err).should.equal('xyz789\ncaused by: {"name":"TypeError","message":"foo"}'); + }); + + it('should not throw for circular non-Error object causes', () => { + const firstCause = { first: true }; + const secondCause = { second: true, firstCause }; + + // @ts-ignore + firstCause.secondCause = secondCause; + + const err = new ErrorWithCause('foo', { cause: firstCause }); + err.stack = 'xyz789'; + stackWithCauses(err).should.equal('xyz789\ncaused by: '); + }); + + // Copied from https://github.com/nodejs/node/blob/5e6f9c3e346b196ab299a3fce485d7aa5fbf3802/test/parallel/test-util-inspect.js#L663-L677 + it('for falsy causes', () => { + const falsyCause1 = new ErrorWithCause('', { cause: false }); + delete falsyCause1.stack; + + // @ts-ignore + // eslint-disable-next-line unicorn/no-null + const falsyCause2 = new ErrorWithCause(undefined, { cause: null }); + falsyCause2.stack = ''; + + const undefinedCause = new ErrorWithCause('', { cause: undefined }); + undefinedCause.stack = ''; + + stackWithCauses(falsyCause1).should.equal('\ncaused by: false'); + stackWithCauses(falsyCause2).should.equal('\ncaused by: null'); + stackWithCauses(undefinedCause).should.equal('\ncaused by: undefined'); + }); + }); });