Как стать автором
Обновить

composer vs npm: многомодульная разработка

PHP *JavaScript *Программирование *

Последние года три-четыре я при программировании на PHP использовал composer для управления зависимостями приложения. Сейчас появилась потребность перейти на nodejs и, как следствие, настроить привычную для себя среду разработки. Благо, что я использую IDE PhpStorm, который позволяет работать и с PHP, и с JS. Особенностью проектов, в которых я участвую, является многомодульность. Функциональность разделяется между модулями не столько для повторного использования, сколько для уменьшения итоговой сложности приложения за счёт декомпозиции на слабосвязанные компоненты. В общем, для этих проектов нормально, когда в рамках решения одной задачи изменения вносятся в несколько модулей и коммитятся в несколько репозиториев.


image


При настройке nodejs-проекта я столкнулся с некоторыми особенностями, которые осложняют многомодульную разработку. Данная публикация родилась в процессе попытки разобраться с этими особенностями. Под катом взгляд PHP'шника на развёртывание nodejs-проекта.


Структура демо-проекта


Проект состоит из 3 модулей:


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

image


Код каждого модуля располагается на github'е:



Дескрипторы модуля для соответствующих менеджеров зависимостей (composer.json и package.json) находятся в каждом модуле, т.е., каждый модуль можно разворачивать и как php-модуль, и как js-модуль. PHP-код и JS-код в модулях просто размещаются рядом, не пересекаясь друг с другом.


Запуск приложения на выполнение:


$ php index.php
$ nodejs index.js

Результат работы в обоих случаях:


This is application.
This is func module.
This is base module.

Цели


Проект в рабочем окружении должен позволять:


  • отслеживать через IDE изменения в каждом модуле, участвующем в разработке;
  • при помощи IDE коммитить изменения в разные репозитории в рамках одного действия;
  • использовать отладчик для трассировки выполнения кода модулей;

Развёртывание через composer


Здесь всё привычно. В дескрипторе развёртывания (composer.json) приложения указываем адреса репозиториев с модулями и прописываем master-ветки модулей в качестве зависимостей с желаемой версией:


{
  "require": {
    "flancer64/habr-cvsn-mod-base": "dev-master as 0.1.0",
    "flancer64/habr-cvsn-mod-func": "dev-master as 0.1.0"
  },
  "repositories": [
    {
      "type": "vcs",
      "url": "https://github.com/flancer64/habr-cvsn-mod-base"
    },
    {
      "type": "vcs",
      "url": "https://github.com/flancer64/habr-cvsn-mod-func"
    }
  ]
}

После выполнения команды:


$ composer install

В каталоге ./vendor появляются подкаталоги с модулями, в свою очередь содержащие .git-каталоги:


  • ./vendor/
    • ./flancer64/
      • ./habr-cvsn-mod-base/
        • ./.git/
      • ./habr-cvsn-mod-base/
        • ./.git/

Т.е., composer сразу разворачивает зависимости в виде, пригодном для разработки (контроля версий). Остаётся только настроить IDE PhpStorm (поставить зависимые модули под контроль версий):


image


и можно отслеживать изменения во всех разрабатываемых модулях:


image


и одновременно коммитить все изменения в локальные репозитории:


image


и push'ить в удалённые:


image


С отладкой также нет проблем. Можем ставить точки останова (breakpoints) на любой строке кода базового модуля — после запуска приложения под отладчиком останов происходит там, где нужно:


image


В общем, привычное окружение, удобное, как домашние тапочки.


Обычное развёртывание через npm


