以简单、稳定、一致的方式处理错误。
招聘我
如果您正在寻找一位 Node.js API 或 CLI 工程师(具有11年经验),请联系我。我最近在 Netlify Build 和 Netlify Plugins 担任技术主管2.5年。我可以接受全职远程职位。
特性
简单的模式用于:
稳定性:
- 🚨 规范化无效错误
- 🛡️ 100% 测试覆盖率
- 🤓 严格的 TypeScript 类型
插件
modern-errors-cli
:处理 CLI 模块中的错误modern-errors-process
:处理进程错误modern-errors-bugs
:打印报告错误的位置modern-errors-serialize
:序列化/解析错误modern-errors-clean
:清理堆栈跟踪modern-errors-http
:创建 HTTP 错误响应modern-errors-winston
:使用 Winston 记录错误modern-errors-switch
:执行特定类的逻辑- 🔌 创建你自己的插件
示例
创建错误类。
import ModernError from 'modern-errors'
export const BaseError = ModernError.subclass('BaseError')
export const UnknownError = BaseError.subclass('UnknownError')
export const InputError = BaseError.subclass('InputError')
export const AuthError = BaseError.subclass('AuthError')
export const DatabaseError = BaseError.subclass('DatabaseError')
设置错误属性。
throw new InputError('无效的文件路径', { props: { filePath: '/...' } })
包装错误。
try {
// ...
} catch (cause) {
throw new InputError('无法读取文件。', { cause })
}
规范化错误。
try {
throw '缺少文件路径。'
} catch (error) {
// 从字符串规范化为 `BaseError` 实例
throw BaseError.normalize(error)
}
使用插件。
import ModernError from 'modern-errors'
import modernErrorsSerialize from 'modern-errors-serialize'
export const BaseError = ModernError.subclass('BaseError', {
plugins: [modernErrorsSerialize],
})
// ...
// 将错误序列化为 JSON,然后再解析回相同的错误实例
const error = new InputError('缺少文件路径。')
const errorString = JSON.stringify(error)
const identicalError = BaseError.parse(JSON.parse(errorString))
安装
npm install modern-errors
如果使用任何插件,也必须安装它们。
npm install modern-errors-{插件名}
这个包同时适用于 Node.js >=18.18.0 和浏览器。
这是一个 ES 模块。它必须使用 import 或 import() 语句加载,而不是 require()。如果使用 TypeScript,必须将其配置为输出 ES 模块,而不是 CommonJS。
使用方法
⛑️ 错误类
创建错误类
import ModernError from 'modern-errors'
export const BaseError = ModernError.subclass('BaseError')
export const UnknownError = BaseError.subclass('UnknownError')
export const InputError = BaseError.subclass('InputError')
export const AuthError = BaseError.subclass('AuthError')
export const DatabaseError = BaseError.subclass('DatabaseError')
导出错误类
导出并记录所有错误类允许使用者对其进行检查。这也使得在模块之间共享错误类成为可能。
检查错误类
if (error instanceof InputError) {
// ...
}
错误子类
ErrorClass.subclass()
返回一个子类。
父类的选项与其子类的选项合并。
export const BaseError = ModernError.subclass('BaseError', {
props: { isError: true },
})
export const InputError = BaseError.subclass('InputError', {
props: { isUserError: true },
})
const error = new InputError('...')
console.log(error.isError) // true
console.log(error.isUserError) // true
console.log(error instanceof BaseError) // true
console.log(error instanceof InputError) // true
🏷️ 错误属性
错误类属性
const InputError = BaseError.subclass('InputError', {
props: { isUserError: true },
})
const error = new InputError('...')
console.log(error.isUserError) // true
错误实例属性
const error = new InputError('...', { props: { isUserError: true } })
console.log(error.isUserError) // true
内部错误属性
内部或秘密的错误属性可以用_
作为前缀。这使它们成为不可枚举的,从而防止被迭代或记录。
const error = new InputError('...', {
props: { userId: 6, _isUserError: true },
})
console.log(error.userId) // 6
console.log(error._isUserError) // true
console.log(Object.keys(error)) // ['userId']
console.log(error) // 记录`userId`,但不记录`_isUserError`
🎀 包装错误
抛出错误
throw new InputError('缺少文件路径。')
包装内部错误
任何错误的消息、类和选项都可以使用标准的cause
选项进行包装。
内部错误不会被设置为cause
属性,而是直接合并到外部错误中,包括其message
、stack
、name
、AggregateError.errors
和任何附加属性。
try {
// ...
} catch (cause) {
throw new InputError('无法读取文件。', { cause })
}
包装错误消息
外部错误消息会被追加,除非它为空。如果外部错误消息以:
或:\n
结尾,则会被前置。
const cause = new InputError('文件不存在。')
// InputError: 文件不存在。
throw new InputError('', { cause })
// InputError: 文件不存在。
// 无法读取文件。
throw new InputError('无法读取文件。', { cause })
// InputError: 无法读取文件:文件不存在。
throw new InputError(`无法读取文件:`, { cause })
// InputError: 无法读取文件:
// 文件不存在。
throw new InputError(`无法读取文件:\n`, { cause })
包装错误类
外部错误的类会替换内部错误的类。
try {
throw new AuthError('...')
} catch (cause) {
// 现在是InputError
throw new InputError('...', { cause })
}
除非外部错误的类是父类,比如BaseError
。
try {
throw new AuthError('...')
} catch (cause) {
// 仍然是AuthError
throw new BaseError('...', { cause })
}
包装错误选项
try {
throw new AuthError('...', innerOptions)
} catch (cause) {
// `outerOptions`与`innerOptions`合并
throw new BaseError('...', { ...outerOptions, cause })
}
聚合错误
errors
选项将多个错误聚合成一个。这类似于new AggregateError(errors)
,但适用于任何错误类。
const databaseError = new DatabaseError('...')
const authError = new AuthError('...')
throw new InputError('...', { errors: [databaseError, authError] })
// InputError: ... {
// [errors]: [
// DatabaseError: ...
// AuthError: ...
// ]
// }
🚨 规范化错误
包装的错误
任何错误都可以直接传递给cause
或errors
选项,即使它是无效的、未知的或未规范化的。
try {
// ...
} catch (cause) {
throw new InputError('...', { cause })
}
无效错误
操作不是Error
实例或具有无效属性的错误可能导致意外的错误。
BaseError.normalize()
可以解决这个问题。
try {
throw '缺少文件路径。'
} catch (invalidError) {
// 这会失败:`invalidError.message`是`undefined`
console.log(invalidError.message.trim())
}
try {
throw '缺少文件路径。'
} catch (invalidError) {
const normalizedError = BaseError.normalize(invalidError)
// 这样可以正常工作: '缺少文件路径。'
// `normalizedError` 是 `BaseError` 的实例。
console.log(normalizedError.message.trim())
}
🐞 未知错误
处理已知错误
已知错误应该在 try {} catch {}
块中处理,并用特定类包装。该块应该只覆盖可能抛出错误的语句,以防止捕获其他不相关的错误。
try {
return regExp.test(value)
} catch (error) {
// 现在是 `InputError` 实例
throw new InputError('无效的正则表达式:', { cause: error })
}
标准化未知错误
如果错误没有按照上述方式处理,就被视为_未知_。这表示发生了意外异常,通常是一个bug。
BaseError.normalize(error, UnknownError)
将 UnknownError
类分配给这些错误。
export const UnknownError = BaseError.subclass('UnknownError')
try {
return regExp.test(value)
} catch (error) {
// 现在是 `UnknownError` 实例
throw BaseError.normalize(error, UnknownError)
}
顶级错误处理器
用BaseError.normalize(error, UnknownError)
包装模块的主要函数可以确保每个抛出的错误都是有效的,应用了插件,并且具有已知或UnknownError
类。
export const main = () => {
try {
// ...
} catch (error) {
throw BaseError.normalize(error, UnknownError)
}
}
🔌 插件
插件列表
插件扩展了 modern-errors
的功能。所有可用的插件都列在这里。
添加插件
要使用插件,请先安装它,然后将其传递给plugins
选项。
npm install modern-errors-{pluginName}
import ModernError from 'modern-errors'
import modernErrorsBugs from 'modern-errors-bugs'
import modernErrorsSerialize from 'modern-errors-serialize'
export const BaseError = ModernError.subclass('BaseError', {
plugins: [modernErrorsBugs, modernErrorsSerialize],
})
// ...
自定义插件
请查看以下文档以创建你自己的插件。
插件选项
大多数插件可以通过选项进行配置。选项的名称与插件相同。
const options = {
// `modern-errors-bugs` 选项
bugs: 'https://github.com/my-name/my-project/issues',
// `props` 可以像插件选项一样配置和修改
props: { userId: 5 },
}
插件选项可以应用于(按优先顺序):
- 任何错误:
ModernError.subclass()
的第二个参数
export const BaseError = ModernError.subclass('BaseError', options)
- 特定类(及其子类)的任何错误:
ErrorClass.subclass()
的第二个参数
export const InputError = BaseError.subclass('InputError', options)
- 特定错误:
new ErrorClass()
的第二个参数
throw new InputError('...', options)
- 插件方法调用: 最后一个参数,仅传递该插件的选项
ErrorClass[methodName](...args, options[pluginName])
error[methodName](...args, options[pluginName])
🔧 自定义逻辑
custom
选项可用于为错误类
提供额外的方法、constructor
、属性或选项。
export const InputError = BaseError.subclass('InputError', {
// `class` 必须继承自父错误类
custom: class extends BaseError {
// 如果定义了 `constructor`,其参数必须是 (message, options)
// 可以定义额外的 `options`。
constructor(message, options) {
message += options?.suffix ?? ''
super(message, options)
}
isUserInput() {
// ...
}
},
})
const error = new InputError('错误的用户名', { suffix: ': 示例' })
console.log(error.message) // '错误的用户名: 示例'
console.log(error.isUserInput())
🤓 TypeScript
请查看以下文档了解有关 TypeScript 类型的信息。
API
ModernError
顶级 ErrorClass
。
ErrorClass.subclass(name, options?)
name
: string
options
: ClassOptions?
创建并返回一个子 ErrorClass
。
options
options.props
类型: object
options.plugins
类型: Plugin[]
options.custom
类型: class extends ErrorClass {}
自定义类,用于添加任何方法、constructor
或属性。
options.*
任何插件选项也可以在此指定。
new ErrorClass(message, options?)
message
: string
options
: InstanceOptions?
返回值: Error
options
options.props
类型: object
options.cause
类型: any
被包装的内部错误。
options.errors
类型: any[]
被聚合的错误数组。
options.*
任何插件选项也可以在此指定。
ErrorClass.normalize(error, NewErrorClass?)
error
: Error | any
NewErrorClass
: ErrorClass
的子类
返回值: Error
标准化无效错误。
如果 error
的类是 ErrorClass
的子类,它将保持不变。
否则,它将被转换为 NewErrorClass
,默认为 ErrorClass
本身。
模块
这个框架汇集了一系列可以单独使用的模块:
error-custom-class
:创建一个错误类error-class-utils
:正确创建错误类的实用工具error-serializer
:将错误转换为/从普通对象转换normalize-exception
:规范化异常/错误is-error-instance
:检查值是否为Error
实例merge-error-cause
:将错误与其cause
合并set-error-class
:正确更新错误的类set-error-message
:正确更新错误的消息wrap-error-message
:正确包装错误的消息set-error-props
:正确更新错误的属性set-error-stack
:正确更新错误的堆栈handle-cli-error
:💣 CLI 应用程序的错误处理程序 💥log-process-errors
:为 Node.js 进程错误提供一些 ❤error-http-response
:创建 HTTP 错误响应winston-error-format
:使用 Winston 记录错误
支持
如有任何问题,请_不要犹豫_在 GitHub 上提交问题。
我们欢迎所有人,不论个人背景如何。我们执行行为准则以促进积极和包容的环境。
贡献
这个项目是用 ❤️ 制作的。回馈的最简单方式是给它加星并在网上分享。
如果文档不清楚或有错别字,请点击页面的编辑
按钮(铅笔图标)并提出修改建议。
如果您想帮助我们修复错误或添加新功能,请查看我们的指南。欢迎提交拉取请求!