3 * https://github.com/cowboy/node-globule
5 * Copyright (c) 2013 "Cowboy" Ben Alman
6 * Licensed under the MIT license.
11 var fs = require('fs');
12 var path = require('path');
14 var _ = require('lodash');
15 var glob = require('glob');
16 var minimatch = require('minimatch');
19 var globule = exports;
21 // Process specified wildcard glob patterns or filenames against a
22 // callback, excluding and uniquing files in the result set.
23 function processPatterns(patterns, fn) {
24 return _.flatten(patterns).reduce(function(result, pattern) {
25 if (pattern.indexOf('!') === 0) {
26 // If the first character is ! all matches via this pattern should be
27 // removed from the result set.
28 pattern = pattern.slice(1);
29 return _.difference(result, fn(pattern));
31 // Otherwise, add all matching filepaths to the result set.
32 return _.union(result, fn(pattern));
37 // Match a filepath or filepaths against one or more wildcard patterns. Returns
38 // all matching filepaths. This behaves just like minimatch.match, but supports
39 // any number of patterns.
40 globule.match = function(patterns, filepaths, options) {
41 // Return empty set if either patterns or filepaths was omitted.
42 if (patterns == null || filepaths == null) { return []; }
43 // Normalize patterns and filepaths to arrays.
44 if (!_.isArray(patterns)) { patterns = [patterns]; }
45 if (!_.isArray(filepaths)) { filepaths = [filepaths]; }
46 // Return empty set if there are no patterns or filepaths.
47 if (patterns.length === 0 || filepaths.length === 0) { return []; }
48 // Return all matching filepaths.
49 return processPatterns(patterns, function(pattern) {
50 return minimatch.match(filepaths, pattern, options || {});
54 // Match a filepath or filepaths against one or more wildcard patterns. Returns
55 // true if any of the patterns match.
56 globule.isMatch = function() {
57 return globule.match.apply(null, arguments).length > 0;
60 // Return an array of all file paths that match the given wildcard patterns.
61 globule.find = function() {
62 var args = _.toArray(arguments);
63 // If the last argument is an options object, remove it from args.
64 var options = _.isPlainObject(args[args.length - 1]) ? args.pop() : {};
65 // Use the first argument if it's an Array, otherwise use all arguments.
66 var patterns = _.isArray(args[0]) ? args[0] : args;
67 // Return empty set if there are no patterns or filepaths.
68 if (patterns.length === 0) { return []; }
69 var srcBase = options.srcBase || options.cwd;
70 // Create glob-specific options object.
71 var globOptions = _.extend({}, options);
73 globOptions.cwd = srcBase;
75 // Get all matching filepaths.
76 var matches = processPatterns(patterns, function(pattern) {
77 return glob.sync(pattern, globOptions);
79 // If srcBase and prefixBase were specified, prefix srcBase to matched paths.
80 if (srcBase && options.prefixBase) {
81 matches = matches.map(function(filepath) {
82 return path.join(srcBase, filepath);
87 matches = matches.filter(function(filepath) {
88 // If srcBase was specified but prefixBase was NOT, prefix srcBase
89 // temporarily, for filtering.
90 if (srcBase && !options.prefixBase) {
91 filepath = path.join(srcBase, filepath);
94 if (_.isFunction(options.filter)) {
95 return options.filter(filepath, options);
97 // If the file is of the right type and exists, this should work.
98 return fs.statSync(filepath)[options.filter]();
101 // Otherwise, it's probably not the right type.
109 var pathSeparatorRe = /[\/\\]/g;
111 first: /(\.[^\/]*)?$/,
112 last: /(\.[^\/\.]*)?$/,
114 function rename(dest, options) {
116 if (options.flatten) {
117 dest = path.basename(dest);
119 // Change the extension?
121 dest = dest.replace(extDotRe[options.extDot], options.ext);
123 // Join dest and destBase?
124 if (options.destBase) {
125 dest = path.join(options.destBase, dest);
130 // Build a mapping of src-dest filepaths from the given set of filepaths.
131 globule.mapping = function(filepaths, options) {
132 // Return empty set if filepaths was omitted.
133 if (filepaths == null) { return []; }
134 options = _.defaults({}, options, {
140 // Find all files matching pattern, using passed-in options.
141 filepaths.forEach(function(src) {
142 // Generate destination filename.
143 var dest = options.rename(src, options);
144 // Prepend srcBase to all src paths.
145 if (options.srcBase) {
146 src = path.join(options.srcBase, src);
148 // Normalize filepaths to be unix-style.
149 dest = dest.replace(pathSeparatorRe, '/');
150 src = src.replace(pathSeparatorRe, '/');
151 // Map correct src path to dest path.
152 if (fileByDest[dest]) {
153 // If dest already exists, push this src onto that dest's src array.
154 fileByDest[dest].src.push(src);
156 // Otherwise create a new src-dest file mapping object.
161 // And store a reference for later use.
162 fileByDest[dest] = files[files.length - 1];
168 // Return a mapping of src-dest filepaths from files matching the given
169 // wildcard patterns.
170 globule.findMapping = function(patterns, options) {
171 return globule.mapping(globule.find(patterns, options), options);