В отличие от composernpm не предполагает, что в проекте какие-то модули из каталога node_modules могут находиться под контролем версий. Мы можем в дескрипторе развёртывания package.json указать, что модуль нужно подгружать из внешнего репозитория (например, с githib'а):


{
  "dependencies": {
    "habr-cvsn-mod-base": "github:flancer64/habr-cvsn-mod-base",
    "habr-cvsn-mod-func": "github:flancer64/habr-cvsn-mod-func"
  }
}

но у нас нет опции, чтобы npm при этом создал локальный репозиторий для загруженного модуля (подкаталог .git).


После выполнения команды:


$ npm install

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


image


(отсутствует подкаталог ./.git/ в ./node_modules/habr-cvsn-mod-base/)


Зато отладчик останавливается в базовом модуле без проблем:


image


Развёртывание через npm с использованием опции link


Чтобы модули из ./node_modules/ можно было держать под контролем версий, разработчики npm предлагают использовать опцию 'link'. Если коротко, то суть подхода заключается в том, что модули, которые нужно версионировать, клонируются в произвольное место на диске разработчика (допустим, в /home/alex/work/habr/), а затем линкуются в /usr/lib/node_modules/ при помощи команды:


# npm link

(мне понадобились права root'а для выполнения)


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


$ npm link habr-cvsn-mod-base
$ npm link habr-cvsn-mod-func

npm найдёт соответствующие модули в /usr/lib/node_modules/ и замкнёт на них соответствующие подкаталоги из ./node_modules/ проекта:


$ ls -lh ./node_modules/
total 0
lrwxrwxrwx 1 alex alex 57 jūl  2 16:18 habr-cvsn-mod-base -> ../../../../../../usr/lib/node_modules/habr-cvsn-mod-base
lrwxrwxrwx 1 alex alex 57 jūl  2 16:18 habr-cvsn-mod-func -> ../../../../../../usr/lib/node_modules/habr-cvsn-mod-func

Модули в /usr/lib/node_modules/ сами, в свою очередь, являются ссылками на оригинальное местонахождение модулей:


$ ls -lh /usr/lib/node_modules/
...
lrwxrwxrwx  1 root root   39 jūl  2 16:18 habr-cvsn-mod-base -> /home/alex/work/habr/habr-cvsn-mod-base
lrwxrwxrwx  1 root root   39 jūl  2 16:18 habr-cvsn-mod-func -> /home/alex/work/habr/habr-cvsn-mod-func
...

И по своему постоянному месту "прописки" содержат локальный репозиторий:


$ ls -lha /home/alex/work/habr/habr-cvsn-mod-base
...
drwxrwxr-x 8 alex alex 4,0K jūl  2 16:18 .git
...

Таким образом, мы можем настроить IDE на контроль изменений в зависимостях проекта:


image


Проблемы начинаются при попытке запустить приложение:


$ nodejs index.js 
internal/modules/cjs/loader.js:670
    throw err;
    ^

Error: Cannot find module 'habr-cvsn-mod-base'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:668:15)
    at Function.Module._load (internal/modules/cjs/loader.js:591:27)
    at Module.require (internal/modules/cjs/loader.js:723:19)
    at require (internal/modules/cjs/helpers.js:14:16)
    at Object.<anonymous> (/home/alex/work/habr/habr-cvsn-mod-func/src/index.js:3:14)
...

Приложение видит залинкованный функциональный модуль, но сам функциональный модуль не видит залинкованный базовый. Для выхода из положения предназначен ключ '--preserve-symlinks' для nodejs:


$ nodejs --preserve-symlinks index.js

Добавляем ключ в команду запуска проекта в IDE:


image


Теперь запуск проходит, но есть проблемы с отладкой — не отрабатывают точки останова в зависимостях. Можно остановиться в головном модуле и спуститься по шагам в исходники зависимостей, но самой точки останова IDE PhpStorm в runtime не видит, хотя и отображает:


image


Разработчики IDE говорят, что должно работать, но не работает (кэш сбрасывал, IDE перезапускал).


В общем-то, целью этой публикации был опрос js-коллег, как они выкручиваются в подобной ситуации, но в процессе написания статьи вырисовалась ещё одна комбинация для развёртывания проекта:


Размещение исходников во внутренних каталогах npm-проекта


Оказалось, что если для линковки использовать клоны модулей с github'а, созданные composer'ом в подкаталоге ./vendor/, а не завязываться на внешние по отношению к проекту каталоги, то js-скрипты запускаются без ключа --preserve-symlinks и, что для меня более важно, IDE PhpStorm видит точки останова внутри модулей. Т.к. нет смысла использовать composer лишь для клонирования модулей проекта, я использовал обычный git и склонировал исходники модулей в подкаталог ./own_modules. После чего повторил манипуляции из предыдущего пункта:


  • связал модули в подкаталоге ./own_modules/... с системной библиотекой /usr/lib/node_modules/;
  • связал модули в системной библиотеке с проектом;
  • настроил IDE PhpStorm на работу с локальными репозиториями в подкаталоге ./own_modules/;

image


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


Резюме


Сравнив два подхода к сборке многомодульных приложений (PHP с composer'ом и JS с npm), могу сделать вывод, что composer более дружелюбен к разработчикам, чем npm. Возможно, что разработчики composer (первый релиз в 2012) учли опыт разработчиков npm (первый релиз в 2010). Тем не менее, при некоторых дополнительных усилиях, npm также предоставляет возможность разрабатывать многомодульные приложения в довольно комфортных условиях.


Командные скрипты для развёртывания проекта в различных режимах:


Теги: composerphpjavascriptnpmмодульностьphpstormdebug
Хабы: PHP JavaScript Программирование
Всего голосов 17: ↑14 и ↓3 +11
Комментарии 3
Комментарии Комментарии 3

Похожие публикации

Лучшие публикации за сутки