-
Notifications
You must be signed in to change notification settings - Fork 107
/
Copy pathdirectory-tree.js
111 lines (96 loc) · 2.79 KB
/
directory-tree.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
'use strict';
const FS = require('fs');
const PATH = require('path');
const constants = {
DIRECTORY: 'directory',
FILE: 'file'
};
function safeReadDirSync (path) {
let dirData = {};
try {
dirData = FS.readdirSync(path);
} catch (ex) {
//User does not have permissions, ignore directory
if (ex.code == 'EACCES' || ex.code == 'EPERM') return null;
else throw ex;
}
return dirData;
}
/**
* Normalizes windows style paths by replacing double backslahes with single forward slahes (unix style).
* @param {string} path
* @return {string}
*/
function normalizePath(path) {
return path.replace(/\\/g, '/');
}
/**
* Tests if the supplied parameter is of type RegExp
* @param {any} regExp
* @return {Boolean}
*/
function isRegExp(regExp) {
return typeof regExp === 'object' && regExp.constructor == RegExp;
}
/**
* Collects the files and folders for a directory path into an Object, subject
* to the options supplied, and invoking optional
* @param {String} path
* @param {Object} options
* @param {function} onEachFile
* @param {function} onEachDirectory
* @return {Object}
*/
function directoryTree (path, options, onEachFile, onEachDirectory) {
const name = PATH.basename(path);
path = options && options.normalizePath ? normalizePath(path) : path;
const item = { path, name };
let stats;
try { stats = FS.statSync(path); }
catch (e) { return null; }
// Skip if it matches the exclude regex
if (options && options.exclude) {
const excludes = isRegExp(options.exclude) ? [options.exclude] : options.exclude;
if (excludes.some((exclusion) => exclusion.test(path))) {
return null;
}
}
if (stats.isFile()) {
const ext = PATH.extname(path).toLowerCase();
// Skip if it does not match the extension regex
if (options && options.extensions && !options.extensions.test(ext))
return null;
item.size = stats.size; // File size in bytes
item.extension = ext;
item.type = constants.FILE;
if (options && options.attributes) {
options.attributes.forEach((attribute) => {
item[attribute] = stats[attribute];
});
}
if (onEachFile) {
onEachFile(item, PATH, stats);
}
}
else if (stats.isDirectory()) {
let dirData = safeReadDirSync(path);
if (dirData === null) return null;
if (options && options.attributes) {
options.attributes.forEach((attribute) => {
item[attribute] = stats[attribute];
});
}
item.children = dirData
.map(child => directoryTree(PATH.join(path, child), options, onEachFile, onEachDirectory))
.filter(e => !!e);
item.size = item.children.reduce((prev, cur) => prev + cur.size, 0);
item.type = constants.DIRECTORY;
if (onEachDirectory) {
onEachDirectory(item, PATH, stats);
}
} else {
return null; // Or set item.size = 0 for devices, FIFO and sockets ?
}
return item;
}
module.exports = directoryTree;