TypeScript project references, tek bir büyük kod tabanını her biri kendi tsconfig.json dosyasına sahip küçük projelere bölmenizi sağlar. Bir proje, bağlı olduğu projeyi "references" dizisiyle gösterir; referans verilen projede "composite": true ayarı bulunmalıdır. tsc --build (kısaca tsc -b) komutu da tüm referansları bulur, hangilerinin güncel olmadığını tespit eder ve yalnızca onları doğru sırada derler.

Bu yazıda iki paketli küçük bir yapıyı TypeScript 6.0.3 ile sıfırdan kurup derledik, karşılaştığımız gerçek hata mesajlarını da ekledik. tsconfig dosyasının temel alanlarını bilmiyorsanız önce tsconfig.json rehberine göz atmanızı öneririz.

Project references hangi sorunu çözer?

Dokümantasyon, kaynak ve test klasörü olan tek tsconfig'li bir projeden yola çıkıyor ve şu sıkıntıları sayıyor: uygulama dosyalarının yanlışlıkla test dosyalarını import edebilmesi, src ile test klasörlerini birlikte derlerken çıktıda istenmeyen klasör adlarının oluşması, uygulamanın yalnızca iç kısmı değiştiğinde testlerin de baştan denetlenmesi. Birden fazla tsconfig kullanmak bunların bir kısmını çözer, ama bu kez güncellik denetimi olmaz ve tsc komutunu her proje için ayrı çalıştırmanız gerekir.

Modül seçim rehberi de benzer bir öneri yapıyor: tek bir tsconfig yalnızca tek bir ortamı (hangi global değişkenlerin olduğu, modüllerin nasıl davrandığı) temsil edebilir. Sunucu kodu, tarayıcı kodu ve test kodu aynı uygulamada yaşıyorsa her birinin kendi tsconfig dosyası olmalı ve bunlar project references ile birbirine bağlanmalıdır.

Örnek yapı

Örneğimizde ortak adında bir yardımcı paket ve onu kullanan bir api paketi var. Kök klasördeki package.json dosyasında "type": "module" tanımlı.

bash
proje/
├── package.json
├── tsconfig.base.json
├── tsconfig.json
└── packages/
    ├── ortak/
    │   ├── tsconfig.json
    │   └── src/fiyat.ts
    └── api/
        ├── tsconfig.json
        └── src/index.ts

Ortak ayarlar extends ile paylaşılıyor. Dokümantasyon, tsconfig sayısı arttığında ortak seçenekleri tek bir dosyada toplamayı öneriyor:

json
// tsconfig.base.json
{
  "compilerOptions": {
    "module": "nodenext",
    "composite": true,
    "declarationMap": true
  }
}

// packages/ortak/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "rootDir": "./src",
    "outDir": "./dist"
  },
  "include": ["src"]
}

// packages/api/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "rootDir": "./src",
    "outDir": "./dist"
  },
  "include": ["src"],
  "references": [{ "path": "../ortak" }]
}

Kökteki tsconfig.json bir "çözüm" dosyasıdır: kendisi dosya derlemez, yalnızca alt projeleri listeler. Dokümantasyona göre files dizisini boş bırakmak, aynı dosyaların iki kez derlenmesini önler.

json
{
  "files": [],
  "references": [
    { "path": "./packages/ortak" },
    { "path": "./packages/api" }
  ]
}
typescript
// packages/ortak/src/fiyat.ts
export function kdvEkle(tutar: number, oran = 0.2): number {
  return Math.round(tutar * (1 + oran) * 100) / 100;
}

// packages/api/src/index.ts
import { kdvEkle } from "../../ortak/dist/fiyat.js";
console.log(kdvEkle(100)); // 120

tsc --build ile derlemek

tsc --build ile derlemek: --verbose, --dry, --clean, --force

Kök klasörde npx tsc -b --verbose çalıştırdık. Zaman damgalarını ve uzun mutlak yolları kısaltarak çıktı şöyle:

bash
npx tsc -b --verbose
Projects in this build:
    * packages/ortak/tsconfig.json
    * packages/api/tsconfig.json
    * tsconfig.json
