Skip to content

Commit

Permalink
Add SystemError
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed Jun 5, 2022
1 parent ce51bd7 commit 8503c7e
Show file tree
Hide file tree
Showing 13 changed files with 55 additions and 38 deletions.
1 change: 0 additions & 1 deletion src/bin/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ const COMMANDS = { run, show, remove, dev }
// Error type-specific behavior
const ERROR_PROPS = {
default: { exitCode: 5 },
CoreError: { exitCode: 5 },
PluginError: { exitCode: 4 },
UserCodeError: { exitCode: 3 },
UserError: { exitCode: 2, short: true },
Expand Down
9 changes: 5 additions & 4 deletions src/config/normalize/lib/error.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { modernErrors } from '../../../error/modern.js'

const { InputError, DefinitionError, KeywordError, CoreError, onError } =
modernErrors(['InputError', 'DefinitionError', 'KeywordError', 'CoreError'])
const { InputError, DefinitionError, KeywordError, onError } = modernErrors([
'InputError',
'DefinitionError',
'KeywordError',
])

export {
// Invalid `inputs`
Expand All @@ -10,8 +13,6 @@ export {
DefinitionError,
// Bug in a keyword|plugin
KeywordError,
// Bug in the library itself
CoreError,
// Top-level error handler
onError,
}
8 changes: 4 additions & 4 deletions src/config/plugin/error.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import normalizeException from 'normalize-exception'

import { CoreError, PluginError, UserError } from '../../error/main.js'
import { PluginError, UserError } from '../../error/main.js'

// Translate error classes from the plugins library to error classes from this
// library
export const handlePluginsError = function (error) {
const { name } = normalizeException(error)
const ErrorType = name in ERROR_MAP ? ERROR_MAP[name] : CoreError
const ErrorType = name in ERROR_MAP ? ERROR_MAP[name] : Error
return new ErrorType('', { cause: error })
}

const ERROR_MAP = {
CoreError,
UserError: CoreError,
SystemError: Error,
UserError: Error,
PluginError,
ConsumerError: UserError,
}
9 changes: 5 additions & 4 deletions src/config/plugin/lib/error.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { modernErrors } from '../../../error/modern.js'

const { CoreError, UserError, PluginError, ConsumerError, onError } =
modernErrors(['CoreError', 'UserError', 'PluginError', 'ConsumerError'])
const { UserError, PluginError, ConsumerError, onError } = modernErrors([
'UserError',
'PluginError',
'ConsumerError',
])

export {
// Error from the library
CoreError,
// Error from the library's user, who defines available plugin types
UserError,
// Error from a plugin author, who defines a specific plugin
Expand Down
4 changes: 2 additions & 2 deletions src/config/plugin/lib/location_normalize.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CoreError, ConsumerError } from './error.js'
import { ConsumerError } from './error.js'
import { NORMALIZE_LOCATIONS } from './location_type.js'
import { safeNormalizeConfig } from './normalize.js'

Expand All @@ -19,7 +19,7 @@ export const normalizeLocation = async function ({
},
keywords,
InputErrorType: ConsumerError,
DefinitionErrorType: CoreError,
DefinitionErrorType: Error,
})
}

Expand Down
4 changes: 1 addition & 3 deletions src/config/plugin/lib/normalize.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { normalizeInputs } from '../../normalize/lib/main.js'

import { CoreError } from './error.js'

// Call `normalizeConfig` while assigning the right error types
export const safeNormalizeConfig = async function (config, rules, opts) {
try {
Expand All @@ -18,5 +16,5 @@ const getErrorType = function (error, { InputErrorType, DefinitionErrorType }) {
return InputErrorType
}

return error.name === 'DefinitionError' ? DefinitionErrorType : CoreError
return error.name === 'DefinitionError' ? DefinitionErrorType : Error
}
4 changes: 2 additions & 2 deletions src/config/plugin/lib/shape.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getDummyRules } from '../../normalize/dummy.js'

import { PluginError, UserError, CoreError } from './error.js'
import { PluginError, UserError } from './error.js'
import { safeNormalizeConfig } from './normalize.js'
import { normalizeRuleName, validateSharedProp } from './shared.js'

Expand All @@ -18,7 +18,7 @@ export const normalizeShape = async function ({
},
keywords,
InputErrorType: PluginError,
DefinitionErrorType: CoreError,
DefinitionErrorType: Error,
})

if (shape === undefined) {
Expand Down
4 changes: 2 additions & 2 deletions src/config/plugin/lib/top.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { normalizeObjectOrString } from '../../normalize/transform.js'

import { CoreError, ConsumerError } from './error.js'
import { ConsumerError } from './error.js'
import { getExampleLocation } from './location_normalize.js'
import { safeNormalizeConfig } from './normalize.js'

Expand All @@ -17,7 +17,7 @@ export const normalizePluginConfigTop = async function (
all: { cwd, prefix, parent: name, context: { pluginProp, builtins } },
keywords,
InputErrorType: ConsumerError,
DefinitionErrorType: CoreError,
DefinitionErrorType: Error,
})
return { originalLocation, pluginConfig: pluginConfigA, locationName }
}
Expand Down
4 changes: 0 additions & 4 deletions src/error/main.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { modernErrors } from './modern.js'

