readfilesstream.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. var fs = require('fs'),
  2. mm = require('minimatch'),
  3. path = require('path');
  4. /**
  5. * merge two objects by extending target object with source object
  6. * @param target object to merge
  7. * @param source object to merge
  8. * @param {Boolean} [modify] whether to modify the target
  9. * @returns {Object} extended object
  10. */
  11. function extend(target, source, modify) {
  12. var result = target ? modify ? target : extend({}, target, true) : {};
  13. if (!source) return result;
  14. for (var key in source) {
  15. if (source.hasOwnProperty(key) && source[key] !== undefined) {
  16. result[key] = source[key];
  17. }
  18. }
  19. return result;
  20. }
  21. /**
  22. * determine if a string is contained within an array or matches a regular expression
  23. * @param {String} str string to match
  24. * @param {Array|Regex} match array or regular expression to match against
  25. * @returns {Boolean} whether there is a match
  26. */
  27. function matches(str, match) {
  28. if (Array.isArray(match)) {
  29. var l = match.length;
  30. for( var s=0; s < l; s++) {
  31. if ( mm(str,match[s])) {
  32. return true;
  33. }
  34. }
  35. return false;
  36. }
  37. return match.test(str);
  38. }
  39. /**
  40. * read files and call a function with the contents of each file
  41. * @param {String} dir path of dir containing the files to be read
  42. * @param {String} encoding file encoding (default is 'utf8')
  43. * @param {Object} options options hash for encoding, recursive, and match/exclude
  44. * @param {Function(error, string)} callback callback for each files content
  45. * @param {Function(error)} complete fn to call when finished
  46. */
  47. function readFilesStream(dir, options, callback, complete) {
  48. if (typeof options === 'function') {
  49. complete = callback;
  50. callback = options;
  51. options = {};
  52. }
  53. if (typeof options === 'string') options = {
  54. encoding: options
  55. };
  56. options = extend({
  57. recursive: true,
  58. encoding: 'utf8',
  59. doneOnErr: true
  60. }, options);
  61. var files = [];
  62. var done = function(err) {
  63. if (typeof complete === 'function') {
  64. if (err) return complete(err);
  65. complete(null, files);
  66. }
  67. };
  68. fs.readdir(dir, function(err, list) {
  69. if (err)  {
  70. if (options.doneOnErr === true) {
  71. if (err.code === 'EACCES') return done();
  72. return done(err);
  73. }
  74. }
  75. var i = 0;
  76. if (options.reverse === true ||
  77. (typeof options.sort == 'string' &&
  78. (/reverse|desc/i).test(options.sort))) {
  79. list = list.reverse();
  80. } else if (options.sort !== false) list = list.sort();
  81. (function next() {
  82. var filename = list[i++];
  83. if (!filename) return done(null, files);
  84. var file = path.join(dir, filename);
  85. fs.stat(file, function(err, stat) {
  86. if (err && options.doneOnErr === true) return done(err);
  87. if (stat && stat.isDirectory()) {
  88. if (options.recursive) {
  89. if (options.matchDir && !matches(filename, options.matchDir)) return next();
  90. if (options.excludeDir && matches(filename, options.excludeDir)) return next();
  91. readFilesStream(file, options, callback, function(err, sfiles) {
  92. if (err && options.doneOnErr === true) return done(err);
  93. files = files.concat(sfiles);
  94. next();
  95. });
  96. } else next();
  97. } else if (stat && stat.isFile()) {
  98. if (options.match && !matches(filename, options.match)) return next();
  99. if (options.exclude && matches(filename, options.exclude)) return next();
  100. if (options.filter && !options.filter(filename)) return next();
  101. if (options.shortName) files.push(filename);
  102. else files.push(file);
  103. var stream = fs.createReadStream(file);
  104. if (options.encoding !== null) {
  105. stream.setEncoding(options.encoding);
  106. }
  107. stream.on('error',function(err) {
  108. if (options.doneOnErr === true) return done(err);
  109. next();
  110. });
  111. if (callback.length > 3)
  112. if (options.shortName) callback(null, stream, filename, next);
  113. else callback(null, stream, file, next);
  114. else callback(null, stream, next);
  115. }
  116. else {
  117. next();
  118. }
  119. });
  120. })();
  121. });
  122. }
  123. module.exports = readFilesStream;