Composer и пакеты nodejs как зависимости

Недавно у меня возникла необходимость прописать один из пакетов nodejs как зависимость для моего Symfony бандла. Как скормить composer'у такой пакет я не знал. Далее я хочу рассказать о том, какие варианты решения я рассматривал, на каком из них в итоге остановился и почему именно на нем. Также хочу сказать, что способы применимы не только при написании бандлов, но и любых библиотек.

На сколько я знаю, composer позволяет определять в качестве зависимостей любые php библиотеки, находящиеся к примеру на packagist.org. Также в composer'е можно прописывать различные php расширения в качестве зависимостей, к примеру:

{
    "require": {
        "ext-mongo": "*"
    }
}

Мне необходимо было подключить csscomb и тривиального способа с помощью секции require для меня не оказалось. В качестве вариантов решения рассматривал следующие подходы:

Описание зависимостей и их установки в документации

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

yum install node
nmp install -g csscomb

Описываем что необходимо установить nodejs и потом пакет csscomb.

Плюсы:

- минимум усилий для разработчика бандла

Минусы:

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

- отсутствие автоматического контроля на наличие всех необходимых зависимостей и их версий

Хуки сomposer'а

Как и большинство людей я стараюсь делать бандлы простыми в использовании для конечного пользователя, чтобы максимум что им пришлось бы сделать - нажать кнопку Enter и бандл вместе с его зависимостями уже развернут в проекте. Как мы видим, предыдущий вариант явно не соответсвует моим целям. Вместо одной кнопки пользователям придется ставить все пакеты nodejs вручную. Несмотря на то, что в моем бандле был необходим лишь один пакет nodejs, я старался решить задачу в более общем смысле, и найти подход, который можно было бы в дальнейшем использовать в подобных случаях.

Composer позволяет задавать список компанд, которые должны исполняться после обновления зависимостей (post-update-cmd) или после выполнения composer install (post-install-cmd). Давайте посмотрим, что мы можем сделать, имея их на примете. 

Мы можем написать скрипт, например на bash'e, который проверял бы наличие необходимых пакетов nodejs в системе и в случае необходимости устанавливал их. Как вариант можно было бы сделать отдельный конфигурационный файл, где можно было бы прописывать именно пакеты nodejs, от которых зависит проект. Далее считывать его и исполнять наш скрипт.

Данный вариант кажется мне вполне рабочим, но для его реализации понадобится придумать пусть и простой, но формат описания зависимостей для пакетов nodejs. Здесь можно было бы использовать формат аналогичный composer'у, зачем изобретать свой велосипед, если все уже есть. Далее, чтобы мы не написали, какой бы язык не использовали, в итоге у нас получится файл, который мы все равно должны включать в каждый проект, который требует зависимости от пакетов nodejs. Для улучшения данного момента мы можем написать скрипт на php и добавить его на packagist.org, тогда можно было бы его подключать как любую другую php зависимость:

{
    "require": {
        "fdevs/node-dependencies": "*"
    }
}

Помимо этого надо было бы описывать в документации, что необходимо не забыть добавить его в post-update-cmd и post-install-cmd. Работы не так уж мало, учитывая что задача добавления зависимости сама по себе как бы кажется тривиальной.

Плюсы:

- пользователю не придется заниматься установкой пакетов nodejs вручную

- в итоге пользователь получит инструмент, расширяющий возможности composer'а и позволяющий относительно удобно прописывать в зависимостях пакеты nodejs

Минусы:

- все также необходимо руками устанавливать сам nodejs

- необходимо добавлять скрипт в секции post-update-cmd и post-install-cmd в composer.json

- решение является затратным по времени реализации

Готовое решение

Думаю многие со мной согласятся в том, что хорошей практикой является research перед тем как садиться и писать код. В ходе относительно недолгого поиска на packagist.org я наткнулся на уже готовую реализацию задума описанного выше, но как по мне сделанного более красиво и удобно, в качестве плагина для композера.

Плагин eloquent/composer-npm-bridge позволяет пользоваться package.json (по ссылке предоставлен довольно удобный cheat sheet, благодаря которому можно очень быстро и просто понять что к чему), который используется для описания пакетов nodejs и по сути является аналогом нашего composer.json.

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

1) Добавляем eloquent/composer-npm-bridge в качестве зависимости в наш composer.json:

{
    "require": {
        "eloquent/composer-npm-bridge": "~2"
    }
}

2) Создаем package.json в корне бандла (проекта). В моем случае он имел следущий вид:

{
    "name": "css-fixer-bundle",
    "version": "1.0.0",
    "description": "Fixes css styles",
    "author": "Victor Melnik",
    "licence": "MIT",
    "dependencies": {
        "csscomb": "3.*"
    },
    "private": true,
    "subdomain": "fdevs"
}

Главное что нас тут интересует - секция dependencies, аналог секции require composer'а. Название и версия, все просто. Таким образом с помошью этого composer-npm-bridge я указал, что мой бандл требует csscomb для своей работы

3) Готово

При выполнении команд composer update, composer install этот плагин будет считывать package.json или npm-shrinkwrap.json соответственно (аналог нашего composer.lock) и решит все проблемы с npm зависимостями за вас.

Плюсы:

- настройка занимает минимум времени и дает максимум результатов

- полностью избавляем конечного пользователя от установки и обновления пакетов nodejs

- решение не является одноразовым, а легко применимо в любом проекте

Минусы:

- все же необходимо добавить одну строчку в документацию, что пользователю необходимо иметь nodejs

Вывод

В итоге мы нашли способ, именно способ, а не разовое решение, который позволит нам в дальнейшем легко обозначать зависимости от пакетов nodejs в любом проекте, а также избавить уже конечных пользователей от головной боли и ненужных действий. Какой бы уникальной не казалась задача, не забывайте о research'е, т.к. велика вероятность, что кто-то уже сталкивался с чем-то подобным.