From c81a13a89d1bf79054216d10548b599977032db1 Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Thu, 1 Feb 2024 20:54:34 +0300 Subject: [PATCH 01/14] add filesystem commands --- package.json | 13 +++++ src/index.js | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 package.json create mode 100644 src/index.js diff --git a/package.json b/package.json new file mode 100644 index 0000000..7c2d1e0 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "file-manager", + "version": "1.0.0", + "description": "", + "main": "src/index.js", + "type": "module", + "scripts": { + "start": "node src/index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Dzmitry Yarmoshkin", + "license": "ISC" +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..30a08f3 --- /dev/null +++ b/src/index.js @@ -0,0 +1,137 @@ +import { argv, stdin, stdout } from 'node:process'; +import { pipeline } from 'node:stream/promises'; +import os from 'node:os'; +import path from 'node:path'; +import { createReadStream, close, createWriteStream } from 'node:fs'; +import fs from 'node:fs/promises'; +import { fileURLToPath } from 'node:url'; +import { existsSync } from 'node:fs'; + +const __filename = fileURLToPath(import.meta.url); +export const __dirname = path.dirname(__filename); + +const getUsernameFromArgs = () => { + const args = argv.slice(2); + for (let i = 0; i < args.length; i++) { + if (args[i].startsWith('--username=')) { + return args[i].substring(args[i].indexOf('=') + 1); + } + } +}; + +const start = async () => { + const username = getUsernameFromArgs(); + const userHomeDir = os.homedir(); + const startingDir = userHomeDir; + // TODO remove 'Desktop' from path + let workingDirectory = path.join(startingDir, 'Desktop'); + console.log(`Welcome to the File Manager, ${username}!`); + console.log(`You are currently in ${workingDirectory}`); + + process.on('SIGINT', () => { + console.log(`Thank you for using File Manager, ${username}, goodbye!`); + process.exit(0); + }); + + process.stdin.on('data', async (data) => { + const userInput = data.toString().replace(/(\r\n|\n|\r)/gm, ''); + + let command; + if (userInput === 'up') { + command = 'up'; + } else if (userInput.startsWith('cd ')) { + command = 'cd'; + } else if (userInput === 'ls') { + command = 'ls'; + } else if (userInput.startsWith('cat ')) { + command = 'cat'; + } else if (userInput.startsWith('add ')) { + command = 'add'; + } else if (userInput.startsWith('rn ')) { + command = 'rn'; + } else if (userInput.startsWith('cp ')) { + command = 'cp'; + } else if (userInput.startsWith('mv ')) { + command = 'mv'; + } else if (userInput.startsWith('rm ')) { + command = 'rm'; + } + + // TODO and ".exit" command + // try catch ? + // rewrite "rn" to accept filename as a second argument + // same as above folr "cp" and others + if (command === 'up') { + workingDirectory = path.join(workingDirectory, '../'); + } else if (command === 'cd') { + const parsedPath = userInput.substring(3); + const newDirectory = path.join(workingDirectory, parsedPath); + + if (existsSync(newDirectory)) { + workingDirectory = newDirectory; + } + } else if (command === 'ls') { + const files = await fs.readdir(workingDirectory); + const arr = []; + for (const file of files) { + const fileStat = await fs.lstat(path.join(workingDirectory, file)); + const isFile = fileStat.isFile(); + const fileType = isFile ? 'file' : 'directory'; + + arr.push({ Name: file, Type: fileType }); + } + + console.table(arr, ['Name', 'Type']); + } else if (command === 'cat') { + const parsedPath = userInput.substring(4); + const sourceFilePath = path.join(workingDirectory, parsedPath); + const sourceStream = createReadStream(sourceFilePath); + + sourceStream.pipe(stdout); + } else if (command === 'add') { + const filename = userInput.substring(4); + const filePath = path.join(workingDirectory, filename); + await fs.writeFile(filePath, '', { flag: 'wx' }); + } else if (command === 'rn') { + const parsedSourcePath = userInput.substring(3, userInput.indexOf(' ', 3)); + const renamedFilename = userInput.substring(userInput.indexOf(' ', 3) + 1); + const sourceFilePath = path.join(workingDirectory, parsedSourcePath); + const renamedFilePath = path.join(workingDirectory, renamedFilename); + + await fs.rename(sourceFilePath, renamedFilePath); + } else if (command === 'cp') { + const parsedSourcePath = userInput.substring(3, userInput.indexOf(' ', 3)); + const renamedFilename = userInput.substring(userInput.indexOf(' ', 3) + 1); + const sourceFilePath = path.join(workingDirectory, parsedSourcePath); + const renamedFilePath = path.join(workingDirectory, renamedFilename); + + const sourceStream = createReadStream(sourceFilePath); + const destStream = createWriteStream(renamedFilePath); + // console.log('sourceFilePath', sourceFilePath); + // console.log('renamedFilePath', renamedFilePath); + + await pipeline(sourceStream, destStream); + } else if (command === 'mv') { + const parsedSourcePath = userInput.substring(3, userInput.indexOf(' ', 3)); + const renamedFilename = userInput.substring(userInput.indexOf(' ', 3) + 1); + const sourceFilePath = path.join(workingDirectory, parsedSourcePath); + const renamedFilePath = path.join(workingDirectory, renamedFilename); + + const sourceStream = createReadStream(sourceFilePath); + const destStream = createWriteStream(renamedFilePath); + + await pipeline(sourceStream, destStream); + await fs.unlink(sourceFilePath); + } else if (command === 'mv') { + const parsedSourcePath = userInput.substring(3); + const sourceFilePath = path.join(workingDirectory, parsedSourcePath); + + await fs.unlink(sourceFilePath); + } else { + } + + console.log(`You are currently in ${workingDirectory}`); + }); +}; + +start(); From 9faea7a096456d67244419f338e221aa05d4448e Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Fri, 2 Feb 2024 20:00:13 +0300 Subject: [PATCH 02/14] refactor --- .vscode/settings.json | 8 +++ src/commands/add.js | 11 ++++ src/commands/cat.js | 13 ++++ src/commands/cd.js | 8 +++ src/commands/cp.js | 19 ++++++ src/commands/ls.js | 19 ++++++ src/commands/mv.js | 8 +++ src/commands/rm.js | 11 ++++ src/commands/rn.js | 15 +++++ src/commands/up.js | 5 ++ src/events/exit.js | 13 ++++ src/events/input.js | 62 ++++++++++++++++++ src/index.js | 142 +++--------------------------------------- src/parseCommand.js | 18 ++++++ src/setStartingDir.js | 11 ++++ src/user.js | 29 +++++++++ src/utils/utils.js | 17 +++++ 17 files changed, 277 insertions(+), 132 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 src/commands/add.js create mode 100644 src/commands/cat.js create mode 100644 src/commands/cd.js create mode 100644 src/commands/cp.js create mode 100644 src/commands/ls.js create mode 100644 src/commands/mv.js create mode 100644 src/commands/rm.js create mode 100644 src/commands/rn.js create mode 100644 src/commands/up.js create mode 100644 src/events/exit.js create mode 100644 src/events/input.js create mode 100644 src/parseCommand.js create mode 100644 src/setStartingDir.js create mode 100644 src/user.js create mode 100644 src/utils/utils.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..8ee7c22 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "editor.formatOnSave": true, + "[javascript]": { + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit" + } + } +} diff --git a/src/commands/add.js b/src/commands/add.js new file mode 100644 index 0000000..29c890f --- /dev/null +++ b/src/commands/add.js @@ -0,0 +1,11 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { cwd } from 'node:process'; +import { extractArgument } from '../utils/utils.js'; + +export const add = async (userInput) => { + const filename = extractArgument(userInput, 1); + const filePath = path.join(cwd(), filename); + + await fs.writeFile(filePath, '', { flag: 'wx' }); +}; diff --git a/src/commands/cat.js b/src/commands/cat.js new file mode 100644 index 0000000..d9184e2 --- /dev/null +++ b/src/commands/cat.js @@ -0,0 +1,13 @@ +import { createReadStream } from 'node:fs'; +import path from 'node:path'; +import { cwd, stdout } from 'node:process'; +import { pipeline } from 'node:stream/promises'; +import { extractArgument } from '../utils/utils.js'; + +export const cat = async (userInput) => { + const parsedPath = extractArgument(userInput, 1); + const sourceFilePath = path.join(cwd(), parsedPath); + const sourceStream = createReadStream(sourceFilePath); + + await pipeline(sourceStream, stdout); +}; diff --git a/src/commands/cd.js b/src/commands/cd.js new file mode 100644 index 0000000..89b9398 --- /dev/null +++ b/src/commands/cd.js @@ -0,0 +1,8 @@ +import { chdir } from 'node:process'; +import { extractArgument } from '../utils/utils.js'; + +export const cd = (userInput) => { + const parsedPath = extractArgument(userInput, 1); + + chdir(parsedPath); +}; diff --git a/src/commands/cp.js b/src/commands/cp.js new file mode 100644 index 0000000..04886b9 --- /dev/null +++ b/src/commands/cp.js @@ -0,0 +1,19 @@ +import { createReadStream, createWriteStream } from 'node:fs'; +import path from 'node:path'; +import { pipeline } from 'node:stream/promises'; +import { extractArgument } from '../utils/utils.js'; +import { cwd } from 'node:process'; + +export const cp = async (userInput) => { + const parsedSourcePath = extractArgument(userInput, 1); + const destFolder = extractArgument(userInput, 2); + const sourceFilePath = path.join(cwd(), parsedSourcePath); + const destFolderPath = path.join(cwd(), destFolder); + + const sourceFilename = path.basename(sourceFilePath); + const destFilePath = path.join(destFolderPath, sourceFilename); + const sourceStream = createReadStream(sourceFilePath); + const destStream = createWriteStream(destFilePath); + + await pipeline(sourceStream, destStream); +}; diff --git a/src/commands/ls.js b/src/commands/ls.js new file mode 100644 index 0000000..fa04f78 --- /dev/null +++ b/src/commands/ls.js @@ -0,0 +1,19 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { cwd } from 'node:process'; + +export const ls = async () => { + const files = await fs.readdir(cwd()); + const arr = []; + + // TODO add file sorting + for (const file of files) { + const fileStat = await fs.lstat(path.join(cwd(), file)); + const isFile = fileStat.isFile(); + const fileType = isFile ? 'file' : 'directory'; + + arr.push({ Name: file, Type: fileType }); + } + + console.table(arr, ['Name', 'Type']); +}; diff --git a/src/commands/mv.js b/src/commands/mv.js new file mode 100644 index 0000000..472c8ea --- /dev/null +++ b/src/commands/mv.js @@ -0,0 +1,8 @@ +import { cwd } from 'node:process'; +import { cp } from './cp.js'; +import { rm } from './rm.js'; + +export const mv = async (userInput) => { + await cp(userInput, cwd()); + await rm(userInput, cwd()); +}; diff --git a/src/commands/rm.js b/src/commands/rm.js new file mode 100644 index 0000000..bb5842a --- /dev/null +++ b/src/commands/rm.js @@ -0,0 +1,11 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { cwd } from 'node:process'; +import { extractArgument } from '../utils/utils.js'; + +export const rm = async (userInput) => { + const parsedSourcePath = extractArgument(userInput, 1); + const sourceFilePath = path.join(cwd(), parsedSourcePath); + + await fs.unlink(sourceFilePath); +}; diff --git a/src/commands/rn.js b/src/commands/rn.js new file mode 100644 index 0000000..9ab97bb --- /dev/null +++ b/src/commands/rn.js @@ -0,0 +1,15 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { cwd } from 'node:process'; +import { extractArgument } from '../utils/utils.js'; + +export const rn = async (userInput) => { + const parsedSourcePath = extractArgument(userInput, 1); + const renamedFilename = extractArgument(userInput, 2); + + const sourceFilePath = path.join(cwd(), parsedSourcePath); + const sourceFileDir = path.dirname(sourceFilePath); + const renamedFilePath = path.join(sourceFileDir, renamedFilename); + + await fs.rename(sourceFilePath, renamedFilePath); +}; diff --git a/src/commands/up.js b/src/commands/up.js new file mode 100644 index 0000000..e7d1043 --- /dev/null +++ b/src/commands/up.js @@ -0,0 +1,5 @@ +import { chdir } from 'node:process'; + +export const up = () => { + chdir('..'); +}; diff --git a/src/events/exit.js b/src/events/exit.js new file mode 100644 index 0000000..918c20f --- /dev/null +++ b/src/events/exit.js @@ -0,0 +1,13 @@ +import { logWithColor } from '../utils/utils.js'; + +export const setExitEvents = (username) => { + const message = `Thank you for using File Manager, ${username}, goodbye!`; + + process.on('SIGINT', () => { + process.exit(0); + }); + + process.on('exit', () => { + logWithColor(message, 'yellow'); + }); +}; diff --git a/src/events/input.js b/src/events/input.js new file mode 100644 index 0000000..3ae7b83 --- /dev/null +++ b/src/events/input.js @@ -0,0 +1,62 @@ +import { COMMANDS, parseCommand } from '../parseCommand.js'; +import { logWithColor } from '../utils/utils.js'; + +export const setInputEvent = () => { + process.stdin.on('data', async (data) => { + const userInput = data.toString().replace(/(\r\n|\n|\r)/gm, ''); + + const command = parseCommand(userInput); + + // TODO add support for filenames with spaces + try { + switch (command) { + case COMMANDS.UP: + up(); + break; + + case COMMANDS.CD: + cd(userInput); + break; + + case COMMANDS.LS: + await ls(); + break; + + case COMMANDS.CAT: + await cat(userInput); + break; + + case COMMANDS.ADD: + await add(userInput); + break; + + case COMMANDS.RN: + await rn(userInput); + break; + + case COMMANDS.CP: + await cp(userInput); + break; + + case COMMANDS.MV: + await mv(userInput); + break; + + case COMMANDS.RM: + await rm(userInput); + break; + + case COMMANDS.EXIT: + process.exit(0); + + default: + logWithColor('Invalid input', 'red'); + break; + } + } catch (err) { + logWithColor(`Operation failed\n${err.message}`, 'red'); + } + + console.log(`You are currently in ${cwd()}`); + }); +}; diff --git a/src/index.js b/src/index.js index 30a08f3..d99cd82 100644 --- a/src/index.js +++ b/src/index.js @@ -1,137 +1,15 @@ -import { argv, stdin, stdout } from 'node:process'; -import { pipeline } from 'node:stream/promises'; -import os from 'node:os'; -import path from 'node:path'; -import { createReadStream, close, createWriteStream } from 'node:fs'; -import fs from 'node:fs/promises'; -import { fileURLToPath } from 'node:url'; -import { existsSync } from 'node:fs'; +import { setExitEvents } from './events/exit.js'; +import { setInputEvent } from './events/input.js'; +import { setStartingDir } from './setStartingDir.js'; +import { getUsernameFromArgs, greetUser } from './user.js'; -const __filename = fileURLToPath(import.meta.url); -export const __dirname = path.dirname(__filename); - -const getUsernameFromArgs = () => { - const args = argv.slice(2); - for (let i = 0; i < args.length; i++) { - if (args[i].startsWith('--username=')) { - return args[i].substring(args[i].indexOf('=') + 1); - } - } -}; - -const start = async () => { +const init = async () => { const username = getUsernameFromArgs(); - const userHomeDir = os.homedir(); - const startingDir = userHomeDir; - // TODO remove 'Desktop' from path - let workingDirectory = path.join(startingDir, 'Desktop'); - console.log(`Welcome to the File Manager, ${username}!`); - console.log(`You are currently in ${workingDirectory}`); - - process.on('SIGINT', () => { - console.log(`Thank you for using File Manager, ${username}, goodbye!`); - process.exit(0); - }); - - process.stdin.on('data', async (data) => { - const userInput = data.toString().replace(/(\r\n|\n|\r)/gm, ''); - - let command; - if (userInput === 'up') { - command = 'up'; - } else if (userInput.startsWith('cd ')) { - command = 'cd'; - } else if (userInput === 'ls') { - command = 'ls'; - } else if (userInput.startsWith('cat ')) { - command = 'cat'; - } else if (userInput.startsWith('add ')) { - command = 'add'; - } else if (userInput.startsWith('rn ')) { - command = 'rn'; - } else if (userInput.startsWith('cp ')) { - command = 'cp'; - } else if (userInput.startsWith('mv ')) { - command = 'mv'; - } else if (userInput.startsWith('rm ')) { - command = 'rm'; - } - - // TODO and ".exit" command - // try catch ? - // rewrite "rn" to accept filename as a second argument - // same as above folr "cp" and others - if (command === 'up') { - workingDirectory = path.join(workingDirectory, '../'); - } else if (command === 'cd') { - const parsedPath = userInput.substring(3); - const newDirectory = path.join(workingDirectory, parsedPath); - - if (existsSync(newDirectory)) { - workingDirectory = newDirectory; - } - } else if (command === 'ls') { - const files = await fs.readdir(workingDirectory); - const arr = []; - for (const file of files) { - const fileStat = await fs.lstat(path.join(workingDirectory, file)); - const isFile = fileStat.isFile(); - const fileType = isFile ? 'file' : 'directory'; - - arr.push({ Name: file, Type: fileType }); - } - - console.table(arr, ['Name', 'Type']); - } else if (command === 'cat') { - const parsedPath = userInput.substring(4); - const sourceFilePath = path.join(workingDirectory, parsedPath); - const sourceStream = createReadStream(sourceFilePath); - - sourceStream.pipe(stdout); - } else if (command === 'add') { - const filename = userInput.substring(4); - const filePath = path.join(workingDirectory, filename); - await fs.writeFile(filePath, '', { flag: 'wx' }); - } else if (command === 'rn') { - const parsedSourcePath = userInput.substring(3, userInput.indexOf(' ', 3)); - const renamedFilename = userInput.substring(userInput.indexOf(' ', 3) + 1); - const sourceFilePath = path.join(workingDirectory, parsedSourcePath); - const renamedFilePath = path.join(workingDirectory, renamedFilename); - - await fs.rename(sourceFilePath, renamedFilePath); - } else if (command === 'cp') { - const parsedSourcePath = userInput.substring(3, userInput.indexOf(' ', 3)); - const renamedFilename = userInput.substring(userInput.indexOf(' ', 3) + 1); - const sourceFilePath = path.join(workingDirectory, parsedSourcePath); - const renamedFilePath = path.join(workingDirectory, renamedFilename); - - const sourceStream = createReadStream(sourceFilePath); - const destStream = createWriteStream(renamedFilePath); - // console.log('sourceFilePath', sourceFilePath); - // console.log('renamedFilePath', renamedFilePath); - - await pipeline(sourceStream, destStream); - } else if (command === 'mv') { - const parsedSourcePath = userInput.substring(3, userInput.indexOf(' ', 3)); - const renamedFilename = userInput.substring(userInput.indexOf(' ', 3) + 1); - const sourceFilePath = path.join(workingDirectory, parsedSourcePath); - const renamedFilePath = path.join(workingDirectory, renamedFilename); - - const sourceStream = createReadStream(sourceFilePath); - const destStream = createWriteStream(renamedFilePath); - - await pipeline(sourceStream, destStream); - await fs.unlink(sourceFilePath); - } else if (command === 'mv') { - const parsedSourcePath = userInput.substring(3); - const sourceFilePath = path.join(workingDirectory, parsedSourcePath); - - await fs.unlink(sourceFilePath); - } else { - } - console.log(`You are currently in ${workingDirectory}`); - }); + setStartingDir(); + setExitEvents(username); + greetUser(username); + setInputEvent(); }; -start(); +init(); diff --git a/src/parseCommand.js b/src/parseCommand.js new file mode 100644 index 0000000..d14c7a9 --- /dev/null +++ b/src/parseCommand.js @@ -0,0 +1,18 @@ +import { extractArgument } from './utils/utils.js'; + +export const COMMANDS = { + UP: 'up', + CD: 'cd', + LS: 'ls', + CAT: 'cat', + ADD: 'add', + RN: 'rn', + CP: 'cp', + MV: 'mv', + RM: 'rm', + EXIT: '.exit', +}; + +export const parseCommand = (userInput) => { + return extractArgument(userInput, 0); +}; diff --git a/src/setStartingDir.js b/src/setStartingDir.js new file mode 100644 index 0000000..26d4299 --- /dev/null +++ b/src/setStartingDir.js @@ -0,0 +1,11 @@ +import os from 'node:os'; +import path from 'node:path'; +import { chdir } from 'node:process'; + +export const setStartingDir = () => { + const userHomeDir = os.homedir(); + // TODO remove 'Desktop' from path + const startingDir = path.join(userHomeDir, 'Desktop'); + + chdir(startingDir); +}; diff --git a/src/user.js b/src/user.js new file mode 100644 index 0000000..7be378b --- /dev/null +++ b/src/user.js @@ -0,0 +1,29 @@ +import { argv, cwd } from 'node:process'; +import { logWithColor } from './utils/utils.js'; + +export const getUsernameFromArgs = () => { + const args = process.argv.slice(2); + const usernameArg = args.find((arg) => arg.startsWith('--username=')); + + if (!usernameArg) { + logWithColor( + `Oops! It looks like you forgot to provide a username argument. Please start the program again using the following command:\nnpm run start -- --username=your_username`, + 'red' + ); + process.exit(1); + } + + const username = usernameArg.substring(usernameArg.indexOf('=') + 1); + + if (!username) { + logWithColor(`Oops! It looks like you provided an empty username. Please provide a valid username.`, 'red'); + process.exit(1); + } + + return username; +}; + +export const greetUser = (username) => { + console.log(`Welcome to the File Manager, ${username}!`); + console.log(`You are currently in ${cwd()}`); +}; diff --git a/src/utils/utils.js b/src/utils/utils.js new file mode 100644 index 0000000..e34a5d8 --- /dev/null +++ b/src/utils/utils.js @@ -0,0 +1,17 @@ +export const extractArgument = (userInput, argNumber) => { + const args = userInput.trim().split(/\s+/); + return args[argNumber]; +}; + +export const logWithColor = (text, color) => { + const colors = { + reset: '\x1b[0m', + black: '\x1b[30m', + red: '\x1b[31m', + green: '\x1b[32m', + yellow: '\x1b[33m', + blue: '\x1b[34m', + }; + + console.log(`${colors[color]}${text}${colors.reset}`); +}; From 8af046c1b5c7eeb8bb897d2d1e13cd9b7a74054f Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Sun, 4 Feb 2024 14:01:14 +0300 Subject: [PATCH 03/14] add os info and hash --- src/commands/{ => filesystem}/add.js | 2 +- src/commands/{ => filesystem}/cat.js | 4 +-- src/commands/{ => filesystem}/cd.js | 2 +- src/commands/{ => filesystem}/cp.js | 4 +-- src/commands/{ => filesystem}/ls.js | 6 ++-- src/commands/{ => filesystem}/mv.js | 0 src/commands/{ => filesystem}/rm.js | 2 +- src/commands/{ => filesystem}/rn.js | 2 +- src/commands/{ => filesystem}/up.js | 0 src/commands/hash/hash.js | 13 +++++++++ src/commands/os-info/arch.js | 5 ++++ src/commands/os-info/cpus.js | 14 ++++++++++ src/commands/os-info/eol.js | 5 ++++ src/commands/os-info/homedir.js | 5 ++++ src/commands/os-info/username.js | 6 ++++ src/events/input.js | 42 +++++++++++++++++++++++++++- src/parseCommand.js | 16 ++++++++++- src/setStartingDir.js | 4 +-- src/utils/utils.js | 6 ++++ 19 files changed, 123 insertions(+), 15 deletions(-) rename src/commands/{ => filesystem}/add.js (84%) rename src/commands/{ => filesystem}/cat.js (77%) rename src/commands/{ => filesystem}/cd.js (72%) rename src/commands/{ => filesystem}/cp.js (92%) rename src/commands/{ => filesystem}/ls.js (74%) rename src/commands/{ => filesystem}/mv.js (100%) rename src/commands/{ => filesystem}/rm.js (84%) rename src/commands/{ => filesystem}/rn.js (89%) rename src/commands/{ => filesystem}/up.js (100%) create mode 100644 src/commands/hash/hash.js create mode 100644 src/commands/os-info/arch.js create mode 100644 src/commands/os-info/cpus.js create mode 100644 src/commands/os-info/eol.js create mode 100644 src/commands/os-info/homedir.js create mode 100644 src/commands/os-info/username.js diff --git a/src/commands/add.js b/src/commands/filesystem/add.js similarity index 84% rename from src/commands/add.js rename to src/commands/filesystem/add.js index 29c890f..c37354e 100644 --- a/src/commands/add.js +++ b/src/commands/filesystem/add.js @@ -1,7 +1,7 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { cwd } from 'node:process'; -import { extractArgument } from '../utils/utils.js'; +import { extractArgument } from '../../utils/utils.js'; export const add = async (userInput) => { const filename = extractArgument(userInput, 1); diff --git a/src/commands/cat.js b/src/commands/filesystem/cat.js similarity index 77% rename from src/commands/cat.js rename to src/commands/filesystem/cat.js index d9184e2..ce835bc 100644 --- a/src/commands/cat.js +++ b/src/commands/filesystem/cat.js @@ -2,12 +2,12 @@ import { createReadStream } from 'node:fs'; import path from 'node:path'; import { cwd, stdout } from 'node:process'; import { pipeline } from 'node:stream/promises'; -import { extractArgument } from '../utils/utils.js'; +import { extractArgument } from '../../utils/utils.js'; export const cat = async (userInput) => { const parsedPath = extractArgument(userInput, 1); const sourceFilePath = path.join(cwd(), parsedPath); const sourceStream = createReadStream(sourceFilePath); - await pipeline(sourceStream, stdout); + await pipeline(sourceStream, stdout, { end: false }); }; diff --git a/src/commands/cd.js b/src/commands/filesystem/cd.js similarity index 72% rename from src/commands/cd.js rename to src/commands/filesystem/cd.js index 89b9398..17a4145 100644 --- a/src/commands/cd.js +++ b/src/commands/filesystem/cd.js @@ -1,5 +1,5 @@ import { chdir } from 'node:process'; -import { extractArgument } from '../utils/utils.js'; +import { extractArgument } from '../../utils/utils.js'; export const cd = (userInput) => { const parsedPath = extractArgument(userInput, 1); diff --git a/src/commands/cp.js b/src/commands/filesystem/cp.js similarity index 92% rename from src/commands/cp.js rename to src/commands/filesystem/cp.js index 04886b9..0da2c3b 100644 --- a/src/commands/cp.js +++ b/src/commands/filesystem/cp.js @@ -1,8 +1,8 @@ import { createReadStream, createWriteStream } from 'node:fs'; import path from 'node:path'; -import { pipeline } from 'node:stream/promises'; -import { extractArgument } from '../utils/utils.js'; import { cwd } from 'node:process'; +import { pipeline } from 'node:stream/promises'; +import { extractArgument } from '../../utils/utils.js'; export const cp = async (userInput) => { const parsedSourcePath = extractArgument(userInput, 1); diff --git a/src/commands/ls.js b/src/commands/filesystem/ls.js similarity index 74% rename from src/commands/ls.js rename to src/commands/filesystem/ls.js index fa04f78..2d39e39 100644 --- a/src/commands/ls.js +++ b/src/commands/filesystem/ls.js @@ -4,7 +4,7 @@ import { cwd } from 'node:process'; export const ls = async () => { const files = await fs.readdir(cwd()); - const arr = []; + const fileInfoArray = []; // TODO add file sorting for (const file of files) { @@ -12,8 +12,8 @@ export const ls = async () => { const isFile = fileStat.isFile(); const fileType = isFile ? 'file' : 'directory'; - arr.push({ Name: file, Type: fileType }); + fileInfoArray.push({ Name: file, Type: fileType }); } - console.table(arr, ['Name', 'Type']); + console.table(fileInfoArray, ['Name', 'Type']); }; diff --git a/src/commands/mv.js b/src/commands/filesystem/mv.js similarity index 100% rename from src/commands/mv.js rename to src/commands/filesystem/mv.js diff --git a/src/commands/rm.js b/src/commands/filesystem/rm.js similarity index 84% rename from src/commands/rm.js rename to src/commands/filesystem/rm.js index bb5842a..06ebde5 100644 --- a/src/commands/rm.js +++ b/src/commands/filesystem/rm.js @@ -1,7 +1,7 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { cwd } from 'node:process'; -import { extractArgument } from '../utils/utils.js'; +import { extractArgument } from '../../utils/utils.js'; export const rm = async (userInput) => { const parsedSourcePath = extractArgument(userInput, 1); diff --git a/src/commands/rn.js b/src/commands/filesystem/rn.js similarity index 89% rename from src/commands/rn.js rename to src/commands/filesystem/rn.js index 9ab97bb..afab3fa 100644 --- a/src/commands/rn.js +++ b/src/commands/filesystem/rn.js @@ -1,7 +1,7 @@ import fs from 'node:fs/promises'; import path from 'node:path'; import { cwd } from 'node:process'; -import { extractArgument } from '../utils/utils.js'; +import { extractArgument } from '../../utils/utils.js'; export const rn = async (userInput) => { const parsedSourcePath = extractArgument(userInput, 1); diff --git a/src/commands/up.js b/src/commands/filesystem/up.js similarity index 100% rename from src/commands/up.js rename to src/commands/filesystem/up.js diff --git a/src/commands/hash/hash.js b/src/commands/hash/hash.js new file mode 100644 index 0000000..a5c03bd --- /dev/null +++ b/src/commands/hash/hash.js @@ -0,0 +1,13 @@ +import { createHash } from 'node:crypto'; +import { createReadStream } from 'node:fs'; +import { stdout } from 'node:process'; +import { pipeline } from 'node:stream/promises'; +import { extractArgument } from '../../utils/utils.js'; + +export const calculateHash = async (userInput) => { + const sourceFilePath = extractArgument(userInput, 1); + const hash = createHash('sha256'); + const input = createReadStream(sourceFilePath); + + await pipeline(input, hash.setEncoding('hex'), stdout, { end: false }); +}; diff --git a/src/commands/os-info/arch.js b/src/commands/os-info/arch.js new file mode 100644 index 0000000..8c5d7ce --- /dev/null +++ b/src/commands/os-info/arch.js @@ -0,0 +1,5 @@ +import { arch as nodeArch } from 'node:os'; + +export const arch = () => { + console.log(`Node.js binary architecture: ${nodeArch()}`); +}; diff --git a/src/commands/os-info/cpus.js b/src/commands/os-info/cpus.js new file mode 100644 index 0000000..600d882 --- /dev/null +++ b/src/commands/os-info/cpus.js @@ -0,0 +1,14 @@ +import { availableParallelism, cpus as nodeCPUS } from 'os'; + +export const cpus = () => { + const cpuData = nodeCPUS(); + const coreCount = availableParallelism(); + const cpuInfoArray = []; + + console.log(`Number of cores: ${coreCount}`); + cpuData.forEach((core) => { + cpuInfoArray.push({ Model: core.model, Speed: `${(core.speed / 1000).toFixed(2)} GHz` }); + }); + + console.table(cpuInfoArray, ['Model', 'Speed']); +}; diff --git a/src/commands/os-info/eol.js b/src/commands/os-info/eol.js new file mode 100644 index 0000000..21e8c0b --- /dev/null +++ b/src/commands/os-info/eol.js @@ -0,0 +1,5 @@ +import { EOL } from 'os'; + +export const eol = () => { + console.log(`System End-Of-Line (EOL): ${JSON.stringify(EOL)}`); +}; diff --git a/src/commands/os-info/homedir.js b/src/commands/os-info/homedir.js new file mode 100644 index 0000000..c881ef6 --- /dev/null +++ b/src/commands/os-info/homedir.js @@ -0,0 +1,5 @@ +import { getHomeDir } from '../../utils/utils.js'; + +export const homedir = () => { + console.log(`Home directory: ${getHomeDir()}`); +}; diff --git a/src/commands/os-info/username.js b/src/commands/os-info/username.js new file mode 100644 index 0000000..4764498 --- /dev/null +++ b/src/commands/os-info/username.js @@ -0,0 +1,6 @@ +import { userInfo } from 'node:os'; + +export const username = () => { + const user = userInfo(); + console.log(`Current system user name: ${user.username}`); +}; diff --git a/src/events/input.js b/src/events/input.js index 3ae7b83..f70cf64 100644 --- a/src/events/input.js +++ b/src/events/input.js @@ -1,3 +1,19 @@ +import { cwd } from 'node:process'; +import { add } from '../commands/filesystem/add.js'; +import { cat } from '../commands/filesystem/cat.js'; +import { cd } from '../commands/filesystem/cd.js'; +import { cp } from '../commands/filesystem/cp.js'; +import { ls } from '../commands/filesystem/ls.js'; +import { mv } from '../commands/filesystem/mv.js'; +import { rm } from '../commands/filesystem/rm.js'; +import { rn } from '../commands/filesystem/rn.js'; +import { up } from '../commands/filesystem/up.js'; +import { calculateHash } from '../commands/hash/hash.js'; +import { arch } from '../commands/os-info/arch.js'; +import { cpus } from '../commands/os-info/cpus.js'; +import { eol } from '../commands/os-info/eol.js'; +import { homedir } from '../commands/os-info/homedir.js'; +import { username } from '../commands/os-info/username.js'; import { COMMANDS, parseCommand } from '../parseCommand.js'; import { logWithColor } from '../utils/utils.js'; @@ -46,6 +62,30 @@ export const setInputEvent = () => { await rm(userInput); break; + case COMMANDS.HASH: + await calculateHash(userInput); + break; + + case COMMANDS.OS_EOL: + eol(); + break; + + case COMMANDS.OS_CPUS: + cpus(); + break; + + case COMMANDS.OS_HOMEDIR: + homedir(); + break; + + case COMMANDS.OS_USERNAME: + username(); + break; + + case COMMANDS.OS_ARCH: + arch(); + break; + case COMMANDS.EXIT: process.exit(0); @@ -57,6 +97,6 @@ export const setInputEvent = () => { logWithColor(`Operation failed\n${err.message}`, 'red'); } - console.log(`You are currently in ${cwd()}`); + console.log(`\nYou are currently in ${cwd()}`); }); }; diff --git a/src/parseCommand.js b/src/parseCommand.js index d14c7a9..f707660 100644 --- a/src/parseCommand.js +++ b/src/parseCommand.js @@ -10,9 +10,23 @@ export const COMMANDS = { CP: 'cp', MV: 'mv', RM: 'rm', + HASH: 'hash', EXIT: '.exit', + OS: 'os', + OS_EOL: '--EOL', + OS_CPUS: '--cpus', + OS_HOMEDIR: '--homedir', + OS_USERNAME: '--username', + OS_ARCH: '--architecture', }; export const parseCommand = (userInput) => { - return extractArgument(userInput, 0); + const command = extractArgument(userInput, 0); + + if (command === COMMANDS.OS) { + const argument = extractArgument(userInput, 1); + return argument; + } + + return command; }; diff --git a/src/setStartingDir.js b/src/setStartingDir.js index 26d4299..ee90d48 100644 --- a/src/setStartingDir.js +++ b/src/setStartingDir.js @@ -1,9 +1,9 @@ -import os from 'node:os'; import path from 'node:path'; import { chdir } from 'node:process'; +import { getHomeDir } from './utils/utils.js'; export const setStartingDir = () => { - const userHomeDir = os.homedir(); + const userHomeDir = getHomeDir(); // TODO remove 'Desktop' from path const startingDir = path.join(userHomeDir, 'Desktop'); diff --git a/src/utils/utils.js b/src/utils/utils.js index e34a5d8..97d515e 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -1,3 +1,5 @@ +import { homedir } from 'node:os'; + export const extractArgument = (userInput, argNumber) => { const args = userInput.trim().split(/\s+/); return args[argNumber]; @@ -15,3 +17,7 @@ export const logWithColor = (text, color) => { console.log(`${colors[color]}${text}${colors.reset}`); }; + +export const getHomeDir = () => { + return homedir(); +}; From b36836ac53734d5bec4f81dfcc21ccdfb32c08c7 Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Sun, 4 Feb 2024 14:20:33 +0300 Subject: [PATCH 04/14] add archiving --- src/commands/archiving/compress.js | 15 +++++++++++++++ src/commands/archiving/decompress.js | 15 +++++++++++++++ src/events/input.js | 10 ++++++++++ src/parseCommand.js | 2 ++ 4 files changed, 42 insertions(+) create mode 100644 src/commands/archiving/compress.js create mode 100644 src/commands/archiving/decompress.js diff --git a/src/commands/archiving/compress.js b/src/commands/archiving/compress.js new file mode 100644 index 0000000..1ff7821 --- /dev/null +++ b/src/commands/archiving/compress.js @@ -0,0 +1,15 @@ +import { createReadStream, createWriteStream } from 'node:fs'; +import { pipeline } from 'node:stream/promises'; +import { createBrotliCompress } from 'node:zlib'; +import { extractArgument } from '../../utils/utils.js'; + +export const compress = async (userInput) => { + const sourceFilePath = extractArgument(userInput, 1); + const destFilePath = extractArgument(userInput, 2); + + const gzip = createBrotliCompress(); + const sourceStream = createReadStream(sourceFilePath); + const destStream = createWriteStream(destFilePath); + + await pipeline(sourceStream, gzip, destStream); +}; diff --git a/src/commands/archiving/decompress.js b/src/commands/archiving/decompress.js new file mode 100644 index 0000000..d7422c7 --- /dev/null +++ b/src/commands/archiving/decompress.js @@ -0,0 +1,15 @@ +import { createReadStream, createWriteStream } from 'node:fs'; +import { pipeline } from 'node:stream/promises'; +import { createBrotliDecompress } from 'node:zlib'; +import { extractArgument } from '../../utils/utils.js'; + +export const decompress = async (userInput) => { + const sourceFilePath = extractArgument(userInput, 1); + const destFilePath = extractArgument(userInput, 2); + + const gzip = createBrotliDecompress(); + const sourceStream = createReadStream(sourceFilePath); + const destStream = createWriteStream(destFilePath); + + await pipeline(sourceStream, gzip, destStream); +}; diff --git a/src/events/input.js b/src/events/input.js index f70cf64..e95d0a7 100644 --- a/src/events/input.js +++ b/src/events/input.js @@ -1,4 +1,6 @@ import { cwd } from 'node:process'; +import { compress } from '../commands/archiving/compress.js'; +import { decompress } from '../commands/archiving/decompress.js'; import { add } from '../commands/filesystem/add.js'; import { cat } from '../commands/filesystem/cat.js'; import { cd } from '../commands/filesystem/cd.js'; @@ -66,6 +68,14 @@ export const setInputEvent = () => { await calculateHash(userInput); break; + case COMMANDS.COMPRESS: + await compress(userInput); + break; + + case COMMANDS.DECOMPRESS: + await decompress(userInput); + break; + case COMMANDS.OS_EOL: eol(); break; diff --git a/src/parseCommand.js b/src/parseCommand.js index f707660..4995411 100644 --- a/src/parseCommand.js +++ b/src/parseCommand.js @@ -11,6 +11,8 @@ export const COMMANDS = { MV: 'mv', RM: 'rm', HASH: 'hash', + COMPRESS: 'compress', + DECOMPRESS: 'decompress', EXIT: '.exit', OS: 'os', OS_EOL: '--EOL', From 1bf1579c375883999871ac495a9cefde84dc9aa6 Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Sun, 4 Feb 2024 18:06:39 +0300 Subject: [PATCH 05/14] add sorting for ls --- src/commands/filesystem/ls.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/commands/filesystem/ls.js b/src/commands/filesystem/ls.js index 2d39e39..106d15e 100644 --- a/src/commands/filesystem/ls.js +++ b/src/commands/filesystem/ls.js @@ -6,14 +6,25 @@ export const ls = async () => { const files = await fs.readdir(cwd()); const fileInfoArray = []; - // TODO add file sorting + const folders = []; + const filesList = []; for (const file of files) { const fileStat = await fs.lstat(path.join(cwd(), file)); const isFile = fileStat.isFile(); const fileType = isFile ? 'file' : 'directory'; + const fileInfo = { Name: file, Type: fileType }; - fileInfoArray.push({ Name: file, Type: fileType }); + if (fileType === 'directory') { + folders.push(fileInfo); + } else { + filesList.push(fileInfo); + } } + folders.sort((a, b) => a.Name.localeCompare(b.Name)); + filesList.sort((a, b) => a.Name.localeCompare(b.Name)); + + fileInfoArray.push(...folders, ...filesList); + console.table(fileInfoArray, ['Name', 'Type']); }; From a4d27f0dc49f906a19638e4c40b3ffa34dbb381f Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Sun, 4 Feb 2024 18:50:19 +0300 Subject: [PATCH 06/14] add support for filenames with spaces --- src/events/input.js | 1 - src/utils/utils.js | 11 ++++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/events/input.js b/src/events/input.js index e95d0a7..3d4de09 100644 --- a/src/events/input.js +++ b/src/events/input.js @@ -25,7 +25,6 @@ export const setInputEvent = () => { const command = parseCommand(userInput); - // TODO add support for filenames with spaces try { switch (command) { case COMMANDS.UP: diff --git a/src/utils/utils.js b/src/utils/utils.js index 97d515e..1ece39f 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -1,10 +1,15 @@ import { homedir } from 'node:os'; export const extractArgument = (userInput, argNumber) => { - const args = userInput.trim().split(/\s+/); - return args[argNumber]; -}; + const args = userInput.trim().match(/(?:[^\s"]+|"[^"]*")/g) || []; + + let argument = args[argNumber] || ''; + if (argument.startsWith('"') && argument.endsWith('"')) { + argument = argument.substring(1, argument.length - 1); + } + return argument; +}; export const logWithColor = (text, color) => { const colors = { reset: '\x1b[0m', From 19b0ffd9f379282b24b3efe0d8fefbb201a53969 Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Sun, 4 Feb 2024 21:22:17 +0300 Subject: [PATCH 07/14] replace path.join with path.resolve --- src/commands/filesystem/add.js | 5 +++-- src/commands/filesystem/cat.js | 2 +- src/commands/filesystem/cp.js | 8 ++++---- src/commands/filesystem/ls.js | 2 +- src/commands/filesystem/rm.js | 2 +- src/commands/filesystem/rn.js | 7 ++++--- 6 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/commands/filesystem/add.js b/src/commands/filesystem/add.js index c37354e..779e1e6 100644 --- a/src/commands/filesystem/add.js +++ b/src/commands/filesystem/add.js @@ -4,8 +4,9 @@ import { cwd } from 'node:process'; import { extractArgument } from '../../utils/utils.js'; export const add = async (userInput) => { - const filename = extractArgument(userInput, 1); - const filePath = path.join(cwd(), filename); + const parsedFilename = extractArgument(userInput, 1); + const filename = path.basename(parsedFilename); + const filePath = path.resolve(cwd(), filename); await fs.writeFile(filePath, '', { flag: 'wx' }); }; diff --git a/src/commands/filesystem/cat.js b/src/commands/filesystem/cat.js index ce835bc..468acbd 100644 --- a/src/commands/filesystem/cat.js +++ b/src/commands/filesystem/cat.js @@ -6,7 +6,7 @@ import { extractArgument } from '../../utils/utils.js'; export const cat = async (userInput) => { const parsedPath = extractArgument(userInput, 1); - const sourceFilePath = path.join(cwd(), parsedPath); + const sourceFilePath = path.resolve(cwd(), parsedPath); const sourceStream = createReadStream(sourceFilePath); await pipeline(sourceStream, stdout, { end: false }); diff --git a/src/commands/filesystem/cp.js b/src/commands/filesystem/cp.js index 0da2c3b..08a828e 100644 --- a/src/commands/filesystem/cp.js +++ b/src/commands/filesystem/cp.js @@ -6,12 +6,12 @@ import { extractArgument } from '../../utils/utils.js'; export const cp = async (userInput) => { const parsedSourcePath = extractArgument(userInput, 1); - const destFolder = extractArgument(userInput, 2); - const sourceFilePath = path.join(cwd(), parsedSourcePath); - const destFolderPath = path.join(cwd(), destFolder); + const parsedDestPath = extractArgument(userInput, 2); + const sourceFilePath = path.resolve(cwd(), parsedSourcePath); + const destFolderPath = path.resolve(cwd(), parsedDestPath); const sourceFilename = path.basename(sourceFilePath); - const destFilePath = path.join(destFolderPath, sourceFilename); + const destFilePath = path.resolve(destFolderPath, sourceFilename); const sourceStream = createReadStream(sourceFilePath); const destStream = createWriteStream(destFilePath); diff --git a/src/commands/filesystem/ls.js b/src/commands/filesystem/ls.js index 106d15e..1476895 100644 --- a/src/commands/filesystem/ls.js +++ b/src/commands/filesystem/ls.js @@ -9,7 +9,7 @@ export const ls = async () => { const folders = []; const filesList = []; for (const file of files) { - const fileStat = await fs.lstat(path.join(cwd(), file)); + const fileStat = await fs.lstat(path.resolve(cwd(), file)); const isFile = fileStat.isFile(); const fileType = isFile ? 'file' : 'directory'; const fileInfo = { Name: file, Type: fileType }; diff --git a/src/commands/filesystem/rm.js b/src/commands/filesystem/rm.js index 06ebde5..642843c 100644 --- a/src/commands/filesystem/rm.js +++ b/src/commands/filesystem/rm.js @@ -5,7 +5,7 @@ import { extractArgument } from '../../utils/utils.js'; export const rm = async (userInput) => { const parsedSourcePath = extractArgument(userInput, 1); - const sourceFilePath = path.join(cwd(), parsedSourcePath); + const sourceFilePath = path.resolve(cwd(), parsedSourcePath); await fs.unlink(sourceFilePath); }; diff --git a/src/commands/filesystem/rn.js b/src/commands/filesystem/rn.js index afab3fa..621107d 100644 --- a/src/commands/filesystem/rn.js +++ b/src/commands/filesystem/rn.js @@ -5,11 +5,12 @@ import { extractArgument } from '../../utils/utils.js'; export const rn = async (userInput) => { const parsedSourcePath = extractArgument(userInput, 1); - const renamedFilename = extractArgument(userInput, 2); + const parsedDestPath = extractArgument(userInput, 2); - const sourceFilePath = path.join(cwd(), parsedSourcePath); + const destFilename = path.basename(parsedDestPath); + const sourceFilePath = path.resolve(cwd(), parsedSourcePath); const sourceFileDir = path.dirname(sourceFilePath); - const renamedFilePath = path.join(sourceFileDir, renamedFilename); + const renamedFilePath = path.resolve(sourceFileDir, destFilename); await fs.rename(sourceFilePath, renamedFilePath); }; From cfcfb9a6bae3dd85e8d33b61e4eb4ed7b68d8c62 Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Mon, 5 Feb 2024 21:14:18 +0300 Subject: [PATCH 08/14] update start dir --- src/setStartingDir.js | 5 +---- src/user.js | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/setStartingDir.js b/src/setStartingDir.js index ee90d48..2f9307e 100644 --- a/src/setStartingDir.js +++ b/src/setStartingDir.js @@ -1,11 +1,8 @@ -import path from 'node:path'; import { chdir } from 'node:process'; import { getHomeDir } from './utils/utils.js'; export const setStartingDir = () => { - const userHomeDir = getHomeDir(); - // TODO remove 'Desktop' from path - const startingDir = path.join(userHomeDir, 'Desktop'); + const startingDir = getHomeDir(); chdir(startingDir); }; diff --git a/src/user.js b/src/user.js index 7be378b..aa73d3e 100644 --- a/src/user.js +++ b/src/user.js @@ -1,4 +1,4 @@ -import { argv, cwd } from 'node:process'; +import { cwd } from 'node:process'; import { logWithColor } from './utils/utils.js'; export const getUsernameFromArgs = () => { From 31a4711cec150b30a09527d390c14c37d4314ff6 Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Mon, 5 Feb 2024 21:57:52 +0300 Subject: [PATCH 09/14] add additional check --- src/commands/archiving/compress.js | 4 +++- src/commands/archiving/decompress.js | 4 +++- src/commands/filesystem/cp.js | 4 +++- src/commands/filesystem/ls.js | 2 +- src/utils/utils.js | 9 +++++++++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/commands/archiving/compress.js b/src/commands/archiving/compress.js index 1ff7821..3fe1f62 100644 --- a/src/commands/archiving/compress.js +++ b/src/commands/archiving/compress.js @@ -1,12 +1,14 @@ import { createReadStream, createWriteStream } from 'node:fs'; import { pipeline } from 'node:stream/promises'; import { createBrotliCompress } from 'node:zlib'; -import { extractArgument } from '../../utils/utils.js'; +import { checkFileExists, extractArgument } from '../../utils/utils.js'; export const compress = async (userInput) => { const sourceFilePath = extractArgument(userInput, 1); const destFilePath = extractArgument(userInput, 2); + await checkFileExists(sourceFilePath); + const gzip = createBrotliCompress(); const sourceStream = createReadStream(sourceFilePath); const destStream = createWriteStream(destFilePath); diff --git a/src/commands/archiving/decompress.js b/src/commands/archiving/decompress.js index d7422c7..e1a7f38 100644 --- a/src/commands/archiving/decompress.js +++ b/src/commands/archiving/decompress.js @@ -1,12 +1,14 @@ import { createReadStream, createWriteStream } from 'node:fs'; import { pipeline } from 'node:stream/promises'; import { createBrotliDecompress } from 'node:zlib'; -import { extractArgument } from '../../utils/utils.js'; +import { checkFileExists, extractArgument } from '../../utils/utils.js'; export const decompress = async (userInput) => { const sourceFilePath = extractArgument(userInput, 1); const destFilePath = extractArgument(userInput, 2); + await checkFileExists(sourceFilePath); + const gzip = createBrotliDecompress(); const sourceStream = createReadStream(sourceFilePath); const destStream = createWriteStream(destFilePath); diff --git a/src/commands/filesystem/cp.js b/src/commands/filesystem/cp.js index 08a828e..dbd7115 100644 --- a/src/commands/filesystem/cp.js +++ b/src/commands/filesystem/cp.js @@ -2,7 +2,7 @@ import { createReadStream, createWriteStream } from 'node:fs'; import path from 'node:path'; import { cwd } from 'node:process'; import { pipeline } from 'node:stream/promises'; -import { extractArgument } from '../../utils/utils.js'; +import { checkFileExists, extractArgument } from '../../utils/utils.js'; export const cp = async (userInput) => { const parsedSourcePath = extractArgument(userInput, 1); @@ -10,6 +10,8 @@ export const cp = async (userInput) => { const sourceFilePath = path.resolve(cwd(), parsedSourcePath); const destFolderPath = path.resolve(cwd(), parsedDestPath); + await checkFileExists(sourceFilePath); + const sourceFilename = path.basename(sourceFilePath); const destFilePath = path.resolve(destFolderPath, sourceFilename); const sourceStream = createReadStream(sourceFilePath); diff --git a/src/commands/filesystem/ls.js b/src/commands/filesystem/ls.js index 1476895..74fe012 100644 --- a/src/commands/filesystem/ls.js +++ b/src/commands/filesystem/ls.js @@ -9,7 +9,7 @@ export const ls = async () => { const folders = []; const filesList = []; for (const file of files) { - const fileStat = await fs.lstat(path.resolve(cwd(), file)); + const fileStat = await fs.stat(path.resolve(cwd(), file)); const isFile = fileStat.isFile(); const fileType = isFile ? 'file' : 'directory'; const fileInfo = { Name: file, Type: fileType }; diff --git a/src/utils/utils.js b/src/utils/utils.js index 1ece39f..e967430 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -1,3 +1,4 @@ +import fs from 'node:fs/promises'; import { homedir } from 'node:os'; export const extractArgument = (userInput, argNumber) => { @@ -26,3 +27,11 @@ export const logWithColor = (text, color) => { export const getHomeDir = () => { return homedir(); }; + +export const checkFileExists = async () => { + try { + await fs.access(sourceFilePath); + } catch (error) { + throw new Error('File does not exist.'); + } +}; From 6ff18fdefe6d969360703aeba9700f05e0da5352 Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Mon, 5 Feb 2024 22:05:21 +0300 Subject: [PATCH 10/14] refactor getCurrentDirectory --- src/commands/filesystem/add.js | 4 ++-- src/commands/filesystem/cat.js | 5 +++-- src/commands/filesystem/cp.js | 6 +++--- src/commands/filesystem/ls.js | 6 +++--- src/commands/filesystem/mv.js | 6 +++--- src/commands/filesystem/rm.js | 4 ++-- src/commands/filesystem/rn.js | 4 ++-- src/{setStartingDir.js => directory.js} | 6 +++++- src/events/input.js | 4 ++-- src/index.js | 2 +- src/user.js | 4 ++-- 11 files changed, 28 insertions(+), 23 deletions(-) rename src/{setStartingDir.js => directory.js} (58%) diff --git a/src/commands/filesystem/add.js b/src/commands/filesystem/add.js index 779e1e6..b92dd6d 100644 --- a/src/commands/filesystem/add.js +++ b/src/commands/filesystem/add.js @@ -1,12 +1,12 @@ import fs from 'node:fs/promises'; import path from 'node:path'; -import { cwd } from 'node:process'; +import { getCurrentDirectory } from '../../directory.js'; import { extractArgument } from '../../utils/utils.js'; export const add = async (userInput) => { const parsedFilename = extractArgument(userInput, 1); const filename = path.basename(parsedFilename); - const filePath = path.resolve(cwd(), filename); + const filePath = path.resolve(getCurrentDirectory(), filename); await fs.writeFile(filePath, '', { flag: 'wx' }); }; diff --git a/src/commands/filesystem/cat.js b/src/commands/filesystem/cat.js index 468acbd..2caf0a2 100644 --- a/src/commands/filesystem/cat.js +++ b/src/commands/filesystem/cat.js @@ -1,12 +1,13 @@ import { createReadStream } from 'node:fs'; import path from 'node:path'; -import { cwd, stdout } from 'node:process'; +import { stdout } from 'node:process'; import { pipeline } from 'node:stream/promises'; +import { getCurrentDirectory } from '../../directory.js'; import { extractArgument } from '../../utils/utils.js'; export const cat = async (userInput) => { const parsedPath = extractArgument(userInput, 1); - const sourceFilePath = path.resolve(cwd(), parsedPath); + const sourceFilePath = path.resolve(getCurrentDirectory(), parsedPath); const sourceStream = createReadStream(sourceFilePath); await pipeline(sourceStream, stdout, { end: false }); diff --git a/src/commands/filesystem/cp.js b/src/commands/filesystem/cp.js index dbd7115..2858e99 100644 --- a/src/commands/filesystem/cp.js +++ b/src/commands/filesystem/cp.js @@ -1,14 +1,14 @@ import { createReadStream, createWriteStream } from 'node:fs'; import path from 'node:path'; -import { cwd } from 'node:process'; import { pipeline } from 'node:stream/promises'; +import { getCurrentDirectory } from '../../directory.js'; import { checkFileExists, extractArgument } from '../../utils/utils.js'; export const cp = async (userInput) => { const parsedSourcePath = extractArgument(userInput, 1); const parsedDestPath = extractArgument(userInput, 2); - const sourceFilePath = path.resolve(cwd(), parsedSourcePath); - const destFolderPath = path.resolve(cwd(), parsedDestPath); + const sourceFilePath = path.resolve(getCurrentDirectory(), parsedSourcePath); + const destFolderPath = path.resolve(getCurrentDirectory(), parsedDestPath); await checkFileExists(sourceFilePath); diff --git a/src/commands/filesystem/ls.js b/src/commands/filesystem/ls.js index 74fe012..47998b7 100644 --- a/src/commands/filesystem/ls.js +++ b/src/commands/filesystem/ls.js @@ -1,15 +1,15 @@ import fs from 'node:fs/promises'; import path from 'node:path'; -import { cwd } from 'node:process'; +import { getCurrentDirectory } from '../../directory.js'; export const ls = async () => { - const files = await fs.readdir(cwd()); + const files = await fs.readdir(getCurrentDirectory()); const fileInfoArray = []; const folders = []; const filesList = []; for (const file of files) { - const fileStat = await fs.stat(path.resolve(cwd(), file)); + const fileStat = await fs.stat(path.resolve(getCurrentDirectory(), file)); const isFile = fileStat.isFile(); const fileType = isFile ? 'file' : 'directory'; const fileInfo = { Name: file, Type: fileType }; diff --git a/src/commands/filesystem/mv.js b/src/commands/filesystem/mv.js index 472c8ea..9fde6a9 100644 --- a/src/commands/filesystem/mv.js +++ b/src/commands/filesystem/mv.js @@ -1,8 +1,8 @@ -import { cwd } from 'node:process'; +import { getCurrentDirectory } from '../../directory.js'; import { cp } from './cp.js'; import { rm } from './rm.js'; export const mv = async (userInput) => { - await cp(userInput, cwd()); - await rm(userInput, cwd()); + await cp(userInput, getCurrentDirectory()); + await rm(userInput, getCurrentDirectory()); }; diff --git a/src/commands/filesystem/rm.js b/src/commands/filesystem/rm.js index 642843c..8286ca9 100644 --- a/src/commands/filesystem/rm.js +++ b/src/commands/filesystem/rm.js @@ -1,11 +1,11 @@ import fs from 'node:fs/promises'; import path from 'node:path'; -import { cwd } from 'node:process'; +import { getCurrentDirectory } from '../../directory.js'; import { extractArgument } from '../../utils/utils.js'; export const rm = async (userInput) => { const parsedSourcePath = extractArgument(userInput, 1); - const sourceFilePath = path.resolve(cwd(), parsedSourcePath); + const sourceFilePath = path.resolve(getCurrentDirectory(), parsedSourcePath); await fs.unlink(sourceFilePath); }; diff --git a/src/commands/filesystem/rn.js b/src/commands/filesystem/rn.js index 621107d..2657995 100644 --- a/src/commands/filesystem/rn.js +++ b/src/commands/filesystem/rn.js @@ -1,6 +1,6 @@ import fs from 'node:fs/promises'; import path from 'node:path'; -import { cwd } from 'node:process'; +import { getCurrentDirectory } from '../../directory.js'; import { extractArgument } from '../../utils/utils.js'; export const rn = async (userInput) => { @@ -8,7 +8,7 @@ export const rn = async (userInput) => { const parsedDestPath = extractArgument(userInput, 2); const destFilename = path.basename(parsedDestPath); - const sourceFilePath = path.resolve(cwd(), parsedSourcePath); + const sourceFilePath = path.resolve(getCurrentDirectory(), parsedSourcePath); const sourceFileDir = path.dirname(sourceFilePath); const renamedFilePath = path.resolve(sourceFileDir, destFilename); diff --git a/src/setStartingDir.js b/src/directory.js similarity index 58% rename from src/setStartingDir.js rename to src/directory.js index 2f9307e..1927138 100644 --- a/src/setStartingDir.js +++ b/src/directory.js @@ -1,4 +1,4 @@ -import { chdir } from 'node:process'; +import { chdir, cwd } from 'node:process'; import { getHomeDir } from './utils/utils.js'; export const setStartingDir = () => { @@ -6,3 +6,7 @@ export const setStartingDir = () => { chdir(startingDir); }; + +export const getCurrentDirectory = () => { + return cwd(); +}; diff --git a/src/events/input.js b/src/events/input.js index 3d4de09..e709292 100644 --- a/src/events/input.js +++ b/src/events/input.js @@ -1,4 +1,3 @@ -import { cwd } from 'node:process'; import { compress } from '../commands/archiving/compress.js'; import { decompress } from '../commands/archiving/decompress.js'; import { add } from '../commands/filesystem/add.js'; @@ -16,6 +15,7 @@ import { cpus } from '../commands/os-info/cpus.js'; import { eol } from '../commands/os-info/eol.js'; import { homedir } from '../commands/os-info/homedir.js'; import { username } from '../commands/os-info/username.js'; +import { getCurrentDirectory } from '../directory.js'; import { COMMANDS, parseCommand } from '../parseCommand.js'; import { logWithColor } from '../utils/utils.js'; @@ -106,6 +106,6 @@ export const setInputEvent = () => { logWithColor(`Operation failed\n${err.message}`, 'red'); } - console.log(`\nYou are currently in ${cwd()}`); + console.log(`\nYou are currently in ${getCurrentDirectory()}`); }); }; diff --git a/src/index.js b/src/index.js index d99cd82..8b5a15d 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,6 @@ import { setExitEvents } from './events/exit.js'; import { setInputEvent } from './events/input.js'; -import { setStartingDir } from './setStartingDir.js'; +import { setStartingDir } from './directory.js'; import { getUsernameFromArgs, greetUser } from './user.js'; const init = async () => { diff --git a/src/user.js b/src/user.js index aa73d3e..2eb2f15 100644 --- a/src/user.js +++ b/src/user.js @@ -1,4 +1,4 @@ -import { cwd } from 'node:process'; +import { getCurrentDirectory } from './directory.js'; import { logWithColor } from './utils/utils.js'; export const getUsernameFromArgs = () => { @@ -25,5 +25,5 @@ export const getUsernameFromArgs = () => { export const greetUser = (username) => { console.log(`Welcome to the File Manager, ${username}!`); - console.log(`You are currently in ${cwd()}`); + console.log(`You are currently in ${getCurrentDirectory()}`); }; From 228bc4dbdaae63178d64f219b43805187e6a5ac1 Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Mon, 5 Feb 2024 22:12:14 +0300 Subject: [PATCH 11/14] refactoring --- src/{parseCommand.js => commands/commands.js} | 2 +- src/events/input.js | 2 +- src/utils/utils.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) rename src/{parseCommand.js => commands/commands.js} (91%) diff --git a/src/parseCommand.js b/src/commands/commands.js similarity index 91% rename from src/parseCommand.js rename to src/commands/commands.js index 4995411..3c1a742 100644 --- a/src/parseCommand.js +++ b/src/commands/commands.js @@ -1,4 +1,4 @@ -import { extractArgument } from './utils/utils.js'; +import { extractArgument } from '../utils/utils.js'; export const COMMANDS = { UP: 'up', diff --git a/src/events/input.js b/src/events/input.js index e709292..81b2fd7 100644 --- a/src/events/input.js +++ b/src/events/input.js @@ -1,5 +1,6 @@ import { compress } from '../commands/archiving/compress.js'; import { decompress } from '../commands/archiving/decompress.js'; +import { COMMANDS, parseCommand } from '../commands/commands.js'; import { add } from '../commands/filesystem/add.js'; import { cat } from '../commands/filesystem/cat.js'; import { cd } from '../commands/filesystem/cd.js'; @@ -16,7 +17,6 @@ import { eol } from '../commands/os-info/eol.js'; import { homedir } from '../commands/os-info/homedir.js'; import { username } from '../commands/os-info/username.js'; import { getCurrentDirectory } from '../directory.js'; -import { COMMANDS, parseCommand } from '../parseCommand.js'; import { logWithColor } from '../utils/utils.js'; export const setInputEvent = () => { diff --git a/src/utils/utils.js b/src/utils/utils.js index e967430..d84c26a 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -11,6 +11,7 @@ export const extractArgument = (userInput, argNumber) => { return argument; }; + export const logWithColor = (text, color) => { const colors = { reset: '\x1b[0m', From b0d48b32128a26a1d5fc451e5dcc99a23ad95965 Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Mon, 5 Feb 2024 23:46:11 +0300 Subject: [PATCH 12/14] add args validation --- src/commands/commands.js | 57 +++++++++++++++++++++++++--------------- src/events/input.js | 43 +++++++++++++++--------------- src/utils/utils.js | 11 +++++--- 3 files changed, 64 insertions(+), 47 deletions(-) diff --git a/src/commands/commands.js b/src/commands/commands.js index 3c1a742..51780d0 100644 --- a/src/commands/commands.js +++ b/src/commands/commands.js @@ -1,31 +1,46 @@ -import { extractArgument } from '../utils/utils.js'; +import { extractArgument, extractArguments } from '../utils/utils.js'; export const COMMANDS = { - UP: 'up', - CD: 'cd', - LS: 'ls', - CAT: 'cat', - ADD: 'add', - RN: 'rn', - CP: 'cp', - MV: 'mv', - RM: 'rm', - HASH: 'hash', - COMPRESS: 'compress', - DECOMPRESS: 'decompress', - EXIT: '.exit', - OS: 'os', - OS_EOL: '--EOL', - OS_CPUS: '--cpus', - OS_HOMEDIR: '--homedir', - OS_USERNAME: '--username', - OS_ARCH: '--architecture', + UP: { commandName: 'up', expectedArgs: 0 }, + CD: { commandName: 'cd', expectedArgs: 1 }, + LS: { commandName: 'ls', expectedArgs: 0 }, + CAT: { commandName: 'cat', expectedArgs: 1 }, + ADD: { commandName: 'add', expectedArgs: 1 }, + RN: { commandName: 'rn', expectedArgs: 2 }, + CP: { commandName: 'cp', expectedArgs: 2 }, + MV: { commandName: 'mv', expectedArgs: 2 }, + RM: { commandName: 'rm', expectedArgs: 2 }, + HASH: { commandName: 'hash', expectedArgs: 1 }, + COMPRESS: { commandName: 'compress', expectedArgs: 2 }, + DECOMPRESS: { commandName: 'decompress', expectedArgs: 2 }, + EXIT: { commandName: '.exit', expectedArgs: 0 }, + OS: { commandName: 'os', expectedArgs: 1 }, + OS_EOL: { commandName: '--EOL' }, + OS_CPUS: { commandName: '--cpus' }, + OS_HOMEDIR: { commandName: '--homedir' }, + OS_USERNAME: { commandName: '--username' }, + OS_ARCH: { commandName: '--architecture' }, +}; + +export const checkArgumentCount = (command, userInput) => { + for (const key in COMMANDS) { + if (COMMANDS[key].commandName === command) { + const expectedArgs = COMMANDS[key].expectedArgs; + const args = extractArguments(userInput); + + if (args.length - 1 !== expectedArgs) { + throw new Error(`Incorrect number of arguments provided. Expected ${expectedArgs} arguments.`); + } + } + } }; export const parseCommand = (userInput) => { const command = extractArgument(userInput, 0); - if (command === COMMANDS.OS) { + checkArgumentCount(command, userInput); + + if (command === COMMANDS.OS.commandName) { const argument = extractArgument(userInput, 1); return argument; } diff --git a/src/events/input.js b/src/events/input.js index 81b2fd7..a6a6bde 100644 --- a/src/events/input.js +++ b/src/events/input.js @@ -21,81 +21,80 @@ import { logWithColor } from '../utils/utils.js'; export const setInputEvent = () => { process.stdin.on('data', async (data) => { - const userInput = data.toString().replace(/(\r\n|\n|\r)/gm, ''); - - const command = parseCommand(userInput); - try { + const userInput = data.toString().replace(/(\r\n|\n|\r)/gm, ''); + const command = parseCommand(userInput); + switch (command) { - case COMMANDS.UP: + case COMMANDS.UP.commandName: up(); break; - case COMMANDS.CD: + case COMMANDS.CD.commandName: cd(userInput); break; - case COMMANDS.LS: + case COMMANDS.LS.commandName: await ls(); break; - case COMMANDS.CAT: + case COMMANDS.CAT.commandName: await cat(userInput); break; - case COMMANDS.ADD: + case COMMANDS.ADD.commandName: await add(userInput); break; - case COMMANDS.RN: + case COMMANDS.RN.commandName: await rn(userInput); break; - case COMMANDS.CP: + case COMMANDS.CP.commandName: await cp(userInput); break; - case COMMANDS.MV: + case COMMANDS.MV.commandName: await mv(userInput); break; - case COMMANDS.RM: + case COMMANDS.RM.commandName: await rm(userInput); break; - case COMMANDS.HASH: + case COMMANDS.HASH.commandName: await calculateHash(userInput); break; - case COMMANDS.COMPRESS: + case COMMANDS.COMPRESS.commandName: await compress(userInput); break; - case COMMANDS.DECOMPRESS: + case COMMANDS.DECOMPRESS.commandName: await decompress(userInput); break; - case COMMANDS.OS_EOL: + case COMMANDS.OS_EOL.commandName: eol(); break; - case COMMANDS.OS_CPUS: + case COMMANDS.OS_CPUS.commandName: cpus(); break; - case COMMANDS.OS_HOMEDIR: + case COMMANDS.OS_HOMEDIR.commandName: homedir(); break; - case COMMANDS.OS_USERNAME: + case COMMANDS.OS_USERNAME.commandName: username(); break; - case COMMANDS.OS_ARCH: + case COMMANDS.OS_ARCH.commandName: arch(); break; - case COMMANDS.EXIT: + case COMMANDS.EXIT.commandName: process.exit(0); default: diff --git a/src/utils/utils.js b/src/utils/utils.js index d84c26a..c30f63f 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -1,9 +1,12 @@ import fs from 'node:fs/promises'; import { homedir } from 'node:os'; -export const extractArgument = (userInput, argNumber) => { - const args = userInput.trim().match(/(?:[^\s"]+|"[^"]*")/g) || []; +export const extractArguments = (userInput) => { + return userInput.trim().match(/(?:[^\s"]+|"[^"]*")/g) || []; +}; +export const extractArgument = (userInput, argNumber) => { + const args = extractArguments(userInput); let argument = args[argNumber] || ''; if (argument.startsWith('"') && argument.endsWith('"')) { argument = argument.substring(1, argument.length - 1); @@ -29,9 +32,9 @@ export const getHomeDir = () => { return homedir(); }; -export const checkFileExists = async () => { +export const checkFileExists = async (filePath) => { try { - await fs.access(sourceFilePath); + await fs.access(filePath); } catch (error) { throw new Error('File does not exist.'); } From fa68de985e4ac2763047f14006f3eabc625b3982 Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Mon, 5 Feb 2024 23:57:06 +0300 Subject: [PATCH 13/14] remove unused args --- src/commands/filesystem/mv.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/commands/filesystem/mv.js b/src/commands/filesystem/mv.js index 9fde6a9..573d704 100644 --- a/src/commands/filesystem/mv.js +++ b/src/commands/filesystem/mv.js @@ -1,8 +1,7 @@ -import { getCurrentDirectory } from '../../directory.js'; import { cp } from './cp.js'; import { rm } from './rm.js'; export const mv = async (userInput) => { - await cp(userInput, getCurrentDirectory()); - await rm(userInput, getCurrentDirectory()); + await cp(userInput); + await rm(userInput); }; From 4dd2f75fc7fba85e337a7c187dbe4cb61ef8b93b Mon Sep 17 00:00:00 2001 From: Dzmitry Yarmoshkin Date: Mon, 5 Feb 2024 23:58:05 +0300 Subject: [PATCH 14/14] fix args count for rm command --- src/commands/commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/commands.js b/src/commands/commands.js index 51780d0..714c424 100644 --- a/src/commands/commands.js +++ b/src/commands/commands.js @@ -9,7 +9,7 @@ export const COMMANDS = { RN: { commandName: 'rn', expectedArgs: 2 }, CP: { commandName: 'cp', expectedArgs: 2 }, MV: { commandName: 'mv', expectedArgs: 2 }, - RM: { commandName: 'rm', expectedArgs: 2 }, + RM: { commandName: 'rm', expectedArgs: 1 }, HASH: { commandName: 'hash', expectedArgs: 1 }, COMPRESS: { commandName: 'compress', expectedArgs: 2 }, DECOMPRESS: { commandName: 'decompress', expectedArgs: 2 },