Skip to content

Added support for maxDepth #67

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 90 additions & 75 deletions lib/directory-tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@
const FS = require('fs');
const PATH = require('path');
const constants = {
DIRECTORY: 'directory',
FILE: 'file'
DIRECTORY: 'directory',
FILE: 'file'
}

function safeReadDirSync (path) {
let dirData = {};
try {
dirData = FS.readdirSync(path);
} catch(ex) {
if (ex.code == "EACCES")
//User does not have permissions, ignore directory
return null;
else throw ex;
}
return dirData;
function safeReadDirSync(path) {
let dirData = {};
try {
dirData = FS.readdirSync(path);
} catch (ex) {
if (ex.code == "EACCES")
//User does not have permissions, ignore directory
return null;
else throw ex;
}
return dirData;
}

/**
Expand All @@ -26,7 +26,7 @@ function safeReadDirSync (path) {
* @return {string}
*/
function normalizePath(path) {
return path.replace(/\\/g, '/');
return path.replace(/\\/g, '/');
}

/**
Expand All @@ -35,7 +35,7 @@ function normalizePath(path) {
* @return {Boolean}
*/
function isRegExp(regExp) {
return typeof regExp === "object" && regExp.constructor == RegExp;
return typeof regExp === "object" && regExp.constructor == RegExp;
}

/**
Expand All @@ -47,66 +47,81 @@ function isRegExp(regExp) {
* @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;
function directoryTree(path, options, onEachFile, onEachDirectory) {
const name = PATH.basename(path);
path = options && options.normalizePath ? normalizePath(path) : path;

// Validate maxDepth value
let maxDepth = (options && options.maxDepth !== null && isFinite(options.maxDepth)) ? options.maxDepth : 99999999;

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];
});
}

// Decrease depth for next directory items
if (maxDepth !== null && maxDepth > 0) {
options.maxDepth = options.maxDepth - 1;
}

let children = dirData
.map(child => directoryTree(PATH.join(path, child), options, onEachFile, onEachDirectory))
.filter(e => !!e);
item.size = children.reduce((prev, cur) => prev + cur.size, 0);
item.type = constants.DIRECTORY;
if (onEachDirectory) {
onEachDirectory(item, PATH, stats);
}

// If depth is not reached, then add childrens, else set blank array
if (maxDepth !== null && maxDepth > 0) {
item.children = children;
} else {
item.children = [];
}
} else {
return null; // Or set item.size = 0 for devices, FIFO and sockets ?
}
return item;
}

module.exports = directoryTree;