Pull to refresh

Двенадцать простых начальных шагов разработки модуля для Node.js

Reading time 13 min
Views 23K
[Аристотель]«Начало — более чем половина всего».

Это очень древний GTD-принцип: возраст его, вероятно, исчисляется тысячелетиями. (Например, Викицитатник в настоящее время приписывает его Аристотелю, хотя и без подтверждающего указания на первоисточник.) Суть его в том, что с чистого листа начинать какой-либо проект бывает очень трудно (и даже приходится заставлять себя); а вот если есть несколько простых начальных шагов, выполнение которых приводит к появлению частично готового проекта, то продолжить работу над ним «по инерции» становится куда проще — так просто, как если бы проект этот ужé был не только начат, но и готов даже более, чем наполовину. А кроме того, когда заранее хорошо знаешь, какими должны быть начальные шаги, то тогда трудно бывает допустить ошибку, совершая их.

Мне довелось сочинить более десятка модулей для Node.js с открытым исходным кодом и опубликовать их в качестве пакетов npm. Чем больше модулей я делал, тем сильнее понимал (в том числе путём проб и ошибок), что начальные шаги для их создания могут быть одинаковыми и даже могут совершаться в одном и том же порядке. Сегодня я публикую этот порядок в надежде на то, что он станет подспорьем в работе программистов, сочиняющих свой код на языке JavaScript для движка Node.

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

Шаг 1. Придумывание имени пакета npm и репозитория


Без имени ничего не получится. Не имея имени для своего проекта, вы не сможете создать для него ни репозиторий на Гитхабе, ни пакет npm.

