From f698ea0e8667d84a95530ab7cf6fb60322bc23f4 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Wed, 17 Nov 2021 13:34:37 +0700 Subject: [PATCH] Require Node.js 12.20 and move to ESM --- .github/funding.yml | 3 -- .github/workflows/main.yml | 4 +-- index.d.ts | 24 +++++++-------- index.js | 61 +++++++++++++++++++++----------------- index.test-d.ts | 2 +- package.json | 12 ++++---- readme.md | 38 +++++++++++++----------- test.js | 31 +++++++++---------- 8 files changed, 92 insertions(+), 83 deletions(-) delete mode 100644 .github/funding.yml diff --git a/.github/funding.yml b/.github/funding.yml deleted file mode 100644 index 1a630e9..0000000 --- a/.github/funding.yml +++ /dev/null @@ -1,3 +0,0 @@ -github: sindresorhus -open_collective: sindresorhus -custom: https://sindresorhus.com/donate diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c1870cf..3b8aa86 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,12 +10,12 @@ jobs: fail-fast: false matrix: node-version: + - 16 - 14 - 12 - - 10 steps: - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - run: npm install diff --git a/index.d.ts b/index.d.ts index 5e9b37c..bfbbfdc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -33,12 +33,12 @@ export interface Options { /** Serialize an `Error` object into a plain object. -Non-error values are passed through. -Custom properties are preserved. -Buffer properties are replaced with `[object Buffer]`. -Circular references are handled. -If the input object has a `.toJSON()` method, then it's called instead of serializing the object's properties. -It's up to `.toJSON()` implementation to handle circular references and enumerability of the properties. +- Non-error values are passed through. +- Custom properties are preserved. +- Buffer properties are replaced with `[object Buffer]`. +- Circular references are handled. +- If the input object has a `.toJSON()` method, then it's called instead of serializing the object's properties. +- It's up to `.toJSON()` implementation to handle circular references and enumerability of the properties. @example ``` @@ -98,12 +98,12 @@ export function serializeError(error: ErrorType, options?: Options): /** 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). -Enumerable properties are kept enumerable (all properties besides the non-enumerable ones). -Circular references are handled. +- `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). +- Enumerable properties are kept enumerable (all properties besides the non-enumerable ones). +- Circular references are handled. @example ``` diff --git a/index.js b/index.js index a6ca592..d794d7d 100644 --- a/index.js +++ b/index.js @@ -1,12 +1,11 @@ -'use strict'; - -class NonError extends Error { +export class NonError extends Error { constructor(message) { super(NonError._prepareSuperMessage(message)); + Object.defineProperty(this, 'name', { value: 'NonError', configurable: true, - writable: true + writable: true, }); if (Error.captureStackTrace) { @@ -24,18 +23,30 @@ class NonError extends Error { } const commonProperties = [ - {property: 'name', enumerable: false}, - {property: 'message', enumerable: false}, - {property: 'stack', enumerable: false}, - {property: 'code', enumerable: true} + { + property: 'name', + enumerable: false, + }, + { + property: 'message', + enumerable: false, + }, + { + property: 'stack', + enumerable: false, + }, + { + property: 'code', + enumerable: true, + }, ]; -const isCalled = Symbol('.toJSON called'); +const toJsonWasCalled = Symbol('.toJSON was called'); const toJSON = from => { - from[isCalled] = true; + from[toJsonWasCalled] = true; const json = from.toJSON(); - delete from[isCalled]; + delete from[toJsonWasCalled]; return json; }; @@ -45,7 +56,7 @@ const destroyCircular = ({ to_, forceEnumerable, maxDepth, - depth + depth, }) => { const to = to_ || (Array.isArray(from) ? [] : {}); @@ -55,11 +66,12 @@ const destroyCircular = ({ return to; } - if (typeof from.toJSON === 'function' && from[isCalled] !== true) { + if (typeof from.toJSON === 'function' && from[toJsonWasCalled] !== true) { return toJSON(from); } for (const [key, value] of Object.entries(from)) { + // eslint-disable-next-line node/prefer-global/buffer if (typeof Buffer === 'function' && Buffer.isBuffer(value)) { to[key] = '[object Buffer]'; continue; @@ -79,10 +91,10 @@ const destroyCircular = ({ to[key] = destroyCircular({ from: from[key], - seen: seen.slice(), + seen: [...seen], forceEnumerable, maxDepth, - depth + depth, }); continue; } @@ -96,7 +108,7 @@ const destroyCircular = ({ value: from[property], enumerable: forceEnumerable ? true : enumerable, configurable: true, - writable: true + writable: true, }); } } @@ -104,7 +116,7 @@ const destroyCircular = ({ return to; }; -const serializeError = (value, options = {}) => { +export function serializeError(value, options = {}) { const {maxDepth = Number.POSITIVE_INFINITY} = options; if (typeof value === 'object' && value !== null) { @@ -113,7 +125,7 @@ const serializeError = (value, options = {}) => { seen: [], forceEnumerable: true, maxDepth, - depth: 0 + depth: 0, }); } @@ -124,9 +136,9 @@ const serializeError = (value, options = {}) => { } return value; -}; +} -const deserializeError = (value, options = {}) => { +export function deserializeError(value, options = {}) { const {maxDepth = Number.POSITIVE_INFINITY} = options; if (value instanceof Error) { @@ -140,15 +152,10 @@ const deserializeError = (value, options = {}) => { seen: [], to_: newError, maxDepth, - depth: 0 + depth: 0, }); return newError; } return new NonError(value); -}; - -module.exports = { - serializeError, - deserializeError -}; +} diff --git a/index.test-d.ts b/index.test-d.ts index b9c544e..ea7412e 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -11,5 +11,5 @@ expectType(deserializeError({ message: 'error message', stack: 'at :1:13', name: 'name', - code: 'code' + code: 'code', })); diff --git a/package.json b/package.json index 6d04cad..c6675bf 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,10 @@ "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, + "type": "module", + "exports": "./index.js", "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "scripts": { "test": "xo && ava && tsd" @@ -31,11 +33,11 @@ "deserialize" ], "dependencies": { - "type-fest": "^0.20.2" + "type-fest": "^2.5.3" }, "devDependencies": { - "ava": "^2.4.0", - "tsd": "^0.14.0", - "xo": "^0.37.1" + "ava": "^3.15.0", + "tsd": "^0.18.0", + "xo": "^0.46.4" } } diff --git a/readme.md b/readme.md index e7abe24..4c9a4ea 100644 --- a/readme.md +++ b/readme.md @@ -6,14 +6,14 @@ Useful if you for example need to `JSON.stringify()` or `process.send()` the err ## Install -``` -$ npm install serialize-error +```sh +npm install serialize-error ``` ## Usage ```js -const {serializeError, deserializeError} = require('serialize-error'); +import {serializeError, deserializeError} from 'serialize-error'; const error = new Error('🦄'); @@ -39,19 +39,19 @@ Type: `Error | unknown` Serialize an `Error` object into a plain object. -Non-error values are passed through. -Custom properties are preserved. -Non-enumerable properties are kept non-enumerable (name, message, stack). -Enumerable properties are kept enumerable (all properties besides the non-enumerable ones). -Buffer properties are replaced with `[object Buffer]`. -Circular references are handled. -If the input object has a `.toJSON()` method, then it's called instead of serializing the object's properties. -It's up to `.toJSON()` implementation to handle circular references and enumerability of the properties. +- Non-error values are passed through. +- Custom properties are preserved. +- Non-enumerable properties are kept non-enumerable (name, message, stack). +- Enumerable properties are kept enumerable (all properties besides the non-enumerable ones). +- Buffer properties are replaced with `[object Buffer]`. +- Circular references are handled. +- If the input object has a `.toJSON()` method, then it's called instead of serializing the object's properties. +- It's up to `.toJSON()` implementation to handle circular references and enumerability of the properties. `.toJSON` examples: ```js -const {serializeError} = require('serialize-error'); +import {serializeError} from 'serialize-error'; class ErrorWithDate extends Error { constructor() { @@ -67,7 +67,7 @@ serializeError(error); ``` ```js -const {serializeError} = require('serialize-error'); +import {serializeError} from 'serialize-error'; class ErrorWithToJSON extends Error { constructor() { @@ -92,10 +92,12 @@ Type: `{[key: string]: unknown} | unknown` 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. -Circular references are handled. +- `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). +- Enumerable properties are kept enumerable (all properties besides the non-enumerable ones). +- Circular references are handled. ### options @@ -109,7 +111,7 @@ Default: `Number.POSITIVE_INFINITY` The maximum depth of properties to preserve when serializing/deserializing. ```js -const {serializeError} = require('serialize-error'); +import {serializeError} from 'serialize-error'; const error = new Error('🦄'); error.one = {two: {three: {}}}; diff --git a/test.js b/test.js index c1d8111..0e45c35 100644 --- a/test.js +++ b/test.js @@ -1,3 +1,4 @@ +import {Buffer} from 'node:buffer'; import test from 'ava'; import {serializeError, deserializeError} from './index.js'; @@ -108,7 +109,7 @@ test('should not access deep non-enumerable properties', t => { enumerable: false, get() { throw new Error('some other error'); - } + }, }); error.object = object; t.notThrows(() => serializeError(error)); @@ -157,7 +158,7 @@ test('should deserialize error', t => { test('should deserialize and preserve existing properties', t => { const deserialized = deserializeError({ message: 'foo', - customProperty: true + customProperty: true, }); t.true(deserialized instanceof Error); t.is(deserialized.message, 'foo'); @@ -169,7 +170,7 @@ test('should deserialize plain object', t => { message: 'error message', stack: 'at :1:13', name: 'name', - code: 'code' + code: 'code', }; const deserialized = deserializeError(object); @@ -184,7 +185,7 @@ test('deserialized name, stack and message should not be enumerable, other props const object = { message: 'error message', stack: 'at :1:13', - name: 'name' + name: 'name', }; const nonEnumerableProps = Object.keys(object); @@ -193,7 +194,7 @@ test('deserialized name, stack and message should not be enumerable, other props path: './path', errno: 1, syscall: 'syscall', - randomProperty: 'random' + randomProperty: 'random', }; const enumerableProps = Object.keys(enumerables); @@ -217,9 +218,9 @@ test('should deserialize properties up to `Options.maxDepth` levels deep', t => stack: error.stack, one: { two: { - three: {} - } - } + three: {}, + }, + }, }; const levelZero = deserializeError(object, {maxDepth: 0}); @@ -261,7 +262,7 @@ test('should serialize custom error with `.toJSON`', t => { toJSON() { return { message: this.message, - amount: `$${this.value}` + amount: `$${this.value}`, }; } } @@ -269,7 +270,7 @@ test('should serialize custom error with `.toJSON`', t => { const serialized = serializeError(error); t.deepEqual(serialized, { message: 'foo', - amount: '$10' + amount: '$10', }); t.true(serialized.stack === undefined); }); @@ -286,9 +287,9 @@ test('should serialize custom error with a property having `.toJSON`', t => { amount: 20, toJSON() { return { - amount: `$${this.amount}` + amount: `$${this.amount}`, }; - } + }, }; const error = new CustomError(value); const serialized = serializeError(error); @@ -297,8 +298,8 @@ test('should serialize custom error with a property having `.toJSON`', t => { message: 'foo', name: 'CustomError', value: { - amount: '$20' - } + amount: '$20', + }, }); t.not(stack, undefined); }); @@ -321,7 +322,7 @@ test('should serialize custom error with `.toJSON` defined with `serializeError` t.deepEqual(rest, { message: 'foo', name: 'CustomError', - value: 30 + value: 30, }); t.not(stack, undefined); });