Skip to content
Open
Show file tree
Hide file tree
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
83 changes: 42 additions & 41 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,43 +1,44 @@
{
"name": "node-nodejs-basics",
"version": "1.0.0",
"description": "This repository is the part of nodejs-assignments https://github.com/AlreadyBored/nodejs-assignments",
"engines": {
"node": ">=22.14.0"
},
"type": "module",
"scripts": {
"cli:args": "node src/cli/args.js --some-arg value1 --other 1337 --arg2 42",
"cli:env": "npx cross-env SOME=any RSS_foo=bar RSS_bar=baz node src/cli/env.js",
"cp": "node src/cp/cp.js",
"fs:copy": "node src/fs/copy.js",
"fs:create": "node src/fs/create.js",
"fs:delete": "node src/fs/delete.js",
"fs:list": "node src/fs/list.js",
"fs:read": "node src/fs/read.js",
"fs:rename": "node src/fs/rename.js",
"hash": "node src/hash/calcHash.js",
"modules": "node src/modules/esm.mjs",
"streams:read": "node src/streams/read.js",
"streams:transform": "node src/streams/transform.js",
"streams:write": "node src/streams/write.js",
"wt": "node src/wt/main.js",
"zip:compress": "node src/zip/compress.js",
"zip:decompress": "node src/zip/decompress.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/AlreadyBored/node-nodejs-basics.git"
},
"keywords": [
"nodejs",
"assignments",
"alreadybored"
],
"author": "alreadybored",
"license": "ISC",
"bugs": {
"url": "https://github.com/AlreadyBored/node-nodejs-basics/issues"
},
"homepage": "https://github.com/AlreadyBored/node-nodejs-basics#readme"
"name": "node-nodejs-basics",
"version": "1.0.0",
"description": "This repository is the part of nodejs-assignments https://github.com/AlreadyBored/nodejs-assignments",
"engines": {
"node": ">=22.14.0"
},
"type": "module",
"scripts": {
"cli:args": "node src/cli/args.js --some-arg value1 --other 1337 --arg2 42",
"cli:env": "npx cross-env SOME=any RSS_foo=bar RSS_bar=baz node src/cli/env.js",
"cp": "node src/cp/cp.js",
"fs:copy": "node src/fs/copy.js",
"fs:create": "node src/fs/create.js",
"fs:delete": "node src/fs/delete.js",
"fs:list": "node src/fs/list.js",
"fs:read": "node src/fs/read.js",
"fs:rename": "node src/fs/rename.js",
"hash": "node src/hash/calcHash.js",
"modules": "node src/modules/esm.mjs",
"streams:read": "node src/streams/read.js",
"streams:transform": "node src/streams/transform.js",
"streams:write": "node src/streams/write.js",
"wt": "node src/wt/main.js",
"zip:compress": "node src/zip/compress.js",
"zip:decompress": "node src/zip/decompress.js",
"start": "node src/index.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/AlreadyBored/node-nodejs-basics.git"
},
"keywords": [
"nodejs",
"assignments",
"alreadybored"
],
"author": "alreadybored",
"license": "ISC",
"bugs": {
"url": "https://github.com/AlreadyBored/node-nodejs-basics/issues"
},
"homepage": "https://github.com/AlreadyBored/node-nodejs-basics#readme"
}
212 changes: 212 additions & 0 deletions src/FileManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import * as fs from "node:fs/promises";
import * as path from "node:path";
import { createReadStream, createWriteStream } from "node:fs";
import * as os from "node:os";
import { createBrotliCompress, createBrotliDecompress } from "node:zlib";
import { createHash } from "node:crypto";

