Repository: oclif/core
Pull request #102 (2021-01-08 00:50:30)

feat: rm duplicate ts-node registering

codecov[bot]: # [Codecov](https://codecov.io/gh/oclif/core/pull/102?src=pr&el=h1) Report
> Merging [#102](https://codecov.io/gh/oclif/core/pull/102?src=pr&el=desc) (29d5e37) into [master](https://codecov.io/gh/oclif/core/commit/4b44270a1f584d8ee4995b41d93282b24ce11440?el=desc) (4b44270) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/oclif/core/pull/102/graphs/tree.svg?width=650&height=150&src=pr&token=K0kGk9tefA)](https://codecov.io/gh/oclif/core/pull/102?src=pr&el=tree)

```diff
@@          Coverage Diff           @@
##           master    #102   +/-   ##
======================================
  Coverage    0.00%   0.00%           
======================================
  Files           3       3           
  Lines          15      15           
  Branches        2       2           
======================================
  Misses         15      15           
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/oclif/core/pull/102?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/oclif/core/pull/102?src=pr&el=footer). Last update [4b44270...29d5e37](https://codecov.io/gh/oclif/core/pull/102?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/package.json b/package.json
index 9e093da..d5fd767 100644
--- a/package.json
+++ b/package.json
@@ -76,7 +76,8 @@
   "repository": "oclif/core",
   "oclif": {
     "bin": "oclif",
-    "plugins": [
+    "devPlugins": [
+      "@oclif/plugin-help",
       "@oclif/plugin-plugins"
     ]
   },
diff --git a/src/command.ts b/src/command.ts
index 4377c1d..0914577 100644
--- a/src/command.ts
+++ b/src/command.ts
@@ -1,12 +1,10 @@
 import {format, inspect} from 'util'
 
-import {Config, toCached} from './config'
+import {Config} from './config'
 import * as Interfaces from './interfaces'
 import * as Errors from './errors'
 import {PrettyPrintableError} from './errors'
 import * as Parser from './parser'
-import {HelpBase, getHelpClass} from './help'
-import {sortBy, uniqBy} from './util'
 
 const pjson = require('../package.json')
 
@@ -154,22 +152,15 @@ export default abstract class Command {
     const g: any = global
     g['http-call'] = g['http-call'] || {}
     g['http-call']!.userAgent = this.config.userAgent
-    if (this._helpOverride()) return this._help()
   }
 
-  protected parse<F, A extends { [name: string]: any }>(options?: Interfaces.Input<F>, argv = this.argv): Interfaces.ParserOutput<F, A> {
+  protected async parse<F, A extends { [name: string]: any }>(options?: Interfaces.Input<F>, argv = this.argv): Promise<Interfaces.ParserOutput<F, A>> {
     if (!options) options = this.constructor as any
     return Parser.parse(argv, {context: this, ...options})
   }
 
   protected async catch(err: any): Promise<any> {
     if (!err.message) throw err
-    if (err.message.match(/Unexpected arguments?: (-h|--help|help)(,|\n)/)) {
-      return this._help()
-    }
-    if (err.message.match(/Unexpected arguments?: (-v|--version|version)(,|\n)/)) {
-      return this._version()
-    }
     try {
       const {cli} = require('cli-ux')
       const chalk = require('chalk') // eslint-disable-line node/no-extraneous-require
@@ -187,30 +178,4 @@ export default abstract class Command {
       console.error(error)
     }
   }
-
-  protected _help() {
-    const HelpClass = getHelpClass(this.config)
-    const help: HelpBase = new HelpClass(this.config)
-    const cmd = toCached(this.ctor as any as Interfaces.Command.Class)
-    if (!cmd.id) cmd.id = ''
-    let topics = this.config.topics
-    topics = topics.filter((t: any) => !t.hidden)
-    topics = sortBy(topics, (t: any) => t.name)
-    topics = uniqBy(topics, (t: any) => t.name)
-    help.showCommandHelp(cmd, topics)
-    return this.exit(0)
-  }
-
-  protected _helpOverride(): boolean {
-    for (const arg of this.argv) {
-      if (arg === '--help') return true
-      if (arg === '--') return false
-    }
-    return false
-  }
-
-  protected _version() {
-    this.log(this.config.userAgent)
-    return this.exit(0)
-  }
 }
diff --git a/src/config/plugin.ts b/src/config/plugin.ts
index 238fb0c..ec1e681 100644
--- a/src/config/plugin.ts
+++ b/src/config/plugin.ts
@@ -13,8 +13,6 @@ import {Topic} from '../interfaces/topic'
 import {tsPath} from './ts-node'
 import {compact, exists, flatMap, loadJSON, mapValues} from './util'
 
-const ROOT_INDEX_CMD_ID = ''
-
 const _pjson = require('../../package.json')
 
 const hasManifest = function (p: string): boolean {
@@ -174,8 +172,6 @@ export class Plugin implements IPlugin {
       const p = path.parse(file)
       const topics = p.dir.split('/')
       const command = p.name !== 'index' && p.name
-      // support src/commands/index as a "root" command
-      if (!command && this.type === 'core' && p.dir.length === 0 && p.name === 'index') return ROOT_INDEX_CMD_ID
       return [...topics, command].filter(f => f).join(':')
     })
     this._debug('found commands', ids)
diff --git a/src/config/ts-node.ts b/src/config/ts-node.ts
index c3fb2c7..106bc97 100644
--- a/src/config/ts-node.ts
+++ b/src/config/ts-node.ts
@@ -1,16 +1,11 @@
 import * as fs from 'fs'
 import * as path from 'path'
-import * as TSNode from 'ts-node'
 
 import {TSConfig} from '../interfaces/ts-config'
 import {Debug} from './util'
 // eslint-disable-next-line new-cap
 const debug = Debug()
 
-const tsconfigs: {[root: string]: TSConfig} = {}
-const rootDirs: string[] = []
-const typeRoots = [`${__dirname}/../node_modules/@types`]
-
 function loadTSConfig(root: string): TSConfig | undefined {
   const tsconfigPath = path.join(root, 'tsconfig.json')
   let typescript: typeof import('typescript') | undefined
@@ -36,46 +31,6 @@ function loadTSConfig(root: string): TSConfig | undefined {
   }
 }
 
-function registerTSNode(root: string) {
-  if (process.env.OCLIF_TS_NODE === '0') return
-  if (tsconfigs[root]) return
-  const tsconfig = loadTSConfig(root)
-  if (!tsconfig) return
-  debug('registering ts-node at', root)
-  const tsNodePath = require.resolve('ts-node', {paths: [root, __dirname]})
-  const tsNode: typeof TSNode = require(tsNodePath)
-  tsconfigs[root] = tsconfig
-  typeRoots.push(`${root}/node_modules/@types`)
-  if (tsconfig.compilerOptions.rootDirs) {
-    rootDirs.push(...tsconfig.compilerOptions.rootDirs.map(r => path.join(root, r)))
-  } else {
-    rootDirs.push(`${root}/src`)
-  }
-  const cwd = process.cwd()
-  try {
-    process.chdir(root)
-    tsNode.register({
-      skipProject: true,
-      transpileOnly: true,
-      // cache: false,
-      // typeCheck: true,
-      compilerOptions: {
-        esModuleInterop: tsconfig.compilerOptions.esModuleInterop,
-        target: tsconfig.compilerOptions.target || 'es2017',
-        experimentalDecorators: tsconfig.compilerOptions.experimentalDecorators || false,
-        emitDecoratorMetadata: tsconfig.compilerOptions.emitDecoratorMetadata || false,
-        module: 'commonjs',
-        sourceMap: true,
-        rootDirs,
-        typeRoots,
-        jsx: 'react',
-      },
-    })
-  } finally {
-    process.chdir(cwd)
-  }
-}
-
 /**
  * convert a path from the compiled ./lib files to the ./src typescript source
  * this is for developing typescript plugins/CLIs
@@ -87,8 +42,7 @@ export function tsPath(root: string, orig: string | undefined): string | undefin
   if (!orig) return orig
   orig = path.join(root, orig)
   try {
-    registerTSNode(root)
-    const tsconfig = tsconfigs[root]
+    const tsconfig = loadTSConfig(root)
     if (!tsconfig) return orig
     const {rootDir, rootDirs, outDir} = tsconfig.compilerOptions
     const rootDirPath = rootDir || (rootDirs || [])[0]
diff --git a/src/errors/errors/exit.ts b/src/errors/errors/exit.ts
index eeb7c31..11dd684 100644
--- a/src/errors/errors/exit.ts
+++ b/src/errors/errors/exit.ts
@@ -6,7 +6,7 @@ export class ExitError extends CLIError implements OclifError {
 
   code = 'EEXIT'
 
-  constructor(exitCode = 0) {
+  constructor(exitCode = 1) {
     super(`EEXIT: ${exitCode}`, {exit: exitCode})
   }
 
diff --git a/src/flags.ts b/src/flags.ts
index 7afca72..6983a42 100644
--- a/src/flags.ts
+++ b/src/flags.ts
@@ -14,7 +14,7 @@ export function option<T>(options: {parse: OptionFlag<T>['parse']} & Partial<Opt
 
 const _enum = <T = string>(opts: EnumFlagOptions<T>): OptionFlag<T> => {
   return build<T>({
-    parse(input) {
+    async parse(input) {
       if (!opts.options.includes(input)) throw new Error(`Expected --${this.name}=${input} to be one of: ${opts.options.join(', ')}`)
       return input as unknown as T
     },
@@ -33,7 +33,7 @@ export const version = (opts: Partial<BooleanFlag<boolean>> = {}) => {
     // char: 'v',
     description: 'show CLI version',
     ...opts,
-    parse: (_: any, cmd: Command) => {
+    parse: async (_: any, cmd: Command) => {
       cmd.log(cmd.config.userAgent)
       cmd.exit(0)
     },
@@ -45,7 +45,7 @@ export const help = (opts: Partial<BooleanFlag<boolean>> = {}) => {
     // char: 'h',
     description: 'show CLI help',
     ...opts,
-    parse: (_: any, cmd: Command) => {
+    parse: async (_: any, cmd: Command) => {
       (cmd as any)._help()
     },
   })
diff --git a/src/help/index.ts b/src/help/index.ts
index b667bc0..4bf2939 100644
--- a/src/help/index.ts
+++ b/src/help/index.ts
@@ -18,12 +18,10 @@ const {
   bold,
 } = Chalk
 
-const ROOT_INDEX_CMD_ID = ''
-
 function getHelpSubject(args: string[]): string | undefined {
   for (const arg of args) {
     if (arg === '--') return
-    if (arg === 'help' || arg === '--help' || arg === '-h') continue
+    if (arg === 'help' || arg === '--help') continue
     if (arg.startsWith('-')) return
     return arg
   }
@@ -97,8 +95,10 @@ export class Help extends HelpBase {
   public showHelp(argv: string[]) {
     const subject = getHelpSubject(argv)
     if (!subject) {
-      const rootCmd = this.config.findCommand(ROOT_INDEX_CMD_ID)
-      if (rootCmd) this.showCommandHelp(rootCmd)
+      if (this.config.pjson.oclif.default) {
+        const rootCmd = this.config.findCommand(this.config.pjson.oclif.default)
+        if (rootCmd) this.showCommandHelp(rootCmd)
+      }
       this.showRootHelp()
       return
     }
diff --git a/src/interfaces/parser.ts b/src/interfaces/parser.ts
index b65bf26..db8b3a9 100644
--- a/src/interfaces/parser.ts
+++ b/src/interfaces/parser.ts
@@ -1,7 +1,7 @@
 import {AlphabetLowercase, AlphabetUppercase} from './alphabet'
 import {Config} from './config'
 
-export type ParseFn<T> = (input: string) => T
+export type ParseFn<T> = (input: string) => Promise<T>
 
 export interface Arg<T = string> {
   name: string;
@@ -11,6 +11,7 @@ export interface Arg<T = string> {
   parse?: ParseFn<T>;
   default?: T | (() => T);
   options?: string[];
+  ignoreStdin?: boolean;
 }
 
 export interface ArgBase<T> {
@@ -18,9 +19,10 @@ export interface ArgBase<T> {
   description?: string;
   hidden?: boolean;
   parse: ParseFn<T>;
-  default?: T | (() => T);
+  default?: T | (() => Promise<T>);
   input?: string;
   options?: string[];
+  ignoreStdin?: boolean;
 }
 
 export type RequiredArg<T> = ArgBase<T> & {
@@ -77,7 +79,7 @@ export type DefaultContext<T> = {
   flags: { [k: string]: string };
 }
 
-export type Default<T> = T | ((context: DefaultContext<T>) => T)
+export type Default<T> = T | ((context: DefaultContext<T>) => Promise<T>)
 
 export type FlagBase<T, I> = {
   name: string;
@@ -93,7 +95,7 @@ export type FlagBase<T, I> = {
    * also accept an environment variable as input
    */
   env?: string;
-  parse(input: I, context: any): T;
+  parse(input: I, context: any): Promise<T>;
 }
 
 export type BooleanFlag<T> = FlagBase<T, boolean> & {
diff --git a/src/interfaces/pjson.ts b/src/interfaces/pjson.ts
index e0b8e6a..7068b52 100644
--- a/src/interfaces/pjson.ts
+++ b/src/interfaces/pjson.ts
@@ -16,6 +16,7 @@ export namespace PJSON {
       description?: string;
       hooks?: { [name: string]: (string | string[]) };
       commands?: string;
+      default?: string;
       plugins?: string[];
       devPlugins?: string[];
       helpClass?: string;
diff --git a/src/main.ts b/src/main.ts
index 3f610dc..5d5a6f7 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,53 +1,63 @@
+import {format, inspect} from 'util'
+
 import * as Interfaces from './interfaces'
-import {HelpBase, getHelpClass} from './help'
-import Command from './command'
+import {Config} from './config'
+import {getHelpClass} from './help'
 
-const ROOT_INDEX_CMD_ID = ''
+const log = (message = '', ...args: any[]) => {
+  // tslint:disable-next-line strict-type-predicates
+  message = typeof message === 'string' ? message : inspect(message)
+  process.stdout.write(format(message, ...args) + '\n')
+}
 
-export class Main extends Command {
-  static run(argv = process.argv.slice(2), options?: Interfaces.LoadOptions) {
-    return super.run(argv, options || (module.parent && module.parent.parent && module.parent.parent.filename) || __dirname)
+const helpOverride = (argv: string[], config: Interfaces.Config): boolean => {
+  if (argv.length === 0 && !config.pjson.oclif.default) return true
+  for (const arg of argv) {
+    if (arg === '--help') return true
+    if (arg === '--') return false
   }
+  return false
+}
 
-  async init() {
-    const [id, ...argv] = this.argv
-    await this.config.runHook('init', {id, argv})
-    return super.init()
-  }
+const versionOverride = (argv: string[]): boolean => {
+  if (['--version'].includes(argv[0])) return true
+  return false
+}
 
-  async run() {
-    let [id, ...argv] = this.argv
-    this.parse({strict: false, '--': false, ...this.ctor as any})
-    if (!this.config.findCommand(id)) {
-      const topic = this.config.findTopic(id)
-      if (topic) return this._help()
-      if (this.config.findCommand(ROOT_INDEX_CMD_ID)) {
-        id = ROOT_INDEX_CMD_ID
-        argv = this.argv
-      }
-    }
-    await this.config.runCommand(id, argv)
-  }
+export async function run(argv = process.argv.slice(2), options?: Interfaces.LoadOptions) {
+  // return Main.run(argv, options)
+  const config = await Config.load(options || (module.parent && module.parent.parent && module.parent.parent.filename) || __dirname) as Config
 
-  protected _helpOverride(): boolean {
-    if (['-v', '--version', 'version'].includes(this.argv[0])) return this._version() as any
-    if (['-h', 'help'].includes(this.argv[0])) return true
-    if (this.argv.length === 0 && !this.config.findCommand(ROOT_INDEX_CMD_ID)) return true
-    for (const arg of this.argv) {
-      if (arg === '--help') return true
-      if (arg === '--') return false
-    }
-    return false
+  // run init hook
+  let [id, ...argvSlice] = argv
+  await config.runHook('init', {id, argv: argvSlice})
+
+  // display version if applicable
+  if (versionOverride(argv)) {
+    log(config.userAgent)
+    return
   }
 
-  protected _help() {
-    const HelpClass = getHelpClass(this.config)
-    const help: HelpBase = new HelpClass(this.config)
-    help.showHelp(this.argv)
-    return this.exit(0)
+  // display help version if applicable
+  if (helpOverride(argv, config)) {
+    argv = argv.filter(arg => {
+      if (arg === '--help') return false
+      return true
+    })
+    const Help = getHelpClass(config)
+    const help = new Help(config)
+    help.showHelp(argv)
+    return
   }
-}
 
-export function run(argv = process.argv.slice(2), options?: Interfaces.LoadOptions) {
-  return Main.run(argv, options)
+  // find & run command
+  if (!config.findCommand(id)) {
+    const topic = config.findTopic(id)
+    if (topic) return config.runCommand('help', [id])
+    if (config.pjson.oclif.default) {
+      id = config.pjson.oclif.default
+      argvSlice = argv
+    }
+  }
+  await config.runCommand(id, argvSlice)
 }
diff --git a/src/parser/flags.ts b/src/parser/flags.ts
index cac0f0b..75758bc 100644
--- a/src/parser/flags.ts
+++ b/src/parser/flags.ts
@@ -11,7 +11,7 @@ export function build(
 export function build<T>(defaults: Partial<OptionFlag<T>>): Definition<T> {
   return (options: any = {}): any => {
     return {
-      parse: (i: string, _: any) => i,
+      parse: async (i: string, _: any) => i,
       ...defaults,
       ...options,
       input: [] as string[],
@@ -25,7 +25,7 @@ export function boolean<T = boolean>(
   options: Partial<BooleanFlag<T>> = {},
 ): BooleanFlag<T> {
   return {
-    parse: (b, _) => b,
+    parse: async (b, _) => b,
     ...options,
     allowNo: Boolean(options.allowNo),
     type: 'boolean',
@@ -33,7 +33,7 @@ export function boolean<T = boolean>(
 }
 
 export const integer = build({
-  parse: input => {
+  parse: async input => {
     if (!/^-?\d+$/.test(input))
       throw new Error(`Expected an integer but received: ${input}`)
     return parseInt(input, 10)
diff --git a/src/parser/index.ts b/src/parser/index.ts
index ea05449..2850f49 100644
--- a/src/parser/index.ts
+++ b/src/parser/index.ts
@@ -15,7 +15,7 @@ const m = Deps()
 // eslint-disable-next-line node/no-missing-require
 .add('validate', () => require('./validate').validate as typeof Validate.validate)
 
-export function parse<TFlags, TArgs extends { [name: string]: string }>(argv: string[], options: Input<TFlags>): ParserOutput<TFlags, TArgs> {
+export async function parse<TFlags, TArgs extends { [name: string]: string }>(argv: string[], options: Input<TFlags>): Promise<ParserOutput<TFlags, TArgs>> {
   const input = {
     argv,
     context: options.context,
@@ -28,7 +28,7 @@ export function parse<TFlags, TArgs extends { [name: string]: string }>(argv: st
     strict: options.strict !== false,
   }
   const parser = new Parser(input)
-  const output = parser.parse()
+  const output = await parser.parse()
   m.validate({input, output})
   return output as any
 }
diff --git a/src/parser/parse.ts b/src/parser/parse.ts
index be18ea8..71c359d 100644
--- a/src/parser/parse.ts
+++ b/src/parser/parse.ts
@@ -23,6 +23,18 @@ try {
   debug = () => {}
 }
 
+const readStdin = async () => {
+  const {stdin} = process
+  let result
+  if (stdin.isTTY) return result
+  result = ''
+  stdin.setEncoding('utf8')
+  for await (const chunk of stdin) {
+    result += chunk
+  }
+  return result
+}
+
 export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']>, TArgs extends OutputArgs<T['args']>> {
   private readonly argv: string[]
 
@@ -45,7 +57,7 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
     this.metaData = {}
   }
 
-  public parse() {
+  public async parse() {
     this._debugInput()
 
     const findLongFlag = (arg: string) => {
@@ -125,9 +137,9 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
       if (arg) arg.input = input
       this.raw.push({type: 'arg', input})
     }
-    const argv = this._argv()
+    const argv = await this._argv()
     const args = this._args(argv)
-    const flags = this._flags()
+    const flags = await this._flags()
     this._debugOutput(argv, args, flags)
     return {
       args,
@@ -147,7 +159,7 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
     return args
   }
 
-  private _flags(): TFlags {
+  private async _flags(): Promise<TFlags> {
     const flags = {} as any
     this.metaData.flags = {} as any
     for (const token of this._flagTokens) {
@@ -159,13 +171,15 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
         } else {
           flags[token.flag] = true
         }
-        flags[token.flag] = flag.parse(flags[token.flag], this.context)
+        // eslint-disable-next-line no-await-in-loop
+        flags[token.flag] = await flag.parse(flags[token.flag], this.context)
       } else {
         const input = token.input
         if (flag.options && !flag.options.includes(input)) {
           throw new m.errors.FlagInvalidOptionError(flag, input)
         }
-        const value = flag.parse ? flag.parse(input, this.context) : input
+        // eslint-disable-next-line no-await-in-loop
+        const value = flag.parse ? await flag.parse(input, this.context) : input
         if (flag.multiple) {
           flags[token.flag] = flags[token.flag] || []
           flags[token.flag].push(value)
@@ -179,12 +193,14 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
       if (flags[k]) continue
       if (flag.type === 'option' && flag.env) {
         const input = process.env[flag.env]
-        if (input) flags[k] = flag.parse(input, this.context)
+        // eslint-disable-next-line no-await-in-loop
+        if (input) flags[k] = await flag.parse(input, this.context)
       }
       if (!(k in flags) && flag.default !== undefined) {
         this.metaData.flags[k] = {setFromDefault: true}
         if (typeof flag.default === 'function') {
-          flags[k] = flag.default({options: flag, flags, ...this.context})
+          // eslint-disable-next-line no-await-in-loop
+          flags[k] = await flag.default({options: flag, flags, ...this.context})
         } else {
           flags[k] = flag.default
         }
@@ -193,9 +209,10 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
     return flags
   }
 
-  private _argv(): any[] {
+  private async _argv(): Promise<any[]> {
     const args: any[] = []
     const tokens = this._argTokens
+    let stdinRead = false
     for (let i = 0; i < Math.max(this.input.args.length, tokens.length); i++) {
       const token = tokens[i]
       const arg = this.input.args[i]
@@ -204,13 +221,25 @@ export class Parser<T extends ParserInput, TFlags extends OutputFlags<T['flags']
           if (arg.options && !arg.options.includes(token.input)) {
             throw new m.errors.ArgInvalidOptionError(arg, token.input)
           }
-          args[i] = arg.parse(token.input)
+          // eslint-disable-next-line no-await-in-loop
+          args[i] = await arg.parse(token.input)
         } else {
           args[i] = token.input
         }
-      } else if ('default' in arg) {
+      } else if (!arg.ignoreStdin && !stdinRead) {
+        // eslint-disable-next-line no-await-in-loop
+        let stdin = await readStdin()
+        if (stdin) {
+          stdin = stdin.trim()
+          args[i] = stdin
+        }
+        stdinRead = true
+      }
+      if (!args[i] && 'default' in arg) {
         if (typeof arg.default === 'function') {
-          args[i] = arg.default()
+          // eslint-disable-next-line no-await-in-loop
+          const f = await arg.default()
+          args[i] = f
         } else {
           args[i] = arg.default
         }


Test patch:
diff --git a/test/command/command.test.ts b/test/command/command.test.ts
index f6f57f8..213a743 100644
--- a/test/command/command.test.ts
+++ b/test/command/command.test.ts
@@ -1,11 +1,11 @@
 import {expect, fancy} from 'fancy-test'
-import path = require('path')
+// import path = require('path')
 
-import {Config, Command as Base, Flags as flags} from '../../src'
-import {TestHelpClassConfig} from './helpers/test-help-in-src/src/test-help-plugin'
+import {Command as Base, Flags as flags} from '../../src'
+// import {TestHelpClassConfig} from './helpers/test-help-in-src/src/test-help-plugin'
 
 // const pjson = require('../package.json')
-const root = path.resolve(__dirname, '../../package.json')
+// const root = path.resolve(__dirname, '../../package.json')
 
 class Command extends Base {
   static description = 'test command'
@@ -208,7 +208,7 @@ describe('command', () => {
         }
 
         async run() {
-          const {flags} = this.parse(CMD)
+          const {flags} = await this.parse(CMD)
           this.log(flags.foo)
         }
       }
@@ -218,153 +218,153 @@ describe('command', () => {
     })
   })
 
-  describe('version', () => {
-    fancy
-    .stdout()
-    .add('config', () => Config.load())
-    .do(async () => {
-      await Command.run(['--version'], root)
-    })
-    .catch(/EEXIT: 0/)
-    .it('shows version', ctx => {
-      expect(ctx.stdout).to.equal(`${ctx.config.userAgent}\n`)
-    })
-  })
-
-  describe('help', () => {
-    fancy
-    .stdout()
-    .do(() => {
-      class CMD extends Command {
-        static flags = {help: flags.help()}
-      }
-      return CMD.run(['--help'], root)
-    })
-    .catch(/EEXIT: 0/)
-    .it('--help', ctx => {
-      expect(ctx.stdout).to.equal(`test command
-
-USAGE
-  $ oclif
-
-OPTIONS
-  --help  show CLI help
-
-`)
-    })
-
-    fancy
-    .stdout()
-    .do(async () => {
-      class CMD extends Command {}
-      await CMD.run(['-h'], root)
-    })
-    .catch(/EEXIT: 0/)
-    .it('-h', ctx => {
-      // expect(process.exitCode).to.equal(0)
-      expect(ctx.stdout).to.equal(`test command
-
-USAGE
-  $ oclif
-
-`)
-    })
-
-    fancy
-    .stdout()
-    .add('config', async () => {
-      const config: TestHelpClassConfig = await Config.load()
-      config.pjson.oclif.helpClass = 'help-class-does-not-exist'
-      return config
-    })
-    .do(async ({config}) => {
-      class CMD extends Command {
-        config = config
-      }
-      await CMD.run(['-h'])
-    })
-    .catch((error: Error) => expect(error.message).to.contain('Unable to load configured help class "help-class-does-not-exist", failed with message:\n'))
-    .it('shows useful error message when configured help class cannot be loaded')
-
-    describe('from a help class', () => {
-      fancy
-      .stdout()
-      .add('config', async () => {
-        const config: TestHelpClassConfig = await Config.load()
-        config.pjson.oclif.helpClass = './test/command/helpers/test-help-in-lib/lib/test-help-plugin'
-        return config
-      })
-      .do(async ({config}) => {
-        class CMD extends Command {
-          static id = 'test-command-for-help-plugin'
-
-          config = config
-        }
-        await CMD.run(['-h'])
-      })
-      .catch(/EEXIT: 0/)
-      .it('-h via a plugin in lib dir (compiled to js)', ctx => {
-        expect(ctx.stdout).to.equal('hello from test-help-plugin #showCommandHelp in the lib folder and in compiled javascript\n')
-        expect(ctx.config.showCommandHelpSpy!.getCalls().length).to.equal(1)
-        expect(ctx.config.showHelpSpy!.getCalls().length).to.equal(0)
-        const [Command, Topics] = ctx.config.showCommandHelpSpy!.firstCall.args
-        expect(Command.id).to.deep.equal('test-command-for-help-plugin')
-        expect(Topics).to.be.an('array')
-      })
-
-      fancy
-      .stdout()
-      .add('config', async () => {
-        const config: TestHelpClassConfig = await Config.load()
-        config.pjson.oclif.helpClass = './test/command/helpers/test-help-in-src/src/test-help-plugin'
-        return config
-      })
-      .do(async ({config}) => {
-        class CMD extends Command {
-          static id = 'test-command-for-help-plugin'
-
-          config = config
-        }
-        await CMD.run(['-h'])
-      })
-      .catch(/EEXIT: 0/)
-      .it('-h via a plugin in src dir (source in ts)', ctx => {
-        expect(ctx.stdout).to.equal('hello from test-help-plugin #showCommandHelp\n')
-        expect(ctx.config.showCommandHelpSpy!.getCalls().length).to.equal(1)
-        expect(ctx.config.showHelpSpy!.getCalls().length).to.equal(0)
-        const [Command, Topics] = ctx.config.showCommandHelpSpy!.firstCall.args
-        expect(Command.id).to.deep.equal('test-command-for-help-plugin')
-        expect(Topics).to.be.an('array')
-      })
-
-      fancy
-      .stdout()
-      .add('config', async () => {
-        const config: TestHelpClassConfig = await Config.load()
-        config.pjson.oclif.helpClass = './test/command/helpers/test-help-in-src/src/test-help-plugin'
-        return config
-      })
-      .do(async ({config}) => {
-        class CMD extends Command {
-          static id = 'test-command-for-help-plugin'
-
-          config = config
-
-          static flags = {help: flags.help()}
-        }
-        return CMD.run(['--help'])
-      })
-      .catch(/EEXIT: 0/)
-      .it('--help via a plugin in src dir (source in ts)', ctx => {
-        expect(ctx.stdout).to.equal('hello from test-help-plugin #showCommandHelp\n')
-        expect(ctx.config.showCommandHelpSpy!.getCalls().length).to.equal(1)
-        expect(ctx.config.showHelpSpy!.getCalls().length).to.equal(0)
-        const [Command, Topics] = ctx.config.showCommandHelpSpy!.firstCall.args
-        expect(Command.id).to.deep.equal('test-command-for-help-plugin')
-        expect(Topics).to.be.an('array')
-      })
-    })
-  })
+  // describe('version', () => {
+  //   fancy
+  //   .stdout()
+  //   .add('config', () => Config.load())
+  //   .do(async () => {
+  //     await Command.run(['--version'], root)
+  //   })
+  //   .catch(/EEXIT: 0/)
+  //   .it('shows version', ctx => {
+  //     expect(ctx.stdout).to.equal(`${ctx.config.userAgent}\n`)
+  //   })
+  // })
+
+  //   describe('help', () => {
+  //     fancy
+  //     .stdout()
+  //     .do(() => {
+  //       class CMD extends Command {
+  //         static flags = {help: flags.help()}
+  //       }
+  //       return CMD.run(['--help'], root)
+  //     })
+  //     .catch(/EEXIT: 0/)
+  //     .it('--help', ctx => {
+  //       expect(ctx.stdout).to.equal(`test command
+
+  // USAGE
+  //   $ oclif
+
+  // OPTIONS
+  //   --help  show CLI help
+
+  // `)
+  //     })
+
+  //     fancy
+  //     .stdout()
+  //     .do(async () => {
+  //       class CMD extends Command {}
+  //       await CMD.run(['--help'], root)
+  //     })
+  //     .catch(/EEXIT: 0/)
+  //     .it('--help', ctx => {
+  //       // expect(process.exitCode).to.equal(0)
+  //       expect(ctx.stdout).to.equal(`test command
+
+  // USAGE
+  //   $ oclif
+
+  // `)
+  //     })
+
+  //     fancy
+  //     .stdout()
+  //     .add('config', async () => {
+  //       const config: TestHelpClassConfig = await Config.load()
+  //       config.pjson.oclif.helpClass = 'help-class-does-not-exist'
+  //       return config
+  //     })
+  //     .do(async ({config}) => {
+  //       class CMD extends Command {
+  //         config = config
+  //       }
+  //       await CMD.run(['--help'])
+  //     })
+  //     .catch((error: Error) => expect(error.message).to.contain('Unable to load configured help class "help-class-does-not-exist", failed with message:\n'))
+  //     .it('shows useful error message when configured help class cannot be loaded')
+
+  //     describe('from a help class', () => {
+  //       fancy
+  //       .stdout()
+  //       .add('config', async () => {
+  //         const config: TestHelpClassConfig = await Config.load()
+  //         config.pjson.oclif.helpClass = './test/command/helpers/test-help-in-lib/lib/test-help-plugin'
+  //         return config
+  //       })
+  //       .do(async ({config}) => {
+  //         class CMD extends Command {
+  //           static id = 'test-command-for-help-plugin'
+
+  //           config = config
+  //         }
+  //         await CMD.run(['--help'])
+  //       })
+  //       .catch(/EEXIT: 0/)
+  //       .it('-h via a plugin in lib dir (compiled to js)', ctx => {
+  //         expect(ctx.stdout).to.equal('hello from test-help-plugin #showCommandHelp in the lib folder and in compiled javascript\n')
+  //         expect(ctx.config.showCommandHelpSpy!.getCalls().length).to.equal(1)
+  //         expect(ctx.config.showHelpSpy!.getCalls().length).to.equal(0)
+  //         const [Command, Topics] = ctx.config.showCommandHelpSpy!.firstCall.args
+  //         expect(Command.id).to.deep.equal('test-command-for-help-plugin')
+  //         expect(Topics).to.be.an('array')
+  //       })
+
+  //       fancy
+  //       .stdout()
+  //       .add('config', async () => {
+  //         const config: TestHelpClassConfig = await Config.load()
+  //         config.pjson.oclif.helpClass = './test/command/helpers/test-help-in-src/src/test-help-plugin'
+  //         return config
+  //       })
+  //       .do(async ({config}) => {
+  //         class CMD extends Command {
+  //           static id = 'test-command-for-help-plugin'
+
+  //           config = config
+  //         }
+  //         await CMD.run(['--help'])
+  //       })
+  //       .catch(/EEXIT: 0/)
+  //       .it('-h via a plugin in src dir (source in ts)', ctx => {
+  //         expect(ctx.stdout).to.equal('hello from test-help-plugin #showCommandHelp\n')
+  //         expect(ctx.config.showCommandHelpSpy!.getCalls().length).to.equal(1)
+  //         expect(ctx.config.showHelpSpy!.getCalls().length).to.equal(0)
+  //         const [Command, Topics] = ctx.config.showCommandHelpSpy!.firstCall.args
+  //         expect(Command.id).to.deep.equal('test-command-for-help-plugin')
+  //         expect(Topics).to.be.an('array')
+  //       })
+
+  //       fancy
+  //       .stdout()
+  //       .add('config', async () => {
+  //         const config: TestHelpClassConfig = await Config.load()
+  //         config.pjson.oclif.helpClass = './test/command/helpers/test-help-in-src/src/test-help-plugin'
+  //         return config
+  //       })
+  //       .do(async ({config}) => {
+  //         class CMD extends Command {
+  //           static id = 'test-command-for-help-plugin'
+
+  //           config = config
+
+  //           static flags = {help: flags.help()}
+  //         }
+  //         return CMD.run(['--help'])
+  //       })
+  //       .catch(/EEXIT: 0/)
+  //       .it('--help via a plugin in src dir (source in ts)', ctx => {
+  //         expect(ctx.stdout).to.equal('hello from test-help-plugin #showCommandHelp\n')
+  //         expect(ctx.config.showCommandHelpSpy!.getCalls().length).to.equal(1)
+  //         expect(ctx.config.showHelpSpy!.getCalls().length).to.equal(0)
+  //         const [Command, Topics] = ctx.config.showCommandHelpSpy!.firstCall.args
+  //         expect(Command.id).to.deep.equal('test-command-for-help-plugin')
+  //         expect(Topics).to.be.an('array')
+  //       })
+  //     })
+  //   })
 
   describe('.log()', () => {
     fancy
diff --git a/test/command/main.test.ts b/test/command/main.test.ts
index 8de34f1..eba0e15 100644
--- a/test/command/main.test.ts
+++ b/test/command/main.test.ts
@@ -1,9 +1,7 @@
 import {expect, fancy} from 'fancy-test'
 import path = require('path')
 
-import {Main} from '../../src/main'
-import {Config} from '../../src'
-import {TestHelpClassConfig} from './helpers/test-help-in-src/src/test-help-plugin'
+import {run} from '../../src/main'
 
 const root = path.resolve(__dirname, '../../package.json')
 const pjson = require(root)
@@ -12,21 +10,19 @@ const version = `@oclif/core/${pjson.version} ${process.platform}-${process.arch
 describe('main', () => {
   fancy
   .stdout()
-  .do(() => Main.run(['plugins'], root))
+  .do(() => run(['plugins'], root))
   .do((output: any) => expect(output.stdout).to.equal('no plugins installed\n'))
   .it('runs plugins')
 
   fancy
   .stdout()
-  .do(() => Main.run(['-v'], root))
-  .catch('EEXIT: 0')
+  .do(() => run(['--version'], root))
   .do((output: any) => expect(output.stdout).to.equal(version + '\n'))
   .it('runs -v')
 
   fancy
   .stdout()
-  .do(() => Main.run(['-h'], root))
-  .catch('EEXIT: 0')
+  .do(() => run(['--help'], root))
   .do((output: any) => expect(output.stdout).to.equal(`base library for oclif CLIs
 
 VERSION
@@ -39,42 +35,9 @@ TOPICS
   plugins  list installed plugins
 
 COMMANDS
+  help     display help for oclif
   plugins  list installed plugins
 
 `))
   .it('runs -h')
-
-  describe('with an alternative help class', async () => {
-    const getMainWithHelpClass = async () => {
-      const config: TestHelpClassConfig = await Config.load(root)
-      config.pjson.oclif.helpClass = './test/command/helpers/test-help-in-src/src/test-help-plugin'
-
-      class MainWithHelpClass extends Main {
-        config = config
-      }
-
-      return MainWithHelpClass
-    }
-
-    fancy
-    .stdout()
-    .do(async () => (await getMainWithHelpClass()).run(['-h']))
-    .catch('EEXIT: 0')
-    .do((output: any) => expect(output.stdout).to.equal('hello showHelp\n'))
-    .it('works with -h')
-
-    fancy
-    .stdout()
-    .do(async () => (await getMainWithHelpClass()).run(['--help']))
-    .catch('EEXIT: 0')
-    .do((output: any) => expect(output.stdout).to.equal('hello showHelp\n'))
-    .it('works with --help')
-
-    fancy
-    .stdout()
-    .do(async () => (await getMainWithHelpClass()).run(['help']))
-    .catch('EEXIT: 0')
-    .do((output: any) => expect(output.stdout).to.equal('hello showHelp\n'))
-    .it('works with help')
-  })
 })
diff --git a/test/config/ts-node.test.ts b/test/config/ts-node.test.ts
index 157fadd..7b7678b 100644
--- a/test/config/ts-node.test.ts
+++ b/test/config/ts-node.test.ts
@@ -39,26 +39,4 @@ describe('tsPath', () => {
     const result = ctx.tsNodePlugin.tsPath(root, orig)
     expect(result).to.equal(path.join(root, orig))
   })
-
-  withMockTsConfig()
-  .it('should leave esModuleInterop undefined by default', (ctx: any) => {
-    ctx.tsNodePlugin.tsPath(root, orig)
-    expect(tsNodeRegisterCallArguments.length).is.equal(1)
-    expect(tsNodeRegisterCallArguments[0])
-    .to.have.nested.property('compilerOptions.esModuleInterop')
-    .equal(undefined)
-  })
-
-  withMockTsConfig({
-    compilerOptions: {
-      esModuleInterop: true,
-    },
-  })
-  .it('should use the provided esModuleInterop option', (ctx: any) => {
-    ctx.tsNodePlugin.tsPath(root, orig)
-    expect(tsNodeRegisterCallArguments.length).is.equal(1)
-    expect(tsNodeRegisterCallArguments[0])
-    .to.have.nested.property('compilerOptions.esModuleInterop')
-    .equal(true)
-  })
 })
diff --git a/test/errors/handle.test.ts b/test/errors/handle.test.ts
index 31945ee..b9d3c8d 100644
--- a/test/errors/handle.test.ts
+++ b/test/errors/handle.test.ts
@@ -66,7 +66,7 @@ describe('handle', () => {
   .stderr()
   .finally(() => delete process.exitCode)
   .it('hides an exit error', ctx => {
-    handle(new ExitError())
+    handle(new ExitError(0))
     expect(ctx.stdout).to.equal('')
     expect(ctx.stderr).to.equal('')
     expect(process.exitCode).to.equal(0)
diff --git a/test/parser/parse.test.ts b/test/parser/parse.test.ts
index 979ea05..fc14374 100644
--- a/test/parser/parse.test.ts
+++ b/test/parser/parse.test.ts
@@ -5,8 +5,8 @@ import {flags, parse} from '../../src/parser'
 import {Interfaces} from '../../src'
 
 describe('parse', () => {
-  it('--bool', () => {
-    const out = parse(['--bool'], {
+  it('--bool', async () => {
+    const out = await parse(['--bool'], {
       flags: {
         bool: flags.boolean(),
       },
@@ -14,16 +14,16 @@ describe('parse', () => {
     expect(out).to.deep.include({flags: {bool: true}})
   })
 
-  it('arg1', () => {
-    const out = parse(['arg1'], {
+  it('arg1', async () => {
+    const out = await parse(['arg1'], {
       args: [{name: 'foo'}],
     })
     expect(out.argv).to.deep.equal(['arg1'])
     expect(out.args).to.deep.equal({foo: 'arg1'})
   })
 
-  it('arg1 arg2', () => {
-    const out = parse(['arg1', 'arg2'], {
+  it('arg1 arg2', async () => {
+    const out = await parse(['arg1', 'arg2'], {
       args: [{name: 'foo'}, {name: 'bar'}],
     })
     expect(out.argv).to.deep.equal(['arg1', 'arg2'])
@@ -31,8 +31,8 @@ describe('parse', () => {
   })
 
   describe('output: array', () => {
-    it('--bool', () => {
-      const out = parse(['--bool'], {
+    it('--bool', async () => {
+      const out = await parse(['--bool'], {
         flags: {
           bool: flags.boolean(),
         },
@@ -40,15 +40,15 @@ describe('parse', () => {
       expect(out.raw[0]).to.deep.include({flag: 'bool'})
     })
 
-    it('arg1', () => {
-      const out = parse(['arg1'], {
+    it('arg1', async () => {
+      const out = await parse(['arg1'], {
         args: [{name: 'foo'}],
       })
       expect(out.raw[0]).to.have.property('input', 'arg1')
     })
 
-    it('parses args and flags', () => {
-      const out = parse(['foo', '--myflag', 'bar', 'baz'], {
+    it('parses args and flags', async () => {
+      const out = await parse(['foo', '--myflag', 'bar', 'baz'], {
         args: [{name: 'myarg'}, {name: 'myarg2'}],
         flags: {myflag: flags.string()},
       })
@@ -58,16 +58,16 @@ describe('parse', () => {
     })
 
     describe('flags', () => {
-      it('parses flags', () => {
-        const out = parse(['--myflag', '--myflag2'], {
+      it('parses flags', async () => {
+        const out = await parse(['--myflag', '--myflag2'], {
           flags: {myflag: flags.boolean(), myflag2: flags.boolean()},
         })
         expect(Boolean(out.flags.myflag)).to.equal(true)
         expect(Boolean(out.flags.myflag2)).to.equal(true)
       })
 
-      it('parses short flags', () => {
-        const out = parse(['-mf'], {
+      it('parses short flags', async () => {
+        const out = await parse(['-mf'], {
           flags: {
             force: flags.boolean({char: 'f'}),
             myflag: flags.boolean({char: 'm'}),
@@ -77,8 +77,8 @@ describe('parse', () => {
         expect(Boolean(out.flags.force)).to.equal(true)
       })
     })
-    it('parses flag value with "=" to separate', () => {
-      const out = parse(['--myflag=foo'], {
+    it('parses flag value with "=" to separate', async () => {
+      const out = await parse(['--myflag=foo'], {
         flags: {
           myflag: flags.string({char: 'm'}),
         },
@@ -86,8 +86,8 @@ describe('parse', () => {
       expect(out.flags).to.deep.equal({myflag: 'foo'})
     })
 
-    it('parses flag value with "=" in value', () => {
-      const out = parse(['--myflag', '=foo'], {
+    it('parses flag value with "=" in value', async () => {
+      const out = await parse(['--myflag', '=foo'], {
         flags: {
           myflag: flags.string({char: 'm'}),
         },
@@ -95,8 +95,8 @@ describe('parse', () => {
       expect(out.flags).to.deep.equal({myflag: '=foo'})
     })
 
-    it('parses short flag value with "="', () => {
-      const out = parse(['-m=foo'], {
+    it('parses short flag value with "="', async () => {
+      const out = await parse(['-m=foo'], {
         flags: {
           myflag: flags.string({char: 'm'}),
         },
@@ -104,8 +104,8 @@ describe('parse', () => {
       expect(out.flags).to.deep.equal({myflag: 'foo'})
     })
 
-    it('parses value of ""', () => {
-      const out = parse(['-m', ''], {
+    it('parses value of ""', async () => {
+      const out = await parse(['-m', ''], {
         flags: {
           myflag: flags.string({char: 'm'}),
         },
@@ -113,9 +113,10 @@ describe('parse', () => {
       expect(out.flags).to.deep.equal({myflag: ''})
     })
 
-    it('requires required flag', () => {
-      expect(() => {
-        parse([], {
+    it('requires required flag', async () => {
+      let message = ''
+      try {
+        await parse([], {
           flags: {
             myflag: flags.string({
               description: 'flag description',
@@ -123,13 +124,16 @@ describe('parse', () => {
             }),
           },
         })
-      }).to.throw(
+      } catch (error) {
+        message = error.message
+      }
+      expect(message).to.equal(
         'Missing required flag:\n --myflag MYFLAG  flag description\nSee more help with --help',
       )
     })
 
-    it('removes flags from argv', () => {
-      const out = parse(['--myflag', 'bar', 'foo'], {
+    it('removes flags from argv', async () => {
+      const out = await parse(['--myflag', 'bar', 'foo'], {
         args: [{name: 'myarg'}],
         flags: {myflag: flags.string()},
       })
@@ -138,9 +142,10 @@ describe('parse', () => {
     })
 
     describe('args', () => {
-      it('requires required args with names', () => {
-        expect(() => {
-          parse(['arg1'], {
+      it('requires required args with names', async () => {
+        let message = ''
+        try {
+          await parse(['arg1'], {
             args: [
               {name: 'arg1', required: true},
               {
@@ -155,35 +160,42 @@ describe('parse', () => {
               },
             ],
           })
-        }).to.throw(`Missing 2 required args:
+        } catch (error) {
+          message = error.message
+        }
+        expect(message).to.equal(`Missing 2 required args:
 arg2  arg2 desc
 arg3  arg3 desc
 See more help with --help`)
       })
 
