From dfd629d95716e6159aa7216c03e28a7fbbb161e7 Mon Sep 17 00:00:00 2001
From: Corey Farrell <git@cfware.com>
Date: Tue, 19 Nov 2019 03:47:57 -0500
Subject: [PATCH] fix: Better error handling for main execution, reporting
 (#1229)

---
 bin/nyc.js                     | 48 ++++++++++++++++++----------------
 lib/commands/check-coverage.js |  4 +--
 lib/commands/helpers.js        | 15 ++++++++++-
 lib/commands/report.js         |  6 ++---
 4 files changed, 45 insertions(+), 28 deletions(-)

diff --git a/bin/nyc.js b/bin/nyc.js
index 5f295abb7..4f216385a 100755
--- a/bin/nyc.js
+++ b/bin/nyc.js
@@ -2,6 +2,7 @@
 'use strict'
 
 const configUtil = require('../lib/config-util')
+const { cliWrapper, suppressEPIPE } = require('../lib/commands/helpers')
 const foreground = require('foreground-child')
 const resolveFrom = require('resolve-from')
 const NYC = require('../index.js')
@@ -81,29 +82,32 @@ async function main () {
   // a non-zero exit codes in either one leads to an overall non-zero exit code.
   process.exitCode = 0
   foreground(childArgs, async () => {
-    var mainChildExitCode = process.exitCode
-
-    await nyc.writeProcessIndex()
-
-    nyc.maybePurgeSourceMapCache()
-    if (argv.checkCoverage) {
-      await nyc.checkCoverage({
-        lines: argv.lines,
-        functions: argv.functions,
-        branches: argv.branches,
-        statements: argv.statements
-      }, argv['per-file'])
-      process.exitCode = process.exitCode || mainChildExitCode
-    }
-
-    if (!argv.silent) {
-      await nyc.report()
+    const mainChildExitCode = process.exitCode
+
+    try {
+      await nyc.writeProcessIndex()
+
+      nyc.maybePurgeSourceMapCache()
+      if (argv.checkCoverage) {
+        await nyc.checkCoverage({
+          lines: argv.lines,
+          functions: argv.functions,
+          branches: argv.branches,
+          statements: argv.statements
+        }, argv['per-file']).catch(suppressEPIPE)
+        process.exitCode = process.exitCode || mainChildExitCode
+      }
+
+      if (!argv.silent) {
+        await nyc.report().catch(suppressEPIPE)
+      }
+    } catch (error) {
+      /* istanbul ignore next */
+      process.exitCode = process.exitCode || mainChildExitCode || 1
+      /* istanbul ignore next */
+      console.error(error.message)
     }
   })
 }
 
-/* istanbul ignore next: the error branch should be unreachable */
-main().catch(error => {
-  console.error(error.message)
-  process.exit(1)
-})
+cliWrapper(main)()
diff --git a/lib/commands/check-coverage.js b/lib/commands/check-coverage.js
index 56ffdf219..cc361d16e 100644
--- a/lib/commands/check-coverage.js
+++ b/lib/commands/check-coverage.js
@@ -1,7 +1,7 @@
 'use strict'
 
 const NYC = require('../../index.js')
-const { cliWrapper, setupOptions } = require('./helpers.js')
+const { cliWrapper, suppressEPIPE, setupOptions } = require('./helpers.js')
 
 exports.command = 'check-coverage'
 
@@ -24,5 +24,5 @@ exports.handler = cliWrapper(async argv => {
     functions: argv.functions,
     branches: argv.branches,
     statements: argv.statements
-  }, argv['per-file'])
+  }, argv['per-file']).catch(suppressEPIPE)
 })
diff --git a/lib/commands/helpers.js b/lib/commands/helpers.js
index 8b967bd81..92e09242b 100644
--- a/lib/commands/helpers.js
+++ b/lib/commands/helpers.js
@@ -50,10 +50,23 @@ module.exports = {
       }
     })
   },
+  /* istanbul ignore next: unsure how to test this */
+  suppressEPIPE (error) {
+    /* Prevent dumping error when `nyc npm t|head` causes stdout to
+     * be closed when reporting runs. */
+    if (error.code !== 'EPIPE') {
+      throw error
+    }
+  },
   cliWrapper (execute) {
     return argv => {
       execute(argv).catch(error => {
-        console.error(error.message)
+        try {
+          console.error(error.message)
+        } catch (_) {
+          /* We need to run process.exit(1) even if stderr is destroyed */
+        }
+
         process.exit(1)
       })
     }
diff --git a/lib/commands/report.js b/lib/commands/report.js
index 8293bf790..6efb44166 100644
--- a/lib/commands/report.js
+++ b/lib/commands/report.js
@@ -1,7 +1,7 @@
 'use strict'
 
 const NYC = require('../../index.js')
-const { cliWrapper, setupOptions } = require('./helpers.js')
+const { cliWrapper, suppressEPIPE, setupOptions } = require('./helpers.js')
 
 exports.command = 'report'
 
@@ -18,13 +18,13 @@ exports.builder = function (yargs) {
 exports.handler = cliWrapper(async argv => {
   process.env.NYC_CWD = process.cwd()
   var nyc = new NYC(argv)
-  await nyc.report()
+  await nyc.report().catch(suppressEPIPE)
   if (argv.checkCoverage) {
     await nyc.checkCoverage({
       lines: argv.lines,
       functions: argv.functions,
       branches: argv.branches,
       statements: argv.statements
-    }, argv['per-file'])
+    }, argv['per-file']).catch(suppressEPIPE)
   }
 })