Import map, <script type="importmap"> içine yazılan bir JSON nesnesidir ve tarayıcının JavaScript modüllerini içe aktarırken modül belirtecini (module specifier) hangi URL'ye çözeceğini belirler. Normalde tarayıcıda import ifadesine tam ya da göreli bir URL yazmanız gerekir ("./moduller/kare.js" gibi). Import map ile import { ciz } from "kare" gibi çıplak adlar (bare specifier) kullanabilir, bir yol önekini toplu olarak başka bir klasöre yönlendirebilir, kodun bulunduğu yere göre farklı sürümler seçebilir ve modüller için bütünlük özetleri tanımlayabilirsiniz.

Bu yazıda import map'in JSON biçimini (imports, scopes, integrity), eşleştirme kurallarını, birden fazla import map'in nasıl birleştirildiğini, karma dosya adlarıyla önbellek yönetimini ve en sık yapılan hataları MDN belgeleri üzerinden anlatıyoruz. Modül betiklerinin HTML'deki genel davranışı script etiketi yazısında ele alınıyor.

Temel kullanım

html
<script type="importmap">
  {
    "imports": {
      "daire": "https://ornek.com/sekiller/daire.js",
      "kare": "./moduller/sekiller/kare.js"
    }
  }
</script>
<script type="module">
  import { ad as daireAdi } from "daire";
  import { ad as kareAdi, ciz } from "kare";
</script>

Bu örnekte imports anahtarı bir "modül belirteci haritası" tutar. "daire" ve "kare" anahtarları, import ifadesinde görünebilecek metinlerdir; değerleri ise çözümleme sırasında o metnin yerine geçecek adreslerdir. Haritadan sonra gelen modüller artık uzun URL'ler yerine bu kısa adları kullanabilir. Harita hem statik import ifadesinde hem de dinamik import() operatöründe kullanılır.

Haritanın kapsamına dikkat edin. MDN'e göre import map yalnızca belgeye yüklenen modüllerdeki import ve import() belirteçlerine uygulanır. Bir <script> öğesinin src özniteliğindeki yola uygulanmaz; yani <script type="module" src="kare"> yazamazsınız. Worker ve worklet içine yüklenen modüllere de uygulanmaz. Ayrıca harita, kendisini kullanan modüllerden önce tanımlanmış ve işlenmiş olmalıdır; bu yüzden head içinde, modül betiklerinden önce yazılır.

Sözdizimi kuralları ve hatalar

Sözdizimi kuralları ve hatalar: Üst düzey anahtarlar, Boş anahtar yok, Değerler metin, Sondaki eğik çizgi

<script type="importmap"> öğesinde src, async, nomodule, defer, crossorigin, integrity ve referrerpolicy öznitelikleri belirtilmemelidir. Harita her zaman öğenin içine satır içi olarak yazılır ve içerik geçerli bir JSON nesnesi olmalıdır.

Harita tanımı bir JSON nesnesi değilse ya da scopes gibi bir anahtar tanımlanmış ama değeri nesne değilse tarayıcı TypeError fırlatır. Şemaya uymayan diğer durumlarda (örneğin geçersiz bir URL değeri) tarayıcılar konsolda uyarı üretir. Harita içindeki kurallar şöyledir:

KuralAçıklama
Üst düzey anahtarlarimports, scopes ve integrity; hepsi isteğe bağlı, değerleri nesne olmalı, boş olabilir
Boş anahtar yokModül belirteci haritasında hiçbir anahtar boş olamaz
Değerler metinHer değer geçerli bir mutlak URL veya / , ./ , ../ ile başlayan bir URL metni olmalı
Sondaki eğik çizgiAnahtar / ile bitiyorsa değer de / ile bitmeli; bu durumda anahtar önek olarak eşleşir
Sıra önemsizBirden fazla anahtar eşleşirse en özgül (en uzun) anahtar seçilir
Göreli adreslerImport map in temel URL sine göre çözülür

Çözülemeyen bir çıplak adı içe aktarmaya çalışırsanız ne olur? MDN'in modüller rehberine göre tarayıcıda çıplak ad kullanmak için import map gerekir; bir modül konumuna çözülemeyen bir belirteç içe aktarılmaya çalışıldığında JavaScript TypeError fırlatır. Node.js'te çıplak adlar dosya sistemindeki standart bir konuma çözüldüğü için çalışır; tarayıcıda bu bilgiyi import map sağlar.

Yol öneklerini eşlemek

Anahtar ve değer ikisi de / ile bittiğinde eşleme bir yol öneki olarak çalışır ve bir URL kümesinin tamamını başka bir yere taşır. Sondaki eğik çizgi yoksa tarayıcı yalnızca belirtecin tamamını eşleştirip değiştirir; varsa belirtecin bir parçası olarak yerine koyar.

