Compare commits
87 Commits
Author | SHA1 | Date |
---|---|---|
AJ ONeal | 7db92bab06 | |
AJ ONeal | b97c97941f | |
AJ ONeal | a55a943b22 | |
AJ ONeal | cbbd8f2743 | |
AJ ONeal | 6593caa2d8 | |
AJ ONeal | 38a7da0a6c | |
AJ ONeal | 280bfd28ae | |
AJ ONeal | 930ddb439b | |
AJ ONeal | 1e516c2a52 | |
wannabesrevenge | 7d943ef1f2 | |
AJ ONeal | b4b38f7520 | |
AJ ONeal | cccd13e0fc | |
AJ ONeal | 0978e93679 | |
AJ ONeal | 527cb82e9d | |
AJ ONeal | a72cd5e478 | |
AJ ONeal | 55af3327a6 | |
AJ ONeal | 1042205948 | |
AJ ONeal | 24d237ae74 | |
AJ ONeal | 767a684da1 | |
AJ ONeal | 0d6bda5385 | |
AJ ONeal | 01a28a0d2b | |
AJ ONeal | 06ccf5d52e | |
claflamme | 1b9032c638 | |
AJ ONeal | 53d7d4f345 | |
Steven Vachon | 2670ffd337 | |
AJ ONeal | cfca163597 | |
AJ ONeal | 65f572556a | |
AJ ONeal | 962f631b42 | |
AJ ONeal | a91f404fc7 | |
Mathieu Chateau | 1201577900 | |
Mathieu Chateau | 6b72860050 | |
AJ ONeal | 754f6bb9c4 | |
AJ ONeal | 908051c694 | |
Gregg Caines | 9e461cd9a5 | |
Gregg Caines | e79860e672 | |
AJ ONeal | 98b8f21ba9 | |
AJ ONeal | 5a5e0397d7 | |
AJ ONeal | 9102a54184 | |
AJ ONeal | 60ddf2a503 | |
AJ ONeal | 7d10ccfb6f | |
AJ ONeal | 3a5d395587 | |
AJ ONeal | ab6527a4c4 | |
AJ ONeal | 4dbe318a1c | |
AJ ONeal | 817e7dea56 | |
AJ ONeal | 8d1fda4cbc | |
AJ ONeal | 16f44f25fe | |
AJ ONeal | dad2e70092 | |
AJ ONeal | ec07897228 | |
Lars-Magnus Skog | 484489abf3 | |
AJ ONeal | 49e9260caa | |
ZECTBynmo | 863a196262 | |
ZECTBynmo | 85e84a0a3f | |
ZECTBynmo | 6c4e267bd1 | |
ZECTBynmo | 14063d8fc3 | |
ZECTBynmo | 142c66c411 | |
ZECTBynmo | 09ba753b80 | |
AJ ONeal | 42cdccff35 | |
David Murdoch | a58ea0d682 | |
AJ ONeal | a26de5a66c | |
AJ ONeal | ca3c95b022 | |
AJ ONeal | 8005996062 | |
AJ ONeal | b7454f32c4 | |
AJ ONeal | 63051eb24f | |
AJ ONeal | 0164f2b749 | |
AJ ONeal | 042cc6d3d6 | |
AJ ONeal | 5ea101960e | |
AJ ONeal | f1c2c4d5e1 | |
AJ ONeal | 2dd4eee860 | |
AJ ONeal | 1ea316fcbc | |
AJ ONeal | cf60919339 | |
AJ ONeal | 8d4dc9804a | |
AJ ONeal | 6b66729f5d | |
AJ ONeal | 69d37ac924 | |
AJ ONeal | 4a32ae2060 | |
AJ ONeal | 8df1d51fc8 | |
AJ ONeal | 0d5fcfd917 | |
AJ ONeal | f0f1d935ea | |
AJ ONeal | 364d4b552c | |
AJ ONeal | 35284be148 | |
AJ ONeal | e4ef96ad36 | |
AJ ONeal | c1d25bf637 | |
AJ ONeal | 4df55b7ced | |
AJ ONeal | fa29c9140c | |
AJ ONeal | a834a070af | |
AJ ONeal | edc248289b | |
AJ ONeal | 1088866a81 | |
AJ ONeal | 2afcadfeb4 |
|
@ -0,0 +1,2 @@
|
||||||
|
node_modules
|
||||||
|
walk-test
|
|
@ -0,0 +1,14 @@
|
||||||
|
{ "node": true
|
||||||
|
, "browser": true
|
||||||
|
, "jquery": true
|
||||||
|
|
||||||
|
, "onevar": true
|
||||||
|
, "laxcomma": true
|
||||||
|
, "laxbreak": true
|
||||||
|
|
||||||
|
, "eqeqeq": true
|
||||||
|
, "immed": true
|
||||||
|
, "undef": true
|
||||||
|
, "unused": true
|
||||||
|
, "latedef": true
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
Copyright 2011 AJ ONeal
|
||||||
|
|
||||||
|
This is open source software; you can redistribute it and/or modify it under the
|
||||||
|
terms of either:
|
||||||
|
|
||||||
|
a) the "MIT License"
|
||||||
|
b) the "Apache-2.0 License"
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
Apache-2.0 License Summary
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
324
README.md
324
README.md
|
@ -1,110 +1,272 @@
|
||||||
node-walk
|
# 2021 Update
|
||||||
====
|
|
||||||
|
|
||||||
A port python's `os.walk`, but using Node.JS conventions.
|
Consider using [`@root/walk`](https://npmjs.org/package/@root/walk) instead.
|
||||||
|
|
||||||
* EventEmitter
|
I created `walk` quite literally a decade ago, in the Node v0.x days.
|
||||||
* Asynchronous
|
Back then using an EventEmitter seemed like the thing to do. Nowadays,
|
||||||
* Chronological (optionally)
|
it seems a bit overkill for the simple task of walking over directories.
|
||||||
* Built-in flow-control
|
|
||||||
|
There's nothing wrong with `walk` - it's about the same as it was 10 years ago -
|
||||||
|
however, at only 50 lines of code long, `@root/walk` is much simpler and much faster.
|
||||||
|
|
||||||
|
# node-walk
|
||||||
|
|
||||||
|
| a [Root](https://rootprojects.org) project
|
||||||
|
|
||||||
|
nodejs walk implementation.
|
||||||
|
|
||||||
|
This is somewhat of a port python's `os.walk`, but using Node.JS conventions.
|
||||||
|
|
||||||
|
- EventEmitter
|
||||||
|
- Asynchronous
|
||||||
|
- Chronological (optionally)
|
||||||
|
- Built-in flow-control
|
||||||
|
- includes Synchronous version (same API as Asynchronous)
|
||||||
|
|
||||||
As few file descriptors are opened at a time as possible.
|
As few file descriptors are opened at a time as possible.
|
||||||
This is particularly well suited for single hard disks which are not flash or solid state.
|
This is particularly well suited for single hard disks which are not flash or solid state.
|
||||||
|
|
||||||
Memory usage is high (120mb for 60,000 dirs), but reduction is being investigated.
|
## Installation
|
||||||
Patches welcome.
|
|
||||||
|
|
||||||
Installation
|
```bash
|
||||||
----
|
npm install --save walk
|
||||||
|
```
|
||||||
|
|
||||||
npm install walk
|
# Getting Started
|
||||||
|
|
||||||
Usage
|
```javascript
|
||||||
====
|
'use strict';
|
||||||
|
|
||||||
var walk = require('walk').walk,
|
var walk = require('walk');
|
||||||
options,
|
var fs = require('fs');
|
||||||
walker;
|
var walker;
|
||||||
|
var options = {};
|
||||||
|
|
||||||
options = {
|
walker = walk.walk('/tmp', options);
|
||||||
followLinks: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
walker = walk("path/to/dir", options);
|
walker.on('file', function (root, fileStats, next) {
|
||||||
|
fs.readFile(fileStats.name, function () {
|
||||||
|
// doStuff
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
walker.on("directories", function (root, dirStatsArray, next) {
|
walker.on('errors', function (root, nodeStatsArray, next) {
|
||||||
// dirStatsArray is an array of `stat` objects with the additional attributes
|
next();
|
||||||
// * type
|
});
|
||||||
// * error
|
|
||||||
// * name
|
walker.on('end', function () {
|
||||||
|
console.log('all done');
|
||||||
next();
|
});
|
||||||
});
|
```
|
||||||
|
|
||||||
|
## Common Events
|
||||||
|
|
||||||
|
All single event callbacks are in the form of `function (root, stat, next) {}`.
|
||||||
|
|
||||||
|
All multiple event callbacks callbacks are in the form of `function (root, stats, next) {}`, except **names** which is an array of strings.
|
||||||
|
|
||||||
|
All **error** event callbacks are in the form `function (root, stat/stats, next) {}`.
|
||||||
|
**`stat.error`** contains the error.
|
||||||
|
|
||||||
|
- `names`
|
||||||
|
- `directory`
|
||||||
|
- `directories`
|
||||||
|
- `file`
|
||||||
|
- `files`
|
||||||
|
- `end`
|
||||||
|
- `nodeError` (`stat` failed)
|
||||||
|
- `directoryError` (`stat` succedded, but `readdir` failed)
|
||||||
|
- `errors` (a collection of any errors encountered)
|
||||||
|
|
||||||
|
A typical `stat` event looks like this:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{ dev: 16777223,
|
||||||
|
mode: 33188,
|
||||||
|
nlink: 1,
|
||||||
|
uid: 501,
|
||||||
|
gid: 20,
|
||||||
|
rdev: 0,
|
||||||
|
blksize: 4096,
|
||||||
|
ino: 49868100,
|
||||||
|
size: 5617,
|
||||||
|
blocks: 16,
|
||||||
|
atime: Mon Jan 05 2015 18:18:10 GMT-0700 (MST),
|
||||||
|
mtime: Thu Sep 25 2014 21:21:28 GMT-0600 (MDT),
|
||||||
|
ctime: Thu Sep 25 2014 21:21:28 GMT-0600 (MDT),
|
||||||
|
birthtime: Thu Sep 25 2014 21:21:28 GMT-0600 (MDT),
|
||||||
|
name: 'README.md',
|
||||||
|
type: 'file' }
|
||||||
|
```
|
||||||
|
|
||||||
|
# Advanced Example
|
||||||
|
|
||||||
|
Both Asynchronous and Synchronous versions are provided.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var walk = require('walk');
|
||||||
|
var fs = require('fs');
|
||||||
|
var options;
|
||||||
|
var walker;
|
||||||
|
|
||||||
|
options = {
|
||||||
|
followLinks: false,
|
||||||
|
// directories with these keys will be skipped
|
||||||
|
filters: ['Temp', '_Temp'],
|
||||||
|
};
|
||||||
|
|
||||||
|
walker = walk.walk('/tmp', options);
|
||||||
|
|
||||||
|
// OR
|
||||||
|
// walker = walk.walkSync("/tmp", options);
|
||||||
|
|
||||||
|
walker.on('names', function (root, nodeNamesArray) {
|
||||||
|
nodeNamesArray.sort(function (a, b) {
|
||||||
|
if (a > b) return 1;
|
||||||
|
if (a < b) return -1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
walker.on('directories', function (root, dirStatsArray, next) {
|
||||||
|
// dirStatsArray is an array of `stat` objects with the additional attributes
|
||||||
|
// * type
|
||||||
|
// * error
|
||||||
|
// * name
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
walker.on('file', function (root, fileStats, next) {
|
||||||
|
fs.readFile(fileStats.name, function () {
|
||||||
|
// doStuff
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
walker.on('errors', function (root, nodeStatsArray, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
walker.on('end', function () {
|
||||||
|
console.log('all done');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sync
|
||||||
|
|
||||||
|
Note: You **can't use EventEmitter** if you want truly synchronous walker
|
||||||
|
(although it's synchronous under the hood, it appears not to be due to the use of `process.nextTick()`).
|
||||||
|
|
||||||
|
Instead **you must use `options.listeners`** for truly synchronous walker.
|
||||||
|
|
||||||
|
Although the sync version uses all of the `fs.readSync`, `fs.readdirSync`, and other sync methods,
|
||||||
|
I don't think I can prevent the `process.nextTick()` that `EventEmitter` calls.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var walk = require('walk');
|
||||||
|
var fs = require('fs');
|
||||||
|
var options;
|
||||||
|
var walker;
|
||||||
|
|
||||||
|
// To be truly synchronous in the emitter and maintain a compatible api,
|
||||||
|
// the listeners must be listed before the object is created
|
||||||
|
options = {
|
||||||
|
listeners: {
|
||||||
|
names: function (root, nodeNamesArray) {
|
||||||
|
nodeNamesArray.sort(function (a, b) {
|
||||||
|
if (a > b) return 1;
|
||||||
|
if (a < b) return -1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
directories: function (root, dirStatsArray, next) {
|
||||||
|
// dirStatsArray is an array of `stat` objects with the additional attributes
|
||||||
|
// * type
|
||||||
|
// * error
|
||||||
|
// * name
|
||||||
|
|
||||||
walker.on("file", function (root, fileStats, next) {
|
|
||||||
fs.readFile(file, function () {
|
|
||||||
// doStuff
|
|
||||||
next();
|
next();
|
||||||
});
|
},
|
||||||
});
|
file: function (root, fileStats, next) {
|
||||||
|
fs.readFile(fileStats.name, function () {
|
||||||
|
// doStuff
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
errors: function (root, nodeStatsArray, next) {
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
walker.on("errors", function (root, nodeStatsArray, next) {
|
walker = walk.walkSync('/tmp', options);
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
walker.on("end", function () {
|
console.log('all done');
|
||||||
console.log("all done");
|
})();
|
||||||
});
|
```
|
||||||
|
|
||||||
API
|
# API
|
||||||
====
|
|
||||||
|
|
||||||
Emitted Values
|
Emitted Values
|
||||||
|
|
||||||
* `root` - the containing the files to be inspected
|
- `on('XYZ', function(root, stats, next) {})`
|
||||||
* *stats[Array]* - a single `stats` object or an array with some added attributes
|
|
||||||
* type - 'file', 'directory', etc
|
- `root` - the containing the files to be inspected
|
||||||
* error
|
- _stats[Array]_ - a single `stats` object or an array with some added attributes
|
||||||
* name - the name of the file, dir, etc
|
- type - 'file', 'directory', etc
|
||||||
* next - no more files will be read until this is called
|
- error
|
||||||
|
- name - the name of the file, dir, etc
|
||||||
|
- next - no more files will be read until this is called
|
||||||
|
|
||||||
Single Events - fired immediately
|
Single Events - fired immediately
|
||||||
|
|
||||||
* `end` - No files, dirs, etc left to inspect
|
- `end` - No files, dirs, etc left to inspect
|
||||||
|
|
||||||
* `directoryError` - Error when `fstat` succeeded, but reading path failed (Probably due to permissions).
|
- `directoryError` - Error when `fstat` succeeded, but reading path failed (Probably due to permissions).
|
||||||
* `node` - a `stats` object for a node of any type
|
- `nodeError` - Error `fstat` did not succeeded.
|
||||||
* `file` - includes links when `followLinks` is `true`
|
- `node` - a `stats` object for a node of any type
|
||||||
* `directory`
|
- `file` - includes links when `followLinks` is `true`
|
||||||
* `blockDevice`
|
- `directory` - **NOTE** you could get a recursive loop if `followLinks` and a directory links to its parent
|
||||||
* `characterDevice`
|
- `symbolicLink` - always empty when `followLinks` is `true`
|
||||||
* `symbolicLink` - always empty when `followLinks` is `true`
|
- `blockDevice`
|
||||||
* `FIFO`
|
- `characterDevice`
|
||||||
* `socket`
|
- `FIFO`
|
||||||
|
- `socket`
|
||||||
|
|
||||||
Array Arguments
|
Events with Array Arguments - fired after all files in the dir have been `stat`ed
|
||||||
|
|
||||||
* `errors` - errors encountered by `fs.stat` when reading ndes in a directory
|
- `names` - before any `stat` takes place. Useful for sorting and filtering.
|
||||||
* `nodes` - an array of `stats` of any type
|
|
||||||
* `files`
|
|
||||||
* `directories` - modification of this array - sorting, removing, etc - affects traversal
|
|
||||||
* `blockDevices`
|
|
||||||
* `characterDevices`
|
|
||||||
* `symbolicLinks`
|
|
||||||
* `FIFOs`
|
|
||||||
* `sockets`
|
|
||||||
|
|
||||||
**Warning** when following links, an infinite loop is possible
|
- Note: the array is an array of `string`s, not `stat` objects
|
||||||
|
- Note: the `next` argument is a `noop`
|
||||||
|
|
||||||
Comparisons
|
- `errors` - errors encountered by `fs.stat` when reading ndes in a directory
|
||||||
====
|
- `nodes` - an array of `stats` of any type
|
||||||
|
- `files`
|
||||||
|
- `directories` - modification of this array - sorting, removing, etc - affects traversal
|
||||||
|
- `symbolicLinks`
|
||||||
|
- `blockDevices`
|
||||||
|
- `characterDevices`
|
||||||
|
- `FIFOs`
|
||||||
|
- `sockets`
|
||||||
|
|
||||||
|
**Warning** beware of infinite loops when `followLinks` is true (using `walk-recurse` varient).
|
||||||
|
|
||||||
|
# Comparisons
|
||||||
|
|
||||||
Tested on my `/System` containing 59,490 (+ self) directories (and lots of files).
|
Tested on my `/System` containing 59,490 (+ self) directories (and lots of files).
|
||||||
The size of the text output was 6mb.
|
The size of the text output was 6mb.
|
||||||
|
|
||||||
`find`:
|
`find`:
|
||||||
time bash -c "find /System -type d | wc"
|
time bash -c "find /System -type d | wc"
|
||||||
59491 97935 6262916
|
59491 97935 6262916
|
||||||
|
|
||||||
real 2m27.114s
|
real 2m27.114s
|
||||||
user 0m1.193s
|
user 0m1.193s
|
||||||
|
@ -116,12 +278,12 @@ Note that `find.js` omits the start directory
|
||||||
|
|
||||||
time bash -c "node examples/find.js /System -type d | wc"
|
time bash -c "node examples/find.js /System -type d | wc"
|
||||||
59490 97934 6262908
|
59490 97934 6262908
|
||||||
|
|
||||||
# Test 1
|
# Test 1
|
||||||
real 2m52.273s
|
real 2m52.273s
|
||||||
user 0m20.374s
|
user 0m20.374s
|
||||||
sys 0m27.800s
|
sys 0m27.800s
|
||||||
|
|
||||||
# Test 2
|
# Test 2
|
||||||
real 2m23.725s
|
real 2m23.725s
|
||||||
user 0m18.019s
|
user 0m18.019s
|
||||||
|
@ -132,3 +294,13 @@ Note that `find.js` omits the start directory
|
||||||
user 0m17.661s
|
user 0m17.661s
|
||||||
sys 0m24.008s
|
sys 0m24.008s
|
||||||
|
|
||||||
|
In conclusion node.js asynchronous walk is much slower than regular "find".
|
||||||
|
|
||||||
|
# LICENSE
|
||||||
|
|
||||||
|
`node-walk` is available under the following licenses:
|
||||||
|
|
||||||
|
- MIT
|
||||||
|
- Apache 2
|
||||||
|
|
||||||
|
Copyright 2011 - Present AJ ONeal
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var walk = require('walk')
|
||||||
|
, count = 0
|
||||||
|
, emitter
|
||||||
|
, saneCount = 0
|
||||||
|
;
|
||||||
|
|
||||||
|
function sort(a,b) {
|
||||||
|
a= a.toLowerCase();
|
||||||
|
b= b.toLowerCase();
|
||||||
|
if (a > b) return -1;
|
||||||
|
if (a < b) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.argv.forEach(function(startpath, index) {
|
||||||
|
if (index > 1) {
|
||||||
|
emitter = walk.walk(startpath);
|
||||||
|
|
||||||
|
// Non-`stat`ed Nodes
|
||||||
|
emitter.on('name', function (path, file, stat) {
|
||||||
|
saneCount += 1;
|
||||||
|
//console.log( ["[", count, "] ", path, '/', file].join('') )
|
||||||
|
//console.log( [path, '/', file].join('') )
|
||||||
|
});
|
||||||
|
emitter.on('names', function (path, files, stats) {
|
||||||
|
files.sort(sort);
|
||||||
|
//console.log('sort: ' + files.join(' ; '));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Single `stat`ed Nodes
|
||||||
|
emitter.on('error', function (path, err, next) {
|
||||||
|
next()
|
||||||
|
// ignore
|
||||||
|
});
|
||||||
|
emitter.on('directoryError', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('nodeError', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('node', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
//console.log( ["[", count, "] ", path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
emitter.on('file', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
//console.log( ["[", count, "] ", path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('directory', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('symbolicLink', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('blockDevice', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('characterDevice', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('FIFO', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('socket', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Grouped `stat`ed Nodes
|
||||||
|
emitter.on('errors', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('nodes', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
emitter.on('files', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('directories', function (path, stats, next) {
|
||||||
|
//delete stats[1];
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('symbolicLinks', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('blockDevices', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('characterDevices', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('FIFOs', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('sockets', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// The end of all things
|
||||||
|
emitter.on('end', function () {
|
||||||
|
console.log("The eagle has landed. [" + count + " == " + saneCount + "]");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}());
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*jshint strict:true node:true es5:true onevar:true laxcomma:true laxbreak:true*/
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// "FIFO" isn't easy to convert to camelCase and back reliably
|
||||||
|
var isFnodeTypes = [
|
||||||
|
"isFile", "isDirectory", "isSymbolicLink", "isBlockDevice", "isCharacterDevice", "isFIFO", "isSocket"
|
||||||
|
],
|
||||||
|
fnodeTypes = [
|
||||||
|
"file", "directory", "symbolicLink", "blockDevice", "characterDevice", "FIFO", "socket"
|
||||||
|
],
|
||||||
|
fnodeTypesPlural = [
|
||||||
|
"files", "directories", "symbolicLinks", "blockDevices", "characterDevices", "FIFOs", "sockets"
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
function createNodeGroups() {
|
||||||
|
var nodeGroups = {};
|
||||||
|
fnodeTypesPlural.concat("nodes", "errors").forEach(function (fnodeTypePlural) {
|
||||||
|
nodeGroups[fnodeTypePlural] = [];
|
||||||
|
});
|
||||||
|
return nodeGroups;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Determine each file node's type
|
||||||
|
//
|
||||||
|
function sortFnodesByType(stat, fnodes) {
|
||||||
|
var i, isType;
|
||||||
|
|
||||||
|
for (i = 0; i < isFnodeTypes.length; i += 1) {
|
||||||
|
isType = isFnodeTypes[i];
|
||||||
|
if (stat[isType]()) {
|
||||||
|
stat.type = fnodeTypes[i];
|
||||||
|
fnodes[fnodeTypesPlural[i]].push(stat);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Get the current number of listeners (which may change)
|
||||||
|
// Emit events to each listener
|
||||||
|
// Wait for all listeners to `next()` before continueing
|
||||||
|
// (in theory this may avoid disk thrashing)
|
||||||
|
function emitSingleEvents(emitter, path, stats, next, self) {
|
||||||
|
var num = 1 + emitter.listeners(stats.type).length + emitter.listeners("node").length;
|
||||||
|
|
||||||
|
function nextWhenReady(flag) {
|
||||||
|
if (flag) {
|
||||||
|
stats.flag = flag;
|
||||||
|
}
|
||||||
|
num -= 1;
|
||||||
|
if (0 === num) { next.call(self); }
|
||||||
|
}
|
||||||
|
|
||||||
|
emitter.emit(stats.type, path, stats, nextWhenReady);
|
||||||
|
emitter.emit("node", path, stats, nextWhenReady);
|
||||||
|
nextWhenReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Since the risk for disk thrashing among anything
|
||||||
|
// other than files is relatively low, all types are
|
||||||
|
// emitted at once, but all must complete before advancing
|
||||||
|
function emitPluralEvents(emitter, path, nodes, next, self) {
|
||||||
|
var num = 1;
|
||||||
|
|
||||||
|
function nextWhenReady() {
|
||||||
|
num -= 1;
|
||||||
|
if (0 === num) { next.call(self); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fnodeTypesPlural.concat(["nodes", "errors"]).forEach(function (fnodeType) {
|
||||||
|
if (0 === nodes[fnodeType].length) { return; }
|
||||||
|
num += emitter.listeners(fnodeType).length;
|
||||||
|
emitter.emit(fnodeType, path, nodes[fnodeType], nextWhenReady);
|
||||||
|
});
|
||||||
|
nextWhenReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
emitNodeType: emitSingleEvents,
|
||||||
|
emitNodeTypeGroups: emitPluralEvents,
|
||||||
|
isFnodeTypes: isFnodeTypes,
|
||||||
|
fnodeTypes: fnodeTypes,
|
||||||
|
fnodeTypesPlural: fnodeTypesPlural,
|
||||||
|
sortFnodesByType: sortFnodesByType,
|
||||||
|
createNodeGroups: createNodeGroups
|
||||||
|
};
|
||||||
|
}());
|
|
@ -0,0 +1,93 @@
|
||||||
|
(function () {
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
// Array.prototype.forEachAsync(next, item, i, collection)
|
||||||
|
require('futures/forEachAsync');
|
||||||
|
|
||||||
|
function noop() {}
|
||||||
|
|
||||||
|
var fs = require('fs'),
|
||||||
|
path = require('path'),
|
||||||
|
EventEmitter = require('events').EventEmitter,
|
||||||
|
TypeEmitter = require('./node-type-emitter');
|
||||||
|
|
||||||
|
// 2010-11-25 jorge@jorgechamorro.com
|
||||||
|
function create(pathname, cb) {
|
||||||
|
var emitter = new EventEmitter(),
|
||||||
|
q = [],
|
||||||
|
queue = [q],
|
||||||
|
curpath;
|
||||||
|
|
||||||
|
function walk() {
|
||||||
|
fs.readdir(curpath, function(err, files) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('directoryError', curpath, { error: err }, noop);
|
||||||
|
//emitter.emit('error', curpath, { error: err });
|
||||||
|
}
|
||||||
|
// XXX bug was here. next() was omitted
|
||||||
|
if (!files || 0 == files.length) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
var fnodeGroups = TypeEmitter.createNodeGroups();
|
||||||
|
|
||||||
|
// TODO could allow user to selectively stat
|
||||||
|
// and don't stat if there are no stat listeners
|
||||||
|
emitter.emit('names', curpath, files, noop);
|
||||||
|
files.forEachAsync(function (cont, file) {
|
||||||
|
emitter.emit('name', curpath, file, noop);
|
||||||
|
fs.lstat(curpath + path.sep + file, function (err, stat) {
|
||||||
|
stat = stat || {};
|
||||||
|
stat.name = file;
|
||||||
|
if (err) {
|
||||||
|
stat.error = err;
|
||||||
|
//emitter.emit('error', curpath, stat);
|
||||||
|
emitter.emit('nodeError', curpath, stat, noop);
|
||||||
|
fnodeGroups.errors.push(stat);
|
||||||
|
cont();
|
||||||
|
} else {
|
||||||
|
TypeEmitter.sortFnodesByType(stat, fnodeGroups);
|
||||||
|
TypeEmitter.emitNodeType(emitter, curpath, stat, cont);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
if (fnodeGroups.errors.length) {
|
||||||
|
emitter.emit('errors', curpath, fnodeGroups.errors, noop);
|
||||||
|
}
|
||||||
|
TypeEmitter.emitNodeTypeGroups(emitter, curpath, fnodeGroups, function () {
|
||||||
|
var dirs = [];
|
||||||
|
fnodeGroups.directories.forEach(function (stat) {
|
||||||
|
dirs.push(stat.name);
|
||||||
|
});
|
||||||
|
dirs.forEach(fullPath);
|
||||||
|
queue.push(q = dirs);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
if (q.length) {
|
||||||
|
curpath = q.pop();
|
||||||
|
return walk();
|
||||||
|
}
|
||||||
|
if (queue.length -= 1) {
|
||||||
|
q = queue[queue.length-1];
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
emitter.emit('end');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullPath(v,i,o) {
|
||||||
|
o[i]= [curpath, path.sep, v].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
curpath = pathname;
|
||||||
|
walk();
|
||||||
|
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = create;
|
||||||
|
}());
|
443
lib/walk.js
443
lib/walk.js
|
@ -1,165 +1,302 @@
|
||||||
// TODO
|
// Adapted from work by jorge@jorgechamorro.com on 2010-11-25
|
||||||
// * add types by listener dynamically
|
(function () {
|
||||||
// * unroll loops for better readability?
|
"use strict";
|
||||||
// * should emitted errors wait for `next()`?
|
|
||||||
(function (undefined) {
|
|
||||||
var fs = require('fs'),
|
|
||||||
upath = require('path'),
|
|
||||||
util = require('util'),
|
|
||||||
Futures = require('futures'),
|
|
||||||
events = require('events'),
|
|
||||||
noop = function () {},
|
|
||||||
// "FIFO" isn't easy to convert to camelCame and back reliably
|
|
||||||
isFnodeTypes = [
|
|
||||||
"isFile", "isDirectory", "isBlockDevice", "isCharacterDevice", "isSymbolicLink", "isFIFO", "isSocket"
|
|
||||||
],
|
|
||||||
fnodeTypes = [
|
|
||||||
"file", "directory", "blockDevice", "characterDevice", "symbolicLink", "FIFO", "socket"
|
|
||||||
],
|
|
||||||
fnodeTypesPlural = [
|
|
||||||
"files", "directories", "blockDevices", "characterDevices", "symbolicLinks", "FIFOs", "sockets"
|
|
||||||
];
|
|
||||||
|
|
||||||
function newVersion() {
|
function noop() {}
|
||||||
throw new Error("New Version. Please see API on github.com/coolaj86/node-walk");
|
|
||||||
|
var fs = require('fs')
|
||||||
|
, forEachAsync = require('foreachasync').forEachAsync
|
||||||
|
, EventEmitter = require('events').EventEmitter
|
||||||
|
, TypeEmitter = require('./node-type-emitter')
|
||||||
|
, util = require('util')
|
||||||
|
, path = require('path')
|
||||||
|
;
|
||||||
|
|
||||||
|
function appendToDirs(stat) {
|
||||||
|
/*jshint validthis:true*/
|
||||||
|
if(stat.flag && stat.flag === NO_DESCEND) { return; }
|
||||||
|
this.push(stat.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new walk instance
|
function wFilesHandlerWrapper(items) {
|
||||||
function create(path, options, cb) {
|
/*jshint validthis:true*/
|
||||||
if (cb) {
|
this._wFilesHandler(noop, items);
|
||||||
newVersion();
|
}
|
||||||
|
|
||||||
|
function Walker(pathname, options, sync) {
|
||||||
|
EventEmitter.call(this);
|
||||||
|
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
me._wStat = options.followLinks && 'stat' || 'lstat';
|
||||||
|
me._wStatSync = me._wStat + 'Sync';
|
||||||
|
me._wsync = sync;
|
||||||
|
me._wq = [];
|
||||||
|
me._wqueue = [me._wq];
|
||||||
|
me._wcurpath = undefined;
|
||||||
|
me._wfilters = options.filters || [];
|
||||||
|
me._wfirstrun = true;
|
||||||
|
me._wcurpath = pathname;
|
||||||
|
|
||||||
|
if (me._wsync) {
|
||||||
|
//console.log('_walkSync');
|
||||||
|
me._wWalk = me._wWalkSync;
|
||||||
|
} else {
|
||||||
|
//console.log('_walkASync');
|
||||||
|
me._wWalk = me._wWalkAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
var emitter = new events.EventEmitter(),
|
|
||||||
fstat = (options||{}).followLinks ? fs.stat : fs.lstat;
|
|
||||||
|
|
||||||
|
options.listeners = options.listeners || {};
|
||||||
|
Object.keys(options.listeners).forEach(function (event) {
|
||||||
|
var callbacks = options.listeners[event]
|
||||||
|
;
|
||||||
|
|
||||||
// Get the current number of listeners (which may change)
|
if ('function' === typeof callbacks) {
|
||||||
// Emit events to each listener
|
callbacks = [callbacks];
|
||||||
// Wait for all listeners to `next()` before continueing
|
|
||||||
// (in theory this may avoid disk thrashing)
|
|
||||||
function emitSingleEvents(path, stats, next) {
|
|
||||||
var num = 1 + emitter.listeners(stats.type).length + emitter.listeners("node").length;
|
|
||||||
|
|
||||||
function nextWhenReady() {
|
|
||||||
num -= 1;
|
|
||||||
if (0 === num) { next(); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emitter.emit(stats.type, path, stats, nextWhenReady);
|
callbacks.forEach(function (callback) {
|
||||||
emitter.emit("node", path, stats, nextWhenReady);
|
me.on(event, callback);
|
||||||
nextWhenReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Since the risk for disk thrashing among anything
|
|
||||||
// other than files is relatively low, all types are
|
|
||||||
// emitted at once, but all must complete before advancing
|
|
||||||
function emitPluralEvents(path, nodes, next) {
|
|
||||||
var num = 1;
|
|
||||||
|
|
||||||
function nextWhenReady() {
|
|
||||||
num -= 1;
|
|
||||||
if (0 === num) { next(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
fnodeTypesPlural.concat(["nodes", "errors"]).forEach(function (fnodeType) {
|
|
||||||
if (0 === nodes[fnodeType].length) { return; }
|
|
||||||
num += emitter.listeners(fnodeType).length;
|
|
||||||
emitter.emit(fnodeType, path, nodes[fnodeType], nextWhenReady);
|
|
||||||
});
|
});
|
||||||
nextWhenReady();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Determine each file node's type
|
|
||||||
//
|
|
||||||
function sortFnodesByType(path, stats, fnodes, nextFile) {
|
|
||||||
isFnodeTypes.forEach(function (isType, i) {
|
|
||||||
if (stats[isType]()) {
|
|
||||||
if (stats.type) { throw new Error("is_" + type + " and " + isType); }
|
|
||||||
stats.type = fnodeTypes[i];
|
|
||||||
fnodes[fnodeTypesPlural[i]].push(stats);
|
|
||||||
// TODO throw to break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (!stats.type) { throw new Error(upath.join(path, stats.name) + ' isAnUndefinedType'); }
|
|
||||||
emitSingleEvents(path, stats, nextFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Asynchronously get the stats
|
|
||||||
//
|
|
||||||
function getStats(path, files, walkDirs) {
|
|
||||||
var nodeGroups = {};
|
|
||||||
|
|
||||||
fnodeTypesPlural.concat("nodes", "errors").forEach(function (fnodeTypePlural) {
|
|
||||||
nodeGroups[fnodeTypePlural] = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
function nextFile() {
|
|
||||||
var file = files.pop(), dirs = [], fnames = [];
|
|
||||||
|
|
||||||
if (undefined === file) {
|
|
||||||
emitPluralEvents(path, nodeGroups, function () {
|
|
||||||
nodeGroups.directories.forEach(function (dir) {
|
|
||||||
dirs.push(dir.name);
|
|
||||||
});
|
|
||||||
walkDirs(dirs);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fstat(upath.join(path, file), function (err, stats) {
|
|
||||||
stats = stats || {};
|
|
||||||
stats.name = file;
|
|
||||||
nodeGroups.nodes.push(stats);
|
|
||||||
if (err) {
|
|
||||||
stats.error = err;
|
|
||||||
stats.type = 'error';
|
|
||||||
nodeGroups.errors.push(stats);
|
|
||||||
//emitter.emit('fileError', path, stats, noop);
|
|
||||||
return nextFile();
|
|
||||||
}
|
|
||||||
sortFnodesByType(path, stats, nodeGroups, nextFile);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
nextFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
function walk(path, next) {
|
|
||||||
fs.readdir(path, function (err, nodes) {
|
|
||||||
if (err) {
|
|
||||||
emitter.emit('directoryError', path, { error: err, name: path }, noop);
|
|
||||||
return next(); /*TODO*/ throw err;
|
|
||||||
}
|
|
||||||
getStats(path, nodes, function (dirs) {
|
|
||||||
walkDirs(path, dirs, next);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function walkDirs(path, dirs, cb) {
|
|
||||||
function nextDir() {
|
|
||||||
var dir = dirs.pop();
|
|
||||||
if (undefined === dir) {
|
|
||||||
delete dirs;
|
|
||||||
return cb();
|
|
||||||
}
|
|
||||||
walk(upath.join(path, dir), nextDir);
|
|
||||||
}
|
|
||||||
nextDir();
|
|
||||||
}
|
|
||||||
|
|
||||||
walk(upath.normalize(path), function () {
|
|
||||||
emitter.emit('end');
|
|
||||||
});
|
});
|
||||||
emitter.walk = newVersion;
|
|
||||||
emitter.whenever = newVersion;
|
me._wWalk();
|
||||||
return emitter;
|
|
||||||
}
|
}
|
||||||
module.exports = create;
|
|
||||||
module.exports.isFnodeTypes = isFnodeTypes;
|
// Inherits must come before prototype additions
|
||||||
module.exports.fnodeTypes = fnodeTypes;
|
util.inherits(Walker, EventEmitter);
|
||||||
module.exports.fnodeTypesPlural = fnodeTypesPlural;
|
|
||||||
|
Walker.prototype._wLstatHandler = function (err, stat) {
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
stat = stat || {};
|
||||||
|
stat.name = me._wcurfile;
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
stat.error = err;
|
||||||
|
//me.emit('error', curpath, stat);
|
||||||
|
// TODO v3.0 (don't noop the next if there are listeners)
|
||||||
|
me.emit('nodeError', me._wcurpath, stat, noop);
|
||||||
|
me._wfnodegroups.errors.push(stat);
|
||||||
|
me._wCurFileCallback();
|
||||||
|
} else {
|
||||||
|
TypeEmitter.sortFnodesByType(stat, me._wfnodegroups);
|
||||||
|
// NOTE: wCurFileCallback doesn't need thisness, so this is okay
|
||||||
|
TypeEmitter.emitNodeType(me, me._wcurpath, stat, me._wCurFileCallback, me);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Walker.prototype._wFilesHandler = function (cont, file) {
|
||||||
|
var statPath
|
||||||
|
, me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
me._wcurfile = file;
|
||||||
|
me._wCurFileCallback = cont;
|
||||||
|
me.emit('name', me._wcurpath, file, noop);
|
||||||
|
|
||||||
|
statPath = me._wcurpath + path.sep + file;
|
||||||
|
|
||||||
|
if (!me._wsync) {
|
||||||
|
// TODO how to remove this anony?
|
||||||
|
fs[me._wStat](statPath, function (err, stat) {
|
||||||
|
me._wLstatHandler(err, stat);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
me._wLstatHandler(null, fs[me._wStatSync](statPath));
|
||||||
|
} catch(e) {
|
||||||
|
me._wLstatHandler(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Walker.prototype._wOnEmitDone = function () {
|
||||||
|
var me = this
|
||||||
|
, dirs = []
|
||||||
|
;
|
||||||
|
|
||||||
|
me._wfnodegroups.directories.forEach(appendToDirs, dirs);
|
||||||
|
dirs.forEach(me._wJoinPath, me);
|
||||||
|
me._wqueue.push(me._wq = dirs);
|
||||||
|
me._wNext();
|
||||||
|
};
|
||||||
|
Walker.prototype._wPostFilesHandler = function () {
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
if (me._wfnodegroups.errors.length) {
|
||||||
|
// TODO v3.0 (don't noop the next)
|
||||||
|
// .errors is an array of stats with { name: name, error: error }
|
||||||
|
me.emit('errors', me._wcurpath, me._wfnodegroups.errors, noop);
|
||||||
|
}
|
||||||
|
// XXX emitNodeTypes still needs refactor
|
||||||
|
TypeEmitter.emitNodeTypeGroups(me, me._wcurpath, me._wfnodegroups, me._wOnEmitDone, me);
|
||||||
|
};
|
||||||
|
Walker.prototype._wReadFiles = function () {
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
if (!me._wcurfiles || 0 === me._wcurfiles.length) {
|
||||||
|
return me._wNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO could allow user to selectively stat
|
||||||
|
// and don't stat if there are no stat listeners
|
||||||
|
me.emit('names', me._wcurpath, me._wcurfiles, noop);
|
||||||
|
|
||||||
|
if (me._wsync) {
|
||||||
|
me._wcurfiles.forEach(wFilesHandlerWrapper, me);
|
||||||
|
me._wPostFilesHandler();
|
||||||
|
} else {
|
||||||
|
forEachAsync(me._wcurfiles, me._wFilesHandler, me).then(me._wPostFilesHandler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Walker.prototype._wReaddirHandler = function (err, files) {
|
||||||
|
var fnodeGroups = TypeEmitter.createNodeGroups()
|
||||||
|
, me = this
|
||||||
|
, parent
|
||||||
|
, child
|
||||||
|
;
|
||||||
|
|
||||||
|
me._wfnodegroups = fnodeGroups;
|
||||||
|
me._wcurfiles = files;
|
||||||
|
|
||||||
|
// no error, great
|
||||||
|
if (!err) {
|
||||||
|
me._wReadFiles();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO path.sep
|
||||||
|
me._wcurpath = me._wcurpath.replace(/\/$/, '');
|
||||||
|
|
||||||
|
// error? not first run? => directory error
|
||||||
|
if (!me._wfirstrun) {
|
||||||
|
// TODO v3.0 (don't noop the next if there are listeners)
|
||||||
|
me.emit('directoryError', me._wcurpath, { error: err }, noop);
|
||||||
|
// TODO v3.0
|
||||||
|
//me.emit('directoryError', me._wcurpath.replace(/^(.*)\/.*$/, '$1'), { name: me._wcurpath.replace(/^.*\/(.*)/, '$1'), error: err }, noop);
|
||||||
|
me._wReadFiles();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// error? first run? => maybe a file, maybe a true error
|
||||||
|
me._wfirstrun = false;
|
||||||
|
|
||||||
|
// readdir failed (might be a file), try a stat on the parent
|
||||||
|
parent = me._wcurpath.replace(/^(.*)\/.*$/, '$1');
|
||||||
|
fs[me._wStat](parent, function (e, stat) {
|
||||||
|
|
||||||
|
if (stat) {
|
||||||
|
// success
|
||||||
|
// now try stat on this as a child of the parent directory
|
||||||
|
child = me._wcurpath.replace(/^.*\/(.*)$/, '$1');
|
||||||
|
me._wcurfiles = [child];
|
||||||
|
me._wcurpath = parent;
|
||||||
|
} else {
|
||||||
|
// TODO v3.0
|
||||||
|
//me.emit('directoryError', me._wcurpath.replace(/^(.*)\/.*$/, '$1'), { name: me._wcurpath.replace(/^.*\/(.*)/, '$1'), error: err }, noop);
|
||||||
|
// TODO v3.0 (don't noop the next)
|
||||||
|
// the original readdir error, not the parent stat error
|
||||||
|
me.emit('nodeError', me._wcurpath, { error: err }, noop);
|
||||||
|
}
|
||||||
|
|
||||||
|
me._wReadFiles();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Walker.prototype._wFilter = function () {
|
||||||
|
var me = this
|
||||||
|
, exclude
|
||||||
|
;
|
||||||
|
|
||||||
|
// Stop directories that contain filter keywords
|
||||||
|
// from continuing through the walk process
|
||||||
|
exclude = me._wfilters.some(function (filter) {
|
||||||
|
if (me._wcurpath.match(filter)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return exclude;
|
||||||
|
};
|
||||||
|
Walker.prototype._wWalkSync = function () {
|
||||||
|
//console.log('walkSync');
|
||||||
|
var err
|
||||||
|
, files
|
||||||
|
, me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
try {
|
||||||
|
files = fs.readdirSync(me._wcurpath);
|
||||||
|
} catch(e) {
|
||||||
|
err = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
me._wReaddirHandler(err, files);
|
||||||
|
};
|
||||||
|
Walker.prototype._wWalkAsync = function () {
|
||||||
|
//console.log('walkAsync');
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
// TODO how to remove this anony?
|
||||||
|
fs.readdir(me._wcurpath, function (err, files) {
|
||||||
|
me._wReaddirHandler(err, files);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
Walker.prototype._wNext = function () {
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
if (me._paused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (me._wq.length) {
|
||||||
|
me._wcurpath = me._wq.pop();
|
||||||
|
while (me._wq.length && me._wFilter()) {
|
||||||
|
me._wcurpath = me._wq.pop();
|
||||||
|
}
|
||||||
|
if (me._wcurpath && !me._wFilter()) {
|
||||||
|
me._wWalk();
|
||||||
|
} else {
|
||||||
|
me._wNext();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
me._wqueue.length -= 1;
|
||||||
|
if (me._wqueue.length) {
|
||||||
|
me._wq = me._wqueue[me._wqueue.length - 1];
|
||||||
|
return me._wNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
// To not break compatibility
|
||||||
|
//process.nextTick(function () {
|
||||||
|
me.emit('end');
|
||||||
|
//});
|
||||||
|
};
|
||||||
|
Walker.prototype._wJoinPath = function (v, i, o) {
|
||||||
|
var me = this
|
||||||
|
;
|
||||||
|
|
||||||
|
o[i] = [me._wcurpath, path.sep, v].join('');
|
||||||
|
};
|
||||||
|
Walker.prototype.pause = function () {
|
||||||
|
this._paused = true;
|
||||||
|
};
|
||||||
|
Walker.prototype.resume = function () {
|
||||||
|
this._paused = false;
|
||||||
|
this._wNext();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.walk = function (path, opts) {
|
||||||
|
return new Walker(path, opts, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.walkSync = function (path, opts) {
|
||||||
|
return new Walker(path, opts, true);
|
||||||
|
};
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var walk = require('../lib/walk-jqueue-2'),
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
function sort(a,b) {
|
||||||
|
a= a.toLowerCase();
|
||||||
|
b= b.toLowerCase();
|
||||||
|
if (a > b) return -1;
|
||||||
|
if (a < b) return 1;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.argv.forEach(function(val, index, array) {
|
||||||
|
if (index > 1) {
|
||||||
|
emitter = walk(val);
|
||||||
|
emitter.on('name', function (path, file, stat) {
|
||||||
|
count += 1;
|
||||||
|
console.log( ["[", count, "] ", path, '/', file].join('') )
|
||||||
|
});
|
||||||
|
emitter.on('names', function (path, files, stats) {
|
||||||
|
files.sort(sort);
|
||||||
|
//console.log('sort: ' + files.join(' ; '));
|
||||||
|
});
|
||||||
|
emitter.on('error', function () {
|
||||||
|
// ignore
|
||||||
|
});
|
||||||
|
emitter.on('stat', function (path, file, stat) {
|
||||||
|
//console.log('stat: ' + file);
|
||||||
|
});
|
||||||
|
emitter.on('stats', function (path, files, stats) {
|
||||||
|
//console.log('stats: ' + files.join(' ; '));
|
||||||
|
});
|
||||||
|
emitter.on('end', function () {
|
||||||
|
console.log("The eagle has landed.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}());
|
|
@ -0,0 +1,123 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var walk = require('../lib/walk-jqueue-3'),
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
function sort(a,b) {
|
||||||
|
a= a.toLowerCase();
|
||||||
|
b= b.toLowerCase();
|
||||||
|
if (a > b) return -1;
|
||||||
|
if (a < b) return 1;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.argv.forEach(function(startpath, index) {
|
||||||
|
if (index > 1) {
|
||||||
|
emitter = walk(startpath);
|
||||||
|
|
||||||
|
// Non-`stat`ed Nodes
|
||||||
|
/*
|
||||||
|
emitter.on('name', function (path, file, stat) {
|
||||||
|
count += 1;
|
||||||
|
//console.log( ["[", count, "] ", path, '/', file].join('') )
|
||||||
|
console.log( [path, '/', file].join('') )
|
||||||
|
});
|
||||||
|
emitter.on('names', function (path, files, stats) {
|
||||||
|
files.sort(sort);
|
||||||
|
//console.log('sort: ' + files.join(' ; '));
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Single `stat`ed Nodes
|
||||||
|
emitter.on('error', function (path, err, next) {
|
||||||
|
// ignore
|
||||||
|
});
|
||||||
|
emitter.on('directoryError', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('node', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
//console.log( ["[", count, "] ", path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
emitter.on('file', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
//console.log( ["[", count, "] ", path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('directory', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('symbolicLink', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
emitter.on('blockDevice', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('characterDevice', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('FIFO', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('socket', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Grouped `stat`ed Nodes
|
||||||
|
emitter.on('errors', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('nodes', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
emitter.on('files', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('directories', function (path, stats, next) {
|
||||||
|
//delete stats[1];
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('symbolicLinks', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('blockDevices', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('characterDevices', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('FIFOs', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('sockets', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The end of all things
|
||||||
|
emitter.on('end', function () {
|
||||||
|
console.log("The eagle has landed.");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}());
|
|
@ -0,0 +1,33 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
(function () {
|
||||||
|
var walk = require('../lib/walk-queue'),
|
||||||
|
emitter = walk(process.argv[2] || '.'),
|
||||||
|
_ = require('underscore'),
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
emitter.on('error', function (err, path, files) {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
emitter.on('nodes', function (path, files, next) {
|
||||||
|
//next();
|
||||||
|
var filenames = _.map(files, function (file) {
|
||||||
|
return path + '/' + file;
|
||||||
|
})
|
||||||
|
filenames.forEach(function (name) {
|
||||||
|
count += 1;
|
||||||
|
console.log('[' + count + '] ' + name)
|
||||||
|
});
|
||||||
|
//filenames.forEach(console.log);
|
||||||
|
//console.log(_.pluck(files, 'name'));
|
||||||
|
});
|
||||||
|
emitter.on('directories', function (path, files, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('directory', function (path, stat, next) {
|
||||||
|
//console.log(stat.name);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('end', function () {
|
||||||
|
console.log("The eagle has landed");
|
||||||
|
});
|
||||||
|
}());
|
|
@ -30,7 +30,8 @@ function walk (file, cb) {
|
||||||
|
|
||||||
function getDirectory (cb) {
|
function getDirectory (cb) {
|
||||||
fs.readdir(file, function(err, files) {
|
fs.readdir(file, function(err, files) {
|
||||||
if (!files) return;
|
// XXX bug was here. `next()` added by coolaj86
|
||||||
|
if (!files) return next();
|
||||||
//if (err) throw Error(err);
|
//if (err) throw Error(err);
|
||||||
files.sort(sort);
|
files.sort(sort);
|
||||||
files.forEach(fullPath);
|
files.forEach(fullPath);
|
|
@ -0,0 +1 @@
|
||||||
|
walk-jorge.js
|
|
@ -0,0 +1,75 @@
|
||||||
|
(function () {
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
var fs = require('fs'),
|
||||||
|
EventEmitter = require('events').EventEmitter;
|
||||||
|
|
||||||
|
// 2010-11-25 jorge@jorgechamorro.com
|
||||||
|
function create (pathname, cb) {
|
||||||
|
var emitter = new EventEmitter(),
|
||||||
|
q = [],
|
||||||
|
queue = [q],
|
||||||
|
curpath;
|
||||||
|
|
||||||
|
function walk() {
|
||||||
|
//cb(curpath);
|
||||||
|
fs.lstat(curpath, function (err, stat) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('error', curpath, err);
|
||||||
|
}
|
||||||
|
if (!stat) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
emitter.emit('node', curpath, stat);
|
||||||
|
if (!stat.isDirectory()) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
fs.readdir(curpath, function(err, files) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('error', curpath, err);
|
||||||
|
}
|
||||||
|
// XXX bug was here. next() was omitted
|
||||||
|
if (!files || 0 == files.length) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
files.sort(sort);
|
||||||
|
emitter.emit('nodes', curpath, files);
|
||||||
|
files.forEach(fullPath);
|
||||||
|
queue.push(q = files);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function next () {
|
||||||
|
if (q.length) {
|
||||||
|
curpath = q.pop();
|
||||||
|
return walk();
|
||||||
|
}
|
||||||
|
if (queue.length -= 1) {
|
||||||
|
q = queue[queue.length-1];
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
emitter.emit('end');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullPath(v,i,o) {
|
||||||
|
o[i]= [curpath, '/', v].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function sort(a,b) {
|
||||||
|
a= a.toLowerCase();
|
||||||
|
b= b.toLowerCase();
|
||||||
|
if (a > b) return -1;
|
||||||
|
if (a < b) return 1;
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
curpath = pathname;
|
||||||
|
walk();
|
||||||
|
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = create;
|
||||||
|
}());
|
|
@ -0,0 +1,79 @@
|
||||||
|
(function () {
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
// Array.prototype.forEachAsync(next, item, i, collection)
|
||||||
|
require('futures/forEachAsync');
|
||||||
|
|
||||||
|
var fs = require('fs'),
|
||||||
|
EventEmitter = require('events').EventEmitter;
|
||||||
|
|
||||||
|
// 2010-11-25 jorge@jorgechamorro.com
|
||||||
|
function create(pathname, cb) {
|
||||||
|
var emitter = new EventEmitter(),
|
||||||
|
q = [],
|
||||||
|
queue = [q],
|
||||||
|
curpath;
|
||||||
|
|
||||||
|
function walk() {
|
||||||
|
fs.readdir(curpath, function(err, files) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('error', curpath, err);
|
||||||
|
}
|
||||||
|
// XXX bug was here. next() was omitted
|
||||||
|
if (!files || 0 == files.length) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
var stats = [];
|
||||||
|
emitter.emit('names', curpath, files, stats);
|
||||||
|
files.forEachAsync(function (cont, file) {
|
||||||
|
emitter.emit('name', curpath, file);
|
||||||
|
fs.lstat(curpath + '/' + file, function (err, stat) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('error', curpath, err);
|
||||||
|
}
|
||||||
|
if (stat) {
|
||||||
|
stat.name = file;
|
||||||
|
stats.push(stat);
|
||||||
|
emitter.emit('stat', curpath, file, stat);
|
||||||
|
}
|
||||||
|
cont();
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
var dirs = []
|
||||||
|
emitter.emit('stats', curpath, files, stats);
|
||||||
|
stats.forEach(function (stat) {
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
dirs.push(stat.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
dirs.forEach(fullPath);
|
||||||
|
queue.push(q = dirs);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
if (q.length) {
|
||||||
|
curpath = q.pop();
|
||||||
|
return walk();
|
||||||
|
}
|
||||||
|
if (queue.length -= 1) {
|
||||||
|
q = queue[queue.length-1];
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
emitter.emit('end');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullPath(v,i,o) {
|
||||||
|
o[i]= [curpath, '/', v].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
curpath = pathname;
|
||||||
|
walk();
|
||||||
|
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = create;
|
||||||
|
}());
|
|
@ -0,0 +1,86 @@
|
||||||
|
(function () {
|
||||||
|
"use strict"
|
||||||
|
|
||||||
|
// Array.prototype.forEachAsync(next, item, i, collection)
|
||||||
|
require('futures/forEachAsync');
|
||||||
|
|
||||||
|
var fs = require('fs'),
|
||||||
|
EventEmitter = require('events').EventEmitter,
|
||||||
|
TypeEmitter = require('./node-type-emitter');
|
||||||
|
|
||||||
|
// 2010-11-25 jorge@jorgechamorro.com
|
||||||
|
function create(pathname, cb) {
|
||||||
|
var emitter = new EventEmitter(),
|
||||||
|
q = [],
|
||||||
|
queue = [q],
|
||||||
|
curpath;
|
||||||
|
|
||||||
|
function walk() {
|
||||||
|
fs.readdir(curpath, function(err, files) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('error', curpath, err);
|
||||||
|
}
|
||||||
|
// XXX bug was here. next() was omitted
|
||||||
|
if (!files || 0 == files.length) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
var stats = [],
|
||||||
|
fnodeGroups = TypeEmitter.createNodeGroups();
|
||||||
|
|
||||||
|
// TODO could allow user to selectively stat
|
||||||
|
// and don't stat if there are no stat listeners
|
||||||
|
emitter.emit('names', curpath, files);
|
||||||
|
files.forEachAsync(function (cont, file) {
|
||||||
|
emitter.emit('name', curpath, file);
|
||||||
|
fs.lstat(curpath + '/' + file, function (err, stat) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('error', curpath, err);
|
||||||
|
}
|
||||||
|
if (!stat) {
|
||||||
|
cont();
|
||||||
|
}
|
||||||
|
stat.name = file;
|
||||||
|
stats.push(stat);
|
||||||
|
//emitter.emit('stat', curpath, file, stat);
|
||||||
|
TypeEmitter.sortFnodesByType(stat, fnodeGroups);
|
||||||
|
TypeEmitter.emitNodeType(emitter, curpath, stat, cont);
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
var dirs = []
|
||||||
|
//emitter.emit('stats', curpath, files, stats);
|
||||||
|
TypeEmitter.emitNodeTypeGroups(emitter, curpath, fnodeGroups, function () {
|
||||||
|
fnodeGroups.directories.forEach(function (stat) {
|
||||||
|
dirs.push(stat.name);
|
||||||
|
});
|
||||||
|
dirs.forEach(fullPath);
|
||||||
|
queue.push(q = dirs);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
if (q.length) {
|
||||||
|
curpath = q.pop();
|
||||||
|
return walk();
|
||||||
|
}
|
||||||
|
if (queue.length -= 1) {
|
||||||
|
q = queue[queue.length-1];
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
emitter.emit('end');
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullPath(v,i,o) {
|
||||||
|
o[i]= [curpath, '/', v].join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
curpath = pathname;
|
||||||
|
walk();
|
||||||
|
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = create;
|
||||||
|
}());
|
|
@ -0,0 +1,165 @@
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var fs = require('fs'),
|
||||||
|
fstat = fs.lstat,
|
||||||
|
Futures = require('futures'),
|
||||||
|
EventEmitter = require('events').EventEmitter,
|
||||||
|
upath = require('path'),
|
||||||
|
// "FIFO" isn't easy to convert to camelCase and back reliably
|
||||||
|
isFnodeTypes = [
|
||||||
|
"isFile", "isDirectory", "isBlockDevice", "isCharacterDevice", "isSymbolicLink", "isFIFO", "isSocket"
|
||||||
|
],
|
||||||
|
fnodeTypes = [
|
||||||
|
"file", "directory", "blockDevice", "characterDevice", "symbolicLink", "FIFO", "socket"
|
||||||
|
],
|
||||||
|
fnodeTypesPlural = [
|
||||||
|
"files", "directories", "blockDevices", "characterDevices", "symbolicLinks", "FIFOs", "sockets"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Get the current number of listeners (which may change)
|
||||||
|
// Emit events to each listener
|
||||||
|
// Wait for all listeners to `next()` before continueing
|
||||||
|
// (in theory this may avoid disk thrashing)
|
||||||
|
function emitSingleEvents(emitter, path, stats, next) {
|
||||||
|
var num = 1 + emitter.listeners(stats.type).length + emitter.listeners("node").length;
|
||||||
|
|
||||||
|
function nextWhenReady() {
|
||||||
|
num -= 1;
|
||||||
|
if (0 === num) { next(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
emitter.emit(stats.type, path, stats, nextWhenReady);
|
||||||
|
emitter.emit("node", path, stats, nextWhenReady);
|
||||||
|
nextWhenReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Since the risk for disk thrashing among anything
|
||||||
|
// other than files is relatively low, all types are
|
||||||
|
// emitted at once, but all must complete before advancing
|
||||||
|
function emitPluralEvents(emitter, path, nodes, next) {
|
||||||
|
var num = 1;
|
||||||
|
|
||||||
|
function nextWhenReady() {
|
||||||
|
num -= 1;
|
||||||
|
if (0 === num) { next(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fnodeTypesPlural.concat(["nodes", "errors"]).forEach(function (fnodeType) {
|
||||||
|
if (0 === nodes[fnodeType].length) { return; }
|
||||||
|
num += emitter.listeners(fnodeType).length;
|
||||||
|
emitter.emit(fnodeType, path, nodes[fnodeType], nextWhenReady);
|
||||||
|
});
|
||||||
|
nextWhenReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Determine each file node's type
|
||||||
|
//
|
||||||
|
function sortFnodesByType(stats, fnodes) {
|
||||||
|
isFnodeTypes.forEach(function (isType, i) {
|
||||||
|
if (stats[isType]()) {
|
||||||
|
if (stats.type) { throw new Error("is_" + type + " and " + isType); }
|
||||||
|
stats.type = fnodeTypes[i];
|
||||||
|
fnodes[fnodeTypesPlural[i]].push(stats);
|
||||||
|
//console.log(isType, fnodeTypesPlural[i], stats.name);
|
||||||
|
// TODO throw to break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
// Won't really ever happen
|
||||||
|
if (!stats.type) {
|
||||||
|
stats.error = new Error(upath.join(path, stats.name) + ' isAnUndefinedType');
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
function create(path) {
|
||||||
|
var emitter = new EventEmitter(),
|
||||||
|
paths = [],
|
||||||
|
path;
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
// path could be local if dirHandler were anonymous
|
||||||
|
//console.log('LEN: '+ paths.length);
|
||||||
|
if (0 == paths.length) {
|
||||||
|
emitter.emit('end');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
path = paths.pop();
|
||||||
|
//console.log("POP: " + path);
|
||||||
|
fs.readdir(path, dirHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodesHandler(nodes, args) {
|
||||||
|
//console.log('USE: ' + path);
|
||||||
|
var statses = [];
|
||||||
|
|
||||||
|
var nodeGroups = {};
|
||||||
|
fnodeTypesPlural.concat("nodes", "errors").forEach(function (fnodeTypePlural) {
|
||||||
|
nodeGroups[fnodeTypePlural] = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
args.forEach(function (arg, i) {
|
||||||
|
var file = nodes[i],
|
||||||
|
err = arg[0],
|
||||||
|
stats = arg[1];
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
stats = { error: err, name: file };
|
||||||
|
emitter.emit('error', err, path, stats);
|
||||||
|
}
|
||||||
|
if (stats) {
|
||||||
|
stats.name = file;
|
||||||
|
sortFnodesByType(stats, nodeGroups);
|
||||||
|
emitter.emit('stat', path, stats);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
emitter.emit('stats', path, statses);
|
||||||
|
nodeGroups['directories'].forEach(function (stat) {
|
||||||
|
paths.push(path + '/' + stat.name);
|
||||||
|
//console.log('PUSH: ' + path + '/' + stat.name);
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
//console.log('USE: ' + path);
|
||||||
|
next();
|
||||||
|
*/
|
||||||
|
emitPluralEvents(emitter, path, nodeGroups, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dirHandler(err, nodes) {
|
||||||
|
//console.log("HANDLE: " + path);
|
||||||
|
var join = Futures.join(),
|
||||||
|
i;
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('error', err, path);
|
||||||
|
}
|
||||||
|
if (!nodes || 0 == nodes.length) {
|
||||||
|
//console.log('EMPTY: ' + path);
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
// TODO don't duplicate efforts
|
||||||
|
emitter.emit('nodes', path, nodes);
|
||||||
|
|
||||||
|
for (i = 0; i < nodes.length; i += 1) {
|
||||||
|
fstat(path + '/' + nodes[i], join.deliverer());
|
||||||
|
}
|
||||||
|
|
||||||
|
join.when(function () {
|
||||||
|
var args = Array.prototype.slice.call(arguments);
|
||||||
|
nodesHandler(nodes, args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//paths.push([path]);
|
||||||
|
paths.push(path);
|
||||||
|
|
||||||
|
|
||||||
|
next();
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = create;
|
||||||
|
}());
|
|
@ -0,0 +1,165 @@
|
||||||
|
// TODO
|
||||||
|
// * add types by listener dynamically
|
||||||
|
// * unroll loops for better readability?
|
||||||
|
// * should emitted errors wait for `next()`?
|
||||||
|
(function (undefined) {
|
||||||
|
var fs = require('fs'),
|
||||||
|
upath = require('path'),
|
||||||
|
util = require('util'),
|
||||||
|
Futures = require('futures'),
|
||||||
|
events = require('events'),
|
||||||
|
noop = function () {},
|
||||||
|
// "FIFO" isn't easy to convert to camelCame and back reliably
|
||||||
|
isFnodeTypes = [
|
||||||
|
"isFile", "isDirectory", "isBlockDevice", "isCharacterDevice", "isSymbolicLink", "isFIFO", "isSocket"
|
||||||
|
],
|
||||||
|
fnodeTypes = [
|
||||||
|
"file", "directory", "blockDevice", "characterDevice", "symbolicLink", "FIFO", "socket"
|
||||||
|
],
|
||||||
|
fnodeTypesPlural = [
|
||||||
|
"files", "directories", "blockDevices", "characterDevices", "symbolicLinks", "FIFOs", "sockets"
|
||||||
|
];
|
||||||
|
|
||||||
|
function newVersion() {
|
||||||
|
throw new Error("New Version. Please see API on github.com/coolaj86/node-walk");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new walk instance
|
||||||
|
function create(path, options, cb) {
|
||||||
|
if (cb) {
|
||||||
|
newVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
var emitter = new events.EventEmitter(),
|
||||||
|
fstat = (options||{}).followLinks ? fs.stat : fs.lstat;
|
||||||
|
|
||||||
|
|
||||||
|
// Get the current number of listeners (which may change)
|
||||||
|
// Emit events to each listener
|
||||||
|
// Wait for all listeners to `next()` before continueing
|
||||||
|
// (in theory this may avoid disk thrashing)
|
||||||
|
function emitSingleEvents(path, stats, next) {
|
||||||
|
var num = 1 + emitter.listeners(stats.type).length + emitter.listeners("node").length;
|
||||||
|
|
||||||
|
function nextWhenReady() {
|
||||||
|
num -= 1;
|
||||||
|
if (0 === num) { next(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
emitter.emit(stats.type, path, stats, nextWhenReady);
|
||||||
|
emitter.emit("node", path, stats, nextWhenReady);
|
||||||
|
nextWhenReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Since the risk for disk thrashing among anything
|
||||||
|
// other than files is relatively low, all types are
|
||||||
|
// emitted at once, but all must complete before advancing
|
||||||
|
function emitPluralEvents(path, nodes, next) {
|
||||||
|
var num = 1;
|
||||||
|
|
||||||
|
function nextWhenReady() {
|
||||||
|
num -= 1;
|
||||||
|
if (0 === num) { next(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fnodeTypesPlural.concat(["nodes", "errors"]).forEach(function (fnodeType) {
|
||||||
|
if (0 === nodes[fnodeType].length) { return; }
|
||||||
|
num += emitter.listeners(fnodeType).length;
|
||||||
|
emitter.emit(fnodeType, path, nodes[fnodeType], nextWhenReady);
|
||||||
|
});
|
||||||
|
nextWhenReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Determine each file node's type
|
||||||
|
//
|
||||||
|
function sortFnodesByType(path, stats, fnodes, nextFile) {
|
||||||
|
isFnodeTypes.forEach(function (isType, i) {
|
||||||
|
if (stats[isType]()) {
|
||||||
|
if (stats.type) { throw new Error("is_" + type + " and " + isType); }
|
||||||
|
stats.type = fnodeTypes[i];
|
||||||
|
fnodes[fnodeTypesPlural[i]].push(stats);
|
||||||
|
// TODO throw to break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!stats.type) { throw new Error(upath.join(path, stats.name) + ' isAnUndefinedType'); }
|
||||||
|
emitSingleEvents(path, stats, nextFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Asynchronously get the stats
|
||||||
|
//
|
||||||
|
function getStats(path, files, walkDirs) {
|
||||||
|
var nodeGroups = {};
|
||||||
|
|
||||||
|
fnodeTypesPlural.concat("nodes", "errors").forEach(function (fnodeTypePlural) {
|
||||||
|
nodeGroups[fnodeTypePlural] = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
function nextFile() {
|
||||||
|
var file = files.pop(), dirs = [], fnames = [];
|
||||||
|
|
||||||
|
if (undefined === file) {
|
||||||
|
emitPluralEvents(path, nodeGroups, function () {
|
||||||
|
nodeGroups.directories.forEach(function (dir) {
|
||||||
|
dirs.push(dir.name);
|
||||||
|
});
|
||||||
|
walkDirs(dirs);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fstat(upath.join(path, file), function (err, stats) {
|
||||||
|
stats = stats || {};
|
||||||
|
stats.name = file;
|
||||||
|
nodeGroups.nodes.push(stats);
|
||||||
|
if (err) {
|
||||||
|
stats.error = err;
|
||||||
|
stats.type = 'error';
|
||||||
|
nodeGroups.errors.push(stats);
|
||||||
|
//emitter.emit('fileError', path, stats, noop);
|
||||||
|
return nextFile();
|
||||||
|
}
|
||||||
|
sortFnodesByType(path, stats, nodeGroups, nextFile);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
nextFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
function walk(path, next) {
|
||||||
|
fs.readdir(path, function (err, nodes) {
|
||||||
|
if (err) {
|
||||||
|
emitter.emit('directoryError', path, { error: err, name: path }, noop);
|
||||||
|
return next(); /*TODO*/ throw err;
|
||||||
|
}
|
||||||
|
getStats(path, nodes, function (dirs) {
|
||||||
|
walkDirs(path, dirs, next);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function walkDirs(path, dirs, cb) {
|
||||||
|
function nextDir() {
|
||||||
|
var dir = dirs.pop();
|
||||||
|
if (undefined === dir) {
|
||||||
|
delete dirs;
|
||||||
|
return cb();
|
||||||
|
}
|
||||||
|
walk(upath.join(path, dir), nextDir);
|
||||||
|
}
|
||||||
|
nextDir();
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(upath.normalize(path), function () {
|
||||||
|
emitter.emit('end');
|
||||||
|
});
|
||||||
|
emitter.walk = newVersion;
|
||||||
|
emitter.whenever = newVersion;
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
module.exports = create;
|
||||||
|
module.exports.isFnodeTypes = isFnodeTypes;
|
||||||
|
module.exports.fnodeTypes = fnodeTypes;
|
||||||
|
module.exports.fnodeTypesPlural = fnodeTypesPlural;
|
||||||
|
}());
|
48
package.json
48
package.json
|
@ -1,12 +1,40 @@
|
||||||
{
|
{
|
||||||
"name" : "walk",
|
"name": "walk",
|
||||||
"description" : "A node port of python's os.walk",
|
"description": "A node port of python's os.walk",
|
||||||
"url" : "github.com/coolaj86/node-walk",
|
"url": "http://git.coolaj86.com/coolaj86/fs-walk.js",
|
||||||
"keywords" : ["util", "os", "sys", "fs", "walk", "walkSync"],
|
"keywords": [
|
||||||
"author" : "AJ ONeal <coolaj86@gmail.com>",
|
"util",
|
||||||
"contributors" : [],
|
"os",
|
||||||
"dependencies" : [],
|
"sys",
|
||||||
"lib" : "lib",
|
"fs",
|
||||||
"main" : "./lib/walk.js",
|
"walk",
|
||||||
"version" : "1.0.0"
|
"walkSync"
|
||||||
|
],
|
||||||
|
"author": "AJ ONeal <coolaj86@gmail.com>",
|
||||||
|
"contributors": [],
|
||||||
|
"dependencies": {
|
||||||
|
"foreachasync": "^3.0.0"
|
||||||
|
},
|
||||||
|
"lib": ".",
|
||||||
|
"main": "./lib/walk.js",
|
||||||
|
"version": "2.3.15",
|
||||||
|
"files": [
|
||||||
|
"lib"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"url": "https://git.coolaj86.com/coolaj86/fs-walk.js.git"
|
||||||
|
},
|
||||||
|
"license": "(MIT OR Apache-2.0)",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://git.coolaj86.com/coolaj86/fs-walk.js/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://git.coolaj86.com/coolaj86/fs-walk.js",
|
||||||
|
"directories": {
|
||||||
|
"example": "examples",
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"devDependencies": {},
|
||||||
|
"scripts": {
|
||||||
|
"test": "./test/walk-test.sh"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var walk = require('../lib/walk'),
|
||||||
|
count = 0,
|
||||||
|
saneCount = 0;
|
||||||
|
|
||||||
|
function sort(a,b) {
|
||||||
|
a= a.toLowerCase();
|
||||||
|
b= b.toLowerCase();
|
||||||
|
if (a > b) return -1;
|
||||||
|
if (a < b) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.argv.forEach(function(startpath, index) {
|
||||||
|
if (index > 1) {
|
||||||
|
emitter = walk(startpath);
|
||||||
|
|
||||||
|
// Non-`stat`ed Nodes
|
||||||
|
emitter.on('name', function (path, file, stat) {
|
||||||
|
saneCount += 1;
|
||||||
|
//console.log( ["[", count, "] ", path, '/', file].join('') )
|
||||||
|
//console.log( [path, '/', file].join('') )
|
||||||
|
});
|
||||||
|
emitter.on('names', function (path, files, stats) {
|
||||||
|
files.sort(sort);
|
||||||
|
//console.log('sort: ' + files.join(' ; '));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Single `stat`ed Nodes
|
||||||
|
emitter.on('error', function (path, err, next) {
|
||||||
|
next()
|
||||||
|
// ignore
|
||||||
|
});
|
||||||
|
emitter.on('directoryError', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('nodeError', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('node', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
//console.log( ["[", count, "] ", path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
emitter.on('file', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
//console.log( ["[", count, "] ", path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('directory', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('symbolicLink', function (path, stat, next) {
|
||||||
|
count += 1;
|
||||||
|
console.log( [path, '/', stat.name].join('') )
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('blockDevice', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('characterDevice', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('FIFO', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('socket', function (path, stat, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Grouped `stat`ed Nodes
|
||||||
|
emitter.on('errors', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('nodes', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
emitter.on('files', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('directories', function (path, stats, next) {
|
||||||
|
//delete stats[1];
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('symbolicLinks', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
emitter.on('blockDevices', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('characterDevices', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('FIFOs', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
emitter.on('sockets', function (path, stats, next) {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// The end of all things
|
||||||
|
emitter.on('end', function () {
|
||||||
|
console.log("The eagle has landed. [" + count + " == " + saneCount + "]");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}());
|
|
@ -0,0 +1,34 @@
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var walk = require('../lib/walk').walk
|
||||||
|
, path = require('path')
|
||||||
|
, dirname = process.argv[2] || './'
|
||||||
|
, walker
|
||||||
|
;
|
||||||
|
|
||||||
|
walker = walk(dirname);
|
||||||
|
|
||||||
|
walker.on('directories', function (root, stats, next) {
|
||||||
|
stats.forEach(function (stat) {
|
||||||
|
console.log('[ds]', path.join(root, stat.name));
|
||||||
|
});
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
walker.on('directory', function (root, stat, next) {
|
||||||
|
console.log('[d]', path.join(root, stat.name));
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
walker.on('file', function (root, stat, next) {
|
||||||
|
console.log('[f]', path.join(root, stat.name));
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
walker.on('end', function () {
|
||||||
|
console.log('All Done!');
|
||||||
|
});
|
||||||
|
}());
|
|
@ -0,0 +1,43 @@
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var walk = require('../lib/walk')
|
||||||
|
, fs = require('fs')
|
||||||
|
, options
|
||||||
|
, walker
|
||||||
|
;
|
||||||
|
|
||||||
|
options = {
|
||||||
|
listeners: {
|
||||||
|
names: function (root, nodeNamesArray) {
|
||||||
|
nodeNamesArray.sort(function (a, b) {
|
||||||
|
if (a > b) return 1;
|
||||||
|
if (a < b) return -1;
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
, directories: function (root, dirStatsArray, next) {
|
||||||
|
// dirStatsArray is an array of `stat` objects with the additional attributes
|
||||||
|
// * type
|
||||||
|
// * error
|
||||||
|
// * name
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
, file: function (root, fileStats, next) {
|
||||||
|
fs.readFile(fileStats.name, function () {
|
||||||
|
// doStuff
|
||||||
|
console.log(root, fileStats.name);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
, errors: function (root, nodeStatsArray, next) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
walker = walk.walkSync("/tmp", options);
|
||||||
|
|
||||||
|
console.log("all done");
|
||||||
|
}());
|
|
@ -0,0 +1,6 @@
|
||||||
|
mkdir -p walk-test/dir1
|
||||||
|
touch walk-test/file-0
|
||||||
|
touch walk-test/file-1
|
||||||
|
touch walk-test/dir1/file-2
|
||||||
|
touch walk-test/dir1/file-3
|
||||||
|
echo "4 files and 2 directories (including '.')"
|
|
@ -1,5 +0,0 @@
|
||||||
mkdir -p walk-test/dir1
|
|
||||||
touch walk-test/file-1
|
|
||||||
touch walk-test/file-2
|
|
||||||
touch walk-test/dir1/file-1
|
|
||||||
touch walk-test/dir1/file-2
|
|
Loading…
Reference in New Issue