13 September 2016

Обзор пакетов Node.js для разбора опций командной строки

Node.JS
Tutorial

Node.js, как и другие среды разработки, предоставляет базовые средства работы с опциями командной строки. В нашем случае это массив process.argv. Но обычно, кроме простейших случаев типа A + B, обрабатывать опции командной строки вручную очень неудобно. Для этого есть несколько популярных пакетов. Автор написал небольшую программу, которая построила сводную таблицу по этим пакетам, выбрал из них три самых популярных и рассмотрел их поближе.


(Материал статьи на 7 января 2020 года по прежнему актуален, сводная таблица обновлена и дополнена.)


Сводная таблица


(Из-за узкого формата страницы пришлось одну таблицу разбить на две: одна с информацией с NPM, другая с GitHub.)


# NPM Package NPM Stars Deprecated Last Version Last Update Created Dependencies
1 commander 1011 4.1.0 2020-01-06 2011-08-15 0
2 yargs 512 15.1.0 2020-01-02 2013-11-23 11
3 minimist 432 1.2.0 2019-08-11 2013-06-25 0
4 optimist 143 V 0.6.1 2018-03-21 2010-12-21 2
5 meow 79 6.0.0 2019-12-07 2013-01-24 11
6 cli 69 1.0.1 2018-03-15 2011-01-01 2
7 command-line-args 49 5.1.1 2019-03-31 2014-05-27 4
8 nopt 47 4.0.1 2020-01-02 2011-03-30 2
9 nomnom 32 V 1.8.1 2018-03-17 2011-04-08 2
10 argparse 21 1.0.10 2018-02-27 2012-05-17 1
11 stdio 9 2.0.1 2019-12-19 2013-03-16 0
12 dashdash 9 1.14.1 2017-12-28 2013-02-28 1
13 has-flag 5 4.0.0 2019-04-06 2015-07-08 0
14 clp 2 4.0.11 2019-01-03 2015-04-17 3
15 clap 1 2.0.1 2019-12-17 2014-02-10 1
16 argentum 0 0.6.0 2016-07-29 2015-11-26 0
17 getoptie 0 1.0.2 2015-03-09 2015-03-09 0

# NPM Package GitHub Repository GitHub Stars Last Commit
1 commander tj/commander.js 16885 2020-01-06
2 yargs yargs/yargs 7154 2020-01-02
3 minimist substack/minimist 3950 2015-08-29
4 optimist substack/node-optimist 2589 2014-02-05
5 meow sindresorhus/meow 2032 2019-12-07
6 cli node-js-libs/cli 772 2016-10-23
7 command-line-args 75lb/command-line-args 451 2019-09-22
8 nopt npm/nopt 478 2019-01-26
9 nomnom harthur/nomnom 471 2015-09-09
10 argparse nodeca/argparse 359 2019-11-05
11 stdio sgmonda/stdio 143 2019-12-19
12 dashdash trentm/node-dashdash 124 2019-08-28
13 has-flag sindresorhus/has-flag 53 2019-05-31
14 clp IonicaBizau/clp 12 2019-01-03
15 clap lahmatiy/clap 16 2020-01-06
16 argentum rumkin/argentum 1 2016-07-29
17 getoptie avz/node-getoptie 0 2015-03-09

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


Пакеты в таблице упорядочены по рейтингу, который считается на основе количества звёзд на NPM и GitHub по формуле:


npmStars * k + githubStars

Коэффициент k понадобился, так как звёзды на NPM выглядят "весомее" звёзд на GitHub. Сам коэффициент считается очень просто: суммируем количество звёзд на NPM и на GitHub, затем делим число звёзд на GitHub на число звёзд на NPM, округляем получившееся число, это и есть наш коэффициент k:


k = floor( Sgithub / Snpm)

Из получившейся таблицы хорошо видно, что главный фаворит, это пакет commander. Далее идут с близким рейтингом пакеты minimist и yargs. Хороший рейтинг имеет также пакет optimist, но автором он объявлен устаревшим, а на его место он рекомендует им же написанный пакет minimist, а также советует посмотреть yargs и nomnom. В качестве преемника optimist также позиционируется пакет yargs. Авторы объявленного устаревшим nomnom рекомендуют commander.


