index.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // requireDir.js
  2. // See README.md for details.
  3. var fs = require('fs');
  4. var path = require('path');
  5. // make a note of the calling file's path, so that we can resolve relative
  6. // paths. this only works if a fresh version of this module is run on every
  7. // require(), so important: we clear the require() cache each time!
  8. var parent = module.parent;
  9. var parentFile = parent.filename;
  10. var parentDir = path.dirname(parentFile);
  11. delete require.cache[__filename];
  12. module.exports = function requireDir(dir, opts) {
  13. // default arguments:
  14. dir = dir || '.';
  15. opts = opts || {};
  16. // resolve the path to an absolute one:
  17. dir = path.resolve(parentDir, dir);
  18. // read the directory's files:
  19. // note that this'll throw an error if the path isn't a directory.
  20. var files = fs.readdirSync(dir);
  21. // to prioritize between multiple files with the same basename, we'll
  22. // first derive all the basenames and create a map from them to files:
  23. var filesForBase = {};
  24. for (var i = 0; i < files.length; i++) {
  25. var file = files[i];
  26. var ext = path.extname(file);
  27. var base = path.basename(file, ext);
  28. (filesForBase[base] = filesForBase[base] || []).push(file);
  29. }
  30. // then we'll go through each basename, and first check if any of the
  31. // basenames' files are directories, since directories take precedence if
  32. // we're recursing and can be ignored if we're not. if a basename has no
  33. // directory, then we'll follow Node's own require() algorithm of going
  34. // through and trying the require.extension keys in order. in the process,
  35. // we create and return a map from basename to require()'d contents! and
  36. // if duplicates are asked for, we'll never short-circuit; we'll just add
  37. // to the map using the full filename as a key also.
  38. var map = {};
  39. // get the array of extensions we need to require
  40. var extensions = opts.extensions || Object.keys(require.extensions);
  41. for (var base in filesForBase) {
  42. // protect against enumerable object prototype extensions:
  43. if (!filesForBase.hasOwnProperty(base)) {
  44. continue;
  45. }
  46. // go through the files for this base and check for directories. we'll
  47. // also create a hash "set" of the non-dir files so that we can
  48. // efficiently check for existence in the next step:
  49. var files = filesForBase[base];
  50. var filesMinusDirs = {};
  51. for (var i = 0; i < files.length; i++) {
  52. var file = files[i];
  53. var abs = path.resolve(dir, file);
  54. // ignore the calling file:
  55. if (abs === parentFile) {
  56. continue;
  57. }
  58. // apply file filter:
  59. if (opts.filter && !opts.filter(abs)) {
  60. continue;
  61. }
  62. if (fs.statSync(abs).isDirectory()) {
  63. if (opts.recurse) {
  64. if (base === 'node_modules') {
  65. continue;
  66. }
  67. map[base] = requireDir(abs, opts);
  68. // if duplicates are wanted, key off the full name too:
  69. if (opts.duplicates) {
  70. map[file] = map[base];
  71. }
  72. }
  73. } else {
  74. filesMinusDirs[file] = abs;
  75. }
  76. }
  77. // if we're recursing and we already encountered a directory for this
  78. // basename, we're done for this base if we're ignoring duplicates:
  79. if (map[base] && !opts.duplicates) {
  80. continue;
  81. }
  82. // otherwise, go through and try each require.extension key!
  83. for (ext of extensions) {
  84. // if a file exists with this extension, we'll require() it:
  85. var file = base + ext;
  86. var abs = filesMinusDirs[file];
  87. if (abs) {
  88. // ignore TypeScript declaration files. They should never be
  89. // `require`d
  90. if (/\.d\.ts$/.test(abs)) {
  91. continue;
  92. }
  93. // delete cache
  94. if (opts.noCache) {
  95. delete require.cache[abs];
  96. }
  97. // if duplicates are wanted, key off the full name always, and
  98. // also the base if it hasn't been taken yet (since this ext
  99. // has higher priority than any that follow it). if duplicates
  100. // aren't wanted, we're done with this basename.
  101. if (opts.duplicates) {
  102. map[file] = require(abs);
  103. if (!map[base]) {
  104. map[base] = map[file];
  105. }
  106. } else {
  107. map[base] = require(abs);
  108. break;
  109. }
  110. }
  111. }
  112. }
  113. if (opts.mapKey || opts.mapValue) {
  114. for (var base in map) {
  115. // protect against enumerable object prototype extensions:
  116. if (!map.hasOwnProperty(base)) {
  117. continue;
  118. }
  119. var newKey = opts.mapKey ? opts.mapKey(map[base], base) : base;
  120. var newVal = opts.mapValue ? opts.mapValue(map[base], newKey) : map[base];
  121. delete map[base];
  122. map[newKey] = newVal;
  123. }
  124. }
  125. return map;
  126. };