diff --git a/index.js b/index.js index c16542d..675053a 100644 --- a/index.js +++ b/index.js @@ -68,6 +68,17 @@ const destroyCircular = ({ return toJSON(from); } + const destroyLocal = value => destroyCircular({ + from: value, + seen: [...seen], + // eslint-disable-next-line unicorn/error-message + to_: isErrorLike(value) ? new Error() : undefined, + forceEnumerable, + maxDepth, + depth, + useToJSON, + }); + for (const [key, value] of Object.entries(from)) { // eslint-disable-next-line node/prefer-global/buffer if (typeof Buffer === 'function' && Buffer.isBuffer(value)) { @@ -92,15 +103,8 @@ const destroyCircular = ({ if (!seen.includes(from[key])) { depth++; + to[key] = destroyLocal(from[key]); - to[key] = destroyCircular({ - from: from[key], - seen: [...seen], - forceEnumerable, - maxDepth, - depth, - useToJSON, - }); continue; } @@ -110,7 +114,7 @@ const destroyCircular = ({ for (const {property, enumerable} of commonProperties) { if (typeof from[property] !== 'undefined' && from[property] !== null) { Object.defineProperty(to, property, { - value: from[property], + value: isErrorLike(from[property]) ? destroyLocal(from[property]) : from[property], enumerable: forceEnumerable ? true : enumerable, configurable: true, writable: true, @@ -155,15 +159,14 @@ export function deserializeError(value, options = {}) { } if (typeof value === 'object' && value !== null && !Array.isArray(value)) { - const newError = new Error(); // eslint-disable-line unicorn/error-message - destroyCircular({ + return destroyCircular({ from: value, seen: [], - to_: newError, + // eslint-disable-next-line unicorn/error-message + to_: new Error(), maxDepth, depth: 0, }); - return newError; } return new NonError(value); diff --git a/test.js b/test.js index 51ccad6..6268288 100644 --- a/test.js +++ b/test.js @@ -200,26 +200,30 @@ test('should deserialize plain object', t => { t.is(deserialized.code, 'code'); }); -test('should deserialize the cause property', t => { - const object = { - message: 'error message', - stack: 'at :1:13', - name: 'name', - code: 'code', - cause: { - message: 'source error message', - stack: 'at :3:14', +for (const property of ['cause', 'any']) { + // `cause` is treated differently from other properties in the code + test(`should deserialize errors on ${property} property`, t => { + const object = { + message: 'error message', + stack: 'at :1:13', name: 'name', code: 'code', - }, - }; - - const {cause} = deserializeError(object); - t.is(cause.message, 'source error message'); - t.is(cause.stack, 'at :3:14'); - t.is(cause.name, 'name'); - t.is(cause.code, 'code'); -}); + [property]: { + message: 'source error message', + stack: 'at :3:14', + name: 'name', + code: 'code', + }, + }; + + const {[property]: nested} = deserializeError(object); + t.true(nested instanceof Error); + t.is(nested.message, 'source error message'); + t.is(nested.stack, 'at :3:14'); + t.is(nested.name, 'name'); + t.is(nested.code, 'code'); + }); +} test('deserialized name, stack, cause an message should not be enumerable, other props should be', t => { const object = {