export class FileManager {
constructor() {
process.chdir(os.homedir());
}

async executeCommand(input) {
const [command, ...args] = input.trim().split(" ");

try {
switch (command) {
case "up":
this.up();
break;
case "cd":
await this.cd(args[0]);
break;
case "ls":
await this.ls();
break;
case "cat":
await this.cat(args[0]);
break;
case "add":
await this.add(args[0]);
break;
case "rn":
await this.rename(args[0], args[1]);
break;
case "cp":
await this.copy(args[0], args[1]);
break;
case "mv":
await this.move(args[0], args[1]);
break;
case "rm":
await this.remove(args[0]);
break;
case "os":
this.osInfo(args[0]);
break;
case "hash":
await this.hash(args[0]);
break;
case "compress":
await this.compress(args[0], args[1]);
break;
case "decompress":
await this.decompress(args[0], args[1]);
break;
case "mkdir":
await this.mkdir(args[0]);
break;
default:
console.log("Invalid input");
}
} catch (err) {
console.log("Operation failed");
}
}

up() {
const currentDir = process.cwd();
const parentDir = path.dirname(currentDir);
const rootDir = path.parse(currentDir).root;

if (currentDir !== rootDir) {
process.chdir(parentDir);
}
}

async cd(pathTo) {
const resolvedPath = path.resolve(process.cwd(), pathTo);
const rootDir = path.parse(resolvedPath).root;

if (!resolvedPath.startsWith(rootDir)) {
console.log("Operation failed");
return;
}

await fs.access(resolvedPath);
process.chdir(resolvedPath);
}

async ls() {
const files = await fs.readdir(process.cwd(), { withFileTypes: true });
const sorted = files.sort((a, b) => {
if (a.isDirectory() === b.isDirectory()) {
return a.name.localeCompare(b.name);
}
return a.isDirectory() ? -1 : 1;
});

console.log("Type\tName");
sorted.forEach((entry) => {
console.log(`${entry.isDirectory() ? "DIR" : "FILE"}\t${entry.name}`);
});
}

async cat(filePath) {
const readable = createReadStream(filePath);
readable.pipe(process.stdout);
return new Promise((resolve, reject) => {
readable.on("end", resolve);
readable.on("error", reject);
});
}

async add(filename) {
await fs.writeFile(filename, "");
}

async rename(oldPath, newPath) {
await fs.rename(oldPath, newPath);
}

async copy(sourcePath, destPath) {
const fileName = path.basename(sourcePath);
const destinationFile = path.join(destPath, fileName);
const readable = createReadStream(sourcePath);
const writable = createWriteStream(destinationFile);

readable.pipe(writable);
return new Promise((resolve, reject) => {
writable.on("finish", resolve);
writable.on("error", reject);
});
}

async move(sourcePath, destPath) {
await this.copy(sourcePath, destPath);
await fs.unlink(sourcePath);
}

async remove(filePath) {
await fs.unlink(filePath);
}

osInfo(flag) {
switch (flag) {
case "--EOL":
console.log(JSON.stringify(os.EOL));
break;
case "--cpus":
const cpus = os.cpus();
console.log(`Overall CPUs: ${cpus.length}`);
cpus.forEach((cpu, i) => {
console.log(`CPU ${i + 1}: ${cpu.model} (${cpu.speed / 1000} GHz)`);
});
break;
case "--homedir":
console.log(os.homedir());
break;
case "--username":
console.log(os.userInfo().username);
break;
case "--architecture":
console.log(os.arch());
break;
default:
console.log("Invalid OS flag");
}
}

async hash(filePath) {
const readable = createReadStream(filePath);
const hash = createHash("sha256");
readable.pipe(hash);

return new Promise((resolve, reject) => {
hash.on("finish", () => {
console.log(hash.digest("hex"));
resolve();
});
hash.on("error", reject);
});
}

async compress(sourcePath, destPath) {
const brotli = createBrotliCompress();
const source = createReadStream(sourcePath);
const destination = createWriteStream(destPath);

source.pipe(brotli).pipe(destination);
return new Promise((resolve, reject) => {
destination.on("finish", resolve);
destination.on("error", reject);
});
}

async decompress(sourcePath, destPath) {
const brotli = createBrotliDecompress();
const source = createReadStream(sourcePath);
const destination = createWriteStream(destPath);

source.pipe(brotli).pipe(destination);
return new Promise((resolve, reject) => {
destination.on("finish", resolve);
destination.on("error", reject);
});
}

async mkdir(dirName) {
await fs.mkdir(path.join(process.cwd(), dirName));
}
}
17 changes: 6 additions & 11 deletions src/cli/args.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
const parseArgs = () => {
let parsedArgs = "";
export const parseArgs = () => {
let parsedArgs = [];
let args = process.argv.slice(2);
for (let i = 0; i < args.length; i++) {
if (args[i].includes("--")) {
parsedArgs += args[i] + " is ";
} else {
parsedArgs += args[i];
if (args[i] !== args[args.length - 1]) {
parsedArgs += ", ";
}
let [key, val] = args[i].split("=");
parsedArgs.push({ [key.slice(2)]: val });
}
}
console.log(parsedArgs);
};

parseArgs();
return parsedArgs;
};
14 changes: 14 additions & 0 deletions src/get-username.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { parseArgs } from "./cli/args.js";

export function getUsername() {
const parsedArgs = parseArgs();
let username = "";

for (let arg of parsedArgs) {
let vars = Object.entries(arg)[0];
if (vars[0] === "username") {
username = vars[1];
}
}
return username;
}
36 changes: 36 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { getUsername } from "./get-username.js";
import { FileManager } from "./FileManager.js";
import * as readline from "node:readline";
import { stdin as input, stdout as output } from "node:process";

const username = getUsername();
const fileManager = new FileManager();

console.log(`Welcome to the File Manager, ${username}!`);
console.log(`You are currently in ${process.cwd()}`);

const rl = readline.createInterface({ input, output });

rl.on("line", async (input) => {
if (input.trim() === ".exit") {
rl.close();
return;
}

try {
await fileManager.executeCommand(input);
} catch (err) {
console.log("Operation failed");
}

console.log(`You are currently in ${process.cwd()}`);
});

process.on("SIGINT", () => {
rl.close();
});

rl.on("close", () => {
console.log(`Thank you for using File Manager, ${username}, goodbye!`);
process.exit(0);
});