From ec911e34382835474fc3e8b80fa78699afe7c7c2 Mon Sep 17 00:00:00 2001 From: Rafa Mel Date: Sat, 27 Apr 2019 04:20:13 +0200 Subject: [PATCH] feat(exposed): wraps all exposed fns errors as WrappedError, so their stacktrace doesn't get logged --- src/core/paths/index.ts | 1 + src/exposed/exec/parallel.ts | 57 ++++++++++++++++++---------------- src/exposed/exec/series.ts | 29 +++++++++-------- src/exposed/json.ts | 29 +++++++++-------- src/exposed/options.ts | 3 +- src/exposed/prompts/confirm.ts | 51 ++++++++++++++++-------------- src/exposed/prompts/select.ts | 41 +++++++++++++----------- src/exposed/tags/ensure.ts | 17 +++++----- src/exposed/tags/exists.ts | 17 +++++----- src/exposed/tags/log.ts | 11 ++++--- src/exposed/tags/rm.ts | 17 +++++----- src/exposed/tags/silent.ts | 2 +- src/utils/errors.ts | 1 - 13 files changed, 153 insertions(+), 123 deletions(-) diff --git a/src/core/paths/index.ts b/src/core/paths/index.ts index 7addf89..bc4b3fe 100644 --- a/src/core/paths/index.ts +++ b/src/core/paths/index.ts @@ -35,6 +35,7 @@ export async function getRootPaths(directories: { } catch (err) { if (!root) return null; throw wrap.ensure(err, { + allow: [], message: `root scope couldn't be retrieved: ${root}` }); } diff --git a/src/exposed/exec/parallel.ts b/src/exposed/exec/parallel.ts index 2de1280..bccecae 100644 --- a/src/exposed/exec/parallel.ts +++ b/src/exposed/exec/parallel.ts @@ -29,35 +29,38 @@ export interface IParallel { * @returns A `TScript`, as a function, that won't be executed until called by `kpo` -hence, calling `parallel` won't have any effect until the returned function is called. */ const parallel: IParallel = function parallel(commands, options = {}) { - return async function parallel(args?: string[]): Promise { - const argv: string[] = Array.isArray(commands) - ? commands.concat() - : [commands]; + return (args?: string[]): Promise => { + return wrap.throws(async () => { + const argv: string[] = Array.isArray(commands) + ? commands.concat() + : [commands]; - if (options.names && options.names.length) { - argv.push('--names', options.names.join(',')); - } - if (options.colors && options.colors.length) { - argv.push('-c', options.colors.join(',')); - } - if (options.early) { - argv.push('--kill-others-on-fail'); - } + if (options.names && options.names.length) { + argv.push('--names', options.names.join(',')); + } + if (options.colors && options.colors.length) { + argv.push('-c', options.colors.join(',')); + } + if (options.early) { + argv.push('--kill-others-on-fail'); + } - try { - await core.exec( - require.resolve('concurrently/bin/concurrently'), - args ? argv.concat(args) : argv, - true, - options - ); - } catch (e) { - const err = wrap.ensure(e, { - message: 'Parallel commands execution failed' - }); - if (options.silent) logger.error(err); - else throw err; - } + try { + await core.exec( + require.resolve('concurrently/bin/concurrently'), + args ? argv.concat(args) : argv, + true, + options + ); + } catch (e) { + const err = wrap.ensure(e, { + allow: [], + message: 'Parallel commands execution failed' + }); + if (options.silent) logger.error(err); + else throw err; + } + }); }; }; diff --git a/src/exposed/exec/series.ts b/src/exposed/exec/series.ts index 93d5340..de8d991 100644 --- a/src/exposed/exec/series.ts +++ b/src/exposed/exec/series.ts @@ -1,6 +1,7 @@ import core from '~/core'; import { IExecOptions, TScript, IOfType } from '~/types'; import logger from '~/utils/logger'; +import { wrap } from '~/utils/errors'; export interface ISeriesOptions extends IExecOptions { /** @@ -26,21 +27,23 @@ export interface ISeries { * @returns A `TScript`, as a function, that won't be executed until called by `kpo` -hence, calling `series` won't have any effect until the returned function is called. */ const series: ISeries = function series(commands, options = {}) { - return async function series(args?: string[]): Promise { - if (!Array.isArray(commands)) commands = [commands]; + return (args?: string[]): Promise => { + return wrap.throws(async () => { + if (!Array.isArray(commands)) commands = [commands]; - let err: Error | null = null; - for (let command of commands) { - try { - if (!command) throw Error(`No command passed for series`); - await core.exec(command, args || [], false, options); - } catch (e) { - err = e; - if (options.force || options.silent) logger.error(err); - if (!options.force) break; + let err: Error | null = null; + for (let command of commands) { + try { + if (!command) throw Error(`No command passed for series`); + await core.exec(command, args || [], false, options); + } catch (e) { + err = e; + if (options.force || options.silent) logger.error(err); + if (!options.force) break; + } } - } - if (err && !options.silent) throw err; + if (err && !options.silent) throw err; + }); }; }; diff --git a/src/exposed/json.ts b/src/exposed/json.ts index 7d468c4..f28f41e 100644 --- a/src/exposed/json.ts +++ b/src/exposed/json.ts @@ -4,6 +4,7 @@ import { exists } from '~/utils/file'; import core from '~/core'; import { IOfType, TScript } from '~/types'; import { rejects } from 'errorish'; +import { wrap } from '~/utils/errors'; /** * Reads a JSON `file` and passes it as an argument to a callback `fn`. If the callback returns an object, **`file` will be overwritten** with its contents. `file` can be relative to the project's directory. @@ -13,19 +14,21 @@ export default function json( file: string, fn: (json: IOfType) => IOfType | void | Promise | void> ): TScript { - return async function json(): Promise { - if (!path.isAbsolute(file)) { - const paths = await core.paths(); - file = path.join(paths.directory, file); - } + return (): Promise => { + return wrap.throws(async () => { + if (!path.isAbsolute(file)) { + const paths = await core.paths(); + file = path.join(paths.directory, file); + } - await exists(file, { fail: true }); - const json = await fs.readJSON(file).catch(rejects); - const response = await fn(json); - if (response) { - await fs - .writeFile(file, JSON.stringify(response, null, 2)) - .catch(rejects); - } + await exists(file, { fail: true }); + const json = await fs.readJSON(file).catch(rejects); + const response = await fn(json); + if (response) { + await fs + .writeFile(file, JSON.stringify(response, null, 2)) + .catch(rejects); + } + }); }; } diff --git a/src/exposed/options.ts b/src/exposed/options.ts index 07fbd09..b44144c 100644 --- a/src/exposed/options.ts +++ b/src/exposed/options.ts @@ -1,6 +1,7 @@ import { options as _options } from '~/core'; import { IScopeOptions } from '~/types'; +import { wrap } from '~/utils/errors'; export default function options(opts: IScopeOptions): void { - return _options.setScope(opts); + return wrap.throws(() => _options.setScope(opts)); } diff --git a/src/exposed/prompts/confirm.ts b/src/exposed/prompts/confirm.ts index 77880ce..8f1d086 100644 --- a/src/exposed/prompts/confirm.ts +++ b/src/exposed/prompts/confirm.ts @@ -1,6 +1,7 @@ import { TScript } from '~/types'; import prompts from 'prompts'; import { status } from 'promist'; +import { wrap } from '~/utils/errors'; /** * Options taken by `confirm`. @@ -33,32 +34,34 @@ function confirm(options?: IConfirmOptions): TScript; * @returns A `TScript`, as a function, that won't be executed until called by `kpo` -hence, calling `confirm` won't have any effect until the returned function is called. */ function confirm(...args: any[]): TScript { - return async function confirm(): Promise { - const message: string = - args[0] && typeof args[0] === 'string' ? args[0] : 'Continue?'; - const options: IConfirmOptions = - args[1] || (args[0] && typeof args[0] !== 'string' ? args[0] : {}); + return (): Promise => { + return wrap.throws(async () => { + const message: string = + args[0] && typeof args[0] === 'string' ? args[0] : 'Continue?'; + const options: IConfirmOptions = + args[1] || (args[0] && typeof args[0] !== 'string' ? args[0] : {}); - const promise = status( - prompts({ - type: 'confirm', - name: 'value', - message: - message + - (options.timeout - ? ` [${Math.round(options.timeout / 100) / 10}s]` - : ''), - initial: options.initial === undefined ? true : options.initial - }) - ); + const promise = status( + prompts({ + type: 'confirm', + name: 'value', + message: + message + + (options.timeout + ? ` [${Math.round(options.timeout / 100) / 10}s]` + : ''), + initial: options.initial === undefined ? true : options.initial + }) + ); - if (options.timeout) { - setTimeout(() => { - if (promise.status === 'pending') process.stdin.emit('data', '\n'); - }, options.timeout); - } + if (options.timeout) { + setTimeout(() => { + if (promise.status === 'pending') process.stdin.emit('data', '\n'); + }, options.timeout); + } - const response = await promise; - return (response.value ? options.yes : options.no) || undefined; + const response = await promise; + return (response.value ? options.yes : options.no) || undefined; + }); }; } diff --git a/src/exposed/prompts/select.ts b/src/exposed/prompts/select.ts index 6918c88..921079d 100644 --- a/src/exposed/prompts/select.ts +++ b/src/exposed/prompts/select.ts @@ -1,5 +1,6 @@ import { TScript, IOfType } from '~/types'; import prompts from 'prompts'; +import { wrap } from '~/utils/errors'; /** * Options taken by `select`. @@ -24,26 +25,30 @@ function select(options?: ISelectOptions): TScript; * @returns A `TScript`, as a function, that won't be executed until called by `kpo` -hence, calling `select` won't have any effect until the returned function is called. */ function select(...args: any[]): TScript { - return async function select(): Promise { - const message: string = - args[0] && typeof args[0] === 'string' ? args[0] : 'Choose an option'; - const options: ISelectOptions = - args[1] || (args[0] && typeof args[0] !== 'string' ? args[0] : {}); + return (): Promise => { + return wrap.throws(async () => { + const message: string = + args[0] && typeof args[0] === 'string' ? args[0] : 'Choose an option'; + const options: ISelectOptions = + args[1] || (args[0] && typeof args[0] !== 'string' ? args[0] : {}); - const keys = Object.keys(options.values || {}); - if (!keys.length) throw Error(`No values passed to select`); + const keys = Object.keys(options.values || {}); + if (!keys.length) throw Error(`No values passed to select`); - const response = await prompts({ - type: 'select', - name: 'value', - message: message, - choices: keys.map((key) => ({ - title: key, - value: key - })), - initial: options.initial ? Math.max(0, keys.indexOf(options.initial)) : 0 - }); + const response = await prompts({ + type: 'select', + name: 'value', + message: message, + choices: keys.map((key) => ({ + title: key, + value: key + })), + initial: options.initial + ? Math.max(0, keys.indexOf(options.initial)) + : 0 + }); - return options.values[response.value] || undefined; + return options.values[response.value] || undefined; + }); }; } diff --git a/src/exposed/tags/ensure.ts b/src/exposed/tags/ensure.ts index f3472f1..cf289c0 100644 --- a/src/exposed/tags/ensure.ts +++ b/src/exposed/tags/ensure.ts @@ -4,6 +4,7 @@ import core from '~/core'; import { TScript } from '~/types'; import asTag from '~/utils/as-tag'; import { rejects } from 'errorish'; +import { wrap } from '~/utils/errors'; export default ensure; @@ -17,13 +18,15 @@ function ensure( * @returns A `TScript`, as a function, that won't be executed until called by `kpo` -hence, calling `ensure` won't have any effect until the returned function is called. */ function ensure(...args: any[]): TScript { - return async function ensure(): Promise { - let directory = asTag(args.shift(), ...args); - if (!path.isAbsolute(directory)) { - const paths = await core.paths(); - directory = path.join(paths.directory, directory); - } + return (): Promise => { + return wrap.throws(async () => { + let directory = asTag(args.shift(), ...args); + if (!path.isAbsolute(directory)) { + const paths = await core.paths(); + directory = path.join(paths.directory, directory); + } - await fs.ensureDir(directory).catch(rejects); + await fs.ensureDir(directory).catch(rejects); + }); }; } diff --git a/src/exposed/tags/exists.ts b/src/exposed/tags/exists.ts index 5ddd38a..aa46617 100644 --- a/src/exposed/tags/exists.ts +++ b/src/exposed/tags/exists.ts @@ -3,6 +3,7 @@ import core from '~/core'; import asTag from '~/utils/as-tag'; import { exists as _exists } from '~/utils/file'; import { TScript } from '~/types'; +import { wrap } from '~/utils/errors'; export default exists; @@ -16,12 +17,14 @@ function exists( * @returns A `TScript`, as a function, that won't be executed until called by `kpo` -hence, calling `exists` won't have any effect until the returned function is called. */ function exists(...args: any[]): TScript { - return async function exists(): Promise { - let file = asTag(args.shift(), ...args); - if (!path.isAbsolute(file)) { - const paths = await core.paths(); - file = path.join(paths.directory, file); - } - await _exists(file, { fail: true }); + return (): Promise => { + return wrap.throws(async () => { + let file = asTag(args.shift(), ...args); + if (!path.isAbsolute(file)) { + const paths = await core.paths(); + file = path.join(paths.directory, file); + } + await _exists(file, { fail: true }); + }); }; } diff --git a/src/exposed/tags/log.ts b/src/exposed/tags/log.ts index 273f106..7c6dd80 100644 --- a/src/exposed/tags/log.ts +++ b/src/exposed/tags/log.ts @@ -1,5 +1,6 @@ import { TScript } from '~/types'; import asTag from '~/utils/as-tag'; +import { wrap } from '~/utils/errors'; export default log; @@ -10,9 +11,11 @@ function log(literals: TemplateStringsArray, ...placeholders: any[]): TScript; * @returns A `TScript`, as a function, that won't be executed until called by `kpo` -hence, calling `log` won't have any effect until the returned function is called. */ function log(...args: any[]): TScript { - return function log(): void { - const message = asTag(args.shift(), ...args); - // eslint-disable-next-line no-console - console.log(message); + return (): void => { + return wrap.throws(() => { + const message = asTag(args.shift(), ...args); + // eslint-disable-next-line no-console + console.log(message); + }); }; } diff --git a/src/exposed/tags/rm.ts b/src/exposed/tags/rm.ts index 25e9816..89dffda 100644 --- a/src/exposed/tags/rm.ts +++ b/src/exposed/tags/rm.ts @@ -4,6 +4,7 @@ import core from '~/core'; import asTag from '~/utils/as-tag'; import { TScript } from '~/types'; import { rejects } from 'errorish'; +import { wrap } from '~/utils/errors'; export default rm; @@ -14,13 +15,15 @@ function rm(literals: TemplateStringsArray, ...placeholders: any[]): TScript; * @returns A `TScript`, as a function, that won't be executed until called by `kpo` -hence, calling `rm` won't have any effect until the returned function is called. */ function rm(...args: any[]): TScript { - return async function rm(): Promise { - let file = asTag(args.shift(), ...args); - if (!path.isAbsolute(file)) { - const paths = await core.paths(); - file = path.join(paths.directory, file); - } + return (): Promise => { + return wrap.throws(async () => { + let file = asTag(args.shift(), ...args); + if (!path.isAbsolute(file)) { + const paths = await core.paths(); + file = path.join(paths.directory, file); + } - await fs.remove(file).catch(rejects); + await fs.remove(file).catch(rejects); + }); }; } diff --git a/src/exposed/tags/silent.ts b/src/exposed/tags/silent.ts index 59d8876..f13e1c1 100644 --- a/src/exposed/tags/silent.ts +++ b/src/exposed/tags/silent.ts @@ -16,8 +16,8 @@ function silent( */ function silent(...args: any[]): TScript { return async function silent(argv): Promise { - const command = asTag(args.shift(), ...args); try { + const command = asTag(args.shift(), ...args); await core.exec(command, argv, false); } catch (err) { logger.error(err); diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 9559577..acab18e 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -20,7 +20,6 @@ export const open = scope.set('_kpo_open_', { }); export const wrap = scope.set('_kpo_wrap_', { - allow: [], Error: CustomError, Errorish: WrappedError });