const {
CoreError,
PluginError,
UserCodeError,
UserError,
LimitError,
StopError,
onError,
} = modernErrors([
'CoreError',
'PluginError',
'UserCodeError',
'UserError',
Expand All @@ -18,8 +16,6 @@ const {
])

export {
// Bug in the library itself
CoreError,
// Bug in a plugin (reporter|runner)
PluginError,
// Invalid tasks or tasks file
Expand Down
32 changes: 29 additions & 3 deletions src/error/modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,41 @@ import { allowErrorTypes } from './types.js'
// - Any browsers: `stacktrace.js`
export const modernErrors = function (errorNames, opts) {
const { onCreate } = getOpts(opts)
const SystemError = createSystemError(errorNames)
const ErrorTypes = getErrorTypes(errorNames, onCreate)
const onErrorHandler = onError.bind(undefined, Object.values(ErrorTypes))
const onErrorHandler = onError.bind(
undefined,
Object.values(ErrorTypes),
SystemError,
)
return { ...ErrorTypes, onError: onErrorHandler }
}

const getOpts = function ({ onCreate } = {}) {
return { onCreate }
}

// Create the SystemError type used for unknown error types.
// We purposely do not export this type nor expose it except through
// `error.name` because:
// - Users do not need it throw system errors since any uncaught error will be
// converted to it
// - This ensures instantiating a system error never throws
// - This makes the API simpler, stricter and more consistent
const createSystemError = function (errorNames) {
validateSystemName(errorNames)
return errorType(SYSTEM_ERROR_NAME)
}

const validateSystemName = function (errorNames) {
if (errorNames.includes(SYSTEM_ERROR_NAME)) {
throw new Error(`Error type must not be named "${SYSTEM_ERROR_NAME}".
"${SYSTEM_ERROR_NAME}" is reserved for exceptions matching none of the error types.`)
}
}

const SYSTEM_ERROR_NAME = 'SystemError'

const getErrorTypes = function (errorNames, onCreate) {
return Object.fromEntries(
errorNames.map((errorName) => [errorName, errorType(errorName, onCreate)]),
Expand All @@ -37,8 +63,8 @@ const getErrorTypes = function (errorNames, onCreate) {

// Error handler that normalizes an error, merge its `error.cause` and ensure
// its type is among an allowed list of types.
const onError = function (ErrorTypes, error) {
const onError = function (ErrorTypes, SystemError, error) {
const errorA = mergeErrorCause(error)
const errorB = allowErrorTypes(errorA, ErrorTypes)
const errorB = allowErrorTypes(errorA, ErrorTypes, SystemError)
return errorB
}
6 changes: 3 additions & 3 deletions src/error/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import mergeErrorCause from 'merge-error-cause'
import normalizeException from 'normalize-exception'

// Ensure an error is among a specific set of error types.
// Otherwise, assign a default error type (`errorTypes[0]`).
export const allowErrorTypes = function (error, ErrorTypes) {
// Otherwise, assign a default SystemError type.
export const allowErrorTypes = function (error, ErrorTypes, SystemError) {
const errorA = normalizeException(error)
return ErrorTypes.some((ErrorType) => errorA instanceof ErrorType)
? errorA
: mergeErrorCause(new ErrorTypes[0]('', { cause: errorA }))
: mergeErrorCause(new SystemError('', { cause: errorA }))
}
4 changes: 2 additions & 2 deletions src/run/process/task_error.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const applyErrorProps = function (name, message) {
// - Whether the error should be reported as caused by the user or the plugin
// - Prefixing the error message
const ERROR_PROPS = {
CoreError: {
SystemError: {
ErrorType: PluginError,
prefix: 'Runner internal bug',
},
Expand All @@ -57,7 +57,7 @@ const ERROR_PROPS = {
},
}

const DEFAULT_ERROR_NAME = 'CoreError'
const DEFAULT_ERROR_NAME = 'SystemError'

// Keep the stack trace from the runner's process, but update it with the new
// `name` and `message`
Expand Down
4 changes: 0 additions & 4 deletions src/runners/common/error.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { modernErrors } from '../../error/modern.js'

const {
CoreError,
IpcSerializationError,
TasksLoadError,
TasksSyntaxError,
TasksRunError,
ConfigError,
onError,
} = modernErrors([
'CoreError',
'IpcSerializationError',
'TasksLoadError',
'TasksSyntaxError',
Expand All @@ -18,8 +16,6 @@ const {
])

export {
// Error from the library itself
CoreError,
// Could not JSON-stringify IPC payload
IpcSerializationError,
// Tasks file throws when loading
Expand Down

0 comments on commit 8503c7e

Please sign in to comment.