html
<script type="importmap">
  {
    "imports": {
      "sekiller/": "./moduller/sekiller/",
      "diger-sekiller/": "https://ornek.com/moduller/sekiller/"
    }
  }
</script>
<script type="module">
  import { ad } from "sekiller/daire.js";
  // ./moduller/sekiller/daire.js olarak çözülür
</script>

Bu yöntemle npm ekosistemindeki "paket" kullanımını taklit edebilirsiniz. MDN'in örneğinde lodash çıplak adı paketin ana dosyasına, lodash/ öneki ise paket klasörüne eşlenir. Böylece hem paketin tamamını hem de içindeki tek bir modülü içe aktarabilirsiniz:

html
<script type="importmap">
  {
    "imports": {
      "lodash": "/node_modules/lodash-es/lodash.js",
      "lodash/": "/node_modules/lodash-es/"
    }
  }
</script>
<script type="module">
  import _ from "lodash";
  import fp from "lodash/fp.js";
</script>

fp modülünü .js uzantısı olmadan ("lodash/fp") içe aktarmak isterseniz bunun için ayrı bir çıplak anahtar tanımlamanız gerekir. MDN bunun tek bir modül için makul olduğunu, çok sayıda modülde ise iyi ölçeklenmediğini belirtir.

Anahtarların tek sözcüklük adlar olması gerekmez. Yol ayırıcı içerebilir, mutlak bir URL olabilir ya da /, ./, ../ ile başlayan göreli bir yol olabilir. Mutlak URL anahtarı, örneğin uzak bir CDN adresini kendi sunucunuzdaki bir kopyaya yönlendirmek için kullanışlıdır. Eşleştirmeden önce belirteç normalleştirilir: ./foo/../js/app.js, ./js/app.js olarak çözülür ve bu yüzden ./js/app.js anahtarıyla eşleşir. Birden fazla anahtar eşleşebiliyorsa en özgül olan kazanır; "zeytin/dal/" anahtarı "zeytin/" anahtarından önce eşleşir.

Mini örnek: belirteçler adım adım nasıl çözülür?

Mini örnek: belirteçler adım adım nasıl çözülür? grafik, araclar/para.js, araclar/eski/tarih.js, grafik/eksen.js

