Когда я создал angular-async-local-storage, было легко создать модуль Angular и использовать его прямо в моем приложении. Но поскольку он может помочь другим разработчикам, я хотел, чтобы он был модулем многократного использования, упакованным и потребляемым, как любые другие модули Angular.

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

Этот пост предназначен для опытных разработчиков, которые уже знают основные концепции Angular и знают, как создать базовое приложение. Французская версия этого поста доступна здесь.

Обновление: Angular 6

Это сообщение больше не актуально. В Angular 6 создать библиотеку Angular так же просто, как ng g library name. См. Официальную документацию по интерфейсу командной строки.

Того же автора

Создание модуля Angular: подводные камни

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

Во-первых, никогда не импортируйте BrowserModule. Ваш модуль является модулем feature, только конечный пользователь должен импортировать BrowserModule в модуль root приложения. Если вам нужны общие директивы (* ngIf, * ngFor…), импортируйте CommonModule.

Если ваш модуль предназначен для создания новых компонентов, директив или каналов, не забудьте экспортировать их. Заявленные доступны только внутри вашего модуля.

Самое главное, не смешивать компоненты / директивы / каналы и службы в одном модуле. Почему?

  • Служба, предоставляемая в модуле, будет доступна везде в приложении, поэтому ваш модуль следует импортировать только один раз, в модуль root пользовательского приложения (например, модуль Http).
  • Экспортированный компонент / директива / канал будет доступен только в модуле, импортирующем ваш, поэтому ваш модуль должен быть импортирован в каждый пользовательский модуль (корневой и / или функциональные модули), которым они нужны (например, CommonModule) .

Если вам это непонятно, вам следует мой другой пост« Понимание модулей Angular (NgModule) и их области действия », поскольку это важный (и сбивающий с толку) момент в Angular.

Наконец, соблюдайте правило Angular: никогда не используйте напрямую API-интерфейсы браузера (например, DOM). Если вы это сделаете, ваш модуль не будет совместим с Универсальным серверным рендерингом и другими расширенными опциями Angular. Если вам действительно нужно использовать API для конкретного браузера (localStorage…), вам следует попробовать / отловить ошибки.

Экспорт общедоступного API

Когда вы используете официальный модуль Angular, у вас есть только одна точка входа для импорта всего, что вам нужно (например, '@angular/http').

Поэтому вам нужно создать index.ts файл, экспортирующий все общедоступные API вашего модуля. Он должен, по крайней мере, содержать ваш NgModule, а также ваши компоненты или службы (пользователю нужно будет импортировать их, чтобы внедрить туда, где они необходимы).

Компоненты / директивы / каналы не будут импортированы пользователем напрямую, но вам необходимо экспортировать их, чтобы они были совместимы с AoT (спасибо Isaac Mann за эту информацию).

Инструменты сборки

Вот где я начал бороться. Итак, мне удалось скопировать, как работают официальные модули Angular, такие как HttpModule. Они используют:

  • Typescript через компилятор Angular (ngc) для транспилирования,
  • Rollupjs для упаковки,
  • Uglify-js для минимизации.
npm install @angular/compiler @angular/compiler-cli typescript rollup uglify-js --save-dev

Конфигурация TypeScript

Вот tsconfig.json моего модуля:

Есть несколько важных отличий от вашего классического tsconfig.json:

  • явные "paths" для других используемых вами модулей необходимы, так как окончательный комплект не будет включать их напрямую (подробнее об этом позже).
  • "angularCompilerOptions": { "strictMetadataEmit": true } необходимо для совместимости с AoT.
  • "declaration": true важен для создания файлов определений типов, поэтому у пользователя будет Intellisense для вашего модуля.
  • "noImplicitAny": true и "strictNullChecks": true рекомендуются во избежание ошибок и для совместимости со всеми пользовательскими конфигурациями. "noImplicitAny": true необходимо соблюдать начиная с Angular 4.0, а "strictNullChecks": true начиная с Angular 4.1.
  • "module": "es2015" важен для производительности, а "sourceMap": true - для отладки, но здесь ничего конкретного.
  • "stripInternal": true избегайте бесполезных объявлений для внутренних API и "skipLibCheck": true избегайте блокирования (безвредными) ошибками в используемых вами библиотеках.

Конфигурация накопительного пакета

Модули Angular поставляются в формате UMD, поэтому ваш rollup.config.js должен быть установлен соответственно. Вот пример:

Скрипт входа - это ваш перенесенный index.ts, поэтому он должен соответствовать вашей конфигурации TypeScript. bundles/modulename.umd.js - это обычный путь и имя, используемое модулями Angular.

Rollup требует moduleName для формата UMD. Это будет объект JavaScript, поэтому не используйте специальные символы (без тире).

Затем именно здесь происходит важный момент. В вашем модуле используются элементы Angular (по крайней мере, NgModule декоратор), но ваш пакет не должен не включать Angular.

Почему? Angular уже будет включен в приложение пользователя. Если ваш модуль тоже включает его, он будет там дважды, и будут фатальные (и непонятные) ошибки.

Поэтому вам нужно установить Angular как глобальный. И вам нужно знать имя модуля UMD для каждого модуля. Он следует этому соглашению: ng.modulename (ng.core, ng.common, ng.http...).

То же самое и с RxJS, если ваш модуль его использует. И имена модулей здесь довольно запутаны. Для классов (Observable…) это Rx. Для операторов (map, filter…) это Rx.Observable.prototype. Для прямых методов классов (of, fromEvent…) это Rx.Observable.

Строительство, наконец

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

Потом:

npm run build

Обратите внимание, что транспиляция не выполняется непосредственно TypeScript, вам следует использовать компилятор Angular (ngc): это TypeScript с некоторой дополнительной магией Angular.

Публикация в npm

Не публиковать все на npm, только dist каталог.

Вам нужно будет создать новый конкретный dist/package.json. Например :

Некоторые конкретные моменты:

  • "version" должен следовать семантическому управлению версиями. Любое критическое изменение означает увеличение основного числа (даже если это небольшое изменение). И когда вы изменяете свой модуль, чтобы не отставать от Angular, это незначительное приращение числа.
  • "main" и "module" пути необходимы для импорта пользователем . "typings" путь для Intellisense.
  • "licence": "MIT": важна лицензия на открытый исходный код, иначе ваш модуль бесполезен. Angular использует лицензию MIT, и вам следует ее придерживаться.
  • Модули Angular, которые вы использовали, будут перечислены в peerDependencies. По-прежнему используйте semver со знаком ^, иначе ваш модуль будет устаревать при каждом обновлении Angular. Для других библиотек (RxJS, zone.js…) вы можете увидеть текущие требования Angular здесь.

Не забудьте написать README с документацией по вашему API. В противном случае ваш модуль бесполезен. Вы можете использовать такую ​​библиотеку, как copyfiles, чтобы скопировать README из корневого каталога проекта (отображается на Github) в каталог dist (отображается в репозитории npm).

А с настроенной учетной записью npm теперь вы можете опубликовать свой модуль:

cd dist
npm publish

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