globule.js 5.5 KB

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