1 |
|
2 | (function () {
|
3 | "use strict";
|
4 |
|
5 | function noop() {}
|
6 |
|
7 | var fs = require('fs')
|
8 | , forEachAsync = require('foreachasync').forEachAsync
|
9 | , EventEmitter = require('events').EventEmitter
|
10 | , TypeEmitter = require('./node-type-emitter')
|
11 | , util = require('util')
|
12 | , path = require('path')
|
13 | ;
|
14 |
|
15 | function appendToDirs(stat) {
|
16 |
|
17 | if(stat.flag && stat.flag === NO_DESCEND) { return; }
|
18 | this.push(stat.name);
|
19 | }
|
20 |
|
21 | function wFilesHandlerWrapper(items) {
|
22 |
|
23 | this._wFilesHandler(noop, items);
|
24 | }
|
25 |
|
26 | function Walker(pathname, options, sync) {
|
27 | EventEmitter.call(this);
|
28 |
|
29 | var me = this
|
30 | ;
|
31 |
|
32 | options = options || {};
|
33 | me._wStat = options.followLinks && 'stat' || 'lstat';
|
34 | me._wStatSync = me._wStat + 'Sync';
|
35 | me._wsync = sync;
|
36 | me._wq = [];
|
37 | me._wqueue = [me._wq];
|
38 | me._wcurpath = undefined;
|
39 | me._wfilters = options.filters || [];
|
40 | me._wfirstrun = true;
|
41 | me._wcurpath = pathname;
|
42 |
|
43 | if (me._wsync) {
|
44 |
|
45 | me._wWalk = me._wWalkSync;
|
46 | } else {
|
47 |
|
48 | me._wWalk = me._wWalkAsync;
|
49 | }
|
50 |
|
51 | options.listeners = options.listeners || {};
|
52 | Object.keys(options.listeners).forEach(function (event) {
|
53 | var callbacks = options.listeners[event]
|
54 | ;
|
55 |
|
56 | if ('function' === typeof callbacks) {
|
57 | callbacks = [callbacks];
|
58 | }
|
59 |
|
60 | callbacks.forEach(function (callback) {
|
61 | me.on(event, callback);
|
62 | });
|
63 | });
|
64 |
|
65 | me._wWalk();
|
66 | }
|
67 |
|
68 |
|
69 | util.inherits(Walker, EventEmitter);
|
70 |
|
71 | Walker.prototype._wLstatHandler = function (err, stat) {
|
72 | var me = this
|
73 | ;
|
74 |
|
75 | stat = stat || {};
|
76 | stat.name = me._wcurfile;
|
77 |
|
78 | if (err) {
|
79 | stat.error = err;
|
80 |
|
81 |
|
82 | me.emit('nodeError', me._wcurpath, stat, noop);
|
83 | me._wfnodegroups.errors.push(stat);
|
84 | me._wCurFileCallback();
|
85 | } else {
|
86 | TypeEmitter.sortFnodesByType(stat, me._wfnodegroups);
|
87 |
|
88 | TypeEmitter.emitNodeType(me, me._wcurpath, stat, me._wCurFileCallback, me);
|
89 | }
|
90 | };
|
91 | Walker.prototype._wFilesHandler = function (cont, file) {
|
92 | var statPath
|
93 | , me = this
|
94 | ;
|
95 |
|
96 |
|
97 | me._wcurfile = file;
|
98 | me._wCurFileCallback = cont;
|
99 | me.emit('name', me._wcurpath, file, noop);
|
100 |
|
101 | statPath = me._wcurpath + path.sep + file;
|
102 |
|
103 | if (!me._wsync) {
|
104 |
|
105 | fs[me._wStat](statPath, function (err, stat) {
|
106 | me._wLstatHandler(err, stat);
|
107 | });
|
108 | return;
|
109 | }
|
110 |
|
111 | try {
|
112 | me._wLstatHandler(null, fs[me._wStatSync](statPath));
|
113 | } catch(e) {
|
114 | me._wLstatHandler(e);
|
115 | }
|
116 | };
|
117 | Walker.prototype._wOnEmitDone = function () {
|
118 | var me = this
|
119 | , dirs = []
|
120 | ;
|
121 |
|
122 | me._wfnodegroups.directories.forEach(appendToDirs, dirs);
|
123 | dirs.forEach(me._wJoinPath, me);
|
124 | me._wqueue.push(me._wq = dirs);
|
125 | me._wNext();
|
126 | };
|
127 | Walker.prototype._wPostFilesHandler = function () {
|
128 | var me = this
|
129 | ;
|
130 |
|
131 | if (me._wfnodegroups.errors.length) {
|
132 |
|
133 |
|
134 | me.emit('errors', me._wcurpath, me._wfnodegroups.errors, noop);
|
135 | }
|
136 |
|
137 | TypeEmitter.emitNodeTypeGroups(me, me._wcurpath, me._wfnodegroups, me._wOnEmitDone, me);
|
138 | };
|
139 | Walker.prototype._wReadFiles = function () {
|
140 | var me = this
|
141 | ;
|
142 |
|
143 | if (!me._wcurfiles || 0 === me._wcurfiles.length) {
|
144 | return me._wNext();
|
145 | }
|
146 |
|
147 |
|
148 |
|
149 | me.emit('names', me._wcurpath, me._wcurfiles, noop);
|
150 |
|
151 | if (me._wsync) {
|
152 | me._wcurfiles.forEach(wFilesHandlerWrapper, me);
|
153 | me._wPostFilesHandler();
|
154 | } else {
|
155 | forEachAsync(me._wcurfiles, me._wFilesHandler, me).then(me._wPostFilesHandler);
|
156 | }
|
157 | };
|
158 | Walker.prototype._wReaddirHandler = function (err, files) {
|
159 | var fnodeGroups = TypeEmitter.createNodeGroups()
|
160 | , me = this
|
161 | , parent
|
162 | , child
|
163 | ;
|
164 |
|
165 | me._wfnodegroups = fnodeGroups;
|
166 | me._wcurfiles = files;
|
167 |
|
168 |
|
169 | if (!err) {
|
170 | me._wReadFiles();
|
171 | return;
|
172 | }
|
173 |
|
174 |
|
175 | me._wcurpath = me._wcurpath.replace(/\/$/, '');
|
176 |
|
177 |
|
178 | if (!me._wfirstrun) {
|
179 |
|
180 | me.emit('directoryError', me._wcurpath, { error: err }, noop);
|
181 |
|
182 |
|
183 | me._wReadFiles();
|
184 | return;
|
185 | }
|
186 |
|
187 |
|
188 | me._wfirstrun = false;
|
189 |
|
190 |
|
191 | parent = me._wcurpath.replace(/^(.*)\/.*$/, '$1');
|
192 | fs[me._wStat](parent, function (e, stat) {
|
193 |
|
194 | if (stat) {
|
195 |
|
196 |
|
197 | child = me._wcurpath.replace(/^.*\/(.*)$/, '$1');
|
198 | me._wcurfiles = [child];
|
199 | me._wcurpath = parent;
|
200 | } else {
|
201 |
|
202 |
|
203 |
|
204 |
|
205 | me.emit('nodeError', me._wcurpath, { error: err }, noop);
|
206 | }
|
207 |
|
208 | me._wReadFiles();
|
209 | });
|
210 | };
|
211 | Walker.prototype._wFilter = function () {
|
212 | var me = this
|
213 | , exclude
|
214 | ;
|
215 |
|
216 |
|
217 |
|
218 | exclude = me._wfilters.some(function (filter) {
|
219 | if (me._wcurpath.match(filter)) {
|
220 | return true;
|
221 | }
|
222 | });
|
223 |
|
224 | return exclude;
|
225 | };
|
226 | Walker.prototype._wWalkSync = function () {
|
227 |
|
228 | var err
|
229 | , files
|
230 | , me = this
|
231 | ;
|
232 |
|
233 | try {
|
234 | files = fs.readdirSync(me._wcurpath);
|
235 | } catch(e) {
|
236 | err = e;
|
237 | }
|
238 |
|
239 | me._wReaddirHandler(err, files);
|
240 | };
|
241 | Walker.prototype._wWalkAsync = function () {
|
242 |
|
243 | var me = this
|
244 | ;
|
245 |
|
246 |
|
247 | fs.readdir(me._wcurpath, function (err, files) {
|
248 | me._wReaddirHandler(err, files);
|
249 | });
|
250 | };
|
251 | Walker.prototype._wNext = function () {
|
252 | var me = this
|
253 | ;
|
254 |
|
255 | if (me._paused) {
|
256 | return;
|
257 | }
|
258 | if (me._wq.length) {
|
259 | me._wcurpath = me._wq.pop();
|
260 | while (me._wq.length && me._wFilter()) {
|
261 | me._wcurpath = me._wq.pop();
|
262 | }
|
263 | if (me._wcurpath && !me._wFilter()) {
|
264 | me._wWalk();
|
265 | } else {
|
266 | me._wNext();
|
267 | }
|
268 | return;
|
269 | }
|
270 | me._wqueue.length -= 1;
|
271 | if (me._wqueue.length) {
|
272 | me._wq = me._wqueue[me._wqueue.length - 1];
|
273 | return me._wNext();
|
274 | }
|
275 |
|
276 |
|
277 |
|
278 | me.emit('end');
|
279 |
|
280 | };
|
281 | Walker.prototype._wJoinPath = function (v, i, o) {
|
282 | var me = this
|
283 | ;
|
284 |
|
285 | o[i] = [me._wcurpath, path.sep, v].join('');
|
286 | };
|
287 | Walker.prototype.pause = function () {
|
288 | this._paused = true;
|
289 | };
|
290 | Walker.prototype.resume = function () {
|
291 | this._paused = false;
|
292 | this._wNext();
|
293 | };
|
294 |
|
295 | exports.walk = function (path, opts) {
|
296 | return new Walker(path, opts, false);
|
297 | };
|
298 |
|
299 | exports.walkSync = function (path, opts) {
|
300 | return new Walker(path, opts, true);
|
301 | };
|
302 | }());
|