Приготовьте вкусный CLI

Node.js
Приготовьте вкусный CLI

Наверху написано, очень хочу написать рецепт, страдаю ограниченными кулинарными способностями, поэтому заголовок враньё, ха-ха ^_~

Сегодня поговорим о разработке инструментов командной строки (например, CLI: интерфейс командной строки, CLI будет использоваться для замены длинных существительных инструментов командной строки ниже).

Прочитав эту статью, вы получите более полное представление о разработке CLI от начала до конца.

Вы также можете добавить эту статью в закладки, когда захотите разработать CLI, вернитесь и посмотрите ее, вы всегда найдете то, что хотите.

Дэниел: Арахисовая кола готова, просто подожди и начинай.

Ладно, пошли, пошли!

> Сделайте первый шаг: инициализируйте проект

Создайте пустой каталог проекта (все начинается сcook-cliНапример, так здесь мы называем этоcook-cli), а затем нажмите команду в этом каталоге для инициализации, процесс выглядит следующим образом:

$ mkdir cook-cli
$ cd cook-cli
$ npm init --yes

пройти черезnpm initкоманда, инициализирует каталог вNode.jsпроект, он будет вcook-cliсоздается в каталогеpackage.jsonдокумент.

Добавить--yesОн автоматически ответит на все вопросы, заданные в процессе инициализации.Вы можете попробовать убрать этот параметр и отвечать на вопросы по порядку самостоятельно.

> Основная ветка открыта: скелетный код CLI

Проект инициализирован, давайте добавим скелетный код и позволим CLI немного поработать.

  • исполнитель

мы создаемsrc/index.jsФайл, отвечающий за реализацию функциональной логики CLI, и есть фактическая работа. код показывает, как показано ниже:

export function cli(args) {
    console.log('I like cooking');
}
  • пресс-секретарь

Затем создайтеbin/cookфайл, который является исполняемым входным файлом CLI и представителем CLI в исполняемой среде. код показывает, как показано ниже:

#!/usr/bin/env node

require = require('esm')(module /*, options*/);
require('../src').cli(process.argv);

Осторожно, вы обнаружите, что он используется здесьesmЭтот модуль, его роль состоит в том, чтобы позволить нам использовать его непосредственно в исходном коде js.ECMAScript modulesканонические загрузочные модули, т.е. использовать напрямуюimportа такжеexport. надsrc/index.jsможно написать прямо в кодеexportблагодаря этому модулю.

(Пожалуйста, запустите в корневом каталоге проектаnpm i esmустановить модуль)

  • официальное объявление

У нас есть представители, но мы должны их предать гласности. так вpackage.jsonувеличить вbinзаявление, объявляющее о существовании пресс-секретаря. следующим образом:

{
  ...
  "bin": {
    "cook": "./bin/cook"
  },
  ...
}

> Репетиция момента: локальный запуск и отладка

До появления CLI локальная разработка и отладка были необходимы, поэтому был необходим удобный путь отладки.

Дэниел: Разрабатывая веб-приложение, я могу отлаживать функционал через браузер. Как насчет CLI вчера?

CLI в конечном итоге запускается на терминале, поэтому сначала нам нужно зарегистрировать его как локальную командную строку. Метод очень прост, достаточно выполнить следующую команду в корневом каталоге проекта:

$ npm link

Эта команда зарегистрируетcookCLI и свяжите его код логики выполнения с каталогом вашего проекта, чтобы каждый раз, когда вы его сохраняете, он сразу же вступал в силу.

Попробуйте выполнить следующие команды:

$ cook

Дэниел: Красиво! Но у меня все еще есть проблема, я хочу установить точки останова в vscode для отладки, поэтому иногда проще устранить неполадки.

Ты прав. Способ тоже очень простой, достаточно добавить в vscode следующую конфигурацию, путь такой:调试 > 添加配置. В соответствии с фактическими отлаживаемыми параметрами команды изменитеargsможно использовать значение.

{
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Cook",
            "program": "${workspaceFolder}/bin/cook",
            "args": ["hello"] // Fill in the parameters you want to debug
        }
    ]
}

> Распознавание намерений: анализ входных параметров

Небольшая интерлюдия: хотя вы можете часто сталкиваться с различными CLI в своей работе, вот краткое введение в некоторые термины, связанные с CLI:

  • Команда и подкоманда
# cook 即为命令
$ cook

# start 即为 cook 的 子命令
$ cook start
  • параметры команды
# -V 为简写模式(short flag)的选项(注意:只能一个字母,多个字母代表多个选项)
$ cook -V

# --version 为全写模式(long name)的选项
$ cook --version
  • Аргументы команды (аргумент)