Заранее убедитесь в том, что имя пакета npm ещё не занято. (Вот пример: если осенью вам вздумается назвать свой пакет «autumn» или «fall», то посещение адресов https://www.npmjs.org/package/autumn и https://www.npmjs.org/package/fall без труда убедит вас в том, что пакеты npm с такими именами ужé существуют.)

Если именем вашего пакета npm послужит какое-нибудь простое слово или общеизвестный технический термин, то подумайте о том, не прибавить ли префикс «node-» к нему для того, чтобы получить название репозитория. (Например, код пакета sqlite3 хранится в репозитории node-sqlite3, а код пакета open хранится в репозитории node-open.) Такое прибавление делается для удобства программистов, работающих над одноимёнными проектами на других языках программирования. (Например, если у кого-нибудь ужé есть проект под именем «open» на Rust, Ruby или Python, то он всё же и репозиторий «node-open» сможет форкнуть невозбранно, а не испытает последствия конфликта имён.)

А короткое ли будет имя у вашего пакета npm или длинное? Если короткое, то его проще вручную набрать (например, в командной строке после «npm install»), а если длинное, то его будут копипастить (это действие происходит безошибочнее, но дольше — и поэтому воспринимается как менее удобное). Также учтите, что сайт nodei.co выдаёт такие гистограммы со статистикою скачиваний пакета npm, ширина которых зависит от длины имени пакета (чтобы имя целиком могло поместиться на гистограмме). Вот два примера:

[гистограмма №1: uue]

[гистограмма №2: fidonet-jam]

Для модулей, наделённых слишком длинными именами, рост ширины такой гистограммы может вызвать (и вызовет, непременно вызовет!) необходимость размещения статистики на отдельной строке, а не справа или слева от какой-нибудь другой картинки, когда вы станете подготавливать сводную информацию о своём модуле — даже такую простую, как README на Гитхабе.

Шаг 2. Придумывание краткого описания


Краткое описание пригодится и при создании проекта на Гитхабе, и позднее (например, при заполнении поля description в файле package.json).

Лучше всего поместить туда всего одно краткое предложение с описанием того, что делает ваш модуль. Оно будет способствовать поисковой находимости и сопутствовать названию пакета или репозитория на многих сайтах с гиперссылками. Если сделать описание слишком длинным, то оно не поместится на карточку в Твиттере (и на других сайтах, обрезающих описания по длине); кроме того, даже если поместится, то при беглом чтении списков публика всё равно не станет дочитывать до конца его.

Шаг 3. Создание репозитория на Гитхабе с автоматически заполненным README-файлом.


Как правило, репозитории модулей Node создаются на Гитхабе; мне попадались и исключения из этого правила, но редко. Поэтому все шаги я стану здесь излагать с расчётом на употребление Гитхаба и Git, а читателям из числа пользователей других DVCS и (или) других хостингов открытого исходного кода предоставляю адаптировать эти шаги для себя самим: дело это не хитрое.

На странице https://github.com/new предлагают заполнить имя и описание репозитория (то и другое вы могли придумать на двух предыдущих шагах), а также автоматически создать README-файл (описание проекта), файл с текстом лицензии (чтобы обозначить, насколько ваш код открыт) и файл .gitignore (чтобы затем не закачать в репозиторий никаких лишних, временных, вспомогательных, мусорных файлов).

Когда в репозитории на Гитхабе с самого начала есть хотя бы один файл, то репозиторий можно просто клонировать (например, командою «git clone») — это быстрее и проще (примерно в полтора или даже в два раза), чем сперва инициализировать локальный репозиторий (например, командою «git init»), а затем руками в настройках прописывать ему адрес гитхабовского репозитория как origin.

Но автосозданный Гитхабом файл лицензии зачастую приходится править под себя (например, вместо гитхабовского логина указывать своё полное имя или название организации), да и в автосозданный .gitignore вы можете также пожелать чего-нибудь добавить в соответствии со своим представлением о том, какие файлы в репозитории не нужны. Поэтому, если вы создаёте не первый модуль Node в своей жизни, то лучше ограничиться автосозданием одного только файла README, а лицензию и .gitignore скопировать в готовом виде из одного из своих прежних модулей. Делать же их заново (пускай и не с нуля, а из результатов гитхабовской автогенерации) — это и муда (無駄、 лишняя работа), и напрасное захламление истории правок.

После создания репозитория на Гитхабе не позабудьте внести все те изменения в настройках репозитория, которые считаете необходимыми для него. Например, если мало времени и поэтому некогда приглядывать за вики, то можно отключить в репозитории поддержку вики. А если, например, вы привыкли видеть в списке «Starred» (в списке репозиториев, помеченных звёздами) не только некоторые чужие репозитории, но и все свои репозитории, то тогда можно тотчас же поставить свежесозданному репозиторию его первую звезду.

Шаг 4. Добавление текстового файла с указанием лицензии


С этим шагом лучше всего не затягивать: чем раньше вы дадите понять, под какою лицензией распространяются все последующие правки, тем лучше.

Как я и говорил на предыдущем шаге, если вы создаёте не первый модуль Node в своей жизни, то можно скопировать файл LICENSE из другого модуля, подправить при необходимости год в тексте — и можно коммитить.

Если же вы в первый раз принимаете решение о том, под какой лицензией распространять свой открытый исходный код, то можете последовать примеру сообщества (а в сообществе Node.js, насколько я могу судить, лицензия MIT наиболее популярна, хотя встречаются и исключения из этого правила), или проконсультироваться с сайтом choosealicense.com, или и то, и другое вместе.

Шаг 5. Добавление файлов .gitignore и .npmignore.


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

При разработке проекта, в котором один или несколько модулей Node используются в качестве зависимостей, бывает вполне уместно добавить в файл .gitignore имя каталога node_modules, потому что соответствие между версией и содержимым пакета npm является однозначным — следовательно, достаточно хранить в своём репозитории Git только файл package.json (в котором перечислены имена и версии зависимостей), но не сами необходимые модули.

(Для расширения кругозора упомяну, что Mikeal Rogers высказывал и деятельно продвигал совершенно противоположную рекомендацию. Но это случилось до того, как в 2014 году соответствие между версией и содержимым пакета npm сделалось однозначным.)

Также в файл .gitignore полезно добавить имя отчёта npm-debug.log, появляющегося в том случае, если при работе с пакетами npm возникла какая-нибудь ошибка.

В итоге файл .gitignore приобретает какой-то такой вид:

.gitignore
# Архивы, дистрибутивы
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip

# Базы данных, логи
*.log
*.sql
*.sqlite

# Спецфайлы операционных систем
Thumbs.db
Desktop.ini
.DS_Store*
ehthumbs.db
Icon?

# Модули Node, логи npm
node_modules
npm-debug.log

Файл .npmignore примерно аналогичен файлу .gitignore но в нём перечисляются имена тех файлов и подкаталогов, которые не должны попасть в npm-пакет вашего модуля.

Я рекомендую скопировать в .npmignore всё содержимое вашего .gitignore, а затем прибавить к нему название подкаталога test или другого такого места, в котором вы собираетесь хранить тесты своего модуля. Тесты чрезвычайно полезны в разработке, поэтому вполне уместны и в репозитории Git — но скачивание и хранение тестов было бы лишним грузом для конечных пользователей вашего модуля, а именно из таковых состоит большинство потребителей npm-пакета.

Как правило, начиная работу над новым модулем Node, можно скопировать файлы .gitignore и .npmignore в неизменном виде из одного из прежних своих модулей (если только вы создаёте не первый модуль Node в своей жизни). Корректировать их приходится в редких случаях, но надо знать, что такие случаи бывают. (В частности, если какой-нибудь модуль имеет дело с ZIP-архивами, то строчку «*.zip» из файла .gitignore неизбежно придётся убрать хотя бы для того, чтобы поместить один или несколько тестовых примеров в репозиторий — и такая необходимость возникла у меня при разработке модуля для чтения фидонетовских ноудлистов, например.)

Шаг 6. Описание модуля в README.md, упоминание о лицензии и о неоконченности разработки.


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

Теперь это описание можно несколько пополнить, рассказав о целях и задачах проекта. Кроме того, в отдельном подразделе описания уместно упомянуть тип лицензии (например, MIT или BSD) и имя того файла (как правило, LICENSE), в котором лежит полный текст лицензии.

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

После такого пополнения файл README годится в качестве первоначального описания первоначальной версии npm-пакета.

Однако, если вам довелось упомянуть в README ещё и планируемое имя пакета, то не торопитесь помещать результаты этого шага в репозиторий (например, git-командою commit), а уж тем более не торопитесь публиковать их (например, git-командою push) на Гитхабе. Дело в том, что если первоначальная регистрация пакета с запланированным именем (а именно ей мы займёмся на следующем шаге) не удастся, то в README придётся прописывать другое имя (и будет лучше, если это не приведёт к неприятному выбору между засорением и подчисткой истории правок); публикация же на Гитхабе (если она предшествует публикации в npm) и вовсе способна насторожить не только каких-нибудь киберсквоттеров (которые пожелают застолбить указанное имя пакета раньше вас), но и обыкновенных пользователей (которые будут пробовать команду «npm install имя-пакета» и удивляться тому, чего это она не работает как следует).

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

Шаг 7. Сочинение package.json и регистрация пакета npm самой начальной версии.


Конечной целью этого шага является регистрация npm-пакета под ранее задуманным именем, чтобы застолбить это имя.

Начать такой шаг приходится созданием файла package.json и заполнением сразу нескольких полей объекта внутри него. При этом надо одновременно соблюдать формат JSON и то описание смысла полей, которое приведено на сайте npm.

Существует команда «npm init», которая задаёт вам вопросы о модуле и самостоятельно заполняет package.json в соответствии с ответами. Однако я предпочитаю заполнять package.json в текстовом редакторе — это позволяет обозревать весь код JSON в чистом виде (не разбавленный вопросами) и притом даёт простую возможность возвратиться на пару строк выше, чтобы подправить или переменить чего-нибудь по мере нужды.

Вообще-то документации, лежащей на сайте npm, должно хватать всем; но я не утерплю и на Хабрахабре бегло коснусь смысла основных полей, которые в package.json помещаются.

Значением для поля «name» указывается желаемое имя пакета npm. Его вы могли придумать ранее (на первом шаге).

Значением для поля «version» указывается версия пакета в соответствии со стандартом Semantic Versioning. Для регистрации самой начальной версии я рекомендую «"0.0.1"».

Значением для поля «description» указывается краткое описание пакета. Его вы могли придумать ранее (на втором шаге).

Значением для поля «keywords» указывается массив ключевых слов, по которым пакет могут отыскать в поиске по сайту npm. Всякий, кто когда-либо перечислял метки под текстом блогозаписи на Хабрахабре, без труда справится и с этой задачею.

Значением для поля «license» указывается строка-идентификатор, соответствующая стандарту SPDX ID и означающая лицензию, выбранную вами для своего кода ранее (на четвёртом шаге).

Значением для поля «author» указывается строка (или объект) с именем автора пакета (а также, может быть, с контактными данными). Укажите там себя.

Значением для поля «repository» указывается объект, содержащий тип и адрес репозитория.

Значением для поля «main» указывается путь к главному файлу кода модуля (это тот самый файл, который из Node будет вызван функцией «require(moduleName)», если вместо moduleName указать имя модуля).

Если модуль подразумевается как утилита, работающая из командной строки, то тогда такому модулю полезным будет в package.json свойство «preferGlobal» со значением «true» (которое будет рекомендовать глобальную установку модуля) и свойство «bin» со значением наподобие «"./moduleName.js"», содержащим путь к некоторому джаваскрипту (к тому именно, который будет запускаться по команде, совпадающей с именем модуля). Строка «#!/usr/bin/env node» должна стать первою в таком джаваскрипте, потому что npm ожидает её увидеть там (даже если установка и последующая работа модуля совершается под Windows).

Код модуля на этом шаге может быть простейшим — например, содержать какой-нибудь «hello world» или даже «"use strict"».

В описание коммита уместно поместить фразу «пакет зарегистрирован» (или «пакет опубликован») и совершать этот коммит только после того, как команда «npm publish» окажется успешною (если окажется). Таким образом, коммит не окажется преждевременным (подача команды «npm publish» может выявить одну или несколько простых ошибок, исправление которых только напрасно захламляло бы историю правок).

Шаг 8. Объяснение установки в README.md


Во время этого шага уместно поместить в файл README.md объяснения, сообщающие будущим пользователям смысл двух команд:

  • npm install имяПакета — команда, которая установит npm-пакет из хранилища npm,
     
  • npm install https://github.com/имяПользователя/имяРепозитория/tarball/master — команда, которая установит модуль из master-ветки гитхабовского репозитория.

Также полезно объяснить будущим пользователям разницу между результатами той и другой команды — например, рассказать о принятом ранее (на пятом шаге) решении хранить тесты только на Гитхабе, а не в npm (чтобы не увеличивать объём npm-пакета).

Можно ещё рассказать, что и последняя версия файла README.md будет только на Гитхабе (если только вы не планируете создавать и публиковать очередную новую версию своего npm-пакета не только после изменений в коде, но также и после изменений в README).

Во время этого же шага можно также получить на сайте nodei.co подходящий код информативных статистических счётчиков и гистограмм, указывающих на количество зависимостей и установок модуля, и также поместить их в файл README.md.

Шаг 9. Самое начало работы над основным кодом модуля


На этом этапе зачаток модуля (то есть какой-нибудь «hello world» или даже «"use strict"») потихоньку превращается в код, делающий нечто разумное — а значит, содержащий для этой цели одну или несколько функций (или методов объекта, или команд утилиты).

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

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

Шаг 10. Содействовать тестированию и объяснить его


На этом шаге надобно поместить в README.md понятные сведения о том, как установить и как использовать средства тестирования правильности написания исходного кода (например, JSHint) и его поведения (например, Mocha).

На этом этапе в репозиторий также добавляются конфигурационные файлы для средств тестирования (например, .jshintrc для JSHint).

В объекте внутри файла package.json заполняется поле scripts.test (а если средств тестирования два-три, то также и поля scripts.pretest да scripts.posttest).

Пример:

"scripts": {
   "pretest": "jshint имяМодуля.js test/",
   "test": "mocha --reporter spec --timeout 60s"
}

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

Шаг 11. Конфигурирование Travis CI


Travis CI — это сервис, предоставляющий услугу бесплатного тестирования открытого исходного кода: после каждого коммита он способен автоматически скачивать код с Гитхаба и запускать тесты в различных окружениях, чтобы посмотреть, хорошо ли работает код.

К первоначальному употреблению Travis CI неплохо бы подготовиться, то есть прочесть документацию и пройти регистрацию.

Подключение же нового джаваскриптового модуля гораздо проще и состоит из двух несложных шагов.

Во-первых, надо зайти по адресу https://travis-ci.org/profile/ в свой пользовательский профиль. Там появятся переключатели, каждый из которых соответствует одному из репозиториев на Гитхабе; выглядит это как-то так:

[скриншот]

Переключатель, соответствующий новому модулю, следует дёрнуть во включённое положение.

Во-вторых, сразу после этого надо закоммитить файл .travis.yml в корень репозитория. Этот файл описывает окружение, необходимое для запуска тестов. Подробнее о нём вы можете прочесть в документации по Трэвису, но обычное содержание его довольно несложно и выглядит как-то так:

language: node_js
node_js:
  - "0.10"
  - "0.12"
  - iojs
before_install:
  - "npm install -g npm"
  - "npm config set spin false"
before_script:
  - "npm install jshint"
  - "npm install mocha"

В разделе «language» вы видите указание «языка» (точнее, движка) Node, а в разделе «node_js» — перечисление версий, в каждой из которых будет тестироваться модуль.

В разделе «before_install» приведены команды, обновляющие npm до последней стабильной версии, а также отключающие анимацию крутящегося курсора (которая в логах Travis CI всё равно не отображается, так что нечего тратить на неё время).

В разделе «before_script» приведены команды, устанавливающие средства тестирования (в этом примере — JSHint и Mocha).

Шаг 12. Добавление индикатора Travis CI в README.md.


Travis CI предоставляет URL картинки, которую можно поместить один раз в README на Гитхабе и затем всякий раз видеть там, успешно ли прошло на Travis CI тестирование итогов последнего коммита.

На сервере shields.io предоставляется возможность получить несколько другую форму этой картинки — например, изменить объёмность, или закругление краёв, или часть надписи.

Обобщение метода


Здесь заканчивается список простых начальных шагов разработки модуля для Node.js.

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

Приведу пример. В статье про паралич разработчика (которую PerseptronYar перевёл) вы могли видеть на Хабрахабре рассказ о том, что всякий современный сайтостроитель, приступая к работе, без промедления оказывается перед неотступною необходимостью раз за разом делать неочевидный выбор между различными возможностями примерно равной привлекательности — наподобие буриданова осла: Вы можете, однако, сильно упростить себе жизнь в дальнейшем, если сумеете не только один раз сделать такой выбор, но и в дальнейшем попросту придерживаться его. Для этого достаточно зафиксировать в письменном виде выбор, сделанный на каждом таком шаге, записать все эти шаги в их готовом виде (то есть, например, не «выбрать CSS framework», а «установить Bootstrap», если выбрали Bootstrap), а затем следовать им при начале каждого нового проекта, если только в промежутке между проектами не объявился повод сознательно попробовать чего-нибудь новенькое (ну, например, вы сможете отказаться от Underscore.js в пользу новейших возможностей языка JavaScript, когда постепенный переход читателей на новые браузеры обеспечит вам такую возможность).
Tags:
Hubs:
+16
Comments 3
Comments Comments 3

Articles