Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80657 views
1
/**
2
* Copyright 2013 Facebook, Inc.
3
*
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
7
*
8
* http://www.apache.org/licenses/LICENSE-2.0
9
*
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
15
*/
16
17
var fs = require('fs');
18
var path = require('path');
19
20
/**
21
* Scans directories for files with given extensions (async)
22
* Will not follow symlinks. Uses node.js native function to traverse, can
23
* be slower, but more safe than findNative
24
*
25
* @param {Array.<String>} scanDirs Directories to scan, ex: ['html/']
26
* @param {Array.<String>} extensions Extensions to searc for, ex: ['.js']
27
* @param {function|null} ignore Optional function to filter out paths
28
* @param {Function} callback
29
*/
30
function find(scanDirs, extensions, ignore, callback) {
31
var result = [];
32
var activeCalls = 0;
33
34
function readdirRecursive(curDir) {
35
activeCalls++;
36
fs.readdir(curDir, function(err, names) {
37
activeCalls--;
38
39
for (var i = 0; i < names.length; i++) {
40
names[i] = path.join(curDir, names[i]);
41
}
42
43
names.forEach(function(curFile) {
44
if (ignore && ignore(curFile)) {
45
return;
46
}
47
activeCalls++;
48
49
fs.lstat(curFile, function(err, stat) {
50
activeCalls--;
51
52
if (!err && stat && !stat.isSymbolicLink()) {
53
if (stat.isDirectory()) {
54
readdirRecursive(curFile);
55
} else {
56
var ext = path.extname(curFile);
57
if (extensions.indexOf(ext) !== -1) {
58
result.push([curFile, stat.mtime.getTime()]);
59
}
60
}
61
}
62
if (activeCalls === 0) {
63
callback(result);
64
}
65
});
66
});
67
68
if (activeCalls === 0) {
69
callback(result);
70
}
71
});
72
}
73
74
scanDirs.forEach(readdirRecursive);
75
}
76
77
/**
78
* Scans directories for files with given extensions (async)
79
* Will not follow symlinks. Uses native find shell script. Usually faster than
80
* node.js based implementation though as any shell command is suspectable to
81
* attacks. Use with caution.
82
*
83
* @param {Array.<String>} scanDirs Directories to scan, ex: ['html/']
84
* @param {Array.<String>} extensions Extensions to searc for, ex: ['.js']
85
* @param {function|null} ignore Optional function to filter out paths
86
* @param {Function} callback
87
*/
88
function findNative(scanDirs, extensions, ignore, callback) {
89
var os = require('os');
90
if(os.platform() == 'win32'){
91
return find(scanDirs,extensions,ignore,callback);
92
}
93
var spawn = require('child_process').spawn;
94
var args = [].concat(scanDirs);
95
args.push('-type', 'f');
96
extensions.forEach(function(ext, index) {
97
if (index) {
98
args.push('-o');
99
}
100
args.push('-iname');
101
args.push('*' + ext);
102
});
103
104
var findProcess = spawn('find', args);
105
var stdout = '';
106
findProcess.stdout.setEncoding('utf-8');
107
findProcess.stdout.on('data', function(data) {
108
stdout += data;
109
});
110
111
findProcess.stdout.on('close', function(code) {
112
// Split by lines, trimming the trailing newline
113
var lines = stdout.trim().split('\n');
114
if (ignore) {
115
var include = function(x) {
116
return !ignore(x);
117
};
118
lines = lines.filter(include);
119
}
120
var result = [];
121
var count = lines.length;
122
// for (var i = 0; i < count; i++){
123
// if (lines[i]) {
124
// var stat = fs.statSync(lines[i]);
125
// if (stat) {
126
// result.push([lines[i], stat.mtime.getTime()]);
127
// }
128
// }
129
// }
130
// callback(result);
131
lines.forEach(function(path) {
132
fs.stat(path, function(err, stat) {
133
if (stat && !stat.isDirectory()) {
134
result.push([path, stat.mtime.getTime()]);
135
}
136
if (--count === 0) {
137
callback(result);
138
}
139
});
140
});
141
});
142
}
143
144
/**
145
* Wrapper for options for a find call
146
* @class
147
* @param {Object} options
148
*/
149
function FileFinder(options) {
150
this.scanDirs = options && options.scanDirs || ['.'];
151
this.extensions = options && options.extensions || ['.js'];
152
this.ignore = options && options.ignore || null;
153
this.useNative = options && options.useNative || false;
154
}
155
156
/**
157
* @param {Function} callback
158
*/
159
FileFinder.prototype.find = function(callback) {
160
var impl = this.useNative ? findNative : find;
161
impl(this.scanDirs, this.extensions, this.ignore, callback);
162
};
163
164
165
module.exports = FileFinder;
166
module.exports.find = find;
167
module.exports.findNative = findNative;
168
169