2020-05-19 11:43:42 +03:00

114 lines
2.8 KiB
JavaScript

'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) {
if (ex.code == "EACCES" || ex.code == "EPERM") {
//User does not have permissions, ignore directory
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;