Skip to content

Commit

Permalink
Break file
Browse files Browse the repository at this point in the history
  • Loading branch information
ehmicky committed May 29, 2022
1 parent 2f1d798 commit 91f92bb
Show file tree
Hide file tree
Showing 4 changed files with 191 additions and 145 deletions.
115 changes: 115 additions & 0 deletions src/config/normalize/lib/call/error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {
addInputPrefix,
addDefinitionPrefix,
addKeywordPrefix,
} from './prefix.js'
import {
addCurrentDefinition,
addExampleDefinition,
addCurrentInput,
addExampleInput,
} from './suffix.js'

// Handle errors in `keyword.test|normalize|main()` or in keyword functions
export const handleError = function ({
error,
input,
example,
prefix,
originalName,
hasInput,
test,
keyword,
definition,
exampleDefinition,
errorType,
bugType,
}) {
const isValidation = isValidateError(error)
const type = isValidation ? errorType : bugType
return ERROR_HANDLERS[type]({
error,
input,
example,
prefix,
originalName,
hasInput,
test,
keyword,
definition,
exampleDefinition,
isValidation,
})
}

// Consumers can distinguish users errors from system bugs by checking
// the `error.validation` boolean property.
// User errors are distinguished by having error message starting with "must".
// We fail on the first error, as opposed to aggregating all errors
// - Otherwise, a failed property might be used by another property, which
// would also appear as failed, even if it has no issues
// We detect this using the error message instead of the error class because:
// - It is simpler for users
// - It works both on browsers and in Node.js
// - It ensures the error message looks good
const isValidateError = function (error) {
return error instanceof Error && error.message.startsWith('must')
}

const handleInputError = function ({
error,
input,
example,
prefix,
originalName,
hasInput,
test,
}) {
error.validation = true
const errorA = addInputPrefix(error, prefix, originalName)
const errorB = addCurrentInput(errorA, input, hasInput)
const errorC = addExampleInput({ error: errorB, example, hasInput, test })
return errorC
}

const handleDefinitionError = function ({
error,
prefix,
originalName,
keyword,
definition,
exampleDefinition,
isValidation,
}) {
const errorA = addDefinitionPrefix({
error,
prefix,
originalName,
keyword,
isValidation,
})
const errorB = addCurrentDefinition(errorA, definition)
const errorC = addExampleDefinition(errorB, exampleDefinition)
return errorC
}

const handleKeywordError = function ({
error,
input,
prefix,
originalName,
hasInput,
keyword,
definition,
}) {
const errorA = addKeywordPrefix({ error, prefix, originalName, keyword })
const errorB = addCurrentDefinition(errorA, definition)
const errorC = addCurrentInput(errorB, input, hasInput)
return errorC
}

const ERROR_HANDLERS = {
input: handleInputError,
definition: handleDefinitionError,
keyword: handleKeywordError,
}
149 changes: 4 additions & 145 deletions src/config/normalize/lib/call/main.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
/* eslint-disable max-lines */
// TODO: fix max-lines linting
import { inspect } from 'util'

import { wrapError } from '../../../../error/wrap.js'
import { handleError } from './error.js'

