bal-util
Version:
Common utility functions for Node.js used and maintained by Benjamin Lupton
482 lines (467 loc) • 14.1 kB
JavaScript
// Generated by CoffeeScript 2.3.1
// Import
var TaskGroup, balUtilFlow, balUtilPaths, eachr, extendr, extractOpts, ignorefs, pathUtil, safefs, scandir, typeChecker;
pathUtil = require('path');
eachr = require('eachr');
typeChecker = require('typechecker');
extendr = require('extendr');
safefs = require('safefs');
extractOpts = require('extract-opts');
({TaskGroup} = require('taskgroup'));
balUtilFlow = require('./flow');
ignorefs = require('ignorefs');
scandir = require('scandirectory');
// Define
balUtilPaths = {
// =====================================
// Our Extensions
// Resolve a Case Sensitive Path
// next(err, result)
resolveCaseSensitivePath: function(path, next) {
var parentPath;
// Resolve the parent path
parentPath = safefs.getParentPathSync(path) || '/';
if (parentPath === '/') {
return next(null, parentPath);
}
safefs.resolveCaseSensitivePath(parentPath, function(err, parentPath) {
return safefs.readdir(parentPath, function(err, files) {
var file, i, len, relativePathLowerCase;
if (err) {
return next(err);
}
relativePathLowerCase = relativePath.toLowerCase();
for (i = 0, len = files.length; i < len; i++) {
file = files[i];
if (file.toLowerCase() === relativePathLowerCase) {
return next(null, pathUtil.join(parentPath, relativePath));
}
}
err = new Error(`Could not find the path ${relativePath} inside ${parentPath}`);
return next(err);
});
});
// Chain
return safefs;
},
// Copy a file
// Or rather overwrite a file, regardless of whether or not it was existing before
// next(err)
cp: function(src, dst, next) {
// Copy
safefs.readFile(src, 'binary', function(err, data) {
if (err) {
// Error
return next(err);
}
// Success
return safefs.writeFile(dst, data, 'binary', function(err) {
// Forward
return next(err);
});
});
return this;
},
// Prefix path
prefixPathSync: function(path, parentPath) {
path = path.replace(/[\/\\]$/, '');
if (/^([a-zA-Z]\:|\/)/.test(path) === false) {
path = pathUtil.join(parentPath, path);
}
return path;
},
// Is it a directory?
// path can also be a stat object
// next(err,isDirectory,fileStat)
isDirectory: function(path, next) {
// Check if path is a stat object
if ((path != null ? path.isDirectory : void 0) != null) {
return next(null, path.isDirectory(), path);
} else {
// Otherwise fetch the stat and do the check
safefs.stat(path, function(err, stat) {
if (err) {
// Error
return next(err);
}
// Success
return next(null, stat.isDirectory(), stat);
});
}
return this;
},
// Generate a slug for a file
generateSlugSync: function(path) {
var result;
// Slugify
result = path.replace(/[^a-zA-Z0-9]/g, '-').replace(/^-/, '').replace(/-+/, '-');
// Return
return result;
},
// Scan a directory into a list
// next(err,list)
scanlist: function(path, next) {
// Handle
scandir({
path: path,
readFiles: true,
ignoreHiddenFiles: true,
next: function(err, list) {
return next(err, list);
}
});
return this;
},
// Scan a directory into a tree
// next(err,tree)
scantree: function(path, next) {
// Handle
scandir({
path: path,
readFiles: true,
ignoreHiddenFiles: true,
next: function(err, list, tree) {
return next(err, tree);
}
});
return this;
},
// Copy a directory
// If the same file already exists, we will keep the source one
// Usage:
// cpdir({srcPath,outPath,next})
// cpdir(srcPath,outPath,next)
// Callbacks:
// next(err)
cpdir: function(...args) {
var err, i, len, next, opt, opts, outPath, ref, scandirOpts, srcPath;
// Prepare
opts = {};
if (args.length === 1) {
opts = args[0];
} else if (args.length >= 3) {
[srcPath, outPath, next] = args;
opts = {srcPath, outPath, next};
} else {
err = new Error('balUtilPaths.cpdir: unknown arguments');
if (next) {
return next(err);
} else {
throw err;
}
}
// Create opts
scandirOpts = {
path: opts.srcPath,
fileAction: function(fileSrcPath, fileRelativePath, next) {
var fileOutPath;
// Prepare
fileOutPath = pathUtil.join(opts.outPath, fileRelativePath);
// Ensure the directory that the file is going to exists
return safefs.ensurePath(pathUtil.dirname(fileOutPath), function(err) {
// Error
if (err) {
return next(err);
}
// The directory now does exist
// So let's now place the file inside it
return balUtilPaths.cp(fileSrcPath, fileOutPath, function(err) {
// Forward
return next(err);
});
});
},
next: opts.next
};
ref = ['ignorePaths', 'ignoreHiddenFiles', 'ignoreCommonPatterns', 'ignoreCustomPatterns'];
// Passed Scandir Opts
for (i = 0, len = ref.length; i < len; i++) {
opt = ref[i];
scandirOpts[opt] = opts[opt];
}
// Scan all the files in the diretory and copy them over asynchronously
scandir(scandirOpts);
return this;
},
// Replace a directory
// If the same file already exists, we will keep the newest one
// Usage:
// rpdir({srcPath,outPath,next})
// rpdir(srcPath,outPath,next)
// Callbacks:
// next(err)
rpdir: function(...args) {
var err, i, len, next, opt, opts, outPath, ref, scandirOpts, srcPath;
// Prepare
opts = {};
if (args.length === 1) {
opts = args[0];
} else if (args.length >= 3) {
[srcPath, outPath, next] = args;
opts = {srcPath, outPath, next};
} else {
err = new Error('balUtilPaths.cpdir: unknown arguments');
if (next) {
return next(err);
} else {
throw err;
}
}
// Create opts
scandirOpts = {
path: opts.srcPath,
fileAction: function(fileSrcPath, fileRelativePath, next) {
var fileOutPath;
// Prepare
fileOutPath = pathUtil.join(opts.outPath, fileRelativePath);
// Ensure the directory that the file is going to exists
return safefs.ensurePath(pathUtil.dirname(fileOutPath), function(err) {
if (err) {
// Error
return next(err);
}
// Check if it is worthwhile copying that file
return balUtilPaths.isPathOlderThan(fileOutPath, fileSrcPath, function(err, older) {
// The src path has been modified since the out path was generated
if (older === true || older === null) {
// The directory now does exist
// So let's now place the file inside it
return balUtilPaths.cp(fileSrcPath, fileOutPath, function(err) {
// Forward
return next(err);
});
} else {
// The out path is new enough
return next();
}
});
});
},
next: opts.next
};
ref = ['ignorePaths', 'ignoreHiddenFiles', 'ignoreCommonPatterns', 'ignoreCustomPatterns'];
// Passed Scandir Opts
for (i = 0, len = ref.length; i < len; i++) {
opt = ref[i];
scandirOpts[opt] = opts[opt];
}
// Scan all the files in the diretory and copy them over asynchronously
scandir(scandirOpts);
return this;
},
// Write tree
// next(err)
writetree: function(dstPath, tree, next) {
// Ensure Destination
safefs.ensurePath(dstPath, function(err) {
var tasks;
if (err) {
// Checks
return next(err);
}
// Group
tasks = new TaskGroup({
concurrency: 0
}).done(next);
// Cycle
eachr(tree, function(value, fileRelativePath) {
return tasks.addTask(function(complete) {
var fileFullPath;
fileFullPath = pathUtil.join(dstPath, fileRelativePath.replace(/^\/+/, ''));
if (typeChecker.isObject(value)) {
return balUtilPaths.writetree(fileFullPath, value, complete);
} else {
return safefs.writeFile(fileFullPath, value, complete);
}
});
});
// Run the tasks
return tasks.run();
});
return this;
},
// Read path
// Reads a path be it local or remote
// next(err,data)
readPath: function(filePath, opts, next) {
var base, base1, err, http, ref, req, requestOpts, zlib;
[opts, next] = extractOpts(opts, next);
// Request
if (/^http/.test(filePath)) {
// Zlib
zlib = null;
try {
zlib = require('zlib');
} catch (error) {
err = error;
}
// do nothing
// Options
requestOpts = require('url').parse(filePath);
if (requestOpts.path == null) {
requestOpts.path = requestOpts.pathname;
}
if (requestOpts.method == null) {
requestOpts.method = 'GET';
}
if (requestOpts.headers == null) {
requestOpts.headers = {};
}
if (zlib) {
if ((base = requestOpts.headers)['accept-encoding'] == null) {
base['accept-encoding'] = 'gzip,deflate';
}
}
if ((base1 = requestOpts.headers)['user-agent'] == null) {
base1['user-agent'] = 'Wget/1.14 (linux-gnu)';
}
// Prepare request
http = requestOpts.protocol === 'https:' ? require('https') : require('http');
req = http.request(requestOpts);
if (req.setTimeout == null) {
req.setTimeout = function(delay) {
var onTimeout;
onTimeout = function() {
return req.emit('error', new Error('request timed out'));
};
return setTimeout(onTimeout, delay); // alias for node <=0.9
};
}
req.setTimeout((ref = opts.timeout) != null ? ref : 10 * 1000); // 10 second timeout
req.once('error', next);
req.once('timeout', function() {
return req.abort(); // must abort manually, will trigger error event
});
req.once('response', function(res) {
var chunks, locationHeader, ref1;
locationHeader = ((ref1 = res.headers) != null ? ref1.location : void 0) || null;
if (locationHeader && locationHeader !== requestOpts.href) {
req.removeAllListeners();
balUtilPaths.readPath(locationHeader, opts, next);
return;
}
chunks = [];
res.on('data', function(chunk) {
return chunks.push(chunk);
});
return res.once('end', function() {
var data;
data = new Buffer.concat(chunks);
switch (res.headers['content-encoding']) {
case 'gzip':
zlib.unzip(data, next);
break;
case 'deflate':
zlib.inflate(data, next);
break;
default:
return next(null, data);
}
});
});
// End request, start response
req.end();
} else {
// Local
safefs.readFile(filePath, function(err, data) {
if (err) {
return next(err);
}
return next(null, data);
});
}
return this;
},
// Empty
// Check if the file does not exist, or is empty
// next(err,empty)
empty: function(filePath, next) {
// Check if we exist
safefs.exists(filePath, function(exists) {
if (!exists) {
// Return empty if we don't exist
return next(null, true);
}
// We do exist, so check if we have content
return safefs.stat(filePath, function(err, stat) {
if (err) {
// Check
return next(err);
}
// Return whether or not we are actually empty
return next(null, stat.size === 0);
});
});
return this;
},
// Is Path Older Than
// Checks if a path is older than a particular amount of millesconds
// next(err,older)
// older will be null if the path does not exist
isPathOlderThan: function(aPath, bInput, next) {
var bMtime, bPath, mode;
// Handle mtime
bMtime = null;
if (typeChecker.isNumber(bInput)) {
mode = 'time';
bMtime = new Date(new Date() - bInput);
} else {
mode = 'path';
bPath = bInput;
}
// Check if the path exists
balUtilPaths.empty(aPath, function(err, empty) {
if (empty || err) {
// If it doesn't then we should return right away
return next(err, null);
}
// We do exist, so let's check how old we are
return safefs.stat(aPath, function(err, aStat) {
var compare;
if (err) {
// Check
return next(err);
}
// Prepare
compare = function() {
var older;
// Time comparison
if (aStat.mtime < bMtime) {
older = true;
} else {
older = false;
}
// Return result
return next(null, older);
};
// Perform the comparison
if (mode === 'path') {
// Check if the bPath exists
return balUtilPaths.empty(bPath, function(err, empty) {
if (empty || err) {
// Return result if we are empty
return next(err, null);
}
// It does exist so lets get the stat
return safefs.stat(bPath, function(err, bStat) {
if (err) {
// Check
return next(err);
}
// Assign the outer bMtime variable
bMtime = bStat.mtime;
// Perform the comparison
return compare();
});
});
} else {
// We already have the bMtime
return compare();
}
});
});
return this;
}
};
// Export
module.exports = balUtilPaths;