Directory Inspection Tool
Swipe to show menu
This chapter presents you with a challenge: creating an advanced console app named DirInspect Pro. This app will empower you to thoroughly analyze any directory and gain insightful statistics about its files and subdirectories.
Challenge
Picture a scenario where you must navigate a labyrinth of folders containing crucial files and data. DirInspect Pro is your ally in this journey, providing comprehensive insights into the directory's structure and contents.
The Resulting App
Get ready to wield DirInspect Pro's capabilities. The app will furnish you with critical information, such as
- The total number of items;
- The aggregate size of all items;
- The largest file's name and size;
- The detailed list of individual file names and sizes.
Two Paths to Choose
You have two paths ahead.
- The first is to tackle this challenge head-on, honing your skills without guidance;
- The second is to follow a helpful guide that guarantees your success.
Whichever path you choose, you're in for a rewarding journey culminating in creating a captivating and functional console app.
Masterplan
- Step 1: Import Required Modules;
- Step 2: Define getStats Function;
- Step 3: Define analyzeFile Function;
- Step 4: Define analyzeDirectory Function;
- Step 5: Define main Function and Invoke;
- Conclusion;
- Full App Code.
Step 1: Import Required Modules
To embark on this adventure, you'll need the right tools. Begin by importing two key modules: fs.promises to manage the file system asynchronously and path to handle file paths effectively.
const fs = require("fs").promises;
const path = require("path");
- Line 1: imports the built-in
fsmodule. The.promisesproperty provides access to the promises-based API, allowing asynchronous file operations to be written usingasync/await; - Line 2: imports the built-in
pathmodule. This module provides utilities for working with file and directory paths, including path manipulation and normalization.
Step 2: Define getStats Function
The asynchronous function, getStats, takes a file or directory path as an argument and attempts to retrieve its statistics using fs.stat.
- If successful, it returns the statistics;
- If an error occurs, it logs an error message and returns
null.
async function getStats(filePath) {
try {
const stats = await fs.stat(filePath);
return stats;
} catch (err) {
console.error("Error getting stats:", err.message);
return null;
}
}
- Line 1: declares an asynchronous function named
getStatsthat accepts afilePathparameter; - Lines 2, 5: create a
try...catchblock used for error handling during the asynchronous operation; - Line 3: uses
await fs.stat(filePath)to retrieve information about the specified file, such as its size, creation time, and other metadata; - Line 4: returns the
statsobject if thefs.stat()operation completes successfully; - Line 6: logs an error message to the console using
console.error()if the operation fails; - Line 7: returns
nullto indicate that the file statistics could not be retrieved.
Step 3: Define analyzeFile Function
The analyzeFile function uses the getStats function to obtain statistics for a file. If statistics are available (not null), it returns an object containing the file's name (extracted using path.basename) and its size.
async function analyzeFile(filePath) {
const stats = await getStats(filePath);
if (!stats) return null;
return {
name: path.basename(filePath),
size: stats.size,
};
}
- Line 1: declares an asynchronous function named
analyzeFilethat accepts afilePathparameter; - Line 2: uses
await getStats(filePath)to retrieve file statistics for the provided path; - Line 3: checks whether
statsis falsy. If the statistics could not be retrieved, the function returnsnull; - Lines 5-8: create and return an object containing the file name and file size. The file name is extracted using
path.basename(filePath), while the size is retrieved fromstats.size.
Step 4: Define analyzeDirectory Function
The analyzeDirectory function scans a directory and gathers full statistics about its contents.
How it works: it reads all items inside the directory using fs.readdir. For each item:
- Builds the full path with
path.join; - Uses
getStatsto detect whether it's a file or a folder; - If it's a file:
- Calls
analyzeFileto get{ name, size }; - Updates totals, largest file, and the file list.
- Calls
- If it's a directory:
- Recursively calls
analyzeDirectory; - Merges results into the current statistics.
- Recursively calls
async function analyzeDirectory(directoryPath) {
let totalItems = 0;
let totalFiles = 0;
let totalSize = 0;
let largestFile = { name: "", size: 0 };
let fileList = [];
try {
const items = await fs.readdir(directoryPath);
for (const item of items) {
const itemPath = path.join(directoryPath, item);
const stats = await getStats(itemPath);
if (!stats) continue;
totalItems++;
if (stats.isFile()) {
const fileInfo = await analyzeFile(itemPath);
if (!fileInfo) continue;
totalFiles++;
totalSize += fileInfo.size;
if (fileInfo.size > largestFile.size) {
largestFile = fileInfo;
}
fileList.push(fileInfo);
} else if (stats.isDirectory()) {
const subDirectoryStats = await analyzeDirectory(itemPath);
totalItems += subDirectoryStats.totalItems;
totalFiles += subDirectoryStats.totalFiles;
totalSize += subDirectoryStats.totalSize;
if (subDirectoryStats.largestFile.size > largestFile.size) {
largestFile = subDirectoryStats.largestFile;
}
fileList = fileList.concat(subDirectoryStats.fileList);
}
}
return {
totalItems,
totalFiles,
totalSize,
largestFile,
fileList
};
} catch (err) {
console.error("Error analyzing directory contents:", err.message);
return {
totalItems: 0,
totalFiles: 0,
totalSize: 0,
largestFile: { name: "", size: 0 },
fileList: []
};
}
}
-
Line 1: declares an asynchronous function named
analyzeDirectorythat accepts adirectoryPathparameter; -
Lines 2-6: initialize variables used to store directory statistics, including total items, total files, total size, the largest file, and a list of analyzed files;
-
Line 8: starts a
tryblock to handle potential filesystem errors; -
Line 9: reads the contents of the directory using
fs.readdir()and stores the result in theitemsarray; -
Line 11: starts a
for...ofloop to iterate through each item in the directory; -
Line 12: creates the full path for the current item using
path.join(); -
Line 13: retrieves statistics for the current item using
getStats(); -
Line 14: skips the current item if its statistics could not be retrieved;
-
Line 16: increments
totalItemsfor every successfully processed file or directory; -
Lines 18-29: handle file processing:
- Line 19: calls
analyzeFile()to retrieve the file name and size; - Line 20: skips the file if the analysis fails;
- Lines 22-23: update the total file count and accumulated file size;
- Lines 25-27: update
largestFileif the current file is larger than the previously recorded one; - Line 29: adds the file information to
fileList;
- Line 19: calls
-
Lines 30-42: handle directory processing:
- Line 31: recursively calls
analyzeDirectory()for the subdirectory; - Lines 33-35: merge the subdirectory statistics into the current totals;
- Lines 37-39: update
largestFileif the subdirectory contains a larger file; - Line 41: merges file lists using
concat();
- Line 31: recursively calls
-
Lines 45-50: return an object containing the final directory statistics;
-
Lines 52-62: handle errors by logging a message and returning a default statistics object.
Step 5: Define main Function and Invoke
The main function is the entry point of the script. It specifies the directory path to analyze (in this case, ./docs), calls the analyzeDirectory function to obtain the statistics of the directory and its contents, and then outputs the collected information.
The function prints out
- The total number of items;
- The total number of files;
- The total size;
- The details about the largest file;
- The list of files in the directory.
async function main() {
const directoryPath = "./docs";
const directoryStats = await analyzeDirectory(directoryPath);
console.log("Directory Analysis:");
console.log("Total items:", directoryStats.totalItems);
console.log("Total files:", directoryStats.totalFiles);
console.log("Total size (bytes):", directoryStats.totalSize);
console.log(
"Largest file:",
directoryStats.largestFile.name,
"Size:",
directoryStats.largestFile.size,
"bytes"
);
console.log("\nFile List:");
for (const file of directoryStats.fileList) {
console.log(file.name, "Size:", file.size, "bytes");
}
}
main();
-
Line 1: declares an asynchronous function named
main; -
Line 2: defines the
directoryPathvariable, which stores the path to the directory that will be analyzed; -
Line 3: calls
analyzeDirectory()with the specifieddirectoryPathand stores the result in thedirectoryStatsvariable usingawait; -
Lines 5-17: display the calculated directory statistics using
console.log(), including the total number of items, total files, total size, and information about the largest file; -
Lines 18-20: iterate through the
fileListarray stored indirectoryStats;- log the name and size of each file in the directory;
-
Line 23: calls the
main()function to start the analysis process.
Full App Code
const fs = require("fs").promises;
const path = require("path");
async function getStats(filePath) {
try {
const stats = await fs.stat(filePath);
return stats;
} catch (err) {
console.error("Error getting stats:", err.message);
return null;
}
}
async function analyzeFile(filePath) {
const stats = await getStats(filePath);
if (!stats) return null;
return {
name: path.basename(filePath),
size: stats.size
};
}
async function analyzeDirectory(directoryPath) {
let totalItems = 0;
let totalFiles = 0;
let totalSize = 0;
let largestFile = { name: "", size: 0 };
let fileList = [];
try {
const items = await fs.readdir(directoryPath);
for (const item of items) {
const itemPath = path.join(directoryPath, item);
const stats = await getStats(itemPath);
if (!stats) continue;
totalItems++;
if (stats.isFile()) {
const fileInfo = await analyzeFile(itemPath);
if (!fileInfo) continue;
totalFiles++;
totalSize += fileInfo.size;
if (fileInfo.size > largestFile.size) {
largestFile = fileInfo;
}
fileList.push(fileInfo);
} else if (stats.isDirectory()) {
const subDirectoryStats = await analyzeDirectory(itemPath);
totalItems += subDirectoryStats.totalItems;
totalFiles += subDirectoryStats.totalFiles;
totalSize += subDirectoryStats.totalSize;
if (subDirectoryStats.largestFile.size > largestFile.size) {
largestFile = subDirectoryStats.largestFile;
}
fileList = fileList.concat(subDirectoryStats.fileList);
}
}
return {
totalItems,
totalFiles,
totalSize,
largestFile,
fileList
};
} catch (err) {
console.error("Error analyzing directory contents:", err.message);
return {
totalItems: 0,
totalFiles: 0,
totalSize: 0,
largestFile: { name: "", size: 0 },
fileList: []
};
}
}
async function main() {
const directoryPath = "./docs";
const directoryStats = await analyzeDirectory(directoryPath);
console.log("Directory Analysis:");
console.log("Total items:", directoryStats.totalItems);
console.log("Total files:", directoryStats.totalFiles);
console.log("Total size (bytes):", directoryStats.totalSize);
console.log(
"Largest file:",
directoryStats.largestFile.name,
"Size:",
directoryStats.largestFile.size,
"bytes"
);
console.log("\nFile List:");
for (const file of directoryStats.fileList) {
console.log(file.name, "Size:", file.size, "bytes");
}
}
main();
Thanks for your feedback!
Ask AI
Ask AI
Ask anything or try one of the suggested questions to begin our chat