// Call `keyword.test()`
export const callTest = async function ({ test, input, info, keyword }) {
Expand Down Expand Up @@ -106,9 +102,7 @@ const callFunc = async function ({
try {
return hasInput ? await func(input, info) : await func(info)
} catch (error) {
const isValidation = isValidateError(error)
const type = isValidation ? errorType : bugType
throw ERROR_HANDLERS[type]({
throw handleError({
error,
input,
example,
Expand All @@ -119,143 +113,8 @@ const callFunc = async function ({
keyword,
definition,
exampleDefinition,
isValidation,
errorType,
bugType,
})
}
}

// Consumers can distinguish users errors from system bugs by checking
// the `error.validation` boolean property.
// User errors are distinguished by having error message starting with "must".
// We fail on the first error, as opposed to aggregating all errors
// - Otherwise, a failed property might be used by another property, which
// would also appear as failed, even if it has no issues
// We detect this using the error message instead of the error class because:
// - It is simpler for users
// - It works both on browsers and in Node.js
// - It ensures the error message looks good
const isValidateError = function (error) {
return error instanceof Error && error.message.startsWith('must')
}

const handleInputError = function ({
error,
input,
example,
prefix,
originalName,
hasInput,
test,
}) {
error.validation = true
const errorA = addInputPrefix(error, prefix, originalName)
const errorB = addCurrentInput(errorA, input, hasInput)
const errorC = addExampleInput({ error: errorB, example, hasInput, test })
return errorC
}

const handleDefinitionError = function ({
error,
prefix,
originalName,
keyword,
definition,
exampleDefinition,
isValidation,
}) {
const errorA = addDefinitionPrefix({
error,
prefix,
originalName,
keyword,
isValidation,
})
const errorB = addCurrentDefinition(errorA, definition)
const errorC = addExampleDefinition(errorB, exampleDefinition)
return errorC
}

const handleKeywordError = function ({
error,
input,
prefix,
originalName,
hasInput,
keyword,
definition,
}) {
const errorA = addKeywordPrefix({ error, prefix, originalName, keyword })
const errorB = addCurrentDefinition(errorA, definition)
const errorC = addCurrentInput(errorB, input, hasInput)
return errorC
}

const ERROR_HANDLERS = {
input: handleInputError,
definition: handleDefinitionError,
keyword: handleKeywordError,
}

// The `prefix` is the name of the type of property to show in error
// message and warnings such as "Option".
const addInputPrefix = function (error, prefix, originalName) {
return wrapError(error, `${prefix} "${originalName}"`)
}

const addDefinitionPrefix = function ({
error,
prefix,
originalName,
keyword,
isValidation,
}) {
const suffix = isValidation ? 'definition' : 'must have a valid definition:\n'
return wrapError(
error,
`${prefix} "${originalName}"'s keyword "${keyword}" ${suffix}`,
)
}

const addKeywordPrefix = function ({ error, prefix, originalName, keyword }) {
return wrapError(
error,
`${prefix} "${originalName}"'s keyword "${keyword}" bug:`,
)
}

// Add the current definition as error suffix
const addCurrentDefinition = function (error, definition) {
return definition === undefined
? error
: wrapErrorValue(error, 'Current definition', definition)
}

// Add the example definition as error suffix
const addExampleDefinition = function (error, exampleDefinition) {
return exampleDefinition === undefined
? error
: wrapErrorValue(error, 'Example definition', exampleDefinition)
}

// Add the current input as error suffix
const addCurrentInput = function (error, input, hasInput) {
return hasInput ? wrapErrorValue(error, 'Current input', input) : error
}

// Add the example input as error suffix
const addExampleInput = function ({ error, example, hasInput, test }) {
return example !== undefined && (hasInput || test !== undefined)
? wrapErrorValue(error, 'Example input', example)
: error
}

const wrapErrorValue = function (error, name, value) {
return wrapError(error, `\n${name}:${serializeValue(value)}`)
}

const serializeValue = function (value) {
const valueStr = inspect(value)
const separator = valueStr.includes('\n') ? '\n' : ' '
return `${separator}${valueStr}`
}
/* eslint-enable max-lines */
33 changes: 33 additions & 0 deletions src/config/normalize/lib/call/prefix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { wrapError } from '../../../../error/wrap.js'

// The `prefix` is the name of the type of property to show in error
// message and warnings such as "Option".
export const addInputPrefix = function (error, prefix, originalName) {
return wrapError(error, `${prefix} "${originalName}"`)
}

export const addDefinitionPrefix = function ({
error,
prefix,
originalName,
keyword,
isValidation,
}) {
const suffix = isValidation ? 'definition' : 'must have a valid definition:\n'
return wrapError(
error,
`${prefix} "${originalName}"'s keyword "${keyword}" ${suffix}`,
)
}

export const addKeywordPrefix = function ({
error,
prefix,
originalName,
keyword,
}) {
return wrapError(
error,
`${prefix} "${originalName}"'s keyword "${keyword}" bug:`,
)
}
39 changes: 39 additions & 0 deletions src/config/normalize/lib/call/suffix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { inspect } from 'util'

import { wrapError } from '../../../../error/wrap.js'

// Add the current definition as error suffix
export const addCurrentDefinition = function (error, definition) {
return definition === undefined
? error
: wrapErrorValue(error, 'Current definition', definition)
}

// Add the example definition as error suffix
export const addExampleDefinition = function (error, exampleDefinition) {
return exampleDefinition === undefined
? error
: wrapErrorValue(error, 'Example definition', exampleDefinition)
}

// Add the current input as error suffix
export const addCurrentInput = function (error, input, hasInput) {
return hasInput ? wrapErrorValue(error, 'Current input', input) : error
}

// Add the example input as error suffix
export const addExampleInput = function ({ error, example, hasInput, test }) {
return example !== undefined && (hasInput || test !== undefined)
? wrapErrorValue(error, 'Example input', example)
: error
}

const wrapErrorValue = function (error, name, value) {
return wrapError(error, `\n${name}:${serializeValue(value)}`)
}

const serializeValue = function (value) {
const valueStr = inspect(value)
const separator = valueStr.includes('\n') ? '\n' : ' '
return `${separator}${valueStr}`
}

0 comments on commit 91f92bb

Please sign in to comment.