-      it('too many args', () => {
-        expect(() => {
-          parse(['arg1', 'arg2'], {
+      it('too many args', async () => {
+        let message = ''
+        try {
+          await parse(['arg1', 'arg2'], {
             args: [{name: 'arg1', required: true}],
           })
-        }).to.throw('Unexpected argument: arg2\nSee more help with --help')
+        } catch (error) {
+          message = error.message
+        }
+        expect(message).to.equal('Unexpected argument: arg2\nSee more help with --help')
       })
 
-      it('parses args', () => {
-        const out = parse(['foo', 'bar'], {
+      it('parses args', async () => {
+        const out = await parse(['foo', 'bar'], {
           args: [{name: 'myarg'}, {name: 'myarg2'}],
         })
         expect(out.argv).to.deep.equal(['foo', 'bar'])
       })
-      it('skips optional args', () => {
-        const out = parse(['foo'], {
+      it('skips optional args', async () => {
+        const out = await parse(['foo'], {
           args: [{name: 'myarg'}, {name: 'myarg2'}],
         })
         expect(out.argv).to.deep.equal(['foo'])
       })
 
-      it('skips non-required args', () => {
-        const out = parse(['foo'], {
+      it('skips non-required args', async () => {
+        const out = await parse(['foo'], {
           args: [
             {name: 'myarg', required: false},
             {name: 'myarg2', required: false},
@@ -192,15 +204,15 @@ See more help with --help`)
         expect(out.argv).to.deep.equal(['foo'])
       })
 
-      it('parses something looking like a flag as an arg', () => {
-        const out = parse(['--foo'], {
+      it('parses something looking like a flag as an arg', async () => {
+        const out = await parse(['--foo'], {
           args: [{name: 'myarg'}],
         })
         expect(out.argv).to.deep.equal(['--foo'])
       })
 
-      it('parses - as an arg', () => {
-        const out = parse(['-'], {
+      it('parses - as an arg', async () => {
+        const out = await parse(['-'], {
           args: [{name: 'myarg'}],
         })
         expect(out.argv).to.deep.equal(['-'])
@@ -208,60 +220,69 @@ See more help with --help`)
     })
 
     describe('args - no args passed in, with defaults', () => {
-      it('two args: only first is required, only second has a default', () => {
-        expect(() => {
-          parse([], {
+      it('two args: only first is required, only second has a default', async () => {
+        let message = ''
+        try {
+          await parse([], {
             args: [
               {name: 'arg1', required: true},
               {name: 'arg2', required: false, default: 'some_default'},
             ],
           })
-        }).to.throw(`Missing 1 required arg:
+        } catch (error) {
+          message = error.message
+        }
+        expect(message).to.equal(`Missing 1 required arg:
 arg1
 See more help with --help`)
       })
 
-      it('two args: only first is required, only first has a default', () => {
-        expect(() => {
-          parse([], {
-            args: [
-              {name: 'arg1', required: true, default: 'my_default'},
-              {name: 'arg2', required: false},
-            ],
-          })
-        }).to.not.throw()
+      it('two args: only first is required, only first has a default', async () => {
+        await parse([], {
+          args: [
+            {name: 'arg1', required: true, default: 'my_default'},
+            {name: 'arg2', required: false},
+          ],
+        })
+        // won't reach here if thrown
+        expect(() => {}).to.not.throw()
       })
 
-      it('two args: both have a default, only first is required', () => {
-        expect(() => {
-          parse([], {
-            args: [
-              {name: 'arg1', required: true, default: 'my_default'},
-              {name: 'arg2', required: false, default: 'some_default'},
-            ],
-          })
-        }).to.not.throw()
+      it('two args: both have a default, only first is required', async () => {
+        await parse([], {
+          args: [
+            {name: 'arg1', required: true, default: 'my_default'},
+            {name: 'arg2', required: false, default: 'some_default'},
+          ],
+        })
+        // won't reach here if thrown
+        expect(() => {}).to.not.throw()
       })
     })
 
     describe('optional args should always be after required args', () => {
-      it('required arg after optional arg', () => {
-        expect(() => {
-          parse([], {
+      it('required arg after optional arg', async () => {
+        let message = ''
+        try {
+          await parse([], {
             args: [
               {name: 'arg1', required: false},
               {name: 'arg2', required: true, default: 'some_default'},
             ],
           })
-        }).to.throw(`Invalid argument spec:
+        } catch (error) {
+          message = error.message
+        }
+        expect(message).to.equal(`Invalid argument spec:
 arg1 (optional)
 arg2 (required)
 See more help with --help`)
       })
 
-      it('required arg after multiple optional args', () => {
-        expect(() => {
-          parse([], {
+      it('required arg after multiple optional args', async () => {
+        let message = ''
+        try {
+          await parse([], {
             args: [
               {name: 'arg1', required: false},
               {name: 'arg2', required: false, default: 'my_default'},
@@ -269,7 +290,10 @@ See more help with --help`)
               {name: 'arg4', required: true},
             ],
           })
-        }).to.throw(`Invalid argument spec:
+        } catch (error) {
+          message = error.message
+        }
+        expect(message).to.equal(`Invalid argument spec:
 arg1 (optional)
 arg2 (optional)
 arg3 (optional)
@@ -279,8 +303,8 @@ See more help with --help`)
     })
 
     describe('multiple flags', () => {
-      it('parses multiple flags', () => {
-        const out = parse(['--bar', 'a', '--bar=b', '--foo=c', '--baz=d'], {
+      it('parses multiple flags', async () => {
+        const out = await parse(['--bar', 'a', '--bar=b', '--foo=c', '--baz=d'], {
           flags: {
             foo: flags.string(),
             bar: flags.string({multiple: true}),
@@ -294,8 +318,8 @@ See more help with --help`)
     })
 
     describe('strict: false', () => {
-      it('skips flag parsing after "--"', () => {
-        const out = parse(['foo', 'bar', '--', '--myflag'], {
+      it('skips flag parsing after "--"', async () => {
+        const out = await parse(['foo', 'bar', '--', '--myflag'], {
           args: [{name: 'argOne'}],
           flags: {myflag: flags.boolean()},
           strict: false,
@@ -305,8 +329,8 @@ See more help with --help`)
       })
 
       describe('--: false', () => {
-        it('can be disabled', () => {
-          const out = parse(['foo', 'bar', '--', '--myflag'], {
+        it('can be disabled', async () => {
+          const out = await parse(['foo', 'bar', '--', '--myflag'], {
             args: [{name: 'argOne'}],
             strict: false,
             '--': false,
@@ -316,8 +340,8 @@ See more help with --help`)
         })
       })
 
-      it('does not repeat arguments', () => {
-        const out = parse(['foo', '--myflag=foo bar'], {
+      it('does not repeat arguments', async () => {
+        const out = await parse(['foo', '--myflag=foo bar'], {
           strict: false,
         })
         expect(out.argv).to.deep.equal(['foo', '--myflag=foo bar'])
@@ -325,71 +349,83 @@ See more help with --help`)
     })
 
     describe('integer flag', () => {
-      it('parses integers', () => {
-        const out = parse(['--int', '100'], {
+      it('parses integers', async () => {
+        const out = await parse(['--int', '100'], {
           flags: {int: flags.integer(), s: flags.string()},
         })
         expect(out.flags).to.deep.include({int: 100})
       })
 
-      it('parses zero', () => {
-        const out = parse(['--int', '0'], {
+      it('parses zero', async () => {
+        const out = await parse(['--int', '0'], {
           flags: {int: flags.integer(), s: flags.string()},
         })
         expect(out.flags).to.deep.include({int: 0})
       })
 
-      it('parses negative integers', () => {
-        const out = parse(['--int', '-123'], {
+      it('parses negative integers', async () => {
+        const out = await parse(['--int', '-123'], {
           flags: {int: flags.integer(), s: flags.string()},
         })
         expect(out.flags).to.deep.include({int: -123})
       })
 
-      it('does not parse floats', () => {
-        expect(() => {
-          parse(['--int', '3.14'], {
+      it('does not parse floats', async () => {
+        let message = ''
+        try {
+          await parse(['--int', '3.14'], {
             flags: {int: flags.integer()},
           })
-        }).to.throw('Expected an integer but received: 3.14')
+        } catch (error) {
+          message = error.message
+        }
+        expect(message).to.equal('Expected an integer but received: 3.14')
       })
 
-      it('does not parse fractions', () => {
-        expect(() => {
-          parse(['--int', '3/4'], {
+      it('does not parse fractions', async () => {
+        let message = ''
+        try {
+          await parse(['--int', '3/4'], {
             flags: {int: flags.integer()},
           })
-        }).to.throw('Expected an integer but received: 3/4')
+        } catch (error) {
+          message = error.message
+        }
+        expect(message).to.equal('Expected an integer but received: 3/4')
       })
 
-      it('does not parse strings', () => {
-        expect(() => {
-          parse(['--int', 's10'], {
+      it('does not parse strings', async () => {
+        let message = ''
+        try {
+          await parse(['--int', 's10'], {
             flags: {int: flags.integer()},
           })
-        }).to.throw('Expected an integer but received: s10')
+        } catch (error) {
+          message = error.message
+        }
+        expect(message).to.equal('Expected an integer but received: s10')
       })
     })
   })
 
-  it('--no-color', () => {
-    const out = parse(['--no-color'], {})
+  it('--no-color', async () => {
+    const out = await parse(['--no-color'], {})
     expect(out.flags).to.deep.include({color: false})
   })
 
   describe('parse', () => {
-    it('parse', () => {
-      const out = parse(['--foo=bar', '100'], {
-        args: [{name: 'num', parse: i => parseInt(i, 10)}],
-        flags: {foo: flags.string({parse: input => input.toUpperCase()})},
+    it('parse', async () => {
+      const out = await parse(['--foo=bar', '100'], {
+        args: [{name: 'num', parse: async i => parseInt(i, 10)}],
+        flags: {foo: flags.string({parse: async input => input.toUpperCase()})},
       })
       expect(out.flags).to.deep.include({foo: 'BAR'})
       expect(out.args).to.deep.include({num: 100})
       expect(out.argv).to.deep.equal([100])
     })
 
-    // it('gets arg/flag in context', () => {
-    //   const out = parse({
+    // it('gets arg/flag in context', async () => {
+    //   const out = await parse({
     //     args: [{ name: 'num', parse: (_, ctx) => ctx.arg.name!.toUpperCase() }],
     //     argv: ['--foo=bar', '100'],
     //     flags: { foo: flags.string({ parse: (_, ctx) => ctx.flag.name.toUpperCase() }) },
@@ -400,16 +436,16 @@ See more help with --help`)
   })
 
   describe('flag with multiple inputs', () => {
-    it('flag multiple with flag in the middle', () => {
-      const out = parse(['--foo=bar', '--foo', '100', '--hello', 'world'], {
+    it('flag multiple with flag in the middle', async () => {
+      const out = await parse(['--foo=bar', '--foo', '100', '--hello', 'world'], {
         flags: {foo: flags.string({multiple: true}), hello: flags.string()},
       })
       expect(out.flags).to.deep.include({foo: ['bar', '100']})
       expect(out.flags).to.deep.include({hello: 'world'})
     })
 
-    it('flag multiple without flag in the middle', () => {
-      const out = parse(
+    it('flag multiple without flag in the middle', async () => {
+      const out = await parse(
         ['--foo', './a.txt', './b.txt', './c.txt', '--hello', 'world'],
         {
           flags: {
@@ -424,8 +460,8 @@ See more help with --help`)
       expect(out.flags).to.deep.include({hello: 'world'})
     })
 
-    it('flag multiple with arguments', () => {
-      const out = parse(
+    it('flag multiple with arguments', async () => {
+      const out = await parse(
         ['--foo', './a.txt', './b.txt', './c.txt', '--', '15'],
         {
           args: [{name: 'num'}],
@@ -440,8 +476,8 @@ See more help with --help`)
   })
 
   describe('defaults', () => {
-    it('generates metadata for defaults', () => {
-      const out = parse(['-n', 'heroku'], {
+    it('generates metadata for defaults', async () => {
+      const out = await parse(['-n', 'heroku'], {
         flags: {
           name: flags.string({
             char: 'n',
@@ -457,8 +493,8 @@ See more help with --help`)
       })
     })
 
-    it('defaults', () => {
-      const out = parse([], {
+    it('defaults', async () => {
+      const out = await parse([], {
         args: [{name: 'baz', default: 'BAZ'}],
         flags: {foo: flags.string({default: 'bar'})},
       })
@@ -467,27 +503,27 @@ See more help with --help`)
       expect(out.flags).to.deep.include({foo: 'bar'})
     })
 
-    it('accepts falsy', () => {
-      const out = parse([], {
+    it('accepts falsy', async () => {
+      const out = await parse([], {
         args: [{name: 'baz', default: false}],
       })
       expect(out.args).to.deep.include({baz: false})
     })
 
-    it('default as function', () => {
-      const out = parse([], {
+    it('default as function', async () => {
+      const out = await parse([], {
         args: [{name: 'baz', default: () => 'BAZ'}],
-        flags: {foo: flags.string({default: () => 'bar'})},
+        flags: {foo: flags.string({default: async () => 'bar'})},
       })
       expect(out.args).to.deep.include({baz: 'BAZ'})
       expect(out.argv).to.deep.equal(['BAZ'])
       expect(out.flags).to.deep.include({foo: 'bar'})
     })
 
-    it('default has options', () => {
-      const def: Interfaces.Default<string | undefined> = ({options}) =>
+    it('default has options', async () => {
+      const def: Interfaces.Default<string | undefined> = async ({options}) =>
         options.description
-      const out = parse([], {
+      const out = await parse([], {
         // args: [{ name: 'baz', default: () => 'BAZ' }],
         flags: {foo: flags.string({description: 'bar', default: def})},
       })
@@ -496,9 +532,9 @@ See more help with --help`)
       expect(out.flags).to.deep.include({foo: 'bar'})
     })
 
-    it('can default to a different flag', () => {
-      const def: Interfaces.Default<string | undefined> = opts => opts.flags.foo
-      const out = parse(['--foo=bar'], {
+    it('can default to a different flag', async () => {
+      const def: Interfaces.Default<string | undefined> = async opts => opts.flags.foo
+      const out = await parse(['--foo=bar'], {
         flags: {
           bar: flags.string({
             default: def,
@@ -511,8 +547,8 @@ See more help with --help`)
   })
 
   describe('boolean defaults', () => {
-    it('default is true', () => {
-      const out = parse([], {
+    it('default is true', async () => {
+      const out = await parse([], {
         flags: {
           color: flags.boolean({default: true}),
         },
@@ -520,8 +556,8 @@ See more help with --help`)
       expect(out).to.deep.include({flags: {color: true}})
     })
 
-    it('default is false', () => {
-      const out = parse([], {
+    it('default is false', async () => {
+      const out = await parse([], {
         flags: {
           color: flags.boolean({default: false}),
         },
@@ -529,17 +565,17 @@ See more help with --help`)
       expect(out).to.deep.include({flags: {color: false}})
     })
 
-    it('default as function', () => {
-      const out = parse([], {
+    it('default as function', async () => {
+      const out = await parse([], {
         flags: {
-          color: flags.boolean({default: () => true}),
+          color: flags.boolean({default: async () => true}),
         },
       })
       expect(out).to.deep.include({flags: {color: true}})
     })
 
-    it('overridden true default', () => {
-      const out = parse(['--no-color'], {
+    it('overridden true default', async () => {
+      const out = await parse(['--no-color'], {
         flags: {
           color: flags.boolean({allowNo: true, default: true}),
         },
@@ -547,8 +583,8 @@ See more help with --help`)
       expect(out).to.deep.include({flags: {color: false}})
     })
 
-    it('overridden false default', () => {
-      const out = parse(['--color'], {
+    it('overridden false default', async () => {
+      const out = await parse(['--color'], {
         flags: {
           color: flags.boolean({default: false}),
         },
@@ -558,9 +594,9 @@ See more help with --help`)
   })
 
   describe('custom option', () => {
-    it('can pass parse fn', () => {
-      const foo = flags.option({char: 'f', parse: () => 100})
-      const out = parse(['-f', 'bar'], {
+    it('can pass parse fn', async () => {
+      const foo = flags.option({char: 'f', parse: async () => 100})
+      const out = await parse(['-f', 'bar'], {
         flags: {foo},
       })
       expect(out.flags).to.deep.include({foo: 100})
@@ -568,16 +604,16 @@ See more help with --help`)
   })
 
   describe('build', () => {
-    it('can pass parse fn', () => {
-      const foo = flags.build({char: 'f', parse: () => 100})
-      const out = parse(['-f', 'bar'], {
+    it('can pass parse fn', async () => {
+      const foo = flags.build({char: 'f', parse: async () => 100})
+      const out = await parse(['-f', 'bar'], {
         flags: {foo: foo()},
       })
       expect(out.flags).to.deep.include({foo: 100})
     })
-    it('does not require parse fn', () => {
+    it('does not require parse fn', async () => {
       const foo = flags.build({char: 'f'})
-      const out = parse(['-f', 'bar'], {
+      const out = await parse(['-f', 'bar'], {
         flags: {foo: foo()},
       })
       expect(out.flags).to.deep.include({foo: 'bar'})
@@ -585,43 +621,51 @@ See more help with --help`)
   })
 
   describe('flag options', () => {
-    it('accepts valid option', () => {
-      const out = parse(['--foo', 'myotheropt'], {
+    it('accepts valid option', async () => {
+      const out = await parse(['--foo', 'myotheropt'], {
         flags: {foo: flags.string({options: ['myopt', 'myotheropt']})},
       })
       expect(out.flags.foo).to.equal('myotheropt')
     })
 
-    it('fails when invalid', () => {
-      expect(() => {
-        parse(['--foo', 'invalidopt'], {
+    it('fails when invalid', async () => {
+      let message = ''
+      try {
+        await parse(['--foo', 'invalidopt'], {
           flags: {foo: flags.string({options: ['myopt', 'myotheropt']})},
         })
-      }).to.throw('Expected --foo=invalidopt to be one of: myopt, myotheropt')
+      } catch (error) {
+        message = error.message
+      }
+      expect(message).to.equal('Expected --foo=invalidopt to be one of: myopt, myotheropt\nSee more help with --help')
     })
   })
 
   describe('arg options', () => {
-    it('accepts valid option', () => {
-      const out = parse(['myotheropt'], {
+    it('accepts valid option', async () => {
+      const out = await parse(['myotheropt'], {
         args: [{name: 'foo', options: ['myopt', 'myotheropt']}],
       })
       expect(out.args.foo).to.equal('myotheropt')
     })
 
-    it('fails when invalid', () => {
-      expect(() => {
-        parse(['invalidopt'], {
+    it('fails when invalid', async () => {
+      let message = ''
+      try {
+        await parse(['invalidopt'], {
           args: [{name: 'foo', options: ['myopt', 'myotheropt']}],
         })
-      }).to.throw('Expected invalidopt to be one of: myopt, myotheropt')
+      } catch (error) {
+        message = error.message
+      }
+      expect(message).to.equal('Expected invalidopt to be one of: myopt, myotheropt\nSee more help with --help')
     })
   })
 
   describe('env', () => {
-    it('accepts as environment variable', () => {
+    it('accepts as environment variable', async () => {
       process.env.TEST_FOO = '101'
-      const out = parse([], {
+      const out = await parse([], {
         flags: {foo: flags.string({env: 'TEST_FOO'})},
       })
       expect(out.flags.foo).to.equal('101')
@@ -630,12 +674,12 @@ See more help with --help`)
   })
 
   describe('flag context', () => {
-    it('accepts context in parse', () => {
-      const out = parse(['--foo'], {
+    it('accepts context in parse', async () => {
+      const out = await parse(['--foo'], {
         context: {a: 101},
         flags: {
           foo: flags.boolean({
-            parse: (_: any, ctx: any) => ctx.a,
+            parse: async (_: any, ctx: any) => ctx.a,
           }),
         },
       })
@@ -643,16 +687,16 @@ See more help with --help`)
     })
   })
 
-  it('parses multiple flags', () => {
-    const out = parse(['--foo=a', '--foo', 'b'], {
+  it('parses multiple flags', async () => {
+    const out = await parse(['--foo=a', '--foo', 'b'], {
       flags: {foo: flags.string()},
     })
     expect(out.flags.foo).to.equal('b')
   })
 
   describe('dependsOn', () => {
-    it('ignores', () => {
-      parse([], {
+    it('ignores', async () => {
+      await parse([], {
         flags: {
           foo: flags.string({dependsOn: ['bar']}),
           bar: flags.string({char: 'b'}),
@@ -660,8 +704,8 @@ See more help with --help`)
       })
     })
 
-    it('succeeds', () => {
-      const out = parse(['--foo', 'a', '-bb'], {
+    it('succeeds', async () => {
+      const out = await parse(['--foo', 'a', '-bb'], {
         flags: {
           foo: flags.string({dependsOn: ['bar']}),
           bar: flags.string({char: 'b'}),
@@ -671,21 +715,25 @@ See more help with --help`)
       expect(out.flags.bar).to.equal('b')
     })
 
-    it('fails', () => {
-      expect(() => {
-        parse(['--foo', 'a'], {
+    it('fails', async () => {
+      let message = ''
+      try {
+        await parse(['--foo', 'a'], {
           flags: {
             foo: flags.string({dependsOn: ['bar']}),
             bar: flags.string({char: 'b'}),
           },
         })
-      }).to.throw('--bar= must also be provided when using --foo=')
+      } catch (error) {
+        message = error.message
+      }
+      expect(message).to.equal('--bar= must also be provided when using --foo=')
     })
   })
 
   describe('exclusive', () => {
-    it('ignores', () => {
-      parse([], {
+    it('ignores', async () => {
+      await parse([], {
         flags: {
           foo: flags.string({exclusive: ['bar']}),
           bar: flags.string({char: 'b'}),
@@ -693,8 +741,8 @@ See more help with --help`)
       })
     })
 
-    it('succeeds', () => {
-      const out = parse(['--foo', 'a'], {
+    it('succeeds', async () => {
+      const out = await parse(['--foo', 'a'], {
         flags: {
           foo: flags.string({exclusive: ['bar']}),
           bar: flags.string({char: 'b'}),
@@ -703,43 +751,55 @@ See more help with --help`)
       expect(out.flags.foo).to.equal('a')
     })
 
-    it('fails', () => {
-      expect(() => {
-        parse(['--foo', 'a', '-bb'], {
+    it('fails', async () => {
+      let message = ''
+      try {
+        await parse(['--foo', 'a', '-bb'], {
           flags: {
             foo: flags.string({exclusive: ['bar']}),
             bar: flags.string({char: 'b'}),
           },
         })
-      }).to.throw('--bar= cannot also be provided when using --foo=')
+      } catch (error) {
+        message = error.message
+      }
+      expect(message).to.equal('--bar= cannot also be provided when using --foo=')
     })
   })
 
   describe('exactlyOne', () => {
-    it('throws if neither is set', () => {
-      expect(() => {
-        parse([], {
+    it('throws if neither is set', async () => {
+      let message = ''
+      try {
+        await parse([], {
           flags: {
             foo: flags.string({exactlyOne: ['bar']}),
             bar: flags.string({char: 'b', exactlyOne: ['foo']}),
           },
         })
-      }).to.throw()
+      } catch (error) {
+        message = error.message
+      }
+      expect(message).to.equal('Exactly one of the following must be provided: b,a,r')
     })
 
-    it('throws if multiple are set', () => {
-      expect(() => {
-        parse(['--foo', 'a', '--bar', 'b'], {
+    it('throws if multiple are set', async () => {
+      let message = ''
+      try {
+        await parse(['--foo', 'a', '--bar', 'b'], {
           flags: {
             foo: flags.string({exactlyOne: ['bar']}),
             bar: flags.string({char: 'b', exactlyOne: ['foo']}),
           },
         })
-      }).to.throw('--bar= cannot also be provided when using --foo=')
+      } catch (error) {
+        message = error.message
+      }
+      expect(message).to.equal('--bar= cannot also be provided when using --foo=')
     })
 
-    it('succeeds if exactly one', () => {
-      const out = parse(['--foo', 'a', '--else', '4'], {
+    it('succeeds if exactly one', async () => {
+      const out = await parse(['--foo', 'a', '--else', '4'], {
         flags: {
           foo: flags.string({exactlyOne: ['bar']}),
           bar: flags.string({char: 'b', exactlyOne: ['foo']}),
@@ -749,8 +809,8 @@ See more help with --help`)
       expect(out.flags.foo).to.equal('a')
     })
 
-    it('succeeds if exactly one (the other option)', () => {
-      const out = parse(['--bar', 'b', '--else', '4'], {
+    it('succeeds if exactly one (the other option)', async () => {
+      const out = await parse(['--bar', 'b', '--else', '4'], {
         flags: {
           foo: flags.string({exactlyOne: ['bar']}),
           bar: flags.string({char: 'b', exactlyOne: ['foo']}),
@@ -760,8 +820,8 @@ See more help with --help`)
       expect(out.flags.bar).to.equal('b')
     })
 
-    it('succeeds if exactly one of three', () => {
-      const out = parse(['--bar', 'b'], {
+    it('succeeds if exactly one of three', async () => {
+      const out = await parse(['--bar', 'b'], {
         flags: {
           foo: flags.string({exactlyOne: ['bar', 'else']}),
           bar: flags.string({char: 'b', exactlyOne: ['foo', 'else']}),
@@ -771,8 +831,8 @@ See more help with --help`)
       expect(out.flags.bar).to.equal('b')
     })
 
-    it('lets user list flag in its own list', () => {
-      const out = parse(['--bar', 'b'], {
+    it('lets user list flag in its own list', async () => {
+      const out = await parse(['--bar', 'b'], {
         flags: {
           foo: flags.string({exactlyOne: ['foo', 'bar', 'else']}),
           bar: flags.string({char: 'b', exactlyOne: ['foo', 'bar', 'else']}),
@@ -782,36 +842,49 @@ See more help with --help`)
       expect(out.flags.bar).to.equal('b')
     })
 
-    it('fails if multiple of three', () => {
-      expect(() => {
-        parse(['--foo', 'a', '--else', '4'], {
+    it('fails if multiple of three', async () => {
+      let message = ''
+      try {
+        await parse(['--foo', 'a', '--else', '4'], {
           flags: {
             foo: flags.string({exactlyOne: ['bar', 'else']}),
             bar: flags.string({char: 'b', exactlyOne: ['foo', 'else']}),
             else: flags.string({char: 'e', exactlyOne: ['foo', 'bar']}),
           },
         })
-      }).to.throw()
+      } catch (error) {
+        message = error.message
+      }
+      expect(message).to.equal('--else= cannot also be provided when using --foo=')
     })
 
-    it('handles cross-references/pairings that don\'t make sense', () => {
+    it('handles cross-references/pairings that don\'t make sense', async () => {
       const crazyFlags = {
         foo: flags.string({exactlyOne: ['bar']}),
         bar: flags.string({char: 'b', exactlyOne: ['else']}),
         else: flags.string({char: 'e'}),
       }
-      expect(() => {
-        parse(['--foo', 'a', '--bar', '4'], {
+      let message1 = ''
+      try {
+        await parse(['--foo', 'a', '--bar', '4'], {
           flags: crazyFlags,
         })
-      }).to.throw()
+      } catch (error) {
+        message1 = error.message
+      }
+      expect(message1).to.equal('--bar= cannot also be provided when using --foo=')
 
-      expect(() => {
-        parse(['--bar', 'a', '--else', '4'], {
+      let message2 = ''
+      try {
+        await parse(['--bar', 'a', '--else', '4'], {
           flags: crazyFlags,
         })
-      }).to.throw()
-      const out = parse(['--foo', 'a', '--else', '4'], {
+      } catch (error) {
+        message2 = error.message
+      }
+      expect(message2).to.equal('--else= cannot also be provided when using --bar=')
+
+      const out = await parse(['--foo', 'a', '--else', '4'], {
         flags: crazyFlags,
       })
       expect(out.flags.foo).to.equal('a')
@@ -821,24 +894,24 @@ See more help with --help`)
   })
 
   describe('allowNo', () => {
-    it('is undefined if not set', () => {
-      const out = parse([], {
+    it('is undefined if not set', async () => {
+      const out = await parse([], {
         flags: {
           foo: flags.boolean({allowNo: true}),
         },
       })
       expect(out.flags.foo).to.equal(undefined)
     })
-    it('is false', () => {
-      const out = parse(['--no-foo'], {
+    it('is false', async () => {
+      const out = await parse(['--no-foo'], {
         flags: {
           foo: flags.boolean({allowNo: true}),
         },
       })
       expect(out.flags.foo).to.equal(false)
     })
-    it('is true', () => {
-      const out = parse(['--foo'], {
+    it('is true', async () => {
+      const out = await parse(['--foo'], {
         flags: {
           foo: flags.boolean({allowNo: true}),
         },