Skip to content

Commit

Permalink
merge release from rel-0.1.0
Browse files Browse the repository at this point in the history
Conflicts:
	tests/counts.js
  • Loading branch information
Isao Yagi committed May 11, 2013
2 parents 30f6cf1 + d85f31f commit 16e6987
Show file tree
Hide file tree
Showing 21 changed files with 293 additions and 190 deletions.
2 changes: 1 addition & 1 deletion .gitignore
@@ -1,2 +1,2 @@
node_modules
/reports
/artifacts
25 changes: 25 additions & 0 deletions .jshintrc
@@ -0,0 +1,25 @@
{
// enforcing, over defaults
"curly": true, // Require {} for every new block or scope
"eqeqeq": true, // Require triple equals (===) for comparison
//"eqnull": true, // Tolerate use of `== null`
"immed": true, // Require wrapping IIFE in parens e.g. `(function () {} ());`
"iterator": false, // No `__iterator__` property
"latedef": true, // Require variables/functions to be defined before being used
"newcap": true, // Require capitalization of all constructor functions e.g. `new F()`
"noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`
"onevar": true, // Only one `var` statement per function
"trailing": true, // Prohibit trailing whitespace
"undef": true, // Require all non-global variables to be declared
"unused": true, // Require all defined variables be used
//"white": false, // Check against strict whitespace and indentation rules

// relaxing, over defaults
"es5": true, // Allow ES5 syntax (ex: getters and setters)
"globalstrict": true, // Allow global "use strict" (also enables 'strict')

// env
//"devel": true, // Development/debugging (alert, confirm, etc)
"node": true, // node.js
"yui": true
}
4 changes: 2 additions & 2 deletions .travis.yml
@@ -1,4 +1,4 @@
language: node_js
node_js:
- 0.9
- 0.8
- "0.10"
- "0.8"
10 changes: 7 additions & 3 deletions README.md
Expand Up @@ -11,14 +11,18 @@ usage
-----
See [`./examples/`](./examples/).

limitations
-----------
Uses `fs.stat()` so symlinks are not distinguished and cycles are not detected.

test
----
npm test

