9
9
* Module dependencies.
10
10
*/
11
11
12
- var debug = require ( 'debug' ) ( 'mocha:watch' ) ;
13
12
var fs = require ( 'fs' ) ;
14
- var glob = require ( 'glob' ) ;
15
13
var path = require ( 'path' ) ;
16
14
var util = require ( 'util' ) ;
17
- var join = path . join ;
15
+ var glob = require ( 'glob' ) ;
18
16
var he = require ( 'he' ) ;
19
17
var errors = require ( './errors' ) ;
20
18
var createNoFilesMatchPatternError = errors . createNoFilesMatchPatternError ;
21
19
var createMissingArgumentError = errors . createMissingArgumentError ;
22
20
21
+ var assign = ( exports . assign = require ( 'object.assign' ) . getPolyfill ( ) ) ;
22
+
23
23
/**
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.
25
31
*/
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 ;
32
33
33
34
/**
34
35
* Escape special characters in the given string of html.
@@ -62,6 +63,7 @@ exports.isString = function(obj) {
62
63
*/
63
64
exports . watch = function ( files , fn ) {
64
65
var options = { interval : 100 } ;
66
+ var debug = require ( 'debug' ) ( 'mocha:watch' ) ;
65
67
files . forEach ( function ( file ) {
66
68
debug ( 'file %s' , file ) ;
67
69
fs . watchFile ( file , options , function ( curr , prev ) {
@@ -73,14 +75,25 @@ exports.watch = function(files, fn) {
73
75
} ;
74
76
75
77
/**
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>
77
86
*
78
87
* @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']
81
92
*/
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 ) ;
84
97
}
85
98
86
99
/**
@@ -92,24 +105,22 @@ function ignored(path) {
92
105
*
93
106
* @private
94
107
* @param {string } dir
95
- * @param {string[] } [ext =['. js']]
108
+ * @param {string[] } [exts =['js']]
96
109
* @param {Array } [ret=[]]
97
110
* @return {Array }
98
111
*/
99
- exports . files = function ( dir , ext , ret ) {
112
+ exports . files = function ( dir , exts , ret ) {
100
113
ret = ret || [ ] ;
101
- ext = ext || [ 'js' ] ;
102
-
103
- var re = new RegExp ( '\\.(' + ext . join ( '|' ) + ')$' ) ;
114
+ exts = exts || [ 'js' ] ;
104
115
105
116
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 ) ;
113
124
}
114
125
} ) ;
115
126
@@ -506,6 +517,42 @@ exports.canonicalize = function canonicalize(value, stack, typeHint) {
506
517
return canonicalizedObj ;
507
518
} ;
508
519
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
+
509
556
/**
510
557
* Lookup file names at the given `path`.
511
558
*
@@ -520,14 +567,18 @@ exports.canonicalize = function canonicalize(value, stack, typeHint) {
520
567
* @param {string[] } extensions - File extensions to look for.
521
568
* @param {boolean } recursive - Whether to recurse into subdirectories.
522
569
* @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.
523
572
*/
524
573
exports . lookupFiles = function lookupFiles ( filepath , extensions , recursive ) {
525
574
var files = [ ] ;
575
+ var stat ;
526
576
527
577
if ( ! fs . existsSync ( filepath ) ) {
528
578
if ( fs . existsSync ( filepath + '.js' ) ) {
529
579
filepath += '.js' ;
530
580
} else {
581
+ // Handle glob
531
582
files = glob . sync ( filepath ) ;
532
583
if ( ! files . length ) {
533
584
throw createNoFilesMatchPatternError (
@@ -539,8 +590,9 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) {
539
590
}
540
591
}
541
592
593
+ // Handle file
542
594
try {
543
- var stat = fs . statSync ( filepath ) ;
595
+ stat = fs . statSync ( filepath ) ;
544
596
if ( stat . isFile ( ) ) {
545
597
return filepath ;
546
598
}
@@ -549,13 +601,16 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) {
549
601
return ;
550
602
}
551
603
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
+
554
609
try {
555
- var stat = fs . statSync ( file ) ;
610
+ stat = fs . statSync ( pathname ) ;
556
611
if ( stat . isDirectory ( ) ) {
557
612
if ( recursive ) {
558
- files = files . concat ( lookupFiles ( file , extensions , recursive ) ) ;
613
+ files = files . concat ( lookupFiles ( pathname , extensions , recursive ) ) ;
559
614
}
560
615
return ;
561
616
}
@@ -574,11 +629,15 @@ exports.lookupFiles = function lookupFiles(filepath, extensions, recursive) {
574
629
'array'
575
630
) ;
576
631
}
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
+ ) {
579
638
return ;
580
639
}
581
- files . push ( file ) ;
640
+ files . push ( pathname ) ;
582
641
} ) ;
583
642
584
643
return files ;
@@ -797,14 +856,19 @@ exports.ngettext = function(n, msg1, msg2) {
797
856
exports . noop = function ( ) { } ;
798
857
799
858
/**
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
+ *
804
866
* @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
808
872
*/
809
873
exports . createMap = function ( obj ) {
810
874
return assign . apply (
@@ -814,10 +878,16 @@ exports.createMap = function(obj) {
814
878
} ;
815
879
816
880
/**
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
+ *
819
887
* @see {@link module:utils.createMap createMap }
888
+ * @param {...* } [obj] - Arguments to `Object.assign()`.
820
889
* @returns {Object } A frozen object with no prototype, having `...obj` properties
890
+ * @throws {TypeError } if argument is not a non-empty object.
821
891
*/
822
892
exports . defineConstants = function ( obj ) {
823
893
if ( type ( obj ) !== 'object' || ! Object . keys ( obj ) . length ) {
0 commit comments