99 * Module dependencies.
1010 */
1111
12- var debug = require ( 'debug' ) ( 'mocha:watch' ) ;
1312var fs = require ( 'fs' ) ;
14- var glob = require ( 'glob' ) ;
1513var path = require ( 'path' ) ;
1614var util = require ( 'util' ) ;
17- var join = path . join ;
15+ var glob = require ( 'glob' ) ;
1816var he = require ( 'he' ) ;
1917var errors = require ( './errors' ) ;
2018var createNoFilesMatchPatternError = errors . createNoFilesMatchPatternError ;
2119var createMissingArgumentError = errors . createMissingArgumentError ;
2220
21+ var assign = ( exports . assign = require ( 'object.assign' ) . getPolyfill ( ) ) ;
22+
2323/**
24- * Ignored directories.
24+ * Inherit the prototype methods from one constructor into another.
25+ *
26+ * @param {function } ctor - Constructor function which needs to inherit the
27+ * prototype.
28+ * @param {function } superCtor - Constructor function to inherit prototype from.
29+ * @throws {TypeError } if either constructor is null, or if super constructor
30+ * lacks a prototype.
2531 */
26-
27- var ignore = [ 'node_modules' , '.git' ] ;
28-
29- exports . inherits = require ( 'util' ) . inherits ;
30-
31- var assign = ( exports . assign = require ( 'object.assign' ) . getPolyfill ( ) ) ;
32+ exports . inherits = util . inherits ;
3233
3334/**
3435 * Escape special characters in the given string of html.
@@ -62,6 +63,7 @@ exports.isString = function(obj) {
6263 */
6364exports . watch = function ( files , fn ) {
6465 var options = { interval : 100 } ;
66+ var debug = require ( 'debug' ) ( 'mocha:watch' ) ;
6567 files . forEach ( function ( file ) {
6668 debug ( 'file %s' , file ) ;
6769 fs . watchFile ( file , options , function ( curr , prev ) {
@@ -73,14 +75,25 @@ exports.watch = function(files, fn) {
7375} ;
7476
7577/**
76- * Ignored files.
78+ * Predicate to screen `pathname` for further consideration.
79+ *
80+ * @description
81+ * Returns <code>false</code> for pathname referencing:
82+ * <ul>
83+ * <li>'npm' package installation directory
84+ * <li>'git' version control directory
85+ * </ul>
7786 *
7887 * @private
79- * @param {string } path
80- * @return {boolean }
88+ * @param {string } pathname - File or directory name to screen
89+ * @return {boolean } whether pathname should be further considered
90+ * @example
91+ * ['node_modules', 'test.js'].filter(considerFurther); // => ['test.js']
8192 */
82- function ignored ( path ) {
83- return ! ~ ignore . indexOf ( path ) ;
93+ function considerFurther ( pathname ) {
94+ var ignore = [ 'node_modules' , '.git' ] ;
95+
96+ return ! ~ ignore . indexOf ( pathname ) ;
8497}
8598
8699/**
@@ -92,24 +105,22 @@ function ignored(path) {
92105 *
93106 * @private
94107 * @param {string } dir
95- * @param {string[] } [ext =['. js']]
108+ * @param {string[] } [exts =['js']]
96109 * @param {Array } [ret=[]]
97110 * @return {Array }
98111 */
99- exports . files = function ( dir , ext , ret ) {
112+ exports . files = function ( dir , exts , ret ) {
100113 ret = ret || [ ] ;
101- ext = ext || [ 'js' ] ;
102-
103- var re = new RegExp ( '\\.(' + ext . join ( '|' ) + ')$' ) ;
114+ exts = exts || [ 'js' ] ;
104115
105116 fs . readdirSync ( dir )
106- . filter ( ignored )
107- . forEach ( function ( path ) {
108- path = join ( dir , path ) ;
109- if ( fs . lstatSync ( path ) . isDirectory ( ) ) {
110- exports . files ( path , ext , ret ) ;
111- } else if ( path . match ( re ) ) {
112- ret . push ( path ) ;
117+ . filter ( considerFurther )
118+ . forEach ( function ( dirent ) {
119+ var pathname = path . join ( dir , dirent ) ;
120+ if ( fs . lstatSync ( pathname ) . isDirectory ( ) ) {
121+ exports . files ( pathname , exts , ret ) ;
122+ } else if ( hasMatchingExtname ( pathname , exts ) ) {
123+ ret . push ( pathname ) ;
113124 }
114125 } ) ;
115126
@@ -506,6 +517,42 @@ exports.canonicalize = function canonicalize(value, stack, typeHint) {
506517 return canonicalizedObj ;
507518} ;
508519
520+ /**
521+ * Determines if pathname has a matching file extension.
522+ *
523+ * @private
524+ * @param {string } pathname - Pathname to check for match.
525+ * @param {string[] } exts - List of file extensions (sans period).
526+ * @return {boolean } whether file extension matches.
527+ * @example
528+ * hasMatchingExtname('foo.html', ['js', 'css']); // => false
529+ */
530+ function hasMatchingExtname ( pathname , exts ) {
531+ var suffix = path . extname ( pathname ) . slice ( 1 ) ;
532+ return exts . some ( function ( element ) {
533+ return suffix === element ;
534+ } ) ;
535+ }
536+
537+ /**
538+ * Determines if pathname would be a "hidden" file (or directory) on UN*X.
539+ *
540+ * @description
541+ * On UN*X, pathnames beginning with a full stop (aka dot) are hidden during
542+ * typical usage. Dotfiles, plain-text configuration files, are prime examples.
543+ *
544+ * @see {@link http://xahlee.info/UnixResource_dir/writ/unix_origin_of_dot_filename.html|Origin of Dot File Names }
545+ *
546+ * @private
547+ * @param {string } pathname - Pathname to check for match.
548+ * @return {boolean } whether pathname would be considered a hidden file.
549+ * @example
550+ * isHiddenOnUnix('.profile'); // => true
551+ */
552+ function isHiddenOnUnix ( pathname ) {
553+ return path . basename ( pathname ) [ 0 ] === '.' ;
554+ }
555+
509556/**
510557 * Lookup file names at the given `path`.
511558 *
@@ -520,14 +567,18 @@ exports.canonicalize = function canonicalize(value, stack, typeHint) {
520567 * @param {string[] } extensions - File extensions to look for.
521568 * @param {boolean } recursive - Whether to recurse into subdirectories.
522569 * @return {string[] } An array of paths.
570+ * @throws {Error } if no files match pattern.
571+ * @throws {TypeError } if `filepath` is directory and `extensions` not provided.
523572 */
524573exports . lookupFiles = function lookupFiles ( filepath , extensions , recursive ) {
525574 var files = [ ] ;
575+ var stat ;
526576
527577 if ( ! fs . existsSync ( filepath ) ) {
528578 if ( fs . existsSync ( filepath + '.js' ) ) {
529579 filepath += '.js' ;
530580 } else {
581+ // Handle glob
531582 files = glob . sync ( filepath ) ;
532583 if ( ! files . length ) {
533584 throw createNoFilesMatchPatternError (
@@ -539,8 +590,9 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) {
539590 }
540591 }
541592
593+ // Handle file
542594 try {
543- var stat = fs . statSync ( filepath ) ;
595+ stat = fs . statSync ( filepath ) ;
544596 if ( stat . isFile ( ) ) {
545597 return filepath ;
546598 }
@@ -549,13 +601,16 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) {
549601 return ;
550602 }
551603
552- fs . readdirSync ( filepath ) . forEach ( function ( file ) {
553- file = path . join ( filepath , file ) ;
604+ // Handle directory
605+ fs . readdirSync ( filepath ) . forEach ( function ( dirent ) {
606+ var pathname = path . join ( filepath , dirent ) ;
607+ var stat ;
608+
554609 try {
555- var stat = fs . statSync ( file ) ;
610+ stat = fs . statSync ( pathname ) ;
556611 if ( stat . isDirectory ( ) ) {
557612 if ( recursive ) {
558- files = files . concat ( lookupFiles ( file , extensions , recursive ) ) ;
613+ files = files . concat ( lookupFiles ( pathname , extensions , recursive ) ) ;
559614 }
560615 return ;
561616 }
@@ -574,11 +629,15 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) {
574629 'array'
575630 ) ;
576631 }
577- var re = new RegExp ( '\\.(?:' + extensions . join ( '|' ) + ')$' ) ;
578- if ( ! stat . isFile ( ) || ! re . test ( file ) || path . basename ( file ) [ 0 ] === '.' ) {
632+
633+ if (
634+ ! stat . isFile ( ) ||
635+ ! hasMatchingExtname ( pathname , extensions ) ||
636+ isHiddenOnUnix ( pathname )
637+ ) {
579638 return ;
580639 }
581- files . push ( file ) ;
640+ files . push ( pathname ) ;
582641 } ) ;
583642
584643 return files ;
@@ -797,14 +856,19 @@ exports.ngettext = function(n, msg1, msg2) {
797856exports . noop = function ( ) { } ;
798857
799858/**
800- * @summary Creates a map-like object.
801- * @desc A "map" is an object with no prototype, for our purposes. In some cases this would be more appropriate than a `Map`, especially if your environment doesn't support it. Recommended for use in Mocha's public APIs.
802- * @param {...* } [obj] - Arguments to `Object.assign()`
803- * @returns {Object } An object with no prototype, having `...obj` properties
859+ * Creates a map-like object.
860+ *
861+ * @description
862+ * A "map" is an object with no prototype, for our purposes. In some cases
863+ * this would be more appropriate than a `Map`, especially if your environment
864+ * doesn't support it. Recommended for use in Mocha's public APIs.
865+ *
804866 * @public
805- * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
806- * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects
807- * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
867+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map|MDN:Map }
868+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create#Custom_and_Null_objects|MDN:Object.create - Custom objects }
869+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign|MDN:Object.assign }
870+ * @param {...* } [obj] - Arguments to `Object.assign()`.
871+ * @returns {Object } An object with no prototype, having `...obj` properties
808872 */
809873exports . createMap = function ( obj ) {
810874 return assign . apply (
@@ -814,10 +878,16 @@ exports.createMap = function(obj) {
814878} ;
815879
816880/**
817- * @summary Create a read-only map-like object.
818- * This differs from {@link module:utils.createMap createMap} only in that the argument must be non-empty, because the result is frozen.
881+ * Creates a read-only map-like object.
882+ *
883+ * @description
884+ * This differs from {@link module:utils.createMap createMap} only in that
885+ * the argument must be non-empty, because the result is frozen.
886+ *
819887 * @see {@link module:utils.createMap createMap }
888+ * @param {...* } [obj] - Arguments to `Object.assign()`.
820889 * @returns {Object } A frozen object with no prototype, having `...obj` properties
890+ * @throws {TypeError } if argument is not a non-empty object.
821891 */
822892exports . defineConstants = function ( obj ) {
823893 if ( type ( obj ) !== 'object' || ! Object . keys ( obj ) . length ) {
0 commit comments