Створення консольних додатків
Традиційний спосіб керування консольним додатком (CLI applications) - передача параметрів з консольного рядка під час його запуску. Скорочення CLI (command-line interface) перекладається як 'інтерфейс командного рядка'
Вище ми зазначали, що передані параметри під час запуску скрипта доступні в
масиві process.args
: ["node", "/…/youscript.js", "param 1", "param 2", …]
.
І щоб одержати ці параметри треба виконати process.argv.slice(2)
, який
поверне всі розділені пробілами параметри: ["param 1", "param 2", …]
Але обробляти всілякі комбінації параметрів та їх формати вкрай незручно,
для цього зазвичай використовують сторонні npm-модулі. Один з найпопулярніших,
який ми будемо використовувати - це модуль commander
Щоб використовувати інтерактивне введення в консолі на кшталт питання-відповідь, ми будемо
використовувати стандартний модуль Node.js readline
. Ініціалізація достатньо
проста:
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin, // введення зі стандартного потоку
output: process.stdout, // виведення у стандартний потік
});
Ми підключаємо модуль та створюємо екземпляр rl
де в опціях передаємо потоки введення
та виведення, консоль, файл і т.д. У нашому випадку ми беремо стандартні потоки та
будемо працювати в консолі, де запускаємо скрипт. Обробка кожного введеного рядка
відбувається через подію line
:
rl.on('line', cmd => {
console.log(`You just typed: ${cmd}`);
});
Але нас більше цікавить можливість поставити користувачеві питання та отримати на
нього відповідь, аналогічно функції prompt
із браузеру:
rl.question('Як вас звати?', answer => {
console.log(`Приємно познайомитися ${answer}`);
});
Також при якійсь тривалій операції ми можемо поставити розмову на паузу або іншими словами заблокувати введення:
rl.pause();
Щоб закрити інтерфейсом readline, необхідно виконати функцію:
rl.close();
А тепер напишемо додаток – «Вгадай число», де необхідно вгадати задумане програмою число від 1 до 10 та програма в кінці виведе, за скільки спроб нам це вдалося.
Нам знадобляться стандартні модулі fs
, readline
і нестандартні, а отже,
їх треба встановити за допомогою npm
, модуль
commander
та
colors
. Нижче лістинг програми та її
розбір.
const readline = require('readline');
const fs = require('fs').promises;
const { program } = require('commander');
require('colors');
program.option(
'-f, --file [type]',
'file for saving game results',
'results.txt',
);
program.parse(process.argv);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
let count = 0;
const logFile = program.opts().file;
const mind = Math.floor(Math.random() * 10) + 1;
const isValid = value => {
if (isNaN(value)) {
console.log('Введіть число!'.red);
return false;
}
if (value < 1 || value > 10) {
console.log('Число має бути в діапазоні від 1 до 10'.red);
return false;
}
return true;
};
const log = async data => {
try {
await fs.appendFile(logFile, `${data}\n`);
console.log(`Вдалося зберегти результат у файл ${logFile}`.green);
} catch (err) {
console.log(`Не вдалося зберегти файл ${logFile}`.red);
}
};
const game = () => {
rl.question(
'Введіть число від 1 до 10, щоб вгадати задумане: '.yellow,
value => {
let a = +value;
if (!isValid(a)) {
game();
return;
}
count += 1;
if (a === mind) {
console.log('Вітаю Ви вгадали число за %d крок(ів)'.green, count);
log(
`${new Date().toLocaleDateString()}: Вітаю Ви вгадали число за ${count} крок(ів)`,
).finally(() => rl.close());
return;
}
console.log('Ви не вгадали ще спроба'.red);
game();
},
);
};
game();
Уся програма складається із трьох функцій. Основна функція це функції гри
game()
, яка викликає себе рекурсивно до тих пір, поки ми не вгадаємо
задумане число. Спочатку ми підключаємо модуль colors
, який дозволяє нам
розфарбовувати текст у консолі у різноманітні кольори далі підключаємо модуль
commander
, це повноцінне рішення для створення інтерфейсів командного рядка:
const { program } = require('commander');
program.option(
'-f, --file [type]',
'file for saving game results',
'results.txt',
);
program.parse(process.argv);
Ми вказуємо, що опціонально чекаємо на введення параметра -f
або довший запис
--file
. Іншими словами ми визначаємо запуск програми у наступному вигляді
node game.js -f my_log.txt
Ми вказуємо, що необхідно, в program.file
покласти значення my_log.txt
,
але в той же час ми вказуємо, третім параметром program.option
, що якщо
параметр -f
не буде переданий у запуску, то за замовчуванням program.file
буде
дорівнювати results.txt
Далі ми виконуємо ініціалізацію модуля readline
. Вводимо три змінні
які ми будемо надалі використовувати: count
- це підрахунок кількості
спроб, які знадобилися, щоб вгадати число, logFile
- ім'я файлу куди
будуть збережені результати гри, mind
- це випадкове число від 1 до 10,
яке необхідно відгадати.
Функція isValid
відповідає за валідацію введених значень у консолі, вона
перевіряє, щоб значення було саме числом і лежало в діапазоні від 1
до 10. Якщо дані валідні, то функція повертає істину, якщо ні – брехню
Функція log
відповідає за збереження результатів гри. Вона використовує функцію
appendFile
модуля fs
для запису даних. Якщо файл існує, то результати
будуть дописані в існуючий файл, якщо немає файлу - він буде створений. Зверніть
увагу, що функція асинхронна і ми очікуємо виконання операції збереження
результатів.
І нарешті ми підійшли до основної функції game. Усередині відбувається виклик методу
rl.question(
'Введіть число від 1 до 10, щоб вгадати задумане: '.yellow,
(value) => {...});
який прослуховує консоль і при введенні значення викликає callback
функцію,
яка обробляє введене значення.
Якщо ми не проходимо валідацію, то запускаємо функцію гри наново:
let a = +value;
if (!isValid(a)) {
game();
return;
}
Якщо валідацію пройдено, то ми збільшуємо лічильник спроб на 1. Далі ми
порівнюємо введене значення із «задуманим». Якщо число вгадано, ми виводимо
вітання та кількість спроб, витрачену на гру, потім за допомогою функції
log
зберігаємо результат у файлі, і оскільки функція повертає проміс ми в
методі finally
закриваємо інтерфейс введення rl.close()
, якщо ж результат не
співпав, виконуємо рекурсію до тих пір, поки число не буде відгадано.