From dc4fde5089ea58e0142b4c39187a9bf0a2f9de00 Mon Sep 17 00:00:00 2001 From: ehmicky Date: Sun, 29 May 2022 13:45:24 +0200 Subject: [PATCH] Refactoring --- src/config/normalize/lib/call.js | 121 ++++++++++++---------- src/config/normalize/lib/keywords/skip.js | 7 +- src/config/normalize/lib/prefix.js | 13 --- src/config/normalize/lib/validate.js | 19 ---- src/config/normalize/lib/warn.js | 10 +- src/utils/function.js | 4 - 6 files changed, 81 insertions(+), 93 deletions(-) delete mode 100644 src/config/normalize/lib/prefix.js delete mode 100644 src/config/normalize/lib/validate.js delete mode 100644 src/utils/function.js diff --git a/src/config/normalize/lib/call.js b/src/config/normalize/lib/call.js index 7713326de..dfc371c00 100644 --- a/src/config/normalize/lib/call.js +++ b/src/config/normalize/lib/call.js @@ -1,70 +1,92 @@ import { inspect } from 'util' import { wrapError } from '../../../error/wrap.js' -import { maybeFunction } from '../../../utils/function.js' -import { addPrefix } from './prefix.js' -import { handleValidateError } from './validate.js' - -export const callFunc = async function ({ func, input, info, hasInput, test }) { - if (hasInput) { - return await callInputFunc(func, input, info) - } - - if (test === undefined) { - return await callNoInputFunc(func, info) - } - - return await callConstraintFunc(func, info) -} - -// Most rule methods follow the same patterns: -// - Called with `input` and `info` +// Call a function from `test()`, `main()` or a definition. +// They are all: // - Optionally async -export const callInputFunc = async function (func, input, info) { +// - Called with same arguments +// - Error handled +export const callFunc = async function ({ + func, + input, + info: { originalName }, + info: { example, prefix, ...info }, + hasInput, + test, +}) { try { - return await callUserFunc(func.bind(undefined, input), info) + return await (hasInput ? func(input, info) : func(info)) } catch (error) { - const errorA = handleError(error, info) - const errorB = addCurrentValue(errorA, input) - throw addExampleValue(errorB, info) + throw handleError({ + error, + input, + example, + prefix, + originalName, + hasInput, + test, + }) } } -// Some methods are not called with any `input` but their logic requires knowing -// whether it is undefined -const callConstraintFunc = async function (func, info) { - try { - return await callUserFunc(func, info) - } catch (error) { - const errorA = handleError(error, info) - throw addExampleValue(errorA, info) +const handleError = function ({ + error, + input, + example, + prefix, + originalName, + hasInput, + test, +}) { + const isValidation = isValidateError(error) + const errorA = addPrefix({ error, prefix, originalName, isValidation }) + + if (!isValidation) { + return errorA } + + // eslint-disable-next-line fp/no-mutation + errorA.validation = true + const errorB = addCurrentValue(errorA, input, hasInput) + const errorC = addExampleValue({ error: errorB, example, hasInput, test }) + return errorC } -// Some methods are not called with any input -const callNoInputFunc = async function (func, info) { - try { - return await callUserFunc(func, info) - } catch (error) { - throw handleError(error, info) - } +// 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 handleError = function (error, info) { - handleValidateError(error) - return addPrefix(error, info) +const addPrefix = function ({ error, prefix, originalName, isValidation }) { + const prefixA = getPrefix(prefix, originalName) + const prefixB = isValidation ? prefixA : `${prefixA}: ` + return wrapError(error, prefixB) +} + +// The `prefix` is the name of the type of property to show in error +// message and warnings such as "Option". +export const getPrefix = function (prefix, originalName) { + return `${prefix} "${originalName}"` } // Add the current input value as error suffix -const addCurrentValue = function (error, input) { - return error.validation - ? wrapErrorValue(error, 'Current value', input) - : error +const addCurrentValue = function (error, input, hasInput) { + return hasInput ? wrapErrorValue(error, 'Current value', input) : error } -const addExampleValue = function (error, { example }) { - return error.validation && example !== undefined +// Add the example as error suffix +const addExampleValue = function ({ error, example, hasInput, test }) { + return example !== undefined && (hasInput || test !== undefined) ? wrapErrorValue(error, 'Example value', example) : error } @@ -78,8 +100,3 @@ const serializeValue = function (value) { const separator = valueStr.includes('\n') ? '\n' : ' ' return `${separator}${valueStr}` } - -// eslint-disable-next-line no-unused-vars -const callUserFunc = async function (func, { example, prefix, ...info }) { - return await maybeFunction(func, info) -} diff --git a/src/config/normalize/lib/keywords/skip.js b/src/config/normalize/lib/keywords/skip.js index b6a573a41..7481350b0 100644 --- a/src/config/normalize/lib/keywords/skip.js +++ b/src/config/normalize/lib/keywords/skip.js @@ -1,4 +1,4 @@ -import { callInputFunc } from '../call.js' +import { callFunc } from '../call.js' // `undefined` definitions are always skipped because: // - It allows any keyword to be disabled by setting `definition` to @@ -35,7 +35,10 @@ export const shouldSkipKeyword = async function ({ // need to check the input during `test()` but should not pass it during // `main()` nor the definition function const hasSkippedTest = async function (test, input, info) { - return test !== undefined && !(await callInputFunc(test, input, info)) + return ( + test !== undefined && + !(await callFunc({ func: test, input, info, hasInput: true, test })) + ) } // Function definitions returning `undefined` are skipped, unless diff --git a/src/config/normalize/lib/prefix.js b/src/config/normalize/lib/prefix.js deleted file mode 100644 index 641dc4006..000000000 --- a/src/config/normalize/lib/prefix.js +++ /dev/null @@ -1,13 +0,0 @@ -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 addPrefix = function (error, info) { - const prefix = getPrefix(info) - const prefixA = error.validation ? prefix : `${prefix}: ` - return wrapError(error, prefixA) -} - -export const getPrefix = function ({ prefix, originalName }) { - return `${prefix} "${originalName}"` -} diff --git a/src/config/normalize/lib/validate.js b/src/config/normalize/lib/validate.js deleted file mode 100644 index 49e850fe1..000000000 --- a/src/config/normalize/lib/validate.js +++ /dev/null @@ -1,19 +0,0 @@ -// 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 -export const handleValidateError = function (error) { - if (isValidateError(error)) { - error.validation = true - } -} - -const isValidateError = function (error) { - return error instanceof Error && error.message.startsWith('must') -} diff --git a/src/config/normalize/lib/warn.js b/src/config/normalize/lib/warn.js index 1b783d3ee..542d27580 100644 --- a/src/config/normalize/lib/warn.js +++ b/src/config/normalize/lib/warn.js @@ -1,10 +1,14 @@ -import { getPrefix } from './prefix.js' +import { getPrefix } from './call.js' // When a new warning is returned, add it to the list -export const addWarning = function ({ warning }, warnings, info) { +export const addWarning = function ( + { warning }, + warnings, + { prefix, originalName }, +) { return warning === undefined ? warnings - : [...warnings, `${getPrefix(info)} ${warning}`] + : [...warnings, `${getPrefix(prefix, originalName)} ${warning}`] } // Log all warnings at the end. diff --git a/src/utils/function.js b/src/utils/function.js deleted file mode 100644 index 6ce02bfa7..000000000 --- a/src/utils/function.js +++ /dev/null @@ -1,4 +0,0 @@ -// A value to be either a function or directly a return value -export const maybeFunction = function (value, ...args) { - return typeof value === 'function' ? value(...args) : value -}