Bower: зачем фронтенду нужен менеджер пакетов

Статья написана в 2014-м году, во времена npm 2, который был не очень популярен среди фронтендеров из-за описанных в статье недостатков. Сейчас пользуйтесь Ярном или npm, а статью считайте исторической.

Статья впервые была опубликована в майском номере журнала Хакер. В журнал вошла сильно сокращённая версия; ниже — полная.

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

Хождение по сайтам библиотек, скачивание и распаковка архивов, копирование файлов в проект — всё это заменяется парой команд в терминале.

У многих языков программирования есть стандартные менеджеры пакетов, которыми разработчики пользуются для установки всех библиотек: gem у Руби, pip у Питона и другие. У серверного Яваскрипта есть npm (почему он не подходит для клиентского — ниже), а у клиентского яваскрипта до недавнего времени ничего не было. Было множество разных пакетных менеджеров (Jam, Component, Volo, Ender), но большинство из них так и не стали популярными, а от менеджера пакетов, которым не поставишь нужных библиотек, толку мало.

Бовер — не стандартный менеджер пакетов для клиентского Яваскрипта, но самый популярный: сейчас там больше 11 тысяч пакетов.

Бовер не навязывает пользователю свою систему сборки, а разработчику пакетов — метод подключения библиотеки (AMD, CommonJS и другие). Всё, что делает Бовер — устанавливает нужные проекту пакеты подходящих версий вместе с их зависимостями. Другими словами: просто загружает файлы нужных библиотек и всё, что нужно для их работы в специальную папку. Остальное остаётся на усмотрение разработчика.

Почему не npm

Главное отличие npm и Бовера — подход к установке зависимостей пакетов. npm устанавливает зависимости для каждого пакета отдельно, в итоге получается большое дерево пакетов (nodemodules/grunt/nodemodules/glob/nodemodules/…), где может быть несколько версий одного и того же пакета. В клиентском Яваскрипте это недопустимо: нельзя подключить на страницу две версии jQuery или любой другой библиотеки. В Бовере каждый пакет устанавливается один раз (jQuery всегда будет в папке bowercomponents/jquery, сколько бы пакетов от него не зависело) и в случае конфликта зависимостей, Бовер просто не станет устанавливать пакет, не совместимый с уже установленными.

Установка Бовера

Для работы с Бовером вам потребуются Node и Git. Установка:

npm install -g bower

Работа с пакетами

Попробуем что-нибудь установить, например, jQuery:

bower install --save jquery  # Или bower i -S jquery

Эта команда скачает jQuery последней версии в папку bower_components/jquery.

Флаг --save говорит Боверу, что он должен сохранить имя пакета и его версию в файл-манифест — bower.json. В этом файле хранится список всех зависимостей проекта (пакетов, установленных через Бовер) и другие метаданные, нужные для создания своих пакетов (об этом в конце статьи). Вместе с именами пакетов можно указать версии, с которыми ваш проект гарантированно будет работать.

У нас такого файла ещё нет, о чём и говорит строчка «No bower.json file to save to, use bower init to create one» в логе. Создадим его:

bower init

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

На вопрос «set currently installed components as dependencies?» нужно ответить «Yes» — все ранее установленные компоненты (в нашем случае это jQuery) автоматически попадут в созданный JSON-файл. А на вопрос «would you like to mark this package as private which prevents it from being accidentally published to the registry?» — тоже «Yes» — это предотвратит случайную регистрацию пакета в реестре Бовере.

Поставим ещё несколько пакетов:

bower install --save social-likes jquery-icheck fotorama

И посмотрим, что у нас получилось:

bower list
bower check-new     Checking for new versions of the project dependencies..
bowertest#0.0.0 /Users/admin/bowertest
├─┬ fotorama#4.5.1
│ └── jquery#2.1.0 (2.1.1-beta1 available)
├── jquery#2.1.0 (2.1.1-beta1 available)
├─┬ jquery-icheck#1.0.2
│ └── jquery#2.1.0 (2.1.1-beta1 available)
└─┬ social-likes#3.0.2
  └── jquery#2.1.0

