forked from olegskl/walk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
walk.js
137 lines (110 loc) · 4.06 KB
/
walk.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* @license The MIT License (MIT).
* Copyright (c) 2012 Oleg Sklyanchuk.
* http://opensource.org/licenses/mit-license.html
* @fileOverview Async directory walker for Node.js with recursion control.
* @author Oleg Sklyanchuk
* @version 0.1.2
*/
// JSLint directives:
/*jslint node: true */
// ECMAScript 5 strict mode:
'use strict';
var fs = require('fs'), // filesystem
pathJoin = require('path').join; // filepath helper
/**
* Noop function.
*/
function noop() {}
/**
* Walks an item with a given worker and an optional callback.
*
* @param {String} item Full path of a folder or file.
* @param {Function} worker A worker function applied to every item the walker
* steps on. The worker should return a truthy value for sub-directories that
* are to be walked.
* @param {Function} callback A callback function.
* @param [Boolean] ignoreWorker When truthy, ignores the worker function.
*
* @returns {Undefined}
*/
function walk(item, worker, callback, ignoreWorker) {
// Obtain the stats of the seed item to determine a way to handle it:
fs.stat(item, function (err, stats) {
// Interrupt on error:
if (err) {
callback(err);
return;
}
// Do not proceed if the item doesn't match the criteria:
// (any falsy value is acceptable)
if (!ignoreWorker && !worker(item, stats)) {
callback();
return;
}
// When the walker steps on directories:
// 1. Read the directory contents into memory;
// 2. Walk across the directory contents;
if (stats.isDirectory()) {
// Asynchronously read directory contents into memory, keeping in
// mind that readdir provides a list of item names, not full paths:
fs.readdir(item, function (err, itemNames) {
// Cache variables for faster retreival:
var i = 0, // item iteration identifier
itemCount = 0, // directory items counter
pending = 0, // keeps track of items
itemPath; // full item path
// If failed to retrieve the list of directory items,
// do not walk the directory and escalate the error object:
if (err) {
callback(err);
return;
}
// It's now safe to refer to itemNames:
pending = itemCount = itemNames.length;
// If the directory is empty, do not walk it:
if (itemCount === 0) {
callback();
return;
}
// A callback to be called for every item that has been walked:
function itemWalkCallback(err) {
if ((pending -= 1) === 0) {
callback(err);
}
}
// Iterating through the items in the folder:
for (i = 0; i < itemCount; i += 1) {
itemPath = pathJoin(item, itemNames[i]);
walk(itemPath, worker, itemWalkCallback);
}
});
}
});
}
/**
* Initiates walking a seed item with a given worker and an optional callback.
*
* @example walk('path/to/dir', worker, callback);
*
* @param {String} item Seed path of a folder or file.
* @param {Function} worker A worker function applied to every item the walker
* steps on. The worker should return a truthy value for sub-directories that
* are to be walked.
* @param [Function] callback A callback function.
*
* @returns {Undefined}
*/
module.exports = function (item, worker, callback) {
// Do not initiate walking without a valid worker:
if (typeof worker !== 'function') {
callback('worker not defined');
return;
}
// If callback is not defined, assign a noop to it:
if (typeof callback !== 'function') {
callback = noop;
}
// Start walking, but force the worker to ignore the seed item:
walk(item, worker, callback, true);
};