-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
138 lines (122 loc) · 4.08 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
'use strict';
const fs = require('fs');
const nodeRequire = require;
const File = require('./lib/file');
/**
* Add a property to an object with a value
*
* @param {Object} object
* @param {*} value
* @param {File} file
* @param {Function} transform A transform function that takes the value and the file
* If `transform` returns a function, then the object will use it as a getter
*
*/
function defineProperty (object, file, transform, value) {
const property = { enumerable: true };
const result = transform(file, value);
if (result !== undefined) {
// If dirTransform returns a function, then use it as a getter, otherwise just return the value
property[( result instanceof Function ) ? 'get' : 'value'] = result;
return Object.defineProperty(object, file.key || file.basename, property);
}
}
/**
* Make sure transform functions are defined
*
* @param {Object} [options]
* @param {RegExp|Function} [options.filter]
* @param {Function} [options.dirTransform]
* @param {Function} [options.fileTransform]
*/
function defaultOptions (options) {
const opts = {};
if (!(options instanceof Object)) {
options = {};
}
if (options.filter instanceof RegExp) {
opts.filter = file => opts.filter.test(file.basename);
} else if (options.filter instanceof Function) {
opts.filter = options.filter;
} else {
opts.filter = file => (
file.isDirectory || file.isRequirable
);
}
if (options.dirTransform instanceof Function) {
opts.dirTransform = options.dirTransform;
} else {
opts.dirTransform = (f, v) => v;
}
if (options.fileTransform instanceof Function) {
opts.fileTransform = options.fileTransform;
} else {
opts.fileTransform = f => nodeRequire(f.fullpath);
}
return opts;
}
/**
* Read a directory tree and return an object
*
* @param {String} dir The directory to read. Can be relative or absolute.
* @param {Object} [options]
* @param {Boolean} [options.dirTransform] A function called with the object result from scanning a directory
* @param {Function} [options.fileTransform] A function called with one argument that is the result of require(file)
* @returns {Promise.<Object>}
*/
function readDirectoryAsync (dir, options) {
const opts = defaultOptions(options);
return new Promise((resolve, reject) => {
fs.readdir(dir, (err, files) => {
if (err) { return reject(err); }
if (!Array.isArray(files)) { return resolve({}); }
let promises = [];
const result = files.reduce((accum, name) => {
const file = new File(dir, name);
if (opts.filter(file)) {
if (file.isDirectory) {
promises.push(readDirectoryAsync(file.fullpath, options)
.then(obj => defineProperty(accum, file, opts.dirTransform, obj)));
} else {
defineProperty(accum, file, opts.fileTransform);
}
}
return accum;
}, {});
Promise.all(promises).then(() => resolve(result));
});
});
}
/**
* Read a directory tree and return a object
*
* @param {String} dir The directory to read. Can be relative or absolute.
* @param {Object} [options]
* @param {Function} [options.dirTransform] A function called with the object result from scanning a directory
* @param {Function} [options.fileTransform] A function called with one argument that is the result of require(file)
* @returns {Object}
*
* @example
* readDirectory('./fixtures', { dirTransform: Object.freeze, fileTransform: v => () => _.cloneDeep(v) })
* .then(console.log)
*/
function readDirectory (dir, options) {
const opts = defaultOptions(options);
const files = fs.readdirSync(dir);
if (!Array.isArray(files)) { return {}; }
return files.reduce((accum, name) => {
const file = new File(dir, name);
if (opts.filter(file)) {
if (file.isDirectory) { // Recurse into sub-directories
defineProperty(accum, file, opts.dirTransform, readDirectory(file.fullpath, options));
} else {
defineProperty(accum, file, opts.fileTransform);
}
}
return accum;
}, {});
}
module.exports = {
readDirectory,
readDirectoryAsync
};