modern-errors
Advanced tools
Comparing version 5.0.0 to 5.1.0
import{setNonEnumProp}from"../../utils/descriptors.js"; | ||
import{callInstanceMethod}from"./call.js"; | ||
import{callInstanceMethod,callMixedMethod}from"./call.js"; | ||
@@ -39,2 +39,12 @@ | ||
setNonEnumProp( | ||
ErrorClass, | ||
methodName, | ||
callMixedMethod.bind(undefined,{ | ||
methodFunc, | ||
plugin, | ||
plugins, | ||
ErrorClass | ||
})); | ||
}; | ||
@@ -41,0 +51,0 @@ |
@@ -20,2 +20,23 @@ import{getMethodOpts}from"../../options/method.js"; | ||
return callMethod({methodFunc,plugin,plugins,error,args}); | ||
}; | ||
export const callMixedMethod=function( | ||
{methodFunc,plugin,plugins,ErrorClass}, | ||
error, | ||
...args) | ||
{ | ||
const errorA=ErrorClass.normalize(error); | ||
return callMethod({methodFunc,plugin,plugins,error:errorA,args}); | ||
}; | ||
const callMethod=function({methodFunc,plugin,plugins,error,args}){ | ||
const{args:argsA,methodOpts}=getMethodOpts(args,plugin); | ||
@@ -22,0 +43,0 @@ const info=getErrorPluginInfo({error,methodOpts,plugins,plugin}); |
export const validateSameMethods=function({ | ||
instanceMethods, | ||
staticMethods, | ||
fullName | ||
}){ | ||
const duplicateName=findDuplicateKey(staticMethods,instanceMethods); | ||
if(duplicateName!==undefined){ | ||
throw new TypeError( | ||
`The plugin "${fullName}" must not define "${duplicateName}()" as both "instanceMethods" and "staticMethods".`); | ||
} | ||
}; | ||
export const validateDuplicatePlugins=function(plugins,ParentError){ | ||
@@ -33,28 +49,22 @@ plugins.forEach((pluginA,indexA)=>{ | ||
throw new TypeError( | ||
`The "plugins" option of ${ParentError.name}.subclass() must not include "${pluginA.fullName}": this plugin has already been included.`); | ||
`The "plugins" option of "${ParentError.name}.subclass()" must not include "${pluginA.fullName}": this plugin has already been included.`); | ||
} | ||
validateDuplicateMethods(pluginA,pluginB,"staticMethods"); | ||
validateDuplicateMethods(pluginA,pluginB,"instanceMethods"); | ||
}; | ||
const duplicateName=findDuplicateKey( | ||
{...pluginA.instanceMethods,...pluginA.staticMethods}, | ||
{...pluginB.instanceMethods,...pluginB.staticMethods}); | ||
const validateDuplicateMethods=function(pluginA,pluginB,propName){ | ||
Object.keys(pluginA[propName]).forEach((methodName)=>{ | ||
validateDuplicateMethod({pluginA,pluginB,propName,methodName}); | ||
}); | ||
}; | ||
const validateDuplicateMethod=function({ | ||
pluginA, | ||
pluginB, | ||
propName, | ||
methodName | ||
}){ | ||
if(pluginB[propName][methodName]!==undefined){ | ||
if(duplicateName!==undefined){ | ||
throw new TypeError( | ||
`The plugins "${pluginA.fullName}" and "${pluginB.fullName}" must not both define the same "${propName}.${methodName}" property.`); | ||
`The plugins "${pluginA.fullName}" and "${pluginB.fullName}" must not both define the same "${duplicateName}()" method.`); | ||
} | ||
}; | ||
const findDuplicateKey=function(objectA,objectB){ | ||
const keysA=Object.keys(objectA); | ||
return Object.keys(objectB).find((key)=>keysA.includes(key)); | ||
}; | ||
//# sourceMappingURL=duplicate.js.map |
@@ -6,4 +6,4 @@ import isPlainObj from"is-plain-obj"; | ||
import{validateDuplicatePlugins}from"./duplicate.js"; | ||
import{normalizeMethods}from"./methods.js"; | ||
import{validateDuplicatePlugins,validateSameMethods}from"./duplicate.js"; | ||
import{normalizeAllMethods}from"./methods.js"; | ||
import{validatePluginName}from"./name.js"; | ||
@@ -42,19 +42,7 @@ | ||
validateOptionalFuncs(pluginA); | ||
const pluginB=normalizeMethods({ | ||
plugin:pluginA, | ||
propName:"instanceMethods", | ||
coreObject:Error.prototype, | ||
coreObjectName:"error", | ||
forbiddenNames:new Set([]) | ||
}); | ||
const pluginC=normalizeMethods({ | ||
plugin:pluginB, | ||
propName:"staticMethods", | ||
coreObject:Error, | ||
coreObjectName:"Error", | ||
forbiddenNames:new Set(["normalize","subclass"]) | ||
}); | ||
const pluginD=normalizeIsOptions({plugin:pluginC}); | ||
const pluginE=normalizeGetOptions({plugin:pluginD}); | ||
return pluginE; | ||
const pluginB=normalizeAllMethods(pluginA); | ||
validateSameMethods(pluginB); | ||
const pluginC=normalizeIsOptions({plugin:pluginB}); | ||
const pluginD=normalizeGetOptions({plugin:pluginC}); | ||
return pluginD; | ||
}; | ||
@@ -61,0 +49,0 @@ |
import isPlainObj from"is-plain-obj"; | ||
export const normalizeMethods=function({ | ||
export const normalizeAllMethods=function(plugin){ | ||
const pluginA=normalizeMethods({ | ||
...INSTANCE_METHODS, | ||
plugin, | ||
propName:"instanceMethods" | ||
}); | ||
const pluginB=normalizeMethods({ | ||
...STATIC_METHODS, | ||
plugin:pluginA, | ||
propName:"instanceMethods" | ||
}); | ||
const pluginC=normalizeMethods({ | ||
...STATIC_METHODS, | ||
plugin:pluginB, | ||
propName:"staticMethods" | ||
}); | ||
return pluginC; | ||
}; | ||
const INSTANCE_METHODS={ | ||
coreObject:Error.prototype, | ||
coreObjectName:"error", | ||
forbiddenNames:new Set([]) | ||
}; | ||
const STATIC_METHODS={ | ||
coreObject:Error, | ||
coreObjectName:"Error", | ||
forbiddenNames:new Set(["normalize","subclass"]) | ||
}; | ||
const normalizeMethods=function({ | ||
plugin, | ||
propName, | ||
@@ -7,0 +38,0 @@ coreObject, |
@@ -60,3 +60,3 @@ import type { ErrorInstance } from './merge/cause/main.js' | ||
// - Type narrowing with `instanceof` does not work if there are any plugins | ||
// with static methods. This is due to the following bug: | ||
// with instance|static methods. This is due to the following bug: | ||
// https://github.com/microsoft/TypeScript/issues/50844 | ||
@@ -77,5 +77,5 @@ // - When a `custom` class overrides a plugin's instance method, it must be | ||
// Minor limitations: | ||
// - Plugin static methods should not be allowed to override `Error.*` | ||
// - Plugin instance|static methods should not be allowed to override `Error.*` | ||
// (e.g. `prepareStackTrace()`) | ||
// - Plugins should not be allowed to define static or instance methods already | ||
// - Plugins should not be allowed to define instance|static methods already | ||
// defined by other plugins |
@@ -5,6 +5,8 @@ import type { Plugin } from '../plugins/shape.js' | ||
/** | ||
* Options passed to plugin methods: `error.{instanceMethod}(..., options)` or | ||
* `ErrorClass.{staticMethod}(..., options)` | ||
* Options passed to plugin methods: | ||
* `ErrorClass.{staticMethod}(..., options)`, | ||
* `ErrorClass.{instanceMethod}(error, ..., options)` or | ||
* `error.{instanceMethod}(..., options)` | ||
*/ | ||
export type MethodOptions<PluginArg extends Plugin> = | ||
ExternalPluginOptions<PluginArg> |
@@ -9,3 +9,3 @@ import type { MethodOptions } from '../options/method.js' | ||
*/ | ||
type InstanceMethod = ( | ||
export type InstanceMethod = ( | ||
info: InfoParameter['instanceMethods'], | ||
@@ -12,0 +12,0 @@ ...args: readonly never[] |
@@ -20,3 +20,4 @@ import type { GetOptions, IsOptions } from '../options/get.js' | ||
* | ||
* // Add error instance methods like `error.exampleMethod(...args)` | ||
* // Add instance methods like `ErrorClass.exampleMethod(error, ...args)` or | ||
* // `error.exampleMethod(...args)` | ||
* instanceMethods: { | ||
@@ -28,3 +29,3 @@ * exampleMethod(info, ...args) { | ||
* | ||
* // Add `ErrorClass` static methods like `ErrorClass.staticMethod(...args)` | ||
* // Add static methods like `ErrorClass.staticMethod(...args)` | ||
* staticMethods: { | ||
@@ -106,6 +107,6 @@ * staticMethod(info, ...args) { | ||
* ```js | ||
* // `error.exampleMethod('one', true)` results in: | ||
* // `ErrorClass.exampleMethod(error, 'one', true)` results in: | ||
* // options: true | ||
* // args: ['one'] | ||
* // `error.exampleMethod('one', 'two')` results in: | ||
* // `ErrorClass.exampleMethod(error, 'one', 'two')` results in: | ||
* // options: undefined | ||
@@ -132,11 +133,10 @@ * // args: ['one', 'two'] | ||
/** | ||
* Add error instance methods like `error.methodName(...args)`. | ||
* Add error instance methods like `ErrorClass.methodName(error, ...args)` or | ||
* `error.methodName(...args)`. Unlike static methods, this should be used | ||
* when the method's main argument is an `error` instance. | ||
* | ||
* The first argument `info` is provided by `modern-errors`. | ||
* The other `...args` are forwarded from the method's call. | ||
* The first argument `info` is provided by `modern-errors`. The `error` | ||
* argument is passed as `info.error`. The other `...args` are forwarded from | ||
* the method's call. | ||
* | ||
* If the logic involves an `error` instance or error-specific `options`, | ||
* instance methods should be preferred over static methods. | ||
* Otherwise, static methods should be used. | ||
* | ||
* @example | ||
@@ -146,6 +146,7 @@ * ```js | ||
* name: 'example', | ||
* // `error.concatMessage("one")` returns `${error.message} - one` | ||
* // `ErrorClass.concatMessage(error, "one")` or `error.concatMessage("one")` | ||
* // return `${error.message} - one` | ||
* instanceMethods: { | ||
* concatMessage({ error }, string) { | ||
* return `${error.message} - ${string}` | ||
* concatMessage(info, string) { | ||
* return `${info.error.message} - ${string}` | ||
* }, | ||
@@ -159,6 +160,8 @@ * }, | ||
/** | ||
* Add error static methods like `ErrorClass.methodName(...args)`. | ||
* Add error static methods like `ErrorClass.methodName(...args)`. Unlike | ||
* instance methods, this should be used when the method's main argument is | ||
* _not_ an `error` instance. | ||
* | ||
* The first argument `info` is provided by `modern-errors`. | ||
* The other `...args` are forwarded from the method's call. | ||
* The first argument `info` is provided by `modern-errors`. `info.error` is | ||
* not defined. The other `...args` are forwarded from the method's call. | ||
* | ||
@@ -165,0 +168,0 @@ * @example |
@@ -13,2 +13,3 @@ import type { ErrorName } from 'error-custom-class' | ||
import type { PluginsStaticMethods } from '../../plugins/static.js' | ||
import type { PluginsMixedMethods } from '../../plugins/mixed.js' | ||
import type { OmitKeys } from '../../utils.js' | ||
@@ -130,3 +131,4 @@ import type { NormalizeError } from '../normalize/main.js' | ||
> & | ||
PluginsStaticMethods<PluginsArg> | ||
PluginsStaticMethods<PluginsArg> & | ||
PluginsMixedMethods<PluginsArg> | ||
@@ -133,0 +135,0 @@ /** |
{ | ||
"name": "modern-errors", | ||
"version": "5.0.0", | ||
"version": "5.1.0", | ||
"type": "module", | ||
@@ -67,9 +67,9 @@ "exports": { | ||
"is-error-instance": "^1.3.0", | ||
"modern-errors-bugs": "^2.0.1", | ||
"modern-errors-clean": "^3.0.0", | ||
"modern-errors-cli": "^2.0.0", | ||
"modern-errors-http": "^2.0.0", | ||
"modern-errors-process": "^2.0.0", | ||
"modern-errors-serialize": "^2.0.0", | ||
"modern-errors-winston": "^2.0.0", | ||
"modern-errors-bugs": "^2.1.0", | ||
"modern-errors-clean": "^3.1.0", | ||
"modern-errors-cli": "^2.1.0", | ||
"modern-errors-http": "^2.1.0", | ||
"modern-errors-process": "^2.1.0", | ||
"modern-errors-serialize": "^2.1.0", | ||
"modern-errors-winston": "^2.1.0", | ||
"test-each": "^5.6.0" | ||
@@ -76,0 +76,0 @@ }, |
@@ -377,3 +377,3 @@ <picture> | ||
ensures every error being thrown is [valid](#invalid-errors), applies | ||
[plugins](#using-plugins-with-unknown-errors), and has a class that is either | ||
[plugins](#-plugins), and has a class that is either | ||
[_known_](#create-error-classes) or [`UnknownError`](#-unknown-errors). | ||
@@ -436,20 +436,2 @@ | ||
### Using plugins with unknown errors | ||
[`BaseError.normalize()`](#errorclassnormalizeerror-newerrorclass) is required | ||
for [_unknown_ errors](#-unknown-errors) to use [plugins](#-plugins). | ||
<!-- eslint-skip --> | ||
```js | ||
try { | ||
return regExp.test(value) | ||
} catch (error) { | ||
error.examplePluginMethod() // This throws | ||
const normalizedError = BaseError.normalize(error) | ||
normalizedError.examplePluginMethod() // This works | ||
} | ||
``` | ||
## 🔌 Plugins | ||
@@ -456,0 +438,0 @@ |
91317
55
2209
681