Когда проектов становится чуть больше чем один, возникает необходимость как-то переиспользовать не только отдельные модули с кодом, но и сами 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.



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


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


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


cd mylibapp
npx ng generate library mylib --prefix mlb

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



Как видно, теперь, третьим под-проектом добавилась наша наша библиотечка. Она имеет свой отдельный 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


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



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



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


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


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


Плюсы:


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

Итого


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


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

Комментарии (0)