Tests use James Halliday's [tape](https://github.com/substack/tape/) test harness. If you have Krishnan Anantheswaran's [istanbul](https://github.com/gotwarlost/istanbul/), or Jarrod Overson's [plato](https://github.com/jsoverson/plato) installed globally you can do these things too, respectively:
Tests use Isaac Schlueter's [tap](https://github.com/isaacs/node-tap) test harness. If you have Krishnan Anantheswaran's [istanbul](https://github.com/gotwarlost/istanbul/), or Jarrod Overson's [plato](https://github.com/jsoverson/plato) installed globally you can do these things too, respectively:

npm run-script cover
npm run-script plato
npm run cover
npm run plato

license
-------
Expand Down
2 changes: 1 addition & 1 deletion examples/copydemo.js
Expand Up @@ -26,7 +26,7 @@ scan.on('file', function (err, pathname, stat) {

scan.on('error', console.error);

scan.on('done', function (count) {
scan.on('done', function (err, count) {
console.log('done. %d things scanned. files copied to %s', count, to);
});

Expand Down
4 changes: 2 additions & 2 deletions examples/finddemo.js
Expand Up @@ -13,7 +13,7 @@ var ignore = ['node_modules', /\/.(git|svn)/],


// add a custom event
scan.typeSetter = function (err, pathname, stat) {
scan.typer = function (err, pathname, stat) {
if (pathname.match(/\.js$/)) {
return 'javascript!';
}
Expand All @@ -33,7 +33,7 @@ scan.on('file', function (err, pathname, stat) {
scan.on('error', console.error);

// results
scan.on('done', function (count) {
scan.on('done', function (err, count) {
console.log('\nscanned %d things', count);
console.log('found these js files:\n ', found.join('\n '));
});
Expand Down
180 changes: 89 additions & 91 deletions index.js
Expand Up @@ -3,143 +3,141 @@
* Copyrights licensed under the MIT License.
* See the accompanying LICENSE file for terms.
*/
/*jshint node:true */
'use strict';

var fs = require('fs'),
path = require('path'),
Stream = require('stream');


/**
* Set the type of event to fire
* @param {object} err fs.stat() Error object, or null
* @param {object} stat fs.Stats obj, see `man 2 stat` http://bit.ly/Sb0KRd
* @param {string} item pathname of file, directory, socket, fifo, etc.
* @return {string} name of event to emit
*/
function typer(err, pathname, stat) {
var type = 'other';
if (stat.isFile()) {
type = 'file';
} else if (stat.isDirectory()) {
type = 'dir';
}
return type;
}

function arrayify(arg) {
return [].concat(arg).filter(function isDefined(item) {
/*jshint eqnull:true */
return item != null;
});
}

/**
* @constructor
* @param {array} ignore Array of strings or regexes for exclusion matching
* @param {function} fn Function that returns a event name string, or falsey
* @events file, dir, other, ignored, *, error
*/
function Scan(ignore, fn) {
this.count = 0;
this.errors = 0;
this.ignore = arrayify(ignore);
if ('function' === typeof fn) {
this.typer = fn;
}
}

Scan.prototype = Object.create(Stream.prototype);

/**
* @param {array} list Paths to begin walking. Events emitted for every item.
* Pathnames emitted are relative to the pathnames in the list.
*/
Scan.prototype.relatively = function(list) {
this.statOne(arrayify(list));
};

/**
* @param {array} list Paths to begin walking. Events emitted for every item.
* Pathnames emitted are absolute.
*/
Scan.prototype.absolutely = function(list) {
function resolve(pathname) {
return path.resolve(pathname); // pass only 1st arg of map to resolve()
}
this.statOne(arrayify(list).map(resolve));
};

/**
* @param {array} list Items remaining to fs.stat().
* @param {object} self This instance object.
*/
Scan.prototype.statOne = function(list) {
var item = list.shift();
if (item) {
this.count++;
fs.stat(item, this.getStatCb(item, list));
} else {
this.emit('done', null, this.count);
}
};

/**
* @param {string} item File system path.
* @param {array} list Items remaining to fs.stat().
* @param {object} self This instance object.
* @return {function} Closure for fs.stat() callback.
*/
function getStatCb(item, list, self) {
Scan.prototype.getStatCb = function(item, list) {
var self = this;

function pathing(subitem) {
return path.join(item, subitem);
}

function recurse(err, sublist) {
function readdirCb(err, sublist) {
process.nextTick(function() {
statOne(list.concat(sublist.map(pathing)), self);
self.statOne(list.concat(sublist.map(pathing)));
});
}

/**
* @param {object} err fs.stat() Error object, or null
* @param {object} stat fs.Stats obj, see `man 2 stat` http://bit.ly/Sb0KRd
* @param {string} item Pathname
* @return {string} Type of filesystem item and name of event emitted
*/
function typer(err, pathname, stat) {
var type = 'other';
if (stat.isFile()) {
type = 'file';
} else if (stat.isDirectory()) {
type = 'dir';
}
return type;
}

return function statCb(err, stat) {
var type;

if (err) {
type = 'error';
} else if (self.ignore.some(matchCb(item))) {
self.errors++;
} else if (self.ignore.some(String.prototype.match.bind(item))) {
type = 'ignored';
} else {
type = self.typeSetter(err, item, stat) || typer(err, item, stat);
type = self.typer(err, item, stat) || typer(err, item, stat);
}

self.emit(type, err, item, stat);
self.emit('*', err, item, stat, type);

if ('dir' === type) {
fs.readdir(item, recurse);
fs.readdir(item, readdirCb);

} else if (list.length) {
statOne(list, self);
self.statOne(list);

} else {
self.emit('done', self.count);
self.emit('done', self.errors, self.count);
}
};
}

/**
* @param {array} list Items remaining to fs.stat().
* @param {object} self This instance object.
*/
function statOne(list, self) {
var item = list.shift();
if (item) {
self.count++;
fs.stat(item, getStatCb(item, list, self));
}
}

function matchCb(str) {
return function (re) {
return str.match(re);
};
}

function arrayify(input) {
var arr;
if(undefined === input) {
arr = [];
} else if(('string' === typeof input) || (input instanceof RegExp)) {
arr = [input];
} else if(!(input instanceof Array)) {
throw new TypeError('arguments must be either strings or arrays');
} else {
arr = input.slice();
}
return arr;
}

/**
* @constructor
* @param {array} ignore Array of strings or regexes for exclusion matching
* @events file, dir, other, ignored, *, error
*/
function Scan(ignore) {
this.count = 0;
this.ignore = arrayify(ignore);
}

/**
* Inherit from core streams module to get emit()/on(), additional stream-iness
* not used yet. Could use EventEmitter...
*/
Scan.prototype = Object.create(Stream.prototype);

/**
* @param {array} list Paths to begin walking. Events emitted for every item.
* Pathnames emitted are relative to the pathnames in the list.
*/
Scan.prototype.relatively = function(list) {
statOne(arrayify(list), this);
};

/**
* @param {array} list Paths to begin walking. Events emitted for every item.
* Pathnames emitted are absolute.
*/
Scan.prototype.absolutely = function(list) {
statOne(arrayify(list).map(path.resolve), this);
};

/**
* @param {object} err fs.stat() Error object, or null
* @param {object|null} err fs.stat() Error object, or null
* @param {string} item Pathname
* @param {object} stat fs.Stats object, see `man 2 stat`, http://bit.ly/Sb0KRd
* @return {string} Name of event/type. If falsey, typer() will be used.
*/
Scan.prototype.typeSetter = function(err, item, stat) {
Scan.prototype.typer = function(err, item, stat) {
/*jshint unused:false */
// stub for user-provided event category typer
};

Expand Down
46 changes: 26 additions & 20 deletions package.json
@@ -1,22 +1,28 @@
{
"name": "scanfs",
"version": "0.0.2",
"description": "Walks filesystem breadth-first, emitting events on the way.",
"main": "index.js",
"dependencies": {},
"devDependencies": {
"tape": "~0.2.2"
},
"scripts": {
"test": "tests/runner tests/*.js",
"cover": "istanbul cover --dir reports/coverage tests/runner tests/*.js",
"plato": "plato -d reports/plato index.js"
},
"repository": {
"type": "git",
"url": "git://github.com/isao/scanfs.git"
},
"author": "Isao Yagi <isao@yahoo-inc.com>",
"license": "MIT",
"keywords": ["filesystem", "walk", "find", "traverse"]
"name": "scanfs",
"version": "0.1.0",
"description": "Walks filesystem breadth-first, emitting events on the way.",
"main": "index.js",
"dependencies": {},
"devDependencies": {
"tap": "~0.4.1"
},
"scripts": {
"lint": "jshint *.js",
"test": "node tests",
"cover": "istanbul cover --dir artifacts/coverage tests",
"plato": "plato -d artifacts/plato *.js"
},
"repository": {
"type": "git",
"url": "git://github.com/isao/scanfs.git"
},
"author": "Isao Yagi <isao@yahoo-inc.com>",
"license": "MIT",
"keywords": [
"filesystem",
"walk",
"find",
"traverse"
]
}

0 comments on commit 16e6987

Please sign in to comment.