# source.js 和 target.js 都为 cp 命令的参数
$ cp source.js target.js

Фактически, подкоманды также являются параметрами команды.

Хорошо, из приведенного выше введения мы хотим реализовать CLI, и анализ входных параметров (включая подкоманду, параметры, аргумент) неизбежен, поэтому давайте посмотрим правде в глаза.

командир: Эй, брат, не бойся, это я!

Да, братан, как хорошо, что ты есть. Далее мы используемcommanderЭтот модуль анализирует входные параметры Процесс и пример следующие:

  • Установка модуля
$ npm i commander
  • пример src/index.js
......
import program from 'commander';

export function cli(args) {
    program.parse(args);
}

Получите это в одном предложении, это так аккуратно и аккуратно.

Даниэль: Что насчет участия? Как это использовать?

В следующих примерах мы будем использовать эти проанализированные объекты ввода. Так что, пожалуйста, расслабьтесь.

> Не могу жить без тебя: версии и помощь

Версия и справочная информация являются обязательной частью интерфейса командной строки, иначе это выглядит непрофессионально. Давайте посмотрим, как это сделать.

Исправлятьsrc/index.js, код показан ниже:

import program from 'commander';
import pkg from '../package.json';

export function cli(args) {
    program.version(pkg.version, '-V, --version').usage('<command> [options]');
    
    program.parse(args);
}

пройти черезprogram.versionа такжеusageЦепной звонок сделан, и это все еще так холодно.

Попробуйте выполнить следующие команды:

$ cook -V

$ cook -h

> Добавить генералы: добавить подкоманды

Теперь приступаем к обогащению функционала CLI, начиная с добавления подкомандыstartНачинать.

у него есть параметрfoodи вариант--fruit, код показан ниже:

......
export function cli(args) {
  .....

  program
    .command('start <food>')
    .option('-f, --fruit <name>', 'Fruit to be added')
    .description('Start cooking food')
    .action(function(food, option) {
      console.log(`run start command`);
      console.log(`argument: ${food}`);
      console.log(`option: fruit = ${option.fruit}`);
    });

  program.parse(args);
}

В приведенном выше примере показано, как получить проанализированные входные параметры вactionВы можете получить все, что хотите, и то, что вы хотите сделать, полностью зависит от вас.

Попробуйте запустить подкоманду:

$ cook start pizza -f apple

> Ищите иностранную помощь: вызовите внешнюю команду

Иногда нам нужно вызывать внешние команды в CLI, напримерnpmкакой-то тип.

execa: Моя очередь выступать. ┏ (^ω^)=☞

  • Установка модуля
$ npm i execa
  • пример src/index.js
......
import execa from 'execa';

export function cli(args) {
  .....

  program
    .command('npm-version')
    .description('Display npm version')
    .action(async function() {
      const { stdout } = await execa('npm -v');
      console.log('Npm version:', stdout);
    });

  program.parse(args);
}

над переваломexecaвызвать внешнюю командуnpm -v. давай, распечатывайnpmНомер его версии:

$ cook npm-version

> Облегчение коммуникации: обеспечение взаимодействия человека с компьютером

Иногда мы хотим, чтобы интерфейс командной строки взаимодействовал с пользователем с помощью метода вопросов и ответов, когда пользователь предоставляет нужную нам информацию, вводя или выбирая.

В этот момент подул порыв ветра, и я увиделInquirer.jsБегать по разноцветным облакам.

  • Установка модуля
$ npm i inquirer

Наиболее распространенные сценарии: ввод текста, опция, проверка, радио. Примеры следующие:

  • пример src/index.js
......
import inquirer from 'inquirer';

export function cli(args) {
  ......

  program
    .command('ask')
    .description('Ask some questions')
    .action(async function(option) {
      const answers = await inquirer.prompt([
        {
          type: 'input',
          name: 'name',
          message: 'What is your name?'
        },
        {
          type: 'confirm',
          name: 'isAdult',
          message: 'Are you over 18 years old?'
        },
        {
          type: 'checkbox',
          name: 'favoriteFrameworks',
          choices: ['Vue', 'React', 'Angular'],
          message: 'What are you favorite frameworks?'
        },
        {
          type: 'list',
          name: 'favoriteLanguage',
          choices: ['Chinese', 'English', 'Japanese'],
          message: 'What is you favorite language?'
        }
      ]);
      console.log('your answers:', answers);
    });

  program.parse(args);
}

Код простой, переходим непосредственно к рендерам:

> Уменьшите беспокойство: ждите напоминаний

Опыт взаимодействия человека с компьютером очень важен.Если работа не может быть завершена немедленно, необходимо своевременно сообщать пользователю о текущем ходе работы, что может уменьшить беспокойство пользователя в ожидании.