Kuralları bir arada görmek için aşağıdaki haritayı ve belgenin kökünde (https://site.ornek/) duran bir sayfayı düşünün. Harita bir çıplak ad, bir yol öneki ve aynı önekin daha özgül bir alt yolunu içeriyor.

html
<script type="importmap">
  {
    "imports": {
      "grafik": "/vendor/grafik/grafik.js",
      "araclar/": "/js/araclar/",
      "araclar/eski/": "/js/arsiv/araclar/"
    }
  }
</script>
import ifadesindeki belirteçEşleşen anahtarSonuç
grafikgrafik (tam eşleşme)https://site.ornek/vendor/grafik/grafik.js
araclar/para.jsaraclar/ (önek)https://site.ornek/js/araclar/para.js
araclar/eski/tarih.jsaraclar/eski/ (daha uzun önek kazanır)https://site.ornek/js/arsiv/araclar/tarih.js
grafik/eksen.jsYok: grafik anahtarı / ile bitmediği için önek değildirÇözülemez, TypeError
tabloYokÇözülemez, TypeError

Tablodaki dördüncü satır en sık karşılaşılan şaşkınlıktır: "grafik" anahtarı yalnızca belirtecin tamamı grafik olduğunda eşleşir. Paketin içindeki başka dosyalara da erişmek istiyorsanız, MDN'in lodash örneğindeki gibi ayrıca "grafik/" önek anahtarını eklemeniz gerekir. Üçüncü satır ise "en özgül anahtar kazanır" kuralını gösterir: araclar/eski/tarih.js hem araclar/ hem araclar/eski/ ile eşleşebilir, daha uzun olan seçilir. Değerler / ile başladığı için sayfanın kökenine göre mutlak yola çözülür.

scopes: koda göre farklı sürüm

scopes anahtarı, yalnızca içe aktarmayı yapan betiğin URL'si belirli bir yolu içeriyorsa kullanılan eşlemeler tanımlar. Böylece aynı belirteç, onu kimin içe aktardığına göre farklı bir modüle çözülebilir. npm gibi paket yöneticilerinin bir uygulamada aynı paketin farklı sürümlerini barındırmasına benzer bir sürüm yönetimi sağlar.

html
<script type="importmap">
  {
    "imports": {
      "harika-modul": "/node_modules/harika-modul/index.js"
    },
    "scopes": {
      "/node_modules/bagimlilik/": {
        "harika-modul": "/node_modules/baska/konum/harika-modul/index.js"
      }
    }
  }
</script>

Bu haritada, URL'si /node_modules/bagimlilik/ içeren bir betik harika-modul'ü içe aktarırsa /node_modules/baska/konum/ altındaki sürüm kullanılır. Başka bir yerdeki betik aynı adı içe aktarırsa imports altındaki ana sürüm gelir. Birden fazla kapsam eşleşirse önce en özgül (en uzun anahtarlı) kapsam denenir; orada uygun belirteç yoksa bir sonraki kapsama, en sonunda imports haritasına düşülür.

MDN'in vurguladığı bir ayrıntı: kapsam, adresin nasıl çözüleceğini değiştirmez. Eşlenen değerin kapsam yoluyla eşleşmesi gerekmez ve göreli adresler her zaman import map'in temel URL'sine göre çözülür, içe aktaran betiğin konumuna göre değil.

integrity: modüller için bütünlük özeti

integrity anahtarı, modül URL'lerini bütünlük meta verilerine eşler. Değerler, <script> öğesinin integrity özniteliğindeki biçimle aynıdır (sha384-... gibi). Böylece statik ya da dinamik olarak içe aktarılan modüllerin içeriğinin değiştirilmediğinden emin olabilirsiniz. Anahtarlar mutlak ya da /, ./, ../ ile başlayan göreli URL'lerdir. Bir modülü içe aktaran veya önyükleyen betiğin URL'si bir anahtarla eşleşirse, kendi bütünlük bilgisi zaten yoksa bu değer uygulanır; bu sayede integrity özniteliği taşımayan üst düzey veya önyüklenen modüller için de bir yedek sağlanır.

html
<script type="importmap">
  {
    "imports": {
      "kare": "./moduller/sekiller/kare.js"
    },
    "integrity": {
      "./moduller/sekiller/kare.js": "sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
    }
  }
</script>

Bu örnekte özet doğrudan kare.js dosyasına, dolaylı olarak da "kare" çıplak adına uygulanır. Özetin nasıl üretildiği ve tarayıcının onu nasıl doğruladığı integrity ve crossorigin yazısında anlatılıyor.

Birden fazla import map

Destekleyen tarayıcılarda belgenin herhangi bir yerinde bir veya daha fazla import map tanımlayabilirsiniz; tek koşul, onlara bağlı modüllerden önce tanımlanmalarıdır. MDN, bazı tarayıcı sürümlerinin yalnızca tek bir import map'e izin verdiğini ve onun da herhangi bir modül yüklenmeden önce gelmesi gerektiğini not eder; tarayıcı desteği için MDN uyumluluk tablosuna bakın. Tarayıcı içeride tek bir global import map tutar ve her yeni haritayı kaydedildiği anda buna birleştirir.

html
<script type="importmap">
  { "imports": { "/uygulama/": "./ozgun-uygulama/" } }
</script>
<script type="importmap">
  {
    "imports": { "/uygulama/yardimci": "./yardimci/index.mjs" },
    "scopes": { "/js": { "/uygulama/": "./js-uygulama/" } }
  }
</script>

Bu iki harita, üç kuralı birden içeren tek bir haritayla eşdeğerdir. Birleştirmenin iki kısıtı vardır. Birincisi, yeni haritadaki bir belirteç daha önce çözülmüşse o kural düşürülür; sonraki çözümlemeler önceki sonucu verir. İkincisi, global haritada zaten bir URL'ye eşlenmiş bir belirteç yeni haritada tekrar tanımlanırsa yeni kural düşürülür ve önceki eşleme geçerli kalır. Yani sonradan gelen bir harita önceki bir kuralı ezemez; kuralları değiştirmek istiyorsanız ilk tanımı değiştirmeniz gerekir.

Karma dosya adlarıyla önbellek yönetimi

Önbelleği kolaylaştırmak için betik dosyalarına genellikle içerik özetini içeren adlar verilir (uygulama-fg7744e1b.js gibi). Sorun şudur: bir modül değişince adı değişir ve onu bu adla içe aktaran tüm modüllerin de güncellenmesi gerekir; bu da zincirleme güncellemelere ve gereksiz indirmelere yol açar. MDN'in önerdiği çözümde kod karmasız bir ada bağımlı olur, import map ise bu adı gerçek dosyaya eşler.

html
<script type="importmap">
  {
    "imports": {
      "ana_betik": "/dist/uygulama-fg7744e1b.js",
      "bagimlilik_betigi": "/dist/bagimlilik-3qn7e4b1q.js"
    }
  }
</script>

bagimlilik_betigi değiştiğinde yalnızca import map'teki dosya adını güncellemeniz yeter. Onu içe aktaran JavaScript dosyalarının kaynak kodu değişmez, çünkü import ifadesindeki belirteç aynı kalır.

Destek denetimi ve modül önyükleme

Import map desteğini HTMLScriptElement.supports() statik yöntemiyle denetleyebilirsiniz. MDN, desteklemeyen tarayıcılarda modül çözümleme sorunlarını önlemek için es-module-shims adlı bir polyfill kullanılabileceğini de belirtir.

javascript
if (HTMLScriptElement.supports?.("importmap")) {
  console.log("Tarayıcı import map destekliyor.");
}

Import map modüllerin nereden geleceğini söyler, ama indirmeyi hızlandırmaz. Modüller ve bağımlılıkları sıralı olarak keşfedildiği için büyük uygulamalarda <link rel="modulepreload"> ile önemli modülleri önceden indirebilirsiniz; bu konu preload ve modulepreload yazısında anlatılıyor.

Uygulama: eksiksiz bir sayfa

Aşağıdaki sayfada import map head içinde, modül betiklerinden önce tanımlanıyor. Uygulama kodu js/ana.js dosyasında duruyor ve içinde import { formatla } from "araclar/para.js" gibi kısa belirteçler kullanabiliyor. modulepreload ile ana modül ve kritik bağımlılığı önceden indiriliyor.

html
<!doctype html>
<html lang="tr">
  <head>
    <meta charset="utf-8" />
    <title>Fatura Hesaplayıcı</title>
    <script type="importmap">
      {
        "imports": {
          "araclar/": "/js/araclar/",
          "grafik": "/vendor/grafik/grafik.js"
        }
      }
    </script>
    <link rel="modulepreload" href="js/ana.js" />
    <link rel="modulepreload" href="js/araclar/para.js" />
    <script type="module" src="js/ana.js"></script>
  </head>
  <body>
    <h1>Fatura Hesaplayıcı</h1>
  </body>
</html>

Sık yapılan hatalar

Sık yapılan hatalar: Haritayı modülden sonra yazmak, src ile harici harita yüklemek, Haritanın script src'ye uygulanacağını sanmak, Önekte eğik çizgiyi unutmak
  • Haritayı modülden sonra yazmak: harita, onu kullanan modüller yüklenmeden önce tanımlanmalıdır.
  • src ile harici harita yüklemek: import map öğesinde src belirtilmemelidir; JSON satır içi yazılır.
  • Haritanın script src'ye uygulanacağını sanmak: harita yalnızca import ve import() belirteçlerine uygulanır.
  • Önekte eğik çizgiyi unutmak: anahtar / ile bitiyorsa değer de / ile bitmelidir.
  • Geçersiz JSON: içerik bir JSON nesnesi olarak ayrıştırılamazsa harita kullanılamaz; tarayıcı hata veya konsol uyarısı üretir.
  • Sonraki haritayla kuralı ezmeye çalışmak: zaten eşlenmiş veya çözülmüş belirteçler yeni haritada düşürülür.
  • Değerleri çıplak yazmak: değer moduller/kare.js gibi önek olmadan yazılamaz; ./moduller/kare.js ya da mutlak URL kullanın.
  • Worker içinde kullanmayı beklemek: import map yalnızca belgeye uygulanır.

Sık Sorulan Sorular

Import map bir derleme aracının yerini tutar mı?

Import map yalnızca modül belirteçlerinin çözümlenmesini çözer: çıplak adlar, yol yönlendirme ve sürüm seçimi. MDN, bunun tarayıcı ve sunucu arasında aynı JavaScript kitaplıklarını kullanmayı kolaylaştırdığını söyler. Kodun birleştirilmesi, küçültülmesi veya başka dillerden dönüştürülmesi ayrı işlerdir.

Göreli değerler neye göre çözülür?

Import map'i içeren belgenin temel URL'sine göre. Kapsamlar bu kuralı değiştirmez; içe aktaran betiğin konumu göreli değerin çözümünü etkilemez.

Birden fazla anahtar eşleşirse hangisi seçilir?

En özgül olan, yani en uzun eşleşen anahtar. JSON'daki yazılış sırası önemli değildir.

Import map ile JSON veya CSS içe aktarabilir miyim?

Import map yalnızca adresi çözer. JSON veya CSS'i modül olarak içe aktarmak için MDN, import ifadesinde with { type: "json" } gibi içe aktarma öznitelikleri (import attributes) kullanılmasını anlatır; tür belirtilmezse tarayıcı kaynağın JavaScript olduğunu varsayar.

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: HTML öğrenme rehberi · script etiketi · modulepreload · integrity özniteliği · JavaScript öğrenme rehberi