Repository: istanbuljs/nyc
Pull request #1184 (2019-09-24 19:30:45)

chore: Use async functions where reasonable
Might be missing a couple places but I wanted to be careful to avoid stuff that is used by the `signal-exit` callback.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/25905112/badge)](https://coveralls.io/builds/25905112)

Coverage increased (+0.7%) to 99.359% when pulling **bf55c3c2908853ed56bdf8e39999e9600be0bdc5 on coreyfarrell:async** into **c18fb0a9a8eae5904298d87c62d9934243de8674 on istanbuljs:master**.


Patch:
diff --git a/bin/nyc.js b/bin/nyc.js
index b5d6f52..841205a 100755
--- a/bin/nyc.js
+++ b/bin/nyc.js
@@ -1,4 +1,5 @@
 #!/usr/bin/env node
+'use strict'
 
 const configUtil = require('../lib/config-util')
 const foreground = require('foreground-child')
@@ -18,11 +19,19 @@ const config = configUtil.loadConfig(yargs.parse(instrumenterArgs))
 configUtil.addCommandsAndHelp(yargs)
 const argv = yargs.config(config).parse(instrumenterArgs)
 
-if ([
-  'check-coverage', 'report', 'instrument', 'merge'
-].indexOf(argv._[0]) !== -1) {
-  // look in lib/commands for logic.
-} else if (argv._.length) {
+async function main () {
+  if (['check-coverage', 'report', 'instrument', 'merge'].includes(argv._[0])) {
+    // look in lib/commands for logic.
+    return
+  }
+
+  if (argv._.length === 0) {
+    // I don't have a clue what you're doing.
+    process.exitCode = 1
+    yargs.showHelp()
+    return
+  }
+
   // if instrument is set to false,
   // enable a noop instrumenter.
   if (!argv.instrument) argv.instrumenter = './lib/instrumenters/noop'
@@ -30,11 +39,14 @@ if ([
 
   var nyc = (new NYC(argv))
   if (argv.clean) {
-    nyc.reset()
+    await nyc.reset()
   } else {
-    nyc.createTempDirectory()
+    await nyc.createTempDirectory()
+  }
+
+  if (argv.all) {
+    await nyc.addAllFiles()
   }
-  if (argv.all) nyc.addAllFiles()
 
   var env = {
     // Support running nyc as a user without HOME (e.g. linux 'nobody'),
@@ -60,14 +72,14 @@ if ([
     // use the same argv description, but don't exit
     // for flags like --help.
     configUtil.buildYargs().parse(process.argv.slice(2))
-  ), function (done) {
+  ), async () => {
     var mainChildExitCode = process.exitCode
 
-    nyc.writeProcessIndex()
+    await nyc.writeProcessIndex()
 
     nyc.maybePurgeSourceMapCache()
     if (argv.checkCoverage) {
-      nyc.checkCoverage({
+      await nyc.checkCoverage({
         lines: argv.lines,
         functions: argv.functions,
         branches: argv.branches,
@@ -77,13 +89,9 @@ if ([
     }
 
     if (!argv.silent) {
-      nyc.report()
+      await nyc.report()
     }
-
-    return done()
   })
-} else {
-  // I don't have a clue what you're doing.
-  process.exitCode = 1
-  yargs.showHelp()
 }
+
+main()
diff --git a/index.js b/index.js
index 36bd06c..6df5dd0 100755
--- a/index.js
+++ b/index.js
@@ -5,8 +5,10 @@
 const cachingTransform = require('caching-transform')
 const cpFile = require('cp-file')
 const findCacheDir = require('find-cache-dir')
-const fs = require('fs')
-const glob = require('glob')
+const fs = require('./lib/fs-promises')
+const os = require('os')
+const { debuglog, promisify } = require('util')
+const glob = promisify(require('glob'))
 const Hash = require('./lib/hash')
 const libCoverage = require('istanbul-lib-coverage')
 const libHook = require('istanbul-lib-hook')
@@ -18,12 +20,12 @@ const onExit = require('signal-exit')
 const path = require('path')
 const reports = require('istanbul-reports')
 const resolveFrom = require('resolve-from')
-const rimraf = require('rimraf')
+const rimraf = promisify(require('rimraf'))
 const SourceMaps = require('./lib/source-maps')
 const testExclude = require('test-exclude')
-const util = require('util')
+const pMap = require('p-map')
 
-const debugLog = util.debuglog('nyc')
+const debugLog = debuglog('nyc')
 
 let selfCoverageHelper
 
@@ -168,11 +170,12 @@ class NYC {
     return source
   }
 
-  addAllFiles () {
+  async addAllFiles () {
     this._loadAdditionalModules()
 
     this.fakeRequire = true
-    this.exclude.globSync(this.cwd).forEach(relFile => {
+    const files = await this.exclude.glob(this.cwd)
+    files.forEach(relFile => {
       const filename = path.resolve(this.cwd, relFile)
       this.addFile(filename)
       const coverage = coverageFinder()
@@ -190,19 +193,20 @@ class NYC {
     this.writeCoverageFile()
   }
 
-  instrumentAllFiles (input, output, cb) {
+  async instrumentAllFiles (input, output) {
     let inputDir = '.' + path.sep
-    const visitor = relFile => {
+    const visitor = async relFile => {
       const inFile = path.resolve(inputDir, relFile)
-      const inCode = fs.readFileSync(inFile, 'utf-8')
+      const inCode = await fs.readFile(inFile, 'utf-8')
       const outCode = this._transform(inCode, inFile) || inCode
 
       if (output) {
-        const mode = fs.statSync(inFile).mode
+        const { mode } = await fs.stat(inFile)
         const outFile = path.resolve(output, relFile)
-        mkdirp.sync(path.dirname(outFile))
-        fs.writeFileSync(outFile, outCode)
-        fs.chmodSync(outFile, mode)
+
+        await mkdirp(path.dirname(outFile))
+        await fs.writeFile(outFile, outCode)
+        await fs.chmod(outFile, mode)
       } else {
         console.log(outCode)
       }
@@ -210,26 +214,29 @@ class NYC {
 
     this._loadAdditionalModules()
 
-    try {
-      const stats = fs.lstatSync(input)
-      if (stats.isDirectory()) {
-        inputDir = input
-
-        const filesToInstrument = this.exclude.globSync(input)
-
-        if (this.config.completeCopy && output) {
-          const globOptions = { dot: true, nodir: true, ignore: ['**/.git', '**/.git/**', path.join(output, '**')] }
-          glob.sync(path.resolve(input, '**'), globOptions)
-            .forEach(src => cpFile.sync(src, path.join(output, path.relative(input, src))))
-        }
-        filesToInstrument.forEach(visitor)
-      } else {
-        visitor(input)
+    const stats = await fs.lstat(input)
+    if (stats.isDirectory()) {
+      inputDir = input
+
+      const filesToInstrument = await this.exclude.glob(input)
+
+      const concurrency = output ? os.cpus().length : 1
+      if (this.config.completeCopy && output) {
+        await pMap(
+          await glob(path.resolve(input, '**'), {
+            dot: true,
+            nodir: true,
+            ignore: ['**/.git', '**/.git/**', path.join(output, '**')]
+          }),
+          src => cpFile(src, path.join(output, path.relative(input, src))),
+          { concurrency }
+        )
       }
-    } catch (err) {
-      return cb(err)
+
+      await pMap(filesToInstrument, visitor, { concurrency })
+    } else {
+      await visitor(input)
     }
-    cb()
   }
 
   _transform (code, filename) {
@@ -307,26 +314,21 @@ class NYC {
     }
   }
 
-  cleanup () {
-    if (!process.env.NYC_CWD) rimraf.sync(this.tempDirectory())
-  }
-
-  clearCache () {
+  async createTempDirectory () {
+    await mkdirp(this.tempDirectory())
     if (this.cache) {
-      rimraf.sync(this.cacheDirectory)
+      await mkdirp(this.cacheDirectory)
     }
-  }
-
-  createTempDirectory () {
-    mkdirp.sync(this.tempDirectory())
-    if (this.cache) mkdirp.sync(this.cacheDirectory)
 
-    mkdirp.sync(this.processInfo.directory)
+    await mkdirp(this.processInfo.directory)
   }
 
-  reset () {
-    this.cleanup()
-    this.createTempDirectory()
+  async reset () {
+    if (!process.env.NYC_CWD) {
+      await rimraf(this.tempDirectory())
+    }
+
+    await this.createTempDirectory()
   }
 
   _wrapExit () {
@@ -386,14 +388,15 @@ class NYC {
     this.processInfo.save()
   }
 
-  getCoverageMapFromAllCoverageFiles (baseDirectory) {
+  async getCoverageMapFromAllCoverageFiles (baseDirectory) {
     const map = libCoverage.createCoverageMap({})
 
-    this.eachReport(undefined, (report) => {
+    const data = await this.coverageData(baseDirectory)
+    data.forEach(report => {
       map.merge(report)
-    }, baseDirectory)
+    })
 
-    map.data = this.sourceMaps.remapCoverage(map.data)
+    map.data = await this.sourceMaps.remapCoverage(map.data)
 
     // depending on whether source-code is pre-instrumented
     // or instrumented using a JIT plugin like @babel/require
@@ -406,11 +409,11 @@ class NYC {
     return map
   }
 
-  report () {
+  async report () {
     const context = libReport.createContext({
       dir: this.reportDirectory(),
       watermarks: this.config.watermarks,
-      coverageMap: this.getCoverageMapFromAllCoverageFiles()
+      coverageMap: await this.getCoverageMapFromAllCoverageFiles()
     })
 
     this.reporter.forEach((_reporter) => {
@@ -422,32 +425,31 @@ class NYC {
     })
 
     if (this._showProcessTree) {
-      this.showProcessTree()
+      await this.showProcessTree()
     }
   }
 
-  writeProcessIndex () {
+  async writeProcessIndex () {
     const db = new ProcessDB(this.processInfo.directory)
-    db.writeIndex()
+    await db.writeIndex()
   }
 
-  showProcessTree () {
+  async showProcessTree () {
     const db = new ProcessDB(this.processInfo.directory)
-    console.log(db.renderTree(this))
+    console.log(await db.renderTree(this))
   }
 
-  checkCoverage (thresholds, perFile) {
-    var map = this.getCoverageMapFromAllCoverageFiles()
-    var nyc = this
+  async checkCoverage (thresholds, perFile) {
+    const map = await this.getCoverageMapFromAllCoverageFiles()
 
     if (perFile) {
-      map.files().forEach(function (file) {
+      map.files().forEach(file => {
         // ERROR: Coverage for lines (90.12%) does not meet threshold (120%) for index.js
-        nyc._checkCoverage(map.fileCoverageFor(file).toSummary(), thresholds, file)
+        this._checkCoverage(map.fileCoverageFor(file).toSummary(), thresholds, file)
       })
     } else {
       // ERROR: Coverage for lines (90.12%) does not meet global threshold (120%)
-      nyc._checkCoverage(map.getCoverageSummary(), thresholds)
+      this._checkCoverage(map.getCoverageSummary(), thresholds)
     }
   }
 
@@ -465,6 +467,28 @@ class NYC {
     })
   }
 
+  async coverageFiles (baseDirectory) {
+    const files = await fs.readdir(baseDirectory)
+
+    return files.map(f => path.resolve(baseDirectory, f))
+  }
+
+  async coverageFileLoad (filename) {
+    try {
+      const report = JSON.parse(await fs.readFile(filename), 'utf8')
+      await this.sourceMaps.reloadCachedSourceMaps(report)
+      return report
+    } catch (error) {
+      return {}
+    }
+  }
+
+  async coverageData (baseDirectory = this.tempDirectory()) {
+    const files = await this.coverageFiles(baseDirectory)
+    return pMap(files, f => this.coverageFileLoad(f), { concurrency: os.cpus().length })
+  }
+
+  /* istanbul ignore next: legacy function used by istanbul-lib-processinfo. */
   eachReport (filenames, iterator, baseDirectory) {
     baseDirectory = baseDirectory || this.tempDirectory()
 
@@ -484,7 +508,7 @@ class NYC {
           'utf-8'
         ))
 
-        _this.sourceMaps.reloadCachedSourceMaps(report)
+        _this.sourceMaps.reloadCachedSourceMapsSync(report)
       } catch (e) { // handle corrupt JSON output.
         report = {}
       }
@@ -493,16 +517,6 @@ class NYC {
     })
   }
 
-  loadReports (filenames) {
-    var reports = []
-
-    this.eachReport(filenames, (report) => {
-      reports.push(report)
-    })
-
-    return reports
-  }
-
   tempDirectory () {
     return path.resolve(this.cwd, this._tempDirectory)
   }
diff --git a/lib/commands/check-coverage.js b/lib/commands/check-coverage.js
index 62dfb90..6d14c42 100644
--- a/lib/commands/check-coverage.js
+++ b/lib/commands/check-coverage.js
@@ -1,5 +1,6 @@
 const testExclude = require('test-exclude')
 const NYC = require('../../index.js')
+const cliWrapper = require('./cli-wrapper.js')
 
 exports.command = 'check-coverage'
 
@@ -65,13 +66,14 @@ exports.builder = function (yargs) {
     .example('$0 check-coverage --lines 95', "check whether the JSON in nyc's output folder meets the thresholds provided")
 }
 
-exports.handler = function (argv) {
+exports.handler = cliWrapper(async argv => {
   process.env.NYC_CWD = process.cwd()
 
-  ;(new NYC(argv)).checkCoverage({
+  const nyc = new NYC(argv)
+  await nyc.checkCoverage({
     lines: argv.lines,
     functions: argv.functions,
     branches: argv.branches,
     statements: argv.statements
   }, argv['per-file'])
-}
+})
diff --git a/lib/commands/cli-wrapper.js b/lib/commands/cli-wrapper.js
new file mode 100644
index 0000000..5e59846
--- /dev/null
+++ b/lib/commands/cli-wrapper.js
@@ -0,0 +1,10 @@
+'use strict'
+
+module.exports = execute => {
+  return argv => {
+    execute(argv).catch(error => {
+      console.error(error.message)
+      process.exit(1)
+    })
+  }
+}
diff --git a/lib/commands/instrument.js b/lib/commands/instrument.js
index 541878b..cdd89df 100644
--- a/lib/commands/instrument.js
+++ b/lib/commands/instrument.js
@@ -1,7 +1,9 @@
 const NYC = require('../../index.js')
 const path = require('path')
-const rimraf = require('rimraf')
+const { promisify } = require('util')
+const rimraf = promisify(require('rimraf'))
 const testExclude = require('test-exclude')
+const cliWrapper = require('./cli-wrapper.js')
 
 exports.command = 'instrument <input> [output]'
 
@@ -97,30 +99,25 @@ exports.builder = function (yargs) {
     .example('$0 instrument ./lib ./output', 'instrument all .js files in ./lib with coverage and output in ./output')
 }
 
-const errorExit = msg => {
-  console.error(`nyc instrument failed:  ${msg}`)
-  process.exitCode = 1
-}
-
-exports.handler = function (argv) {
+exports.handler = cliWrapper(async argv => {
   if (argv.output && !argv.inPlace && (path.resolve(argv.cwd, argv.input) === path.resolve(argv.cwd, argv.output))) {
-    return errorExit(`cannot instrument files in place, <input> must differ from <output>.  Set '--in-place' to force`)
+    throw new Error(`cannot instrument files in place, <input> must differ from <output>.  Set '--in-place' to force`)
   }
 
   if (path.relative(argv.cwd, path.resolve(argv.cwd, argv.input)).startsWith('..')) {
-    return errorExit('cannot instrument files outside project root directory')
+    throw new Error('cannot instrument files outside project root directory')
   }
 
   if (argv.delete && argv.inPlace) {
-    return errorExit(`cannot use '--delete' when instrumenting files in place`)
+    throw new Error(`cannot use '--delete' when instrumenting files in place`)
   }
 
   if (argv.delete && argv.output && argv.output.length !== 0) {
     const relPath = path.relative(process.cwd(), path.resolve(argv.output))
     if (relPath !== '' && !relPath.startsWith('..')) {
-      rimraf.sync(argv.output)
+      await rimraf(argv.output)
     } else {
-      return errorExit(`attempt to delete '${process.cwd()}' or containing directory.`)
+      throw new Error(`attempt to delete '${process.cwd()}' or containing directory.`)
     }
   }
 
@@ -135,5 +132,5 @@ exports.handler = function (argv) {
   }
 
   const nyc = new NYC(argv)
-  nyc.instrumentAllFiles(argv.input, argv.output, err => err ? errorExit(err.message) : null)
-}
+  await nyc.instrumentAllFiles(argv.input, argv.output)
+})
diff --git a/lib/commands/merge.js b/lib/commands/merge.js
index 98c6ff2..b5b4ba6 100644
--- a/lib/commands/merge.js
+++ b/lib/commands/merge.js
@@ -1,7 +1,8 @@
 'use strict'
-const fs = require('fs')
 const path = require('path')
 const makeDir = require('make-dir')
+const fs = require('../fs-promises')
+const cliWrapper = require('./cli-wrapper.js')
 
 const NYC = require('../../index.js')
 
@@ -33,22 +34,18 @@ exports.builder = function (yargs) {
     .example('$0 merge ./out coverage.json', 'merge together reports in ./out and output as coverage.json')
 }
 
-exports.handler = function (argv) {
+exports.handler = cliWrapper(async argv => {
   process.env.NYC_CWD = process.cwd()
   const nyc = new NYC(argv)
-  let inputStat
-  try {
-    inputStat = fs.statSync(argv.inputDirectory)
-    if (!inputStat.isDirectory()) {
-      console.error(`${argv.inputDirectory} was not a directory`)
-      process.exit(1)
-    }
-  } catch (err) {
-    console.error(`failed access input directory ${argv.inputDirectory} with error:\n\n${err.message}`)
-    process.exit(1)
+  const inputStat = await fs.stat(argv.inputDirectory).catch(error => {
+    throw new Error(`failed access input directory ${argv.inputDirectory} with error:\n\n${error.message}`)
+  })
+
+  if (!inputStat.isDirectory()) {
+    throw new Error(`${argv.inputDirectory} was not a directory`)
   }
-  makeDir.sync(path.dirname(argv.outputFile))
-  const map = nyc.getCoverageMapFromAllCoverageFiles(argv.inputDirectory)
-  fs.writeFileSync(argv.outputFile, JSON.stringify(map, null, 2), 'utf8')
+  await makeDir(path.dirname(argv.outputFile))
+  const map = await nyc.getCoverageMapFromAllCoverageFiles(argv.inputDirectory)
+  await fs.writeFile(argv.outputFile, JSON.stringify(map, null, 2), 'utf8')
   console.info(`coverage files in ${argv.inputDirectory} merged into ${argv.outputFile}`)
-}
+})
diff --git a/lib/commands/report.js b/lib/commands/report.js
index 2af7831..0c61e8a 100644
--- a/lib/commands/report.js
+++ b/lib/commands/report.js
@@ -1,5 +1,6 @@
 const testExclude = require('test-exclude')
 const NYC = require('../../index.js')
+const cliWrapper = require('./cli-wrapper.js')
 
 exports.command = 'report'
 
@@ -101,16 +102,16 @@ exports.builder = function (yargs) {
     .example('$0 report --reporter=lcov', 'output an HTML lcov report to ./coverage')
 }
 
-exports.handler = function (argv) {
+exports.handler = cliWrapper(async argv => {
   process.env.NYC_CWD = process.cwd()
   var nyc = new NYC(argv)
-  nyc.report()
+  await nyc.report()
   if (argv.checkCoverage) {
-    nyc.checkCoverage({
+    await nyc.checkCoverage({
       lines: argv.lines,
       functions: argv.functions,
       branches: argv.branches,
       statements: argv.statements
     }, argv['per-file'])
   }
-}
+})
diff --git a/lib/fs-promises.js b/lib/fs-promises.js
new file mode 100644
index 0000000..c99316a
--- /dev/null
+++ b/lib/fs-promises.js
@@ -0,0 +1,50 @@
+'use strict'
+
+const fs = require('fs')
+
+const { promisify } = require('util')
+
+module.exports = { ...fs }
+
+// Promisify all functions for consistency
+const fns = [
+  'access',
+  'appendFile',
+  'chmod',
+  'chown',
+  'close',
+  'copyFile',
+  'fchmod',
+  'fchown',
+  'fdatasync',
+  'fstat',
+  'fsync',
+  'ftruncate',
+  'futimes',
+  'lchmod',
+  'lchown',
+  'link',
+  'lstat',
+  'mkdir',
+  'mkdtemp',
+  'open',
+  'read',
+  'readdir',
+  'readFile',
+  'readlink',
+  'realpath',
+  'rename',
+  'rmdir',
+  'stat',
+  'symlink',
+  'truncate',
+  'unlink',
+  'utimes',
+  'write',
+  'writeFile'
+]
+fns.forEach(fn => {
+  if (fs[fn]) {
+    module.exports[fn] = promisify(fs[fn])
+  }
+})
diff --git a/lib/source-maps.js b/lib/source-maps.js
index e77981c..3978971 100644
--- a/lib/source-maps.js
+++ b/lib/source-maps.js
@@ -1,8 +1,10 @@
 const convertSourceMap = require('convert-source-map')
 const libCoverage = require('istanbul-lib-coverage')
 const libSourceMaps = require('istanbul-lib-source-maps')
-const fs = require('fs')
+const fs = require('./fs-promises')
+const os = require('os')
 const path = require('path')
+const pMap = require('p-map')
 
 class SourceMaps {
   constructor (opts) {
@@ -41,14 +43,42 @@ class SourceMaps {
     this._sourceMapCache.addInputSourceMapsSync(coverage)
   }
 
-  remapCoverage (obj) {
-    const transformed = this._sourceMapCache.transformCoverage(
+  async remapCoverage (obj) {
+    const transformed = await this._sourceMapCache.transformCoverage(
       libCoverage.createCoverageMap(obj)
     )
     return transformed.map.data
   }
 
-  reloadCachedSourceMaps (report) {
+  async reloadCachedSourceMaps (report) {
+    await pMap(
+      Object.entries(report),
+      async ([absFile, fileReport]) => {
+        if (!fileReport || !fileReport.contentHash) {
+          return
+        }
+
+        const hash = fileReport.contentHash
+        if (!(hash in this.loadedMaps)) {
+          try {
+            const mapPath = this.cachedPath(absFile, hash)
+            this.loadedMaps[hash] = JSON.parse(await fs.readFile(mapPath, 'utf8'))
+          } catch (e) {
+            // set to false to avoid repeatedly trying to load the map
+            this.loadedMaps[hash] = false
+          }
+        }
+
+        if (this.loadedMaps[hash]) {
+          this._sourceMapCache.registerMap(absFile, this.loadedMaps[hash])
+        }
+      },
+      { concurrency: os.cpus().length }
+    )
+  }
+
+  /* istanbul ignore next: legacy function for istanbul-lib-processinfo */
+  reloadCachedSourceMapsSync (report) {
     Object.entries(report).forEach(([absFile, fileReport]) => {
       if (fileReport && fileReport.contentHash) {
         const hash = fileReport.contentHash
diff --git a/package-lock.json b/package-lock.json
index 232dab3..9b6b38d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -136,6 +136,15 @@
       "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==",
       "dev": true
     },
+    "aggregate-error": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
+      "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==",
+      "requires": {
+        "clean-stack": "^2.0.0",
+        "indent-string": "^4.0.0"
+      }
+    },
     "ajv": {
       "version": "6.10.2",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
@@ -501,6 +510,11 @@
       "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
       "dev": true
     },
+    "clean-stack": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+      "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="
+    },
     "cli-cursor": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@@ -2344,10 +2358,9 @@
       "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
     },
     "indent-string": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
-      "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
-      "dev": true
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="
     },
     "inflight": {
       "version": "1.0.6",
@@ -3908,6 +3921,14 @@
         "p-limit": "^2.2.0"
       }
     },
+    "p-map": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz",
+      "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==",
+      "requires": {
+        "aggregate-error": "^3.0.0"
+      }
+    },
     "p-timeout": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz",
@@ -4315,6 +4336,14 @@
       "requires": {
         "indent-string": "^3.0.0",
         "strip-indent": "^2.0.0"
+      },
+      "dependencies": {
+        "indent-string": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
+          "integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=",
+          "dev": true
+        }
       }
     },
     "regenerator-runtime": {
diff --git a/package.json b/package.json
index 7fb48d7..44eb7a4 100644
--- a/package.json
+++ b/package.json
@@ -85,6 +85,7 @@
     "js-yaml": "^3.13.1",
     "make-dir": "^3.0.0",
     "merge-source-map": "^1.1.0",
+    "p-map": "^3.0.0",
     "resolve-from": "^5.0.0",
     "rimraf": "^3.0.0",
     "signal-exit": "^3.0.2",


Test patch:
diff --git a/tap-snapshots/test-nyc-integration.js-TAP.test.js b/tap-snapshots/test-nyc-integration.js-TAP.test.js
index 7de83e6..41f2574 100644
--- a/tap-snapshots/test-nyc-integration.js-TAP.test.js
+++ b/tap-snapshots/test-nyc-integration.js-TAP.test.js
@@ -502,6 +502,16 @@ All files |       0 |      100 |     100 |       0 |
 `
 
 exports[`test/nyc-integration.js TAP execute with exclude-node-modules=false > stdout 3`] = `
+----------|---------|----------|---------|---------|-------------------
+File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
+----------|---------|----------|---------|---------|-------------------
+All files |       0 |      100 |     100 |       0 |                   
+ index.js |       0 |      100 |     100 |       0 | 1                 
+----------|---------|----------|---------|---------|-------------------
+
+`
+
+exports[`test/nyc-integration.js TAP execute with exclude-node-modules=false > stdout 4`] = `
 
 `
 
diff --git a/test/add-all-files.js b/test/add-all-files.js
index 028201d..eab3e58 100644
--- a/test/add-all-files.js
+++ b/test/add-all-files.js
@@ -1,8 +1,7 @@
 'use strict'
 
-const fs = require('fs')
+const fs = require('../lib/fs-promises')
 const path = require('path')
-const { promisify } = require('util')
 
 const t = require('tap')
 const ap = require('any-path')
@@ -13,20 +12,17 @@ const configUtil = require('../self-coverage/lib/config-util')
 const resetState = require('./helpers/reset-state')
 
 const fixtures = path.resolve(__dirname, 'fixtures')
-const writeFile = promisify(fs.writeFile)
-const unlink = promisify(fs.unlink)
-
 const transpileHook = path.resolve(__dirname, 'fixtures/transpile-hook')
 
 t.beforeEach(resetState)
 
 t.test('outputs an empty coverage report for all files that are not excluded', async t => {
   const nyc = new NYC(configUtil.buildYargs(fixtures).parse())
-  nyc.reset()
-  nyc.addAllFiles()
+  await nyc.reset()
+  await nyc.addAllFiles()
 
   const notLoadedPath = path.join(fixtures, './not-loaded.js')
-  const reports = nyc.loadReports().filter(report => ap(report)[notLoadedPath])
+  const reports = (await nyc.coverageData()).filter(report => ap(report)[notLoadedPath])
   const report = reports[0][notLoadedPath]
 
   t.strictEqual(reports.length, 1)
@@ -37,12 +33,12 @@ t.test('outputs an empty coverage report for all files that are not excluded', a
 t.test('outputs an empty coverage report for multiple configured extensions', async t => {
   const cwd = path.resolve(fixtures, './conf-multiple-extensions')
   const nyc = new NYC(configUtil.buildYargs(cwd).parse())
-  nyc.reset()
-  nyc.addAllFiles()
+  await nyc.reset()
+  await nyc.addAllFiles()
 
   const notLoadedPath1 = path.join(cwd, './not-loaded.es6')
   const notLoadedPath2 = path.join(cwd, './not-loaded.js')
-  const reports = nyc.loadReports().filter(report => {
+  const reports = (await nyc.coverageData()).filter(report => {
     const apr = ap(report)
     return apr[notLoadedPath1] || apr[notLoadedPath2]
   })
@@ -59,29 +55,29 @@ t.test('outputs an empty coverage report for multiple configured extensions', as
 })
 
 t.test('transpiles .js files added via addAllFiles', async t => {
-  await writeFile(
+  await fs.writeFile(
     './test/fixtures/needs-transpile.js',
     '--> pork chop sandwiches <--\nconst a = 99',
     'utf-8'
   )
 
   const nyc = new NYC(configUtil.buildYargs(fixtures).parse(['--require', transpileHook]))
-  nyc.reset()
-  nyc.addAllFiles()
+  await nyc.reset()
+  await nyc.addAllFiles()
 
   const needsTranspilePath = path.join(fixtures, './needs-transpile.js')
-  const reports = nyc.loadReports().filter(report => ap(report)[needsTranspilePath])
+  const reports = (await nyc.coverageData()).filter(report => ap(report)[needsTranspilePath])
   const report = reports[0][needsTranspilePath]
 
   t.strictEqual(reports.length, 1)
   t.strictEqual(report.s['0'], 0)
 
-  await unlink(needsTranspilePath)
+  await fs.unlink(needsTranspilePath)
 })
 
 t.test('does not attempt to transpile files when they are excluded', async t => {
   const notNeedTranspilePath = path.join(fixtures, './do-not-need-transpile.do-not-transpile')
-  await writeFile(
+  await fs.writeFile(
     notNeedTranspilePath,
     '--> pork chop sandwiches <--\nconst a = 99',
     'utf-8'
@@ -93,15 +89,15 @@ t.test('does not attempt to transpile files when they are excluded', async t =>
     '--include=needs-transpile.do-not-transpile'
   ]))
 
-  nyc.reset()
+  await nyc.reset()
 
   // If this ran against *.do-not-transpile it would throw
-  nyc.addAllFiles()
-  await unlink(notNeedTranspilePath)
+  await nyc.addAllFiles()
+  await fs.unlink(notNeedTranspilePath)
 })
 
 t.test('transpiles non-.js files added via addAllFiles', async t => {
-  await writeFile(
+  await fs.writeFile(
     './test/fixtures/needs-transpile.whatever',
     '--> pork chop sandwiches <--\nconst a = 99',
     'utf-8'
@@ -112,15 +108,15 @@ t.test('transpiles non-.js files added via addAllFiles', async t => {
     '--extension=.whatever'
   ])))
 
-  nyc.reset()
-  nyc.addAllFiles()
+  await nyc.reset()
+  await nyc.addAllFiles()
 
   const needsTranspilePath = path.join(fixtures, './needs-transpile.whatever')
-  const reports = nyc.loadReports().filter(report => ap(report)[needsTranspilePath])
+  const reports = (await nyc.coverageData()).filter(report => ap(report)[needsTranspilePath])
   const report = reports[0][needsTranspilePath]
 
   t.strictEqual(reports.length, 1)
   t.strictEqual(report.s['0'], 0)
 
-  await unlink(needsTranspilePath)
+  await fs.unlink(needsTranspilePath)
 })
diff --git a/test/cache.js b/test/cache.js
index 5076c4b..b2d7c98 100644
--- a/test/cache.js
+++ b/test/cache.js
@@ -1,8 +1,10 @@
 'use strict'
 
 const path = require('path')
+const { promisify } = require('util')
 
 const t = require('tap')
+const rimraf = promisify(require('rimraf'))
 
 const NYC = require('../self-coverage')
 const configUtil = require('../self-coverage/lib/config-util')
@@ -15,7 +17,7 @@ t.beforeEach(resetState)
 
 async function cacheTest (t, script) {
   const nyc = new NYC(configUtil.buildYargs(fixtures).parse())
-  nyc.clearCache()
+  await rimraf(nyc.cacheDirectory)
 
   const { status } = await runNYC({
     args: [
diff --git a/test/instrument.js b/test/instrument.js
index fe4bcb1..b8127ed 100644
--- a/test/instrument.js
+++ b/test/instrument.js
@@ -1,6 +1,6 @@
 'use strict'
 
-const fs = require('fs')
+const fs = require('../lib/fs-promises')
 const path = require('path')
 const { promisify } = require('util')
 
@@ -12,12 +12,6 @@ const rimraf = promisify(require('rimraf'))
 
 const { runNYC, fixturesCLI } = require('./helpers')
 
-const chmod = promisify(fs.chmod)
-const readdir = promisify(fs.readdir)
-const readFile = promisify(fs.readFile)
-const stat = promisify(fs.stat)
-const writeFile = promisify(fs.writeFile)
-
 const subdir = path.resolve(fixturesCLI, 'subdir')
 const outputDir = path.resolve(subdir, './output-dir')
 const removedByClean = path.resolve(outputDir, 'removed-by-clean')
@@ -40,7 +34,7 @@ t.test('works in directories without a package.json', async t => {
 
   t.strictEqual(status, 0)
   const target = path.resolve(subdir, 'output-dir', 'index.js')
-  t.match(await readFile(target, 'utf8'), /console.log\('Hello, World!'\)/)
+  t.match(await fs.readFile(target, 'utf8'), /console.log\('Hello, World!'\)/)
 })
 
 t.test('can be configured to exit on error', async t => {
@@ -54,10 +48,10 @@ t.test('can be configured to exit on error', async t => {
 
 t.test('allows a single file to be instrumented', async t => {
   const inputPath = path.resolve(fixturesCLI, './half-covered.js')
-  const inputMode = (await stat(inputPath)).mode & 0o7777
+  const inputMode = (await fs.stat(inputPath)).mode & 0o7777
   const newMode = 0o775
   if (!isWindows) {
-    await chmod(inputPath, newMode)
+    await fs.chmod(inputPath, newMode)
   }
 
   const { status } = await runNYC({
@@ -66,15 +60,15 @@ t.test('allows a single file to be instrumented', async t => {
 
   t.strictEqual(status, 0)
 
-  const files = await readdir(outputDir)
+  const files = await fs.readdir(outputDir)
   t.strictSame(files, ['half-covered.js'])
 
   if (!isWindows) {
     const outputPath = path.resolve(outputDir, 'half-covered.js')
-    const outputMode = (await stat(outputPath)).mode & 0o7777
+    const outputMode = (await fs.stat(outputPath)).mode & 0o7777
     t.strictEqual(outputMode, newMode)
 
-    await chmod(inputPath, inputMode)
+    await fs.chmod(inputPath, inputMode)
   }
 })
 
@@ -92,14 +86,14 @@ t.test('allows a directory of files to be instrumented', async t => {
   t.strictEqual(files.includes('node_modules'), false)
 
   const includeTarget = path.resolve(outputDir, 'ignore.js')
-  t.match(await readFile(includeTarget, 'utf8'), /function cov_/)
+  t.match(await fs.readFile(includeTarget, 'utf8'), /function cov_/)
 })
 
 t.test('copies all files from <input> to <output> as well as those that have been instrumented', async t => {
   // force node_modules to exist so we can verify that it is copied.
   const nmDir = path.resolve(fixturesCLI, 'nyc-config-js', 'node_modules')
   await makeDir(nmDir)
-  await writeFile(path.join(nmDir, 'test-file'), '')
+  await fs.writeFile(path.join(nmDir, 'test-file'), '')
 
   const { status } = await runNYC({
     args: ['instrument', '--complete-copy', './nyc-config-js', outputDir]
@@ -107,14 +101,14 @@ t.test('copies all files from <input> to <output> as well as those that have bee
 
   t.strictEqual(status, 0)
 
-  const files = await readdir(outputDir)
+  const files = await fs.readdir(outputDir)
   t.strictEqual(files.includes('index.js'), true)
   t.strictEqual(files.includes('ignore.js'), true)
   t.strictEqual(files.includes('package.json'), true)
   t.strictEqual(files.includes('node_modules'), true)
 
   const includeTarget = path.resolve(outputDir, 'ignore.js')
-  t.match(await readFile(includeTarget, 'utf8'), /function cov_/)
+  t.match(await fs.readFile(includeTarget, 'utf8'), /function cov_/)
 })
 
 t.test('can instrument the project directory', async t => {
@@ -124,7 +118,7 @@ t.test('can instrument the project directory', async t => {
 
   t.strictEqual(status, 0)
 
-  const files = await readdir(outputDir)
+  const files = await fs.readdir(outputDir)
   t.strictEqual(files.includes('args.js'), true)
   t.strictEqual(files.includes('subdir'), true)
 })
@@ -136,7 +130,7 @@ t.test('allows a sub-directory of files to be instrumented', async t => {
 
   t.strictEqual(status, 0)
 
-  const files = await readdir(outputDir)
+  const files = await fs.readdir(outputDir)
   t.strictEqual(files.includes('index.js'), true)
 })
 
@@ -160,10 +154,10 @@ t.test('allows a subdirectory to be excluded via .nycrc file', async t => {
   t.strictEqual(files.includes('bad.js'), true)
 
   const includeTarget = path.resolve(outputDir, 'index.js')
-  t.match(await readFile(includeTarget, 'utf8'), /function cov_/)
+  t.match(await fs.readFile(includeTarget, 'utf8'), /function cov_/)
 
   const excludeTarget = path.resolve(outputDir, 'exclude-me', 'index.js')
-  t.notMatch(await readFile(excludeTarget, 'utf8'), /function cov_/)
+  t.notMatch(await fs.readFile(excludeTarget, 'utf8'), /function cov_/)
 })
 
 t.test('allows a file to be excluded', async t => {
@@ -180,11 +174,11 @@ t.test('allows a file to be excluded', async t => {
 
   t.strictEqual(status, 0)
 
-  const files = await readdir(outputDir)
+  const files = await fs.readdir(outputDir)
   t.strictEqual(files.includes('exclude-me'), true)
 
   const excludeTarget = path.resolve(outputDir, 'exclude-me', 'index.js')
-  t.notMatch(await readFile(excludeTarget, 'utf8'), /function cov_/)
+  t.notMatch(await fs.readFile(excludeTarget, 'utf8'), /function cov_/)
 })
 
 t.test('allows specifying a single sub-directory to be included', async t => {
@@ -200,10 +194,10 @@ t.test('allows specifying a single sub-directory to be included', async t => {
 
   t.strictEqual(status, 0)
 
-  const files = await readdir(outputDir)
+  const files = await fs.readdir(outputDir)
   t.strictEqual(files.includes('include-me'), true)
   const instrumented = path.resolve(outputDir, 'include-me', 'include-me.js')
-  t.match(await readFile(instrumented, 'utf8'), /function cov_/)
+  t.match(await fs.readFile(instrumented, 'utf8'), /function cov_/)
 })
 
 t.test('allows a file to be excluded from an included directory', async t => {
@@ -222,18 +216,18 @@ t.test('allows a file to be excluded from an included directory', async t => {
 
   t.strictEqual(status, 0)
 
-  const files = await readdir(outputDir)
+  const files = await fs.readdir(outputDir)
   t.strictEqual(files.includes('include-me'), true)
 
-  const includeMeFiles = await readdir(path.resolve(outputDir, 'include-me'))
+  const includeMeFiles = await fs.readdir(path.resolve(outputDir, 'include-me'))
   t.strictEqual(includeMeFiles.includes('include-me.js'), true)
   t.strictEqual(includeMeFiles.includes('exclude-me.js'), true)
 
   const includeTarget = path.resolve(outputDir, 'include-me', 'include-me.js')
-  t.match(await readFile(includeTarget, 'utf8'), /function cov_/)
+  t.match(await fs.readFile(includeTarget, 'utf8'), /function cov_/)
 
   const excludeTarget = path.resolve(outputDir, 'exclude-me', 'index.js')
-  t.notMatch(await readFile(excludeTarget, 'utf8'), /function cov_/)
+  t.notMatch(await fs.readFile(excludeTarget, 'utf8'), /function cov_/)
 })
 
 t.test('aborts if trying to write files in place', async t => {
@@ -266,10 +260,10 @@ t.test('can write files in place with --in-place switch', async t => {
   t.strictEqual(status, 0)
 
   const file1 = path.resolve(outputDir, 'file1.js')
-  t.match(await readFile(file1, 'utf8'), /function cov_/)
+  t.match(await fs.readFile(file1, 'utf8'), /function cov_/)
 
   const file2 = path.resolve(outputDir, 'file2.js')
-  t.notMatch(await readFile(file2, 'utf8'), /function cov_/)
+  t.notMatch(await fs.readFile(file2, 'utf8'), /function cov_/)
 })
 
 t.test('aborts if trying to delete while writing files in place', async t => {
@@ -316,7 +310,7 @@ t.test('cleans the output directory if `--delete` is specified', async t => {
 
   t.strictEqual(status, 0)
 
-  const files = await readdir(outputDir)
+  const files = await fs.readdir(outputDir)
   t.strictEqual(files.includes('removed-by-clean'), false)
   t.strictEqual(files.includes('exclude-me'), true)
 })
@@ -334,7 +328,7 @@ t.test('does not clean the output directory by default', async t => {
 
   t.strictEqual(status, 0)
 
-  const files = await readdir(outputDir)
+  const files = await fs.readdir(outputDir)
   t.strictEqual(files.includes('removed-by-clean'), true)
 })
 
diff --git a/test/issue-190.js b/test/issue-190.js
index a2e2671..9e844e6 100644
--- a/test/issue-190.js
+++ b/test/issue-190.js
@@ -1,8 +1,7 @@
 'use strict'
 
-const fs = require('fs')
+const fs = require('../lib/fs-promises')
 const path = require('path')
-const { promisify } = require('util')
 
 const t = require('tap')
 const isWindows = require('is-windows')()
@@ -11,12 +10,6 @@ const { spawn, fixturesCLI, nycBin } = require('./helpers')
 
 const fakebin = path.resolve(fixturesCLI, 'fakebin')
 
-const unlink = promisify(fs.unlink)
-const readFile = promisify(fs.readFile)
-const writeFile = promisify(fs.writeFile)
-const chmod = promisify(fs.chmod)
-const symlink = promisify(fs.symlink)
-
 const spawnOptions = {
   cwd: path.resolve(fixturesCLI, 'run-npm-test-recursive'),
   env: {
@@ -26,9 +19,9 @@ const spawnOptions = {
 
 async function writeFakeNPM (shebang) {
   const targetPath = path.resolve(fakebin, 'npm')
-  const source = await readFile(path.resolve(fakebin, 'npm-template.js'))
-  await writeFile(targetPath, '#!' + shebang + '\n' + source)
-  await chmod(targetPath, 493) // 0o755
+  const source = await fs.readFile(path.resolve(fakebin, 'npm-template.js'))
+  await fs.writeFile(targetPath, '#!' + shebang + '\n' + source)
+  await fs.chmod(targetPath, 493) // 0o755
 }
 
 async function runFakeNPM (t) {
@@ -40,8 +33,8 @@ async function runFakeNPM (t) {
 }
 
 t.beforeEach(() => Promise.all([
-  unlink(path.resolve(fakebin, 'node')).catch(() => {}),
-  unlink(path.resolve(fakebin, 'npm')).catch(() => {})
+  fs.unlink(path.resolve(fakebin, 'node')).catch(() => {}),
+  fs.unlink(path.resolve(fakebin, 'npm')).catch(() => {})
 ]))
 
 t.test('can run "npm test", absolute shebang edition', async t => {
@@ -60,6 +53,6 @@ t.test('can run "npm test", weird bash+dirname shebang edition', async t => {
 
   // This string is taken verbatim from tools/install.py in Node core v5.x
   await writeFakeNPM('/bin/sh\n// 2>/dev/null; exec "`dirname "$0"`/node" "$0" "$@"')
-  await symlink(process.execPath, path.resolve(fakebin, 'node'))
+  await fs.symlink(process.execPath, path.resolve(fakebin, 'node'))
   await runFakeNPM(t)
 })
diff --git a/test/nyc-integration.js b/test/nyc-integration.js
index f992b86..9e4a6da 100644
--- a/test/nyc-integration.js
+++ b/test/nyc-integration.js
@@ -1,7 +1,7 @@
 'use strict'
 
 const path = require('path')
-const fs = require('fs')
+const fs = require('../lib/fs-promises')
 const os = require('os')
 const { promisify } = require('util')
 
@@ -319,16 +319,18 @@ t.test('extracts coverage headers from unexecuted files', async t => {
   })
 
   const files = await glob(path.join(t.tempDir, '*.json'))
-  const coverage = files.reduce(
-    (obj, file) => Object.assign(obj, JSON.parse(fs.readFileSync(file, 'utf-8'))),
-    {}
-  )
-
+  const coverage = []
+  await Promise.all(files.map(async file => {
+    const data = JSON.parse(await fs.readFile(file, 'utf-8'))
+    if (data['./external-instrumenter.js']) {
+      coverage.push(data['./external-instrumenter.js'])
+    }
+  }))
+
+  t.true(coverage.length !== 0)
+  t.true(coverage.every(data => typeof data === 'object'))
   // we should not have executed file, so all counts sould be 0.
-  const data = coverage['./external-instrumenter.js']
-  t.type(data, 'object')
-
-  t.false(Object.values(data.s).some(s => s !== 0))
+  t.true(coverage.every(data => Object.values(data.s).every(s => s === 0)))
 })
 
 t.test('allows an alternative cache folder to be specified', async t => {
@@ -345,7 +347,7 @@ t.test('allows an alternative cache folder to be specified', async t => {
 
   // we should have created foo-cache rather
   // than the default ./node_modules/.cache.
-  t.is(1, fs.readdirSync(cacheDir).length)
+  t.is(1, (await fs.readdir(cacheDir)).length)
 
   await rimraf(cacheDir)
 })
@@ -404,30 +406,30 @@ t.test('--all uses source-maps to exclude original sources from reports', t => t
   cwd: fixturesSourceMaps
 }))
 
-t.test('caches source-maps from .map files', t => {
-  return testSuccess(t, {
+t.test('caches source-maps from .map files', async t => {
+  await testSuccess(t, {
     args: [
       process.execPath,
       './instrumented/s1.min.js'
     ],
     cwd: fixturesSourceMaps
-  }).then(() => {
-    const files = fs.readdirSync(path.resolve(fixturesSourceMaps, 'node_modules/.cache/nyc'))
-    t.true(files.some(f => f.startsWith('s1.min-') && f.endsWith('.map')))
   })
+
+  const files = await fs.readdir(path.resolve(fixturesSourceMaps, 'node_modules/.cache/nyc'))
+  t.true(files.some(f => f.startsWith('s1.min-') && f.endsWith('.map')))
 })
 
-t.test('caches inline source-maps', t => {
-  return testSuccess(t, {
+t.test('caches inline source-maps', async t => {
+  await testSuccess(t, {
     args: [
       process.execPath,
       './instrumented/s2.min.js'
     ],
     cwd: fixturesSourceMaps
-  }).then(() => {
-    const files = fs.readdirSync(path.resolve(fixturesSourceMaps, 'node_modules/.cache/nyc'))
-    t.true(files.some(f => f.startsWith('s2.min-') && f.endsWith('.map')))
   })
+
+  const files = await fs.readdir(path.resolve(fixturesSourceMaps, 'node_modules/.cache/nyc'))
+  t.true(files.some(f => f.startsWith('s2.min-') && f.endsWith('.map')))
 })
 
 t.test('appropriately instruments file with corresponding .map file', t => testSuccess(t, {
@@ -507,6 +509,15 @@ t.test('execute with exclude-node-modules=false', async t => {
     cwd: fixturesENM
   })
 
+  await testSuccess(t, {
+    args: [
+      ...executeNodeModulesArgs,
+      '--check-coverage=false',
+      'report'
+    ],
+    cwd: fixturesENM
+  })
+
   await testFailure(t, {
     args: [
       ...executeNodeModulesArgs,
diff --git a/test/processinfo.js b/test/processinfo.js
index 85ed4a1..f78001e 100644
--- a/test/processinfo.js
+++ b/test/processinfo.js
@@ -1,18 +1,20 @@
 'use strict'
 
 const { resolve } = require('path')
-const bin = resolve(__dirname, '../self-coverage/bin/nyc')
+const { promisify } = require('util')
 const { spawn } = require('child_process')
 const t = require('tap')
-const rimraf = require('rimraf')
+const rimraf = promisify(require('rimraf'))
+const fs = require('../lib/fs-promises')
+
 const node = process.execPath
+const bin = resolve(__dirname, '../self-coverage/bin/nyc')
 const fixturesCLI = resolve(__dirname, './fixtures/cli')
 const tmp = 'processinfo-test'
-const fs = require('fs')
 const resolvedJS = resolve(fixturesCLI, 'selfspawn-fibonacci.js')
 
 rimraf.sync(resolve(fixturesCLI, tmp))
-t.teardown(() => rimraf.sync(resolve(fixturesCLI, tmp)))
+t.teardown(() => rimraf(resolve(fixturesCLI, tmp)))
 
 t.test('build some processinfo', t => {
   var args = [
@@ -36,48 +38,38 @@ t.test('build some processinfo', t => {
   })
 })
 
-t.test('validate the created processinfo data', t => {
-  const covs = fs.readdirSync(resolve(fixturesCLI, tmp))
+t.test('validate the created processinfo data', async t => {
+  const covs = (await fs.readdir(resolve(fixturesCLI, tmp)))
     .filter(f => f !== 'processinfo')
-  t.plan(covs.length * 2)
 
-  covs.forEach(f => {
-    fs.readFile(resolve(fixturesCLI, tmp, f), 'utf8', (er, covjson) => {
-      if (er) {
-        throw er
-      }
-      const covdata = JSON.parse(covjson)
-      t.same(Object.keys(covdata), [resolvedJS])
-      // should have matching processinfo for each cov json
-      const procInfoFile = resolve(fixturesCLI, tmp, 'processinfo', f)
-      fs.readFile(procInfoFile, 'utf8', (er, procInfoJson) => {
-        if (er) {
-          throw er
-        }
-        const procInfoData = JSON.parse(procInfoJson)
-        t.match(procInfoData, {
-          pid: Number,
-          ppid: Number,
-          uuid: f.replace(/\.json$/, ''),
-          argv: [
-            node,
-            resolvedJS,
-            /[1-5]/
-          ],
-          execArgv: [],
-          cwd: fixturesCLI,
-          time: Number,
-          coverageFilename: resolve(fixturesCLI, tmp, f),
-          files: [ resolvedJS ]
-        })
-      })
+  await Promise.all(covs.map(async f => {
+    const covdata = JSON.parse(await fs.readFile(resolve(fixturesCLI, tmp, f), 'utf8'))
+    t.same(Object.keys(covdata), [resolvedJS])
+
+    // should have matching processinfo for each cov json
+    const procInfoFile = resolve(fixturesCLI, tmp, 'processinfo', f)
+    const procInfoData = JSON.parse(await fs.readFile(procInfoFile, 'utf8'))
+    t.match(procInfoData, {
+      pid: Number,
+      ppid: Number,
+      uuid: f.replace(/\.json$/, ''),
+      argv: [
+        node,
+        resolvedJS,
+        /[1-5]/
+      ],
+      execArgv: [],
+      cwd: fixturesCLI,
+      time: Number,
+      coverageFilename: resolve(fixturesCLI, tmp, f),
+      files: [ resolvedJS ]
     })
-  })
+  }))
 })
 
-t.test('check out the index', t => {
+t.test('check out the index', async t => {
   const indexFile = resolve(fixturesCLI, tmp, 'processinfo', 'index.json')
-  const indexJson = fs.readFileSync(indexFile, 'utf-8')
+  const indexJson = await fs.readFile(indexFile, 'utf-8')
   const index = JSON.parse(indexJson)
   const u = /^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/
   t.match(index, {
@@ -91,5 +83,4 @@ t.test('check out the index', t => {
       }
     }
   })
-  t.end()
 })
diff --git a/test/report.js b/test/report.js
index a38af81..5668cbb 100644
--- a/test/report.js
+++ b/test/report.js
@@ -31,7 +31,7 @@ async function testSignal (t, signal) {
   })
 
   const checkFile = path.join(fixtures, `${signal}.js`)
-  const reports = nyc.loadReports().filter(report => report[checkFile])
+  const reports = (await nyc.coverageData()).filter(report => report[checkFile])
 
   t.strictEqual(reports.length, 1)
 }
@@ -44,9 +44,9 @@ t.test('allows coverage report to be output in an alternative directory', async
   const nyc = new NYC(configUtil.buildYargs().parse(
     ['--report-dir=./alternative-report', '--reporter=lcov']
   ))
-  nyc.reset()
+  await nyc.reset()
 
-  nyc.report()
+  await nyc.report()
   t.strictEqual(fs.existsSync('./alternative-report/lcov.info'), true)
   await rimraf('./alternative-report')
 })
diff --git a/test/source-map-support.js b/test/source-map-support.js
index 178d4dd..a68f95c 100644
--- a/test/source-map-support.js
+++ b/test/source-map-support.js
@@ -16,7 +16,7 @@ t.beforeEach(resetState)
 
 t.test('handles stack traces', async t => {
   const nyc = new NYC(configUtil.buildYargs().parse('--produce-source-map'))
-  nyc.reset()
+  await nyc.reset()
   nyc.wrap()
 
   const check = require('./fixtures/stack-trace')
@@ -26,7 +26,7 @@ t.test('handles stack traces', async t => {
 
 t.test('does not handle stack traces when disabled', async t => {
   const nyc = new NYC(configUtil.buildYargs().parse())
-  nyc.reset()
+  await nyc.reset()
   nyc.wrap()
 
   const check = require('./fixtures/stack-trace')
diff --git a/test/temp-dir.js b/test/temp-dir.js
index 629f77f..3f1b489 100644
--- a/test/temp-dir.js
+++ b/test/temp-dir.js
@@ -1,6 +1,6 @@
 'use strict'
 
-const fs = require('fs')
+const fs = require('../lib/fs-promises')
 const path = require('path')
 const { promisify } = require('util')
 
@@ -9,8 +9,6 @@ const rimraf = promisify(require('rimraf'))
 
 const { runNYC, fixturesCLI } = require('./helpers')
 
-const readdir = promisify(fs.readdir)
-
 function cleanup () {
   return Promise.all([
     rimraf(path.resolve(fixturesCLI, '.nyc_output')),
@@ -29,12 +27,12 @@ t.test('creates the default \'tempDir\' when none is specified', async t => {
 
   t.strictEqual(status, 0)
 
-  const cliFiles = await readdir(path.resolve(fixturesCLI))
+  const cliFiles = await fs.readdir(path.resolve(fixturesCLI))
   t.strictEqual(cliFiles.includes('.nyc_output'), true)
   t.strictEqual(cliFiles.includes('.temp_dir'), false)
   t.strictEqual(cliFiles.includes('.temp_directory'), false)
 
-  const tempFiles = await readdir(path.resolve(fixturesCLI, '.nyc_output'))
+  const tempFiles = await fs.readdir(path.resolve(fixturesCLI, '.nyc_output'))
   t.strictEqual(tempFiles.length, 2) // the coverage file, and processinfo
 })
 
@@ -52,12 +50,12 @@ t.test('prefers \'tempDirectory\' to \'tempDir\'', async t => {
 
   t.strictEqual(status, 0)
 
-  const cliFiles = await readdir(path.resolve(fixturesCLI))
+  const cliFiles = await fs.readdir(path.resolve(fixturesCLI))
   t.strictEqual(cliFiles.includes('.nyc_output'), false)
   t.strictEqual(cliFiles.includes('.temp_dir'), false)
   t.strictEqual(cliFiles.includes('.temp_directory'), true)
 
-  const tempFiles = await readdir(path.resolve(fixturesCLI, '.temp_directory'))
+  const tempFiles = await fs.readdir(path.resolve(fixturesCLI, '.temp_directory'))
   t.strictEqual(tempFiles.length, 2)
 })
 
@@ -73,11 +71,11 @@ t.test('uses the \'tempDir\' option if \'tempDirectory\' is not set', async t =>
 
   t.strictEqual(status, 0)
 
-  const cliFiles = await readdir(path.resolve(fixturesCLI))
+  const cliFiles = await fs.readdir(path.resolve(fixturesCLI))
   t.strictEqual(cliFiles.includes('.nyc_output'), false)
   t.strictEqual(cliFiles.includes('.temp_dir'), true)
   t.strictEqual(cliFiles.includes('.temp_directory'), false)
 
-  const tempFiles = await readdir(path.resolve(fixturesCLI, '.temp_dir'))
+  const tempFiles = await fs.readdir(path.resolve(fixturesCLI, '.temp_dir'))
   t.strictEqual(tempFiles.length, 2)
 })
diff --git a/test/wrap.js b/test/wrap.js
index 8e7c8ad..c78ea4f 100644
--- a/test/wrap.js
+++ b/test/wrap.js
@@ -20,7 +20,7 @@ t.beforeEach(resetState)
 
 t.test('wraps modules with coverage counters when they are required', async t => {
   const nyc = new NYC(configUtil.buildYargs().parse())
-  nyc.reset()
+  await nyc.reset()
   nyc.wrap()
 
   const check = require('./fixtures/check-instrumented')
@@ -37,7 +37,7 @@ t.test('wraps modules with coverage counters when the custom require hook compil
   }
 
   const nyc = new NYC(configUtil.buildYargs().parse())
-  nyc.reset()
+  await nyc.reset()
   nyc.wrap()
 
   // install the custom require hook
@@ -50,7 +50,7 @@ t.test('wraps modules with coverage counters when the custom require hook compil
 
 t.test('assigns a function to custom extensions', async t => {
   const nyc = new NYC(configUtil.buildYargs(configMultExt).parse())
-  nyc.reset()
+  await nyc.reset()
   nyc.wrap()
 
   t.type(require.extensions['.es6'], 'function') // eslint-disable-line
@@ -76,7 +76,7 @@ t.test('calls the `_handleJs` function for custom file extensions', async t => {
     return code
   }
 
-  nyc.reset()
+  await nyc.reset()
   nyc.wrap()
 
   require('./fixtures/conf-multiple-extensions/check-instrumented.es6')
@@ -88,15 +88,15 @@ t.test('calls the `_handleJs` function for custom file extensions', async t => {
 t.test('does not output coverage for files that have not been included, by default', async t => {
   const nyc = new NYC(configUtil.buildYargs(process.cwd()).parse())
   nyc.wrap()
-  nyc.reset()
+  await nyc.reset()
 
-  const reports = nyc.loadReports().filter(report => report['./test/fixtures/not-loaded.js'])
+  const reports = (await nyc.coverageData()).filter(report => report['./test/fixtures/not-loaded.js'])
   t.strictEqual(reports.length, 0)
 })
 
 t.test('tracks coverage appropriately once the file is required', async t => {
   const nyc = new NYC(configUtil.buildYargs(fixtures).parse())
-  nyc.reset()
+  await nyc.reset()
   nyc.wrap()
 
   require('./fixtures/not-loaded')
@@ -104,7 +104,7 @@ t.test('tracks coverage appropriately once the file is required', async t => {
   nyc.writeCoverageFile()
 
   const notLoadedPath = path.join(fixtures, './not-loaded.js')
-  const reports = nyc.loadReports().filter(report => report[notLoadedPath])
+  const reports = (await nyc.coverageData()).filter(report => report[notLoadedPath])
   const report = reports[0][notLoadedPath]
 
   t.strictEqual(reports.length, 1)