readfiles.js 4.5 KB

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