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