├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── package.json └── test ├── fixtures └── a │ └── b │ └── c │ └── d │ └── hello └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Conference Compass B.V. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | fs-walk 2 | ======= 3 | 4 | Synchronous and asynchronous recursive directory walking for node. Uses callbacks to mimic the API of the `fs` module. 5 | 6 | ## Usage 7 | 8 | To asynchronously walk the `/etc` directory, and for example, change the file and directory permissions, do: 9 | 10 | ```js 11 | var walk = require('fs-walk'); 12 | 13 | walk.walk('/etc', function(basedir, filename, stat, next) { 14 | var perm = stat.isDirectory() ? 0755 : 0644; 15 | fs.chmod(path.join(basedir, filename), perm, next); 16 | }, function(err) { 17 | if (err) console.log(err); 18 | }); 19 | ``` 20 | 21 | You can achieve the same result, synchronously: 22 | 23 | ```js 24 | var walk = require('fs-walk'); 25 | 26 | walk.walkSync('/etc', function(basedir, filename, stat) { 27 | var perm = stat.isDirectory() ? 0755 : 0644; 28 | fs.chmodSync(path.join(basedir, filename), perm, next); 29 | }); 30 | ``` 31 | 32 | Two convenience functions are provided for convenience, `walk.files` and `walk.dirs` (and their synchronous counterparts) to walk through files and directories. 33 | 34 | ```js 35 | var walk = require('fs-walk'); 36 | 37 | walk.files('/etc', function(basedir, filename, stat, next) { 38 | fs.chmod(path.join(basedir, filename), 0644, next); 39 | }, function(err) { 40 | if (err) console.log(err); 41 | }); 42 | 43 | walk.dirs('/etc', function(basedir, filename, stat, next) { 44 | fs.chmod(path.join(basedir, filename), 0755, next); 45 | }, function(err) { 46 | if (err) console.log(err); 47 | }); 48 | ``` 49 | 50 | 51 | ## License 52 | 53 | (The MIT License) 54 | 55 | Copyright (c) 2013 Conference Compass BV 56 | 57 | Maintainer: 58 | Alberto Gonzalez 59 | 60 | Permission is hereby granted, free of charge, to any person obtaining 61 | a copy of this software and associated documentation files (the 62 | 'Software'), to deal in the Software without restriction, including 63 | without limitation the rights to use, copy, modify, merge, publish, 64 | distribute, sublicense, and/or sell copies of the Software, and to 65 | permit persons to whom the Software is furnished to do so, subject to 66 | the following conditions: 67 | 68 | The above copyright notice and this permission notice shall be 69 | included in all copies or substantial portions of the Software. 70 | 71 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 72 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 73 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 74 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 75 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 76 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 77 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 78 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | , path = require('path') 3 | , async = require('async') 4 | 5 | /** 6 | * Asynchronously list `dir` and all its descendents, 7 | * calling `iterator` for each entry. Once all entries have 8 | * been listed, calls `callback`. 9 | * 10 | * @param {String} dir Path to the base directory of the listing. 11 | * @param {Function} iterator Function that will be called 12 | * for each entry in the listing, passing 4 arguments: 13 | * * `basedir`: Base directory of the entry (relative to `dir`). 14 | * * `file`: Name of the file 15 | * * `stat`: Result of calling `fs.stat` 16 | * * `next`: Asynchronous callback 17 | * Passing an error to the iterator `next` callback will stop iteration 18 | * and the main `callback` will be called. 19 | * @param {Function} callback Asynchronous callback. 20 | */ 21 | var walk = exports.walk = function(dir, iterator, callback) { 22 | var dirs = [dir]; 23 | 24 | async.whilst( 25 | function test(cb) { cb(null, dirs.length > 0); }, 26 | function(nextd) { 27 | var dir = dirs.shift(); 28 | async.waterfall([ 29 | function(nextrd) { 30 | fs.readdir(dir, nextrd); 31 | }, 32 | function(files, nextf) { 33 | async.each(files, function(file, nexts) { 34 | var f = path.join(dir, file); 35 | fs.stat(f, function(err, stat) { 36 | if(err){ 37 | nexts(f + ' is not a valid file or directory'); 38 | } else { 39 | if (stat && stat.isDirectory()) { 40 | dirs.push(f); 41 | } 42 | 43 | if (stat) { 44 | iterator(dir, file, stat, nexts); 45 | } else { 46 | nexts(f + ' is not a valid file or directory'); 47 | } 48 | } 49 | }); 50 | }, nextf); 51 | } 52 | ], 53 | nextd); 54 | }, 55 | callback); 56 | 57 | }; 58 | 59 | /** 60 | * Synchronously list `dir` and all its descendents, 61 | * calling `iterator` for each entry. 62 | * 63 | * @param {String} dir Path to the base directory of the listing. 64 | * @param {Function} iterator Function that will be called 65 | * for each entry in the listing, passing 4 arguments: 66 | * * `basedir`: Base directory of the entry (relative to `dir`). 67 | * * `file`: Name of the file 68 | * * `stat`: Result of calling `fs.stat` 69 | * 70 | */ 71 | var walkSync = exports.walkSync = function(dir, iterator) { 72 | var dirs = [dir]; 73 | 74 | while (dirs.length) { 75 | var dir = dirs.shift(); 76 | var files = fs.readdirSync(dir); 77 | files.forEach(function(file) { 78 | var f = path.join(dir, file); 79 | var stat = fs.statSync(f); 80 | if (stat && stat.isDirectory()) { 81 | dirs.push(f); 82 | } 83 | if (stat) { 84 | iterator(dir, file, stat); 85 | } 86 | }); 87 | } 88 | 89 | }; 90 | 91 | /** 92 | * Asynchronously list all files in `dir` and its 93 | * descendants 94 | */ 95 | var files = exports.files = function(dir, iterator, callback) { 96 | walk(dir, function(root, file, stat, next) { 97 | if (stat.isFile()) { 98 | iterator(root, file, stat, next); 99 | } else { 100 | next(); 101 | } 102 | }, callback); 103 | }; 104 | 105 | /** 106 | * Synchronously list all files in `dir` and its 107 | * descendants 108 | */ 109 | var filesSync = exports.filesSync = function(dir, iterator) { 110 | walkSync(dir, function(root, file, stat) { 111 | if (stat.isFile()) { 112 | iterator(root, file, stat); 113 | } 114 | }); 115 | }; 116 | 117 | /** 118 | * Asynchronously list all directories in `dir` and its 119 | * descendants 120 | */ 121 | var dirs = exports.dirs = function(dir, iterator, callback) { 122 | walk(dir, function(root, file, stat, next) { 123 | if (stat.isDirectory()) { 124 | iterator(root, file, stat, next); 125 | } else { 126 | next(); 127 | } 128 | }, callback); 129 | }; 130 | 131 | /** 132 | * Synchronously list all directories in `dir` and its 133 | * descendants 134 | */ 135 | var dirsSync = exports.dirsSync = function(dir, iterator) { 136 | walkSync(dir, function(root, file, stat) { 137 | if (stat.isDirectory()) { 138 | iterator(root, file, stat); 139 | } 140 | }); 141 | }; 142 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fs-walk", 3 | "description": "Synchronous and asynchronous recursive directory listing for node", 4 | "version": "0.0.2", 5 | "repository" : { 6 | "type" : "git", 7 | "url" : "https://github.com/confcompass/fs-walk.git" 8 | }, 9 | "author" : "Alberto Gonzalez