Skip to content
Merged
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
2 changes: 2 additions & 0 deletions mocks/test.tsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Тестовое объявление об аренде Комнатка в Париже 2022-01-10T19:00:00.000Z Paris preview.jpg photo1;photo2;photo3 false true 4.6 house 1 2 100 Breakfast;Baby seat https://user 50 48.85661 2.351499
Тестовое объявление об аренде2 Комнатка в Кёльне 2022-04-10T19:00:00.000Z Cologne preview.jpg photo1;photo2;photo3 true false 4.6 room 1 2 100 Breakfast;Baby seat https://user 33 50.938361 6.959974
76 changes: 51 additions & 25 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,27 @@
"start": "npm run build && node ./dist/main.js",
"build": "npm run clean && npm run compile",
"lint": "eslint src/ --ext .ts",
"lint:fix": "eslint src/ --ext .ts --fix",
"compile": "tsc -p tsconfig.json",
"clean": "rimraf dist",
"ts": "ts-node"
},
"dependencies": {
"chalk": "5.6.2"
},
"devDependencies": {
"@types/node": "18.17.17",
"@typescript-eslint/eslint-plugin": "6.7.0",
"@typescript-eslint/parser": "6.7.0",
"eslint": "8.49.0",
"eslint-config-htmlacademy": "9.1.1",
"rimraf": "5.0.1",
"ts-node": "10.9.1",
"ts-node": "10.9.2",
"typescript": "5.2.2"
},
"type": "module",
"engines": {
"node": "^18.0.0",
"node": ">20.19",
"npm": ">=8"
}
}
40 changes: 40 additions & 0 deletions src/cli/cli-application.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { Command } from './commands/command.interface.js';
import { CommandParser } from './command-parser.js';

type CommandCollection = Record<string, Command>;

export class CLIApplication {
private commands: CommandCollection = {};

constructor(
private readonly defaultCommand: string = '--help'
) {}

public registerCommands(commandList: Command[]): void {
commandList.forEach((command) => {
if (Object.hasOwn(this.commands, command.getName())) {
throw new Error(`Command ${command.getName()} is already registered`);
}
this.commands[command.getName()] = command;
});
}

public getCommand(commandName: string): Command {
return this.commands[commandName] ?? this.getDefaultCommand();
}

public getDefaultCommand(): Command | never {
if (! this.commands[this.defaultCommand]) {
throw new Error(`The default command (${this.defaultCommand}) is not registered.`);
}
return this.commands[this.defaultCommand];
}

public processCommand(argv: string[]): void {
const parsedCommand = CommandParser.parse(argv);
const [commandName] = Object.keys(parsedCommand);
const command = this.getCommand(commandName);
const commandArguments = parsedCommand[commandName] ?? [];
command.execute(...commandArguments);
}
}
19 changes: 19 additions & 0 deletions src/cli/command-parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
type ParsedCommand = Record<string, string[]>

export class CommandParser {
static parse(cliArguments: string[]): ParsedCommand {
const parsedCommand: ParsedCommand = {};
let currentCommand = '';

for (const argument of cliArguments) {
if (argument.startsWith('--')) {
parsedCommand[argument] = [];
currentCommand = argument;
} else if (currentCommand && argument) {
parsedCommand[currentCommand].push(argument);
}
}

return parsedCommand;
}
}
4 changes: 4 additions & 0 deletions src/cli/commands/command.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Command {
getName(): string;
execute(...params: string[]): void;
}
25 changes: 25 additions & 0 deletions src/cli/commands/help.command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { Command } from './command.interface.js';
import chalk from 'chalk';

export class HelpCommand implements Command {
execute(..._params: string[]): void {
console.info(chalk.bgBlackBright(
`
Программа для подготовки данных для REST API сервера.
Пример:
cli.js --<command> [--arguments]
Команды:
--version: # выводит номер версии
--help: # печатает этот текст
--import <path>: # импортирует данные из TSV
--generate <n> <path> <url> # генерирует произвольное количество тестовых данных
`
));

}

getName(): string {
return '--help';
}

}
23 changes: 23 additions & 0 deletions src/cli/commands/import.command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { Command } from './command.interface.js';
import { TsvFileReader } from '../../shared/lib/tsv-file-reader.js';

export class ImportCommand implements Command {
execute(...params: string[]): void {
const [filename] = params;
const fileReader = new TsvFileReader(filename);
try {
fileReader.read();
console.log(fileReader.toArray());
} catch (error) {
if (!(error instanceof Error)) {
throw error;
}
console.error(`Details ${error.message}`);
}
}

getName(): string {
return '--import';
}

}
45 changes: 45 additions & 0 deletions src/cli/commands/version.command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { resolve } from 'node:path';
import type { Command } from './command.interface.js';
import { readFileSync } from 'node:fs';
import chalk from 'chalk';

interface PackageJsonData {
version: string
}


export class VersionCommand implements Command {
constructor(private readonly filepath: string = './package.json') {
}

private readVersion(): string {
const json = readFileSync(resolve(this.filepath), 'utf-8');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тоже полезно знать, что функции файловой системы которые *Sync блокируют выполнение потока, то есть использовать их нужно аккуратно и тогда когда ты действительно понимаешь что это не затормозит какие то паралельные процессы.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

да, спасибо, тут по примеру из лекции просто синхронную функцию вызвал

const parsedData: unknown = JSON.parse(json);

if (!this.isPackageJson(parsedData)) {
throw new Error('Invalid package.json');
}

return parsedData.version;
}

private isPackageJson(value: unknown): value is PackageJsonData {
return value !== null && typeof value === 'object' && !Array.isArray(value) && Object.hasOwn(value, 'version');
}

execute(..._params: string[]): void {
try {
const ver = this.readVersion();
console.log(chalk.green(ver));
} catch (e) {
console.error(`Failed to read version from ${this.filepath}`);
if (e instanceof Error) {
console.error(chalk.bgRed(e.message));
}
}
}

getName(): string {
return '--version';
}
}
5 changes: 5 additions & 0 deletions src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './cli-application.js';
export * from './command-parser.js';
export * from './commands/help.command.js';
export * from './commands/version.command.js';
export * from './commands/import.command.js';
15 changes: 15 additions & 0 deletions src/main.cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env node
import { CLIApplication, HelpCommand, ImportCommand, VersionCommand } from './cli/index.js';

function bootstrap() {
const cliApplication = new CLIApplication();
cliApplication.registerCommands([
new HelpCommand(),
new VersionCommand(),
new ImportCommand(),
]);

cliApplication.processCommand(process.argv);
}

bootstrap();
3 changes: 3 additions & 0 deletions src/shared/lib/file-reader.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface FileReader {
read(): void
}
Loading