Project 'packages/ortak/tsconfig.json' is out of date because output file 'packages/ortak/tsconfig.tsbuildinfo' does not exist
Building project '.../packages/ortak/tsconfig.json'...
Project 'packages/api/tsconfig.json' is out of date because output file 'packages/api/tsconfig.tsbuildinfo' does not exist
Building project '.../packages/api/tsconfig.json'...

Derleyici önce ortak, sonra api projesini derledi. Her projede dist klasörüne .js, .d.ts ve .d.ts.map dosyaları, proje klasörüne de tsconfig.tsbuildinfo yazıldı. node packages/api/dist/index.js komutu 120 yazdı. Hiçbir şeyi değiştirmeden komutu tekrar çalıştırdığımızda iki proje de atlandı:

bash
Project 'packages/ortak/tsconfig.json' is up to date because newest input 'packages/ortak/src/fiyat.ts' is older than output 'packages/ortak/tsconfig.tsbuildinfo'
Project 'packages/api/tsconfig.json' is up to date because newest input 'packages/api/src/index.ts' is older than output 'packages/api/tsconfig.tsbuildinfo' 

Komut satırında verdiğiniz projelerin sırası önemli değildir; tsc -b bağımlılıkların her zaman önce derlenmesi için sırayı kendisi düzenler. Birden fazla proje de verebilirsiniz: tsc -b src test gibi.

BayrakNe yapar
--verboseHangi projenin neden derlendiğini ya da atlandığını yazar
--dryNe yapılacağını gösterir ama derlemez
--cleanBelirtilen projelerin çıktılarını siler
--forceTüm projeleri güncel değilmiş gibi yeniden derler
--watchİzleme modunda çalışır
--stopBuildOnErrorsÜst projede hata varsa bağımlı projeleri derlemez

composite ayarı neleri değiştirir?

Referans verilen her projede composite açık olmalıdır. Bu ayar, derleme araçlarının bir projenin çıktısını hızla bulabilmesi ve güncel olup olmadığını anlayabilmesi için bazı kurallar koyar:

  • rootDir açıkça yazılmazsa tsconfig dosyasının klasörü kabul edilir. TypeScript 6.0'da bu artık tüm projelerin varsayılanı.
  • Tüm uygulama dosyaları include kalıbıyla eşleşmeli ya da files dizisinde listelenmelidir.
  • declaration varsayılan olarak açılır, çünkü bağımlı projeler .d.ts çıktısını kullanır.
  • incremental varsayılan olarak açılır ve derleme bilgisi .tsbuildinfo dosyasına kaydedilir.

ortak projesinde composite değerini false yaptığımızda api projesi şu hatayı verdi:

bash
packages/api/tsconfig.json(8,18): error TS6306: Referenced project '.../packages/ortak' must have setting "composite": true.

api içinden include kapsamı dışındaki bir dosyayı import ettiğimizde de iki hata birlikte geldi: dosya rootDir altında olmadığı için TS6059 ve projenin dosya listesinde bulunmadığı için TS6307 (Projects must list all files or use an 'include' pattern.).

Referans verilen projeden import etmek

Bir projeye referans verdiğinizde, o projedeki modülleri import etmek kaynak .ts dosyasını değil, projenin ürettiği .d.ts çıktısını yükler. Bu yüzden bağımlı proje derlenmeden önce üst projenin derlenmiş olması gerekir. ortak projesini derlemeden api projesini tek başına denetlediğimizde şu hatayı aldık:

bash
packages/api/src/index.ts(1,25): error TS6305: Output file '.../packages/ortak/dist/fiyat.d.ts' has not been built from source file '.../packages/ortak/src/fiyat.ts'.

Çözüm, derlemeyi tsc -p yerine tsc -b ile yapmaktır; böylece ortak önce derlenir. Import yolunu seçerken çalışma anını da düşünmelisiniz: tsc import yollarını değiştirmez. Örneğimizde api, ortak paketinin dist klasörünü gösteriyor; böylece aynı yol hem TypeScript hem Node.js için geçerli. Paketler gerçek npm paketleri gibi birbirini kullanacaksa, modül referansı paths yerine npm, yarn ya da pnpm workspace kullanmayı öneriyor. Alias tarafını paths ve rootDir yazısında ayrıca ele aldık.

