Тестируем создание библиотеки компонент для Angular с помощью новой команды для Angular/Cli — library

vzyy8r9awmadsuo40my_bkiwvgq.png

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

Проблема в том, что второй вариант требует значительных усилий по подготовке и каждый такой проект уникальный — со своим инструментарием в котором каждому новому разработчику нужно разбираться заново. В конце июля, команда Angular предложила свое, комплексное, решение этой проблемы добавив в angular/cli новую команду для создания библиотек — library.

Давайте посмотрим, что из этого получилось.

Для тестов, взята самая свежая из стабильных версий angular/cli — 6.1.5 (04.09.2018)


Идеальный мир

В идеальном мире все должно быть удобно. Так, для библиотеки компонент я бы выделил три важных момента


  • Единообразность проектов и быстрый старт
  • Удобство разработки
  • Удобство распространения


Итак начем со старта

Для того что бы создать свою библиотеку нам нужно сделать два шага — создать новый проект и добавить к нему библиотеку. Сначала создадим новый проект:

npx @angular/cli@latest new mylibapp


npx

Я использую npx что бы не устанавливать cli глобально и избегать npm run конструкций. Если у вас npm версии 5.2 или новее — попробуйте. Подробнее почитать можно здесь

После выполнения команды, мы увидим стандартный (для 6 ангуляра, который отличается от 5ой версии) проект в котором будут созданы два под-проекта — основной mylibapp и mylibapp-e2e. Сам angular проект теперь описывается в angular.json.

y2c5tainati1vflp_ybvd3t2eci.png

Библиотеки, как видим, пока нет.

И вот он первый нюанс. Наше название уже занято основным проектом, и назвать библиотеку так же уже не выйдет. Поэтому, если вы хотите назвать библиотеку my-super-library, сначала нужно создать проект, который должен называться как-то по-другому. Например, my-super-library-project. И только потом, создавать библиотеку с желаемым названием.

Теперь создадим третий под-проект и сгенерируем библиотеку.

cd mylibapp
npx ng generate library mylib --prefix mlb

Указывать префикс не обязательно, но очень желательно что бы не пересекаться с другими библиотеками.

4xjqyf4fibv_lks9vokgqkkyrri.png

Как видно, теперь, третьим под-проектом добавилась наша наша библиотечка. Она имеет свой отдельный package.json, tsconfig и karma.conf.js, что позволяет настраивать ее без боязни задеть остальные проекты. Кстати, при желании мы можем добавить еще одну библиотеку и она тоже будет отдельным подпроектом. Но вот почему библиотеку нельзя было выделить совсем отдельным проектом (как например в .Net) я не знаю. И если e2e проект не сложно удалить руками, то основной проект — уже нет. В итоге в репозитории появляется лишний код, что не очень хорошо.

Теперь давайте посмотрим, какие инструменты мы получаем сразу. Это связка tslint + codelyzer, karma + jasmine и protractor для e2e. Т.е. стандартный набор angular проекта, ничего специфичного для библиотеки нам не подвезли. Это немного странно, так как какой-то инструмент для просмотра компонент и рендера их в документацию (например storybook) просто must have. Но ладно, будем считать, что тут нам просто оставили пространство для маневра.

Давайте запустим тесты и линтер что бы убедиться, что все работает.

npm test mylib
npx ng lint mylib

У меня все прошло без проблем, но для тестирования был использован Chrome, что тоже странно. Я ничего против него не имею, но на билд серверах его на 90% не будет. Почему не использовали тот же Puppeteer — не понятно.

Подведем итоги:

Плюсы


  • Быстрый старт нового проекта
  • Единообразный подход

Минусы


  • Лишний код в проекте
  • Очевидные вещи нужно допиливать руками

Пока ничего критичного, продолжаем копать дальше.


Разработка

Кое-какие компоненты у нас уже есть «из коробки», давайте на них посмотрим. Поскольку никаких специальных инструментов у нас нет, будем использовать основной проект (вот он оказывается зачем нужен). Для этого нам нужно сбилдить библиотеку, сделать импорт библиотечного модуля и запустить основной проект.


немного кода
npx ng build mylib
import { MylibModule } from "mylib";

...

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule, MylibModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
npm start

После того как все выполнится, мы увидим наш компонент из библиотеки. Но снова есть нюанс — watch режим для библиотеки пока не сделали, нужно каждый раз запускать билд библиотеки самостоятельно? Watch появится только в angular/cli 6.2+. И не из коробки, для этого придется добавить новый флаг в tsconfig.json

tsconfig.json

