Skip to content

Commit

Permalink
Add support for cause property (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
fregante authored and sindresorhus committed Apr 1, 2022
1 parent 855fe3d commit 7e8be51
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 14 deletions.
2 changes: 1 addition & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Deserialize a plain object or any value into an `Error` object.
- `Error` objects are passed through.
- Non-error values are wrapped in a `NonError` error.
- Custom properties are preserved.
- Non-enumerable properties are kept non-enumerable (name, message, stack).
- Non-enumerable properties are kept non-enumerable (name, message, stack, cause).
- Enumerable properties are kept enumerable (all properties besides the non-enumerable ones).
- Circular references are handled.
Expand Down
6 changes: 5 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ const commonProperties = [
property: 'code',
enumerable: true,
},
{
property: 'cause',
enumerable: false,
},
];

const toJsonWasCalled = Symbol('.toJSON was called');
Expand Down Expand Up @@ -101,7 +105,7 @@ const destroyCircular = ({
}

for (const {property, enumerable} of commonProperties) {
if (typeof from[property] === 'string') {
if (typeof from[property] !== 'undefined' && from[property] !== null) {
Object.defineProperty(to, property, {
value: from[property],
enumerable: forceEnumerable ? true : enumerable,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"convert",
"process",
"send",
"cause",
"deserialize"
],
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Deserialize a plain object or any value into an `Error` object.
- `Error` objects are passed through.
- Non-error values are wrapped in a `NonError` error.
- Custom properties are preserved.
- Non-enumerable properties are kept non-enumerable (name, message, stack).
- Non-enumerable properties are kept non-enumerable (name, message, stack, cause).
- Enumerable properties are kept enumerable (all properties besides the non-enumerable ones).
- Circular references are handled.

Expand Down
51 changes: 40 additions & 11 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@ test('should serialize nested errors', t => {
t.is(serialized.innerError.message, 'inner error');
});

test('should serialize the cause property', t => {
const error = new Error('outer error');
error.cause = new Error('inner error');

const serialized = serializeError(error);
t.is(serialized.message, 'outer error');
t.is(serialized.cause.message, 'inner error');
});

test('should handle top-level null values', t => {
const serialized = serializeError(null);
t.is(serialized, null);
Expand Down Expand Up @@ -191,13 +200,38 @@ test('should deserialize plain object', t => {
t.is(deserialized.code, 'code');
});

test('deserialized name, stack and message should not be enumerable, other props should be', t => {
test('should deserialize the cause property', t => {
const object = {
message: 'error message',
stack: 'at <anonymous>:1:13',
name: 'name',
code: 'code',
cause: {
message: 'source error message',
stack: 'at <anonymous>:3:14',
name: 'name',
code: 'code',
},
};

const {cause} = deserializeError(object);
t.is(cause.message, 'source error message');
t.is(cause.stack, 'at <anonymous>:3:14');
t.is(cause.name, 'name');
t.is(cause.code, 'code');
});

test('deserialized name, stack, cause an message should not be enumerable, other props should be', t => {
const object = {
message: 'error message',
stack: 'at <anonymous>:1:13',
name: 'name',
cause: {
message: 'cause error message',
stack: 'at <anonymous>:4:20',
name: 'name',
},
};
const nonEnumerableProps = Object.keys(object);

const enumerables = {
code: 'code',
Expand All @@ -206,18 +240,13 @@ test('deserialized name, stack and message should not be enumerable, other props
syscall: 'syscall',
randomProperty: 'random',
};
const enumerableProps = Object.keys(enumerables);

const deserialized = deserializeError({...object, ...enumerables});
const deserializedEnumerableProps = Object.keys(deserialized);

for (const prop of nonEnumerableProps) {
t.false(deserializedEnumerableProps.includes(prop));
}

for (const prop of enumerableProps) {
t.true(deserializedEnumerableProps.includes(prop));
}
t.deepEqual(
Object.keys(enumerables),
Object.keys(deserialized),
);
});

test('should deserialize properties up to `Options.maxDepth` levels deep', t => {
Expand Down

0 comments on commit 7e8be51

Please sign in to comment.