grunt
Version:
A task-based command line build tool for JavaScript projects.
231 lines (215 loc) • 7.83 kB
JavaScript
/*
* grunt
* https://github.com/cowboy/grunt
*
* Copyright (c) 2012 "Cowboy" Ben Alman
* Licensed under the MIT license.
* http://benalman.com/about/license/
*/
var fs = require('fs');
var path = require('path');
var glob = require('glob-whatev');
// Return an array of all file paths that match the given wildcard patterns.
exports.expand = function() {
// Use the first argument if it's an Array, otherwise convert the arguments
// object to an array and use that.
var patterns = arguments[0] instanceof Array ? arguments[0] : util.toArray(arguments);
// Generate a should-be-unique number.
var uid = +new Date();
// Return a flattened, uniqued array of matching file paths.
return underscore(patterns).chain().flatten().map(function(pattern) {
// Just return the pattern if it's an internal directive.
if (task.getDirectiveParts(pattern)) { return pattern; }
// Otherwise, expand paths.
return glob.glob(pattern, glob.GLOB_DEFAULT | glob.GLOB_NOCHECK);
}).flatten().uniq(false, function(filepath) {
// Only unique file paths, but don't unique <something> directives, in case
// they are repeated intentionally.
return task.getDirectiveParts(filepath) ? ++uid : filepath;
}).filter(function(filepath) {
// Just return the filepath if it's an internal directive.
if (task.getDirectiveParts(filepath)) { return filepath; }
try {
return fs.statSync(filepath).mode !== 16877;
} catch(e) {
throw task.taskError(e.message, e);
}
}).value();
};
// Return an array of all file paths that match the given wildcard patterns,
// plus any URLs that were passed at the end.
exports.expandToUrls = function() {
// Use the first argument if it's an Array, otherwise convert the arguments
// object to an array and use that.
var patterns = arguments[0] instanceof Array ? arguments[0] : util.toArray(arguments);
var urls = [];
// Filter all URLs out of patterns list and store them in a separate array.
patterns = patterns.filter(function(pattern) {
if (/^(?:file|https?):\/\//i.test(pattern)) {
// Push onto urls array.
urls.push(pattern);
// Remove from patterns array.
return false;
}
// Otherwise, keep pattern.
return true;
});
// Return expanded filepaths with urls at end.
return exports.expand(patterns).map(function(filepath) {
return 'file://' + path.resolve(filepath);
}).concat(urls);
};
// Like mkdir -p. Create a directory and any intermediary directories.
exports.mkdir = function(dirpath) {
var parts = [];
dirpath.split('/').forEach(function(part) {
parts.push(part);
var subpath = parts.join('/');
if (part && !path.existsSync(subpath)) {
fs.mkdirSync(subpath, '0755');
}
});
};
// Recurse into a directory, executing callback for each file.
exports.recurse = function(rootdir, callback, subdir) {
var abspath = subdir ? path.join(rootdir, subdir) : rootdir;
fs.readdirSync(abspath).forEach(function(filename) {
var filepath = path.join(abspath, filename);
if (fs.statSync(filepath).mode === 16877) {
exports.recurse(rootdir, callback, path.join(subdir, filename));
} else {
callback(path.join(rootdir, subdir, filename), rootdir, subdir, filename);
}
});
};
// Search for a filename in the given directory or all parent directories.
var root = path.resolve('/');
exports.findup = function(rootdir, filename) {
var filepath = path.join(rootdir, filename);
if (path.existsSync(filepath)) {
return filepath;
} else if (rootdir === root) {
return false;
} else {
return exports.findup(path.resolve(rootdir, '..'), filename);
}
};
// Write a file.
exports.write = function(filepath, contents) {
var nowrite = option('no-write');
verbose.write((nowrite ? 'Not actually writing ' : 'Writing ') + filepath + '...');
try {
if (!nowrite) {
// Create path, if necessary.
exports.mkdir(path.dirname(filepath));
// Actually write file.
fs.writeFileSync(filepath, contents);
}
verbose.ok();
return true;
} catch(e) {
verbose.error();
throw task.taskError('Unable to write "' + filepath + '" file (Error code: ' + e.code + ').', e);
}
};
// Read a file, return its contents.
exports.read = function(filepath, encoding) {
var src;
verbose.write('Reading ' + filepath + '...');
try {
src = fs.readFileSync(filepath, encoding ? null : 'utf8');
verbose.ok();
return src;
} catch(e) {
verbose.error();
throw task.taskError('Unable to read "' + filepath + '" file (Error code: ' + e.code + ').', e);
}
};
// Read a file, process its content (optionally), then write the output.
exports.copy = function(srcpath, destpath, callback) {
var src = exports.read(srcpath, true);
if (callback) {
verbose.write('Processing source...');
try {
src = callback(src.toString('utf8'));
verbose.ok();
} catch(e) {
verbose.error();
throw task.taskError('Error while processing "' + srcpath + '" file.', e);
}
}
exports.write(destpath, src);
};
// Read a file, parse its contents, return an object.
exports.readJson = function(filepath) {
var src = this.read(filepath);
var result;
verbose.write('Parsing ' + filepath + '...');
try {
result = JSON.parse(src);
verbose.ok();
return result;
} catch(e) {
verbose.error();
throw task.taskError('Unable to parse "' + filepath + '" file (' + e.message + ').', e);
}
};
// Clear the require cache for all passed filepaths.
exports.clearRequireCache = function() {
// If a non-string argument is passed, it's an array of filepaths, otherwise
// each filepath is passed individually.
var filepaths = typeof arguments[0] !== 'string' ? arguments[0] : util.toArray(arguments);
// For each filepath, clear the require cache, if necessary.
filepaths.forEach(function(filepath) {
var abspath = path.resolve(filepath);
if (require.cache[abspath]) {
verbose.write('Clearing require cache for "' + filepath + '" file...').ok();
delete require.cache[abspath];
}
});
};
// Access files in the user's ".grunt" folder.
exports.userpath = function(filepath) {
var win32 = process.platform === 'win32';
var homepath = process.env[win32 ? 'USERPROFILE' : 'HOME'];
return path.resolve(homepath, '.grunt', filepath);
};
// Get a list of task paths (or task-specific extraspaths).
exports.taskpaths = function() {
var args;
var taskpaths = [];
// Path to user's "tasks" files.
taskpaths.push(exports.userpath('tasks'));
// Paths to user-specified --tasks dirs.
var optiontasks = option('tasks') ? option('tasks').slice() : [];
taskpaths = taskpaths.concat(optiontasks.map(function(relpath) {
return path.resolve(relpath);
}));
// Path to built-in task files.
taskpaths.push(path.resolve(__dirname, '../../tasks'));
// If arguments were specified, join them to pathnames.
if (arguments.length > 0) {
args = util.toArray(arguments);
taskpaths = taskpaths.map(function(dirpath) {
return path.join.apply(path, [dirpath].concat(args));
});
}
// Return only directories that actually exist!
return taskpaths.filter(function(dirpath) {
return path.existsSync(dirpath) && fs.statSync(dirpath).isDirectory();
});
};
// Find all task files matching a given path.
exports.taskfiles = function() {
var filepath = path.join.apply(path, arguments);
var basename = path.basename(filepath);
return exports.taskpaths(path.dirname(filepath)).map(function(dirpath) {
return path.join(dirpath, basename);
}).filter(function(filepath) {
return path.existsSync(filepath);
});
};
// Find the first task file matching a given path.
exports.taskfile = function() {
return exports.taskfiles.apply(exports, arguments)[0];
};