Таким образом в первую очередь нужно рассмотреть пакеты commander, minimist и yargs. Вероятно есть смысл также обратить внимание на пакеты meow и nopt, но не в этот раз.


commander


Научиться использовать пакет commander несложно. Автор предоставил, хоть и не всегда ясную, но всё же неплохую документацию. Чтобы разобраться, как использовать этот пакет, нужно было как следует поэкспериментировать. Ниже я опишу основные моменты этого пакета.


Итак, после того как мы загрузили пакет:


const commander = require('commander')

Мы можем, вызывая последовательно или раздельно его функции, настроить его на обработку опций командной строки. При этом пакет обеспечивает:


  • короткие опции, например, -s;
  • длинные опции, например, --source;
  • альтернативные названия опций, например, --source и -s;
  • дополнительные параметры;
  • значения по-умолчанию для дополнительных параметров;
  • обработчики для дополнительных параметров;
  • субкоманды, например, install package;
  • автоматическое формирование подсказки;
  • настройку подстказки.

Короткие опции объявляются так:


commander
  .option('-a', 'option a')

Первый аргумент функции option задаёт формат опции, а второй даёт ей словесное описание. Доступ к опции -a в коде программы осуществляется через соответствующее свойство commander:


if (commander.a) {
  console.log(commander.a)
}

Пример для длинной опции:


commander
  .option('--camel-case-option', 'camel case option')

При этом в коде доступ к опции будет происходить по имени camelCaseOption.


Возможно задание для опций параметров как обязательных, так необязательных:


commander
  .option('-s, --source <path>', 'source file')
  .option('-l, --list [items]', 'value list', toArray, [])

Во втором случае, параметр у опции list необязателен, для него назначены функция-обработчик и значение по-умолчанию.


Параметры опций могут обрабатываться также с помощью регулярных выражений, например:


commander
  .option('--size [size]', 'size', /^(large|medium|small)$/i)

Субкоманда подразумевает, что для неё пишется отдельный модуль. При этом, если основная программа называется program, а субкоманда command, то модуль субкоманды должен называться program-command. Опции, переданные после субкоманды передаются модулю команды.


commander
  .command('search <first> [other...]', 'search with query')
  .alias('s')

Для автоматической подсказки можно указать версию программы:


commander.version('0.2.0')

Подсказка может быть сопровождена дополнительными действия, например, дополнена нестандартными текстом. Для этого нужно обрабатывать событие --help.


commander.on('--help', () => {
  console.log('  Examples:')
  console.log('')
  console.log('    node commander.js')
  console.log('    node commander.js --help')
  console.log('    node commander.js -h')
  ...
  console.log('    node commander.js --size large')
  console.log('    node commander.js search a b c')
  console.log('    node commander.js -abc')
})

Завершается настройка вызовом функции parse с параметром process.argv:


commander.parse(process.argv)

minimist


Автор пакета minimist предоставил весьма минималистичную документацию. Но всё равно попробуем разобраться.


После того как мы загрузили пакет, подключим и воспользуемся им:


const minimist = require('minimist')

const args = minimist(process.argv.slice(2))

console.dir(args)

Этот незамысловатый код позволит нам начать работать с этим пакетом. Поэкспериментируем:


node minimist.js

{ _: [] }

Что мы здесь видим? Набор разобранных опций организуется в объект. Свойство с именем _ содержит список параметров, не связанных с опциями. Например:


node minimist.js a b c

{ _: [ 'a', 'b', 'c' ] }

Продолжим эксперимент:


node minimist.js --help

{ _: [], help: true }

Как видим, minimist не предоставляет автоматического отображения подсказки, а просто определяет наличие данной опции.


Поэкспериментируем ещё:


node minimist.js -abc

{ _: [], a: true, b: true, c: true }

Всё верно. Посмотрим ещё:


node minimist.js --camel-case-option

{ _: [], 'camel-case-option': true }

В отличие от minimist никаких преобразований.


Опция с параметром:


node minimist.js --source path

{ _: [], source: 'path' }

Со знаком равно тоже работает:


node minimist.js --source=path

{ _: [], source: 'path' }

Поддерживается специальный режим передачи опций с использванием --:


node minimist.js -h -- --size=large

{ _: [ '--size=large' ], h: true }

Аргументы, следующие за -- не обрабатываются и просто помещаются в свойство _.


Вот в общем-то и всё, что есть в базе. Посмотрим, какие возможности настройки обработки опций предлагает нам minimist.


Для настройки обработки аргументов командной строки мы должны передать парсеру второй параметр с нашими настройками. Рассмотрим на примерах:


const minimist = require('minimist')

const args = minimist(process.argv.slice(2), {
  string: ['size'],
  boolean: true,
  alias: {'help': 'h'},
  default: {'help': true},
  unknown: (arg) => {
    console.error('Unknown option: ', arg)
    return false
  }
})

console.dir(args)

node minimist-with-settings.js --help

{ _: [], help: true, h: true }

node minimist-with-settings.js -h

{ _: [], h: true, help: true }

Мы задали для опции --help синоним -h. Результат, как видим, идентичен.


Опция boolean, установленная в true, говорит о том, что все опции без параметров после знака равно будут иметь булево значение. Например:


node minimist-with-settings.js --no-help

{ _: [], help: false, h: false }

Здесь мы увидели, как обрабатываются булевы опции: префикс no устанавливает значение опции равным false.


Но такой пример при этом больше не работает, нужен знак равно:


node minimist-with-settings.js --size large

Unknown option:  large
{ _: [], size: '', help: true, h: true }

Здесь же мы увидели обработку неизвестной опции и опции по-умолчанию.


Общий вывод: по сравнению с commander довольно минималистично, но вполне удобно.


yargs


В отличие от minimist и commander yargs предлагает весьма пространную документацию, доступную по ссылке.


Как обычно начнём с минимального примера:


const yargs = require('yargs')

console.dir(yargs.argv)

node yargs.js

{ _: [], '$0': 'yargs.js' }

Здесь мы видим пустой список необработанных опций, а также имя файла нашей программы.


Рассмотрим пример посложней:


node yargs.js -abc --help --size=large 1 2 3

{ _: [ 1, 2, 3 ],
  a: true,
  b: true,
  c: true,
  help: true,
  size: 'large',
  '$0': 'yargs.js' }

Здесь поинтереснее будет: во-первых, переданные опции восприняты верно; во-вторых, для их обработки мы не написали ни строчки кода.


Но уже здесь видно, что опция --help без предварительной настройки по предназначению не обрабатывается.


Рассмотрим теперь как использовать yargs в более сложных случаях на следующем примере:


const yargs = require('yargs')

yargs
  .usage('Usage: $0 -abc [--list 1,2,3] --size large|meduim|small [--help]')
  .version('1.0.0')
  .demand(['size'])
  .choices('size', ['large', 'medium', 'small'])
  .default('list', [], 'List of values')
  .describe('list', 'value list')
  .array('list')
  .help('help')
  .alias('help', 'h')
  .example('$0 --size=medium')
  .epilog('(c) 2016 My Name')

console.dir(yargs.argv)

node yargs.js -h

Получаем:


Usage: yargs.js -abc [--list 1,2,3] --size large|meduim|small [--help]

Options:
  --version   Show version number                                      [boolean]
  --list      value list                       [array] [default: List of values]
  --help, -h  Show help                                                [boolean]
  --size                        [required] [choices: "large", "medium", "small"]

Examples:
  yargs.js --size=medium
(c) 2016 My Name

В этом примере мы задали текст, который будет выводиться с опцией help. Опции help мы также указали синоним h. А ещё указали версию программы, которая будет выводиться с опцией version.


Опция size обязательная, более того, для неё задан список допустимых значений.