Команда bower list показывает список всех установленных пакетов. Тут мы видим, что все пакеты зависят от jQuery, и что Бовер смог найти подходящую всем версию — jQuery 2.1.0.

На файловой системе это выглядит вот так:

tree -L 2
.
├── bower.json
└── bower_components
    ├── fotorama
    ├── jquery
    ├── jquery-icheck
    └── social-likes

5 directories, 1 file

Каждый пакет устанавливается в свою папку, вложенных пакетов нет, jQuery встречается только один раз. В корне проекта лежит созданный командой bower init файл bower.json, но теперь там перечисленны уже все пакеты, которые показывает bower list, а не только jQuery.

Для удаления пакетов используется команда bower uninstall:

bower uninstall --save jquery-icheck  # Или bower un -S jquery-icheck

Вы можете спокойно удалять папку bower_components или добавить её в ваш .gitignore. Команда bower install (без дополнительных параметров) вернёт всё как было:

bower install

Развёртывание проекта

Есть два подхода к развёртыванию проектов:

  1. В репозиторий добавляется только файл-манифест и все пакеты устанавливаются во время развёртывания проекта. Так в репозитории не хранится ничего лишнего, но если во время развёртывания упадёт Гитхаб или другой сервер, с которого устанавливаются пакеты, будут проблемы.
  2. В репозиторий добавляется не только bower.json, но папка bower_components. Так развёртывание не зависит от внешних серверов, но репозиторий раздувается десятками (а то и сотнями) лишних файлов.

Семантические версии (semver)

Semver — это, во-первых, подход к версионированию библиотек: формат номера версии МАЖОРНАЯ.МИНОРНАЯ.ПАТЧ и правила, по которым следует увеличивать то или иное число.

А во-вторых — это способ описания требуемых версий зависимостей, который используют Бовер и npm.

При установке с флагом --save версии пакетов добавляются в bower.json в виде ~1.0.1. Тильда в начале говорит о том, что при установке будет выбрана версия 1.0.1 или с большим последним числом (ПАТЧ), если она есть. Таким образом будет установлена версия с последними исправлениями ошибок, но полностью совместимая с той, что указана в файле-манифесте.

Обновление зависимостей

В Бовере есть команда bower update, но она обновляет пакеты с учётом требований файла-манифеста. Например, если там указан jQuery ~2.0.0, то Бовер сможет обновить jQuery до версии, скажем, 2.0.9, но 2.1.0 уже не поставит, потому что эта версия не соответствует формуле ~2.0.0.

Для обновления пакетов (и bower.json) до действительно последних версий можно воспользоваться утилитой bower-update. Устанавливаем:

npm install -g bower-update

Запускаем:

bower-update

Поиск пакетов

Есть два способа найти пакет в Бовере: гиковский и обычный.

Гиковский:

bower search jquery
Search results:

    jquery git://github.com/jquery/jquery.git
    jquery-ui git://github.com/components/jqueryui
    ...

Обычный: открыть в браузере bower.io/search.

Автоматическая сборка

Бовер перекладывает проблемы сборки установленных пакетов на плечи разработчика. Самый простой способ — просто склеить JS-файлы Грантом, Галпом или любой другой системой сборки, которой вы пользуетесь.

Я пользуюсь Грантом, поэтому расскажу, как склеивать пакеты Грантом. О том как пользоваться Грантом была большая статья в июльском номере прошлого года, поэтому покажу сразу конфиг плагина grunt-contrib-concat:

concat: {
  main: {
    src: [
      'bower_components/jquery/jquery.min.js',
      'bower_components/fotorama/….js',
      'bower_components/jquery-icheck/….js',
      'bower_components/social-likes/social-likes.min.js’,
      'scripts/*.js'  // Скрипты вашего сайта
    ],
    dest: 'build/scripts.js'
  }
}

