Repository: istanbuljs/nyc
Pull request #1052 (2019-04-04 06:13:32)

docs: move setup docs out of the readme

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

Coverage remained the same at 96.349% when pulling **a34305979d7d8299654bf1f15bb4a4db1f545e3f on docs/move-setups** into **31817de8e0da44f3d3fef7fc62cfc22cabcadc57 on master**.


Patch:
diff --git a/README.md b/README.md
index 90d351e..84c0ef1 100644
--- a/README.md
+++ b/README.md
@@ -421,76 +421,9 @@ rather than having to ignore every instance of that method:
 }
 ```
 
-## Integrating with coveralls
+## [Integrating with coveralls](./docs/setup-coveralls.md)
 
-[coveralls.io](https://coveralls.io) is a great tool for adding
-coverage reports to your GitHub project. Here's how to get nyc
-integrated with coveralls and travis-ci.org:
-
-1. add the coveralls and nyc dependencies to your module:
-
-  ```shell
-  npm install coveralls nyc --save-dev
-  ```
-
-2. update the scripts in your package.json to include these bins:
-
-  ```json
-  {
-     "scripts": {
-       "test": "nyc mocha",
-       "coverage": "nyc report --reporter=text-lcov | coveralls"
-     }
-  }
-  ```
-
-3. For private repos, add the environment variable `COVERALLS_REPO_TOKEN` to travis.
-
-4. add the following to your `.travis.yml`:
-
-  ```yaml
-  after_success: npm run coverage
-  ```
-
-That's all there is to it!
-
-> Note: by default coveralls.io adds comments to pull-requests on GitHub, this can feel intrusive. To disable this, click on your repo on coveralls.io and uncheck `LEAVE COMMENTS?`.
-
-## Integrating with codecov
-
-`nyc npm test && nyc report --reporter=text-lcov > coverage.lcov && codecov`
-
-[codecov](https://codecov.io/) is a great tool for adding
-coverage reports to your GitHub project, even viewing them inline on GitHub with a browser extension:
-
-Here's how to get `nyc` integrated with codecov and travis-ci.org:
-
-1. add the codecov and nyc dependencies to your module:
-
-  ```shell
-  npm install codecov nyc --save-dev
-  ```
-
-2. update the scripts in your package.json to include these bins:
-
-  ```json
-  {
-     "scripts": {
-       "test": "nyc tap ./test/*.js",
-       "coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov"
-     }
-  }
-  ```
-
-3. For private repos, add the environment variable `CODECOV_TOKEN` to travis.
-
-4. add the following to your `.travis.yml`:
-
-  ```yaml
-  after_success: npm run coverage
-  ```
-
-That's all there is to it!
+## [Integrating with codecov](./docs/setup-codecov.md)
 
 ## Integrating with TAP formatters
 
diff --git a/docs/setup-codecov.md b/docs/setup-codecov.md
new file mode 100644
index 0000000..9eabf4b
--- /dev/null
+++ b/docs/setup-codecov.md
@@ -0,0 +1,34 @@
+# Integrating with codecov.io
+
+> **tl;dr**: 
+> `nyc --reporter=lcov npm test && npx codecov`
+
+[codecov](https://codecov.io/) is a great tool for adding
+coverage reports to your GitHub project, even viewing them inline on GitHub with a browser extension:
+
+Here's how to get `nyc` integrated with codecov and travis-ci.org, assuming you have the `npx` executable (included with npm v5.2 and above):
+
+1. add the codecov and nyc dependencies to your module:
+
+  ```shell
+  npm install nyc --save-dev
+  ```
+
+2. update the scripts in your package.json to include these lines:
+
+  ```json
+  {
+     "scripts": {
+       "test": "nyc --reporter=lcov mocha ./test/*.js",
+       "coverage": "npx codecov"
+     }
+  }
+  ```
+
+3. For private repos, add the environment variable `CODECOV_TOKEN` to travis.
+
+4. add the following to your `.travis.yml`:
+
+  ```yaml
+  after_success: npm run coverage
+  ```
diff --git a/docs/setup-coveralls.md b/docs/setup-coveralls.md
new file mode 100644
index 0000000..623bd1b
--- /dev/null
+++ b/docs/setup-coveralls.md
@@ -0,0 +1,34 @@
+# Integrating with coveralls.io
+
+[coveralls.io](https://coveralls.io) is a great tool for adding
+coverage reports to your GitHub project. Here's how to get nyc
+integrated with coveralls and travis-ci.org:
+
+1. add the coveralls and nyc dependencies to your module:
+
+  ```shell
+  npm install coveralls nyc --save-dev
+  ```
+
+2. update the scripts in your package.json to include these bins:
+
+  ```json
+  {
+     "scripts": {
+       "test": "nyc mocha",
+       "coverage": "nyc report --reporter=text-lcov | coveralls"
+     }
+  }
+  ```
+
+3. For private repos, add the environment variable `COVERALLS_REPO_TOKEN` to travis.
+
+4. add the following to your `.travis.yml`:
+
+  ```yaml
+  after_success: npm run coverage
+  ```
+
+That's all there is to it!
+
+> Note: by default coveralls.io adds comments to pull-requests on GitHub, this can feel intrusive. To disable this, click on your repo on coveralls.io and uncheck `LEAVE COMMENTS?`.
diff --git a/index.js b/index.js
index 638b500..00e6cc8 100755
--- a/index.js
+++ b/index.js
@@ -179,9 +179,11 @@ NYC.prototype.instrumentAllFiles = function (input, output, cb) {
     const outCode = this._transform(inCode, inFile) || inCode
 
     if (output) {
+      const mode = fs.statSync(inFile).mode
       const outFile = path.resolve(output, relFile)
       mkdirp.sync(path.dirname(outFile))
-      fs.writeFileSync(outFile, outCode, 'utf-8')
+      fs.writeFileSync(outFile, outCode)
+      fs.chmodSync(outFile, mode)
     } else {
       console.log(outCode)
     }
diff --git a/lib/commands/instrument.js b/lib/commands/instrument.js
index 1820c58..3c0fe9d 100644
--- a/lib/commands/instrument.js
+++ b/lib/commands/instrument.js
@@ -1,6 +1,7 @@
 const NYC = require('../../index.js')
 const path = require('path')
 const rimraf = require('rimraf')
+const testExclude = require('test-exclude')
 
 exports.command = 'instrument <input> [output]'
 
@@ -48,6 +49,16 @@ exports.builder = function (yargs) {
       type: 'boolean',
       description: 'should nyc exit when an instrumentation failure occurs?'
     })
+    .option('include', {
+      alias: 'n',
+      default: [],
+      describe: 'a list of specific files and directories that should be instrumented, glob patterns are supported'
+    })
+    .option('exclude', {
+      alias: 'x',
+      default: testExclude.defaultExclude,
+      describe: 'a list of specific files and directories that should not be instrumented, glob patterns are supported'
+    })
     .option('es-modules', {
       default: true,
       type: 'boolean',
@@ -62,6 +73,28 @@ exports.builder = function (yargs) {
 }
 
 exports.handler = function (argv) {
+  if (argv.output && (path.resolve(argv.cwd, argv.input) === path.resolve(argv.cwd, argv.output))) {
+    console.error(`nyc instrument failed: cannot instrument files in place, <input> must differ from <output>`)
+    process.exitCode = 1
+    return
+  }
+
+  if (path.relative(argv.cwd, path.resolve(argv.cwd, argv.input)).startsWith('..')) {
+    console.error(`nyc instrument failed: cannot instrument files outside of project root directory`)
+    process.exitCode = 1
+    return
+  }
+
+  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)
+    } else {
+      console.error(`nyc instrument failed: attempt to delete '${process.cwd()}' or containing directory.`)
+      process.exit(1)
+    }
+  }
+
   // If instrument is set to false enable a noop instrumenter.
   argv.instrumenter = (argv.instrument)
     ? './lib/instrumenters/istanbul'
@@ -76,20 +109,12 @@ exports.handler = function (argv) {
     cwd: argv.cwd,
     compact: argv.compact,
     preserveComments: argv.preserveComments,
+    include: argv.include,
+    exclude: argv.exclude,
     esModules: argv.esModules,
     exitOnError: argv.exitOnError
   })
 
-  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)
-    } else {
-      console.error(`nyc instrument failed: attempt to delete '${process.cwd()}' or containing directory.`)
-      process.exit(1)
-    }
-  }
-
   nyc.instrumentAllFiles(argv.input, argv.output, err => {
     if (err) {
       console.error(err.message)
diff --git a/package-lock.json b/package-lock.json
index 7bc532b..dd60e2f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -285,7 +285,7 @@
         },
         "chalk": {
           "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
           "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
           "dev": true,
           "requires": {
@@ -304,7 +304,7 @@
         },
         "strip-ansi": {
           "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
           "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
           "dev": true,
           "requires": {
@@ -846,7 +846,7 @@
       "dependencies": {
         "minimist": {
           "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
           "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
           "dev": true
         }
@@ -1355,7 +1355,7 @@
         },
         "load-json-file": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+          "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
           "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
           "dev": true,
           "requires": {
@@ -1607,7 +1607,7 @@
     },
     "external-editor": {
       "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
+      "resolved": "http://registry.npmjs.org/external-editor/-/external-editor-2.2.0.tgz",
       "integrity": "sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A==",
       "dev": true,
       "requires": {
@@ -1697,7 +1697,7 @@
     },
     "foreground-child": {
       "version": "1.5.6",
-      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz",
+      "resolved": "http://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz",
       "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=",
       "requires": {
         "cross-spawn": "^4",
@@ -1826,7 +1826,7 @@
         },
         "load-json-file": {
           "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+          "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
           "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
           "dev": true,
           "requires": {
@@ -1863,7 +1863,7 @@
         },
         "minimist": {
           "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
           "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
           "dev": true
         },
@@ -2273,7 +2273,7 @@
     },
     "is-obj": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
+      "resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
       "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
       "dev": true
     },
@@ -2728,7 +2728,7 @@
         },
         "minimist": {
           "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
           "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
           "dev": true
         },
@@ -2813,7 +2813,7 @@
     },
     "minimist": {
       "version": "0.0.10",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+      "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
       "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
     },
     "minimist-options": {
@@ -2846,7 +2846,7 @@
     },
     "mkdirp": {
       "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+      "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
       "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
       "requires": {
         "minimist": "0.0.8"
@@ -2854,7 +2854,7 @@
       "dependencies": {
         "minimist": {
           "version": "0.0.8",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+          "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
           "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
         }
       }
@@ -4393,7 +4393,7 @@
     },
     "readable-stream": {
       "version": "2.3.6",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+      "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
       "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
       "dev": true,
       "requires": {
@@ -4755,7 +4755,7 @@
       "dependencies": {
         "minimist": {
           "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
           "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
           "dev": true
         }
@@ -5069,7 +5069,7 @@
     },
     "through": {
       "version": "2.3.8",
-      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
       "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
       "dev": true
     },
@@ -5264,7 +5264,7 @@
         },
         "strip-ansi": {
           "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
           "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
           "dev": true,
           "requires": {
@@ -5345,7 +5345,7 @@
     },
     "wrap-ansi": {
       "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+      "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
       "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
       "requires": {
         "string-width": "^1.0.1",
@@ -5377,7 +5377,7 @@
         },
         "strip-ansi": {
           "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
           "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
           "requires": {
             "ansi-regex": "^2.0.0"


Test patch:
diff --git a/test/fixtures/cli/.instrument-nycrc b/test/fixtures/cli/.instrument-nycrc
new file mode 100644
index 0000000..4063df5
--- /dev/null
+++ b/test/fixtures/cli/.instrument-nycrc
@@ -0,0 +1,5 @@
+{
+  "exclude": [
+    "**/exclude-me/**"
+  ]
+}
diff --git a/test/fixtures/cli/subdir/.gitignore b/test/fixtures/cli/subdir/.gitignore
index 21ba3bc..a5cb032 100644
--- a/test/fixtures/cli/subdir/.gitignore
+++ b/test/fixtures/cli/subdir/.gitignore
@@ -1,1 +1,2 @@
 output-dir
+!node_modules
diff --git a/test/fixtures/cli/subdir/input-dir/exclude-me/index.js b/test/fixtures/cli/subdir/input-dir/exclude-me/index.js
new file mode 100644
index 0000000..239a04e
--- /dev/null
+++ b/test/fixtures/cli/subdir/input-dir/exclude-me/index.js
@@ -0,0 +1,2 @@
+'use strict';
+console.log('Hello, World!')
diff --git a/test/fixtures/cli/subdir/input-dir/include-me/exclude-me.js b/test/fixtures/cli/subdir/input-dir/include-me/exclude-me.js
new file mode 100644
index 0000000..239a04e
--- /dev/null
+++ b/test/fixtures/cli/subdir/input-dir/include-me/exclude-me.js
@@ -0,0 +1,2 @@
+'use strict';
+console.log('Hello, World!')
diff --git a/test/fixtures/cli/subdir/input-dir/include-me/include-me.js b/test/fixtures/cli/subdir/input-dir/include-me/include-me.js
new file mode 100644
index 0000000..239a04e
--- /dev/null
+++ b/test/fixtures/cli/subdir/input-dir/include-me/include-me.js
@@ -0,0 +1,2 @@
+'use strict';
+console.log('Hello, World!')
diff --git a/test/fixtures/cli/subdir/input-dir/node_modules/index.js b/test/fixtures/cli/subdir/input-dir/node_modules/index.js
new file mode 100644
index 0000000..239a04e
--- /dev/null
+++ b/test/fixtures/cli/subdir/input-dir/node_modules/index.js
@@ -0,0 +1,2 @@
+'use strict';
+console.log('Hello, World!')
diff --git a/test/nyc-integration.js b/test/nyc-integration.js
index 23745e6..999e510 100644
--- a/test/nyc-integration.js
+++ b/test/nyc-integration.js
@@ -609,17 +609,17 @@ describe('the nyc cli', function () {
       })
 
       it('works in directories without a package.json', function (done) {
-        var args = [bin, 'instrument', './input-dir', './output-dir']
+        const args = [bin, 'instrument', './input-dir', './output-dir']
 
-        var subdir = path.resolve(fixturesCLI, 'subdir')
-        var proc = spawn(process.execPath, args, {
+        const subdir = path.resolve(fixturesCLI, 'subdir')
+        const proc = spawn(process.execPath, args, {
           cwd: subdir,
           env: env
         })
 
         proc.on('exit', function (code) {
           code.should.equal(0)
-          var target = path.resolve(subdir, 'output-dir', 'index.js')
+          const target = path.resolve(subdir, 'output-dir', 'index.js')
           fs.readFileSync(target, 'utf8')
             .should.match(/console.log\('Hello, World!'\)/)
           done()
@@ -627,16 +627,10 @@ describe('the nyc cli', function () {
       })
 
       it('can be configured to exit on error', function (done) {
-        var args = [
-          bin,
-          'instrument',
-          '--exit-on-error',
-          './input-dir',
-          './output-dir'
-        ]
+        const args = [bin, 'instrument', '--exit-on-error', './input-dir', './output-dir']
 
-        var subdir = path.resolve(fixturesCLI, 'subdir')
-        var proc = spawn(process.execPath, args, {
+        const subdir = path.resolve(fixturesCLI, 'subdir')
+        const proc = spawn(process.execPath, args, {
           cwd: subdir,
           env: env
         })
@@ -648,51 +642,210 @@ describe('the nyc cli', function () {
       })
 
       it('allows a single file to be instrumented', function (done) {
-        var args = [bin, 'instrument', './half-covered.js', './output']
+        const args = [bin, 'instrument', './half-covered.js', './output']
 
-        var proc = spawn(process.execPath, args, {
+        const inputPath = path.resolve(fixturesCLI, './half-covered.js')
+        const inputMode = fs.statSync(inputPath).mode & 0o7777
+        const newMode = 0o775
+        if (process.platform !== 'win32') {
+          fs.chmodSync(inputPath, newMode)
+        }
+
+        const proc = spawn(process.execPath, args, {
           cwd: fixturesCLI,
           env: env
         })
 
         proc.on('close', function (code) {
           code.should.equal(0)
-          var files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
+          const files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
           files.length.should.equal(1)
           files.should.include('half-covered.js')
+
+          if (process.platform !== 'win32') {
+            const outputPath = path.resolve(fixturesCLI, 'output', 'half-covered.js')
+            const outputMode = fs.statSync(outputPath).mode & 0o7777
+            outputMode.should.equal(newMode)
+
+            fs.chmodSync(inputPath, inputMode)
+          }
+
           done()
         })
       })
 
       it('allows a directory of files to be instrumented', function (done) {
-        var args = [bin, 'instrument', './', './output']
+        const args = [bin, 'instrument', './nyc-config-js', './output']
 
-        var proc = spawn(process.execPath, args, {
+        const proc = spawn(process.execPath, args, {
+          cwd: fixturesCLI,
+          env: env
+        })
+
+        proc.on('close', function (code) {
+          code.should.equal(0)
+          const files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
+          files.should.include('index.js')
+          files.should.include('ignore.js')
+          files.should.not.include('package.json')
+          files.should.not.include('node_modules')
+          done()
+        })
+      })
+
+      it('can instrument the project directory', function (done) {
+        const args = [bin, 'instrument', '.', './output']
+
+        const proc = spawn(process.execPath, args, {
           cwd: fixturesCLI,
           env: env
         })
 
         proc.on('close', function (code) {
           code.should.equal(0)
-          var files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
-          files.should.include('env.js')
-          files.should.include('es6.js')
+          const files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
+          files.should.include('args.js')
+          files.should.include('subdir')
           done()
         })
       })
 
       it('allows a sub-directory of files to be instrumented', function (done) {
-        var args = [bin, 'instrument', './subdir/input-dir', './output']
+        const args = [bin, 'instrument', './subdir/input-dir', './output']
 
-        var proc = spawn(process.execPath, args, {
+        const proc = spawn(process.execPath, args, {
+          cwd: fixturesCLI,
+          env: env
+        })
+
+        proc.on('close', function (code) {
+          code.should.equal(0)
+          const files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
+          files.should.include('index.js')
+          done()
+        })
+      })
+
+      it('allows a subdirectory to be excluded via .nycrc file', function (done) {
+        const args = [bin, 'instrument', '--nycrc-path', './.instrument-nycrc', './subdir/input-dir', './output']
+
+        const proc = spawn(process.execPath, args, {
           cwd: fixturesCLI,
           env: env
         })
 
         proc.on('close', function (code) {
           code.should.equal(0)
-          var files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
+          const files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
+          files.length.should.not.equal(0)
+          files.should.not.include('exclude-me')
+          files.should.not.include('node_modules')
           files.should.include('index.js')
+          files.should.include('bad.js')
+          const includeTarget = path.resolve(fixturesCLI, 'output', 'index.js')
+          fs.readFileSync(includeTarget, 'utf8')
+            .should.match(/var cov_/)
+          done()
+        })
+      })
+
+      it('allows a file to be excluded', function (done) {
+        const args = [bin, 'instrument', '--exclude', 'exclude-me/index.js', './subdir/input-dir', './output']
+
+        const proc = spawn(process.execPath, args, {
+          cwd: fixturesCLI,
+          env: env
+        })
+
+        proc.on('close', function (code) {
+          code.should.equal(0)
+          const files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
+          files.length.should.not.equal(0)
+          files.should.not.include('exclude-me')
+          done()
+        })
+      })
+
+      it('allows specifying a single sub-directory to be included', function (done) {
+        const args = [bin, 'instrument', '--include', '**/include-me/**', './subdir/input-dir', './output']
+
+        const proc = spawn(process.execPath, args, {
+          cwd: fixturesCLI,
+          env: env
+        })
+
+        proc.on('close', function (code) {
+          code.should.equal(0)
+          const files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
+          files.length.should.not.equal(0)
+          files.should.include('include-me')
+          const instrumented = path.resolve(fixturesCLI, 'output', 'include-me', 'include-me.js')
+          fs.readFileSync(instrumented, 'utf8')
+            .should.match(/var cov_/)
+          done()
+        })
+      })
+
+      it('allows a file to be excluded from an included directory', function (done) {
+        const args = [bin, 'instrument', '--exclude', '**/exclude-me.js', '--include', '**/include-me/**', './subdir/input-dir', './output']
+
+        const proc = spawn(process.execPath, args, {
+          cwd: fixturesCLI,
+          env: env
+        })
+
+        proc.on('close', function (code) {
+          code.should.equal(0)
+          const files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
+          files.length.should.not.equal(0)
+          files.should.include('include-me')
+          const includeMeFiles = fs.readdirSync(path.resolve(fixturesCLI, 'output', 'include-me'))
+          includeMeFiles.length.should.not.equal(0)
+          includeMeFiles.should.include('include-me.js')
+          includeMeFiles.should.not.include('exclude-me.js')
+          const instrumented = path.resolve(fixturesCLI, 'output', 'include-me', 'include-me.js')
+          fs.readFileSync(instrumented, 'utf8')
+            .should.match(/var cov_/)
+          done()
+        })
+      })
+
+      it('aborts if trying to write files in place', function (done) {
+        const args = [bin, 'instrument', '--delete', './', './']
+
+        const proc = spawn(process.execPath, args, {
+          cwd: fixturesCLI,
+          env: env
+        })
+
+        let stderr = ''
+        proc.stderr.on('data', function (chunk) {
+          stderr += chunk
+        })
+
+        proc.on('close', function (code) {
+          code.should.equal(1)
+          stderr.should.include('nyc instrument failed: cannot instrument files in place')
+          done()
+        })
+      })
+
+      it('aborts if trying to instrument files from outside the project root directory', function (done) {
+        const args = [bin, 'instrument', '--delete', '../', './']
+
+        const proc = spawn(process.execPath, args, {
+          cwd: fixturesCLI,
+          env: env
+        })
+
+        let stderr = ''
+        proc.stderr.on('data', function (chunk) {
+          stderr += chunk
+        })
+
+        proc.on('close', function (code) {
+          code.should.equal(1)
+          stderr.should.include('nyc instrument failed: cannot instrument files outside of project root directory')
           done()
         })
       })
@@ -750,7 +903,7 @@ describe('the nyc cli', function () {
         })
 
         it('cleans the output directory if `--delete` is specified', function (done) {
-          const args = [bin, 'instrument', '--delete', 'true', './', './output']
+          const args = [bin, 'instrument', '--delete', 'true', './subdir/input-dir', './output']
 
           const proc = spawn(process.execPath, args, {
             cwd: fixturesCLI,
@@ -759,16 +912,17 @@ describe('the nyc cli', function () {
 
           proc.on('close', function (code) {
             code.should.equal(0)
-            const subdirExists = fs.existsSync(path.resolve(fixturesCLI, './output/subdir/input-dir'))
+            const subdirExists = fs.existsSync(path.resolve(fixturesCLI, './output'))
             subdirExists.should.equal(true)
             const files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
             files.should.not.include('removed-by-clean')
+            files.should.include('exclude-me')
             done()
           })
         })
 
         it('does not clean the output directory by default', function (done) {
-          const args = [bin, 'instrument', './', './output']
+          const args = [bin, 'instrument', './subdir/input-dir', './output']
 
           const proc = spawn(process.execPath, args, {
             cwd: fixturesCLI,
@@ -777,7 +931,7 @@ describe('the nyc cli', function () {
 
           proc.on('close', function (code) {
             code.should.equal(0)
-            const subdirExists = fs.existsSync(path.resolve(fixturesCLI, './output/subdir/input-dir'))
+            const subdirExists = fs.existsSync(path.resolve(fixturesCLI, './output'))
             subdirExists.should.equal(true)
             const files = fs.readdirSync(path.resolve(fixturesCLI, './output'))
             files.should.include('removed-by-clean')
@@ -786,7 +940,7 @@ describe('the nyc cli', function () {
         })
 
         it('aborts if trying to clean process.cwd()', function (done) {
-          const args = [bin, 'instrument', '--delete', './', './']
+          const args = [bin, 'instrument', '--delete', './src', './']
 
           const proc = spawn(process.execPath, args, {
             cwd: fixturesCLI,