Chapter 11 - Organizing Files (JavaScript)
Here's a JavaScript-flavoured version of the same concepts, with small JS examples for each idea.
Organizing files (intro)
Chapter goal: automate copying, renaming, moving, deleting, and compressing many files/folders instead of doing it manually in a file explorer.
Example scenario: copy only all PDFs in a tree:
const fs = require("fs");
const path = require("path");
const os = require("os");
const root = path.join(os.homedir(), "project");
const dest = path.join(os.homedir(), "pdf_backup");
fs.mkdirSync(dest, { recursive: true });
function copyPdfs(dir) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
copyPdfs(full);
} else if (entry.name.endsWith(".pdf")) {
fs.copyFileSync(full, path.join(dest, entry.name));
}
}
}
copyPdfs(root);
The fs module (like shutil)
Node.js fs provides high-level file operations: copy, move/rename, and delete.
Copying files and folders
fs.copyFileSync(src, dst)copies a single file.fs.cpSync(src, dst, { recursive: true })copies a whole directory tree (Node 16.7+).
const fs = require("fs");
const path = require("path");
const os = require("os");
const h = os.homedir();
fs.copyFileSync(
path.join(h, "spam", "file1.txt"),
path.join(h, "file1.txt")
);
fs.copyFileSync(
path.join(h, "spam", "file1.txt"),
path.join(h, "spam", "file2.txt")
);
fs.cpSync(path.join(h, "spam"), path.join(h, "spam_backup"), { recursive: true });
Moving and renaming files and folders
fs.renameSync(src, dst) moves and optionally renames a file/folder.
const fs = require("fs");
const path = require("path");
const os = require("os");
const h = os.homedir();
fs.mkdirSync(path.join(h, "spam2"), { recursive: true });
fs.renameSync(
path.join(h, "spam", "file1.txt"),
path.join(h, "spam2", "file1.txt")
);
fs.renameSync(
path.join(h, "spam", "file1.txt"),
path.join(h, "spam2", "new_name.txt")
);
Permanently deleting files and folders
fs.unlinkSync(path)deletes a file.fs.rmdirSync(path)deletes an empty folder.fs.rmSync(path, { recursive: true })deletes a folder tree.
Dry-run pattern:
const fs = require("fs");
const path = require("path");
const os = require("os");
for (const f of fs.readdirSync(os.homedir())) {
if (f.endsWith(".txt")) {
const full = path.join(os.homedir(), f);
// fs.unlinkSync(full); // real delete
console.log("Deleting", full);
}
}
Deleting to the recycle bin
Use the trash npm package to move to recycle bin instead of permanent delete.
const trash = require("trash");
await trash(["file1.txt"]);
Walking a directory tree
fs.readdirSync (like os.listdir / iterdir)
fs.readdirSync(path)returns an array of names.- With
{ withFileTypes: true }you getDirentobjects with.isFile()/.isDirectory().
const fs = require("fs");
const os = require("os");
console.log(fs.readdirSync(os.homedir()));
Recursive walk (like os.walk)
JavaScript has no built-in os.walk, but a recursive function with readdirSync does the same.
const fs = require("fs");
const path = require("path");
const os = require("os");
function walk(dir) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
const subfolders = entries.filter((e) => e.isDirectory()).map((e) => e.name);
const files = entries.filter((e) => e.isFile()).map((e) => e.name);
console.log("The current folder is", dir);
for (const sf of subfolders) console.log(" SUBFOLDER:", sf);
for (const f of files) console.log(" FILE:", f);
for (const sf of subfolders) {
walk(path.join(dir, sf));
}
}
walk(path.join(os.homedir(), "spam"));
Example: renaming all files to uppercase
Inside the walk, use fs.renameSync to rename each file.
const fs = require("fs");
const path = require("path");
const os = require("os");
function renameUpper(dir) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
renameUpper(full);
} else {
fs.renameSync(full, path.join(dir, entry.name.toUpperCase()));
}
}
}
renameUpper(path.join(os.homedir(), "spam"));
Compressing files with adm-zip (like zipfile)
Node.js has no built-in zip module. The adm-zip package is popular for creating and reading ZIPs.
Creating and adding to ZIP files
const AdmZip = require("adm-zip");
const fs = require("fs");
fs.writeFileSync("file1.txt", "Hello".repeat(10000));
const zip = new AdmZip();
zip.addLocalFile("file1.txt");
zip.writeZip("example.zip");
Reading ZIP files
const AdmZip = require("adm-zip");
const zip = new AdmZip("example.zip");
const entries = zip.getEntries();
for (const entry of entries) {
console.log(entry.entryName);
console.log(entry.header.size, entry.header.compressedSize);
}
Extracting from ZIP files
extractAllTo(path)extracts everything.extractEntryTo(name, path)extracts one member.
const AdmZip = require("adm-zip");
const zip = new AdmZip("example.zip");
zip.extractAllTo("./spam", true);
zip.extractEntryTo("file1.txt", "./some/new/folders");
Project: backup_to_zip (incrementing ZIP backups)
Program: backs up a folder into numbered ZIPs like spam_1.zip, spam_2.zip, etc.
Step 1: Figure out the ZIP file's name
const fs = require("fs");
const path = require("path");
function backupToZip(folder) {
const folderName = path.basename(folder);
let number = 1;
let zipFilename;
while (true) {
zipFilename = `${folderName}_${number}.zip`;
if (!fs.existsSync(zipFilename)) break;
number++;
}
// ...
Step 2: Create the new ZIP file
const AdmZip = require("adm-zip");
console.log(`Creating ${zipFilename}...`);
const zip = new AdmZip();
Step 3: Walk the directory tree and add files
function addFiles(dir) {
const entries = fs.readdirSync(dir, { withFileTypes: true });
console.log(`Adding files in folder ${dir}...`);
for (const entry of entries) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
addFiles(full);
} else {
console.log(` Adding file ${entry.name}...`);
zip.addLocalFile(full);
}
}
}
addFiles(folder);
zip.writeZip(zipFilename);
console.log("Done.");
}
backupToZip(path.join(require("os").homedir(), "spam"));
Ideas for other programs
Suggested variations using recursive walk, zip, and filters:
- Archive only files with certain extensions (e.g.
.txt,.js). - Archive everything except certain extensions.
- Only archive folders that are very large or recently modified.
Example filter idea:
const AdmZip = require("adm-zip");
const fs = require("fs");
const path = require("path");
const os = require("os");
const root = path.join(os.homedir(), "project");
const zip = new AdmZip();
function addJsFiles(dir) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
addJsFiles(full);
} else if (entry.name.endsWith(".js")) {
zip.addLocalFile(full);
}
}
}
addJsFiles(root);
zip.writeZip("code_backup.zip");
Summary (chapter recap)
- Use
fsto copy, move, rename, delete safely (with dry runs). - Use recursive
readdirSyncwithwithFileTypesto operate on entire directory trees. - Use
adm-zipto compress/extract ZIP archives, combining with file operations for backups and packaging.
Practice questions
The chapter ends with conceptual questions, for example:
- Difference between
fs.copyFileSync()andfs.cpSync()? - Which function renames files?
- Difference between
trashdelete andfs.rmSyncdelete? - How do you create a zip in Node.js?
(Answers in short: single file vs whole tree; fs.renameSync; recycle-bin vs permanent delete; use adm-zip or similar package.)
Practice programs
Four suggested exercises (same as Python, adapted to Node.js):
- Selectively Copying – copy only certain extensions into a new folder.
- Deleting Unneeded Files – find and print files over a size threshold (e.g. 100MB).
- Renumbering Files – close gaps in numbered filenames.
- Converting Dates – rename files from
MM-DD-YYYYtoDD-MM-YYYYusing recursive walk, regex, andfs.renameSync.
Example snippet for "Deleting Unneeded Files":
const fs = require("fs");
const path = require("path");
const os = require("os");
function findLargeFiles(dir) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
findLargeFiles(full);
} else {
const stats = fs.statSync(full);
if (stats.size > 100 * 1024 * 1024) {
console.log(full, "is larger than 100MB");
}
}
}
}
findLargeFiles(os.homedir());
Overall idea of the chapter
The chapter's main message is: Node.js provides fs for copying, moving, renaming, and deleting files, recursive readdirSync for walking directory trees, and packages like adm-zip for zip archives. The dry-run pattern and trash-based deletion are equally important safety habits in JavaScript.