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.
Актуальная версия статьи
Периодически я актуализирую этот обзор, самая свежая версия этой статьи в репозитории на GitHub. Если вам понравился этот обзор, и вы готовы поддержать автора донатом, заходите на мою страничку на Boosty. Ваша поддержка подогревает мой энтузиазм!
Архив
В первоначальной редакции от 13 сентября 2016 года сводная таблица в начале статьи была следующей:
Обновление
Добавлены в таблицу ещё три пакета, о которых сообщили в комментариях читатели:
- NPM: argparse | GitHub: nodeca/argparse
- NPM: argentum | GitHub: rumkin/argentum
- NPM: getoptie | GitHub: avz/node-getoptie
И два пакета, которые удалось обнаружить благодаря сервису npms.io:
- NPM: cli | GitHub: node-js-libs/cli
- NPM: dashdash | GitHub: trentm/node-dashdash
Соответственно, обновлённая таблица ниже:
Немного аналитики
Спустя три месяца с момента написания и публикации этой статьи на Habrahabr в
период с сентября по декабрь 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.
Из них отдали свои голоса следующим образом:
- 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
Обновлена сводная таблица. Рейтинг пакетов существенно не изменился. Результаты голосования также показывают, что за год предпочтения ощутимо не поменялись.