Обзор пакетов Node.js для разбора опций командной строки
Node.js, как и другие среды разработки, предоставляет базовые средства работы с
опциями командной строки. В нашем случае это массив process.argv
. Но обычно,
кроме простейших случаев типа A + B, обрабатывать опции командной строки
вручную очень неудобно. Для этого есть несколько популярных пакетов. Я написал
небольшую программу, которая построила сводную таблицу по этим пакетам,
выбрал из них три самых популярных и рассмотрел их поближе. (Материал статьи на
7 января 2020 года по прежнему актуален, сводная таблица обновлена и дополнена.)
Данные из NPM:
# | 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 |
Данные из GitHub:
# | 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, но не в этот раз. Уже после публикации статьи, благодаря сервису npms.io удалось обнаружить ещё один популярный пакет cli.
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 предлагает весьма пространную документацию, доступную по ссылке: http://yargs.js.org/docs/.
Как обычно начнём с минимального примера:
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.
Немного аналитики
Спустя три месяца с момента написания и публикации этой статьи на Habrahabr в период с сентября по декабрь 2016 произошли некоторые интересные изменения в сводной таблице.
-
Рейтинг почти всех модулей вырос за исключением малопопулярных stdio, getoptie, argentum.
-
Вперёд “рванул” yargs и вышел на второе место, сместив на третье minimist. Это вполне объяснимо: minimist не развивается уже больше года, в то время как для yargs регулярно выходят новые версии.
-
Пакет command-line-args обошёл stdio.
-
Из 14 пакетов только два улучшили свои позиции. Но скорость роста рейтинга разная, поэтому через несколько месяцев можно ожидать выявления новых фаворитов.
-
Не совершенствовались следующие модули: minimist, optimist (deprecated), nomnom (deprecated), stdio, getoptie и argentum. Вероятно эти модули скоро можно будет объявлять deprecated.
Самый главный вывод, какой я бы хотел сделать, это то, что стоит задуматься, а нужно ли в своих новых проектах использовать популярный minimist, ведь он достаточно давно не разрабатывается?
Для удобства сравнения сводных таблиц привожу их здесь в виде картинок:
-
Сводная таблица за сентябрь 2016
-
Сводная таблица за декабрь 2016
Расклад голосования на Habrahabr таков. Проголосовало 72 читателя, воздержалось 65. Из них отдали свои голоса следующим образом:
-
yargs 31% (22)
-
commander 29% (21)
-
minimist 21% (15)
-
process.argv 8% (6)
-
другой пакет 7% (5)
-
optimist 4% (3)
Налицо наибольшая популярность у yargs и commander, при этом minimist также достаточно популерен.
Обновление от 8 февраля 2019
Сводная таблица обновлена, преобразована в формат Markdown и дополнена пакетами
has-flag
, clp
и clap
. Результаты голосования остались примерно такими же,
какими были при последнем обновлении от 30 декабря 2016 года.
Обновление от 7 января 2020
Обновлена сводная таблица. Рейтинг пакетов существенно не изменился. Результаты голосования также показывают, что за год предпочтения ощутимо не поменялись.
Примечание
Эта статья в немного другой редакции была первоначально опубликована (13 сентября 2016) на Habrahabr: https://habrahabr.ru/post/309936/
(c) Симоненко Евгений, 2016