Hata olduğunda tsc -b nasıl davranır?

Proje referansları dokümantasyonu, tsc -b komutunun tüm projeler için noEmitOnError açıkmış gibi davrandığını söylüyor. Bu davranış TypeScript 5.6 ile değişti: sürüm notlarına göre --build modu artık bağımlılıklarda hata olsa bile derlemeyi sürdürüyor, hataları raporluyor ve çıktıları elinden geldiğince üretiyor. Bunu takip edebilmek için --incremental ya da composite açık olmasa bile her proje için .tsbuildinfo dosyası yazılıyor.

Bunu denemek için ortak projesine bilinçli bir tip hatası ekledik. Düz tsc -b hatayı raporladı ve çıkış kodu 2 oldu, ama api/dist klasörü yine üretildi. Aynı durumu --stopBuildOnErrors ile çalıştırdığımızda bağımlı proje atlandı:

bash
npx tsc -b --stopBuildOnErrors --verbose
packages/ortak/src/fiyat.ts(4,14): error TS2322: Type 'string' is not assignable to type 'number'.
Project 'packages/api/tsconfig.json' can't be built because its dependency 'packages/ortak' has errors

Sürekli entegrasyon (CI) ortamında ilk hatada durmak istiyorsanız bu bayrağı kullanabilirsiniz. Sürüm notları bayrağın amacını bu şekilde açıklıyor.

Dikkat edilecekler

Dikkat edilecekler: Editör çıktılara ihtiyaç duyar, Zaman damgaları, outFile artık yok, tsc tek başına referansları derlemez
  • Editör çıktılara ihtiyaç duyar: Bağımlı projeler üst projelerin .d.ts dosyalarını kullandığı için, depoyu klonladıktan sonra bir kez derlemeniz gerekebilir. Dokümantasyona göre VS Code bunu bellekte üreterek hafifletir; çok büyük projelerde disableSourceOfProjectReferenceRedirect ile bu davranış kapatılabilir.
  • Zaman damgaları: Derleme çıktılarını sürüm kontrolüne ekliyorsanız, bazı işlemlerden sonra --force ile tam derleme gerekebilir.
  • outFile artık yok: TypeScript 6.0, outFile seçeneğini kaldırdı. Eski dokümanlarda geçen outFile tabanlı yapılandırmalar yerine bir paketleyici kullanın.
  • tsc tek başına referansları derlemez: Mevcut derleme akışlarını bozmamak için bağımlılıklar yalnızca --build ile otomatik derlenir.

Sık Sorulan Sorular

tsc -b ile tsc -p arasındaki fark nedir?

tsc -p tek bir projeyi derler ve referans verilen projeleri derlemez, yalnızca çıktılarını okur. tsc -b ise referans grafiğini dolaşır, güncel olmayan projeleri bulur ve hepsini bağımlılık sırasına göre derler.

composite olmadan project references kullanılabilir mi?

Referans veren proje için şart değildir, ama referans verilen her projede composite: true olmalıdır; yoksa TS6306 hatası alırsınız.

.tsbuildinfo dosyasını sürüm kontrolüne eklemeli miyim?

incremental dokümantasyonuna göre bu dosyalar çalışma anında JavaScript tarafından kullanılmaz ve güvenle silinebilir. Derleme önbelleği oldukları için genellikle çıktı klasörleriyle aynı şekilde ele alınırlar.

Kök tsconfig.json dosyasında neden files boş?

Kök dosya yalnızca alt projeleri bir araya getirir. files boş bırakılmazsa aynı kaynak dosyalar hem kök hem alt proje tarafından derlenir. En az bir referans varken boş files dizisi hata sayılmaz.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: TypeScript öğrenme rehberi · tsconfig.json rehberi · paths, rootDir ve outDir · .d.ts declaration dosyaları · TypeScript ile Node.js · sık TypeScript hataları