--- /dev/null
+/*
+ * globule
+ * https://github.com/cowboy/node-globule
+ *
+ * Copyright (c) 2013 "Cowboy" Ben Alman
+ * Licensed under the MIT license.
+ */
+
+'use strict';
+
+var fs = require('fs');
+var path = require('path');
+
+var _ = require('lodash');
+var glob = require('glob');
+var minimatch = require('minimatch');
+
+// The module.
+var globule = exports;
+
+// Process specified wildcard glob patterns or filenames against a
+// callback, excluding and uniquing files in the result set.
+function processPatterns(patterns, fn) {
+ return _.flatten(patterns).reduce(function(result, pattern) {
+ if (pattern.indexOf('!') === 0) {
+ // If the first character is ! all matches via this pattern should be
+ // removed from the result set.
+ pattern = pattern.slice(1);
+ return _.difference(result, fn(pattern));
+ } else {
+ // Otherwise, add all matching filepaths to the result set.
+ return _.union(result, fn(pattern));
+ }
+ }, []);
+}
+
+// Match a filepath or filepaths against one or more wildcard patterns. Returns
+// all matching filepaths. This behaves just like minimatch.match, but supports
+// any number of patterns.
+globule.match = function(patterns, filepaths, options) {
+ // Return empty set if either patterns or filepaths was omitted.
+ if (patterns == null || filepaths == null) { return []; }
+ // Normalize patterns and filepaths to arrays.
+ if (!_.isArray(patterns)) { patterns = [patterns]; }
+ if (!_.isArray(filepaths)) { filepaths = [filepaths]; }
+ // Return empty set if there are no patterns or filepaths.
+ if (patterns.length === 0 || filepaths.length === 0) { return []; }
+ // Return all matching filepaths.
+ return processPatterns(patterns, function(pattern) {
+ return minimatch.match(filepaths, pattern, options || {});
+ });
+};
+
+// Match a filepath or filepaths against one or more wildcard patterns. Returns
+// true if any of the patterns match.
+globule.isMatch = function() {
+ return globule.match.apply(null, arguments).length > 0;
+};
+
+// Return an array of all file paths that match the given wildcard patterns.
+globule.find = function() {
+ var args = _.toArray(arguments);
+ // If the last argument is an options object, remove it from args.
+ var options = _.isPlainObject(args[args.length - 1]) ? args.pop() : {};
+ // Use the first argument if it's an Array, otherwise use all arguments.
+ var patterns = _.isArray(args[0]) ? args[0] : args;
+ // Return empty set if there are no patterns or filepaths.
+ if (patterns.length === 0) { return []; }
+ var srcBase = options.srcBase || options.cwd;
+ // Create glob-specific options object.
+ var globOptions = _.extend({}, options);
+ if (srcBase) {
+ globOptions.cwd = srcBase;
+ }
+ // Get all matching filepaths.
+ var matches = processPatterns(patterns, function(pattern) {
+ return glob.sync(pattern, globOptions);
+ });
+ // If srcBase and prefixBase were specified, prefix srcBase to matched paths.
+ if (srcBase && options.prefixBase) {
+ matches = matches.map(function(filepath) {
+ return path.join(srcBase, filepath);
+ });
+ }
+ // Filter result set?
+ if (options.filter) {
+ matches = matches.filter(function(filepath) {
+ // If srcBase was specified but prefixBase was NOT, prefix srcBase
+ // temporarily, for filtering.
+ if (srcBase && !options.prefixBase) {
+ filepath = path.join(srcBase, filepath);
+ }
+ try {
+ if (_.isFunction(options.filter)) {
+ return options.filter(filepath, options);
+ } else {
+ // If the file is of the right type and exists, this should work.
+ return fs.statSync(filepath)[options.filter]();
+ }
+ } catch(err) {
+ // Otherwise, it's probably not the right type.
+ return false;
+ }
+ });
+ }
+ return matches;
+};
+
+var pathSeparatorRe = /[\/\\]/g;
+var extDotRe = {
+ first: /(\.[^\/]*)?$/,
+ last: /(\.[^\/\.]*)?$/,
+};
+function rename(dest, options) {
+ // Flatten path?
+ if (options.flatten) {
+ dest = path.basename(dest);
+ }
+ // Change the extension?
+ if (options.ext) {
+ dest = dest.replace(extDotRe[options.extDot], options.ext);
+ }
+ // Join dest and destBase?
+ if (options.destBase) {
+ dest = path.join(options.destBase, dest);
+ }
+ return dest;
+}
+
+// Build a mapping of src-dest filepaths from the given set of filepaths.
+globule.mapping = function(filepaths, options) {
+ // Return empty set if filepaths was omitted.
+ if (filepaths == null) { return []; }
+ options = _.defaults({}, options, {
+ extDot: 'first',
+ rename: rename,
+ });
+ var files = [];
+ var fileByDest = {};
+ // Find all files matching pattern, using passed-in options.
+ filepaths.forEach(function(src) {
+ // Generate destination filename.
+ var dest = options.rename(src, options);
+ // Prepend srcBase to all src paths.
+ if (options.srcBase) {
+ src = path.join(options.srcBase, src);
+ }
+ // Normalize filepaths to be unix-style.
+ dest = dest.replace(pathSeparatorRe, '/');
+ src = src.replace(pathSeparatorRe, '/');
+ // Map correct src path to dest path.
+ if (fileByDest[dest]) {
+ // If dest already exists, push this src onto that dest's src array.
+ fileByDest[dest].src.push(src);
+ } else {
+ // Otherwise create a new src-dest file mapping object.
+ files.push({
+ src: [src],
+ dest: dest,
+ });
+ // And store a reference for later use.
+ fileByDest[dest] = files[files.length - 1];
+ }
+ });
+ return files;
+};
+
+// Return a mapping of src-dest filepaths from files matching the given
+// wildcard patterns.
+globule.findMapping = function(patterns, options) {
+ return globule.mapping(globule.find(patterns, options), options);
+};