file-utils
Version:
Sync file utility for Node.js command line tools
165 lines (134 loc) • 4.38 kB
JavaScript
'use strict';
var path = require('path');
var util = require('util');
var _ = require('lodash');
var File = require('./file');
module.exports = Env;
function Env(opt) {
File.apply(this, arguments);
this._base = opt.base || '';
this._destBase = opt.dest || '';
this._writeFilters = {};
this._validationFilters = {};
var methodsToPrefix = [ 'mkdir', 'recurse', 'read', 'readJSON', 'write', 'delete',
'exists', 'isLink', 'isDir', 'isFile' ];
// Prefix path arguments with this environment root dir
methodsToPrefix.forEach(function(methodName) {
this[methodName] = function() {
var args = _.toArray(arguments);
args[0] = this.fromBase(args[0]);
return File.prototype[methodName].apply(this, args);
};
}.bind(this));
this.copy = function() {
var args = _.toArray(arguments);
args[0] = this.fromBase(args[0]);
args[1] = this.fromDestBase(args[1]);
return File.prototype.copy.apply(this, args);
};
this._actualWrite = this.write;
this.write = function(filepath, contents, options) {
return this.applyWriteFilters({
path: filepath,
contents: contents
}, this.applyValidationFilters, options);
};
}
util.inherits(Env, File);
Env.prototype.setBase = function(filepath) {
this._base = filepath;
};
// Return a path prefixed by the base (if not absolute)
Env.prototype.fromBase = function(filepath) {
if (this.isPathAbsolute(filepath)) {
return filepath;
}
return path.join(_.isFunction(this._base) ? this._base() : this._base, filepath);
};
Env.prototype.setDestBase = function(filepath) {
this._destBase = filepath;
};
// Return a path prefixed by the destination base (if not absolute)
Env.prototype.fromDestBase = function(filepath) {
if (this.isPathAbsolute(filepath)) {
return filepath;
}
return path.join(_.isFunction(this._destBase) ? this._destBase() : this._destBase, filepath);
};
Env.prototype.registerWriteFilter = function(name, filter) {
this._writeFilters[name] = filter;
};
Env.prototype.removeWriteFilter = function(name) {
delete this._writeFilters[name];
};
Env.prototype.registerValidationFilter = function(name, filter) {
this._validationFilters[name] = filter;
};
Env.prototype.removeValidationFilter = function(name) {
delete this._validationFilters[name];
};
Env.prototype.applyWriteFilters = function(file, validate, options) {
var writeFilters = _.reduce(this._writeFilters, function(m, v) { m.push(v); return m; }, []);
if (!writeFilters.length) {
return validate.call(this, file, options);
}
var i = 0;
var output;
var recurse = function(file) {
i++;
if (writeFilters[i]) {
runAsync(writeFilters[i], recurse, file);
} else {
output = validate.call(this, file, options);
}
}.bind(this);
runAsync(writeFilters[i], recurse, file);
return output;
};
Env.prototype.applyValidationFilters = function( file, options ) {
var validationFilters = _.reduce(this._validationFilters, function(m, v) { m.push(v); return m; }, []);
if (!validationFilters.length) {
return this._actualWrite.call(this, file.path, file.contents, options);
}
var i = 0;
var output;
var recurse = function(validated) {
if ( validated !== true ) {
return this.log.write(validated || 'Not actually writing to '+ file.path +' haven\'t pass validation' );
}
i++;
if (validationFilters[i]) {
runAsync(validationFilters[i], recurse, file);
} else {
output = this._actualWrite.call(this, file.path, file.contents, options);
}
}.bind(this);
runAsync(validationFilters[i], recurse, file);
return output;
};
/**
* Allow a function to be run async by using `this.async()`. If not, then the function is
* runned synchronously.
* @param {Function} func The function to run
* @param {Function} cb Callback to be provided even if the function is run sync
* @param {...rest} Arguments passed to `func`
* @return {null}
*/
function runAsync( func, cb ) {
var rest = [];
var len = 1;
while ( len++ < arguments.length ) {
rest.push( arguments[len] );
}
var async = false;
var returnValue = func.apply({
async: function() {
async = true;
return _.once(cb);
}
}, rest );
// Note: Call the callback synchronously to keep the sync flow by default
if ( !async ) {
cb(returnValue);
}
}