Home

Створення консольних додатків

Зміст

  1. Функція invokeAction()
  2. process.args - вбудований
  3. Commander
  4. yargs
  5. Консольна гра

Функція invokeAction()

    const bols = require("./bols");
    async function invoActions({action, id, title, author}) {
        switch(action) {
            case "getAll":
                const allBols = await bols.readAll();
                console.log(allBols);
                return;
    
            case "getById":
                const bol = await bols.getById(id);
                console.log(bol);
                return;
    
            case "create":
                const newBol = await bols.writeNewBol({title, author});
                console.log(newBol);
                return;
    
            case "update":
                const updateBol = await bols.updateById(id, {title, author});
                console.log(updateBol);
                return;
    
            case "remove":
                const removeBol = await bols.deleteById(id);
                console.log(removeBol);
                return;
    
            default:
                return;
        }
    }

process.args

Вище ми зазначали, що передані параметри під час запуску скрипта доступні в масиві process.args: ["node", "/…/youscript.js", "param 1", "param 2", …]. І щоб одержати ці параметри, потрібно виконати process.argv.slice(2), який поверне всі розділені пробілами параметри: ["param 1", "param 2", …]

Приклад введення команди в консоль

    node app.js --action update --id 132 --title "Title" --author "Author"

Приклад частини коду. Перевірив

    const actiionIndex = process.argv.indexOf("--action");
    if(actiionIndex != -1) {
        const action = process.argv[actiionIndex + 1];
        const id = process.argv[actiionIndex + 3];
        const title = process.argv[actiionIndex + 5];
        const author = process.argv[actiionIndex + 7];
        // console.log({action, id, title, author});
        invoActions({action, id, title, author})
            .catch(err => console.error(err));
    }

Але обробляти всілякі комбінації параметрів та їх формати вкрай незручно, для цього зазвичай використовують сторонні npm-модулі. Один з найпопулярніших, який ми будемо використовувати – це модуль commander

Commander

npm install commander

    const { program } = require('commander'); 

    program
    .option("-a, --action <action>", "Action to invoke")
    .option("-i, --id <id>", "Bols id")
    .option("-t, --title <title>", "Bol title")
    .option("-u, --author <author>", "Bol author")

    program.parse(process.args);
    const options = program.opts();

    invoActions(options).catch(err => console.error(err));

Написати команду в консоль

node comander.js --action update --id "wen" --title "Title" --author "Author"

node comander.js -a update -i "wen" -t "Title" -u "Author"

yargs

npm i yargs

Те саме робить що і Commander - призначений для консольного додатку. Витягує у обєкт ключ значення з рядка консольного додатку

    const argv = require("yargs").argv;
    Вводимо в термінал: 
    node index.js --action create --fileName file_1.txt --content "New text"
    console.log("argv); // 
                

Консольна гра

Напишемо застосунок – «Вгадай число», де необхідно вгадати задумане програмою число від 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();

Щоб використовувати інтерактивне введення в консолі на кшталт питання-відповідь, ми будемо використовувати стандартний модуль 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();

Вся програма складається із трьох функцій. Основна функція – це функція гри 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(), якщо ж результат не співпав, виконуємо рекурсію до тих пір, доки число не буде відгадане.


node-game