Angular Kütüphanesini npm'e Yayınlamak: ng-packagr, peerDependencies, dist-tag'ler ve Trusted Publishing
Angular kütüphanesini ng-packagr ile Angular Package Format'ta paketle, kaynak klasörünü değil dist çıktısını yayınla ve desteklediğin Angular aralığını peerDependencies ile beyan et. @bookstore/ui'de secondary entry point'i ve exports alanını kuruyor, peer aralığı karşılanmayınca ERESOLVE'u, npm 11'in tag'siz prerelease yayınını reddetmesini gösteriyor, dist-tag, deprecate ve trusted publishing workflow'unu ele alıyoruz.
Sürüm notu: Bu yazıdaki örnekler Angular 22.2 (ng-packagr 22.2.6, TypeScript 6.0), Node.js 24, npm 11.19 ve actionlint 1.7.12 ile denenmiştir. npm publish yalnız --dry-run ile koşuldu; hiçbir registry'ye paket yayınlanmadı. Trusted publishing workflow'u actionlint ile doğrulandı; GitHub'da koşulmadı.
Önce Tahmin Et
Sitedeki serilerin ortak örnek uygulaması Bookstore'un (bir online kitapçı) Angular istemcisi bookstore-web'deki fiyat gösterimini, kurumsal portal gibi başka uygulamalar da kullanabilsin diye @bookstore/ui adında bir kütüphaneye taşıdık. Ayşe, paketi yayınlamadan önce içine ne gireceğine bakmak istiyor ve komutu kütüphanenin kaynak klasöründe çalıştırıyor:
$ npm pack projects/bookstore/ui --dry-run
Paketin içinde ne olur? Derlenmiş JavaScript mi, TypeScript kaynakları mı? Bu paketi kuran bir uygulama onu kullanabilir mi?
npm notice 1.4kB README.md
npm notice 131B money/ng-package.json
npm notice 427B money/src/money.ts
npm notice 25B money/src/public-api.ts
npm notice 168B ng-package.json
npm notice 495B package.json
npm notice 652B src/lib/price-tag.spec.ts
npm notice 500B src/lib/price-tag.ts
npm notice 80B src/public-api.ts
npm notice 476B tsconfig.lib.json
npm notice 401B tsconfig.lib.prod.json
npm notice 431B tsconfig.spec.json
npm notice total files: 12
Pakette tek bir JavaScript dosyası yok: TypeScript kaynakları, test dosyası ve tsconfig'ler var. Bu paketi kuran uygulama, Angular'ın beklediği derlenmiş formatı bulamaz; Angular dokümanı da uygulamaların kütüphanenin .ts kaynağına değil, build çıktısına göre derlenmesi gerektiğini söylüyor. Doğru klasör dist/bookstore/ui. Bu yazıda kütüphaneyi oluşturup ng-packagr ile paketliyor, peerDependencies ile hangi Angular sürümlerini desteklediğini beyan ediyor ve dist-tag'ler, deprecate ve trusted publishing ile yayınlama mekaniğini ele alıyoruz. npm supply chain'inin güvenlik tarafını (provenance doğrulama, lifecycle script'leri) Frontend Güvenliği serisinin "npm Supply Chain'i Sertleştirmek" yazısında anlatmıştık; burada yayınlayan tarafın mekaniğine odaklanıyoruz.
Kütüphaneyi Oluşturmak: ng generate library ve Secondary Entry Point
Kütüphane, uygulamayla aynı Angular workspace'inde duruyor:
$ npx ng generate library @bookstore/ui
CREATE projects/bookstore/ui/ng-package.json (167 bytes)
CREATE projects/bookstore/ui/package.json (217 bytes)
CREATE projects/bookstore/ui/src/public-api.ts (62 bytes)
...
UPDATE angular.json (2835 bytes)
UPDATE package.json (860 bytes)
UPDATE tsconfig.json (1136 bytes)
Schematic, projects/bookstore/ui altında bir library projesi açıyor, angular.json'a ekliyor ve devDependencies'e ng-packagr'ı koyuyor. Kütüphanede iki parça var. Para biçimlendirmesi kendi başına işe yarayan bir parça; onu ayrı bir secondary entry point'e, @bookstore/ui/money'ye koyduk. Secondary entry point, kendi ng-package.json'u olan bir alt klasör; ng-packagr onu kendiliğinden buluyor:
{
"$schema": "../../../../node_modules/ng-packagr/ng-package.schema.json",
"lib": {
"entryFile": "src/public-api.ts"
}
}
import { Pipe, PipeTransform } from '@angular/core';
export interface Money {
amount: number;
currency: string;
}
@Pipe({ name: 'money' })
export class MoneyPipe implements PipeTransform {
transform(value: Money | null | undefined, locale = 'en-US'): string {
if (!value) {
return '';
}
return new Intl.NumberFormat(locale, { style: 'currency', currency: value.currency }).format(value.amount);
}
}
Ana entry point'teki component, pipe'ı paket yoluyla içeri alıyor. Angular dokümanı da aynı kütüphanedeki entry point'ler arasında göreli yol yerine paket yolunu kullanmayı öneriyor; göreli import'lar entry point'lerin sınırını bozuyor:
import { Component, input } from '@angular/core';
import { Money, MoneyPipe } from '@bookstore/ui/money';
@Component({
selector: 'bs-price-tag',
imports: [MoneyPipe],
template: `
<span class="price">{{ price() | money: locale() }}</span>
@if (listPrice(); as old) {
<s class="list-price">{{ old | money: locale() }}</s>
}
`,
})
export class PriceTag {
readonly price = input.required<Money>();
readonly listPrice = input<Money>();
readonly locale = input('en-US');
}
/*
* @bookstore/ui paketinin public API'si
*/
export * from './lib/price-tag';
public-api.ts, paketin dışarıya açtığı her şeyin listesi. Burada olmayan bir class paketi kullananlar için yok hükmünde; bu da kütüphanenin SemVer (Semantic Versioning; uyumluluk anlamı taşıyan sürüm kuralları) açısından "public API"sini tanımlıyor. Workspace içindeki uygulamanın kütüphaneyi bulabilmesi için schematic tsconfig.json'a bir path ekliyor; secondary entry point için ikinci satırı biz ekledik, yoksa @bookstore/ui/money çözülmüyor:
"paths": {
"@bookstore/ui": ["./dist/bookstore/ui"],
"@bookstore/ui/*": ["./dist/bookstore/ui/*"]
}
ng-packagr Çıktısı ve Angular Package Format
Kütüphaneyi build edelim:
$ npx ng build @bookstore/ui
Building entry point '@bookstore/ui/money'
✔ Compiling with Angular sources in partial compilation mode.
✔ Generating FESM and DTS bundles
✔ Built @bookstore/ui/money
Building entry point '@bookstore/ui'
✔ Compiling with Angular sources in partial compilation mode.
✔ Generating FESM and DTS bundles
✔ Writing package manifest
✔ Built @bookstore/ui
$ find dist/bookstore/ui -type f | sort
dist/bookstore/ui/.npmignore
dist/bookstore/ui/fesm2022/bookstore-ui-money.mjs
dist/bookstore/ui/fesm2022/bookstore-ui-money.mjs.map
dist/bookstore/ui/fesm2022/bookstore-ui.mjs
dist/bookstore/ui/fesm2022/bookstore-ui.mjs.map
dist/bookstore/ui/money/package.json
dist/bookstore/ui/package.json
dist/bookstore/ui/README.md
dist/bookstore/ui/types/bookstore-ui-money.d.ts
dist/bookstore/ui/types/bookstore-ui.d.ts
ng-packagr, Angular Package Format (APF) denen yapıda bir paket üretiyor: her entry point için tek dosyalık bir ES (ECMAScript; JavaScript'in standart adı) module bundle (fesm2022), TypeScript tip tanımları (types) ve bunları birbirine bağlayan bir package.json. Derleme "partial compilation mode"da yapılıyor. Angular dokümanına göre npm'e yayınlanacak kütüphaneler için önerilen mod bu, çünkü çıktı Angular'ın patch sürümleri arasında stabil kalıyor; full mode ise Angular'ın iç detaylarına bağlı ve yalnız kütüphane ile uygulamanın her zaman birlikte derlendiği durumlar için uygun. Üretilen package.json'ın özeti:
{
"name": "@bookstore/ui",
"version": "1.0.0",
"peerDependencies": {
"@angular/common": "^22.2.0",
"@angular/core": "^22.2.0"
},
"dependencies": {
"tslib": "^2.3.0"
},
"sideEffects": false,
"module": "fesm2022/bookstore-ui.mjs",
"typings": "types/bookstore-ui.d.ts",
"exports": {
"./package.json": {
"default": "./package.json"
},
".": {
"types": "./types/bookstore-ui.d.ts",
"default": "./fesm2022/bookstore-ui.mjs"
},
"./money": {
"types": "./types/bookstore-ui-money.d.ts",
"default": "./fesm2022/bookstore-ui-money.mjs"
}
},
"type": "module"
}
exports alanı paketin hangi yollarla import edilebileceğini belirliyor: kök (@bookstore/ui) ve ./money. Bu alan bir kapı gibi çalışıyor; @bookstore/ui/fesm2022/bookstore-ui.mjs gibi listede olmayan bir iç yolu import etmeye çalışan uygulama hata alıyor. Böylece paketin iç dosya düzeni public API'nin parçası olmaktan çıkıyor ve bir sonraki sürümde dosyaları yeniden düzenlemek breaking change olmuyor. money/package.json ise yalnız geliştirme sırasında gerekiyor; ng-packagr'ın ürettiği .npmignore onu pakete almıyor.
Kütüphanenin testi, uygulamanınki gibi Vitest ile koşuyor. Tsconfig path'leri dist klasörünü gösterdiği için önce build gerekiyor:
import { TestBed } from '@angular/core/testing';
import { PriceTag } from './price-tag';
describe('PriceTag', () => {
it('formats the price with its currency', async () => {
const fixture = TestBed.createComponent(PriceTag);
fixture.componentRef.setInput('price', { amount: 420, currency: 'TRY' });
fixture.componentRef.setInput('listPrice', { amount: 480, currency: 'TRY' });
await fixture.whenStable();
const host = fixture.nativeElement as HTMLElement;
expect(host.querySelector('.price')?.textContent).toBe('TRY\u00a0420.00');
expect(host.querySelector('.list-price')?.textContent).toBe('TRY\u00a0480.00');
});
});
$ npx ng build @bookstore/ui
$ npx ng test @bookstore/ui --watch=false
Test Files 1 passed (1)
Tests 1 passed (1)
Beklenen değerdeki dikkat çekebilir: Intl.NumberFormat, para birimi kodu ile tutar arasına normal boşluk yerine bölünmez boşluk (no-break space) koyuyor. Testi ilk yazdığımızda 'TRY 420.00' beklediğimiz için, ekranda birebir aynı görünen iki string'in eşit olmadığını söyleyen bir hata aldık.
peerDependencies: Desteklenen Angular Aralığını Beyan Etmek
Kaynak klasördeki package.json, paketin sürümünü ve bağımlılıklarını tutuyor:
{
"name": "@bookstore/ui",
"version": "1.0.0",
"description": "Shared Angular UI components of Bookstore.",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/example/bookstore.git",
"directory": "web/projects/bookstore/ui"
},
"peerDependencies": {
"@angular/common": "^22.2.0",
"@angular/core": "^22.2.0"
},
"dependencies": {
"tslib": "^2.3.0"
},
"sideEffects": false,
"publishConfig": {
"access": "public"
}
}
Angular paketleri dependencies'te değil peerDependencies'te. Fark önemli: dependencies'e yazılan bir paket, kütüphaneyle birlikte kurulur ve uygulamanınkinden farklı bir sürümse node_modules'ta ikinci bir kopya oluşur. Angular'da iki @angular/core kopyası, iki ayrı injector dünyası ve anlaşılması zor runtime hataları demek. peerDependencies ise "bu paketi sen getir, ben bu aralıkla çalışırım" diyor. Angular dokümanı da kütüphanelerde @angular/* paketlerini peerDependencies olarak yazmayı öneriyor; tslib gibi uygulamayla paylaşılması gerekmeyen yardımcılar dependencies'te kalabiliyor.
Aralık, kütüphanenin söz verdiği şey. ^22.2.0, 22.2 ve sonrasını ama 23'ü değil kabul ediyor. Hâlâ Angular 21'de olan bir uygulamada paketi kurmayı denedik:
$ npm pack dist/bookstore/ui
$ npm install ./bookstore-ui-1.0.0.tgz
npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error
npm error While resolving: institution-portal@undefined
npm error Found: @angular/common@21.2.26
npm error node_modules/@angular/common
npm error @angular/common@"21.2.26" from the root project
npm error
npm error Could not resolve dependency:
npm error peer @angular/common@"^22.2.0" from @bookstore/ui@1.0.0
npm error node_modules/@bookstore/ui
npm error @bookstore/ui@"file:bookstore-ui-1.0.0.tgz" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry this command with --force or --legacy-peer-deps to accept an incorrect (and potentially broken) dependency resolution.
npm, peer aralığı karşılanmadığında kurulumu durduruyor. --legacy-peer-deps ile zorlamak mümkün, ama hata mesajının dediği gibi sonuç bozuk bir bağımlılık ağacı olabilir. Angular dokümanının bir kuralı da bunu destekliyor: uygulamanın Angular sürümü, kütüphanenin derlendiği sürümle aynı ya da daha yeni olmalı. Bu yüzden aralığı geniş tutmak (>=20.0.0 gibi) bir kolaylık değil; 22 ile derlenmiş bir paketi 20'deki uygulamaya kurdurur ve hata runtime'a kalır.
Kütüphanenin kendi sürümünü Angular'ın major'ına hizalamak (Angular 22 için @bookstore/ui 22.x) sık görülen bir tercih. Artısı, kullanıcının hangi kütüphane sürümünün hangi Angular'la çalıştığını tek bakışta görmesi. Eksisi, kütüphanenin kendi breaking change'lerinin Angular'ın takvimine sıkışması: Angular major'ı değişmeden bir API'yi kıramazsın ya da kırarsan numara artık bir şey anlatmaz. Bookstore'da kütüphanenin kendi SemVer'ini tutuyoruz ve desteklenen Angular aralığını peerDependencies ile CHANGELOG'da açıkça yazıyoruz; Angular'ın yeni major'ına geçmek, peer aralığı değiştiği için kütüphanede de bir major oluyor.
Yayınlamak: dist Klasörü, Scoped Paket ve Registry
Yayın her zaman build çıktısından yapılıyor. --dry-run, paketin içeriğini ve nereye, hangi tag'le gideceğini registry'ye dokunmadan gösteriyor:
$ npm publish dist/bookstore/ui --dry-run
npm notice 📦 @bookstore/ui@1.0.0
npm notice Tarball Contents
npm notice 1.4kB README.md
npm notice 878B fesm2022/bookstore-ui-money.mjs
npm notice 1.1kB fesm2022/bookstore-ui-money.mjs.map
npm notice 2.4kB fesm2022/bookstore-ui.mjs
npm notice 1.6kB fesm2022/bookstore-ui.mjs.map
npm notice 915B package.json
npm notice 407B types/bookstore-ui-money.d.ts
npm notice 789B types/bookstore-ui.d.ts
...
npm warn publish This command requires you to be logged in to https://registry.npmjs.org/ (dry-run)
npm notice Publishing to https://registry.npmjs.org/ with tag latest and public access (dry-run)
+ @bookstore/ui@1.0.0
@bookstore/ui bir scoped paket: adı bir scope ile başlıyor. npmjs'te scoped paketler varsayılan olarak private kabul ediliyor ve ilk yayında --access public gerekiyor; bunu her seferinde hatırlamamak için kaynak package.json'a publishConfig.access koyduk ve çıktıdaki "public access" ondan geliyor. repository alanı da önemli: trusted publishing'de npm, bu adresin workflow'un çalıştığı repository ile birebir eşleşmesini istiyor.
Scope'un bir faydası daha var: bir scope'u tamamen başka bir registry'ye yönlendirebiliyorsun. Paketi şirket içi bir registry'de (GitHub Packages, Azure Artifacts ya da kendi kurduğun bir sunucu) tutuyorsan, tüketen projenin .npmrc'sinde yalnız o scope için adres ve kimlik bilgisi tanımlanıyor; diğer bütün paketler npmjs'ten gelmeye devam ediyor:
@bookstore:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
Token değeri dosyaya yazılmıyor, ortam değişkeninden okunuyor. Yerel bir registry'de (Docker'da Verdaccio) gerçek bir yayın denedik ve npm 11'in davranışını gördük:
$ npm publish dist/bookstore/ui --registry http://localhost:18487/
...
npm error code ENEEDAUTH
npm error need auth This command requires you to be logged in to http://localhost:18487/
npm error need auth You need to authorize this machine using `npm adduser`
npm, registry'ye istek göndermeden önce bu registry için bir kimlik bilgisi arıyor; yoksa yayın hiç başlamıyor. Yerel registry'de bir kullanıcı hesabı açmak yerine yayın komutlarını --dry-run ile sınırladık. CI'da bu kimlik bilgisini aşağıda göreceğimiz trusted publishing sağlıyor.
dist-tag'ler, Prerelease ve Deprecate
npm install @bookstore/ui dediğinde npm sürümü doğrudan seçmiyor, latest dist-tag'inin işaret ettiği sürümü kuruyor. dist-tag'ler, sürümlere verilen hareketli isimler. Angular'ın kendi paketi bunun iyi bir örneği:
$ npm view @angular/core dist-tags
{
...
'v20-lts': '20.3.35',
'v21-lts': '21.2.26',
next: '22.3.0-rc.0',
latest: '22.2.6'
}
latest kararlı sürümü, next bir sonraki sürümün release candidate'ini gösteriyor; her eski major için bir vNN-lts tag'i de LTS (Long Term Support; uzun süreli destek) dönemindeki son patch'i işaret ediyor. Böylece Angular 21'de kalan bir proje npm install @angular/core@v21-lts ile kendi major'ındaki en son düzeltmeyi alabiliyor.
Bir prerelease'i yayınlarken npm 11 seni korumaya alıyor:
$ npm version 1.1.0-next.0 --no-git-tag-version --prefix projects/bookstore/ui
v1.1.0-next.0
$ npx ng build @bookstore/ui
$ npm publish dist/bookstore/ui --dry-run
npm error You must specify a tag using --tag when publishing a prerelease version.
$ npm publish dist/bookstore/ui --dry-run --tag next
npm notice Publishing to https://registry.npmjs.org/ with tag next and public access (dry-run)
+ @bookstore/ui@1.1.0-next.0
Tag vermeden prerelease yayınlamak artık hata. Eskiden bu komut 1.1.0-next.0'ı latest yapar ve npm install @bookstore/ui diyen herkese prerelease kurulurdu. Tag'leri sonradan yönetmek için npm dist-tag komutları var (registry'de yetki gerektirdikleri için yerelde koşulmadı):
npm dist-tag add @bookstore/ui@1.1.0 latest
npm dist-tag add @bookstore/ui@1.0.3 v1-lts
npm dist-tag rm @bookstore/ui next
npm dist-tag ls @bookstore/ui
Bir sürümde ciddi bir hata bulduğunda npm'deki karşılığı deprecate. Paket kaldırılmıyor, ama kuran herkes bir uyarı görüyor; mesajı boş string ile verince deprecation geri alınıyor (bu komutlar da yerelde koşulmadı):
# tüm 1.0.x sürümleri
npm deprecate @bookstore/ui@"1.0.x" "PriceTag shows the wrong currency in 1.0.x. Use 1.1.0 or later."
# geri almak
npm deprecate @bookstore/ui@"1.0.x" ""
Kullanıcı tarafında deprecation böyle görünüyor; npmjs'te deprecate edilmiş gerçek bir paketten:
$ npm view request@2.88.2 deprecated
request has been deprecated, see https://github.com/request/request/issues/3142
Trusted Publishing ile Yayın
NuGet'te gördüğümüz modelin npm karşılığı aynı mantıkla çalışıyor: npmjs'te paketin ayarlarına bir trusted publisher ekliyorsun (organizasyon ya da kullanıcı, repository, workflow dosyasının adı, isteğe bağlı environment) ve workflow uzun ömürlü bir token yerine GitHub'ın OIDC (OpenID Connect; kimliği imzalı kısa ömürlü token'larla taşıyan standart) token'ıyla yayın yapıyor. npm dokümanına göre bunun için npm CLI 11.5.1 ve Node.js 22.14 ya da daha yenisi gerekiyor ve yalnız GitHub'ın kendi runner'ları destekleniyor. Public repository'den public bir paket yayınlandığında provenance da kendiliğinden üretiliyor. Bookstore'un workflow'u:
name: publish-ui
on:
push:
tags: ['ui/v*']
permissions:
contents: read
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
defaults:
run:
working-directory: web
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: 24
registry-url: https://registry.npmjs.org
cache: npm
cache-dependency-path: web/package-lock.json
- run: npm ci
- run: npx ng build @bookstore/ui
- name: Publish (prereleases go to the next dist-tag)
working-directory: web/dist/bookstore/ui
run: |
version=$(node -p "require('./package.json').version")
if [ "ui/v$version" != "$GITHUB_REF_NAME" ]; then
echo "Tag $GITHUB_REF_NAME does not match package version $version" >&2
exit 1
fi
if [[ "$version" == *-* ]]; then
npm publish --tag next
else
npm publish
fi
Workflow ui/v* tag'iyle tetikleniyor ve ilk iş olarak tag ile package.json'daki sürümün aynı olduğunu kontrol ediyor; ikisi ayrışırsa yayın durur. Sürümde tire varsa (1.1.0-next.0) next tag'iyle, yoksa latest ile yayınlıyor; npm'in prerelease kuralını workflow'a taşımış oluyoruz. Tag kontrolünü ve tag seçimini yerelde aynı değişkenlerle koşturduk; workflow actionlint ile doğrulandı, GitHub'da koşulmadı. npm dokümanı, trusted publisher kurulduktan sonra paketin ayarlarında token ile yayını tamamen kapatmayı da öneriyor; böylece yayının tek yolu bu workflow oluyor.
Üretimde Dikkat
Paketin içeriğini her sürümde kontrol et. npm publish --dry-run ya da npm pack çıktısını CI'da bir artifact olarak saklamak, "bu sürümde neden 2 MB'lık bir sourcemap var?" sorusunu yayından önce sormayı sağlıyor.
Peer aralığını test ederek genişlet. Kütüphaneyi Angular 22 ve 23'te çalışır ilan etmeden önce her iki sürümle derlenen bir test uygulamasında dene. Beyan ettiğin aralık, CI'da gerçekten denediğin aralık olmalı.
LTS dist-tag'ini planla. Kütüphanenin eski bir major'ını desteklemeye devam ediyorsan Angular gibi bir v1-lts tag'i tut ve 1.x'e çıkan her güvenlik düzeltmesinde güncelle; aksi hâlde eski major'daki kullanıcılar hangi sürümün güncel olduğunu bilemez.
Yayın yetkisini kişilerden al. Trusted publishing kurulduktan sonra token'la yayını kapat, eski automation token'larını iptal et. Kişisel bir hesapta duran yayın yetkisi, o hesabın ele geçirilmesiyle paketin ele geçirilmesi demek.
Sık Düşülen Tuzaklar
dist yerine kaynak klasörünü yayınlamak. Tahmin bölümündeki durum: TypeScript kaynakları ve test dosyaları npm'e gider, paketi kuran uygulama derlenmiş kodu bulamaz. Yayın komutunu her zaman build çıktısının klasöründe çalıştır ve CI'da bundan başka bir yol bırakma.
Angular'ı dependencies'e yazmak. Uygulamada iki Angular kopyası oluşur ve hata çoğu zaman kütüphanenin değil uygulamanın kodunda patlıyor gibi görünür.
Peer aralığını fazla geniş bırakmak. >=20 gibi bir aralık, denenmemiş sürümlerde kurulumu sessizce kabul ettirir; npm'in ERESOLVE kontrolü işe yaramaz hâle gelir.
Prerelease'i latest'e düşürmek. npm 11 tag'siz prerelease yayınını engelliyor, ama npm dist-tag add @bookstore/ui@1.1.0-next.0 latest gibi elle yapılan bir değişiklik aynı sonucu doğurur.
Secondary entry point'leri relatif yolla bağlamak. ../money/src/money gibi bir import build'i bozar ya da iki entry point'te aynı kodun iki kopyasını üretir; aynı kütüphane içinde de paket yolunu kullan.
Küçük Bir Deneme
★☆☆ dist/bookstore/ui'yi npm pack ile paketle, tarball'ı aç ve package.json'daki exports alanını incele. Bir uygulamadan @bookstore/ui/fesm2022/bookstore-ui.mjs'i import etmeyi dene; hangi hatayı alıyorsun?
★★☆ Kütüphanenin peer aralığını ^21.0.0 || ^22.2.0 yap ve Angular 21'deki test projesine kurmayı tekrar dene. Kurulum geçiyor mu? Kütüphane gerçekten Angular 21'de çalışıyor mu, nasıl doğrularsın?
★★★ Kütüphaneye @bookstore/ui/testing adında, testlerde kullanılacak sahte veriler içeren ikinci bir secondary entry point ekle. Ana paketin bundle boyutu değişiyor mu? Tüketen uygulama yalnız @bookstore/ui'yi kullandığında testing kodu bundle'a giriyor mu?
NuGet ve npm paketleri artık sürümlü, doğrulanmış ve anahtarsız yayınlanıyor. Serinin son yazısında bütün bu artifact'ların nerede durduğuna bakıyoruz: feed'ler ve registry'ler, container image tag'leri, hotfix akışı, release takvimi ve destek politikası.