"angularCompilerOptions": {
    "enableResourceInlining": true,
}

А после этого запускать билд c флагом watch:

ng build mylib --watch

Если же вы в силу каких-то причин будете использовать cli младше 6.2, билдить придется самостоятельно, что, прямо скажем, — плохо.

Теперь давайте добавим новый компонент. Для этого нужно выполнить стандартную команду generate component. Из-за того, что библиотека не наш основной проект, приходится использовать флаг проекта, что тоже немного раздражает (а вот если бы библиотека была самостоятельным решением…).

npx ng generate component some-nice-image --project mylib

Теперь под mylib/src создадим папку assets, добавим картинку и снова пересоберем библиотеку что бы увидеть резульат. И тут нас ждет еще один сюрприз — картинки нет. Оказывается, что ресурсы, используемые в библиотеке не попадают в билд автоматически, их нужно копировать самостоятельно (или вот так). И вроде бы не страшно, но все равно как-то не правильно.

Зато, из-коробки должен работать tree-shaking. Давайте создадим еще один компонент в библиотеке, но не будем использовать его в основном проекте. Собираем основной проект в продакш режиме

npx build --prod

И видим, что размер бандла не изменился. Tree-Shaking с библиотеками действительно работает!

Теперь неплохо было бы попробовать поставить какую-то зависимость. Поскольку каждый проект имеет свой собственный package.json нам нужно сначала перейти в папку библиотеки и выполнять команду npm install

npm i -D @drag13/when-do
npm i @drag13/round-to

Я специально поставил их по-разному что бы проверить как потом с этим справится упаковщик. Все ставится без проблем. Пробуем собрать и получаем предупреждение


Distributing npm packages with 'dependencies' is not recommended. Please consider adding drag13/round-to to 'peerDependencies’or remove it from 'dependencies

Распространение npm пакетов с зависимостями не желательно. Пожалуйста, подумайте, что бы добавить зависимость drag13/round-to к peerDependencies или вообще убрать ее из зависимостей

а, затем, и ошибку:


Dependency drag13/round-to must be explicitly whitelisted

Зависимость drag13/round-to должна быть явно добавлена в белый список

Вот это уже интересно, by design, библиотека не хочет иметь прямых зависимостей. Пробуем переместить нашу зависимость в секцию peerDependencies и собраться заново — вуаля, все работает. Но это значит порядок установки сторонних библиотек теперь другой. Сначала ставим зависимость на основной модуль, потом, ручками добавляем в секцию peerDependencies библиотеки.

Остальное работает так же, как и в обычном Angular проекте.

Коротко подведем итоги:

Плюсы:


  • Работаем в знакомом окружении со знакомыми командами
  • Есть tree-shaking из коробки

Минусы:


  • Для «посмотреть компонент» нужно использовать целый проект
  • Пока еще нет watch режима
  • Ресурсы нужно копировать вручную или настраивать билд-процесс самостоятельно.

И, наконец, переходим к публикации


Публикация

Вот тут все прямо хорошо. Для публикации angular/cli использует уже неплохо зарекомендовавший себя ng-packgr который самостоятельно собирает наш код в пригодный для публикации npm пакет оставляя за бортом настройку package.json файла (а это не мало), минификацию, упаковку в разные форматы (например в UMD).

Для того, чтобы опубликовать свой пакет (или посмотреть что там внутри) нужно выполнить три команды

npx ng build --prod
cd dist/mylib
npm publish

Если вы не хотите паблишить, замените команду publish на pack

В результате у меня получилось следующее:

3h_bhbz93wewuwvdnhclpt0z5sy.png

Для начала давайте заглянем в package.json, который выглядит совсем не так оригинальный package.json нашей библиотеки.

xug-ogiokkrxhddurgedhjjaejw.png

Как видим, packagr не стал удалять наши devDependencies, хотя некоторые так делают. Кроме того теоретически радует количество форматов, которые описаны в package.json (пусть я и половины их не знаю).

Внутри пакет содержит минифицированный и не минифицированный бандл в формате UMD, и еще несколько бандлов внутреннего формата angular (fesm5, fesm2015). Но, главное, теперь об этом не будет болеть голова разработчиков что просто замечательно.

Перейдем к выводам

Плюсы:


  • Удобство
  • Продуманность


Итого

Решение получилось интересное, но сырое. Старт и публикация очень удобны, но к разработке пока есть вопросы. Особенно растраивает, что сейчас библиотека не является самостоятельным проектом by design, а скорее дополнением к основному проекту с возможностью публикации.

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

© Habrahabr.ru