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