fs-utilities
Version:
utility module for interacting with the file system e.g. asynchronously retreiving list of folders
210 lines (173 loc) • 6.88 kB
JavaScript
/* jslint node: true */
var fs = require('fs');
var path = require('path');
var async = require('async');
/**
* @callback errorDataCallback
* @param {Object|null} err - contains an object representing an occured error or null
* @param {*} data - returned data
*/
/**
* @callback dataCallback
* @param {*} data - returned data
*/
/**
* @callback errorCallback
* @param {Object|null} err - contains an object representing an occured error or null
*/
/**
* Creates a new NotAFolderError object.
*
* @constructor
* @augments Error
* @param {string} fileName - name of the file that is not a directory
*/
function NotAFolderError( fileName ) {
this.name = "NotAFolderError";
this.message = fileName + ' is not a directory';
}
NotAFolderError.prototype = Error.prototype;
/**
* Asynchronously tests if the specified file is a directory.
*
* @param {string} directoryPath - fullpath to file which should be tested
* @param {errorDataCallback} callback - will be executed after test finished. If `directoryPath` is not
* a folder, second callback argument will be undefined.
*/
function checkIfIsDirectory( directoryPath, callback ){
fs.stat( directoryPath, function( err, stats ){
if( err ){
callback( err );
} else if( stats.isDirectory() ){
callback( null, directoryPath );
} else {
callback( new NotAFolderError( directoryPath ) );
}
});
}
/**
* Synchron function to test if the specified file is a directory.
*
* @param {string} directoryPath - fullpath to file which should be tested
* @returns {Boolean} true if tested file is indeed a folder
*/
function checkIfIsDirectorySync( directoryPath ){
var stats = fs.statSync( directoryPath );
return stats.isDirectory();
}
/**
* Asynchron function which will pass an array containing all subfolders of the given directory
* to the also passed callback function.
*
* @param {string} directoryPath - path to look for subfolders
* @param {errorDataCallback} callback - will be executed after all subfolders have been detected.
* If case of an error, the search will be aborted and the second
* argument of the callback function will be undefined.
*/
function getSubfolders( directoryPath, callback ){
var folders = [];
var iteratorFunction = function( item, finishedCallback ){
var fullFilePath = path.join( directoryPath, item );
checkIfIsDirectory( fullFilePath, function( err, folder ){
if( !err ){
folders.push( folder );
} else if( !(err instanceof NotAFolderError) ){
finishedCallback( err );
}
finishedCallback();
});
};
// get all files contained in the folder $directoryPath
fs.readdir( directoryPath, function( err, files ){
if( err ){
callback( err );
}
// iterate through those files and add all found folders to $folders
async.each( files, iteratorFunction, function( err ){
callback( err, folders );
});
});
}
/**
* Synchronously generates and returns an array containing all subfolders of the given directory.
*
* @param {string} directoryPath - path to look for subfolders
* @returns {string[]} array containing the full path of all subfolders located in directoryPath
*/
function getSubfoldersSync( directoryPath ){
var files = fs.readdirSync( directoryPath );
var fullFileName;
var folders = [];
var i;
for( i in files ){
fullFileName = path.join( directoryPath, files[i] );
if( checkIfIsDirectorySync( fullFileName ) ){
folders.push( fullFileName );
}
}
return folders;
}
/**
* Asynchron function which will recursively traverse all subfolers.
*
* @param {string} directoryPath - path from where to start traversing
* @param {dataCallback} callback - will be called for each traversed folder
* @param {string[]} [opt_excludedDirectories] - list of folders that will be skiped
* @param {errorCallback} [opt_finishedCallback] - function that is called after all subfolders
* have been traversed
*/
function traverseAllSubFolders( directoryPath,
callback,
opt_excludedDirectories,
opt_finishedCallback ){
opt_excludedDirectories = opt_excludedDirectories || [];
opt_finishedCallback = opt_finishedCallback || function(){};
var iteratorFunction = function( item, iteratorCallback ){
var folderName = path.basename( item );
if( opt_excludedDirectories.indexOf( folderName ) === -1 ){
callback( item );
traverseAllSubFolders( item, callback, opt_excludedDirectories, iteratorCallback );
} else {
iteratorCallback();
}
};
getSubfolders( directoryPath, function( err, folders ){
if( ! err ){
async.each( folders, iteratorFunction, opt_finishedCallback);
} else {
opt_finishedCallback( err );
}
});
}
/**
* Synchronous function which will recursively traverse all subfolers.
*
* @param {string} directoryPath - path from where to start traversing
* @param {dataCallback} callback - will be called for each traversed folder
* @param {string[]} [opt_excludedDirectories] - list of folders that will be skiped
*/
function traverseAllSubFoldersSync( directoryPath,
callback,
opt_excludedDirectories ){
opt_excludedDirectories = opt_excludedDirectories || [];
var folders = getSubfoldersSync( directoryPath );
var folderName;
var i;
for( i in folders ){
folderName = path.basename( folders[i] );
if( opt_excludedDirectories.indexOf( folderName ) === -1 ){
callback( folders[i] );
traverseAllSubFoldersSync( folders[i], callback, opt_excludedDirectories );
}
}
}
module.exports.NotAFolderError = NotAFolderError;
module.exports.checkIfIsDirectory = checkIfIsDirectory;
module.exports.checkIfIsDirectorySync = checkIfIsDirectorySync;
module.exports.getSubfolders = getSubfolders;
module.exports.getSubfoldersSync = getSubfoldersSync;
module.exports.traverseAllSubFolders = traverseAllSubFolders;
module.exports.traverseAllSubFoldersSync = traverseAllSubFoldersSync;
// legacy
module.exports.getSubfoders = getSubfolders;
module.exports.getSubfodersSync = getSubfoldersSync;