Skip to content

Commit

Permalink
Merge pull request fshost#40 from AckerApple/master
Browse files Browse the repository at this point in the history
added options, promise method, ability to run sync mode, and much more
  • Loading branch information
fshost authored Dec 5, 2016
2 parents 03510a2 + ecdfc03 commit cba03d9
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 72 deletions.
55 changes: 54 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,29 @@
# node-dir
A lightweight Node.js module with methods for some common directory and file operations, including asynchronous, non-blocking methods for recursively getting an array of files, subdirectories, or both, and methods for recursively, sequentially reading and processing the contents of files in a directory and its subdirectories, with several options available for added flexibility if needed.

### Table of Contents

- [installation](#installation)
- [usage](#usage)
- [methods](#methods)
- [readFiles( dir, options, fileCallback, finishedCallback)](#readfiles-dir-options-filecallback-finishedcallback)
- [readFilesStream( dir, options, streamCallback, finishedCallback)](#readfilesstream-dir-options-streamcallback-finishedcallback)
- [readFilesStream examples](#readfilesstream-examples)
- [files( dir, callback )](#files-dir-callback)
- [files( dir, {sync:true} )](#files-dir-synctrue)
- [promiseFiles( dir, callback )](#promisefiles-dir-callback)
- [subdirs( dir, callback )](#subdirs-dir-callback)
- [paths(dir, [combine], callback )](#pathsdir-combine-callback)
- [API Docs](#api-docs)
- [files(dir, type, callback, options)](#filesdir-type-callback-options)
- [License](#license)

#### installation

npm install node-dir

### usage

#### methods
For the sake of brevity, assume that the following line of code precedes all of the examples.

Expand All @@ -32,7 +51,7 @@ Valid options are:

A reverse sort can also be achieved by setting the sort option to 'reverse', 'desc', or 'descending' string value.

examples
#### readFilesStream examples

```javascript
// display contents of files in this script's directory
Expand Down Expand Up @@ -111,6 +130,25 @@ dir.files(__dirname, function(err, files) {
console.log(files);
});
```
#### files( dir, {sync:true} )
Synchronously iterate the files of a directory and its subdirectories and pass an array of file paths to a callback.

```javascript
var files = dir.files(__dirname, {sync:true});
console.log(files);
```

#### promiseFiles( dir, callback )
Asynchronously iterate the files of a directory and its subdirectories and pass an array of file paths to a callback.

```javascript
dir.promiseFiles(__dirname)
.then((files)=>{
console.log(files);
})
.catch(e=>console.error(e))
```

Note that for the files and subdirs the object returned is an array, and thus all of the standard array methods are available for use in your callback for operations like filters or sorting. Some quick examples:

Expand Down Expand Up @@ -167,6 +205,21 @@ dir.paths(__dirname, true, function(err, paths) {
});
```

## API Docs

### files(dir, type, callback, options)

- **dir** - directory path to read
- **type**='file'
- 'file' returns only file listings
- 'dir' returns only directory listings
- 'all' returns {dirs:[], files:[]}
- 'combine' returns []
- **callback** -
- **options**
- **sync**=false - results are returned inline and no callbacks are used
- **shortName**=false - instead of fullpath file names, just get the names
- **recursive**=true - traverse through all children of given path

## License
MIT licensed (See LICENSE.txt)
4 changes: 1 addition & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
var dirpaths = require('./lib/paths');

exports.files = dirpaths.files;
exports.paths = dirpaths.paths;
exports.subdirs = dirpaths.subdirs;
Object.assign(exports, dirpaths)
exports.readFiles = require('./lib/readfiles');
exports.readFilesStream = require('./lib/readfilesstream');
226 changes: 159 additions & 67 deletions lib/paths.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
var fs = require('fs'),
path = require('path');

exports.promiseFiles = function promiseFiles(dir, type, options){
type = type || 'file'

var processor = function(res,rej){
var cb = function(err,data){
if(err)return rej(err)
res(data)
}
exports.files(dir,type,cb,options)
}
return new Promise(processor)
}

/**
* find all files or subdirs (recursive) and pass to callback fn
*
Expand All @@ -13,75 +26,139 @@
* console.log('files:', files);
* });
*/
exports.files = function files(dir, type, callback, /* used internally */ ignoreType) {

var pending,
results = {
files: [],
dirs: []
};
var done = function() {
if (ignoreType || type === 'all') {
callback(null, results);
} else {
callback(null, results[type + 's']);
exports.files = function files(dir, type, callback, options) {
var ofType = typeof type
if(ofType == 'object'){
options = options || type
type = 'file'
callback = function(){}
}else if (ofType !== 'string') {
//ignoreType = callback;
callback = type;
type = 'file';
}

options = options || {}

var pending,
results = {
files: [],
dirs: []
};

var done = function() {
if(type==='combine'){
results = results.files.concat(results.dirs)
} else if (!type || options.ignoreType || ['all','combine'].indexOf(type)>=0) {
results = results
} else {
results = results[type + 's']
}

if(options.sync)return;


callback(null, results);
};

var getStatHandler = function(statPath, name, lstatCalled) {
return function(err, stat) {
if (err) {
if (!lstatCalled) {
return fs.lstat(statPath, getStatHandler(statPath, name, true));
}
return callback(err);
}

var pushVal = options.shortName ? name : statPath

if (stat && stat.isDirectory() && stat.mode !== 17115) {
if (type !== 'file') {
results.dirs.push(pushVal);
}
};

var getStatHandler = function(statPath, lstatCalled) {
return function(err, stat) {
if (err) {
if (!lstatCalled) {
return fs.lstat(statPath, getStatHandler(statPath, true));
}
return callback(err);

if (options.recursive==null || options.recursive) {
var subloop = function(err, res) {
if (err){
return callback(err)
}
if (stat && stat.isDirectory() && stat.mode !== 17115) {
if (type !== 'file') {
results.dirs.push(statPath);
}
files(statPath, type, function(err, res) {
if (err) return callback(err);
if (type === 'all') {
results.files = results.files.concat(res.files);
results.dirs = results.dirs.concat(res.dirs);
} else if (type === 'file') {
results.files = results.files.concat(res.files);
} else {
results.dirs = results.dirs.concat(res.dirs);
}
if (!--pending) done();
}, true);

if(type === 'combine'){
results.files = results.files.concat(res);
}else if (type === 'all') {
results.files = results.files.concat(res.files);
results.dirs = results.dirs.concat(res.dirs);
} else if (type === 'file') {
results.files = results.files.concat(res.files);
} else {
if (type !== 'dir') {
results.files.push(statPath);
}
// should be the last statement in statHandler
if (!--pending) done();
results.dirs = results.dirs.concat(res.dirs);
}

if (!--pending){
done();
}
};
};
}

var newOptions = Object.assign({}, options)
newOptions.ignoreType = true
var moreResults = files(statPath, type, subloop, newOptions);

if(options.sync){
subloop(null, moreResults)
}
}else if (!--pending){
done()
}
} else {
if (type !== 'dir') {
results.files.push(pushVal);
}
// should be the last statement in statHandler
if (!--pending){
done()
}
}
}
}

const onDirRead = function(err, list) {
if (err) return callback(err);

pending = list.length;
if (!pending) return done();

for (var file, i = 0, l = list.length; i < l; i++) {
file = path.join(dir, list[i]);

if (typeof type !== 'string') {
ignoreType = callback;
callback = type;
type = 'file';
if(options.sync){
var res = fs.statSync(file);
getStatHandler(file,list[i])(null, res)
}else{
fs.stat(file, getStatHandler(file,list[i]));
}
}

fs.stat(dir, function(err, stat) {
if (err) return callback(err);
if(stat && stat.mode === 17115) return done();

fs.readdir(dir, function(err, list) {
if (err) return callback(err);
pending = list.length;
if (!pending) return done();
for (var file, i = 0, l = list.length; i < l; i++) {
file = path.join(dir, list[i]);
fs.stat(file, getStatHandler(file));
}
});
});
return results
}

const onStat = function(err, stat) {
if (err) return callback(err);
if (stat && stat.mode === 17115) return done();

if(options.sync){
const list = fs.readdirSync(dir)
return onDirRead(null, list)
}else{
fs.readdir(dir, onDirRead)
}
}

if(options.sync){
const stat = fs.statSync(dir);
return onStat(null, stat)
}else{
fs.stat(dir, onStat);
}
};


Expand Down Expand Up @@ -136,9 +213,24 @@ exports.paths = function paths(dir, combine, callback) {
* console.log('subdirs:', paths.dirs);
* });
*/
exports.subdirs = function subdirs(dir, callback) {
exports.files(dir, 'dir', function(err, subdirs) {
if (err) return callback(err);
callback(null, subdirs);
});
exports.subdirs = function subdirs(dir, callback, type, options) {
options = options || {}

const iCallback = function(err, subdirs) {
if (err) return callback(err);

if(type=='combine'){
subdirs = subdirs.files.concat(subdirs.dirs)
}

if(options.sync)return subdirs

callback(null, subdirs);
}

const res = exports.files(dir, 'dir', iCallback, options)

if(options && options.sync){
return iCallback(null,res)
}
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "node-dir",
"version": "0.1.16",
"version": "0.1.17",
"description": "asynchronous file and directory operations for Node.js",
"main": "index",
"homepage": "https://github.com/fshost",
Expand Down
31 changes: 31 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1122,8 +1122,39 @@ describe('readfilesstream method', function() {
});
});

it("#promiseFiles", function(done) {
dir.promiseFiles(tdir)
.then(function(files) {
var relFiles = files.map(function(curPath) {
return path.relative(fixturesDir, curPath);
});
relFiles.sort().should.eql([
'testdir/file1.txt',
'testdir/file2.text',
'testdir/subdir/file3.txt',
'testdir/subdir/file4.text'
]);
})
.then(done).catch(done)
});

describe("files method", function() {

it("#files(path, {sync:true}",
function() {
var files = dir.files(tdir,'file',()=>{},{sync:true});
var relFiles = files.map(function(curPath) {
return path.relative(fixturesDir, curPath);
});

relFiles.sort().should.eql([
'testdir/file1.txt',
'testdir/file2.text',
'testdir/subdir/file3.txt',
'testdir/subdir/file4.text'
]);
});

it("should iterate the files of a directory (recursively) and pass their filenames to a callback", function(done) {
dir.files(tdir, function(err, files) {
should.not.exist(err);
Expand Down

0 comments on commit cba03d9

Please sign in to comment.