diff --git a/src/combination/ids/name.js b/src/combination/ids/name.js index d36ee3ef9..80b779f3c 100644 --- a/src/combination/ids/name.js +++ b/src/combination/ids/name.js @@ -6,7 +6,7 @@ import { getCombDimensionsIds } from './get.js' // Retrieve error message prefix showing a combination's dimension ids export const getCombinationPrefix = function (combination, noDimensions) { const combinationName = getCombinationName(combination, noDimensions) - return combinationName === '' ? '' : `In ${combinationName}:\n` + return combinationName === '' ? undefined : `In ${combinationName}.` } // Retrieve string with each combination's dimension id. diff --git a/src/combination/tasks/find.js b/src/combination/tasks/find.js index bd8a3c9b1..afcea5197 100644 --- a/src/combination/tasks/find.js +++ b/src/combination/tasks/find.js @@ -1,5 +1,4 @@ import { PluginError } from '../../error/main.js' -import { wrapError } from '../../error/wrap.js' import { measureCombinations } from '../../run/measure/main.js' // Each combination has its own process, in order to prevent them from @@ -27,8 +26,8 @@ export const findTasks = async function ({ ) validateDuplicateTaskIds(ids) return ids.map((id) => ({ id, taskPath, runner })) - } catch (error) { - throw wrapError(error, `In tasks file "${taskPath}":\n`) + } catch (cause) { + throw new Error(`Tasks file: "${taskPath}"`, { cause }) } } diff --git a/src/combination/tasks/load.js b/src/combination/tasks/load.js index 4b60e9175..127d3e2ee 100644 --- a/src/combination/tasks/load.js +++ b/src/combination/tasks/load.js @@ -1,5 +1,4 @@ import { PluginError } from '../../error/main.js' -import { wrapError } from '../../error/wrap.js' import { computeRunnerVersions } from '../../top/system/versions/compute.js' // Select the runners and retrieve their related spawn options using @@ -30,7 +29,7 @@ export const loadRunner = async function ( const launchRunner = async function ({ id, config, launch }) { try { return await launch(config) - } catch (error) { - throw wrapError(error, `In runner '${id}':`, PluginError) + } catch (cause) { + throw new PluginError(`Could not launch runner "${id}"`, { cause }) } } diff --git a/src/config/load/contents.js b/src/config/load/contents.js index e9dad30e4..6aa4043ee 100644 --- a/src/config/load/contents.js +++ b/src/config/load/contents.js @@ -4,7 +4,6 @@ import { inspect } from 'util' import isPlainObj from 'is-plain-obj' import { UserError } from '../../error/main.js' -import { wrapError } from '../../error/wrap.js' import { importJsDefault } from '../../utils/import.js' import { loadYamlFile } from '../../utils/yaml.js' @@ -32,8 +31,8 @@ export const loadConfigContents = async function (configPath) { const loadContents = async function (loadFunc, configPath) { try { return await loadFunc(configPath) - } catch (error) { - throw wrapError(error, 'File cannot be loaded:', UserError) + } catch (cause) { + throw new UserError('File cannot be loaded.', { cause }) } } diff --git a/src/config/load/main.js b/src/config/load/main.js index 7d1294283..8739a3695 100644 --- a/src/config/load/main.js +++ b/src/config/load/main.js @@ -1,7 +1,6 @@ import { dirname } from 'path' import { UserError } from '../../error/main.js' -import { wrapError } from '../../error/wrap.js' import { addBases } from '../cwd.js' import { deepMerge } from '../merge.js' @@ -120,12 +119,10 @@ const getParentConfigWithBases = async function (configPath, childConfigPaths) { childConfigPathsA, ) return configWithBases - } catch (error) { - throw wrapError( - error, - `Invalid configuration file '${configPath}':\n`, - UserError, - ) + } catch (cause) { + throw new UserError(`Invalid configuration file '${configPath}'.`, { + cause, + }) } } diff --git a/src/config/load/resolve.js b/src/config/load/resolve.js index e7984800f..d721fe761 100644 --- a/src/config/load/resolve.js +++ b/src/config/load/resolve.js @@ -1,7 +1,5 @@ import { createRequire } from 'module' -import { wrapError } from '../../error/wrap.js' - // The `config` can be: // - a Node module name starting with "[@scope/]spyd-config-" // - a file path @@ -19,10 +17,10 @@ const resolveNpm = function (configOpt, cwd) { try { return createRequire(`${cwd}/`).resolve(configOpt) } catch (error) { - throw wrapError( - error, + throw new Error( `must be a valid package name. -This Node module was not found, please ensure it is installed.\n`, +This Node module was not found, please ensure it is installed:\n`, + { cause: error }, ) } } diff --git a/src/config/normalize/main.js b/src/config/normalize/main.js index 517dc4b6f..72051db08 100644 --- a/src/config/normalize/main.js +++ b/src/config/normalize/main.js @@ -1,5 +1,4 @@ import { UserError } from '../../error/main.js' -import { wrapError } from '../../error/wrap.js' import { normalizeInputs } from './lib/main.js' @@ -13,7 +12,7 @@ export const normalizeConfig = async function (config, rules, opts) { }) if (error) { - throw wrapError(error, '', UserError) + throw new UserError('', { cause: error }) } return inputs diff --git a/src/config/plugin/error.js b/src/config/plugin/error.js index 3fbb60e81..7903a86b4 100644 --- a/src/config/plugin/error.js +++ b/src/config/plugin/error.js @@ -1,13 +1,12 @@ import { CoreError, PluginError, UserError } from '../../error/main.js' import { normalizeError } from '../../error/normalize/main.js' -import { wrapError } from '../../error/wrap.js' // Translate error classes from the plugins library to error classes from this // library export const handlePluginsError = function (error) { const { name } = normalizeError(error) const ErrorType = name in ERROR_MAP ? ERROR_MAP[name] : CoreError - return wrapError(error, '', ErrorType) + return new ErrorType('', { cause: error }) } const ERROR_MAP = { diff --git a/src/history/data/fs.js b/src/history/data/fs.js index 41c252468..95ec5aa96 100644 --- a/src/history/data/fs.js +++ b/src/history/data/fs.js @@ -4,7 +4,6 @@ import { pathExists } from 'path-exists' import { isDirectory, isFile } from 'path-type' import { UserError } from '../../error/main.js' -import { wrapError } from '../../error/wrap.js' // Find all filenames of the history directory export const listFilenames = async function (historyDir) { @@ -15,8 +14,8 @@ export const listFilenames = async function (historyDir) { export const readRawResult = async function (path) { try { return await readFile(path, 'utf8') - } catch (error) { - throw wrapError(error, 'History file could not be read:', UserError) + } catch (cause) { + throw new UserError('History file could not be read.', { cause }) } } @@ -24,8 +23,8 @@ export const readRawResult = async function (path) { export const writeRawResult = async function (path, rawResultStr) { try { return await writeFile(path, rawResultStr) - } catch (error) { - throw wrapError(error, 'History file could not be written:', UserError) + } catch (cause) { + throw new UserError('History file could not be written.', { cause }) } } @@ -33,8 +32,8 @@ export const writeRawResult = async function (path, rawResultStr) { export const deleteRawResult = async function (path) { try { await unlink(path) - } catch (error) { - throw wrapError(error, 'History file could not be deleted:', UserError) + } catch (cause) { + throw new UserError('History file could not be deleted.', { cause }) } } @@ -51,12 +50,10 @@ export const ensureHistoryDir = async function (historyDir) { try { await mkdir(historyDir, { recursive: true }) - } catch (error) { - throw wrapError( - error, - `Could not create history directory "${historyDir}"\n`, - UserError, - ) + } catch (cause) { + throw new UserError(`Could not create history directory "${historyDir}"`, { + cause, + }) } } diff --git a/src/history/delta/find.js b/src/history/delta/find.js index 81f54edb9..b6ac8dc6c 100644 --- a/src/history/delta/find.js +++ b/src/history/delta/find.js @@ -1,5 +1,4 @@ import { UserError } from '../../error/main.js' -import { wrapError } from '../../error/wrap.js' import { getDeltaError } from './error.js' import { findFormat } from './formats/main.js' @@ -107,7 +106,7 @@ const findByDelta = async function ({ metadataGroups, delta, cwd, name }) { try { return await format.find(metadataGroups, delta.value, cwd) - } catch (error) { - throw wrapError(error, getDeltaError(delta, name)) + } catch (cause) { + throw new UserError(`${getDeltaError(delta, name)}:`, { cause }) } } diff --git a/src/history/delta/transform.js b/src/history/delta/transform.js index e41e7595b..85d5bf708 100644 --- a/src/history/delta/transform.js +++ b/src/history/delta/transform.js @@ -1,4 +1,3 @@ -import { wrapError } from '../../error/wrap.js' import { findValue } from '../../utils/find.js' import { getDeltaTypeMessage } from './error.js' @@ -30,8 +29,8 @@ const parseDelta = function ({ parse, type }, deltaOriginal) { try { const value = parse(deltaOriginal) return value === undefined ? undefined : { type, value } - } catch (error) { - throw wrapError(error, `\nType: ${getDeltaTypeMessage(type)}`) + } catch (cause) { + throw new Error(`must be a valid ${getDeltaTypeMessage(type)}.`, { cause }) } } diff --git a/src/history/serialize/contents.js b/src/history/serialize/contents.js index 452194b96..5709339ef 100644 --- a/src/history/serialize/contents.js +++ b/src/history/serialize/contents.js @@ -1,5 +1,4 @@ import { UserError } from '../../error/main.js' -import { wrapError } from '../../error/wrap.js' // Parse the contents of a rawResult. // Parsing/serializing rawResults is abstracted away from the main store logic, @@ -7,8 +6,8 @@ import { wrapError } from '../../error/wrap.js' export const parseRawResult = function (rawResultStr) { try { return JSON.parse(rawResultStr) - } catch (error) { - throw wrapError(error, 'History files is invalid JSON:', UserError) + } catch (cause) { + throw new UserError('History file is invalid JSON.', { cause }) } } diff --git a/src/report/contents/get.js b/src/report/contents/get.js index 732799852..017fa64f9 100644 --- a/src/report/contents/get.js +++ b/src/report/contents/get.js @@ -1,7 +1,6 @@ import omit from 'omit.js' import { PluginError } from '../../error/main.js' -import { wrapError } from '../../error/wrap.js' import { FORMATS } from '../formats/list.js' // Retrieve reporter's contents by calling all `reporter.report()` then @@ -40,8 +39,8 @@ const callReportFunc = async function ({ startData, ]) return { content, result, format, footerString, output, colors } - } catch (error) { - throw wrapError(error, `When calling reporter "${id}":`, PluginError) + } catch (cause) { + throw new PluginError(`Could not call reporter "${id}".`, { cause }) } } diff --git a/src/report/contents/insert.js b/src/report/contents/insert.js index d68aa28cf..770ac928c 100644 --- a/src/report/contents/insert.js +++ b/src/report/contents/insert.js @@ -6,7 +6,6 @@ import stripFinalNewline from 'strip-final-newline' import writeFileAtomic from 'write-file-atomic' import { UserError } from '../../error/main.js' -import { wrapError } from '../../error/wrap.js' // By default, the `output` configuration property overwrites the file. // However, contents can be inserted instead between any two lines with the @@ -34,8 +33,8 @@ export const detectInsert = async function (output) { const getFileContent = async function (output) { try { return await readFile(output, 'utf8') - } catch (error) { - throw wrapError(error, `Could not read "output" "${output}"\n`, UserError) + } catch (cause) { + throw new UserError(`Could not read "output" "${output}"`, { cause }) } } @@ -89,7 +88,7 @@ const END_LINE_TOKEN = 'spyd-end' const writeFileContent = async function (output, fileContent) { try { await writeFileAtomic(output, fileContent) - } catch { - throw wrapError(`Could not write to file "${output}"\n`, UserError) + } catch (cause) { + throw new UserError(`Could not write to file "${output}"`, { cause }) } } diff --git a/src/report/contents/output.js b/src/report/contents/output.js index efa42fb73..c1ae9675c 100644 --- a/src/report/contents/output.js +++ b/src/report/contents/output.js @@ -4,7 +4,6 @@ import { dirname } from 'path' import writeFileAtomic from 'write-file-atomic' import { UserError } from '../../error/main.js' -import { wrapError } from '../../error/wrap.js' import { printToStdout } from '../tty.js' import { detectInsert, insertContents } from './insert.js' @@ -47,11 +46,7 @@ const overwriteContents = async function (output, content) { try { await writeFileAtomic(output, content) - } catch (error) { - throw wrapError( - error, - `Could not write to "output" "${output}"\n`, - UserError, - ) + } catch (cause) { + throw new UserError(`Could not write to "output" "${output}"`, { cause }) } } diff --git a/src/report/start_end.js b/src/report/start_end.js index 4a94f2c68..3a39e1af9 100644 --- a/src/report/start_end.js +++ b/src/report/start_end.js @@ -1,5 +1,4 @@ import { PluginError } from '../error/main.js' -import { wrapError } from '../error/wrap.js' // Call all `reporter.start()` export const startReporters = async function (config) { @@ -15,12 +14,10 @@ const startReporter = async function (reporter) { try { const startData = await reporter.start() return { ...reporter, startData } - } catch (error) { - throw wrapError( - error, - `When starting reporter "${reporter.id}":`, - PluginError, - ) + } catch (cause) { + throw new PluginError(`Could not start reporter "${reporter.id}".`, { + cause, + }) } } @@ -36,7 +33,7 @@ const endReporter = async function ({ end, startData, id }) { try { await end(startData) - } catch (error) { - throw wrapError(error, `When ending reporter "${id}":`, PluginError) + } catch (cause) { + throw new PluginError(`Could not end reporter "${id}".`, { cause }) } } diff --git a/src/run/logs/additional.js b/src/run/logs/additional.js index 05c5bba0d..4a0772dd6 100644 --- a/src/run/logs/additional.js +++ b/src/run/logs/additional.js @@ -14,7 +14,7 @@ export const getAdditionalMessage = function (taskLogs) { return '' } - return `${additionalMessage.message}\n` + return `${additionalMessage.message}\n\n` } const ADDITIONAL_MESSAGES = [ diff --git a/src/run/logs/error.js b/src/run/logs/error.js index 3d4e80373..63f7838ff 100644 --- a/src/run/logs/error.js +++ b/src/run/logs/error.js @@ -1,8 +1,6 @@ import { Buffer } from 'buffer' import { open } from 'fs/promises' -import { wrapError } from '../../error/wrap.js' - import { getAdditionalMessage } from './additional.js' import { normalizeLogs } from './normalize.js' @@ -21,7 +19,9 @@ export const addErrorTaskLogs = async function (error, logsPath) { } const additionalMessage = getAdditionalMessage(taskLogsA) - return wrapError(error, `\n${additionalMessage}\nTask logs:\n${taskLogsA}`) + return new Error(`${additionalMessage}Task logs:\n${taskLogsA}`, { + cause: error, + }) } // Read the last lines from the logs file diff --git a/src/run/measure/single.js b/src/run/measure/single.js index 925ff356f..ac41551a5 100644 --- a/src/run/measure/single.js +++ b/src/run/measure/single.js @@ -1,6 +1,5 @@ import { getCombinationPrefix } from '../../combination/ids/name.js' import { useConfigSelectors } from '../../config/select/use.js' -import { wrapError } from '../../error/wrap.js' import { startLogs, stopLogs, hasLogs } from '../logs/create.js' import { addErrorTaskLogs } from '../logs/error.js' import { startLogsStream, stopLogsStream } from '../logs/stream.js' @@ -15,7 +14,6 @@ import { } from '../process/runner.js' import { throwIfStopped } from '../stop/error.js' -// eslint-disable-next-line import/max-dependencies import { runEvents } from './events.js' // Measure a single combination @@ -99,6 +97,7 @@ const handleError = async function ({ onTaskExit, noDimensions, ...args }) { try { return await Promise.race([onTaskExit, runEvents(args)]) } catch (error) { - throw wrapError(error, getCombinationPrefix(args.combination, noDimensions)) + const prefix = getCombinationPrefix(args.combination, noDimensions) + throw prefix === undefined ? error : new Error(prefix, { cause: error }) } } diff --git a/src/run/process/ipc.js b/src/run/process/ipc.js index 1d79fee3d..4053f79ce 100644 --- a/src/run/process/ipc.js +++ b/src/run/process/ipc.js @@ -5,7 +5,6 @@ import { promisify } from 'util' import getStream from 'get-stream' import { PluginError } from '../../error/main.js' -import { wrapError } from '../../error/wrap.js' import { throwOnStreamError } from './error.js' import { throwOnTaskError } from './task_error.js' @@ -41,8 +40,8 @@ const sendPayload = async function (payload, { res }) { throwOnStreamError(res), promisify(res.end.bind(res))(payloadString), ]) - } catch (error) { - throw wrapError(error, 'Could not send HTTP response:', PluginError) + } catch (cause) { + throw new PluginError('Could not send HTTP response.', { cause }) } } @@ -71,7 +70,7 @@ const parseReturnValue = async function (req) { ]) const returnValue = JSON.parse(returnValueString) return returnValue - } catch (error) { - throw wrapError(error, 'Could not receive HTTP request:', PluginError) + } catch (cause) { + throw new PluginError('Could not receive HTTP request.', { cause }) } } diff --git a/src/run/process/runner.js b/src/run/process/runner.js index df68c26ac..fa63ab4ff 100644 --- a/src/run/process/runner.js +++ b/src/run/process/runner.js @@ -1,5 +1,4 @@ import { noUnhandledRejection } from '../../error/unhandled_rejection.js' -import { wrapError } from '../../error/wrap.js' import { spawnProcess } from '../../utils/spawn.js' import { throwOnSpawnExit, throwOnTaskExit } from './error.js' @@ -61,8 +60,8 @@ export const spawnRunnerProcess = async function ({ await waitForIpcSetup(childProcess, server) const onTaskExit = noUnhandledRejection(throwOnTaskExit(childProcess)) return { childProcess, onTaskExit } - } catch (error) { - throw wrapError(error, `In runner "${id}":`) + } catch (cause) { + throw new Error(`Could not start runner "${id}".`, { cause }) } } diff --git a/src/top/system/versions/compute.js b/src/top/system/versions/compute.js index 24251a7c1..292371e06 100644 --- a/src/top/system/versions/compute.js +++ b/src/top/system/versions/compute.js @@ -1,7 +1,6 @@ import pProps from 'p-props' import { PluginError } from '../../../error/main.js' -import { wrapError } from '../../../error/wrap.js' import { mapValues, mapKeys } from '../../../utils/map.js' import { spawnProcess } from '../../../utils/spawn.js' @@ -80,12 +79,11 @@ const getRunnerVersion = async function ({ version, id, spawnOptions, cwd }) { cwd, ) return stdout - } catch (error) { - throw wrapError( - error, + } catch (cause) { + throw new PluginError( `Could not start runner "${id}". -Retrieving runner versions failed: ${version.join(' ')}\n`, - PluginError, +Retrieving runner versions failed: ${version.join(' ')}`, + { cause }, ) } } diff --git a/src/utils/yaml.js b/src/utils/yaml.js index 4c3c323e3..93ed7d60d 100644 --- a/src/utils/yaml.js +++ b/src/utils/yaml.js @@ -3,8 +3,6 @@ import { extname } from 'path' import { load as loadYaml, JSON_SCHEMA } from 'js-yaml' -import { wrapError } from '../error/wrap.js' - // Load and parse YAML file export const loadYamlFile = async function (path) { validateFileExtension(path) @@ -25,15 +23,15 @@ const YAML_FILE_EXTENSIONS = new Set(['.yml', '.yaml']) const readYamlFile = async function (path) { try { return await readFile(path, 'utf8') - } catch (error) { - throw wrapError(error, `Could not read file '${path}'\n`) + } catch (cause) { + throw new Error(`Could not read file '${path}'.`, { cause }) } } const parseYaml = function (string, path) { try { return loadYaml(string, { schema: JSON_SCHEMA }) - } catch (error) { - throw wrapError(error, `Invalid YAML in file '${path}'\n`) + } catch (cause) { + throw new Error(`Invalid YAML in file '${path}'.`, { cause }) } }