oraа такжеlistrБок о бок, шагая аккуратными шагами, они приблизились.

Первый, кто играет,ora

  • Установка модуля
$ npm i ora
  • пример src/index.js
......
import ora from 'ora';

export function cli(args) {

  ......

  program
    .command('wait')
    .description('Wait 5 secords')
    .action(async function(option) {
      const spinner = ora('Waiting 5 seconds').start();
      let count = 5;
      
      await new Promise(resolve => {
        let interval = setInterval(() => {
          if (count <= 0) {
            clearInterval(interval);
            spinner.stop();
            resolve();
          } else {
            count--;
            spinner.text = `Waiting ${count} seconds`;
          }
        }, 1000);
      });
    });

  program.parse(args);
}

вот изображение:

listrПотом пришел.

  • Установка модуля
$ npm i listr
  • пример src/index.js
......
import Listr from 'listr';

export function cli(args) {
  ......

  program
    .command('steps')
    .description('some steps')
    .action(async function(option) {
      const tasks = new Listr([
        {
          title: 'Run step 1',
          task: () =>
            new Promise(resolve => {
              setTimeout(() => resolve('1 Done'), 1000);
            })
        },
        {
          title: 'Run step 2',
          task: () =>
            new Promise((resolve) => {
              setTimeout(() => resolve('2 Done'), 1000);
            })
        },
        {
          title: 'Run step 3',
          task: () =>
            new Promise((resolve, reject) => {
              setTimeout(() => reject(new Error('Oh, my god')), 1000);
            })
        }
      ]);

      await tasks.run().catch(err => {
        console.error(err);
      });
    });

  program.parse(args);
}

По-прежнему нечего сказать, по-прежнему прямо на картинке:

> Добавь немного красок: жизнь больше не будет монотонной

chalk: Я литературный юноша, я живу искусством, этим я не должен заниматься.

  • Установка модуля
$ npm i chalk
  • пример src/index.js
.....
import chalk from 'chalk';


export function cli(args) {

  console.log(chalk.yellow('I like cooking'));
  
  .....
  
}

Цветной интерфейс командной строки заставляет вас чувствовать себя лучше:

> Оформление фасада: добавить бордюр

boxen: Это моя специальность, следите за мной!

  • Установка модуля
$ npm i boxen
  • пример src/index.js
......
import boxen from 'boxen';

export function cli(args) {

  console.log(boxen(chalk.yellow('I like cooking'), { padding: 1 }));
  
  ......
}  

Ну, это выглядит более профессионально:

> Объявление результатов: готово к публикации

если тыscopeпубликуется таким образом, как@daniel-dx/cook-cli. затем вpackage.jsonДобавление следующей конфигурации на ваш веб-сайт позволит вам беспрепятственно публиковать (конечно, если вы являетесь платным участником npm, эту конфигурацию можно сохранить)

{
  "publishConfig": {
    "access": "public"
  },
}

У двери запускаем:

$ npm publish

Хорошо, ваш CLI был опубликован для всего мира, и теперь вы можете перейти кwww.npmjs.com/CLI для запроса вашего опубликованного под a.

> Теплое напоминание: пришло время обновиться

update-notifier: Наконец-то пришло мое время, я подожду, пока цветы не исчезнут. Х﹏Х

  • Установка модуля
$ npm i update-notifier
  • пример src/index.js
......

import updateNotifier from 'update-notifier';

import pkg from '../package.json';

export function cli(args) {
  checkVersion();
  
  ......
}

function checkVersion() {
  const notifier = updateNotifier({ pkg, updateCheckInterval: 0 });

  if (notifier.update) {
    notifier.notify();
  }
}

Для локальной отладки мы понижаем локальный CLI на одну версию и ставимpackage.jsonизversionпревратиться в0.0.9, затем запуститеcookПроверьте эффект:

o( ̄︶ ̄) о отлично!


Выше подробно описаны некоторые из необходимых или общих шагов для разработки CLI.

Конечно, если вы просто хотите быстро разработать CLI, как часто говорят некоторые руководители: не рассказывайте мне процесс, я просто хочу результат. Это вполне можно использовать какoclifЭти фреймворки предназначены для разработки интерфейсов командной строки прямо из коробки.

Как программисты, нам все еще нужно посвятить некоторое время и энергию, чтобы понять все тонкости решения, прошлое и настоящее, чтобы мы могли быть более практичными и идти дальше.

Ну вот и все на сегодня, до свидания друзья!

Чуть не забыл, прикрепите исходный код примера:GitHub.com/Daniel-stuff/from…

┏(^0^)┛ До свидания!