У этого способа есть много недостатков: вам нужно смотреть имена файлов для каждого пакета; следить, чтобы файлы собирались в правильном порядке (например, jQuery должен быть выше, чем скрипты, зависящие от него). Плагин grunt-bower-concat может делать это сам: он автоматически склеивает все установленные зависимости в правильном порядке в один файл:

bower_concat: {
  all: {
    dest: 'build/_bower.js',  // Склеенный файл
    exclude: [  // Пакеты, которые нужно исключить из сборки
        'jquery',  // Если jQuery подключается с CDN Гугла
        'modernizr'  // Если подключаем скрипты в конце страницы; Modernizr нужно подключать в <head>
    ]
  }
},
concat: {
  main: {
    src: [
      'build/_bower.js',
      'scripts/*.js'  // Скрипты вашего сайта
    ],
    dest: 'build/scripts.js'
  }
}

Регистрация своих пакетов

Чтобы ваша библиотека стала доступна для установки через Бовер, её нужно зарегистрировать. Для этого:

— В корне проекта должен лежать файл-манифест bower.json. — У проекта должен быть гит-репозиторий (например, на Гитхабе). — Проект должен использовать семантические версии и в репозитории должен быть гит-тег для последней версии.

Для создания файла файла-манифеста снова воспользуемся командой bower init:

bower init
[?] name: awesomelib
[?] version: 0.0.1
[?] description: My awesome jQuery plugin.
[?] main file: jquery.awesomeplugin
[?] keywords: jquery awesome yay
[?] authors: Artem Sapegin <artem@sapegin.ru>
[?] license: MIT
[?] homepage: https://github.com/sapegn/jquery.awesomeplugin.js
[?] set currently installed components as dependencies? Yes
[?] add commonly ignored files to ignore list? Yes
[?] would you like to mark this package as private which prevents it from being accidentally published to the registry? No

{
  name: 'awesomelib',
  version: '0.0.1',
  description: 'My awesome jQuery plugin.',
  main: 'jquery.awesomeplugin.js',
  keywords: [
    'jquery',
    'awesome',
    'yay'
  ],
  authors: [
    'Artem Sapegin <artem@sapegin.ru>'
  ],
  license: 'MIT',
  homepage: 'https://github.com/sapegn/jquery.awesomeplugin',
  ignore: [
    '**/.*',
    'node_modules',
    'bower_components',
    'test',
    'tests'
  ],
  "dependencies": {
    "jquery": "~2.1.0"
  }
}

[?] Looks good? Yes

И хотя обязательно нужно заполнить только имя пакета (поле name), остальные поля тоже очень полезны:

description и keywords помогут пользователям найти вашу библиотеку через интерфейс поиска пакетов. — main определяет главный файл пакета. Это поле может быть использовано автоматическими сборщиками, такими как grunt-bower-concat. — license — всегда указывайте лицензию: она говорит потенциальному пользователю вашего пакета, сможет ли он использовать его в своём проекте. Например, лицензия GPL требует, чтобы любой использующий её проект тоже распространялся по этой лицензии, что не всегда возможно. — ignore — по умолчанию Бовер скачивает весь репозиторий, что, во-первых, увеличивает время установки, а, во-вторых, добавляет в проект лишние файлы. Лучше всего исключить всё, кроме необходимых для работы пакета файлов (главный JS-файл, CSS и так далее), лицензии и ридми. — dependencies — все пакеты, от которых зависит ваш пакет.

Теперь нужно закоммитить bower.json, создать гит-тег с последней версией и запушить всё в удалённый репозиторий:

git add bower.json
git commit -m "Add bower.json."
git tag "v0.0.1"
git push origin --tags

Вот теперь можно регистрировать пакет:

bower register jquery-awesomeplugin git://github.com/sapegin/jquery-awesomeplugin.git

Дальше Бовер сам будет проверять обновления пакета, главное не забывать создавать гит-тег для каждой новой версии.

Для облегчения обновления своих пакетов можно воспользоваться такими инструментами, как grunt-bump или mversion.