index.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. 'use strict';
  2. var through2 = require('through2');
  3. var Combine = require('ordered-read-streams');
  4. var unique = require('unique-stream');
  5. var glob = require('glob');
  6. var micromatch = require('micromatch');
  7. var resolveGlob = require('to-absolute-glob');
  8. var globParent = require('glob-parent');
  9. var path = require('path');
  10. var extend = require('extend');
  11. var sepRe = (process.platform === 'win32' ? /[\/\\]/ : /\/+/);
  12. var gs = {
  13. // Creates a stream for a single glob or filter
  14. createStream: function(ourGlob, negatives, opt) {
  15. var ourOpt = extend({}, opt);
  16. delete ourOpt.root;
  17. // Extract base path from glob
  18. var basePath = ourOpt.base || getBasePath(ourGlob, opt);
  19. // Remove path relativity to make globs make sense
  20. ourGlob = resolveGlob(ourGlob, opt);
  21. // Create globbing stuff
  22. var globber = new glob.Glob(ourGlob, ourOpt);
  23. // Create stream and map events from globber to it
  24. var stream = through2.obj(opt,
  25. negatives.length ? filterNegatives : undefined);
  26. var found = false;
  27. globber.on('error', stream.emit.bind(stream, 'error'));
  28. globber.once('end', function() {
  29. if (opt.allowEmpty !== true && !found && globIsSingular(globber)) {
  30. stream.emit('error',
  31. new Error('File not found with singular glob: ' + ourGlob));
  32. }
  33. stream.end();
  34. });
  35. globber.on('match', function(filename) {
  36. found = true;
  37. stream.write({
  38. cwd: opt.cwd,
  39. base: basePath,
  40. path: path.normalize(filename),
  41. });
  42. });
  43. return stream;
  44. function filterNegatives(filename, enc, cb) {
  45. var matcha = isMatch.bind(null, filename);
  46. if (negatives.every(matcha)) {
  47. cb(null, filename); // Pass
  48. } else {
  49. cb(); // Ignore
  50. }
  51. }
  52. },
  53. // Creates a stream for multiple globs or filters
  54. create: function(globs, opt) {
  55. if (!opt) {
  56. opt = {};
  57. }
  58. if (typeof opt.cwd !== 'string') {
  59. opt.cwd = process.cwd();
  60. }
  61. if (typeof opt.dot !== 'boolean') {
  62. opt.dot = false;
  63. }
  64. if (typeof opt.silent !== 'boolean') {
  65. opt.silent = true;
  66. }
  67. if (typeof opt.nonull !== 'boolean') {
  68. opt.nonull = false;
  69. }
  70. if (typeof opt.cwdbase !== 'boolean') {
  71. opt.cwdbase = false;
  72. }
  73. if (opt.cwdbase) {
  74. opt.base = opt.cwd;
  75. }
  76. // Only one glob no need to aggregate
  77. if (!Array.isArray(globs)) {
  78. globs = [globs];
  79. }
  80. var positives = [];
  81. var negatives = [];
  82. var ourOpt = extend({}, opt);
  83. delete ourOpt.root;
  84. globs.forEach(function(glob, index) {
  85. if (typeof glob !== 'string' && !(glob instanceof RegExp)) {
  86. throw new Error('Invalid glob at index ' + index);
  87. }
  88. var globArray = isNegative(glob) ? negatives : positives;
  89. // Create Minimatch instances for negative glob patterns
  90. if (globArray === negatives && typeof glob === 'string') {
  91. var ourGlob = resolveGlob(glob, opt);
  92. glob = micromatch.matcher(ourGlob, ourOpt);
  93. }
  94. globArray.push({
  95. index: index,
  96. glob: glob,
  97. });
  98. });
  99. if (positives.length === 0) {
  100. throw new Error('Missing positive glob');
  101. }
  102. // Only one positive glob no need to aggregate
  103. if (positives.length === 1) {
  104. return streamFromPositive(positives[0]);
  105. }
  106. // Create all individual streams
  107. var streams = positives.map(streamFromPositive);
  108. // Then just pipe them to a single unique stream and return it
  109. var aggregate = new Combine(streams);
  110. var uniqueStream = unique('path');
  111. var returnStream = aggregate.pipe(uniqueStream);
  112. aggregate.on('error', function(err) {
  113. returnStream.emit('error', err);
  114. });
  115. return returnStream;
  116. function streamFromPositive(positive) {
  117. var negativeGlobs = negatives.filter(indexGreaterThan(positive.index))
  118. .map(toGlob);
  119. return gs.createStream(positive.glob, negativeGlobs, opt);
  120. }
  121. },
  122. };
  123. function isMatch(file, matcher) {
  124. if (typeof matcher === 'function') {
  125. return matcher(file.path);
  126. }
  127. if (matcher instanceof RegExp) {
  128. return matcher.test(file.path);
  129. }
  130. }
  131. function isNegative(pattern) {
  132. if (typeof pattern === 'string') {
  133. return pattern[0] === '!';
  134. }
  135. if (pattern instanceof RegExp) {
  136. return true;
  137. }
  138. }
  139. function indexGreaterThan(index) {
  140. return function(obj) {
  141. return obj.index > index;
  142. };
  143. }
  144. function toGlob(obj) {
  145. return obj.glob;
  146. }
  147. function globIsSingular(glob) {
  148. var globSet = glob.minimatch.set;
  149. if (globSet.length !== 1) {
  150. return false;
  151. }
  152. return globSet[0].every(function isString(value) {
  153. return typeof value === 'string';
  154. });
  155. }
  156. function getBasePath(ourGlob, opt) {
  157. var basePath;
  158. var parent = globParent(ourGlob);
  159. if (parent === '/' && opt && opt.root) {
  160. basePath = path.normalize(opt.root);
  161. } else {
  162. basePath = resolveGlob(parent, opt);
  163. }
  164. if (!sepRe.test(basePath.charAt(basePath.length - 1))) {
  165. basePath += path.sep;
  166. }
  167. return basePath;
  168. }
  169. module.exports = gs;