node yargs.js --size large

{ _: [],
  version: false,
  help: false,
  h: false,
  size: 'large',
  list: [],
  '$0': 'yargs.js' }

Если size передать значение, не соответствующее ни одному из списка, то получим сообщение об ошибке:


node yargs.js --size=middle

...
Invalid values:
  Argument: size, Given: "middle", Choices: "large", "medium", "small"

Для опции list указано значение по умолчанию. Эта опция также трактуется как массив значений:


node yargs.js --list 1 2 3 --size=large

{ _: [],
  version: false,
  help: false,
  h: false,
  list: [ 1, 2, 3 ],
  size: 'large',
  '$0': 'yargs.js' }

Резюме


Пакеты commander и minimist выделяются минимальным числом зависимостей, в то время как yargs поражает не только числом своих зависимостей, но и числом своих возможностей.


Какой пакет лучше, очевидно, сказать нельзя. По мнению автора, minimist вполне достаточен для простейших случаев, но в сложных ситуациях при его использовании придётся написать много кода обработки опций вручную. В этом случае лучше воспользоваться commander или yargs, на ваш вкус.


Все три рассматриваемые здесь пакета имеют определения типов на TypeScript, что позволяет иметь в Code работающий IntelliSense.


Архив


В первоначальной редакции от 13 сентября 2016 года сводная таблица в начале статьи была следующей:


Сводная таблица


Обновление


Добавлены в таблицу ещё три пакета, о которых сообщили в комментариях читатели:



И два пакета, которые удалось обнаружить благодаря сервису npms.io:



Соответственно, обновлённая таблица ниже:


Обновлённая сводная таблица


Немного аналитики


Спустя три месяца с момента написания и публикации этой статьи на Habrahabr в
период с сентября по декабрь 2016 произошли некоторые интересные изменения в
сводной таблице.


Таблица за декабрь 2016


  • Рейтинг почти всех модулей вырос за исключением малопопулярных
    stdio, getoptie, argentum.
  • Вперёд "рванул" yargs и вышел на второе место, сместив на третье minimist.
    Это вполне объяснимо: minimist не развивается уже больше года, в то время как
    для yargs регулярно выходят новые версии.
  • Пакет command-line-args обошёл stdio.
  • Из 14 пакетов только два улучшили свои позиции. Но скорость роста рейтинга
    разная, поэтому через несколько месяцев можно ожидать выявления новых фаворитов.
  • Не совершенствовались следующие модули: minimist, optimist (deprecated),
    nomnom (deprecated), stdio, getoptie и argentum. Вероятно эти модули скоро можно
    будет объявлять deprecated.
    Самый главный вывод, который хотелось бы сделать, это то, что стоит задуматься, а
    нужно ли в своих новых проектах использовать популярный minimist, ведь он
    достаточно давно не разрабатывается?

Расклад голосования на 30 декабря 2016 таков. Проголосовало 72 читателя, воздержалось 65.
Из них отдали свои голоса следующим образом:


  1. yargs 31% (22)
  2. commander 29% (21)
  3. minimist 21% (15)
  4. process.argv 8% (6)
  5. другой пакет 7% (5)
  6. optimist 4% (3)

Налицо наибольшая популярность у yargs и commander, при этом minimist
также достаточно популярен.


Обновление от 8 февраля 2019


Сводная таблица обновлена, преобразована в формат Markdown и дополнена пакетами has-flag, clp и clap. Результаты голосования остались примерно такими же, какими были при последнем обновлении от 30 декабря 2016 года.


Обновление от 7 января 2020


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

Only registered users can participate in poll. Log in, please.
Какой пакет для разбора опций командной строки в Node.js используете Вы? (Если несколько, то выберите наиболее предпочтительный вариант.)
31.91% commander 30
19.15% minimist 18
30.85% yargs 29
3.19% optimist 3
7.45% process.argv 7
7.45% другой 7
94 users voted. 76 users abstained.
Tags:node.jscliобзор
Hubs: Node.JS
+21
14.9k 76
Comments 34