Repository: mochajs/mocha

Problem statement:
"utils.files is not a function" (mocha: 5.0.2)
### Description

Hey, i've just upgraded to the latest mocha version `5.0.2` and I now start to see a `"utils.files is not a function"` error when running in watch mode with passing a glob path. Seems this commit ec8901a23c5194b6f7e6eee9c2568e5020c944ce removed the function which is still used in https://github.com/mochajs/mocha/blob/master/bin/_mocha#L532

### Steps to Reproduce

`NODE_ENV=test mocha './test/**/*.js'`

**Expected behavior:** 

it should execute my tests as before.

**Actual behavior:** 

it crashes all the time

```bash
> NODE_ENV=test mocha './test/**/*.js' "--watch"

/opt/MY-PACKAGE/node_modules/mocha/bin/_mocha:532
  const watchFiles = utils.files(cwd, [ 'js' ].concat(program.watchExtensions));
                           ^

TypeError: utils.files is not a function
    at Object.<anonymous> (/opt/MY-PACKAGE/node_modules/mocha/bin/_mocha:532:28)
    at Module._compile (module.js:643:30)
    at Object.Module._extensions..js (module.js:654:10)
    at Module.load (module.js:556:32)
    at tryModuleLoad (module.js:499:12)
    at Function.Module._load (module.js:491:3)
    at Function.Module.runMain (module.js:684:10)
    at startup (bootstrap_node.js:187:16)
    at bootstrap_node.js:608:3
```

### Versions

- `5.0.2`
- shell: zsh
- The code transpiler being used: babel



Hints:
Same here, looks like `utils.files` was removed in https://github.com/mochajs/mocha/commit/ec8901a23c5194b6f7e6eee9c2568e5020c944ce but is still in use at https://github.com/mochajs/mocha/blob/master/bin/_mocha#L532.
This exists in 5.0.1 as well it seems.
Confirm this is still an issue.
Downgraded to 5.0.1, it works.

Patch:
diff --git a/lib/utils.js b/lib/utils.js
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -8,8 +8,15 @@ var debug = require('debug')('mocha:watch');
 var fs = require('fs');
 var glob = require('glob');
 var path = require('path');
+var join = path.join;
 var he = require('he');
 
+/**
+ * Ignored directories.
+ */
+
+var ignore = ['node_modules', '.git'];
+
 exports.inherits = require('util').inherits;
 
 /**
@@ -54,6 +61,46 @@ exports.watch = function (files, fn) {
   });
 };
 
+/**
+ * Ignored files.
+ *
+ * @api private
+ * @param {string} path
+ * @return {boolean}
+ */
+function ignored (path) {
+  return !~ignore.indexOf(path);
+}
+
+/**
+ * Lookup files in the given `dir`.
+ *
+ * @api private
+ * @param {string} dir
+ * @param {string[]} [ext=['.js']]
+ * @param {Array} [ret=[]]
+ * @return {Array}
+ */
+exports.files = function (dir, ext, ret) {
+  ret = ret || [];
+  ext = ext || ['js'];
+
+  var re = new RegExp('\\.(' + ext.join('|') + ')$');
+
+  fs.readdirSync(dir)
+    .filter(ignored)
+    .forEach(function (path) {
+      path = join(dir, path);
+      if (fs.lstatSync(path).isDirectory()) {
+        exports.files(path, ext, ret);
+      } else if (path.match(re)) {
+        ret.push(path);
+      }
+    });
+
+  return ret;
+};
+
 /**
  * Compute a slug from the given `str`.
  *


Test patch:
diff --git a/test/node-unit/file-utils.spec.js b/test/node-unit/file-utils.spec.js
--- a/test/node-unit/file-utils.spec.js
+++ b/test/node-unit/file-utils.spec.js
@@ -105,6 +105,26 @@ describe('file utils', function () {
     });
   });
 
+  describe('.files', function () {
+    (symlinkSupported ? it : it.skip)('should return broken symlink file path', function () {
+      expect(utils.files(tmpDir, ['js']))
+        .to.contain(tmpFile('mocha-utils-link.js'))
+        .and.contain(tmpFile('mocha-utils.js'))
+        .and.have.length(2);
+
+      expect(existsSync(tmpFile('mocha-utils-link.js')))
+        .to.be(true);
+
+      fs.renameSync(tmpFile('mocha-utils.js'), tmpFile('bob'));
+
+      expect(existsSync(tmpFile('mocha-utils-link.js')))
+        .to.be(false);
+
+      expect(utils.files(tmpDir, ['js']))
+        .to.eql([tmpFile('mocha-utils-link.js')]);
+    });
+  });
+
   afterEach(removeTempDir);
 
   function makeTempDir () {